Commit a66cbb6e authored by Grzegorz Bizon's avatar Grzegorz Bizon

Merge commit '2d980fad' into...

Merge commit '2d980fad' into backstage/gb/improve-jobs-controller-performance

* commit '2d980fad': (51 commits)
parents fe225fd9 2d980fad
...@@ -13,10 +13,8 @@ variables: ...@@ -13,10 +13,8 @@ variables:
BUILD_ASSETS_IMAGE: "false" BUILD_ASSETS_IMAGE: "false"
before_script: before_script:
- bundle --version
- date - date
- source scripts/utils.sh - source scripts/utils.sh
- date
- source scripts/prepare_build.sh - source scripts/prepare_build.sh
- date - date
......
...@@ -6,8 +6,9 @@ ...@@ -6,8 +6,9 @@
.use-pg-10: &use-pg-10 .use-pg-10: &use-pg-10
services: services:
- postgres:10.7 - name: postgres:10.7
- redis:alpine command: ["postgres", "-c", "fsync=off", "-c", "synchronous_commit=off", "-c", "full_page_writes=off"]
- name: redis:alpine
.use-mysql: &use-mysql .use-mysql: &use-mysql
services: services:
...@@ -52,8 +53,10 @@ ...@@ -52,8 +53,10 @@
script: script:
- JOB_NAME=( $CI_JOB_NAME ) - JOB_NAME=( $CI_JOB_NAME )
- TEST_TOOL=${JOB_NAME[0]} - TEST_TOOL=${JOB_NAME[0]}
- export KNAPSACK_REPORT_PATH=knapsack/${CI_PROJECT_NAME}/${TEST_TOOL}_node_${CI_NODE_INDEX}_${CI_NODE_TOTAL}_report.json - TEST_LEVEL=${JOB_NAME[1]}
- export KNAPSACK_GENERATE_REPORT=true - DATABASE=${JOB_NAME[2]}
- export KNAPSACK_REPORT_PATH=knapsack/${CI_PROJECT_NAME}/${TEST_TOOL}_${TEST_LEVEL}_${DATABASE}_node_${CI_NODE_INDEX}_${CI_NODE_TOTAL}_report.json
- export KNAPSACK_GENERATE_REPORT=true KNAPSACK_LOG_LEVEL=debug KNAPSACK_TEST_DIR=spec
- export SUITE_FLAKY_RSPEC_REPORT_PATH=${FLAKY_RSPEC_SUITE_REPORT_PATH} - export SUITE_FLAKY_RSPEC_REPORT_PATH=${FLAKY_RSPEC_SUITE_REPORT_PATH}
- export FLAKY_RSPEC_REPORT_PATH=rspec_flaky/all_${TEST_TOOL}_${CI_NODE_INDEX}_${CI_NODE_TOTAL}_report.json - export FLAKY_RSPEC_REPORT_PATH=rspec_flaky/all_${TEST_TOOL}_${CI_NODE_INDEX}_${CI_NODE_TOTAL}_report.json
- export NEW_FLAKY_RSPEC_REPORT_PATH=rspec_flaky/new_${TEST_TOOL}_${CI_NODE_INDEX}_${CI_NODE_TOTAL}_report.json - export NEW_FLAKY_RSPEC_REPORT_PATH=rspec_flaky/new_${TEST_TOOL}_${CI_NODE_INDEX}_${CI_NODE_TOTAL}_report.json
...@@ -63,7 +66,10 @@ ...@@ -63,7 +66,10 @@
- '[[ -f $FLAKY_RSPEC_REPORT_PATH ]] || echo "{}" > ${FLAKY_RSPEC_REPORT_PATH}' - '[[ -f $FLAKY_RSPEC_REPORT_PATH ]] || echo "{}" > ${FLAKY_RSPEC_REPORT_PATH}'
- '[[ -f $NEW_FLAKY_RSPEC_REPORT_PATH ]] || echo "{}" > ${NEW_FLAKY_RSPEC_REPORT_PATH}' - '[[ -f $NEW_FLAKY_RSPEC_REPORT_PATH ]] || echo "{}" > ${NEW_FLAKY_RSPEC_REPORT_PATH}'
- scripts/gitaly-test-spawn - scripts/gitaly-test-spawn
- knapsack rspec "--color --format documentation --format RspecJunitFormatter --out junit_rspec.xml" - date
- 'export KNAPSACK_TEST_FILE_PATTERN=$(ruby -r./lib/quality/test_level.rb -e "puts Quality::TestLevel.new.pattern(:${TEST_LEVEL})")'
- knapsack rspec "--color --format documentation --format RspecJunitFormatter --out junit_rspec.xml --tag level:${TEST_LEVEL} --tag ~geo"
- date
artifacts: artifacts:
expire_in: 31d expire_in: 31d
when: always when: always
...@@ -140,19 +146,68 @@ setup-test-env: ...@@ -140,19 +146,68 @@ setup-test-env:
except: except:
- /(^docs[\/-].*|.*-docs$)/ - /(^docs[\/-].*|.*-docs$)/
rspec-pg: rspec unit pg:
<<: *rspec-metadata-pg
parallel: 20
rspec integration pg:
<<: *rspec-metadata-pg <<: *rspec-metadata-pg
parallel: 50 parallel: 6
rspec system pg:
<<: *rspec-metadata-pg
parallel: 24
rspec unit pg-10:
<<: *rspec-metadata-pg-10
<<: *only-schedules-master
parallel: 20
rspec integration pg-10:
<<: *rspec-metadata-pg-10
<<: *only-schedules-master
parallel: 6
rspec-pg-10: rspec system pg-10:
<<: *rspec-metadata-pg-10 <<: *rspec-metadata-pg-10
<<: *only-schedules-master <<: *only-schedules-master
parallel: 50 parallel: 24
rspec-mysql: rspec unit mysql:
<<: *rspec-metadata-mysql <<: *rspec-metadata-mysql
<<: *only-schedules-master <<: *only-schedules-master
parallel: 50 parallel: 20
rspec integration mysql:
<<: *rspec-metadata-mysql
<<: *only-schedules-master
parallel: 6
rspec system mysql:
<<: *rspec-metadata-mysql
<<: *only-schedules-master
parallel: 24
.rspec-mysql-on-demand: &rspec-mysql-on-demand
only:
variables:
- $CI_COMMIT_MESSAGE =~ /\[run mysql\]/i
- $CI_COMMIT_REF_NAME =~ /mysql/
rspec unit mysql on-demand:
<<: *rspec-metadata-mysql
<<: *rspec-mysql-on-demand
parallel: 20
rspec integration mysql on-demand:
<<: *rspec-metadata-mysql
<<: *rspec-mysql-on-demand
parallel: 6
rspec system mysql on-demand:
<<: *rspec-metadata-mysql
<<: *rspec-mysql-on-demand
parallel: 24
rspec-fast-spec-helper: rspec-fast-spec-helper:
<<: *rspec-metadata-pg <<: *rspec-metadata-pg
...@@ -164,16 +219,17 @@ rspec-fast-spec-helper: ...@@ -164,16 +219,17 @@ rspec-fast-spec-helper:
script: script:
- export CACHE_CLASSES=true - export CACHE_CLASSES=true
- scripts/gitaly-test-spawn - scripts/gitaly-test-spawn
- bin/rspec --color --format documentation --tag quarantine spec/ - bin/rspec --color --format documentation --tag quarantine -- spec/
rspec-pg-quarantine: rspec quarantine pg:
<<: *rspec-metadata-pg <<: *rspec-metadata-pg
<<: *rspec-quarantine <<: *rspec-quarantine
allow_failure: true allow_failure: true
rspec-mysql-quarantine: rspec quarantine mysql:
<<: *rspec-metadata-mysql <<: *rspec-metadata-mysql
<<: *rspec-quarantine <<: *rspec-quarantine
<<: *only-schedules-master
allow_failure: true allow_failure: true
static-analysis: static-analysis:
......
...@@ -40,12 +40,12 @@ update-tests-metadata: ...@@ -40,12 +40,12 @@ update-tests-metadata:
policy: push policy: push
script: script:
- retry gem install fog-aws mime-types activesupport rspec_profiling postgres-copy --no-document - retry gem install fog-aws mime-types activesupport rspec_profiling postgres-copy --no-document
- scripts/merge-reports ${KNAPSACK_RSPEC_SUITE_REPORT_PATH} knapsack/${CI_PROJECT_NAME}/rspec-pg_node_*.json - scripts/merge-reports ${KNAPSACK_RSPEC_SUITE_REPORT_PATH} knapsack/${CI_PROJECT_NAME}/rspec_*_pg_node_*.json
- '[[ -z ${TESTS_METADATA_S3_BUCKET} ]] || scripts/sync-reports put $TESTS_METADATA_S3_BUCKET $KNAPSACK_RSPEC_SUITE_REPORT_PATH'
- rm -f knapsack/${CI_PROJECT_NAME}/*_node_*.json
- scripts/merge-reports ${FLAKY_RSPEC_SUITE_REPORT_PATH} rspec_flaky/all_*_*.json - scripts/merge-reports ${FLAKY_RSPEC_SUITE_REPORT_PATH} rspec_flaky/all_*_*.json
- FLAKY_RSPEC_GENERATE_REPORT=1 scripts/prune-old-flaky-specs ${FLAKY_RSPEC_SUITE_REPORT_PATH} - FLAKY_RSPEC_GENERATE_REPORT=1 scripts/prune-old-flaky-specs ${FLAKY_RSPEC_SUITE_REPORT_PATH}
- '[[ -z ${TESTS_METADATA_S3_BUCKET} ]] || scripts/sync-reports put $TESTS_METADATA_S3_BUCKET $KNAPSACK_RSPEC_SUITE_REPORT_PATH'
- '[[ -z ${TESTS_METADATA_S3_BUCKET} ]] || scripts/sync-reports put $TESTS_METADATA_S3_BUCKET $FLAKY_RSPEC_SUITE_REPORT_PATH' - '[[ -z ${TESTS_METADATA_S3_BUCKET} ]] || scripts/sync-reports put $TESTS_METADATA_S3_BUCKET $FLAKY_RSPEC_SUITE_REPORT_PATH'
- rm -f knapsack/${CI_PROJECT_NAME}/*_node_*.json
- rm -f rspec_flaky/all_*.json rspec_flaky/new_*.json - rm -f rspec_flaky/all_*.json rspec_flaky/new_*.json
- scripts/insert-rspec-profiling-data - scripts/insert-rspec-profiling-data
only: only:
......
...@@ -361,7 +361,7 @@ group :development, :test do ...@@ -361,7 +361,7 @@ group :development, :test do
gem 'scss_lint', '~> 0.56.0', require: false gem 'scss_lint', '~> 0.56.0', require: false
gem 'haml_lint', '~> 0.31.0', require: false gem 'haml_lint', '~> 0.31.0', require: false
gem 'simplecov', '~> 0.14.0', require: false gem 'simplecov', '~> 0.16.1', require: false
gem 'bundler-audit', '~> 0.5.0', require: false gem 'bundler-audit', '~> 0.5.0', require: false
gem 'benchmark-ips', '~> 2.3.0', require: false gem 'benchmark-ips', '~> 2.3.0', require: false
...@@ -372,6 +372,7 @@ group :development, :test do ...@@ -372,6 +372,7 @@ group :development, :test do
gem 'activerecord_sane_schema_dumper', '1.0' gem 'activerecord_sane_schema_dumper', '1.0'
gem 'stackprof', '~> 0.2.10', require: false gem 'stackprof', '~> 0.2.10', require: false
gem 'derailed_benchmarks', require: false
gem 'simple_po_parser', '~> 1.1.2', require: false gem 'simple_po_parser', '~> 1.1.2', require: false
...@@ -399,6 +400,8 @@ gem 'html2text' ...@@ -399,6 +400,8 @@ gem 'html2text'
gem 'ruby-prof', '~> 0.17.0' gem 'ruby-prof', '~> 0.17.0'
gem 'rbtrace', '~> 0.4', require: false gem 'rbtrace', '~> 0.4', require: false
gem 'memory_profiler', '~> 0.9', require: false
gem 'benchmark-memory', '~> 0.1', require: false
# OAuth # OAuth
gem 'oauth2', '~> 1.4' gem 'oauth2', '~> 1.4'
......
...@@ -82,6 +82,8 @@ GEM ...@@ -82,6 +82,8 @@ GEM
bcrypt (3.1.12) bcrypt (3.1.12)
bcrypt_pbkdf (1.0.0) bcrypt_pbkdf (1.0.0)
benchmark-ips (2.3.0) benchmark-ips (2.3.0)
benchmark-memory (0.1.2)
memory_profiler (~> 0.9)
better_errors (2.5.0) better_errors (2.5.0)
coderay (>= 1.0.0) coderay (>= 1.0.0)
erubi (>= 1.0.0) erubi (>= 1.0.0)
...@@ -155,6 +157,14 @@ GEM ...@@ -155,6 +157,14 @@ GEM
html-pipeline html-pipeline
declarative (0.0.10) declarative (0.0.10)
declarative-option (0.1.0) declarative-option (0.1.0)
derailed_benchmarks (1.3.5)
benchmark-ips (~> 2)
get_process_mem (~> 0)
heapy (~> 0)
memory_profiler (~> 0)
rack (>= 1)
rake (> 10, < 13)
thor (~> 0.19)
descendants_tracker (0.0.4) descendants_tracker (0.0.4)
thread_safe (~> 0.3, >= 0.3.1) thread_safe (~> 0.3, >= 0.3.1)
device_detector (1.0.0) device_detector (1.0.0)
...@@ -174,7 +184,7 @@ GEM ...@@ -174,7 +184,7 @@ GEM
diffy (3.1.0) diffy (3.1.0)
discordrb-webhooks-blackst0ne (3.3.0) discordrb-webhooks-blackst0ne (3.3.0)
rest-client (~> 2.0) rest-client (~> 2.0)
docile (1.1.5) docile (1.3.1)
domain_name (0.5.20180417) domain_name (0.5.20180417)
unf (>= 0.0.5, < 1.0.0) unf (>= 0.0.5, < 1.0.0)
doorkeeper (4.3.2) doorkeeper (4.3.2)
...@@ -377,6 +387,7 @@ GEM ...@@ -377,6 +387,7 @@ GEM
hashie (>= 3.0) hashie (>= 3.0)
health_check (2.6.0) health_check (2.6.0)
rails (>= 4.0) rails (>= 4.0)
heapy (0.1.4)
hipchat (1.5.2) hipchat (1.5.2)
httparty httparty
mimemagic mimemagic
...@@ -478,6 +489,7 @@ GEM ...@@ -478,6 +489,7 @@ GEM
memoist (0.16.0) memoist (0.16.0)
memoizable (0.4.2) memoizable (0.4.2)
thread_safe (~> 0.3, >= 0.3.1) thread_safe (~> 0.3, >= 0.3.1)
memory_profiler (0.9.13)
method_source (0.9.2) method_source (0.9.2)
mime-types (3.2.2) mime-types (3.2.2)
mime-types-data (~> 3.2015) mime-types-data (~> 3.2015)
...@@ -873,11 +885,11 @@ GEM ...@@ -873,11 +885,11 @@ GEM
jwt (>= 1.5, < 3.0) jwt (>= 1.5, < 3.0)
multi_json (~> 1.10) multi_json (~> 1.10)
simple_po_parser (1.1.2) simple_po_parser (1.1.2)
simplecov (0.14.1) simplecov (0.16.1)
docile (~> 1.1.0) docile (~> 1.1)
json (>= 1.8, < 3) json (>= 1.8, < 3)
simplecov-html (~> 0.10.0) simplecov-html (~> 0.10.0)
simplecov-html (0.10.0) simplecov-html (0.10.2)
slack-notifier (1.5.1) slack-notifier (1.5.1)
spring (2.0.2) spring (2.0.2)
activesupport (>= 4.2) activesupport (>= 4.2)
...@@ -1014,6 +1026,7 @@ DEPENDENCIES ...@@ -1014,6 +1026,7 @@ DEPENDENCIES
batch-loader (~> 1.4.0) batch-loader (~> 1.4.0)
bcrypt_pbkdf (~> 1.0) bcrypt_pbkdf (~> 1.0)
benchmark-ips (~> 2.3.0) benchmark-ips (~> 2.3.0)
benchmark-memory (~> 0.1)
better_errors (~> 2.5.0) better_errors (~> 2.5.0)
binding_of_caller (~> 0.8.0) binding_of_caller (~> 0.8.0)
bootsnap (~> 1.4) bootsnap (~> 1.4)
...@@ -1034,6 +1047,7 @@ DEPENDENCIES ...@@ -1034,6 +1047,7 @@ DEPENDENCIES
creole (~> 0.5.0) creole (~> 0.5.0)
database_cleaner (~> 1.7.0) database_cleaner (~> 1.7.0)
deckar01-task_list (= 2.2.0) deckar01-task_list (= 2.2.0)
derailed_benchmarks
device_detector device_detector
devise (~> 4.6) devise (~> 4.6)
devise-two-factor (~> 3.0.0) devise-two-factor (~> 3.0.0)
...@@ -1111,6 +1125,7 @@ DEPENDENCIES ...@@ -1111,6 +1125,7 @@ DEPENDENCIES
lograge (~> 0.5) lograge (~> 0.5)
loofah (~> 2.2) loofah (~> 2.2)
mail_room (~> 0.9.1) mail_room (~> 0.9.1)
memory_profiler (~> 0.9)
method_source (~> 0.8) method_source (~> 0.8)
mimemagic (~> 0.3.2) mimemagic (~> 0.3.2)
mini_magick mini_magick
...@@ -1203,7 +1218,7 @@ DEPENDENCIES ...@@ -1203,7 +1218,7 @@ DEPENDENCIES
sidekiq (~> 5.2.7) sidekiq (~> 5.2.7)
sidekiq-cron (~> 1.0) sidekiq-cron (~> 1.0)
simple_po_parser (~> 1.1.2) simple_po_parser (~> 1.1.2)
simplecov (~> 0.14.0) simplecov (~> 0.16.1)
slack-notifier (~> 1.5.1) slack-notifier (~> 1.5.1)
spring (~> 2.0.0) spring (~> 2.0.0)
spring-commands-rspec (~> 1.0.4) spring-commands-rspec (~> 1.0.4)
......
import { sprintf, __ } from '~/locale';
export default { export default {
computed: { computed: {
resolveButtonTitle() { resolveButtonTitle() {
let title = 'Mark comment as resolved'; let title = __('Mark comment as resolved');
if (this.resolvedBy) { if (this.resolvedBy) {
title = `Resolved by ${this.resolvedBy.name}`; title = sprintf(__('Resolved by %{name}'), { name: this.resolvedBy.name });
} }
return title; return title;
......
import { __ } from '~/locale';
const notImplemented = () => { const notImplemented = () => {
throw new Error('Not implemented!'); throw new Error(__('Not implemented!'));
}; };
export default { export default {
......
import * as mutationTypes from './mutation_types'; import * as mutationTypes from './mutation_types';
import { __ } from '~/locale';
const notImplemented = () => { const notImplemented = () => {
throw new Error('Not implemented!'); throw new Error(__('Not implemented!'));
}; };
export default { export default {
......
import Visibility from 'visibilityjs'; import Visibility from 'visibilityjs';
import Vue from 'vue'; import Vue from 'vue';
import AccessorUtilities from '~/lib/utils/accessor';
import { GlToast } from '@gitlab/ui'; import { GlToast } from '@gitlab/ui';
import PersistentUserCallout from '../persistent_user_callout'; import PersistentUserCallout from '../persistent_user_callout';
import { s__, sprintf } from '../locale'; import { s__, sprintf } from '../locale';
...@@ -43,8 +44,10 @@ export default class Clusters { ...@@ -43,8 +44,10 @@ export default class Clusters {
helpPath, helpPath,
ingressHelpPath, ingressHelpPath,
ingressDnsHelpPath, ingressDnsHelpPath,
clusterId,
} = document.querySelector('.js-edit-cluster-form').dataset; } = document.querySelector('.js-edit-cluster-form').dataset;
this.clusterId = clusterId;
this.store = new ClustersStore(); this.store = new ClustersStore();
this.store.setHelpPaths(helpPath, ingressHelpPath, ingressDnsHelpPath); this.store.setHelpPaths(helpPath, ingressHelpPath, ingressDnsHelpPath);
this.store.setManagePrometheusPath(managePrometheusPath); this.store.setManagePrometheusPath(managePrometheusPath);
...@@ -69,6 +72,10 @@ export default class Clusters { ...@@ -69,6 +72,10 @@ export default class Clusters {
this.errorContainer = document.querySelector('.js-cluster-error'); this.errorContainer = document.querySelector('.js-cluster-error');
this.successContainer = document.querySelector('.js-cluster-success'); this.successContainer = document.querySelector('.js-cluster-success');
this.creatingContainer = document.querySelector('.js-cluster-creating'); this.creatingContainer = document.querySelector('.js-cluster-creating');
this.unreachableContainer = document.querySelector('.js-cluster-api-unreachable');
this.authenticationFailureContainer = document.querySelector(
'.js-cluster-authentication-failure',
);
this.errorReasonContainer = this.errorContainer.querySelector('.js-error-reason'); this.errorReasonContainer = this.errorContainer.querySelector('.js-error-reason');
this.successApplicationContainer = document.querySelector('.js-cluster-application-notice'); this.successApplicationContainer = document.querySelector('.js-cluster-application-notice');
this.showTokenButton = document.querySelector('.js-show-cluster-token'); this.showTokenButton = document.querySelector('.js-show-cluster-token');
...@@ -125,6 +132,13 @@ export default class Clusters { ...@@ -125,6 +132,13 @@ export default class Clusters {
PersistentUserCallout.factory(callout); PersistentUserCallout.factory(callout);
} }
addBannerCloseHandler(el, status) {
el.querySelector('.js-close-banner').addEventListener('click', () => {
el.classList.add('hidden');
this.setBannerDismissedState(status, true);
});
}
addListeners() { addListeners() {
if (this.showTokenButton) this.showTokenButton.addEventListener('click', this.showToken); if (this.showTokenButton) this.showTokenButton.addEventListener('click', this.showToken);
eventHub.$on('installApplication', this.installApplication); eventHub.$on('installApplication', this.installApplication);
...@@ -133,6 +147,9 @@ export default class Clusters { ...@@ -133,6 +147,9 @@ export default class Clusters {
eventHub.$on('saveKnativeDomain', data => this.saveKnativeDomain(data)); eventHub.$on('saveKnativeDomain', data => this.saveKnativeDomain(data));
eventHub.$on('setKnativeHostname', data => this.setKnativeHostname(data)); eventHub.$on('setKnativeHostname', data => this.setKnativeHostname(data));
eventHub.$on('uninstallApplication', data => this.uninstallApplication(data)); eventHub.$on('uninstallApplication', data => this.uninstallApplication(data));
// Add event listener to all the banner close buttons
this.addBannerCloseHandler(this.unreachableContainer, 'unreachable');
this.addBannerCloseHandler(this.authenticationFailureContainer, 'authentication_failure');
} }
removeListeners() { removeListeners() {
...@@ -205,6 +222,8 @@ export default class Clusters { ...@@ -205,6 +222,8 @@ export default class Clusters {
this.errorContainer.classList.add('hidden'); this.errorContainer.classList.add('hidden');
this.successContainer.classList.add('hidden'); this.successContainer.classList.add('hidden');
this.creatingContainer.classList.add('hidden'); this.creatingContainer.classList.add('hidden');
this.unreachableContainer.classList.add('hidden');
this.authenticationFailureContainer.classList.add('hidden');
} }
checkForNewInstalls(prevApplicationMap, newApplicationMap) { checkForNewInstalls(prevApplicationMap, newApplicationMap) {
...@@ -228,9 +247,32 @@ export default class Clusters { ...@@ -228,9 +247,32 @@ export default class Clusters {
} }
} }
setBannerDismissedState(status, isDismissed) {
if (AccessorUtilities.isLocalStorageAccessSafe()) {
window.localStorage.setItem(
`cluster_${this.clusterId}_banner_dismissed`,
`${status}_${isDismissed}`,
);
}
}
isBannerDismissed(status) {
let bannerState;
if (AccessorUtilities.isLocalStorageAccessSafe()) {
bannerState = window.localStorage.getItem(`cluster_${this.clusterId}_banner_dismissed`);
}
return bannerState === `${status}_true`;
}
updateContainer(prevStatus, status, error) { updateContainer(prevStatus, status, error) {
this.hideAll(); this.hideAll();
if (this.isBannerDismissed(status)) {
return;
}
this.setBannerDismissedState(status, false);
// We poll all the time but only want the `created` banner to show when newly created // We poll all the time but only want the `created` banner to show when newly created
if (this.store.state.status !== 'created' || prevStatus !== this.store.state.status) { if (this.store.state.status !== 'created' || prevStatus !== this.store.state.status) {
switch (status) { switch (status) {
...@@ -241,6 +283,12 @@ export default class Clusters { ...@@ -241,6 +283,12 @@ export default class Clusters {
this.errorContainer.classList.remove('hidden'); this.errorContainer.classList.remove('hidden');
this.errorReasonContainer.textContent = error; this.errorReasonContainer.textContent = error;
break; break;
case 'unreachable':
this.unreachableContainer.classList.remove('hidden');
break;
case 'authentication_failure':
this.authenticationFailureContainer.classList.remove('hidden');
break;
case 'scheduled': case 'scheduled':
case 'creating': case 'creating':
this.creatingContainer.classList.remove('hidden'); this.creatingContainer.classList.remove('hidden');
......
...@@ -6,6 +6,7 @@ import 'core-js/fn/array/from'; ...@@ -6,6 +6,7 @@ import 'core-js/fn/array/from';
import 'core-js/fn/array/includes'; import 'core-js/fn/array/includes';
import 'core-js/fn/object/assign'; import 'core-js/fn/object/assign';
import 'core-js/fn/object/values'; import 'core-js/fn/object/values';
import 'core-js/fn/object/entries';
import 'core-js/fn/promise'; import 'core-js/fn/promise';
import 'core-js/fn/promise/finally'; import 'core-js/fn/promise/finally';
import 'core-js/fn/string/code-point-at'; import 'core-js/fn/string/code-point-at';
......
import Vue from 'vue'; import Vue from 'vue';
import ErrorTrackingSettings from './components/app.vue'; import ErrorTrackingSettings from './components/app.vue';
import createStore from './store'; import createStore from './store';
import initSettingsPanels from '~/settings_panels';
export default () => { export default () => {
initSettingsPanels();
const formContainerEl = document.querySelector('.js-error-tracking-form'); const formContainerEl = document.querySelector('.js-error-tracking-form');
const { const {
dataset: { apiHost, enabled, project, token, listProjectsEndpoint, operationsSettingsEndpoint }, dataset: { apiHost, enabled, project, token, listProjectsEndpoint, operationsSettingsEndpoint },
......
...@@ -3,12 +3,12 @@ import { InMemoryCache } from 'apollo-cache-inmemory'; ...@@ -3,12 +3,12 @@ import { InMemoryCache } from 'apollo-cache-inmemory';
import { createUploadLink } from 'apollo-upload-client'; import { createUploadLink } from 'apollo-upload-client';
import csrf from '~/lib/utils/csrf'; import csrf from '~/lib/utils/csrf';
export default (resolvers = {}, baseUrl = '') => { export default (resolvers = {}, config = {}) => {
let uri = `${gon.relative_url_root}/api/graphql`; let uri = `${gon.relative_url_root}/api/graphql`;
if (baseUrl) { if (config.baseUrl) {
// Prepend baseUrl and ensure that `///` are replaced with `/` // Prepend baseUrl and ensure that `///` are replaced with `/`
uri = `${baseUrl}${uri}`.replace(/\/{3,}/g, '/'); uri = `${config.baseUrl}${uri}`.replace(/\/{3,}/g, '/');
} }
return new ApolloClient({ return new ApolloClient({
...@@ -18,7 +18,7 @@ export default (resolvers = {}, baseUrl = '') => { ...@@ -18,7 +18,7 @@ export default (resolvers = {}, baseUrl = '') => {
[csrf.headerKey]: csrf.token, [csrf.headerKey]: csrf.token,
}, },
}), }),
cache: new InMemoryCache(), cache: new InMemoryCache(config.cacheConfig),
resolvers, resolvers,
}); });
}; };
<script> <script>
import { GlLoadingIcon } from '@gitlab/ui'; import { GlLoadingIcon } from '@gitlab/ui';
import createFlash from '~/flash';
import { sprintf, __ } from '../../../locale'; import { sprintf, __ } from '../../../locale';
import getRefMixin from '../../mixins/get_ref'; import getRefMixin from '../../mixins/get_ref';
import getFiles from '../../queries/getFiles.graphql'; import getFiles from '../../queries/getFiles.graphql';
import getProjectPath from '../../queries/getProjectPath.graphql';
import TableHeader from './header.vue'; import TableHeader from './header.vue';
import TableRow from './row.vue'; import TableRow from './row.vue';
import ParentRow from './parent_row.vue';
const PAGE_SIZE = 100;
export default { export default {
components: { components: {
GlLoadingIcon, GlLoadingIcon,
TableHeader, TableHeader,
TableRow, TableRow,
ParentRow,
}, },
mixins: [getRefMixin], mixins: [getRefMixin],
apollo: { apollo: {
files: { projectPath: {
query: getFiles, query: getProjectPath,
variables() {
return {
ref: this.ref,
path: this.path,
};
},
}, },
}, },
props: { props: {
...@@ -32,7 +32,14 @@ export default { ...@@ -32,7 +32,14 @@ export default {
}, },
data() { data() {
return { return {
files: [], projectPath: '',
nextPageCursor: '',
entries: {
trees: [],
submodules: [],
blobs: [],
},
isLoadingFiles: false,
}; };
}, },
computed: { computed: {
...@@ -42,8 +49,66 @@ export default { ...@@ -42,8 +49,66 @@ export default {
{ path: this.path, ref: this.ref }, { path: this.path, ref: this.ref },
); );
}, },
isLoadingFiles() { showParentRow() {
return this.$apollo.queries.files.loading; return !this.isLoadingFiles && this.path !== '';
},
},
watch: {
$route: function routeChange() {
this.entries.trees = [];
this.entries.submodules = [];
this.entries.blobs = [];
this.nextPageCursor = '';
this.fetchFiles();
},
},
mounted() {
// We need to wait for `ref` and `projectPath` to be set
this.$nextTick(() => this.fetchFiles());
},
methods: {
fetchFiles() {
this.isLoadingFiles = true;
return this.$apollo
.query({
query: getFiles,
variables: {
projectPath: this.projectPath,
ref: this.ref,
path: this.path,
nextPageCursor: this.nextPageCursor,
pageSize: PAGE_SIZE,
},
})
.then(({ data }) => {
if (!data) return;
const pageInfo = this.hasNextPage(data.project.repository.tree);
this.isLoadingFiles = false;
this.entries = Object.keys(this.entries).reduce(
(acc, key) => ({
...acc,
[key]: this.normalizeData(key, data.project.repository.tree[key].edges),
}),
{},
);
if (pageInfo && pageInfo.hasNextPage) {
this.nextPageCursor = pageInfo.endCursor;
this.fetchFiles();
}
})
.catch(() => createFlash(__('An error occurding while fetching folder content.')));
},
normalizeData(key, data) {
return this.entries[key].concat(data.map(({ node }) => node));
},
hasNextPage(data) {
return []
.concat(data.trees.pageInfo, data.submodules.pageInfo, data.blobs.pageInfo)
.find(({ hasNextPage }) => hasNextPage);
}, },
}, },
}; };
...@@ -58,18 +123,22 @@ export default { ...@@ -58,18 +123,22 @@ export default {
tableCaption tableCaption
}} }}
</caption> </caption>
<table-header /> <table-header v-once />
<tbody> <tbody>
<table-row <parent-row v-show="showParentRow" :commit-ref="ref" :path="path" />
v-for="entry in files" <template v-for="val in entries">
:id="entry.id" <table-row
:key="entry.id" v-for="entry in val"
:path="entry.flatPath" :id="entry.id"
:type="entry.type" :key="`${entry.flatPath}-${entry.id}`"
/> :current-path="path"
:path="entry.flatPath"
:type="entry.type"
/>
</template>
</tbody> </tbody>
</table> </table>
<gl-loading-icon v-if="isLoadingFiles" class="my-3" size="md" /> <gl-loading-icon v-show="isLoadingFiles" class="my-3" size="md" />
</div> </div>
</div> </div>
</template> </template>
<script>
export default {
props: {
commitRef: {
type: String,
required: true,
},
path: {
type: String,
required: true,
},
},
computed: {
parentRoute() {
const splitArray = this.path.split('/');
splitArray.pop();
return { path: `/tree/${this.commitRef}/${splitArray.join('/')}` };
},
},
methods: {
clickRow() {
this.$router.push(this.parentRoute);
},
},
};
</script>
<template>
<tr v-once @click="clickRow">
<td colspan="3" class="tree-item-file-name">
<router-link :to="parentRoute" :aria-label="__('Go to parent')">
..
</router-link>
</td>
</tr>
</template>
...@@ -6,7 +6,11 @@ export default { ...@@ -6,7 +6,11 @@ export default {
mixins: [getRefMixin], mixins: [getRefMixin],
props: { props: {
id: { id: {
type: Number, type: String,
required: true,
},
currentPath: {
type: String,
required: true, required: true,
}, },
path: { path: {
...@@ -26,7 +30,7 @@ export default { ...@@ -26,7 +30,7 @@ export default {
return `fa-${getIconName(this.type, this.path)}`; return `fa-${getIconName(this.type, this.path)}`;
}, },
isFolder() { isFolder() {
return this.type === 'folder'; return this.type === 'tree';
}, },
isSubmodule() { isSubmodule() {
return this.type === 'commit'; return this.type === 'commit';
...@@ -34,6 +38,12 @@ export default { ...@@ -34,6 +38,12 @@ export default {
linkComponent() { linkComponent() {
return this.isFolder ? 'router-link' : 'a'; return this.isFolder ? 'router-link' : 'a';
}, },
fullPath() {
return this.path.replace(new RegExp(`^${this.currentPath}/`), '');
},
shortSha() {
return this.id.slice(0, 8);
},
}, },
methods: { methods: {
openRow() { openRow() {
...@@ -49,9 +59,11 @@ export default { ...@@ -49,9 +59,11 @@ export default {
<tr v-once :class="`file_${id}`" class="tree-item" @click="openRow"> <tr v-once :class="`file_${id}`" class="tree-item" @click="openRow">
<td class="tree-item-file-name"> <td class="tree-item-file-name">
<i :aria-label="type" role="img" :class="iconName" class="fa fa-fw"></i> <i :aria-label="type" role="img" :class="iconName" class="fa fa-fw"></i>
<component :is="linkComponent" :to="routerLinkTo" class="str-truncated">{{ path }}</component> <component :is="linkComponent" :to="routerLinkTo" class="str-truncated">
{{ fullPath }}
</component>
<template v-if="isSubmodule"> <template v-if="isSubmodule">
@ <a href="#" class="commit-sha">{{ id }}</a> @ <a href="#" class="commit-sha">{{ shortSha }}</a>
</template> </template>
</td> </td>
<td class="d-none d-sm-table-cell tree-commit"></td> <td class="d-none d-sm-table-cell tree-commit"></td>
......
{"__schema":{"types":[{"kind":"INTERFACE","name":"Entry","possibleTypes":[{"name":"Blob"},{"name":"Submodule"},{"name":"TreeEntry"}]}]}}
import Vue from 'vue'; import Vue from 'vue';
import VueApollo from 'vue-apollo'; import VueApollo from 'vue-apollo';
import { IntrospectionFragmentMatcher } from 'apollo-cache-inmemory';
import createDefaultClient from '~/lib/graphql'; import createDefaultClient from '~/lib/graphql';
import introspectionQueryResultData from './fragmentTypes.json';
Vue.use(VueApollo); Vue.use(VueApollo);
const defaultClient = createDefaultClient({ // We create a fragment matcher so that we can create a fragment from an interface
Query: { // Without this, Apollo throws a heuristic fragment matcher warning
files() { const fragmentMatcher = new IntrospectionFragmentMatcher({
return [ introspectionQueryResultData,
{ });
__typename: 'file',
id: 1, const defaultClient = createDefaultClient(
name: 'app', {},
flatPath: 'app', {
type: 'folder', cacheConfig: {
}, fragmentMatcher,
{ dataIdFromObject: obj => {
__typename: 'file', // eslint-disable-next-line no-underscore-dangle
id: 2, switch (obj.__typename) {
name: 'gitlab-svg', // We need to create a dynamic ID for each entry
flatPath: 'gitlab-svg', // Each entry can have the same ID as the ID is a commit ID
type: 'commit', // So we create a unique cache ID with the path and the ID
}, case 'TreeEntry':
{ case 'Submodule':
__typename: 'file', case 'Blob':
id: 3, return `${obj.flatPath}-${obj.id}`;
name: 'index.js', default:
flatPath: 'index.js', // If the type doesn't match any of the above we fallback
type: 'blob', // to using the default Apollo ID
}, // eslint-disable-next-line no-underscore-dangle
{ return obj.id || obj._id;
__typename: 'file', }
id: 4, },
name: 'test.pdf',
flatPath: 'fixtures/test.pdf',
type: 'blob',
},
];
}, },
}, },
}); );
export default new VueApollo({ export default new VueApollo({
defaultClient, defaultClient,
......
query getFiles($path: String!, $ref: String!) { fragment TreeEntry on Entry {
files(path: $path, ref: $ref) @client { id
id flatPath
flatPath type
type }
fragment PageInfo on PageInfo {
hasNextPage
endCursor
}
query getFiles(
$projectPath: ID!
$path: String
$ref: String!
$pageSize: Int!
$nextPageCursor: String
) {
project(fullPath: $projectPath) {
repository {
tree(path: $path, ref: $ref) {
trees(first: $pageSize, after: $nextPageCursor) {
edges {
node {
...TreeEntry
}
}
pageInfo {
...PageInfo
}
}
submodules(first: $pageSize, after: $nextPageCursor) {
edges {
node {
...TreeEntry
}
}
pageInfo {
...PageInfo
}
}
blobs(first: $pageSize, after: $nextPageCursor) {
edges {
node {
...TreeEntry
}
}
pageInfo {
...PageInfo
}
}
}
}
} }
} }
...@@ -11,17 +11,12 @@ export default function createRouter(base, baseRef) { ...@@ -11,17 +11,12 @@ export default function createRouter(base, baseRef) {
mode: 'history', mode: 'history',
base: joinPaths(gon.relative_url_root || '', base), base: joinPaths(gon.relative_url_root || '', base),
routes: [ routes: [
{
path: '/',
name: 'projectRoot',
component: IndexPage,
},
{ {
path: `/tree/${baseRef}(/.*)?`, path: `/tree/${baseRef}(/.*)?`,
name: 'treePath', name: 'treePath',
component: TreePage, component: TreePage,
props: route => ({ props: route => ({
path: route.params.pathMatch, path: route.params.pathMatch.replace(/^\//, ''),
}), }),
beforeEnter(to, from, next) { beforeEnter(to, from, next) {
document document
...@@ -31,6 +26,11 @@ export default function createRouter(base, baseRef) { ...@@ -31,6 +26,11 @@ export default function createRouter(base, baseRef) {
next(); next();
}, },
}, },
{
path: '/',
name: 'projectRoot',
component: IndexPage,
},
], ],
}); });
} }
const entryTypeIcons = { const entryTypeIcons = {
folder: 'folder', tree: 'folder',
commit: 'archive', commit: 'archive',
}; };
......
...@@ -2,6 +2,7 @@ import $ from 'jquery'; ...@@ -2,6 +2,7 @@ import $ from 'jquery';
import axios from './lib/utils/axios_utils'; import axios from './lib/utils/axios_utils';
import Flash, { hideFlash } from './flash'; import Flash, { hideFlash } from './flash';
import { parseBoolean } from './lib/utils/common_utils'; import { parseBoolean } from './lib/utils/common_utils';
import { __ } from './locale';
export default () => { export default () => {
$('body').on('click', '.js-usage-consent-action', e => { $('body').on('click', '.js-usage-consent-action', e => {
...@@ -25,7 +26,7 @@ export default () => { ...@@ -25,7 +26,7 @@ export default () => {
}) })
.catch(() => { .catch(() => {
hideConsentMessage(); hideConsentMessage();
Flash('Something went wrong. Try again later.'); Flash(__('Something went wrong. Try again later.'));
}); });
}); });
}; };
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
import $ from 'jquery'; import $ from 'jquery';
import _ from 'underscore'; import _ from 'underscore';
import axios from './lib/utils/axios_utils'; import axios from './lib/utils/axios_utils';
import { __ } from './locale'; import { s__, __, sprintf } from './locale';
import ModalStore from './boards/stores/modal_store'; import ModalStore from './boards/stores/modal_store';
// TODO: remove eventHub hack after code splitting refactor // TODO: remove eventHub hack after code splitting refactor
...@@ -157,14 +157,20 @@ function UsersSelect(currentUser, els, options = {}) { ...@@ -157,14 +157,20 @@ function UsersSelect(currentUser, els, options = {}) {
.get(0); .get(0);
if (selectedUsers.length === 0) { if (selectedUsers.length === 0) {
return 'Unassigned'; return s__('UsersSelect|Unassigned');
} else if (selectedUsers.length === 1) { } else if (selectedUsers.length === 1) {
return firstUser.name; return firstUser.name;
} else if (isSelected) { } else if (isSelected) {
const otherSelected = selectedUsers.filter(s => s !== selectedUser.id); const otherSelected = selectedUsers.filter(s => s !== selectedUser.id);
return `${selectedUser.name} + ${otherSelected.length} more`; return sprintf(s__('UsersSelect|%{name} + %{length} more'), {
name: selectedUser.name,
length: otherSelected.length,
});
} else { } else {
return `${firstUser.name} + ${selectedUsers.length - 1} more`; return sprintf(s__('UsersSelect|%{name} + %{length} more'), {
name: firstUser.name,
length: selectedUsers.length - 1,
});
} }
}; };
...@@ -218,11 +224,11 @@ function UsersSelect(currentUser, els, options = {}) { ...@@ -218,11 +224,11 @@ function UsersSelect(currentUser, els, options = {}) {
tooltipTitle = _.escape(user.name); tooltipTitle = _.escape(user.name);
} else { } else {
user = { user = {
name: 'Unassigned', name: s__('UsersSelect|Unassigned'),
username: '', username: '',
avatar: '', avatar: '',
}; };
tooltipTitle = __('Assignee'); tooltipTitle = s__('UsersSelect|Assignee');
} }
$value.html(assigneeTemplate(user)); $value.html(assigneeTemplate(user));
$collapsedSidebar.attr('title', tooltipTitle).tooltip('_fixTitle'); $collapsedSidebar.attr('title', tooltipTitle).tooltip('_fixTitle');
...@@ -233,7 +239,11 @@ function UsersSelect(currentUser, els, options = {}) { ...@@ -233,7 +239,11 @@ function UsersSelect(currentUser, els, options = {}) {
'<% if( avatar ) { %> <a class="author-link" href="/<%- username %>"> <img width="24" class="avatar avatar-inline s24" alt="" src="<%- avatar %>"> </a> <% } else { %> <i class="fa fa-user"></i> <% } %>', '<% if( avatar ) { %> <a class="author-link" href="/<%- username %>"> <img width="24" class="avatar avatar-inline s24" alt="" src="<%- avatar %>"> </a> <% } else { %> <i class="fa fa-user"></i> <% } %>',
); );
assigneeTemplate = _.template( assigneeTemplate = _.template(
'<% if (username) { %> <a class="author-link bold" href="/<%- username %>"> <% if( avatar ) { %> <img width="32" class="avatar avatar-inline s32" alt="" src="<%- avatar %>"> <% } %> <span class="author"><%- name %></span> <span class="username"> @<%- username %> </span> </a> <% } else { %> <span class="no-value assign-yourself"> No assignee - <a href="#" class="js-assign-yourself"> assign yourself </a> </span> <% } %>', `<% if (username) { %> <a class="author-link bold" href="/<%- username %>"> <% if( avatar ) { %> <img width="32" class="avatar avatar-inline s32" alt="" src="<%- avatar %>"> <% } %> <span class="author"><%- name %></span> <span class="username"> @<%- username %> </span> </a> <% } else { %> <span class="no-value assign-yourself">
${sprintf(s__('UsersSelect|No assignee - %{openingTag} assign yourself %{closingTag}'), {
openingTag: '<a href="#" class="js-assign-yourself">',
closingTag: '</a>',
})}</span> <% } %>`,
); );
return $dropdown.glDropdown({ return $dropdown.glDropdown({
showMenuAbove: showMenuAbove, showMenuAbove: showMenuAbove,
...@@ -302,7 +312,7 @@ function UsersSelect(currentUser, els, options = {}) { ...@@ -302,7 +312,7 @@ function UsersSelect(currentUser, els, options = {}) {
showDivider += 1; showDivider += 1;
users.unshift({ users.unshift({
beforeDivider: true, beforeDivider: true,
name: 'Unassigned', name: s__('UsersSelect|Unassigned'),
id: 0, id: 0,
}); });
} }
...@@ -310,7 +320,7 @@ function UsersSelect(currentUser, els, options = {}) { ...@@ -310,7 +320,7 @@ function UsersSelect(currentUser, els, options = {}) {
showDivider += 1; showDivider += 1;
name = showAnyUser; name = showAnyUser;
if (name === true) { if (name === true) {
name = 'Any User'; name = s__('UsersSelect|Any User');
} }
anyUser = { anyUser = {
beforeDivider: true, beforeDivider: true,
...@@ -596,7 +606,7 @@ function UsersSelect(currentUser, els, options = {}) { ...@@ -596,7 +606,7 @@ function UsersSelect(currentUser, els, options = {}) {
showEmailUser = $(select).data('emailUser'); showEmailUser = $(select).data('emailUser');
firstUser = $(select).data('firstUser'); firstUser = $(select).data('firstUser');
return $(select).select2({ return $(select).select2({
placeholder: 'Search for a user', placeholder: __('Search for a user'),
multiple: $(select).hasClass('multiselect'), multiple: $(select).hasClass('multiselect'),
minimumInputLength: 0, minimumInputLength: 0,
query: function(query) { query: function(query) {
...@@ -621,7 +631,7 @@ function UsersSelect(currentUser, els, options = {}) { ...@@ -621,7 +631,7 @@ function UsersSelect(currentUser, els, options = {}) {
} }
if (showNullUser) { if (showNullUser) {
nullUser = { nullUser = {
name: 'Unassigned', name: s__('UsersSelect|Unassigned'),
id: 0, id: 0,
}; };
data.results.unshift(nullUser); data.results.unshift(nullUser);
...@@ -629,7 +639,7 @@ function UsersSelect(currentUser, els, options = {}) { ...@@ -629,7 +639,7 @@ function UsersSelect(currentUser, els, options = {}) {
if (showAnyUser) { if (showAnyUser) {
name = showAnyUser; name = showAnyUser;
if (name === true) { if (name === true) {
name = 'Any User'; name = s__('UsersSelect|Any User');
} }
anyUser = { anyUser = {
name: name, name: name,
...@@ -645,7 +655,7 @@ function UsersSelect(currentUser, els, options = {}) { ...@@ -645,7 +655,7 @@ function UsersSelect(currentUser, els, options = {}) {
) { ) {
var trimmed = query.term.trim(); var trimmed = query.term.trim();
emailUser = { emailUser = {
name: 'Invite "' + trimmed + '" by email', name: sprintf(__('Invite "%{trimmed}" by email'), { trimmed }),
username: trimmed, username: trimmed,
id: trimmed, id: trimmed,
invite: true, invite: true,
...@@ -688,7 +698,7 @@ UsersSelect.prototype.initSelection = function(element, callback) { ...@@ -688,7 +698,7 @@ UsersSelect.prototype.initSelection = function(element, callback) {
id = $(element).val(); id = $(element).val();
if (id === '0') { if (id === '0') {
nullUser = { nullUser = {
name: 'Unassigned', name: s__('UsersSelect|Unassigned'),
}; };
return callback(nullUser); return callback(nullUser);
} else if (id !== '') { } else if (id !== '') {
......
...@@ -49,7 +49,7 @@ export default { ...@@ -49,7 +49,7 @@ export default {
required: false, required: false,
default: () => ({ default: () => ({
sourceProjectId: '', sourceProjectId: '',
issueId: '', mergeRequestId: '',
appUrl: '', appUrl: '',
}), }),
}, },
......
...@@ -48,7 +48,7 @@ export default { ...@@ -48,7 +48,7 @@ export default {
visualReviewAppMeta() { visualReviewAppMeta() {
return { return {
appUrl: this.mr.appUrl, appUrl: this.mr.appUrl,
issueId: this.mr.iid, mergeRequestId: this.mr.iid,
sourceProjectId: this.mr.sourceProjectId, sourceProjectId: this.mr.sourceProjectId,
}; };
}, },
......
...@@ -14,7 +14,7 @@ export default { ...@@ -14,7 +14,7 @@ export default {
</script> </script>
<template> <template>
<p v-once class="mr-info-list mr-links source-branch-removal-status append-bottom-0"> <p v-once class="mr-info-list mr-links append-bottom-0">
<span class="status-text" v-html="removesBranchText"> </span> <span class="status-text" v-html="removesBranchText"> </span>
<i v-tooltip :title="tooltipTitle" :aria-label="tooltipTitle" class="fa fa-question-circle"> <i v-tooltip :title="tooltipTitle" :aria-label="tooltipTitle" class="fa fa-question-circle">
</i> </i>
......
...@@ -333,41 +333,45 @@ export default { ...@@ -333,41 +333,45 @@ export default {
<div class="mr-widget-section"> <div class="mr-widget-section">
<component :is="componentName" :mr="mr" :service="service" /> <component :is="componentName" :mr="mr" :service="service" />
<section v-if="shouldRenderCollaborationStatus" class="mr-info-list mr-links"> <div class="mr-widget-info">
{{ s__('mrWidget|Allows commits from members who can merge to the target branch') }} <section v-if="shouldRenderCollaborationStatus" class="mr-info-list mr-links">
</section> <p>
{{ s__('mrWidget|Allows commits from members who can merge to the target branch') }}
</p>
</section>
<mr-widget-related-links <mr-widget-related-links
v-if="shouldRenderRelatedLinks" v-if="shouldRenderRelatedLinks"
:state="mr.state" :state="mr.state"
:related-links="mr.relatedLinks" :related-links="mr.relatedLinks"
/> />
<mr-widget-alert-message <mr-widget-alert-message
v-if="showMergePipelineForkWarning" v-if="showMergePipelineForkWarning"
type="warning" type="warning"
:help-path="mr.mergeRequestPipelinesHelpPath" :help-path="mr.mergeRequestPipelinesHelpPath"
> >
{{ {{
s__( s__(
'mrWidget|Fork merge requests do not create merge request pipelines which validate a post merge result', 'mrWidget|Fork merge requests do not create merge request pipelines which validate a post merge result',
) )
}} }}
</mr-widget-alert-message> </mr-widget-alert-message>
<mr-widget-alert-message <mr-widget-alert-message
v-if="showTargetBranchAdvancedError" v-if="showTargetBranchAdvancedError"
type="danger" type="danger"
:help-path="mr.mergeRequestPipelinesHelpPath" :help-path="mr.mergeRequestPipelinesHelpPath"
> >
{{ {{
s__( s__(
'mrWidget|The target branch has advanced, which invalidates the merge request pipeline. Please update the source branch and retry merging', 'mrWidget|The target branch has advanced, which invalidates the merge request pipeline. Please update the source branch and retry merging',
) )
}} }}
</mr-widget-alert-message> </mr-widget-alert-message>
<source-branch-removal-status v-if="shouldRenderSourceBranchRemovalStatus" /> <source-branch-removal-status v-if="shouldRenderSourceBranchRemovalStatus" />
</div>
</div> </div>
<div v-if="shouldRenderMergeHelp" class="mr-widget-footer"><mr-widget-merge-help /></div> <div v-if="shouldRenderMergeHelp" class="mr-widget-footer"><mr-widget-merge-help /></div>
</div> </div>
......
...@@ -71,11 +71,15 @@ export default { ...@@ -71,11 +71,15 @@ export default {
</div> </div>
<div class="text-secondary"> <div class="text-secondary">
<div v-if="user.bio" class="js-bio d-flex mb-1"> <div v-if="user.bio" class="js-bio d-flex mb-1">
<icon name="profile" css-classes="category-icon" /> <icon name="profile" css-classes="category-icon flex-shrink-0" />
<span class="ml-1">{{ user.bio }}</span> <span class="ml-1">{{ user.bio }}</span>
</div> </div>
<div v-if="user.organization" class="js-organization d-flex mb-1"> <div v-if="user.organization" class="js-organization d-flex mb-1">
<icon v-show="!jobInfoIsLoading" name="work" css-classes="category-icon" /> <icon
v-show="!jobInfoIsLoading"
name="work"
css-classes="category-icon flex-shrink-0"
/>
<span class="ml-1">{{ user.organization }}</span> <span class="ml-1">{{ user.organization }}</span>
</div> </div>
<gl-skeleton-loading <gl-skeleton-loading
...@@ -88,7 +92,7 @@ export default { ...@@ -88,7 +92,7 @@ export default {
<icon <icon
v-show="!locationIsLoading && user.location" v-show="!locationIsLoading && user.location"
name="location" name="location"
css-classes="category-icon" css-classes="category-icon flex-shrink-0"
/> />
<span class="ml-1">{{ user.location }}</span> <span class="ml-1">{{ user.location }}</span>
<gl-skeleton-loading <gl-skeleton-loading
......
@mixin avatar-size($size, $margin-right) {
width: $size;
height: $size;
margin-right: $margin-right;
}
.avatar-circle { .avatar-circle {
float: left; float: left;
margin-right: 15px; margin-right: 15px;
border-radius: $avatar-radius; border-radius: $avatar-radius;
border: 1px solid $gray-normal; border: 1px solid $gray-normal;
&.s16 { @include avatar-size(16px, 6px); } &.s16 { @include avatar-size(16px, 8px); }
&.s18 { @include avatar-size(18px, 6px); } &.s18 { @include avatar-size(18px, 8px); }
&.s19 { @include avatar-size(19px, 6px); } &.s19 { @include avatar-size(19px, 8px); }
&.s20 { @include avatar-size(20px, 7px); } &.s20 { @include avatar-size(20px, 8px); }
&.s24 { @include avatar-size(24px, 8px); } &.s24 { @include avatar-size(24px, 8px); }
&.s26 { @include avatar-size(26px, 8px); } &.s26 { @include avatar-size(26px, 8px); }
&.s32 { @include avatar-size(32px, 10px); } &.s32 { @include avatar-size(32px, 8px); }
&.s36 { @include avatar-size(36px, 10px); } &.s36 { @include avatar-size(36px, 16px); }
&.s40 { @include avatar-size(40px, 10px); } &.s40 { @include avatar-size(40px, 16px); }
&.s46 { @include avatar-size(46px, 15px); } &.s46 { @include avatar-size(46px, 16px); }
&.s48 { @include avatar-size(48px, 10px); } &.s48 { @include avatar-size(48px, 16px); }
&.s60 { @include avatar-size(60px, 12px); } &.s60 { @include avatar-size(60px, 16px); }
&.s64 { @include avatar-size(64px, 14px); } &.s64 { @include avatar-size(64px, 16px); }
&.s70 { @include avatar-size(70px, 14px); } &.s70 { @include avatar-size(70px, 16px); }
&.s90 { @include avatar-size(90px, 15px); } &.s90 { @include avatar-size(90px, 16px); }
&.s100 { @include avatar-size(100px, 15px); } &.s96 { @include avatar-size(96px, 16px); }
&.s110 { @include avatar-size(110px, 15px); } &.s100 { @include avatar-size(100px, 16px); }
&.s140 { @include avatar-size(140px, 15px); } &.s110 { @include avatar-size(110px, 16px); }
&.s160 { @include avatar-size(160px, 20px); } &.s140 { @include avatar-size(140px, 16px); }
&.s160 { @include avatar-size(160px, 16px); }
} }
.avatar { .avatar {
...@@ -39,6 +34,7 @@ ...@@ -39,6 +34,7 @@
padding: 0; padding: 0;
background: $gray-lightest; background: $gray-lightest;
overflow: hidden; overflow: hidden;
border-color: rgba($black, $gl-avatar-border-opacity);
&.avatar-inline { &.avatar-inline {
float: none; float: none;
...@@ -64,41 +60,37 @@ ...@@ -64,41 +60,37 @@
&.avatar-placeholder { &.avatar-placeholder {
border: 0; border: 0;
} }
&:not([href]):hover {
border-color: darken($gray-normal, 10%);
}
} }
.identicon { .identicon {
text-align: center; text-align: center;
vertical-align: top; vertical-align: top;
color: $gl-gray-700; color: $gray-800;
background-color: $gray-darker; background-color: $gray-darker;
// Sizes // Sizes
&.s16 { font-size: 12px; &.s16 { font-size: 10px;
line-height: 1.33; } line-height: 16px; }
&.s24 { font-size: 13px; &.s24 { font-size: 12px;
line-height: 1.8; } line-height: 24px; }
&.s26 { font-size: 20px; &.s26 { font-size: 20px;
line-height: 1.33; } line-height: 1.33; }
&.s32 { font-size: 20px; &.s32 { font-size: 14px;
line-height: 30px; } line-height: 32px; }
&.s40 { font-size: 16px; &.s40 { font-size: 16px;
line-height: 38px; } line-height: 38px; }
&.s48 { font-size: 20px; &.s48 { font-size: 20px;
line-height: 46px; } line-height: 48px; }
&.s60 { font-size: 32px; &.s60 { font-size: 32px;
line-height: 58px; } line-height: 58px; }
&.s64 { font-size: 32px; &.s64 { font-size: 28px;
line-height: 64px; } line-height: 64px; }
&.s70 { font-size: 34px; &.s70 { font-size: 34px;
...@@ -107,6 +99,9 @@ ...@@ -107,6 +99,9 @@
&.s90 { font-size: 36px; &.s90 { font-size: 36px;
line-height: 88px; } line-height: 88px; }
&.s96 { font-size: 48px;
line-height: 96px; }
&.s100 { font-size: 36px; &.s100 { font-size: 36px;
line-height: 98px; } line-height: 98px; }
...@@ -144,7 +139,6 @@ ...@@ -144,7 +139,6 @@
.avatar { .avatar {
border-radius: 0; border-radius: 0;
border: 0;
height: auto; height: auto;
width: 100%; width: 100%;
margin: 0; margin: 0;
......
...@@ -8,7 +8,6 @@ ...@@ -8,7 +8,6 @@
@import 'framework/animations'; @import 'framework/animations';
@import 'framework/vue_transitions'; @import 'framework/vue_transitions';
@import 'framework/avatar';
@import 'framework/asciidoctor'; @import 'framework/asciidoctor';
@import 'framework/banner'; @import 'framework/banner';
@import 'framework/blocks'; @import 'framework/blocks';
......
...@@ -21,7 +21,7 @@ ...@@ -21,7 +21,7 @@
} }
@mixin btn-default { @mixin btn-default {
border-radius: 3px; border-radius: $border-radius-default;
font-size: $gl-font-size; font-size: $gl-font-size;
font-weight: $gl-font-weight-normal; font-weight: $gl-font-weight-normal;
padding: $gl-vert-padding $gl-btn-padding; padding: $gl-vert-padding $gl-btn-padding;
...@@ -37,7 +37,7 @@ ...@@ -37,7 +37,7 @@
@include btn-default; @include btn-default;
} }
@mixin btn-outline($background, $text, $border, $hover-background, $hover-text, $hover-border, $active-background, $active-border) { @mixin btn-outline($background, $text, $border, $hover-background, $hover-text, $hover-border, $active-background, $active-border, $active-text) {
background-color: $background; background-color: $background;
color: $text; color: $text;
border-color: $border; border-color: $border;
...@@ -61,13 +61,22 @@ ...@@ -61,13 +61,22 @@
} }
} }
&:focus {
box-shadow: 0 0 4px 1px $blue-300;
}
&:active { &:active {
background-color: $active-background; background-color: $active-background;
border-color: $active-border; border-color: $active-border;
color: $hover-text; box-shadow: inset 0 2px 4px 0 rgba($black, 0.2);
color: $active-text;
> .icon { > .icon {
color: $hover-text; color: $active-text;
}
&:focus {
box-shadow: inset 0 2px 4px 0 rgba($black, 0.2);
} }
} }
} }
...@@ -164,21 +173,21 @@ ...@@ -164,21 +173,21 @@
&.btn-inverted { &.btn-inverted {
&.btn-success { &.btn-success {
@include btn-outline($white-light, $green-600, $green-500, $green-500, $white-light, $green-600, $green-600, $green-700); @include btn-outline($white-light, $green-600, $green-500, $green-100, $green-700, $green-500, $green-200, $green-600, $green-800);
} }
&.btn-remove, &.btn-remove,
&.btn-danger { &.btn-danger {
@include btn-outline($white-light, $red-500, $red-500, $red-500, $white-light, $red-600, $red-600, $red-700); @include btn-outline($white-light, $red-500, $red-500, $red-100, $red-700, $red-500, $red-200, $red-600, $red-800);
} }
&.btn-warning { &.btn-warning {
@include btn-outline($white-light, $orange-500, $orange-500, $orange-500, $white-light, $orange-600, $orange-600, $orange-700); @include btn-outline($white-light, $orange-500, $orange-500, $orange-100, $orange-700, $orange-500, $orange-200, $orange-600, $orange-800);
} }
&.btn-primary, &.btn-primary,
&.btn-info { &.btn-info {
@include btn-outline($white-light, $blue-500, $blue-500, $blue-500, $white-light, $blue-600, $blue-600, $blue-700); @include btn-outline($white-light, $blue-500, $blue-500, $blue-100, $blue-700, $blue-500, $blue-200, $blue-600, $blue-800);
} }
} }
...@@ -193,11 +202,11 @@ ...@@ -193,11 +202,11 @@
&.btn-close, &.btn-close,
&.btn-close-color { &.btn-close-color {
@include btn-outline($white-light, $orange-600, $orange-500, $orange-500, $white-light, $orange-600, $orange-600, $orange-700); @include btn-outline($white-light, $orange-600, $orange-500, $orange-100, $orange-700, $orange-500, $orange-200, $orange-600, $orange-800);
} }
&.btn-spam { &.btn-spam {
@include btn-outline($white-light, $red-500, $red-500, $red-500, $white-light, $red-600, $red-600, $red-700); @include btn-outline($white-light, $red-500, $red-500, $red-100, $red-700, $red-500, $red-200, $red-600, $red-800);
} }
&.btn-danger, &.btn-danger,
...@@ -402,7 +411,7 @@ ...@@ -402,7 +411,7 @@
.btn-inverted { .btn-inverted {
&-secondary { &-secondary {
@include btn-outline($white-light, $blue-500, $blue-500, $blue-500, $white-light, $blue-600, $blue-600, $blue-700); @include btn-outline($white-light, $blue-500, $blue-500, $blue-100, $blue-700, $blue-500, $blue-200, $blue-600, $blue-800);
} }
} }
......
...@@ -241,6 +241,7 @@ ...@@ -241,6 +241,7 @@
*/ */
&.code { &.code {
padding: 0; padding: 0;
border-radius: 0 0 $border-radius-default $border-radius-default;
} }
.list-inline.previews { .list-inline.previews {
......
...@@ -280,3 +280,7 @@ label { ...@@ -280,3 +280,7 @@ label {
max-width: $input-lg-width; max-width: $input-lg-width;
width: 100%; width: 100%;
} }
.input-group-text {
max-height: $input-height;
}
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
pre { pre {
padding: 10px 0; padding: 10px 0;
border: 0; border: 0;
border-radius: 0; border-radius: 0 0 $border-radius-default $border-radius-default;
font-family: $monospace-font; font-family: $monospace-font;
font-size: $code-font-size; font-size: $code-font-size;
line-height: 19px; line-height: 19px;
...@@ -42,6 +42,7 @@ ...@@ -42,6 +42,7 @@
padding: 10px; padding: 10px;
text-align: right; text-align: right;
float: left; float: left;
border-bottom-left-radius: $border-radius-default;
a { a {
font-family: $monospace-font; font-family: $monospace-font;
......
...@@ -376,3 +376,12 @@ ...@@ -376,3 +376,12 @@
} }
} }
} }
/*
* Mixin that handles the size and right margin of avatars.
*/
@mixin avatar-size($size, $margin-right) {
width: $size;
height: $size;
margin-right: $margin-right;
}
...@@ -589,6 +589,7 @@ $issue-board-list-difference-md: $issue-board-list-difference-sm + $issue-boards ...@@ -589,6 +589,7 @@ $issue-board-list-difference-md: $issue-board-list-difference-sm + $issue-boards
*/ */
$avatar-radius: 50%; $avatar-radius: 50%;
$gl-avatar-size: 40px; $gl-avatar-size: 40px;
$gl-avatar-border-opacity: 0.1;
/* /*
* Blame * Blame
......
...@@ -7,6 +7,7 @@ $secondary: $gray-light; ...@@ -7,6 +7,7 @@ $secondary: $gray-light;
$input-disabled-bg: $gray-light; $input-disabled-bg: $gray-light;
$input-border-color: $gray-200; $input-border-color: $gray-200;
$input-color: $gl-text-color; $input-color: $gl-text-color;
$input-font-size: $gl-font-size;
$font-family-sans-serif: $regular-font; $font-family-sans-serif: $regular-font;
$font-family-monospace: $monospace-font; $font-family-monospace: $monospace-font;
$btn-line-height: 20px; $btn-line-height: 20px;
......
...@@ -69,6 +69,8 @@ ...@@ -69,6 +69,8 @@
align-self: flex-start; align-self: flex-start;
font-weight: 500; font-weight: 500;
font-size: 20px; font-size: 20px;
color: $orange-900;
opacity: 1;
margin: $gl-padding-8 14px 0 0; margin: $gl-padding-8 14px 0 0;
} }
......
...@@ -494,6 +494,12 @@ table.code { ...@@ -494,6 +494,12 @@ table.code {
} }
} }
.line_holder:last-of-type {
td:first-child {
border-bottom-left-radius: $border-radius-default;
}
}
&.left-side-selected { &.left-side-selected {
td.line_content.parallel.right-side { td.line_content.parallel.right-side {
user-select: none; user-select: none;
......
...@@ -21,13 +21,6 @@ ...@@ -21,13 +21,6 @@
color: $login-brand-holder-color; color: $login-brand-holder-color;
} }
h1:first-child {
font-weight: $gl-font-weight-normal;
margin-bottom: 0.68em;
margin-top: 0;
font-size: 34px;
}
h3 { h3 {
font-size: 22px; font-size: 22px;
} }
......
...@@ -87,6 +87,11 @@ ...@@ -87,6 +87,11 @@
padding: $gl-padding; padding: $gl-padding;
} }
.mr-widget-info {
padding-left: $gl-padding-50 - $gl-padding-32;
padding-right: $gl-padding;
}
.mr-state-widget { .mr-state-widget {
color: $gl-text-color; color: $gl-text-color;
...@@ -560,6 +565,10 @@ ...@@ -560,6 +565,10 @@
.mr-links { .mr-links {
padding-left: $status-icon-size + $gl-btn-padding; padding-left: $status-icon-size + $gl-btn-padding;
&:last-child {
padding-bottom: $gl-padding;
}
} }
.mr-info-list { .mr-info-list {
...@@ -1030,11 +1039,6 @@ ...@@ -1030,11 +1039,6 @@
background: $black-transparent; background: $black-transparent;
} }
.source-branch-removal-status {
padding-left: 50px;
padding-bottom: $gl-padding;
}
.mr-compare { .mr-compare {
.diff-file .file-title-flex-parent { .diff-file .file-title-flex-parent {
top: $header-height + 51px; top: $header-height + 51px;
......
...@@ -5,8 +5,10 @@ module Clusters ...@@ -5,8 +5,10 @@ module Clusters
include Presentable include Presentable
include Gitlab::Utils::StrongMemoize include Gitlab::Utils::StrongMemoize
include FromUnion include FromUnion
include ReactiveCaching
self.table_name = 'clusters' self.table_name = 'clusters'
self.reactive_cache_key = -> (cluster) { [cluster.class.model_name.singular, cluster.id] }
PROJECT_ONLY_APPLICATIONS = { PROJECT_ONLY_APPLICATIONS = {
Applications::Jupyter.application_name => Applications::Jupyter, Applications::Jupyter.application_name => Applications::Jupyter,
...@@ -57,6 +59,8 @@ module Clusters ...@@ -57,6 +59,8 @@ module Clusters
validate :no_groups, unless: :group_type? validate :no_groups, unless: :group_type?
validate :no_projects, unless: :project_type? validate :no_projects, unless: :project_type?
after_save :clear_reactive_cache!
delegate :status, to: :provider, allow_nil: true delegate :status, to: :provider, allow_nil: true
delegate :status_reason, to: :provider, allow_nil: true delegate :status_reason, to: :provider, allow_nil: true
delegate :on_creation?, to: :provider, allow_nil: true delegate :on_creation?, to: :provider, allow_nil: true
...@@ -123,15 +127,19 @@ module Clusters ...@@ -123,15 +127,19 @@ module Clusters
end end
def status_name def status_name
if provider provider&.status_name || connection_status.presence || :created
provider.status_name end
else
:created def connection_status
with_reactive_cache do |data|
data[:connection_status]
end end
end end
def created? def calculate_reactive_cache
status_name == :created return unless enabled?
{ connection_status: retrieve_connection_status }
end end
def applications def applications
...@@ -221,6 +229,51 @@ module Clusters ...@@ -221,6 +229,51 @@ module Clusters
@instance_domain ||= Gitlab::CurrentSettings.auto_devops_domain @instance_domain ||= Gitlab::CurrentSettings.auto_devops_domain
end end
def retrieve_connection_status
kubeclient.core_client.discover
rescue *Gitlab::Kubernetes::Errors::CONNECTION
:unreachable
rescue *Gitlab::Kubernetes::Errors::AUTHENTICATION
:authentication_failure
rescue Kubeclient::HttpError => e
kubeclient_error_status(e.message)
rescue => e
Gitlab::Sentry.track_acceptable_exception(e, extra: { cluster_id: id })
:unknown_failure
else
:connected
end
# KubeClient uses the same error class
# For connection errors (eg. timeout) and
# for Kubernetes errors.
def kubeclient_error_status(message)
if message&.match?(/timed out|timeout/i)
:unreachable
else
:authentication_failure
end
end
# To keep backward compatibility with AUTO_DEVOPS_DOMAIN
# environment variable, we need to ensure KUBE_INGRESS_BASE_DOMAIN
# is set if AUTO_DEVOPS_DOMAIN is set on any of the following options:
# ProjectAutoDevops#Domain, project variables or group variables,
# as the AUTO_DEVOPS_DOMAIN is needed for CI_ENVIRONMENT_URL
#
# This method should is scheduled to be removed on
# https://gitlab.com/gitlab-org/gitlab-ce/issues/56959
def legacy_auto_devops_domain
if project_type?
project&.auto_devops&.domain.presence ||
project.variables.find_by(key: 'AUTO_DEVOPS_DOMAIN')&.value.presence ||
project.group&.variables&.find_by(key: 'AUTO_DEVOPS_DOMAIN')&.value.presence
elsif group_type?
group.variables.find_by(key: 'AUTO_DEVOPS_DOMAIN')&.value.presence
end
end
def restrict_modification def restrict_modification
if provider&.on_creation? if provider&.on_creation?
errors.add(:base, "cannot modify during creation") errors.add(:base, "cannot modify during creation")
......
...@@ -13,6 +13,7 @@ class Identity < ApplicationRecord ...@@ -13,6 +13,7 @@ class Identity < ApplicationRecord
before_save :ensure_normalized_extern_uid, if: :extern_uid_changed? before_save :ensure_normalized_extern_uid, if: :extern_uid_changed?
after_destroy :clear_user_synced_attributes, if: :user_synced_attributes_metadata_from_provider? after_destroy :clear_user_synced_attributes, if: :user_synced_attributes_metadata_from_provider?
scope :for_user, ->(user) { where(user: user) }
scope :with_provider, ->(provider) { where(provider: provider) } scope :with_provider, ->(provider) { where(provider: provider) }
scope :with_extern_uid, ->(provider, extern_uid) do scope :with_extern_uid, ->(provider, extern_uid) do
iwhere(extern_uid: normalize_uid(provider, extern_uid)).with_provider(provider) iwhere(extern_uid: normalize_uid(provider, extern_uid)).with_provider(provider)
......
...@@ -22,10 +22,6 @@ module Clusters ...@@ -22,10 +22,6 @@ module Clusters
"https://console.cloud.google.com/kubernetes/clusters/details/#{provider.zone}/#{name}" if gcp? "https://console.cloud.google.com/kubernetes/clusters/details/#{provider.zone}/#{name}" if gcp?
end end
def can_toggle_cluster?
can?(current_user, :update_cluster, cluster) && created?
end
def can_read_cluster? def can_read_cluster?
can?(current_user, :read_cluster, cluster) can?(current_user, :read_cluster, cluster)
end end
......
...@@ -11,12 +11,14 @@ ...@@ -11,12 +11,14 @@
= f.hidden_field :user_id = f.hidden_field :user_id
.form-group.row .form-group.row
= f.label :user_id, class: 'col-sm-2 col-form-label' .col-sm-2.col-form-label
= f.label :user_id
.col-sm-10 .col-sm-10
- name = "#{@abuse_report.user.name} (@#{@abuse_report.user.username})" - name = "#{@abuse_report.user.name} (@#{@abuse_report.user.username})"
= text_field_tag :user_name, name, class: "form-control", readonly: true = text_field_tag :user_name, name, class: "form-control", readonly: true
.form-group.row .form-group.row
= f.label :message, class: 'col-sm-2 col-form-label' .col-sm-2.col-form-label
= f.label :message
.col-sm-10 .col-sm-10
= f.text_area :message, class: "form-control", rows: 2, required: true, value: sanitize(@ref_url) = f.text_area :message, class: "form-control", rows: 2, required: true, value: sanitize(@ref_url)
.form-text.text-muted .form-text.text-muted
......
...@@ -2,13 +2,15 @@ ...@@ -2,13 +2,15 @@
= form_errors(application) = form_errors(application)
= content_tag :div, class: 'form-group row' do = content_tag :div, class: 'form-group row' do
= f.label :name, class: 'col-sm-2 col-form-label' .col-sm-2.col-form-label
= f.label :name
.col-sm-10 .col-sm-10
= f.text_field :name, class: 'form-control' = f.text_field :name, class: 'form-control'
= doorkeeper_errors_for application, :name = doorkeeper_errors_for application, :name
= content_tag :div, class: 'form-group row' do = content_tag :div, class: 'form-group row' do
= f.label :redirect_uri, class: 'col-sm-2 col-form-label' .col-sm-2.col-form-label
= f.label :redirect_uri
.col-sm-10 .col-sm-10
= f.text_area :redirect_uri, class: 'form-control' = f.text_area :redirect_uri, class: 'form-control'
= doorkeeper_errors_for application, :redirect_uri = doorkeeper_errors_for application, :redirect_uri
...@@ -21,14 +23,16 @@ ...@@ -21,14 +23,16 @@
for local tests for local tests
= content_tag :div, class: 'form-group row' do = content_tag :div, class: 'form-group row' do
= f.label :trusted, class: 'col-sm-2 col-form-label pt-0' .col-sm-2.col-form-label.pt-0
= f.label :trusted
.col-sm-10 .col-sm-10
= f.check_box :trusted = f.check_box :trusted
%span.form-text.text-muted %span.form-text.text-muted
Trusted applications are automatically authorized on GitLab OAuth flow. Trusted applications are automatically authorized on GitLab OAuth flow.
.form-group.row .form-group.row
= f.label :scopes, class: 'col-sm-2 col-form-label pt-0' .col-sm-2.col-form-label.pt-0
= f.label :scopes
.col-sm-10 .col-sm-10
= render 'shared/tokens/scopes_form', prefix: 'doorkeeper_application', token: application, scopes: @scopes = render 'shared/tokens/scopes_form', prefix: 'doorkeeper_application', token: application, scopes: @scopes
......
...@@ -10,7 +10,8 @@ ...@@ -10,7 +10,8 @@
= form_errors(@broadcast_message) = form_errors(@broadcast_message)
.form-group.row .form-group.row
= f.label :message, class: 'col-form-label col-sm-2' .col-sm-2.col-form-label
= f.label :message
.col-sm-10 .col-sm-10
= f.text_area :message, class: "form-control js-autosize", = f.text_area :message, class: "form-control js-autosize",
required: true, required: true,
...@@ -20,19 +21,23 @@ ...@@ -20,19 +21,23 @@
.col-sm-10.offset-sm-2 .col-sm-10.offset-sm-2
= link_to 'Customize colors', '#', class: 'js-toggle-colors-link' = link_to 'Customize colors', '#', class: 'js-toggle-colors-link'
.form-group.row.js-toggle-colors-container.toggle-colors.hide .form-group.row.js-toggle-colors-container.toggle-colors.hide
= f.label :color, "Background Color", class: 'col-form-label col-sm-2' .col-sm-2.col-form-label
= f.label :color, "Background Color"
.col-sm-10 .col-sm-10
= f.color_field :color, class: "form-control" = f.color_field :color, class: "form-control"
.form-group.row.js-toggle-colors-container.toggle-colors.hide .form-group.row.js-toggle-colors-container.toggle-colors.hide
= f.label :font, "Font Color", class: 'col-form-label col-sm-2' .col-sm-2.col-form-label
= f.label :font, "Font Color"
.col-sm-10 .col-sm-10
= f.color_field :font, class: "form-control" = f.color_field :font, class: "form-control"
.form-group.row .form-group.row
= f.label :starts_at, _("Starts at (UTC)"), class: 'col-form-label col-sm-2' .col-sm-2.col-form-label
= f.label :starts_at, _("Starts at (UTC)")
.col-sm-10.datetime-controls .col-sm-10.datetime-controls
= f.datetime_select :starts_at, {}, class: 'form-control form-control-inline' = f.datetime_select :starts_at, {}, class: 'form-control form-control-inline'
.form-group.row .form-group.row
= f.label :ends_at, _("Ends at (UTC)"), class: 'col-form-label col-sm-2' .col-sm-2.col-form-label
= f.label :ends_at, _("Ends at (UTC)")
.col-sm-10.datetime-controls .col-sm-10.datetime-controls
= f.datetime_select :ends_at, {}, class: 'form-control form-control-inline' = f.datetime_select :ends_at, {}, class: 'form-control form-control-inline'
.form-actions .form-actions
......
...@@ -6,7 +6,8 @@ ...@@ -6,7 +6,8 @@
= render_if_exists 'admin/namespace_plan', f: f = render_if_exists 'admin/namespace_plan', f: f
.form-group.row.group-description-holder .form-group.row.group-description-holder
= f.label :avatar, _("Group avatar"), class: 'col-form-label col-sm-2' .col-sm-2.col-form-label
= f.label :avatar, _("Group avatar")
.col-sm-10 .col-sm-10
= render 'shared/choose_avatar_button', f: f = render 'shared/choose_avatar_button', f: f
......
...@@ -2,12 +2,14 @@ ...@@ -2,12 +2,14 @@
= form_errors(@identity) = form_errors(@identity)
.form-group.row .form-group.row
= f.label :provider, class: 'col-form-label col-sm-2' .col-sm-2.col-form-label
= f.label :provider
.col-sm-10 .col-sm-10
- values = Gitlab::Auth::OAuth::Provider.providers.map { |name| ["#{Gitlab::Auth::OAuth::Provider.label_for(name)} (#{name})", name] } - values = Gitlab::Auth::OAuth::Provider.providers.map { |name| ["#{Gitlab::Auth::OAuth::Provider.label_for(name)} (#{name})", name] }
= f.select :provider, values, { allow_blank: false }, class: 'form-control' = f.select :provider, values, { allow_blank: false }, class: 'form-control'
.form-group.row .form-group.row
= f.label :extern_uid, _("Identifier"), class: 'col-form-label col-sm-2' .col-sm-2.col-form-label
= f.label :extern_uid, _("Identifier")
.col-sm-10 .col-sm-10
= f.text_field :extern_uid, class: 'form-control', required: true = f.text_field :extern_uid, class: 'form-control', required: true
......
...@@ -2,15 +2,18 @@ ...@@ -2,15 +2,18 @@
= form_errors(@label) = form_errors(@label)
.form-group.row .form-group.row
= f.label :title, class: 'col-form-label col-sm-2' .col-sm-2.col-form-label
= f.label :title
.col-sm-10 .col-sm-10
= f.text_field :title, class: "form-control", required: true = f.text_field :title, class: "form-control", required: true
.form-group.row .form-group.row
= f.label :description, class: 'col-form-label col-sm-2' .col-sm-2.col-form-label
= f.label :description
.col-sm-10 .col-sm-10
= f.text_field :description, class: "form-control js-quick-submit" = f.text_field :description, class: "form-control js-quick-submit"
.form-group.row .form-group.row
= f.label :color, _("Background color"), class: 'col-form-label col-sm-2' .col-sm-2.col-form-label
= f.label :color, _("Background color")
.col-sm-10 .col-sm-10
.input-group .input-group
.input-group-prepend .input-group-prepend
......
...@@ -117,7 +117,8 @@ ...@@ -117,7 +117,8 @@
.card-body .card-body
= form_for @project, url: transfer_admin_project_path(@project), method: :put do |f| = form_for @project, url: transfer_admin_project_path(@project), method: :put do |f|
.form-group.row .form-group.row
= f.label :new_namespace_id, "Namespace", class: 'col-form-label col-sm-3' .col-sm-3.col-form-label
= f.label :new_namespace_id, "Namespace"
.col-sm-9 .col-sm-9
.dropdown .dropdown
= dropdown_toggle('Search for Namespace', { toggle: 'dropdown', field_name: 'new_namespace_id' }, { toggle_class: 'js-namespace-select large' }) = dropdown_toggle('Search for Namespace', { toggle: 'dropdown', field_name: 'new_namespace_id' }, { toggle_class: 'js-namespace-select large' })
......
%fieldset %fieldset
%legend Access %legend Access
.form-group.row .form-group.row
.col-sm-2.text-right .col-sm-2.col-form-label
= f.label :projects_limit, class: 'col-form-label' = f.label :projects_limit
.col-sm-10= f.number_field :projects_limit, min: 0, max: Gitlab::Database::MAX_INT_VALUE, class: 'form-control' .col-sm-10
= f.number_field :projects_limit, min: 0, max: Gitlab::Database::MAX_INT_VALUE, class: 'form-control'
.form-group.row .form-group.row
.col-sm-2.text-right .col-sm-2.col-form-label
= f.label :can_create_group, class: 'col-form-label' = f.label :can_create_group
.col-sm-10= f.check_box :can_create_group .col-sm-10
= f.check_box :can_create_group
.form-group.row .form-group.row
.col-sm-2.text-right .col-sm-2.col-form-label
= f.label :access_level, class: 'col-form-label' = f.label :access_level
.col-sm-10 .col-sm-10
- editing_current_user = (current_user == @user) - editing_current_user = (current_user == @user)
...@@ -34,8 +36,8 @@ ...@@ -34,8 +36,8 @@
You cannot remove your own admin rights. You cannot remove your own admin rights.
.form-group.row .form-group.row
.col-sm-2.text-right .col-sm-2.col-form-label
= f.label :external, class: 'col-form-label' = f.label :external
.hidden{ data: user_internal_regex_data } .hidden{ data: user_internal_regex_data }
.col-sm-10 .col-sm-10
= f.check_box :external do = f.check_box :external do
......
...@@ -5,20 +5,20 @@ ...@@ -5,20 +5,20 @@
%fieldset %fieldset
%legend Account %legend Account
.form-group.row .form-group.row
.col-sm-2.text-right .col-sm-2.col-form-label
= f.label :name, class: 'col-form-label' = f.label :name
.col-sm-10 .col-sm-10
= f.text_field :name, required: true, autocomplete: 'off', class: 'form-control' = f.text_field :name, required: true, autocomplete: 'off', class: 'form-control'
%span.help-inline * required %span.help-inline * required
.form-group.row .form-group.row
.col-sm-2.text-right .col-sm-2.col-form-label
= f.label :username, class: 'col-form-label' = f.label :username
.col-sm-10 .col-sm-10
= f.text_field :username, required: true, autocomplete: 'off', autocorrect: 'off', autocapitalize: 'off', spellcheck: false, class: 'form-control' = f.text_field :username, required: true, autocomplete: 'off', autocorrect: 'off', autocapitalize: 'off', spellcheck: false, class: 'form-control'
%span.help-inline * required %span.help-inline * required
.form-group.row .form-group.row
.col-sm-2.text-right .col-sm-2.col-form-label
= f.label :email, class: 'col-form-label' = f.label :email
.col-sm-10 .col-sm-10
= f.text_field :email, required: true, autocomplete: 'off', class: 'form-control' = f.text_field :email, required: true, autocomplete: 'off', class: 'form-control'
%span.help-inline * required %span.help-inline * required
...@@ -27,8 +27,8 @@ ...@@ -27,8 +27,8 @@
%fieldset %fieldset
%legend Password %legend Password
.form-group.row .form-group.row
.col-sm-2.text-right .col-sm-2.col-form-label
= f.label :password, class: 'col-form-label' = f.label :password
.col-sm-10 .col-sm-10
%strong %strong
Reset link will be generated and sent to the user. Reset link will be generated and sent to the user.
...@@ -38,13 +38,15 @@ ...@@ -38,13 +38,15 @@
%fieldset %fieldset
%legend Password %legend Password
.form-group.row .form-group.row
.col-sm-2.text-right .col-sm-2.col-form-label
= f.label :password, class: 'col-form-label' = f.label :password
.col-sm-10= f.password_field :password, disabled: f.object.force_random_password, class: 'form-control' .col-sm-10
= f.password_field :password, disabled: f.object.force_random_password, class: 'form-control'
.form-group.row .form-group.row
.col-sm-2.text-right .col-sm-2.col-form-label
= f.label :password_confirmation, class: 'col-form-label' = f.label :password_confirmation
.col-sm-10= f.password_field :password_confirmation, disabled: f.object.force_random_password, class: 'form-control' .col-sm-10
= f.password_field :password_confirmation, disabled: f.object.force_random_password, class: 'form-control'
= render partial: 'access_levels', locals: { f: f } = render partial: 'access_levels', locals: { f: f }
...@@ -55,27 +57,31 @@ ...@@ -55,27 +57,31 @@
%fieldset %fieldset
%legend Profile %legend Profile
.form-group.row .form-group.row
.col-sm-2.text-right .col-sm-2.col-form-label
= f.label :avatar, class: 'col-form-label' = f.label :avatar
.col-sm-10 .col-sm-10
= f.file_field :avatar = f.file_field :avatar
.form-group.row .form-group.row
.col-sm-2.text-right .col-sm-2.col-form-label
= f.label :skype, class: 'col-form-label' = f.label :skype
.col-sm-10= f.text_field :skype, class: 'form-control' .col-sm-10
= f.text_field :skype, class: 'form-control'
.form-group.row .form-group.row
.col-sm-2.text-right .col-sm-2.col-form-label
= f.label :linkedin, class: 'col-form-label' = f.label :linkedin
.col-sm-10= f.text_field :linkedin, class: 'form-control' .col-sm-10
= f.text_field :linkedin, class: 'form-control'
.form-group.row .form-group.row
.col-sm-2.text-right .col-sm-2.col-form-label
= f.label :twitter, class: 'col-form-label' = f.label :twitter
.col-sm-10= f.text_field :twitter, class: 'form-control' .col-sm-10
= f.text_field :twitter, class: 'form-control'
.form-group.row .form-group.row
.col-sm-2.text-right .col-sm-2.col-form-label
= f.label :website_url, 'Website', class: 'col-form-label' = f.label :website_url
.col-sm-10= f.text_field :website_url, class: 'form-control' .col-sm-10
= f.text_field :website_url, class: 'form-control'
= render_if_exists 'admin/users/admin_notes', f: f = render_if_exists 'admin/users/admin_notes', f: f
......
...@@ -59,7 +59,7 @@ ...@@ -59,7 +59,7 @@
.append-right-default .append-right-default
= s_("CiVariable|Masked") = s_("CiVariable|Masked")
%button{ type: 'button', %button{ type: 'button',
class: "js-project-feature-toggle project-feature-toggle #{'is-checked' if is_masked}", class: "js-project-feature-toggle project-feature-toggle qa-variable-masked #{'is-checked' if is_masked}",
"aria-label": s_("CiVariable|Toggle masked") } "aria-label": s_("CiVariable|Toggle masked") }
%input{ type: "hidden", %input{ type: "hidden",
class: 'js-ci-variable-input-masked js-project-feature-toggle-input', class: 'js-ci-variable-input-masked js-project-feature-toggle-input',
......
...@@ -5,5 +5,17 @@ ...@@ -5,5 +5,17 @@
.hidden.js-cluster-creating.bs-callout.bs-callout-info{ role: 'alert' } .hidden.js-cluster-creating.bs-callout.bs-callout-info{ role: 'alert' }
= s_('ClusterIntegration|Kubernetes cluster is being created on Google Kubernetes Engine...') = s_('ClusterIntegration|Kubernetes cluster is being created on Google Kubernetes Engine...')
.hidden.row.js-cluster-api-unreachable.bs-callout.bs-callout-warning{ role: 'alert' }
.col-11
= s_('ClusterIntegration|Your cluster API is unreachable. Please ensure your API URL is correct.')
.col-1.p-0
%button.js-close-banner.close.cluster-application-banner-close.h-100.m-0= "×"
.hidden.js-cluster-authentication-failure.row.js-cluster-api-unreachable.bs-callout.bs-callout-warning{ role: 'alert' }
.col-11
= s_('ClusterIntegration|There was a problem authenticating with your cluster. Please ensure your CA Certificate and Token are valid.')
.col-1.p-0
%button.js-close-banner.close.cluster-application-banner-close.h-100.m-0= "×"
.hidden.js-cluster-success.bs-callout.bs-callout-success{ role: 'alert' } .hidden.js-cluster-success.bs-callout.bs-callout-success{ role: 'alert' }
= s_("ClusterIntegration|Kubernetes cluster was successfully created on Google Kubernetes Engine. Refresh the page to see Kubernetes cluster's details") = s_("ClusterIntegration|Kubernetes cluster was successfully created on Google Kubernetes Engine. Refresh the page to see Kubernetes cluster's details")
...@@ -24,7 +24,8 @@ ...@@ -24,7 +24,8 @@
help_path: help_page_path('user/project/clusters/index.md', anchor: 'installing-applications'), help_path: help_page_path('user/project/clusters/index.md', anchor: 'installing-applications'),
ingress_help_path: help_page_path('user/project/clusters/index.md', anchor: 'getting-the-external-endpoint'), ingress_help_path: help_page_path('user/project/clusters/index.md', anchor: 'getting-the-external-endpoint'),
ingress_dns_help_path: help_page_path('user/project/clusters/index.md', anchor: 'manually-determining-the-external-endpoint'), ingress_dns_help_path: help_page_path('user/project/clusters/index.md', anchor: 'manually-determining-the-external-endpoint'),
manage_prometheus_path: manage_prometheus_path } } manage_prometheus_path: manage_prometheus_path,
cluster_id: @cluster.id } }
.js-cluster-application-notice .js-cluster-application-notice
.flash-container .flash-container
......
.form-group .form-group
= f.label :create_chat_team, class: 'col-form-label' do .col-sm-2.col-form-label
%span.mattermost-icon = f.label :create_chat_team do
= custom_icon('icon_mattermost') %span.mattermost-icon
Mattermost = custom_icon('icon_mattermost')
Mattermost
.col-sm-10 .col-sm-10
.form-check.js-toggle-container .form-check.js-toggle-container
.js-toggle-button.form-check-input= f.check_box(:create_chat_team, { checked: true }, true, false) .js-toggle-button.form-check-input= f.check_box(:create_chat_team, { checked: true }, true, false)
......
.form-group.row .form-group.row
= f.label :lfs_enabled, 'Large File Storage', class: 'col-form-label col-sm-2 pt-0' .col-sm-2.col-form-label.pt-0
= f.label :lfs_enabled, 'Large File Storage'
.col-sm-10 .col-sm-10
.form-check .form-check
= f.check_box :lfs_enabled, checked: @group.lfs_enabled?, class: 'form-check-input' = f.check_box :lfs_enabled, checked: @group.lfs_enabled?, class: 'form-check-input'
...@@ -10,12 +11,14 @@ ...@@ -10,12 +11,14 @@
%br/ %br/
%span.descr This setting can be overridden in each project. %span.descr This setting can be overridden in each project.
.form-group.row .form-group.row
= f.label s_('ProjectCreationLevel|Allowed to create projects'), class: 'col-form-label col-sm-2' .col-sm-2.col-form-label
= f.label s_('ProjectCreationLevel|Allowed to create projects')
.col-sm-10 .col-sm-10
= f.select :project_creation_level, options_for_select(::Gitlab::Access.project_creation_options, @group.project_creation_level), {}, class: 'form-control' = f.select :project_creation_level, options_for_select(::Gitlab::Access.project_creation_options, @group.project_creation_level), {}, class: 'form-control'
.form-group.row .form-group.row
= f.label :require_two_factor_authentication, 'Two-factor authentication', class: 'col-form-label col-sm-2 pt-0' .col-sm-2.col-form-label.pt-0
= f.label :require_two_factor_authentication, 'Two-factor authentication'
.col-sm-10 .col-sm-10
.form-check .form-check
= f.check_box :require_two_factor_authentication, class: 'form-check-input' = f.check_box :require_two_factor_authentication, class: 'form-check-input'
......
...@@ -10,15 +10,17 @@ ...@@ -10,15 +10,17 @@
.container.navless-container .container.navless-container
.content .content
= render "layouts/flash" = render "layouts/flash"
.row.append-bottom-15 .row.mt-3
.col-sm-7.brand-holder .col-sm-12
%h1 %h1.mb-3.font-weight-normal
= brand_title = brand_title
.row.mb-3
.col-sm-7.order-12.order-sm-1.brand-holder
= brand_image = brand_image
- if current_appearance&.description? - if current_appearance&.description?
= brand_text = brand_text
- else - else
%h3 %h3.mt-sm-0
= _('Open source software to collaborate on code') = _('Open source software to collaborate on code')
%p %p
...@@ -29,7 +31,7 @@ ...@@ -29,7 +31,7 @@
= render_if_exists 'layouts/devise_help_text' = render_if_exists 'layouts/devise_help_text'
.col-sm-5.new-session-forms-container .col-sm-5.order-1.order-sm-12.new-session-forms-container
= yield = yield
%hr.footer-fixed %hr.footer-fixed
......
...@@ -13,13 +13,18 @@ ...@@ -13,13 +13,18 @@
- unless @user.password_automatically_set? - unless @user.password_automatically_set?
.form-group.row .form-group.row
= f.label :current_password, class: 'col-form-label col-sm-2' .col-sm-2.col-form-label
.col-sm-10= f.password_field :current_password, required: true, class: 'form-control' = f.label :current_password
.col-sm-10
= f.password_field :current_password, required: true, class: 'form-control'
.form-group.row .form-group.row
= f.label :password, class: 'col-form-label col-sm-2' .col-sm-2.col-form-label
.col-sm-10= f.password_field :password, required: true, class: 'form-control' = f.label :password
.col-sm-10
= f.password_field :password, required: true, class: 'form-control'
.form-group.row .form-group.row
= f.label :password_confirmation, class: 'col-form-label col-sm-2' .col-sm-2.col-form-label
= f.label :password_confirmation
.col-sm-10 .col-sm-10
= f.password_field :password_confirmation, required: true, class: 'form-control' = f.password_field :password_confirmation, required: true, class: 'form-control'
.form-actions .form-actions
......
...@@ -5,22 +5,22 @@ ...@@ -5,22 +5,22 @@
%p= msg %p= msg
.form-group.row .form-group.row
= f.label :domain, class: 'col-form-label col-sm-2' do .col-sm-2.col-form-label
= _("Domain") = f.label :domain, _("Domain")
.col-sm-10 .col-sm-10
= f.text_field :domain, required: true, autocomplete: 'off', class: 'form-control', disabled: @domain.persisted? = f.text_field :domain, required: true, autocomplete: 'off', class: 'form-control', disabled: @domain.persisted?
- if Gitlab.config.pages.external_https - if Gitlab.config.pages.external_https
.form-group.row .form-group.row
= f.label :certificate, class: 'col-form-label col-sm-2' do .col-sm-2.col-form-label
= _("Certificate (PEM)") = f.label :certificate, _("Certificate (PEM)")
.col-sm-10 .col-sm-10
= f.text_area :certificate, rows: 5, class: 'form-control' = f.text_area :certificate, rows: 5, class: 'form-control'
%span.help-inline= _("Upload a certificate for your domain with all intermediates") %span.help-inline= _("Upload a certificate for your domain with all intermediates")
.form-group.row .form-group.row
= f.label :key, class: 'col-form-label col-sm-2' do .col-sm-2.col-form-label
= _("Key (PEM)") = f.label :key, _("Key (PEM)")
.col-sm-10 .col-sm-10
= f.text_area :key, rows: 5, class: 'form-control' = f.text_area :key, rows: 5, class: 'form-control'
%span.help-inline= _("Upload a private key for your certificate") %span.help-inline= _("Upload a private key for your certificate")
......
...@@ -6,8 +6,8 @@ ...@@ -6,8 +6,8 @@
.card-body .card-body
= form_errors(@protected_branch) = form_errors(@protected_branch)
.form-group.row .form-group.row
= f.label :name, class: 'col-md-2 text-right' do .col-md-2.text-right
Branch: = f.label :name, 'Branch:'
.col-md-10 .col-md-10
= render partial: "projects/protected_branches/shared/dropdown", locals: { f: f } = render partial: "projects/protected_branches/shared/dropdown", locals: { f: f }
.form-text.text-muted .form-text.text-muted
......
...@@ -6,8 +6,8 @@ ...@@ -6,8 +6,8 @@
.card-body .card-body
= form_errors(@protected_tag) = form_errors(@protected_tag)
.form-group.row .form-group.row
= f.label :name, class: 'col-md-2 text-right' do .col-md-2.text-right
Tag: = f.label :name, 'Tag:'
.col-md-10.protected-tags-dropdown .col-md-10.protected-tags-dropdown
= render partial: "projects/protected_tags/shared/dropdown", locals: { f: f } = render partial: "projects/protected_tags/shared/dropdown", locals: { f: f }
.form-text.text-muted .form-text.text-muted
......
...@@ -2,10 +2,12 @@ ...@@ -2,10 +2,12 @@
- setting = error_tracking_setting - setting = error_tracking_setting
%section.settings.expanded.no-animate %section.settings.no-animate.js-error-tracking-settings
.settings-header .settings-header
%h4 %h4
= _('Error Tracking') = _('Error Tracking')
%button.btn.js-settings-toggle{ type: 'button' }
= _('Expand')
%p %p
= _('To link Sentry to GitLab, enter your Sentry URL and Auth Token.') = _('To link Sentry to GitLab, enter your Sentry URL and Auth Token.')
= link_to _('More information'), help_page_path('user/project/operations/error_tracking'), target: '_blank', rel: 'noopener noreferrer' = link_to _('More information'), help_page_path('user/project/operations/error_tracking'), target: '_blank', rel: 'noopener noreferrer'
......
...@@ -3,6 +3,6 @@ ...@@ -3,6 +3,6 @@
- breadcrumb_title _('Operations Settings') - breadcrumb_title _('Operations Settings')
= render_if_exists 'projects/settings/operations/incidents' = render_if_exists 'projects/settings/operations/incidents'
= render 'projects/settings/operations/error_tracking', expanded: true = render 'projects/settings/operations/error_tracking'
= render 'projects/settings/operations/external_dashboard' = render 'projects/settings/operations/external_dashboard'
= render_if_exists 'projects/settings/operations/tracing' = render_if_exists 'projects/settings/operations/tracing'
...@@ -2,17 +2,20 @@ ...@@ -2,17 +2,20 @@
= form_errors(@label) = form_errors(@label)
.form-group.row .form-group.row
= f.label :title, class: 'col-form-label col-sm-2' .col-sm-2.col-form-label
= f.label :title
.col-sm-10 .col-sm-10
= f.text_field :title, class: "form-control js-label-title qa-label-title", required: true, autofocus: true = f.text_field :title, class: "form-control js-label-title qa-label-title", required: true, autofocus: true
= render_if_exists 'shared/labels/create_label_help_text' = render_if_exists 'shared/labels/create_label_help_text'
.form-group.row .form-group.row
= f.label :description, class: 'col-form-label col-sm-2' .col-sm-2.col-form-label
= f.label :description
.col-sm-10 .col-sm-10
= f.text_field :description, class: "form-control js-quick-submit qa-label-description" = f.text_field :description, class: "form-control js-quick-submit qa-label-description"
.form-group.row .form-group.row
= f.label :color, "Background color", class: 'col-form-label col-sm-2' .col-sm-2.col-form-label
= f.label :color, "Background color"
.col-sm-10 .col-sm-10
.input-group .input-group
.input-group-prepend .input-group-prepend
......
...@@ -7,7 +7,8 @@ ...@@ -7,7 +7,8 @@
= form_errors(@snippet) = form_errors(@snippet)
.form-group.row .form-group.row
= f.label :title, class: 'col-form-label col-sm-2' .col-sm-2.col-form-label
= f.label :title
.col-sm-10 .col-sm-10
= f.text_field :title, class: 'form-control qa-snippet-title', required: true, autofocus: true = f.text_field :title, class: 'form-control qa-snippet-title', required: true, autofocus: true
...@@ -17,7 +18,8 @@ ...@@ -17,7 +18,8 @@
.file-editor .file-editor
.form-group.row .form-group.row
= f.label :file_name, "File", class: 'col-form-label col-sm-2' .col-sm-2.col-form-label
= f.label :file_name, "File"
.col-sm-10 .col-sm-10
.file-holder.snippet .file-holder.snippet
.js-file-title.file-title .js-file-title.file-title
......
---
title: Fix border radii on diff files and repo files
merge_request:
author:
type: fixed
---
title: Validate Kubernetes credentials at cluster creation
merge_request: 27403
author:
type: added
---
title: Fix col-sm-* in forms to keep layout
merge_request: 24885
author: Takuya Noguchi
type: fixed
---
title: Fix padding in MR widget
merge_request: 28472
author:
type: fixed
---
title: Prioritize login form on mobile breakpoint
merge_request: 28360
author:
type: changed
---
title: Fix input group height
merge_request:
author:
type: other
---
title: Bring secondary button styles up to design standard
merge_request: 27920
author:
type: fixed
---
title: Add expand/collapse to error tracking settings
merge_request: 28619
author:
type: added
---
title: Prevent icons from shrinking in User popover when contents exceed container
merge_request: 28696
author:
type: fixed
---
title: Remove the circuit breaker API
merge_request: 28669
author:
type: removed
---
title: Fix milestone references containing &, <, or >
merge_request: 28667
author:
type: fixed
---
title: Update SAST.gitlab-ci.yml - Add SAST_GITLEAKS_ENTROPY_LEVEL
merge_request: 28607
author:
type: fixed
---
title: 'API: change masked attribute type to Boolean'
merge_request: 28758
author:
type: other
...@@ -23,6 +23,8 @@ to help identify if something is wrong: ...@@ -23,6 +23,8 @@ to help identify if something is wrong:
![Geo health check](img/geo_node_healthcheck.png) ![Geo health check](img/geo_node_healthcheck.png)
For information on how to resolve common errors reported from the UI, see [common errors](#common-errors).
If the UI is not working, or you are unable to log in, you can run the Geo If the UI is not working, or you are unable to log in, you can run the Geo
health check manually to get this information as well as a few more details. health check manually to get this information as well as a few more details.
This rake task can be run on an app node in the **primary** or **secondary** This rake task can be run on an app node in the **primary** or **secondary**
...@@ -40,7 +42,8 @@ Checking Geo ... ...@@ -40,7 +42,8 @@ Checking Geo ...
GitLab Geo is available ... yes GitLab Geo is available ... yes
GitLab Geo is enabled ... yes GitLab Geo is enabled ... yes
GitLab Geo secondary database is correctly configured ... yes GitLab Geo secondary database is correctly configured ... yes
Using database streaming replication? ... yes Database replication enabled? ... yes
Database replication working? ... yes
GitLab Geo tracking database is configured to use Foreign Data Wrapper? ... yes GitLab Geo tracking database is configured to use Foreign Data Wrapper? ... yes
GitLab Geo tracking database Foreign Data Wrapper schema is up-to-date? ... yes GitLab Geo tracking database Foreign Data Wrapper schema is up-to-date? ... yes
GitLab Geo HTTP(S) connectivity ... GitLab Geo HTTP(S) connectivity ...
...@@ -68,22 +71,22 @@ Example output: ...@@ -68,22 +71,22 @@ Example output:
``` ```
http://secondary.example.com/ http://secondary.example.com/
----------------------------------------------------- -----------------------------------------------------
GitLab Version: 11.8.1-ee GitLab Version: 11.10.4-ee
Geo Role: Secondary Geo Role: Secondary
Health Status: Healthy Health Status: Healthy
Repositories: 190/190 (100%) Repositories: 289/289 (100%)
Verified Repositories: 190/190 (100%) Verified Repositories: 289/289 (100%)
Wikis: 190/190 (100%) Wikis: 289/289 (100%)
Verified Wikis: 190/190 (100%) Verified Wikis: 289/289 (100%)
LFS Objects: 35/35 (100%) LFS Objects: 8/8 (100%)
Attachments: 528/528 (100%) Attachments: 5/5 (100%)
CI job artifacts: 477/477 (100%) CI job artifacts: 0/0 (0%)
Repositories Checked: 0/190 (0%) Repositories Checked: 0/289 (0%)
Sync Settings: Full Sync Settings: Full
Database replication lag: 0 seconds Database replication lag: 0 seconds
Last event ID seen from primary: 2158 (about 2 minute ago) Last event ID seen from primary: 10215 (about 2 minutes ago)
Last event ID processed by cursor: 2158 (about 2 minute ago) Last event ID processed by cursor: 10215 (about 2 minutes ago)
Last status report was: 4 minutes ago Last status report was: 2 minutes ago
``` ```
## Is Postgres replication working? ## Is Postgres replication working?
...@@ -455,3 +458,57 @@ reload of the FDW schema. To manually reload the FDW schema: ...@@ -455,3 +458,57 @@ reload of the FDW schema. To manually reload the FDW schema:
[database-start-replication]: database.md#step-3-initiate-the-replication-process [database-start-replication]: database.md#step-3-initiate-the-replication-process
[database-pg-replication]: database.md#postgresql-replication [database-pg-replication]: database.md#postgresql-replication
## Common errors
This section documents common errors reported in the admin UI and how to fix them.
### Geo database configuration file is missing
GitLab cannot find or doesn't have permission to access the `database_geo.yml` configuration file.
In an Omnibus GitLab installation, the file should be in `/var/opt/gitlab/gitlab-rails/etc`.
If it doesn't exist or inadvertent changes have been made to it, run `sudo gitlab-ctl reconfigure` to restore it to its correct state.
If this path is mounted on a remote volume, please check your volume configuration and that it has correct permissions.
### Geo node has a database that is writable which is an indication it is not configured for replication with the primary node.
This error refers to a problem with the database replica on a **secondary** node,
which Geo expects to have access to. It usually means, either:
- An unsupported replication method was used (for example, logical replication).
- The instructions to setup a [Geo database replication](database.md) were not followed correctly.
A common source of confusion with **secondary** nodes is that it requires two separate
PostgreSQL instances:
- A read-only replica of the **primary** node.
- A regular, writable instance that holds replication metadata. That is, the Geo tracking database.
### Geo node does not appear to be replicating the database from the primary node.
The most common problems that prevent the database from replicating correctly are:
- **Secondary** nodes cannot reach the **primary** node. Check credentials, firewall rules, etc.
- SSL certificate problems. Make sure you copied `/etc/gitlab/gitlab-secrets.json` from the **primary** node.
- Database storage disk is full.
- Database replication slot is misconfigured.
- Database is not using a replication slot or another alternative and cannot catch-up because WAL files were purged.
Make sure you follow the [Geo database replication](database.md) instructions for supported configuration.
### Geo database version (...) does not match latest migration (...)
If you are using GitLab Omnibus installation, something might have failed during upgrade. You can:
- Run `sudo gitlab-ctl reconfigure`.
- Manually trigger the database migration by running: `sudo gitlab-rake geo:db:migrate` as root on the **secondary** node.
### Geo database is not configured to use Foreign Data Wrapper
This error means the Geo Tracking Database doesn't have the FDW server and credentials
configured.
See [How do I fix a "Foreign Data Wrapper (FDW) is not configured" error?](#how-do-i-fix-a-foreign-data-wrapper-fdw-is-not-configured-error).
...@@ -135,7 +135,7 @@ the contention. ...@@ -135,7 +135,7 @@ the contention.
- 2 or more GitLab application nodes (Unicorn, Workhorse, Sidekiq, PGBouncer) - 2 or more GitLab application nodes (Unicorn, Workhorse, Sidekiq, PGBouncer)
- 1 NFS/Gitaly server - 1 NFS/Gitaly server
![Horizontal architecture diagram](https://docs.gitlab.com/ee/administration/img/high_availability/horizontal.png) ![Horizontal architecture diagram](img/horizontal.png)
### Hybrid ### Hybrid
...@@ -153,7 +153,7 @@ contention due to certain workloads. ...@@ -153,7 +153,7 @@ contention due to certain workloads.
- 1 or more NFS/Gitaly servers - 1 or more NFS/Gitaly servers
- 1 Monitoring node (Prometheus, Grafana) - 1 Monitoring node (Prometheus, Grafana)
![Hybrid architecture diagram](https://docs.gitlab.com/ee/administration/img/high_availability/hybrid.png) ![Hybrid architecture diagram](img/hybrid.png)
#### Reference Architecture #### Reference Architecture
...@@ -194,7 +194,7 @@ with the added complexity of many more nodes to configure, manage and monitor. ...@@ -194,7 +194,7 @@ with the added complexity of many more nodes to configure, manage and monitor.
- 2 or more Web nodes (All other web requests) - 2 or more Web nodes (All other web requests)
- 2 or more NFS/Gitaly servers - 2 or more NFS/Gitaly servers
![Fully Distributed architecture diagram](https://docs.gitlab.com/ee/administration/img/high_availability/fully-distributed.png) ![Fully Distributed architecture diagram](img/fully-distributed.png)
The following pages outline the steps necessary to configure each component The following pages outline the steps necessary to configure each component
separately: separately:
......
...@@ -134,7 +134,7 @@ otherwise the networks will become a single point of failure. ...@@ -134,7 +134,7 @@ otherwise the networks will become a single point of failure.
#### Architecture #### Architecture
![PG HA Architecture](pg_ha_architecture.png) ![PG HA Architecture](img/pg_ha_architecture.png)
Database nodes run two services with PostgreSQL: Database nodes run two services with PostgreSQL:
......
# Circuitbreaker API
NOTE: **Deprecated:**
Support of the circuit breaker is removed, as Gitaly can be configured to
to work without NFS and [communicate solely over HTTP](../administration/gitaly/index.md).
...@@ -59,6 +59,7 @@ description: 'Learn how to contribute to GitLab.' ...@@ -59,6 +59,7 @@ description: 'Learn how to contribute to GitLab.'
- [DeclarativePolicy framework](policies.md) - [DeclarativePolicy framework](policies.md)
- [How Git object deduplication works in GitLab](git_object_deduplication.md) - [How Git object deduplication works in GitLab](git_object_deduplication.md)
- [Geo development](geo.md) - [Geo development](geo.md)
- [Routing](routing.md)
## Performance guides ## Performance guides
......
...@@ -106,43 +106,43 @@ Component statuses are linked to configuration documentation for each component. ...@@ -106,43 +106,43 @@ Component statuses are linked to configuration documentation for each component.
### Component list ### Component list
| Component | Description | [Omnibus GitLab](https://docs.gitlab.com/omnibus/README.html) | [GitLab chart](https://docs.gitlab.com/charts/) | [Minikube Minimal](https://docs.gitlab.com/charts/development/minikube/#deploying-gitlab-with-minimal-settings) | [GitLab.com](https://gitlab.com) | CE/EE | | Component | Description | [Omnibus GitLab](https://docs.gitlab.com/omnibus/README.html) | [GitLab chart](https://docs.gitlab.com/charts/) | [Minikube Minimal](https://docs.gitlab.com/charts/development/minikube/#deploying-gitlab-with-minimal-settings) | [GitLab.com](https://gitlab.com) | [Source](https://docs.gitlab.com/ee/install/installation.html) | [GDK](https://gitlab.com/gitlab-org/gitlab-development-kit) | CE/EE |
| --------- | ----------- |:--------------------:|:------------------:|:-----:|:--------:|:--------:| | --------- | ----------- |:--------------------:|:------------------:|:-----:|:--------:|:--------:|:-------:|:-------:|
| [NGINX](#nginx) | Routes requests to appropriate components, terminates SSL | [][nginx-omnibus] | [][nginx-charts] | [][nginx-charts] | [](https://about.gitlab.com/handbook/engineering/infrastructure/production-architecture/#service-architecture) | CE & EE | | [NGINX](#nginx) | Routes requests to appropriate components, terminates SSL | [][nginx-omnibus] | [][nginx-charts] | [][nginx-charts] | [](https://about.gitlab.com/handbook/engineering/infrastructure/production-architecture/#service-architecture) | [][nginx-source] | ❌ | CE & EE |
| [Unicorn (GitLab Rails)](#unicorn) | Handles requests for the web interface and API | [][unicorn-omnibus] | [][unicorn-charts] | [][unicorn-charts] | [](https://docs.gitlab.com/ee/user/gitlab_com/#unicorn) | CE & EE | | [Unicorn (GitLab Rails)](#unicorn) | Handles requests for the web interface and API | [][unicorn-omnibus] | [][unicorn-charts] | [][unicorn-charts] | [](https://docs.gitlab.com/ee/user/gitlab_com/#unicorn) | [][unicorn-source] | [][gitlab-yml] | CE & EE |
| [Sidekiq](#sidekiq) | Background jobs processor | [][sidekiq-omnibus] | [][sidekiq-charts] | [](https://docs.gitlab.com/charts/charts/gitlab/sidekiq/index.html) | [](https://docs.gitlab.com/ee/user/gitlab_com/#sidekiq) | CE & EE | | [Sidekiq](#sidekiq) | Background jobs processor | [][sidekiq-omnibus] | [][sidekiq-charts] | [](https://docs.gitlab.com/charts/charts/gitlab/sidekiq/index.html) | [](https://docs.gitlab.com/ee/user/gitlab_com/#sidekiq) | [][gitlab-yml] | [][gitlab-yml] | CE & EE |
| [Gitaly](#gitaly) | Git RPC service for handling all git calls made by GitLab | [][gitaly-omnibus] | [][gitaly-charts] | [][gitaly-charts] | [](https://about.gitlab.com/handbook/engineering/infrastructure/production-architecture/#service-architecture) | CE & EE | | [Gitaly](#gitaly) | Git RPC service for handling all git calls made by GitLab | [][gitaly-omnibus] | [][gitaly-charts] | [][gitaly-charts] | [](https://about.gitlab.com/handbook/engineering/infrastructure/production-architecture/#service-architecture) | [][gitaly-source] | ✅ | CE & EE |
| [GitLab Workhorse](#gitlab-workhorse) | Smart reverse proxy, handles large HTTP requests | [][workhorse-omnibus] | [][workhorse-charts] | [][workhorse-charts] | [](https://about.gitlab.com/handbook/engineering/infrastructure/production-architecture/#service-architecture) | CE & EE | | [GitLab Workhorse](#gitlab-workhorse) | Smart reverse proxy, handles large HTTP requests | [][workhorse-omnibus] | [][workhorse-charts] | [][workhorse-charts] | [](https://about.gitlab.com/handbook/engineering/infrastructure/production-architecture/#service-architecture) | [][workhorse-source] | ✅ | CE & EE |
| [GitLab Shell](#gitlab-shell) | Handles `git` over SSH sessions | [][shell-omnibus] | [][shell-charts] | [][shell-charts] | [](https://about.gitlab.com/handbook/engineering/infrastructure/production-architecture/#service-architecture) | CE & EE | | [GitLab Shell](#gitlab-shell) | Handles `git` over SSH sessions | [][shell-omnibus] | [][shell-charts] | [][shell-charts] | [](https://about.gitlab.com/handbook/engineering/infrastructure/production-architecture/#service-architecture) | [][shell-source] | [][gitlab-yml] | CE & EE |
| [GitLab Pages](#gitlab-pages) | Hosts static websites | [][pages-omnibus] | [][pages-charts] | [][pages-charts] | [](https://docs.gitlab.com/ee/user/gitlab_com/#gitlab-pages) | CE & EE | | [GitLab Pages](#gitlab-pages) | Hosts static websites | [][pages-omnibus] | [][pages-charts] | [][pages-charts] | [](https://docs.gitlab.com/ee/user/gitlab_com/#gitlab-pages) | [][pages-source] | [][pages-gdk] | CE & EE |
| [Registry](#registry) | Container registry, allows pushing and pulling of images | [][registry-omnibus] | [][registry-charts] | [][registry-charts] | [](https://docs.gitlab.com/ee/user/project/container_registry.html#build-and-push-images) | CE & EE | | [Registry](#registry) | Container registry, allows pushing and pulling of images | [][registry-omnibus] | [][registry-charts] | [][registry-charts] | [](https://docs.gitlab.com/ee/user/project/container_registry.html#build-and-push-images) | [][registry-source] | [][registry-gdk] | CE & EE |
| [Redis](#redis) | Caching service | [][redis-omnibus] | [][redis-omnibus] | [][redis-charts] | [](https://about.gitlab.com/handbook/engineering/infrastructure/production-architecture/#service-architecture) | CE & EE | | [Redis](#redis) | Caching service | [][redis-omnibus] | [][redis-omnibus] | [][redis-charts] | [](https://about.gitlab.com/handbook/engineering/infrastructure/production-architecture/#service-architecture) | [][redis-source] | ✅ | CE & EE |
| [PostgreSQL](#postgresql) | Database | [][postgres-omnibus] | [][postgres-charts] | [][postgres-charts] | [](https://docs.gitlab.com/ee/user/gitlab_com/#postgresql) | CE & EE | | [PostgreSQL](#postgresql) | Database | [][postgres-omnibus] | [][postgres-charts] | [][postgres-charts] | [](https://docs.gitlab.com/ee/user/gitlab_com/#postgresql) | [][postgres-source] | ✅ | CE & EE |
| [PgBouncer](#pgbouncer) | Database connection pooling, failover | [][pgbouncer-omnibus] | [][pgbouncer-charts] | [][pgbouncer-charts] | [](https://about.gitlab.com/handbook/engineering/infrastructure/production-architecture/#database-architecture) | EE Only | | [PgBouncer](#pgbouncer) | Database connection pooling, failover | [][pgbouncer-omnibus] | [][pgbouncer-charts] | [][pgbouncer-charts] | [](https://about.gitlab.com/handbook/engineering/infrastructure/production-architecture/#database-architecture) | ❌ | ❌ | EE Only |
| [Consul](#consul) | Database node discovery, failover | [][consul-omnibus] | [][consul-charts] | [][consul-charts] | [](https://docs.gitlab.com/ee/user/gitlab_com/#consul) | EE Only | | [Consul](#consul) | Database node discovery, failover | [][consul-omnibus] | [][consul-charts] | [][consul-charts] | [](https://docs.gitlab.com/ee/user/gitlab_com/#consul) | ❌ | ❌ | EE Only |
| [GitLab self-monitoring: Prometheus](#prometheus) | Time-series database, metrics collection, and query service | [][prometheus-omnibus] | [][prometheus-charts] | [][prometheus-charts] | [](https://docs.gitlab.com/ee/user/gitlab_com/#prometheus) | CE & EE | | [GitLab self-monitoring: Prometheus](#prometheus) | Time-series database, metrics collection, and query service | [][prometheus-omnibus] | [][prometheus-charts] | [][prometheus-charts] | [](https://docs.gitlab.com/ee/user/gitlab_com/#prometheus) | ❌ | ❌ | CE & EE |
| [GitLab self-monitoring: Alertmanager](#alertmanager) | Deduplicates, groups, and routes alerts from Prometheus | [][alertmanager-omnibus] | [][alertmanager-charts] | [][alertmanager-charts] | [](https://about.gitlab.com/handbook/engineering/monitoring/) | CE & EE | | [GitLab self-monitoring: Alertmanager](#alertmanager) | Deduplicates, groups, and routes alerts from Prometheus | [][alertmanager-omnibus] | [][alertmanager-charts] | [][alertmanager-charts] | [](https://about.gitlab.com/handbook/engineering/monitoring/) | ❌ | ❌ | CE & EE |
| [GitLab self-monitoring: Grafana](#grafana) | Metrics dashboard | [][grafana-omnibus] | [][grafana-charts] | [][grafana-charts] | [](https://dashboards.gitlab.com/d/RZmbBr7mk/gitlab-triage?refresh=30s) | CE & EE | | [GitLab self-monitoring: Grafana](#grafana) | Metrics dashboard | [][grafana-omnibus] | [][grafana-charts] | [][grafana-charts] | [](https://dashboards.gitlab.com/d/RZmbBr7mk/gitlab-triage?refresh=30s) | ❌ | ❌ | CE & EE |
| [GitLab self-monitoring: Sentry](#sentry) | Track errors generated by the GitLab instance | [][sentry-omnibus] | [][sentry-charts] | [][sentry-charts] | [](https://about.gitlab.com/handbook/support/workflows/services/gitlab_com/500_errors.html#searching-sentry) | CE & EE | | [GitLab self-monitoring: Sentry](#sentry) | Track errors generated by the GitLab instance | [][sentry-omnibus] | [][sentry-charts] | [][sentry-charts] | [](https://about.gitlab.com/handbook/support/workflows/services/gitlab_com/500_errors.html#searching-sentry) | [][gitlab-yml] | [][gitlab-yml] | CE & EE |
| [GitLab self-monitoring: Jaeger](#jaeger) | View traces generated by the GitLab instance | [][jaeger-omnibus] | [][jaeger-charts] | [][jaeger-charts] | [](https://gitlab.com/gitlab-org/omnibus-gitlab/issues/4104) | CE & EE | | [GitLab self-monitoring: Jaeger](#jaeger) | View traces generated by the GitLab instance | [][jaeger-omnibus] | [][jaeger-charts] | [][jaeger-charts] | [](https://gitlab.com/gitlab-org/omnibus-gitlab/issues/4104) | [][jaeger-source] | [][jaeger-gdk] | CE & EE |
| [Redis Exporter](#redis-exporter) | Prometheus endpoint with Redis metrics | [][redis-exporter-omnibus] | [][redis-exporter-charts] | [][redis-exporter-charts] | [](https://about.gitlab.com/handbook/engineering/monitoring/) | CE & EE | | [Redis Exporter](#redis-exporter) | Prometheus endpoint with Redis metrics | [][redis-exporter-omnibus] | [][redis-exporter-charts] | [][redis-exporter-charts] | [](https://about.gitlab.com/handbook/engineering/monitoring/) | ❌ | ❌ | CE & EE |
| [Postgres Exporter](#postgres-exporter) | Prometheus endpoint with PostgreSQL metrics | [][postgres-exporter-omnibus] | [][postgres-exporter-charts] | [][postgres-exporter-charts] | [](https://about.gitlab.com/handbook/engineering/monitoring/) | CE & EE | | [Postgres Exporter](#postgres-exporter) | Prometheus endpoint with PostgreSQL metrics | [][postgres-exporter-omnibus] | [][postgres-exporter-charts] | [][postgres-exporter-charts] | [](https://about.gitlab.com/handbook/engineering/monitoring/) | ❌ | ❌ | CE & EE |
| [PgBouncer Exporter](#pgbouncer-exporter) | Prometheus endpoint with PgBouncer metrics | [][pgbouncer-exporter-omnibus] | [][pgbouncer-exporter-charts] | [][pgbouncer-exporter-charts] | [](https://about.gitlab.com/handbook/engineering/monitoring/) | CE & EE | | [PgBouncer Exporter](#pgbouncer-exporter) | Prometheus endpoint with PgBouncer metrics | [][pgbouncer-exporter-omnibus] | [][pgbouncer-exporter-charts] | [][pgbouncer-exporter-charts] | [](https://about.gitlab.com/handbook/engineering/monitoring/) | ❌ | ❌ | CE & EE |
| [GitLab Monitor](#gitlab-monitor) | Generates a variety of GitLab metrics | [][gitlab-monitor-omnibus] | [][gitab-monitor-charts] | [][gitab-monitor-charts] | [](https://about.gitlab.com/handbook/engineering/monitoring/) | CE & EE | | [GitLab Monitor](#gitlab-monitor) | Generates a variety of GitLab metrics | [][gitlab-monitor-omnibus] | [][gitab-monitor-charts] | [][gitab-monitor-charts] | [](https://about.gitlab.com/handbook/engineering/monitoring/) | ❌ | ❌ | CE & EE |
| [Node Exporter](#node-exporter) | Prometheus endpoint with system metrics | [][node-exporter-omnibus] | [][node-exporter-charts] | [][node-exporter-charts] | [](https://about.gitlab.com/handbook/engineering/monitoring/) | CE & EE | | [Node Exporter](#node-exporter) | Prometheus endpoint with system metrics | [][node-exporter-omnibus] | [][node-exporter-charts] | [][node-exporter-charts] | [](https://about.gitlab.com/handbook/engineering/monitoring/) | ❌ | ❌ | CE & EE |
| [Mattermost](#mattermost) | Open-source Slack alternative | [][mattermost-omnibus] | [][mattermost-charts] | [][mattermost-charts] | [](https://docs.gitlab.com/ee/user/project/integrations/mattermost_slash_commands.html#manual-configuration), [](https://docs.gitlab.com/ee/user/project/integrations/mattermost.html) | CE & EE | | [Mattermost](#mattermost) | Open-source Slack alternative | [][mattermost-omnibus] | [][mattermost-charts] | [][mattermost-charts] | [](https://docs.gitlab.com/ee/user/project/integrations/mattermost_slash_commands.html#manual-configuration), [](https://docs.gitlab.com/ee/user/project/integrations/mattermost.html) | ❌ | ❌ | CE & EE |
| [Minio](#minio) | Object storage service | [][minio-omnibus] | [][minio-charts] | [][minio-charts] | [](https://about.gitlab.com/handbook/engineering/infrastructure/production-architecture/#storage-architecture) | CE & EE | | [MinIO](#minio) | Object storage service | [][minio-omnibus] | [][minio-charts] | [][minio-charts] | [](https://about.gitlab.com/handbook/engineering/infrastructure/production-architecture/#storage-architecture) | ❌ | [][minio-gdk] | CE & EE |
| [Runner](#gitlab-runner) | Executes GitLab CI jobs | [][runner-omnibus] | [][runner-charts] | [][runner-charts] | [](https://docs.gitlab.com/ee/user/gitlab_com/#shared-runners) | CE & EE | | [Runner](#gitlab-runner) | Executes GitLab CI jobs | [][runner-omnibus] | [][runner-charts] | [][runner-charts] | [](https://docs.gitlab.com/ee/user/gitlab_com/#shared-runners) | [][runner-source] | [][runner-gdk] | CE & EE |
| [Database Migrations](#database-migrations) | Database migrations | [][database-migrations-omnibus] | []() | [][database-migrations-charts] | [][database-migrations-charts] | CE & EE | | [Database Migrations](#database-migrations) | Database migrations | [][database-migrations-omnibus] | [][database-migrations-charts] | [][database-migrations-charts] | ✅ | [][database-migrations-source] | ✅ | CE & EE |
| [Certificate Management](#certificate-management) | TLS Settings, Let's Encrypt | [][certificate-management-omnibus] | [][certificate-management-charts] | [][certificate-management-charts] | [](https://about.gitlab.com/handbook/engineering/infrastructure/production-architecture/#secrets-management) | CE & EE | | [Certificate Management](#certificate-management) | TLS Settings, Let's Encrypt | [][certificate-management-omnibus] | [][certificate-management-charts] | [][certificate-management-charts] | [](https://about.gitlab.com/handbook/engineering/infrastructure/production-architecture/#secrets-management) | [][certificate-management-source] | [][certificate-management-gdk] | CE & EE |
| [GitLab Geo Node](#gitlab-geo) | Geographically distributed GitLab nodes | [][geo-omnibus] | [][geo-charts] | [][geo-charts] | ✅ | EE Only | | [GitLab Geo Node](#gitlab-geo) | Geographically distributed GitLab nodes | [][geo-omnibus] | [][geo-charts] | [][geo-charts] | ✅ | [](../administration/geo/replication/configuration_source.md) | [][geo-gdk] | EE Only |
| [LDAP Authentication](#ldap-authentication) | Authenticate users against centralized LDAP directory | [][ldap-omnibus] | [][ldap-charts] | [][ldap-charts] | [](https://about.gitlab.com/pricing/#gitlab-com) | CE & EE | | [LDAP Authentication](#ldap-authentication) | Authenticate users against centralized LDAP directory | [][ldap-omnibus] | [][ldap-charts] | [][ldap-charts] | [](https://about.gitlab.com/pricing/#gitlab-com) | [][gitlab-yml] | [][ldap-gdk] | CE & EE |
| [Outbound email (SMTP)](#outbound-email) | Send email messages to users | [][outbound-email-omnibus] | [][outbound-email-charts] | [][outbound-email-charts] | [](https://docs.gitlab.com/ee/user/gitlab_com/#mail-configuration) | CE & EE | | [Outbound email (SMTP)](#outbound-email) | Send email messages to users | [][outbound-email-omnibus] | [][outbound-email-charts] | [][outbound-email-charts] | [](https://docs.gitlab.com/ee/user/gitlab_com/#mail-configuration) | [][gitlab-yml] | [][gitlab-yml] | CE & EE |
| [Inbound email (SMTP)](#inbound-email) | Receive messages to update issues | [][inbound-email-omnibus] | [][inbound-email-charts] | [][inbound-email-charts] | [](https://docs.gitlab.com/ee/user/gitlab_com/#mail-configuration) | CE & EE | | [Inbound email (SMTP)](#inbound-email) | Receive messages to update issues | [][inbound-email-omnibus] | [][inbound-email-charts] | [][inbound-email-charts] | [](https://docs.gitlab.com/ee/user/gitlab_com/#mail-configuration) | [][gitlab-yml] | [][gitlab-yml] | CE & EE |
| [ElasticSearch](#elasticsearch) | Improved search within GitLab | [][elasticsearch-omnibus] | [][elasticsearch-charts] | [][elasticsearch-charts] | [](https://gitlab.com/groups/gitlab-org/-/epics/153) | EE Only | | [ElasticSearch](#elasticsearch) | Improved search within GitLab | [][elasticsearch-omnibus] | [][elasticsearch-charts] | [][elasticsearch-charts] | [](https://gitlab.com/groups/gitlab-org/-/epics/153) | [][elasticsearch-source] | [][elasticsearch-gdk] | EE Only |
| [Sentry integration](#sentry) | Error tracking for deployed apps | [][sentry-integration] | [][sentry-integration] | [][sentry-integration] | [][sentry-integration] | CE & EE | | [Sentry integration](#sentry) | Error tracking for deployed apps | [][sentry-integration] | [][sentry-integration] | [][sentry-integration] | [][sentry-integration] | [][sentry-integration] | [][sentry-integration] | CE & EE |
| [Jaeger integration](#jaeger) | Distributed tracing for deployed apps | [][jaeger-integration] | [][jaeger-integration] | [][jaeger-integration] | [][jaeger-integration] | EE Only | | [Jaeger integration](#jaeger) | Distributed tracing for deployed apps | [][jaeger-integration] | [][jaeger-integration] | [][jaeger-integration] | [][jaeger-integration] | [][jaeger-integration] | [][jaeger-integration] | EE Only |
| [Kubernetes cluster apps](#kubernetes-cluster-apps) | Deploy [Helm](https://docs.helm.sh/), [Ingress](https://kubernetes.io/docs/concepts/services-networking/ingress/), [Cert-Manager](https://docs.cert-manager.io/en/latest/), [Prometheus](https://prometheus.io/docs/introduction/overview/), a [Runner](https://docs.gitlab.com/runner/), [JupyterHub](http://jupyter.org/), [Knative](https://cloud.google.com/knative) to a cluster | [][managed-k8s-apps] | [][managed-k8s-apps] | [][managed-k8s-apps] | [][managed-k8s-apps] | CE & EE | | [Kubernetes cluster apps](#kubernetes-cluster-apps) | Deploy [Helm](https://docs.helm.sh/), [Ingress](https://kubernetes.io/docs/concepts/services-networking/ingress/), [Cert-Manager](https://docs.cert-manager.io/en/latest/), [Prometheus](https://prometheus.io/docs/introduction/overview/), a [Runner](https://docs.gitlab.com/runner/), [JupyterHub](http://jupyter.org/), [Knative](https://cloud.google.com/knative) to a cluster | [][managed-k8s-apps] | [][managed-k8s-apps] | [][managed-k8s-apps] | [][managed-k8s-apps] | [][managed-k8s-apps] | [][managed-k8s-apps] | CE & EE |
### Component details ### Component details
...@@ -164,13 +164,14 @@ GitLab can be considered to have two layers from a process perspective: ...@@ -164,13 +164,14 @@ GitLab can be considered to have two layers from a process perspective:
- [Project page](https://github.com/prometheus/alertmanager/blob/master/README.md) - [Project page](https://github.com/prometheus/alertmanager/blob/master/README.md)
- Configuration: [Omnibus][alertmanager-omnibus], [Charts][alertmanager-charts] - Configuration: [Omnibus][alertmanager-omnibus], [Charts][alertmanager-charts]
- Layer: Monitoring - Layer: Monitoring
- Process: `alertmanager`
[Alert manager](https://prometheus.io/docs/alerting/alertmanager/) is a tool provided by Prometheus that _"handles alerts sent by client applications such as the Prometheus server. It takes care of deduplicating, grouping, and routing them to the correct receiver integration such as email, PagerDuty, or OpsGenie. It also takes care of silencing and inhibition of alerts."_ You can read more in [issue gitlab-ce#45740](https://gitlab.com/gitlab-org/gitlab-ce/issues/45740) about what we will be alerting on. [Alert manager](https://prometheus.io/docs/alerting/alertmanager/) is a tool provided by Prometheus that _"handles alerts sent by client applications such as the Prometheus server. It takes care of deduplicating, grouping, and routing them to the correct receiver integration such as email, PagerDuty, or OpsGenie. It also takes care of silencing and inhibition of alerts."_ You can read more in [issue gitlab-ce#45740](https://gitlab.com/gitlab-org/gitlab-ce/issues/45740) about what we will be alerting on.
#### Certificate management #### Certificate management
- Project page: [Omnibus](https://github.com/certbot/certbot/blob/master/README.rst), [Charts](https://github.com/jetstack/cert-manager/blob/master/README.md) - Project page: [Omnibus](https://github.com/certbot/certbot/blob/master/README.rst), [Charts](https://github.com/jetstack/cert-manager/blob/master/README.md)
- Configuration: [Omnibus][certificate-management-omnibus], [Charts][certificate-management-charts] - Configuration: [Omnibus][certificate-management-omnibus], [Charts][certificate-management-charts], [Source][certificate-management-source], [GDK][certificate-management-gdk]
- Layer: Core Service (Processor) - Layer: Core Service (Processor)
#### Consul #### Consul
...@@ -183,13 +184,13 @@ Consul is a tool for service discovery and configuration. Consul is distributed, ...@@ -183,13 +184,13 @@ Consul is a tool for service discovery and configuration. Consul is distributed,
#### Database migrations #### Database migrations
- Configuration: [Omnibus][registry-omnibus], [Charts][registry-charts] - Configuration: [Omnibus][registry-omnibus], [Charts][registry-charts], [Source][database-migrations-source]
- Layer: Core Service (Data) - Layer: Core Service (Data)
#### Elasticsearch #### Elasticsearch
- [Project page](https://github.com/elastic/elasticsearch/blob/master/README.textile) - [Project page](https://github.com/elastic/elasticsearch/blob/master/README.textile)
- Configuration: [Omnibus][elasticsearch-omnibus], [Charts][elasticsearch-charts] - Configuration: [Omnibus][elasticsearch-omnibus], [Charts][elasticsearch-charts], [Source][elasticsearch-source], [GDK][elasticsearch-gdk]
- Layer: Core Service (Data) - Layer: Core Service (Data)
Elasticsearch is a distributed RESTful search engine built for the cloud. Elasticsearch is a distributed RESTful search engine built for the cloud.
...@@ -197,14 +198,15 @@ Elasticsearch is a distributed RESTful search engine built for the cloud. ...@@ -197,14 +198,15 @@ Elasticsearch is a distributed RESTful search engine built for the cloud.
#### Gitaly #### Gitaly
- [Project page](https://gitlab.com/gitlab-org/gitaly/blob/master/README.md) - [Project page](https://gitlab.com/gitlab-org/gitaly/blob/master/README.md)
- Configuration: [Omnibus][gitaly-omnibus], [Charts][gitaly-charts] - Configuration: [Omnibus][gitaly-omnibus], [Charts][gitaly-charts], [Source][gitaly-source]
- Layer: Core Service (Data) - Layer: Core Service (Data)
- Process: `gitaly`
Gitaly is a service designed by GitLab to remove our need for NFS for Git storage in distributed deployments of GitLab (think GitLab.com or High Availability Deployments). As of 11.3.0, this service handles all Git level access in GitLab. You can read more about the project [in the project's readme](https://gitlab.com/gitlab-org/gitaly). Gitaly is a service designed by GitLab to remove our need for NFS for Git storage in distributed deployments of GitLab (think GitLab.com or High Availability Deployments). As of 11.3.0, this service handles all Git level access in GitLab. You can read more about the project [in the project's readme](https://gitlab.com/gitlab-org/gitaly).
#### Gitlab Geo #### Gitlab Geo
- Configuration: [Omnibus][geo-omnibus], [Charts][geo-charts] - Configuration: [Omnibus][geo-omnibus], [Charts][geo-charts], [GDK][geo-gdk]
- Layer: Core Service (Processor) - Layer: Core Service (Processor)
#### Gitlab Monitor #### Gitlab Monitor
...@@ -212,12 +214,13 @@ Gitaly is a service designed by GitLab to remove our need for NFS for Git storag ...@@ -212,12 +214,13 @@ Gitaly is a service designed by GitLab to remove our need for NFS for Git storag
- [Project page](https://gitlab.com/gitlab-org/gitlab-monitor) - [Project page](https://gitlab.com/gitlab-org/gitlab-monitor)
- Configuration: [Omnibus][gitlab-monitor-omnibus], [Charts][gitlab-monitor-charts] - Configuration: [Omnibus][gitlab-monitor-omnibus], [Charts][gitlab-monitor-charts]
- Layer: Monitoring - Layer: Monitoring
- Process: `gitlab-monitor`
GitLab Monitor is a process designed in house that allows us to export metrics about GitLab application internals to Prometheus. You can read more [in the project's readme](https://gitlab.com/gitlab-org/gitlab-monitor). GitLab Monitor is a process designed in house that allows us to export metrics about GitLab application internals to Prometheus. You can read more [in the project's readme](https://gitlab.com/gitlab-org/gitlab-monitor).
#### Gitlab Pages #### Gitlab Pages
- Configuration: [Omnibus][pages-omnibus], [Charts][pages-charts] - Configuration: [Omnibus][pages-omnibus], [Charts][pages-charts], [Source][pages-source], [GDK][pages-gdk]
- Layer: Core Service (Processor) - Layer: Core Service (Processor)
GitLab Pages is a feature that allows you to publish static websites directly from a repository in GitLab. GitLab Pages is a feature that allows you to publish static websites directly from a repository in GitLab.
...@@ -227,7 +230,7 @@ You can use it either for personal or business websites, such as portfolios, doc ...@@ -227,7 +230,7 @@ You can use it either for personal or business websites, such as portfolios, doc
#### Gitlab Runner #### Gitlab Runner
- [Project page](https://gitlab.com/gitlab-org/gitlab-runner/blob/master/README.md) - [Project page](https://gitlab.com/gitlab-org/gitlab-runner/blob/master/README.md)
- Configuration: [Omnibus][runner-omnibus], [Charts][runner-charts] - Configuration: [Omnibus][runner-omnibus], [Charts][runner-charts], [Source][runner-source], [GDK][runner-gdk]
- Layer: Core Service (Processor) - Layer: Core Service (Processor)
GitLab Runner runs tests and sends the results to GitLab. GitLab Runner runs tests and sends the results to GitLab.
...@@ -237,7 +240,7 @@ GitLab CI is the open-source continuous integration service included with GitLab ...@@ -237,7 +240,7 @@ GitLab CI is the open-source continuous integration service included with GitLab
#### Gitlab Shell #### Gitlab Shell
- [Project page](https://gitlab.com/gitlab-org/gitlab-shell/blob/master/README.md) - [Project page](https://gitlab.com/gitlab-org/gitlab-shell/blob/master/README.md)
- Configuration: [Omnibus][shell-omnibus], [Charts][shell-charts] - Configuration: [Omnibus][shell-omnibus], [Charts][shell-charts], [Source][shell-source], [GDK][gitlab-yml]
- Layer: Core Service (Processor) - Layer: Core Service (Processor)
[GitLab Shell](https://gitlab.com/gitlab-org/gitlab-shell) is a program designed at GitLab to handle ssh-based `git` sessions, and modifies the list of authorized keys. GitLab Shell is not a Unix shell nor a replacement for Bash or Zsh. [GitLab Shell](https://gitlab.com/gitlab-org/gitlab-shell) is a program designed at GitLab to handle ssh-based `git` sessions, and modifies the list of authorized keys. GitLab Shell is not a Unix shell nor a replacement for Bash or Zsh.
...@@ -245,8 +248,9 @@ GitLab CI is the open-source continuous integration service included with GitLab ...@@ -245,8 +248,9 @@ GitLab CI is the open-source continuous integration service included with GitLab
#### Gitlab Workhorse #### Gitlab Workhorse
- [Project page](https://gitlab.com/gitlab-org/gitlab-workhorse/blob/master/README.md) - [Project page](https://gitlab.com/gitlab-org/gitlab-workhorse/blob/master/README.md)
- Configuration: [Omnibus][gitlab-workhorse-omnibus], [Charts][gitlab-workhorse-charts] - Configuration: [Omnibus][gitlab-workhorse-omnibus], [Charts][gitlab-workhorse-charts], [Source][workhorse-source]
- Layer: Core Service (Processor) - Layer: Core Service (Processor)
- Process: `gitlab-workhorse`
[GitLab Workhorse](https://gitlab.com/gitlab-org/gitlab-workhorse) is a program designed at GitLab to help alleviate pressure from Unicorn. You can read more about the [historical reasons for developing](https://about.gitlab.com/2016/04/12/a-brief-history-of-gitlab-workhorse/). It's designed to act as a smart reverse proxy to help speed up GitLab as a whole. [GitLab Workhorse](https://gitlab.com/gitlab-org/gitlab-workhorse) is a program designed at GitLab to help alleviate pressure from Unicorn. You can read more about the [historical reasons for developing](https://about.gitlab.com/2016/04/12/a-brief-history-of-gitlab-workhorse/). It's designed to act as a smart reverse proxy to help speed up GitLab as a whole.
...@@ -261,7 +265,7 @@ Grafana is an open source, feature rich metrics dashboard and graph editor for G ...@@ -261,7 +265,7 @@ Grafana is an open source, feature rich metrics dashboard and graph editor for G
#### Jaeger #### Jaeger
- [Project page](https://github.com/jaegertracing/jaeger/blob/master/README.md) - [Project page](https://github.com/jaegertracing/jaeger/blob/master/README.md)
- Configuration: [Omnibus][jaeger-omnibus], [Charts][jaeger-charts] - Configuration: [Omnibus][jaeger-omnibus], [Charts][jaeger-charts], [Source][jaeger-source], [GDK][jaeger-gdk]
- Layer: Monitoring - Layer: Monitoring
Jaeger, inspired by Dapper and OpenZipkin, is a distributed tracing system. It can be used for monitoring microservices-based distributed systems. Jaeger, inspired by Dapper and OpenZipkin, is a distributed tracing system. It can be used for monitoring microservices-based distributed systems.
...@@ -271,6 +275,7 @@ Jaeger, inspired by Dapper and OpenZipkin, is a distributed tracing system. It c ...@@ -271,6 +275,7 @@ Jaeger, inspired by Dapper and OpenZipkin, is a distributed tracing system. It c
- [Project page](https://github.com/logrotate/logrotate/blob/master/README.md) - [Project page](https://github.com/logrotate/logrotate/blob/master/README.md)
- Configuration: [Omnibus](https://docs.gitlab.com/omnibus/settings/logs.html#logrotate) - Configuration: [Omnibus](https://docs.gitlab.com/omnibus/settings/logs.html#logrotate)
- Layer: Core Service - Layer: Core Service
- Process: `logrotate`
GitLab is comprised of a large number of services that all log. We started bundling our own logrotate as of 7.4 to make sure we were logging responsibly. This is just a packaged version of the common open source offering. GitLab is comprised of a large number of services that all log. We started bundling our own logrotate as of 7.4 to make sure we were logging responsibly. This is just a packaged version of the common open source offering.
...@@ -285,7 +290,7 @@ Mattermost is an open source, private cloud, Slack-alternative from https://matt ...@@ -285,7 +290,7 @@ Mattermost is an open source, private cloud, Slack-alternative from https://matt
#### MinIO #### MinIO
- [Project page](https://github.com/minio/minio/blob/master/README.md) - [Project page](https://github.com/minio/minio/blob/master/README.md)
- Configuration: [Omnibus][minio-omnibus], [Charts][minio-charts] - Configuration: [Omnibus][minio-omnibus], [Charts][minio-charts], [GDK][minio-gdk]
- Layer: Core Service (Data) - Layer: Core Service (Data)
MinIO is an object storage server released under Apache License v2.0. It is compatible with Amazon S3 cloud storage service. It is best suited for storing unstructured data such as photos, videos, log files, backups and container / VM images. Size of an object can range from a few KBs to a maximum of 5TB. MinIO is an object storage server released under Apache License v2.0. It is compatible with Amazon S3 cloud storage service. It is best suited for storing unstructured data such as photos, videos, log files, backups and container / VM images. Size of an object can range from a few KBs to a maximum of 5TB.
...@@ -293,8 +298,9 @@ MinIO is an object storage server released under Apache License v2.0. It is comp ...@@ -293,8 +298,9 @@ MinIO is an object storage server released under Apache License v2.0. It is comp
#### NGINX #### NGINX
- Project page: [Omnibus](https://github.com/nginx/nginx), [Charts](https://github.com/kubernetes/ingress-nginx/blob/master/README.md) - Project page: [Omnibus](https://github.com/nginx/nginx), [Charts](https://github.com/kubernetes/ingress-nginx/blob/master/README.md)
- Configuration: [Omnibus][nginx-omnibus], [Charts][nginx-charts] - Configuration: [Omnibus][nginx-omnibus], [Charts][nginx-charts], [Source][nginx-source]
- Layer: Core Service (Processor) - Layer: Core Service (Processor)
- Process: `nginx`
Nginx as an ingress port for all HTTP requests and routes them to the approriate sub-systems within GitLab. We are bundling an unmodified version of the popular open source webserver. Nginx as an ingress port for all HTTP requests and routes them to the approriate sub-systems within GitLab. We are bundling an unmodified version of the popular open source webserver.
...@@ -303,6 +309,7 @@ Nginx as an ingress port for all HTTP requests and routes them to the approriate ...@@ -303,6 +309,7 @@ Nginx as an ingress port for all HTTP requests and routes them to the approriate
- [Project page](https://github.com/prometheus/node_exporter/blob/master/README.md) - [Project page](https://github.com/prometheus/node_exporter/blob/master/README.md)
- Configuration: [Omnibus][node-exporter-omnibus], [Charts][node-exporter-charts] - Configuration: [Omnibus][node-exporter-omnibus], [Charts][node-exporter-charts]
- Layer: Monitoring - Layer: Monitoring
- Process: `node-exporter`
[Node Exporter](https://github.com/prometheus/node_exporter) is a Prometheus tool that gives us metrics on the underlying machine (think CPU/Disk/Load). It's just a packaged version of the common open source offering from the Prometheus project. [Node Exporter](https://github.com/prometheus/node_exporter) is a Prometheus tool that gives us metrics on the underlying machine (think CPU/Disk/Load). It's just a packaged version of the common open source offering from the Prometheus project.
...@@ -325,8 +332,9 @@ Prometheus exporter for PgBouncer. Exports metrics at 9127/metrics. ...@@ -325,8 +332,9 @@ Prometheus exporter for PgBouncer. Exports metrics at 9127/metrics.
#### Postgresql #### Postgresql
- [Project page](https://github.com/postgres/postgres/blob/master/README) - [Project page](https://github.com/postgres/postgres/blob/master/README)
- Configuration: [Omnibus][postgres-omnibus], [Charts][postgres-charts] - Configuration: [Omnibus][postgres-omnibus], [Charts][postgres-charts], [Source][postgres-source]
- Layer: Core Service (Data) - Layer: Core Service (Data)
- Process: `postgresql`
GitLab packages the popular Database to provide storage for Application meta data and user information. GitLab packages the popular Database to provide storage for Application meta data and user information.
...@@ -335,6 +343,7 @@ GitLab packages the popular Database to provide storage for Application meta dat ...@@ -335,6 +343,7 @@ GitLab packages the popular Database to provide storage for Application meta dat
- [Project page](https://github.com/wrouesnel/postgres_exporter/blob/master/README.md) - [Project page](https://github.com/wrouesnel/postgres_exporter/blob/master/README.md)
- Configuration: [Omnibus][postgres-exporter-omnibus], [Charts][postgres-exporter-charts] - Configuration: [Omnibus][postgres-exporter-omnibus], [Charts][postgres-exporter-charts]
- Layer: Monitoring - Layer: Monitoring
- Process: `postgres-exporter`
[Postgres-exporter](https://github.com/wrouesnel/postgres_exporter) is the community provided Prometheus exporter that will deliver data about Postgres to Prometheus for use in Grafana Dashboards. [Postgres-exporter](https://github.com/wrouesnel/postgres_exporter) is the community provided Prometheus exporter that will deliver data about Postgres to Prometheus for use in Grafana Dashboards.
...@@ -343,14 +352,16 @@ GitLab packages the popular Database to provide storage for Application meta dat ...@@ -343,14 +352,16 @@ GitLab packages the popular Database to provide storage for Application meta dat
- [Project page](https://github.com/prometheus/prometheus/blob/master/README.md) - [Project page](https://github.com/prometheus/prometheus/blob/master/README.md)
- Configuration: [Omnibus][prometheus-omnibus], [Charts][prometheus-charts] - Configuration: [Omnibus][prometheus-omnibus], [Charts][prometheus-charts]
- Layer: Monitoring - Layer: Monitoring
- Process: `prometheus`
Prometheus is a time-series tool that helps GitLab administrators expose metrics about the individual processes used to provide GitLab the service. Prometheus is a time-series tool that helps GitLab administrators expose metrics about the individual processes used to provide GitLab the service.
#### Redis #### Redis
- [Project page](https://github.com/antirez/redis/blob/unstable/README.md) - [Project page](https://github.com/antirez/redis/blob/unstable/README.md)
- Configuration: [Omnibus][redis-omnibus], [Charts][redis-charts] - Configuration: [Omnibus][redis-omnibus], [Charts][redis-charts], [Source][redis-source]
- Layer: Core Service (Data) - Layer: Core Service (Data)
- Process: `redis`
Redis is packaged to provide a place to store: Redis is packaged to provide a place to store:
...@@ -363,13 +374,14 @@ Redis is packaged to provide a place to store: ...@@ -363,13 +374,14 @@ Redis is packaged to provide a place to store:
- [Project page](https://github.com/oliver006/redis_exporter/blob/master/README.md) - [Project page](https://github.com/oliver006/redis_exporter/blob/master/README.md)
- Configuration: [Omnibus][redis-exporter-omnibus], [Charts][redis-exporter-charts] - Configuration: [Omnibus][redis-exporter-omnibus], [Charts][redis-exporter-charts]
- Layer: Monitoring - Layer: Monitoring
- Process: `redis-exporter`
[Redis Exporter](https://github.com/oliver006/redis_exporter) is designed to give specific metrics about the Redis process to Prometheus so that we can graph these metrics in Grafana. [Redis Exporter](https://github.com/oliver006/redis_exporter) is designed to give specific metrics about the Redis process to Prometheus so that we can graph these metrics in Grafana.
#### Registry #### Registry
- [Project page](https://github.com/docker/distribution/blob/master/README.md) - [Project page](https://github.com/docker/distribution/blob/master/README.md)
- Configuration: [Omnibus][registry-omnibus], [Charts][registry-charts] - Configuration: [Omnibus][registry-omnibus], [Charts][registry-charts], [Source][registry-source], [GDK][registry-gdk]
- Layer: Core Service (Processor) - Layer: Core Service (Processor)
The registry is what users use to store their own Docker images. The bundled The registry is what users use to store their own Docker images. The bundled
...@@ -385,7 +397,7 @@ An external registry can also be configured to use GitLab as an auth endpoint. ...@@ -385,7 +397,7 @@ An external registry can also be configured to use GitLab as an auth endpoint.
#### Sentry #### Sentry
- [Project page](https://github.com/getsentry/sentry/blob/master/README.rst) - [Project page](https://github.com/getsentry/sentry/blob/master/README.rst)
- Configuration: [Omnibus][sentry-omnibus], [Charts][sentry-charts] - Configuration: [Omnibus][sentry-omnibus], [Charts][sentry-charts], [Source][gitlab-yml], [GDK][gitlab-yml]
- Layer: Monitoring - Layer: Monitoring
Sentry fundamentally is a service that helps you monitor and fix crashes in realtime. The server is in Python, but it contains a full API for sending events from any language, in any application. Sentry fundamentally is a service that helps you monitor and fix crashes in realtime. The server is in Python, but it contains a full API for sending events from any language, in any application.
...@@ -393,37 +405,39 @@ Sentry fundamentally is a service that helps you monitor and fix crashes in real ...@@ -393,37 +405,39 @@ Sentry fundamentally is a service that helps you monitor and fix crashes in real
#### Sidekiq #### Sidekiq
- [Project page](https://github.com/mperham/sidekiq/blob/master/README.md) - [Project page](https://github.com/mperham/sidekiq/blob/master/README.md)
- Configuration: [Omnibus][sidekiq-omnibus], [Charts][sidekiq-charts] - Configuration: [Omnibus][sidekiq-omnibus], [Charts][sidekiq-charts], [Source][gitlab-yml], [GDK][gitlab-yml]
- Layer: Core Service (Processor) - Layer: Core Service (Processor)
- Process: `sidekiq`
Sidekiq is a Ruby background job processor that pulls jobs from the redis queue and processes them. Background jobs allow GitLab to provide a faster request/response cycle by moving work into the background. Sidekiq is a Ruby background job processor that pulls jobs from the redis queue and processes them. Background jobs allow GitLab to provide a faster request/response cycle by moving work into the background.
#### Unicorn #### Unicorn
- [Project page](https://gitlab.com/gitlab-org/gitlab-ee/blob/master/README.md) - [Project page](https://gitlab.com/gitlab-org/gitlab-ee/blob/master/README.md)
- Configuration: [Omnibus][unicorn-omnibus], [Charts][unicorn-charts] - Configuration: [Omnibus][unicorn-omnibus], [Charts][unicorn-charts], [Source][unicorn-source], [GDK][gitlab-yml]
- Layer: Core Service (Processor) - Layer: Core Service (Processor)
- Process: `unicorn`
[Unicorn](https://bogomips.org/unicorn/) is a Ruby application server that is used to run the core Rails Application that provides the user facing features in GitLab. Often process output you will see this as `bundle` or `config.ru` depending on the GitLab version. [Unicorn](https://bogomips.org/unicorn/) is a Ruby application server that is used to run the core Rails Application that provides the user facing features in GitLab. Often process output you will see this as `bundle` or `config.ru` depending on the GitLab version.
#### LDAP Authentication #### LDAP Authentication
- Configuration: [Omnibus][ldap-omnibus], [Charts][ldap-charts] - Configuration: [Omnibus][ldap-omnibus], [Charts][ldap-charts], [Source][gitlab-yml], [GDK][ldap-gdk]
- Layer: Core Service (Processor) - Layer: Core Service (Processor)
#### Outbound Email #### Outbound Email
- Configuration: [Omnibus][outbound-email-omnibus], [Charts][outbound-email-charts] - Configuration: [Omnibus][outbound-email-omnibus], [Charts][outbound-email-charts], [Source][gitlab-yml], [GDK][gitlab-yml]
- Layer: Core Service (Processor) - Layer: Core Service (Processor)
#### Inbound Email #### Inbound Email
- Configuration: [Omnibus][inbound-email-omnibus], [Charts][inbound-email-charts] - Configuration: [Omnibus][inbound-email-omnibus], [Charts][inbound-email-charts], [Source][gitlab-yml], [GDK][gitlab-yml]
- Layer: Core Service (Processor) - Layer: Core Service (Processor)
#### Kubernetes Cluster Apps #### Kubernetes Cluster Apps
- Configuration: [Omnibus][managed-k8s-apps], [Charts][managed-k8s-apps] - Configuration: [Omnibus][managed-k8s-apps], [Charts][managed-k8s-apps], [Source][managed-k8s-apps], [GDK][managed-k8s-apps]
- Layer: Core Service (Processor) - Layer: Core Service (Processor)
GitLab provides [GitLab Managed Apps](https://docs.gitlab.com/ee/user/project/clusters/#installing-applications), a one-click install for various applications which can be added directly to your configured cluster. These applications are needed for Review Apps and deployments when using Auto DevOps. You can install them after you create a cluster. GitLab provides [GitLab Managed Apps](https://docs.gitlab.com/ee/user/project/clusters/#installing-applications), a one-click install for various applications which can be added directly to your configured cluster. These applications are needed for Review Apps and deployments when using Auto DevOps. You can install them after you create a cluster.
...@@ -597,24 +611,36 @@ We've also detailed [our architecture of GitLab.com](https://about.gitlab.com/ha ...@@ -597,24 +611,36 @@ We've also detailed [our architecture of GitLab.com](https://about.gitlab.com/ha
[alertmanager-charts]: https://github.com/helm/charts/tree/master/stable/prometheus [alertmanager-charts]: https://github.com/helm/charts/tree/master/stable/prometheus
[nginx-omnibus]: https://docs.gitlab.com/omnibus/settings/nginx.html [nginx-omnibus]: https://docs.gitlab.com/omnibus/settings/nginx.html
[nginx-charts]: https://docs.gitlab.com/charts/charts/nginx/index.html [nginx-charts]: https://docs.gitlab.com/charts/charts/nginx/index.html
[nginx-source]: https://docs.gitlab.com/ee/install/installation.html#9-nginx
[unicorn-omnibus]: https://docs.gitlab.com/omnibus/settings/unicorn.html [unicorn-omnibus]: https://docs.gitlab.com/omnibus/settings/unicorn.html
[unicorn-charts]: https://docs.gitlab.com/charts/charts/gitlab/unicorn/index.html [unicorn-charts]: https://docs.gitlab.com/charts/charts/gitlab/unicorn/index.html
[unicorn-source]: https://docs.gitlab.com/ee/install/installation.html#configure-it
[gitlab-yml]: https://gitlab.com/gitlab-org/gitlab-ce/blob/master/config/gitlab.yml.example
[sidekiq-omnibus]: https://gitlab.com/gitlab-org/omnibus-gitlab/blob/master/files/gitlab-config-template/gitlab.rb.template [sidekiq-omnibus]: https://gitlab.com/gitlab-org/omnibus-gitlab/blob/master/files/gitlab-config-template/gitlab.rb.template
[sidekiq-charts]: https://docs.gitlab.com/charts/charts/gitlab/sidekiq/index.html [sidekiq-charts]: https://docs.gitlab.com/charts/charts/gitlab/sidekiq/index.html
[gitaly-omnibus]: https://docs.gitlab.com/ee/administration/gitaly/ [gitaly-omnibus]: https://docs.gitlab.com/ee/administration/gitaly/
[gitaly-charts]: https://docs.gitlab.com/charts/charts/gitlab/gitaly/index.html [gitaly-charts]: https://docs.gitlab.com/charts/charts/gitlab/gitaly/index.html
[gitaly-source]: https://docs.gitlab.com/ee/install/installation.html#install-gitaly
[workhorse-omnibus]: https://gitlab.com/gitlab-org/omnibus-gitlab/blob/master/files/gitlab-config-template/gitlab.rb.template [workhorse-omnibus]: https://gitlab.com/gitlab-org/omnibus-gitlab/blob/master/files/gitlab-config-template/gitlab.rb.template
[workhorse-charts]: https://docs.gitlab.com/charts/charts/gitlab/unicorn/index.html [workhorse-charts]: https://docs.gitlab.com/charts/charts/gitlab/unicorn/index.html
[workhorse-source]: https://docs.gitlab.com/ee/install/installation.html#install-gitlab-workhorse
[shell-omnibus]: https://gitlab.com/gitlab-org/omnibus-gitlab/blob/master/files/gitlab-config-template/gitlab.rb.template [shell-omnibus]: https://gitlab.com/gitlab-org/omnibus-gitlab/blob/master/files/gitlab-config-template/gitlab.rb.template
[shell-charts]: https://docs.gitlab.com/charts/charts/gitlab/gitlab-shell/index.html [shell-charts]: https://docs.gitlab.com/charts/charts/gitlab/gitlab-shell/index.html
[shell-source]: https://docs.gitlab.com/ee/install/installation.html#install-gitlab-shell
[pages-omnibus]: https://docs.gitlab.com/ee/administration/pages/ [pages-omnibus]: https://docs.gitlab.com/ee/administration/pages/
[pages-charts]: https://gitlab.com/charts/gitlab/issues/37 [pages-charts]: https://gitlab.com/charts/gitlab/issues/37
[pages-source]: https://docs.gitlab.com/ee/install/installation.html#install-gitlab-pages
[pages-gdk]: https://gitlab.com/gitlab-org/gitlab-development-kit/blob/master/doc/howto/pages.md
[registry-omnibus]: https://docs.gitlab.com/ee/administration/container_registry.html#container-registry-domain-configuration [registry-omnibus]: https://docs.gitlab.com/ee/administration/container_registry.html#container-registry-domain-configuration
[registry-charts]: https://docs.gitlab.com/charts/charts/registry/index.html [registry-charts]: https://docs.gitlab.com/charts/charts/registry/index.html
[registry-source]: https://docs.gitlab.com/ee/administration/container_registry.html#enable-the-container-registry
[registry-gdk]: https://gitlab.com/gitlab-org/gitlab-development-kit/blob/master/doc/howto/registry.md
[redis-omnibus]: https://docs.gitlab.com/omnibus/settings/redis.html [redis-omnibus]: https://docs.gitlab.com/omnibus/settings/redis.html
[redis-charts]: https://docs.gitlab.com/charts/charts/redis/index.html [redis-charts]: https://docs.gitlab.com/charts/charts/redis/index.html
[redis-source]: https://docs.gitlab.com/ee/install/installation.html#7-redis
[postgres-omnibus]: https://docs.gitlab.com/omnibus/settings/database.html [postgres-omnibus]: https://docs.gitlab.com/omnibus/settings/database.html
[postgres-charts]: https://github.com/helm/charts/tree/master/stable/postgresql [postgres-charts]: https://github.com/helm/charts/tree/master/stable/postgresql
[postgres-source]: https://docs.gitlab.com/ee/install/installation.html#6-database
[pgbouncer-omnibus]: https://docs.gitlab.com/ee/administration/high_availability/pgbouncer.html [pgbouncer-omnibus]: https://docs.gitlab.com/ee/administration/high_availability/pgbouncer.html
[pgbouncer-charts]: https://docs.gitlab.com/charts/installation/deployment.html#postgresql [pgbouncer-charts]: https://docs.gitlab.com/charts/installation/deployment.html#postgresql
[consul-omnibus]: https://docs.gitlab.com/ee/administration/high_availability/consul.html [consul-omnibus]: https://docs.gitlab.com/ee/administration/high_availability/consul.html
...@@ -627,6 +653,8 @@ We've also detailed [our architecture of GitLab.com](https://about.gitlab.com/ha ...@@ -627,6 +653,8 @@ We've also detailed [our architecture of GitLab.com](https://about.gitlab.com/ha
[sentry-charts]: https://gitlab.com/charts/gitlab/issues/1319 [sentry-charts]: https://gitlab.com/charts/gitlab/issues/1319
[jaeger-omnibus]: https://gitlab.com/gitlab-org/omnibus-gitlab/issues/4104 [jaeger-omnibus]: https://gitlab.com/gitlab-org/omnibus-gitlab/issues/4104
[jaeger-charts]: https://gitlab.com/charts/gitlab/issues/1320 [jaeger-charts]: https://gitlab.com/charts/gitlab/issues/1320
[jaeger-source]: https://docs.gitlab.com/ee/development/distributed_tracing.html#enabling-distributed-tracing
[jaeger-gdk]: https://docs.gitlab.com/ee/development/distributed_tracing.html#using-jaeger-in-the-gitlab-development-kit
[redis-exporter-omnibus]: https://docs.gitlab.com/ee/administration/monitoring/prometheus/redis_exporter.html [redis-exporter-omnibus]: https://docs.gitlab.com/ee/administration/monitoring/prometheus/redis_exporter.html
[redis-exporter-charts]: https://docs.gitlab.com/charts/charts/redis/index.html [redis-exporter-charts]: https://docs.gitlab.com/charts/charts/redis/index.html
[postgres-exporter-omnibus]: https://docs.gitlab.com/ee/administration/monitoring/prometheus/postgres_exporter.html [postgres-exporter-omnibus]: https://docs.gitlab.com/ee/administration/monitoring/prometheus/postgres_exporter.html
...@@ -641,22 +669,32 @@ We've also detailed [our architecture of GitLab.com](https://about.gitlab.com/ha ...@@ -641,22 +669,32 @@ We've also detailed [our architecture of GitLab.com](https://about.gitlab.com/ha
[mattermost-charts]: https://docs.mattermost.com/install/install-mmte-helm-gitlab-helm.html [mattermost-charts]: https://docs.mattermost.com/install/install-mmte-helm-gitlab-helm.html
[minio-omnibus]: https://min.io/download [minio-omnibus]: https://min.io/download
[minio-charts]: https://docs.gitlab.com/charts/charts/minio/index.html [minio-charts]: https://docs.gitlab.com/charts/charts/minio/index.html
[minio-gdk]: https://gitlab.com/gitlab-org/gitlab-development-kit/blob/master/doc/howto/object_storage.md
[runner-omnibus]: https://docs.gitlab.com/runner/ [runner-omnibus]: https://docs.gitlab.com/runner/
[runner-charts]: https://docs.gitlab.com/runner/install/kubernetes.html [runner-charts]: https://docs.gitlab.com/runner/install/kubernetes.html
[runner-source]: https://docs.gitlab.com/runner/
[runner-gdk]: https://gitlab.com/gitlab-org/gitlab-development-kit/blob/master/doc/howto/runner.md
[database-migrations-omnibus]: https://docs.gitlab.com/omnibus/settings/database.html#disabling-automatic-database-migration [database-migrations-omnibus]: https://docs.gitlab.com/omnibus/settings/database.html#disabling-automatic-database-migration
[database-migrations-charts]: https://docs.gitlab.com/charts/charts/gitlab/migrations/index.html [database-migrations-charts]: https://docs.gitlab.com/charts/charts/gitlab/migrations/index.html
[database-migrations-source]: https://docs.gitlab.com/ee/update/upgrading_from_source.html#13-install-libs-migrations-etc
[certificate-management-omnibus]: https://docs.gitlab.com/omnibus/settings/ssl.html [certificate-management-omnibus]: https://docs.gitlab.com/omnibus/settings/ssl.html
[certificate-management-charts]: https://docs.gitlab.com/charts/installation/tls.html [certificate-management-charts]: https://docs.gitlab.com/charts/installation/tls.html
[certificate-management-source]: https://docs.gitlab.com/ee/install/installation.html#using-https
[certificate-management-gdk]: https://gitlab.com/gitlab-org/gitlab-development-kit/blob/master/doc/howto/https.md
[geo-omnibus]: https://docs.gitlab.com/ee/administration/geo/replication/index.html#setup-instructions [geo-omnibus]: https://docs.gitlab.com/ee/administration/geo/replication/index.html#setup-instructions
[geo-charts]: https://gitlab.com/charts/gitlab/issues/8 [geo-charts]: https://gitlab.com/charts/gitlab/issues/8
[geo-gdk]: https://gitlab.com/gitlab-org/gitlab-development-kit/blob/master/doc/howto/geo.md
[ldap-omnibus]: https://docs.gitlab.com/ee/administration/auth/ldap.html [ldap-omnibus]: https://docs.gitlab.com/ee/administration/auth/ldap.html
[ldap-charts]: https://docs.gitlab.com/charts/charts/globals.html#ldap [ldap-charts]: https://docs.gitlab.com/charts/charts/globals.html#ldap
[ldap-gdk]: https://gitlab.com/gitlab-org/gitlab-development-kit/blob/master/doc/howto/ldap.md
[outbound-email-omnibus]: https://docs.gitlab.com/omnibus/settings/smtp.html [outbound-email-omnibus]: https://docs.gitlab.com/omnibus/settings/smtp.html
[outbound-email-charts]: https://docs.gitlab.com/charts/installation/command-line-options.html#outgoing-email-configuration [outbound-email-charts]: https://docs.gitlab.com/charts/installation/command-line-options.html#outgoing-email-configuration
[inbound-email-omnibus]: https://docs.gitlab.com/ee/administration/incoming_email.html [inbound-email-omnibus]: https://docs.gitlab.com/ee/administration/incoming_email.html
[inbound-email-charts]: https://docs.gitlab.com/charts/installation/command-line-options.html#incoming-email-configuration [inbound-email-charts]: https://docs.gitlab.com/charts/installation/command-line-options.html#incoming-email-configuration
[elasticsearch-omnibus]: https://docs.gitlab.com/ee/integration/elasticsearch.html [elasticsearch-omnibus]: https://docs.gitlab.com/ee/integration/elasticsearch.html
[elasticsearch-charts]: https://docs.gitlab.com/ee/integration/elasticsearch.html [elasticsearch-charts]: https://docs.gitlab.com/ee/integration/elasticsearch.html
[elasticsearch-source]: https://docs.gitlab.com/ee/integration/elasticsearch.html
[elasticsearch-gdk]: https://gitlab.com/gitlab-org/gitlab-development-kit/blob/master/doc/howto/elasticsearch.md
[sentry-integration]: https://docs.gitlab.com/ee/user/project/operations/error_tracking.html [sentry-integration]: https://docs.gitlab.com/ee/user/project/operations/error_tracking.html
[jaeger-integration]: https://docs.gitlab.com/ee/user/project/operations/tracing.html [jaeger-integration]: https://docs.gitlab.com/ee/user/project/operations/tracing.html
[managed-k8s-apps]: https://docs.gitlab.com/ee/user/project/clusters/#installing-applications [managed-k8s-apps]: https://docs.gitlab.com/ee/user/project/clusters/#installing-applications
...@@ -27,7 +27,7 @@ are very appreciative of the work done by translators and proofreaders! ...@@ -27,7 +27,7 @@ are very appreciative of the work done by translators and proofreaders!
- Czech - Czech
- Proofreaders needed. - Proofreaders needed.
- Danish - Danish
- Proofreaders needed. - Saederup92 - [GitLab](https://gitlab.com/Saederup92), [Crowdin](https://crowdin.com/profile/Saederup92)
- Dutch - Dutch
- Emily Hendle - [GitLab](https://gitlab.com/pundachan), [Crowdin](https://crowdin.com/profile/pandachan) - Emily Hendle - [GitLab](https://gitlab.com/pundachan), [Crowdin](https://crowdin.com/profile/pandachan)
- Esperanto - Esperanto
......
...@@ -108,11 +108,13 @@ To make sure that indices still fit. You could find great details in: ...@@ -108,11 +108,13 @@ To make sure that indices still fit. You could find great details in:
In order to run the test you can use the following commands: In order to run the test you can use the following commands:
- `rake spec` to run the rspec suite - `bin/rake spec` to run the rspec suite
- `rake karma` to run the karma test suite - `bin/rake spec:unit` to run the only the unit tests
- `rake gitlab:test` to run all the tests - `bin/rake spec:integration` to run the only the integration tests
- `bin/rake spec:system` to run the only the system tests
- `bin/rake karma` to run the karma test suite
Note: `rake spec` takes significant time to pass. Note: `bin/rake spec` takes significant time to pass.
Instead of running full test suite locally you can save a lot of time by running Instead of running full test suite locally you can save a lot of time by running
a single test or directory related to your changes. After you submit merge request a single test or directory related to your changes. After you submit merge request
CI will run full test suite for you. Green CI status in the merge request means CI will run full test suite for you. Green CI status in the merge request means
...@@ -121,6 +123,9 @@ full test suite is passed. ...@@ -121,6 +123,9 @@ full test suite is passed.
Note: You can't run `rspec .` since this will try to run all the `_spec.rb` Note: You can't run `rspec .` since this will try to run all the `_spec.rb`
files it can find, also the ones in `/tmp` files it can find, also the ones in `/tmp`
Note: You can pass RSpec command line options to the `spec:unit`,
`spec:integration`, and `spec:system` tasks, e.g. `bin/rake "spec:unit[--tag ~geo --dry-run]"`.
To run a single test file you can use: To run a single test file you can use:
- `bin/rspec spec/controllers/commit_controller_spec.rb` for a rspec test - `bin/rspec spec/controllers/commit_controller_spec.rb` for a rspec test
......
# Routing
The GitLab backend is written primarily with Rails so it uses [Rails
routing](https://guides.rubyonrails.org/routing.html). Beside Rails best
practices, there are few rules unique to the GitLab application. To
support subgroups, GitLab project and group routes use the wildcard
character to match project and group routes. For example, we might have
a path such as:
/gitlab-com/customer-success/north-america/west/customerA
However, paths can be ambiguous. Consider the following example:
/gitlab-com/edit
It's ambiguous whether there is a subgroup named `edit` or whether
this is a special endpoint to edit the `gitlab-com` group.
To eliminate the ambiguity and to make the backend easier to maintain,
we introduced the `/-/` scope. The purpose of it is to separate group or
project paths from the rest of the routes. Also it helps to reduce the
number of [reserved names](../user/reserved_names.md).
## Global routes
We have a number of global routes. For example:
/-/health
/-/metrics
## Group routes
Every group route must be under the `/-/` scope.
Examples:
gitlab-org/-/edit
gitlab-org/-/activity
gitlab-org/-/security/dashboard
gitlab-org/serverless/-/activity
To achieve that, use the `scope '-'` method.
## Project routes
Every project route must be under the `/-/` scope, except cases where a Git
client or other software requires something different.
Examples:
gitlab-org/gitlab-ce/-/activity
gitlab-org/gitlab-ce/-/jobs/123
gitlab-org/gitlab-ce/-/settings/repository
gitlab-org/serverless/runtimes/-/settings/repository
Currently, only some project routes are placed under the `/-/` scope. However,
you can help us migrate more of them! To migrate project routes:
1. Modify existing routes by adding `-` scope.
1. Add redirects for legacy routes by using `Gitlab::Routing.redirect_legacy_paths`.
1. Create a technical debt issue to remove deprecated routes in later releases.
To get started, see an [example merge request](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/28435).
...@@ -2,19 +2,29 @@ ...@@ -2,19 +2,29 @@
## Test Design ## Test Design
Testing at GitLab is a first class citizen, not an afterthought. It's important we consider the design of our tests Testing at GitLab is a first class citizen, not an afterthought. It's important we consider the design of our tests
as we do the design of our features. as we do the design of our features.
When implementing a feature, we think about developing the right capabilities the right way, which helps us When implementing a feature, we think about developing the right capabilities the right way, which helps us
narrow our scope to a manageable level. When implementing tests for a feature, we must think about developing narrow our scope to a manageable level. When implementing tests for a feature, we must think about developing
the right tests, but then cover _all_ the important ways the test may fail, which can quickly widen our scope to the right tests, but then cover _all_ the important ways the test may fail, which can quickly widen our scope to
a level that is difficult to manage. a level that is difficult to manage.
Test heuristics can help solve this problem. They concisely address many of the common ways bugs Test heuristics can help solve this problem. They concisely address many of the common ways bugs
manifest themselves within our code. When designing our tests, take time to review known test heuristics to inform manifest themselves within our code. When designing our tests, take time to review known test heuristics to inform
our test design. We can find some helpful heuristics documented in the Handbook in the our test design. We can find some helpful heuristics documented in the Handbook in the
[Test Design](https://about.gitlab.com/handbook/engineering/quality/guidelines/test-engineering/test-design/) section. [Test Design](https://about.gitlab.com/handbook/engineering/quality/guidelines/test-engineering/test-design/) section.
## Run tests against MySQL
By default, tests are only run againts PostgreSQL, but you can run them on
demand against MySQL by following one of the following conventions:
| Convention | Valid example |
|:----------------------|:-----------------------------|
| Include `mysql` in your branch name | `enhance-mysql-support` |
| Include `[run mysql]` in your commit message | `Fix MySQL support<br><br>[run mysql]` |
## Test speed ## Test speed
GitLab has a massive test suite that, without [parallelization], can take hours GitLab has a massive test suite that, without [parallelization], can take hours
...@@ -184,11 +194,11 @@ instead of 30+ seconds in case of a regular `spec_helper`. ...@@ -184,11 +194,11 @@ instead of 30+ seconds in case of a regular `spec_helper`.
### `let` variables ### `let` variables
GitLab's RSpec suite has made extensive use of `let`(along with it strict, non-lazy GitLab's RSpec suite has made extensive use of `let`(along with it strict, non-lazy
version `let!`) variables to reduce duplication. However, this sometimes [comes at the cost of clarity][lets-not], version `let!`) variables to reduce duplication. However, this sometimes [comes at the cost of clarity][lets-not],
so we need to set some guidelines for their use going forward: so we need to set some guidelines for their use going forward:
- `let!` variables are preferable to instance variables. `let` variables - `let!` variables are preferable to instance variables. `let` variables
are preferable to `let!` variables. Local variables are preferable to are preferable to `let!` variables. Local variables are preferable to
`let` variables. `let` variables.
- Use `let` to reduce duplication throughout an entire spec file. - Use `let` to reduce duplication throughout an entire spec file.
- Don't use `let` to define variables used by a single test; define them as - Don't use `let` to define variables used by a single test; define them as
...@@ -199,8 +209,8 @@ so we need to set some guidelines for their use going forward: ...@@ -199,8 +209,8 @@ so we need to set some guidelines for their use going forward:
- Try to avoid overriding the definition of one `let` variable with another. - Try to avoid overriding the definition of one `let` variable with another.
- Don't define a `let` variable that's only used by the definition of another. - Don't define a `let` variable that's only used by the definition of another.
Use a helper method instead. Use a helper method instead.
- `let!` variables should be used only in case if strict evaluation with defined - `let!` variables should be used only in case if strict evaluation with defined
order is required, otherwise `let` will suffice. Remember that `let` is lazy and won't order is required, otherwise `let` will suffice. Remember that `let` is lazy and won't
be evaluated until it is referenced. be evaluated until it is referenced.
[lets-not]: https://robots.thoughtbot.com/lets-not [lets-not]: https://robots.thoughtbot.com/lets-not
......
...@@ -4,12 +4,14 @@ ...@@ -4,12 +4,14 @@
_This diagram demonstrates the relative priority of each test type we use. `e2e` stands for end-to-end._ _This diagram demonstrates the relative priority of each test type we use. `e2e` stands for end-to-end._
As of 2019-04-16, we have the following distribution of tests per level: As of 2019-05-01, we have the following distribution of tests per level:
- 67 black-box tests at the system level (aka end-to-end or QA tests) in CE, 98 in EE. This represents 0.3% of all the CE tests (0.3% in EE). | Test level | Community Edition | Enterprise Edition | Community + Enterprise Edition |
- 5,457 white-box tests at the system level (aka system or feature tests) in CE, 6,585 in EE. This represents 24.6% of all the CE tests (20.3% in EE). | --------- | ---------- | -------------- | ----- |
- 8,298 integration tests in CE, 10,633 in EE: 0.3% of all the CE tests (0.3% in EE). This represents 37.2% of all the CE tests (32.8% in EE). | Black-box tests at the system level (aka end-to-end or QA tests) | 68 (0.14%) | 31 (0.2%) | 99 (0.17%) |
- 8,403 unit tests in CE, 15,090 in EE: 0.3% of all the CE tests (0.3% in EE). This represents 37.8% of all the CE tests (46.6% in EE). | White-box tests at the system level (aka system or feature tests) | 5,471 (11.9%) | 969 (7.4%) | 6440 (10.9%) |
| Integration tests | 8,333 (18.2%) | 2,244 (17.2%) | 10,577 (17.9%) |
| Unit tests | 32,031 (69.7%) | 9,778 (75.1%) | 41,809 (71%) |
## Unit tests ## Unit tests
......
# SalesForce OmniAuth Provider # Salesforce OmniAuth Provider
You can integrate your GitLab instance with [SalesForce](https://www.salesforce.com/) to enable users to login to your GitLab instance with their SalesForce account. You can integrate your GitLab instance with [Salesforce](https://www.salesforce.com/) to enable users to log in to your GitLab instance with their Salesforce account.
## Create SalesForce Application ## Create a Salesforce Connected App
To enable SalesForce OmniAuth provider, you must use SalesForce's credentials for your GitLab instance. To enable Salesforce OmniAuth provider, you must use Salesforce's credentials for your GitLab instance.
To get the credentials (a pair of Client ID and Client Secret), you must register an application on SalesForces. To get the credentials (a pair of Client ID and Client Secret), you must [create a Connected App](https://help.salesforce.com/articleView?id=connected_app_create.htm&type=5) on Salesforce.
1. Sign in to [SalesForce](https://www.salesforce.com/). 1. Sign in to [Salesforce](https://login.salesforce.com/).
1. Navigate to **Platform Tools/Apps/App Manager** and click on **New Connected App**. 1. In Setup, enter `App Manager` in the Quick Find box, click **App Manager**, then click **New Connected App**.
1. Fill in the application details into the following fields: 1. Fill in the application details into the following fields:
- **Connected App Name** and **API Name**: Set to any value but consider something like `<Organization>'s GitLab`, `<Your Name>'s GitLab`, or something else that is descriptive. - **Connected App Name** and **API Name**: Set to any value but consider something like `<Organization>'s GitLab`, `<Your Name>'s GitLab`, or something else that is descriptive.
- **Contact Email**: Enter the contact email for Salesforce to use when contacting you or your support team.
- **Description**: Description for the application. - **Description**: Description for the application.
![SalesForce App Details](img/salesforce_app_details.png) ![Salesforce App Details](img/salesforce_app_details.png)
1. Select **API (Enable OAuth Settings)** and click on **Enable OAuth Settings**. 1. Select **API (Enable OAuth Settings)** and click on **Enable OAuth Settings**.
1. Fill in the application details into the following fields: 1. Fill in the application details into the following fields:
- **Callback URL**: The call callback URL. For example, `https://gitlab.example.com/users/auth/salesforce/callback`. - **Callback URL**: The callback URL of your GitLab installation. For example, `https://gitlab.example.com/users/auth/salesforce/callback`.
- **Selected OAuth Scopes**: Move **Access your basic information (id, profile, email, address, phone)** and **Allow access to your unique identifier (openid)** to the right column. - **Selected OAuth Scopes**: Move **Access your basic information (id, profile, email, address, phone)** and **Allow access to your unique identifier (openid)** to the right column.
![SalesForce Oauth App Details](img/salesforce_oauth_app_details.png) ![Salesforce Oauth App Details](img/salesforce_oauth_app_details.png)
1. Click **Save**. 1. Click **Save**.
1. On your GitLab server, open the configuration file. 1. On your GitLab server, open the configuration file.
...@@ -63,17 +64,16 @@ To get the credentials (a pair of Client ID and Client Secret), you must registe ...@@ -63,17 +64,16 @@ To get the credentials (a pair of Client ID and Client Secret), you must registe
app_secret: 'SALESFORCE_CLIENT_SECRET' app_secret: 'SALESFORCE_CLIENT_SECRET'
} }
``` ```
1. Change `SALESFORCE_CLIENT_ID` to the Consumer Key from the SalesForce connected application page. 1. Change `SALESFORCE_CLIENT_ID` to the Consumer Key from the Salesforce connected application page.
1. Change `SALESFORCE_CLIENT_SECRET` to the Consumer Secret from the SalesForce connected application page. 1. Change `SALESFORCE_CLIENT_SECRET` to the Consumer Secret from the Salesforce connected application page.
![SalesForce App Secret Details](img/salesforce_app_secret_details.png) ![Salesforce App Secret Details](img/salesforce_app_secret_details.png)
1. Save the configuration file. 1. Save the configuration file.
1. [Reconfigure GitLab]( ../administration/restart_gitlab.md#omnibus-gitlab-reconfigure ) or [restart GitLab]( ../administration/restart_gitlab.md#installations-from-source ) for the changes to take effect if you 1. [Reconfigure GitLab]( ../administration/restart_gitlab.md#omnibus-gitlab-reconfigure ) or [restart GitLab]( ../administration/restart_gitlab.md#installations-from-source ) for the changes to take effect if you installed GitLab via Omnibus or from source respectively.
installed GitLab via Omnibus or from source respectively.
On the sign in page, there should now be a SalesForce icon below the regular sign in form. On the sign in page, there should now be a Salesforce icon below the regular sign in form.
Click the icon to begin the authentication process. SalesForce will ask the user to sign in and authorize the GitLab application. Click the icon to begin the authentication process. Salesforce will ask the user to sign in and authorize the GitLab application.
If everything goes well, the user will be returned to GitLab and will be signed in. If everything goes well, the user will be returned to GitLab and will be signed in.
NOTE: **Note:** NOTE: **Note:**
GitLab requires the email address of each new user. Once the user is logged in using SalesForce, GitLab will redirect the user to the profile page where they will have to provide the email and verify the email. GitLab requires the email address of each new user. Once the user is logged in using Salesforce, GitLab will redirect the user to the profile page where they will have to provide the email and verify the email.
\ No newline at end of file
...@@ -16,7 +16,7 @@ ...@@ -16,7 +16,7 @@
The new folder needs to have git user ownership and read/write/execute access for git user and its group: The new folder needs to have git user ownership and read/write/execute access for git user and its group:
``` ```
sudo -u git mkdir /var/opt/gitlab/git-data/repository-import-<date>/new_group sudo -u git mkdir -p /var/opt/gitlab/git-data/repository-import-<date>/new_group
``` ```
### Copy your bare repositories inside this newly created folder: ### Copy your bare repositories inside this newly created folder:
......
---
type: reference
---
# GitLab Admin Area **[CORE ONLY]** # GitLab Admin Area **[CORE ONLY]**
The Admin Area provides a web UI for administering some features of GitLab self-managed instances. The Admin Area provides a web UI for administering some features of GitLab self-managed instances.
...@@ -16,7 +20,7 @@ The Admin Area is made up of the following sections: ...@@ -16,7 +20,7 @@ The Admin Area is made up of the following sections:
| Section | Description | | Section | Description |
|:---------------------------|:---------------------------------------------------------------------------------------------------------------------------------------------------------| |:---------------------------|:---------------------------------------------------------------------------------------------------------------------------------------------------------|
| Overview | View your GitLab [Dashboard](#admin-dashboard), and administer [projects](#administer-projects), users, groups, jobs, runners, and Gitaly servers. | | Overview | View your GitLab [Dashboard](#admin-dashboard), and administer [projects](#administer-projects), [users](#administer-users), groups, jobs, runners, and Gitaly servers. |
| Monitoring | View GitLab system information, and information on background jobs, logs, [health checks](monitoring/health_check.md), request profiles, and audit logs. | | Monitoring | View GitLab system information, and information on background jobs, logs, [health checks](monitoring/health_check.md), request profiles, and audit logs. |
| Messages | Send and manage [broadcast messages](broadcast_messages.md) for your users. | | Messages | Send and manage [broadcast messages](broadcast_messages.md) for your users. |
| System Hooks | Configure [system hooks](../../system_hooks/system_hooks.md) for many events. | | System Hooks | Configure [system hooks](../../system_hooks/system_hooks.md) for many events. |
...@@ -90,3 +94,23 @@ You can combine the filter options. For example, to list only public projects wi ...@@ -90,3 +94,23 @@ You can combine the filter options. For example, to list only public projects wi
1. Click the **Public** tab. 1. Click the **Public** tab.
1. Enter `score` in the **Filter by name...** input box. 1. Enter `score` in the **Filter by name...** input box.
## Administer Users
You can administer all users in the GitLab instance from the Admin Area's Users page.
To access the Users page, go to **Admin Area > Overview > Users**.
Click the **Active**, **Admins**, **2FA Enabled**, or **2FA Disabled**, **External**, or
**Without projects** tab to list only users of that criteria.
For each user, their username, email address, are listed, also the date their account was
created and the date of last activity. To edit a user, click the **Edit** button in that user's
row. To delete the user, or delete the user and their contributions, click the cog dropdown in
that user's row, and select the desired option.
To change the sort order, click the sort dropdown and select the desired order. By default the sort dropdown shows **Name**.
To search for users, enter your criteria in the search field. The user search is case
insensitive, and applies partial matching to name and username. To search for an email address,
you must provide the complete email address.
---
type: reference
---
# Labels administration **[CORE ONLY]** # Labels administration **[CORE ONLY]**
## Default Labels In the Admin Area, you can manage labels for the GitLab instance. For more details, see [Labels](../project/labels.md).
### Define your own default Label Set ## Default Labels
Labels that are created within the Labels view on the Admin Dashboard will be automatically added to each new project. Labels created in the Admin Area become available to each _new_ project.
![Default label set](img/admin_labels.png) ![Default label set](img/admin_labels.png)
<!-- ## Troubleshooting
Include any troubleshooting steps that you can foresee. If you know beforehand what issues
one might have when setting this up, or when something is changed, or on upgrading, it's
important to describe those, too. Think of things that may go wrong and include them here.
This is important to minimize requests for support, and to avoid doc comments with
questions that you know someone might ask.
Each scenario can be a third-level heading, e.g. `### Getting error message X`.
If you have none to add when creating a doc, leave this section in place
but commented out to help encourage others to add to it in the future. -->
---
type: howto
---
# Activate all GitLab Enterprise Edition functionality with a license **[STARTER ONLY]** # Activate all GitLab Enterprise Edition functionality with a license **[STARTER ONLY]**
To activate all GitLab Enterprise Edition (EE) functionality, you need to upload To activate all GitLab Enterprise Edition (EE) functionality, you need to upload
...@@ -108,3 +112,15 @@ but only the latest license will be used as the active license. ...@@ -108,3 +112,15 @@ but only the latest license will be used as the active license.
[free trial]: https://about.gitlab.com/free-trial/ [free trial]: https://about.gitlab.com/free-trial/
[pricing]: https://about.gitlab.com/pricing/ [pricing]: https://about.gitlab.com/pricing/
<!-- ## Troubleshooting
Include any troubleshooting steps that you can foresee. If you know beforehand what issues
one might have when setting this up, or when something is changed, or on upgrading, it's
important to describe those, too. Think of things that may go wrong and include them here.
This is important to minimize requests for support, and to avoid doc comments with
questions that you know someone might ask.
Each scenario can be a third-level heading, e.g. `### Getting error message X`.
If you have none to add when creating a doc, leave this section in place
but commented out to help encourage others to add to it in the future. -->
\ No newline at end of file
# Health Check ---
type: concepts, howto
---
> **Notes:** # Health Check
> NOTE: **Note:**
>
> - Liveness and readiness probes were [introduced][ce-10416] in GitLab 9.1. > - Liveness and readiness probes were [introduced][ce-10416] in GitLab 9.1.
> - The `health_check` endpoint was [introduced][ce-3888] in GitLab 8.8 and was > - The `health_check` endpoint was [introduced][ce-3888] in GitLab 8.8 and was
> be deprecated in GitLab 9.1. > be deprecated in GitLab 9.1.
> - [Access token](#access-token-deprecated) has been deprecated in GitLab 9.4 > - [Access token](#access-token-deprecated) has been deprecated in GitLab 9.4
> in favor of [IP whitelist](#ip-whitelist) > in favor of [IP whitelist](#ip-whitelist).
GitLab provides liveness and readiness probes to indicate service health and GitLab provides liveness and readiness probes to indicate service health and
reachability to required services. These probes report on the status of the reachability to required services. These probes report on the status of the
...@@ -17,8 +21,7 @@ traffic until the system is ready or restart the container as needed. ...@@ -17,8 +21,7 @@ traffic until the system is ready or restart the container as needed.
## IP whitelist ## IP whitelist
To access monitoring resources, the requesting client IP needs to be included in a whitelist. To access monitoring resources, the requesting client IP needs to be included in a whitelist.
For details, see [how to add IPs to a whitelist for the monitoring endpoints](../../../administration/monitoring/ip_whitelist.md).
[Read how to add IPs to a whitelist for the monitoring endpoints][admin].
## Using the endpoints ## Using the endpoints
...@@ -87,9 +90,8 @@ will return a valid successful HTTP status code, and a `success` message. ...@@ -87,9 +90,8 @@ will return a valid successful HTTP status code, and a `success` message.
## Access token (Deprecated) ## Access token (Deprecated)
>**Note:** > NOTE: **Note:**
Access token has been deprecated in GitLab 9.4 > Access token has been deprecated in GitLab 9.4 in favor of [IP whitelist](#ip-whitelist).
in favor of [IP whitelist](#ip-whitelist)
An access token needs to be provided while accessing the probe endpoints. The current An access token needs to be provided while accessing the probe endpoints. The current
accepted token can be found under the **Admin area ➔ Monitoring ➔ Health check** accepted token can be found under the **Admin area ➔ Monitoring ➔ Health check**
...@@ -103,10 +105,21 @@ The access token can be passed as a URL parameter: ...@@ -103,10 +105,21 @@ The access token can be passed as a URL parameter:
https://gitlab.example.com/-/readiness?token=ACCESS_TOKEN https://gitlab.example.com/-/readiness?token=ACCESS_TOKEN
``` ```
<!-- ## Troubleshooting
Include any troubleshooting steps that you can foresee. If you know beforehand what issues
one might have when setting this up, or when something is changed, or on upgrading, it's
important to describe those, too. Think of things that may go wrong and include them here.
This is important to minimize requests for support, and to avoid doc comments with
questions that you know someone might ask.
Each scenario can be a third-level heading, e.g. `### Getting error message X`.
If you have none to add when creating a doc, leave this section in place
but commented out to help encourage others to add to it in the future. -->
[ce-10416]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/10416 [ce-10416]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/10416
[ce-3888]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/3888 [ce-3888]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/3888
[pingdom]: https://www.pingdom.com [pingdom]: https://www.pingdom.com
[nagios-health]: https://nagios-plugins.org/doc/man/check_http.html [nagios-health]: https://nagios-plugins.org/doc/man/check_http.html
[newrelic-health]: https://docs.newrelic.com/docs/alerts/alert-policies/downtime-alerts/availability-monitoring [newrelic-health]: https://docs.newrelic.com/docs/alerts/alert-policies/downtime-alerts/availability-monitoring
[kubernetes]: https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-probes/ [kubernetes]: https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-probes/
[admin]: ../../../administration/monitoring/ip_whitelist.md \ No newline at end of file
---
type: reference
---
# Account and limit settings # Account and limit settings
## Repository size limit **[STARTER]** ## Repository size limit **[STARTER]**
> [Introduced][ee-740] in [GitLab Enterprise Edition 8.12][ee-8.12]. > [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/740) in [GitLab Enterprise Edition 8.12](https://about.gitlab.com/2016/09/22/gitlab-8-12-released/#limit-project-size-ee).
> Available in [GitLab Starter](https://about.gitlab.com/pricing/).
Repositories within your GitLab instance can grow quickly, especially if you are Repositories within your GitLab instance can grow quickly, especially if you are
using LFS. Their size can grow exponentially and eat up your storage device quite using LFS. Their size can grow exponentially, rapidly consuming available storage.
fast.
In order to avoid this from happening, you can set a hard limit for your To avoid this from happening, you can set a hard limit for your repositories' size.
repositories' size. This limit can be set globally, per group, or per project, This limit can be set globally, per group, or per project, with per project limits
with per project limits taking the highest priority. taking the highest priority.
There are numerous cases where you'll need to set up a limit for repository size. There are numerous use cases where you might set up a limit for repository size.
For instance, consider the following workflow: For instance, consider the following workflow:
1. Your team develops apps which demand large files to be stored in 1. Your team develops apps which require large files to be stored in
the application repository. the application repository.
1. Although you have enabled [Git LFS](../../../workflow/lfs/manage_large_binaries_with_git_lfs.html#git-lfs) 1. Although you have enabled [Git LFS](../../../workflow/lfs/manage_large_binaries_with_git_lfs.md#git-lfs)
to your project, your storage has grown significantly. to your project, your storage has grown significantly.
1. Before you blow your storage limit up, you set up a limit of 10 GB 1. Before you exceed available storage, you set up a limit of 10 GB
per repository. per repository.
### How it works ### How it works
...@@ -42,12 +46,19 @@ subsequent push will be denied. LFS objects, however, can be checked on first ...@@ -42,12 +46,19 @@ subsequent push will be denied. LFS objects, however, can be checked on first
push and **will** be rejected if the sum of their sizes exceeds the maximum push and **will** be rejected if the sum of their sizes exceeds the maximum
allowed repository size. allowed repository size.
For more manually purging the files, read the docs on For details on manually purging files, see [reducing the repository size using Git](../../project/repository/reducing_the_repo_size_using_git.md).
[reducing the repository size using Git][repo-size].
NOTE: **Note:**
For GitLab.com, the repository size limit is 10 GB.
<!-- ## Troubleshooting
> **Note:** Include any troubleshooting steps that you can foresee. If you know beforehand what issues
> For GitLab.com, the repository size limit is 10 GB. one might have when setting this up, or when something is changed, or on upgrading, it's
important to describe those, too. Think of things that may go wrong and include them here.
This is important to minimize requests for support, and to avoid doc comments with
questions that you know someone might ask.
[ee-740]: https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/740 Each scenario can be a third-level heading, e.g. `### Getting error message X`.
[repo-size]: ../../project/repository/reducing_the_repo_size_using_git.md If you have none to add when creating a doc, leave this section in place
[ee-8.12]: https://about.gitlab.com/2016/09/22/gitlab-8-12-released/#limit-project-size-ee but commented out to help encourage others to add to it in the future. -->
...@@ -4,7 +4,7 @@ GitLab Inc. will periodically collect information about your instance in order ...@@ -4,7 +4,7 @@ GitLab Inc. will periodically collect information about your instance in order
to perform various actions. to perform various actions.
All statistics are opt-out, you can enable/disable them from the admin panel All statistics are opt-out, you can enable/disable them from the admin panel
under **Admin area > Settings > Usage statistics**. under **Admin area > Settings > Metrics and profiling > Usage statistics**.
## Version check **[CORE ONLY]** ## Version check **[CORE ONLY]**
......
...@@ -63,7 +63,7 @@ The following table shows which languages, package managers and frameworks are s ...@@ -63,7 +63,7 @@ The following table shows which languages, package managers and frameworks are s
| Javascript | [ESLint security plugin](https://github.com/nodesecurity/eslint-plugin-security) | 11.8 | | Javascript | [ESLint security plugin](https://github.com/nodesecurity/eslint-plugin-security) | 11.8 |
| Node.js | [NodeJsScan](https://github.com/ajinabraham/NodeJsScan) | 11.1 | | Node.js | [NodeJsScan](https://github.com/ajinabraham/NodeJsScan) | 11.1 |
| PHP | [phpcs-security-audit](https://github.com/FloeDesignTechnologies/phpcs-security-audit) | 10.8 | | PHP | [phpcs-security-audit](https://github.com/FloeDesignTechnologies/phpcs-security-audit) | 10.8 |
| Python ([pip](https://pip.pypa.io/en/stable/)) | [bandit](https://github.com/openstack/bandit) | 10.3 | | Python ([pip](https://pip.pypa.io/en/stable/)) | [bandit](https://github.com/PyCQA/bandit) | 10.3 |
| Ruby on Rails | [brakeman](https://brakemanscanner.org) | 10.3 | | Ruby on Rails | [brakeman](https://brakemanscanner.org) | 10.3 |
| Scala ([Ant](https://ant.apache.org/), [Gradle](https://gradle.org/), [Maven](https://maven.apache.org/) and [SBT](https://www.scala-sbt.org/)) | [SpotBugs](https://spotbugs.github.io/) with the [find-sec-bugs](https://find-sec-bugs.github.io/) plugin | 11.0 (SBT) & 11.9 (Ant, Gradle, Maven) | | Scala ([Ant](https://ant.apache.org/), [Gradle](https://gradle.org/), [Maven](https://maven.apache.org/) and [SBT](https://www.scala-sbt.org/)) | [SpotBugs](https://spotbugs.github.io/) with the [find-sec-bugs](https://find-sec-bugs.github.io/) plugin | 11.0 (SBT) & 11.9 (Ant, Gradle, Maven) |
| Typescript | [TSLint config security](https://github.com/webschik/tslint-config-security/) | 11.9 | | Typescript | [TSLint config security](https://github.com/webschik/tslint-config-security/) | 11.9 |
......
# GitLab Managed Apps
GitLab provides **GitLab Managed Apps**, a one-click install for various applications which can
be added directly to your configured cluster. These applications are
needed for [Review Apps](../../ci/review_apps/index.md) and
[deployments](../../ci/environments.md) when using [Auto DevOps](../../topics/autodevops/index.md).
You can install them after you
[create a cluster](../project/clusters/index.md#adding-and-creating-a-new-gke-cluster-via-gitlab).
## Installing applications
Applications managed by GitLab will be installed onto the `gitlab-managed-apps` namespace.
This namespace:
- Is different from the namespace used for project deployments.
- Is created once.
- Has a non-configurable name.
To see a list of available applications to install:
1. For a:
- Project-level cluster, navigate to your project's **Operations > Kubernetes**.
- Group-level cluster, navigate to your group's **Kubernetes** page.
Install Helm first as it's used to install other applications.
NOTE: **Note:**
As of GitLab 11.6, Helm will be upgraded to the latest version supported
by GitLab before installing any of the applications.
The following applications can be installed:
- [Helm](#helm)
- [Ingress](#ingress)
- [Cert-Manager](#cert-manager)
- [Prometheus](#prometheus)
- [GitLab Runner](#gitlab-runner)
- [JupyterHub](#jupyterhub)
- [Knative](#knative)
With the exception of Knative, the applications will be installed in a dedicated
namespace called `gitlab-managed-apps`.
NOTE: **Note:**
Some applications are installable only for a project-level cluster.
Support for installing these applications in a group-level cluster is
planned for future releases.
For updates, see [the issue tracking
progress](https://gitlab.com/gitlab-org/gitlab-ce/issues/51989).
CAUTION: **Caution:**
If you have an existing Kubernetes cluster with Helm already installed,
you should be careful as GitLab cannot detect it. In this case, installing
Helm via the applications will result in the cluster having it twice, which
can lead to confusion during deployments.
### Helm
> - Available for project-level clusters since GitLab 10.2.
> - Available for group-level clusters since GitLab 11.6.
[Helm](https://docs.helm.sh/) is a package manager for Kubernetes and is
required to install all the other applications. It is installed in its
own pod inside the cluster which can run the `helm` CLI in a safe
environment.
### Cert-Manager
> - Available for project-level clusters since GitLab 11.6.
> - Available for group-level clusters since GitLab 11.6.
[Cert-Manager](https://docs.cert-manager.io/en/latest/) is a native
Kubernetes certificate management controller that helps with issuing
certificates. Installing Cert-Manager on your cluster will issue a
certificate by [Let's Encrypt](https://letsencrypt.org/) and ensure that
certificates are valid and up-to-date.
NOTE: **Note:**
The
[stable/cert-manager](https://github.com/helm/charts/tree/master/stable/cert-manager)
chart is used to install this application with a
[`values.yaml`](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/vendor/cert_manager/values.yaml)
file.
### GitLab Runner
> - Available for project-level clusters since GitLab 10.6.
> - Available for group-level clusters since GitLab 11.10.
[GitLab Runner](https://docs.gitlab.com/runner/) is the open source
project that is used to run your jobs and send the results back to
GitLab. It is used in conjunction with [GitLab
CI/CD](../../ci/README.md), the open-source continuous integration
service included with GitLab that coordinates the jobs. When installing
the GitLab Runner via the applications, it will run in **privileged
mode** by default. Make sure you read the [security
implications](../project/clusters/index.md/#security-implications) before doing so.
NOTE: **Note:**
The
[runner/gitlab-runner](https://gitlab.com/charts/gitlab-runner)
chart is used to install this application with a
[`values.yaml`](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/vendor/runner/values.yaml)
file.
### Ingress
> - Available for project-level clusters since GitLab 10.2.
> - Available for group-level clusters since GitLab 11.6.
[Ingress](https://kubernetes.github.io/ingress-nginx/) can provide load
balancing, SSL termination, and name-based virtual hosting. It acts as a
web proxy for your applications and is useful if you want to use [Auto
DevOps] or deploy your own web apps.
NOTE: **Note:**
The
[stable/nginx-ingress](https://github.com/helm/charts/tree/master/stable/nginx-ingress)
chart is used to install this application with a
[`values.yaml`](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/vendor/ingress/values.yaml)
file.
### JupyterHub
> Available for project-level clusters since GitLab 11.0.
[JupyterHub](https://jupyterhub.readthedocs.io/en/stable/) is a
multi-user service for managing notebooks across a team. [Jupyter
Notebooks](https://jupyter-notebook.readthedocs.io/en/latest/) provide a
web-based interactive programming environment used for data analysis,
visualization, and machine learning.
Authentication will be enabled only for [project
members](../project/members/index.md) with [Developer or
higher](../permissions.md) access to the project.
We use a [custom Jupyter
image](https://gitlab.com/gitlab-org/jupyterhub-user-image/blob/master/Dockerfile)
that installs additional useful packages on top of the base Jupyter. You
will also see ready-to-use DevOps Runbooks built with Nurtch's [Rubix library](https://github.com/amit1rrr/rubix).
More information on
creating executable runbooks can be found in [our Nurtch
documentation](../project/clusters/runbooks/index.md#nurtch-executable-runbooks). Note that
Ingress must be installed and have an IP address assigned before
JupyterHub can be installed.
NOTE: **Note:**
The
[jupyter/jupyterhub](https://jupyterhub.github.io/helm-chart/)
chart is used to install this application with a
[`values.yaml`](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/vendor/jupyter/values.yaml)
file.
### Knative
> Available for project-level clusters since GitLab 11.5.
[Knative](https://cloud.google.com/knative) provides a platform to
create, deploy, and manage serverless workloads from a Kubernetes
cluster. It is used in conjunction with, and includes
[Istio](https://istio.io) to provide an external IP address for all
programs hosted by Knative.
You will be prompted to enter a wildcard
domain where your applications will be exposed. Configure your DNS
server to use the external IP address for that domain. For any
application created and installed, they will be accessible as
`<program_name>.<kubernetes_namespace>.<domain_name>`. This will require
your kubernetes cluster to have [RBAC
enabled](../project/clusters/index.md#rbac-cluster-resources).
NOTE: **Note:**
The
[knative/knative](https://storage.googleapis.com/triggermesh-charts)
chart is used to install this application.
### Prometheus
> - Available for project-level clusters since GitLab 10.4.
> - Available for group-level clusters since GitLab 11.11.
[Prometheus](https://prometheus.io/docs/introduction/overview/) is an
open-source monitoring and alerting system useful to supervise your
deployed applications.
NOTE: **Note:**
The
[stable/prometheus](https://github.com/helm/charts/tree/master/stable/prometheus)
chart is used to install this application with a
[`values.yaml`](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/vendor/prometheus/values.yaml)
file.
## Upgrading applications
> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/24789)
in GitLab 11.8.
The applications below can be upgraded.
| Application | GitLab version |
| ----------- | -------------- |
| Runner | 11.8+ |
To upgrade an application:
1. For a:
- Project-level cluster, navigate to your project's **Operations > Kubernetes**.
- Group-level cluster, navigate to your group's **Kubernetes** page.
1. Select your cluster.
1. If an upgrade is available, the **Upgrade** button is displayed. Click the button to upgrade.
NOTE: **Note:**
Upgrades will reset values back to the values built into the `runner`
chart plus the values set by
[`values.yaml`](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/vendor/runner/values.yaml)
## Uninstalling applications
> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/issues/60665) in
> GitLab 11.11.
The applications below can be uninstalled.
| Application | GitLab version | Notes |
| ----------- | -------------- | ----- |
| Prometheus | 11.11+ | All data will be deleted and cannot be restored. |
To uninstall an application:
1. For a:
- Project-level cluster, navigate to your project's **Operations > Kubernetes**.
- Group-level cluster, navigate to your group's **Kubernetes** page.
1. Select your cluster.
1. Click the **Uninstall** button for the application.
Support for uninstalling all applications is planned for progressive rollout.
To follow progress, see [the relevant
epic](https://gitlab.com/groups/gitlab-org/-/epics/1201).
## Troubleshooting applications
Applications can fail with the following error:
```text
Error: remote error: tls: bad certificate
```
To avoid installation errors:
- Before starting the installation of applications, make sure that time is synchronized
between your GitLab server and your Kubernetes cluster.
- Ensure certificates are not out of sync. When installing applications, GitLab expects a new cluster with no previous installation of Helm.
You can confirm that the certificates match via `kubectl`:
```sh
kubectl get configmaps/values-content-configuration-ingress -n gitlab-managed-apps -o \
"jsonpath={.data['cert\.pem']}" | base64 -d > a.pem
kubectl get secrets/tiller-secret -n gitlab-managed-apps -o "jsonpath={.data['ca\.crt']}" | base64 -d > b.pem
diff a.pem b.pem
```
...@@ -12,33 +12,10 @@ your group, enabling you to use the same cluster across multiple projects. ...@@ -12,33 +12,10 @@ your group, enabling you to use the same cluster across multiple projects.
## Installing applications ## Installing applications
GitLab provides a one-click install for various applications that can be GitLab can install and manage some applications in your group-level
added directly to your cluster. cluster. For more information on installing, upgrading, uninstalling,
and troubleshooting applications for your group cluster, see
NOTE: **Note:** [Gitlab Managed Apps](../../clusters/applications.md).
Applications will be installed in a dedicated namespace called
`gitlab-managed-apps`. If you have added an existing Kubernetes cluster
with Tiller already installed, you should be careful as GitLab cannot
detect it. In this event, installing Tiller via the applications will
result in the cluster having it twice. This can lead to confusion during
deployments.
| Application | GitLab version | Description | Helm Chart |
| ----------- | -------------- | ----------- | ---------- |
| [Helm Tiller](https://docs.helm.sh) | 11.6+ | Helm is a package manager for Kubernetes and is required to install all the other applications. It is installed in its own pod inside the cluster which can run the `helm` CLI in a safe environment. | n/a |
| [Ingress](https://kubernetes.io/docs/concepts/services-networking/ingress) | 11.6+ | Ingress can provide load balancing, SSL termination, and name-based virtual hosting. It acts as a web proxy for your applications and is useful if you want to use [Auto DevOps](../../../topics/autodevops/index.md) or deploy your own web apps. | [stable/nginx-ingress](https://github.com/helm/charts/tree/master/stable/nginx-ingress) |
| [Cert-Manager](https://docs.cert-manager.io/en/latest/) | 11.6+ | Cert-Manager is a native Kubernetes certificate management controller that helps with issuing certificates. Installing Cert-Manager on your cluster will issue a certificate by [Let's Encrypt](https://letsencrypt.org/) and ensure that certificates are valid and up-to-date. | [stable/cert-manager](https://github.com/helm/charts/tree/master/stable/cert-manager) |
| [Prometheus](https://prometheus.io/docs/introduction/overview/) | 11.11+ | Prometheus is an open-source monitoring and alerting system useful to supervise your deployed applications. | [stable/prometheus](https://github.com/helm/charts/tree/master/stable/prometheus) |
| [GitLab Runner](https://docs.gitlab.com/runner/) | 11.10+ | GitLab Runner is the open source project that is used to run your jobs and send the results back to GitLab. It is used in conjunction with [GitLab CI/CD](../../../ci/README.md), the open-source continuous integration service included with GitLab that coordinates the jobs. When installing the GitLab Runner via the applications, it will run in **privileged mode** by default. Make sure you read the [security implications](../../project/clusters/index.md#security-implications) before doing so. | [runner/gitlab-runner](https://gitlab.com/charts/gitlab-runner) |
NOTE: **Note:**
Some [cluster
applications](../../project/clusters/index.md#installing-applications)
are installable only for a project-level cluster. Support for installing these
applications in a group-level cluster is planned for future releases. For updates, see:
- Support installing [JupyterHub in group-level
clusters](https://gitlab.com/gitlab-org/gitlab-ce/issues/51989)
## RBAC compatibility ## RBAC compatibility
......
...@@ -15,7 +15,7 @@ SAML SSO for groups is used only as a convenient way to add users and does not s ...@@ -15,7 +15,7 @@ SAML SSO for groups is used only as a convenient way to add users and does not s
## Configuring your Identity Provider ## Configuring your Identity Provider
1. Navigate to the group and click **Settings > SAML SSO**. 1. Navigate to the group and click **Settings > SAML SSO**.
1. Configure your SAML server using the **Assertion consumer service URL** and **Issuer**. See [your identity provider's documentation](#providers) for more details. 1. Configure your SAML server using the **Assertion consumer service URL** and **Issuer**. Alternatively GitLab provides [metadata XML configuration](#metadata-configuration). See [your identity provider's documentation](#providers) for more details.
1. Configure the SAML response to include a NameID that uniquely identifies each user. 1. Configure the SAML response to include a NameID that uniquely identifies each user.
1. Configure required assertions using the [table below](#assertions). 1. Configure required assertions using the [table below](#assertions).
1. Once the identity provider is set up, move on to [configuring GitLab](#configuring-gitlab). 1. Once the identity provider is set up, move on to [configuring GitLab](#configuring-gitlab).
...@@ -50,6 +50,14 @@ GitLab.com uses the SAML NameID to identify users. The NameID element: ...@@ -50,6 +50,14 @@ GitLab.com uses the SAML NameID to identify users. The NameID element:
| First Name | `first_name`, `firstname`, `firstName` | | | First Name | `first_name`, `firstname`, `firstName` | |
| Last Name | `last_name`, `lastname`, `lastName` | | | Last Name | `last_name`, `lastname`, `lastName` | |
## Metadata configuration
GitLab provides metadata XML that can be used to configure your Identity Provider.
1. Navigate to the group and click **Settings > SAML SSO**.
1. Copy the provided **GitLab metadata URL**
1. Follow your Identity Provider's documentation and paste the metadata URL when it is requested.
## Configuring GitLab ## Configuring GitLab
Once you've set up your identity provider to work with GitLab, you'll need to configure GitLab to use it for authentication: Once you've set up your identity provider to work with GitLab, you'll need to configure GitLab to use it for authentication:
......
...@@ -347,111 +347,10 @@ install it manually. ...@@ -347,111 +347,10 @@ install it manually.
## Installing applications ## Installing applications
GitLab provides **GitLab Managed Apps**, a one-click install for various applications which can GitLab can install and manage some applications in your project-level
be added directly to your configured cluster. These applications are cluster. For more information on installing, upgrading, uninstalling,
needed for [Review Apps](../../../ci/review_apps/index.md) and and troubleshooting applications for your project cluster, see
[deployments](../../../ci/environments.md) when using [Auto DevOps](../../../topics/autodevops/index.md). [Gitlab Managed Apps](../../clusters/applications.md).
You can install them after you
[create a cluster](#adding-and-creating-a-new-gke-cluster-via-gitlab).
Applications managed by GitLab will be installed onto the `gitlab-managed-apps` namespace. This differrent
from the namespace used for project deployments. It is only created once and its name is not configurable.
To see a list of available applications to install:
1. Navigate to your project's **Operations > Kubernetes**.
1. Select your cluster.
Install Helm first as it's used to install other applications.
NOTE: **Note:**
As of GitLab 11.6, Helm will be upgraded to the latest version supported
by GitLab before installing any of the applications.
| Application | GitLab version | Description | Helm Chart |
| ----------- | :------------: | ----------- | --------------- |
| [Helm](https://docs.helm.sh/) | 10.2+ | Helm is a package manager for Kubernetes and is required to install all the other applications. It is installed in its own pod inside the cluster which can run the `helm` CLI in a safe environment. | n/a |
| [Ingress](https://kubernetes.io/docs/concepts/services-networking/ingress/) | 10.2+ | Ingress can provide load balancing, SSL termination, and name-based virtual hosting. It acts as a web proxy for your applications and is useful if you want to use [Auto DevOps] or deploy your own web apps. | [stable/nginx-ingress](https://github.com/helm/charts/tree/master/stable/nginx-ingress) |
| [Cert-Manager](https://docs.cert-manager.io/en/latest/) | 11.6+ | Cert-Manager is a native Kubernetes certificate management controller that helps with issuing certificates. Installing Cert-Manager on your cluster will issue a certificate by [Let's Encrypt](https://letsencrypt.org/) and ensure that certificates are valid and up-to-date. | [stable/cert-manager](https://github.com/helm/charts/tree/master/stable/cert-manager) |
| [Prometheus](https://prometheus.io/docs/introduction/overview/) | 10.4+ | Prometheus is an open-source monitoring and alerting system useful to supervise your deployed applications. | [stable/prometheus](https://github.com/helm/charts/tree/master/stable/prometheus) |
| [GitLab Runner](https://docs.gitlab.com/runner/) | 10.6+ | GitLab Runner is the open source project that is used to run your jobs and send the results back to GitLab. It is used in conjunction with [GitLab CI/CD](../../../ci/README.md), the open-source continuous integration service included with GitLab that coordinates the jobs. When installing the GitLab Runner via the applications, it will run in **privileged mode** by default. Make sure you read the [security implications](#security-implications) before doing so. | [runner/gitlab-runner](https://gitlab.com/charts/gitlab-runner) |
| [JupyterHub](http://jupyter.org/) | 11.0+ | [JupyterHub](https://jupyterhub.readthedocs.io/en/stable/) is a multi-user service for managing notebooks across a team. [Jupyter Notebooks](https://jupyter-notebook.readthedocs.io/en/latest/) provide a web-based interactive programming environment used for data analysis, visualization, and machine learning. We use a [custom Jupyter image](https://gitlab.com/gitlab-org/jupyterhub-user-image/blob/master/Dockerfile) that installs additional useful packages on top of the base Jupyter. Authentication will be enabled only for [project members](../members/index.md) with [Developer or higher](../../permissions.md) access to the project. You will also see ready-to-use DevOps Runbooks built with Nurtch's [Rubix library](https://github.com/amit1rrr/rubix). More information on creating executable runbooks can be found in [our Nurtch documentation](runbooks/index.md#nurtch-executable-runbooks). Note that Ingress must be installed and have an IP address assigned before JupyterHub can be installed. | [jupyter/jupyterhub](https://jupyterhub.github.io/helm-chart/) |
| [Knative](https://cloud.google.com/knative) | 11.5+ | Knative provides a platform to create, deploy, and manage serverless workloads from a Kubernetes cluster. It is used in conjunction with, and includes [Istio](https://istio.io) to provide an external IP address for all programs hosted by Knative. You will be prompted to enter a wildcard domain where your applications will be exposed. Configure your DNS server to use the external IP address for that domain. For any application created and installed, they will be accessible as `<program_name>.<kubernetes_namespace>.<domain_name>`. This will require your kubernetes cluster to have [RBAC enabled](#rbac-cluster-resources). | [knative/knative](https://storage.googleapis.com/triggermesh-charts)
With the exception of Knative, the applications will be installed in a dedicated
namespace called `gitlab-managed-apps`.
CAUTION: **Caution:**
If you have an existing Kubernetes cluster with Helm already installed,
you should be careful as GitLab cannot detect it. In this case, installing
Helm via the applications will result in the cluster having it twice, which
can lead to confusion during deployments.
### Upgrading applications
> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/24789)
in GitLab 11.8.
Users can perform a one-click upgrade for the GitLab Runner application,
when there is an upgrade available.
To upgrade the GitLab Runner application:
1. Navigate to your project's **Operations > Kubernetes**.
1. Select your cluster.
1. Click the **Upgrade** button for the Runnner application.
The **Upgrade** button will not be shown if there is no upgrade
available.
NOTE: **Note:**
Upgrades will reset values back to the values built into the `runner`
chart plus the values set by
[`values.yaml`](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/vendor/runner/values.yaml)
### Uninstalling applications
> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/issues/60665) in
> GitLab 11.11.
The applications below can be uninstalled.
| Application | GitLab version | Notes |
| ----------- | -------------- | ----- |
| Prometheus | 11.11+ | All data will be deleted and cannot be restored. |
To uninstall an application:
1. Navigate to your project's **Operations > Kubernetes**.
1. Select your cluster.
1. Click the **Uninstall** button for the application.
Support for uninstalling all applications is planned for progressive rollout.
To follow progress, see [the relevant
epic](https://gitlab.com/groups/gitlab-org/-/epics/1201).
### Troubleshooting applications
Applications can fail with the following error:
```text
Error: remote error: tls: bad certificate
```
To avoid installation errors:
- Before starting the installation of applications, make sure that time is synchronized
between your GitLab server and your Kubernetes cluster.
- Ensure certificates are not out of sync. When installing applications, GitLab expects a new cluster with no previous installation of Helm.
You can confirm that the certificates match via `kubectl`:
```sh
kubectl get configmaps/values-content-configuration-ingress -n gitlab-managed-apps -o \
"jsonpath={.data['cert\.pem']}" | base64 -d > a.pem
kubectl get secrets/tiller-secret -n gitlab-managed-apps -o "jsonpath={.data['ca\.crt']}" | base64 -d > b.pem
diff a.pem b.pem
```
## Getting the external endpoint ## Getting the external endpoint
......
...@@ -35,7 +35,7 @@ discussions, and descriptions: ...@@ -35,7 +35,7 @@ discussions, and descriptions:
| `/label ~label1 ~label2` | Add label(s). Label names can also start without ~ but mixed syntax is not supported. | ✓ | ✓ | | `/label ~label1 ~label2` | Add label(s). Label names can also start without ~ but mixed syntax is not supported. | ✓ | ✓ |
| `/unlabel ~label1 ~label2` | Remove all or specific label(s)| ✓ | ✓ | | `/unlabel ~label1 ~label2` | Remove all or specific label(s)| ✓ | ✓ |
| `/relabel ~label1 ~label2` | Replace label | ✓ | ✓ | | `/relabel ~label1 ~label2` | Replace label | ✓ | ✓ |
| <code>/copy_metadata #issue &#124; !merge_request</code> | Copy labels and milestone from other issue or merge request | ✓ | ✓ | | <code>/copy_metadata #issue &#124; !merge_request</code> | Copy labels and milestone from other issue or merge request in the project | ✓ | ✓ |
| <code>/estimate &lt;1w 3d 2h 14m&gt;</code> | Set time estimate | ✓ | ✓ | | <code>/estimate &lt;1w 3d 2h 14m&gt;</code> | Set time estimate | ✓ | ✓ |
| `/remove_estimate` | Remove time estimate | ✓ | ✓ | | `/remove_estimate` | Remove time estimate | ✓ | ✓ |
| <code>/spend &lt;time(1h 30m &#124; -1h 5m)&gt; &lt;date(YYYY-MM-DD)&gt;</code> | Add or subtract spent time; optionally, specify the date that time was spent on | ✓ | ✓ | | <code>/spend &lt;time(1h 30m &#124; -1h 5m)&gt; &lt;date(YYYY-MM-DD)&gt;</code> | Add or subtract spent time; optionally, specify the date that time was spent on | ✓ | ✓ |
......
...@@ -2,10 +2,11 @@ ...@@ -2,10 +2,11 @@
>**Notes:** >**Notes:**
> >
> - [Introduced][ce-3050] in GitLab 8.9. > - [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/issues/3050) in GitLab 8.9.
> - Importing will not be possible if the import instance version differs from > - Importing will not be possible if the import instance version differs from
> that of the exporter. > that of the exporter.
> - For GitLab admins, please read through [Project import/export administration](../../../administration/raketasks/project_import_export.md). > - For GitLab admins, please read through
> [Project import/export administration](../../../administration/raketasks/project_import_export.md).
> - For existing installations, the project import option has to be enabled in > - For existing installations, the project import option has to be enabled in
> application settings (`/admin/application_settings`) under 'Import sources'. > application settings (`/admin/application_settings`) under 'Import sources'.
> Ask your administrator if you don't see the **GitLab export** button when > Ask your administrator if you don't see the **GitLab export** button when
...@@ -14,10 +15,9 @@ ...@@ -14,10 +15,9 @@
> on the GitLab instance in application settings (`/admin/application_settings`) > on the GitLab instance in application settings (`/admin/application_settings`)
> under 'Visibility and Access Controls'. > under 'Visibility and Access Controls'.
> - You can find some useful raketasks if you are an administrator in the > - You can find some useful raketasks if you are an administrator in the
> [import_export](../../../administration/raketasks/project_import_export.md) > [import_export](../../../administration/raketasks/project_import_export.md) raketask.
> raketask. > - The exports are stored in a temporary [shared directory](../../../development/shared_files.md)
> - The exports are stored in a temporary [shared directory][tmp] and are deleted > and are deleted every 24 hours by a specific worker.
> every 24 hours by a specific worker.
> - Group members will get exported as project members, as long as the user has > - Group members will get exported as project members, as long as the user has
> maintainer or admin access to the group where the exported project lives. An admin > maintainer or admin access to the group where the exported project lives. An admin
> in the import side is required to map the users, based on email or username. > in the import side is required to map the users, based on email or username.
...@@ -77,9 +77,9 @@ The following items will NOT be exported: ...@@ -77,9 +77,9 @@ The following items will NOT be exported:
## Exporting a project and its data ## Exporting a project and its data
1. Go to the project settings page by clicking on **Edit Project**: 1. Go to your project's homepage.
![Project settings button](img/settings_edit_button.png) 1. Click **Settings** in the sidebar.
1. Scroll down to find the **Export project** button: 1. Scroll down to find the **Export project** button:
...@@ -98,19 +98,14 @@ The following items will NOT be exported: ...@@ -98,19 +98,14 @@ The following items will NOT be exported:
## Importing the project ## Importing the project
1. The new GitLab project import feature is at the far right of the import 1. The GitLab project import feature is the first import option when creating a
options when creating a New Project. Make sure you are in the right namespace new project. Click on **GitLab export**:
and you have entered a project name. Click on **GitLab export**:
![New project](img/import_export_new_project.png) ![New project](img/import_export_new_project.png)
1. You can see where the project will be imported to. You can now select file 1. Enter your project name and URL. Then select the file you exported previously:
exported previously:
![Select file](img/import_export_select_file.png) ![Select file](img/import_export_select_file.png)
1. Click on **Import project** to begin importing. Your newly imported project 1. Click on **Import project** to begin importing. Your newly imported project
page will appear soon. page will appear soon.
[ce-3050]: https://gitlab.com/gitlab-org/gitlab-ce/issues/3050
[tmp]: ../../../development/shared_files.md
...@@ -99,6 +99,7 @@ The repository will push soon. To force a push, click the appropriate button. ...@@ -99,6 +99,7 @@ The repository will push soon. To force a push, click the appropriate button.
## Pulling from a remote repository **[STARTER]** ## Pulling from a remote repository **[STARTER]**
> [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/51) in GitLab Enterprise Edition 8.2. > [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/51) in GitLab Enterprise Edition 8.2.
> [Added Git LFS support](https://gitlab.com/gitlab-org/gitlab-ee/issues/10871) in [GitLab Starter](https://about.gitlab.com/pricing/) 11.11.
You can set up a repository to automatically have its branches, tags, and commits updated from an You can set up a repository to automatically have its branches, tags, and commits updated from an
upstream repository. upstream repository.
......
...@@ -98,7 +98,6 @@ module API ...@@ -98,7 +98,6 @@ module API
mount ::API::Boards mount ::API::Boards
mount ::API::Branches mount ::API::Branches
mount ::API::BroadcastMessages mount ::API::BroadcastMessages
mount ::API::CircuitBreakers
mount ::API::Commits mount ::API::Commits
mount ::API::CommitStatuses mount ::API::CommitStatuses
mount ::API::ContainerRegistry mount ::API::ContainerRegistry
......
# frozen_string_literal: true
module API
class CircuitBreakers < Grape::API
before { authenticated_as_admin! }
resource :circuit_breakers do
params do
requires :type,
type: String,
desc: "The type of circuitbreaker",
values: ['repository_storage']
end
resource ':type' do
namespace '', requirements: { type: 'repository_storage' } do
desc 'Get all git storages' do
detail 'This feature was introduced in GitLab 9.5'
end
get do
present []
end
desc 'Get all failing git storages' do
detail 'This feature was introduced in GitLab 9.5'
end
get 'failing' do
present []
end
desc 'Reset all storage failures and open circuitbreaker' do
detail 'This feature was introduced in GitLab 9.5'
end
delete do
end
end
end
end
end
end
...@@ -55,7 +55,7 @@ module API ...@@ -55,7 +55,7 @@ module API
requires :key, type: String, desc: 'The key of the variable' requires :key, type: String, desc: 'The key of the variable'
requires :value, type: String, desc: 'The value of the variable' requires :value, type: String, desc: 'The value of the variable'
optional :protected, type: String, desc: 'Whether the variable is protected' optional :protected, type: String, desc: 'Whether the variable is protected'
optional :masked, type: String, desc: 'Whether the variable is masked' optional :masked, type: Boolean, desc: 'Whether the variable is masked'
optional :variable_type, type: String, values: Ci::Variable.variable_types.keys, desc: 'The type of variable, must be one of env_var or file. Defaults to env_var' optional :variable_type, type: String, values: Ci::Variable.variable_types.keys, desc: 'The type of variable, must be one of env_var or file. Defaults to env_var'
if Gitlab.ee? if Gitlab.ee?
...@@ -82,7 +82,7 @@ module API ...@@ -82,7 +82,7 @@ module API
optional :key, type: String, desc: 'The key of the variable' optional :key, type: String, desc: 'The key of the variable'
optional :value, type: String, desc: 'The value of the variable' optional :value, type: String, desc: 'The value of the variable'
optional :protected, type: String, desc: 'Whether the variable is protected' optional :protected, type: String, desc: 'Whether the variable is protected'
optional :masked, type: String, desc: 'Whether the variable is masked' optional :masked, type: Boolean, desc: 'Whether the variable is masked'
optional :variable_type, type: String, values: Ci::Variable.variable_types.keys, desc: 'The type of variable, must be one of env_var or file' optional :variable_type, type: String, values: Ci::Variable.variable_types.keys, desc: 'The type of variable, must be one of env_var or file'
if Gitlab.ee? if Gitlab.ee?
......
...@@ -363,6 +363,14 @@ module Banzai ...@@ -363,6 +363,14 @@ module Banzai
group_ref group_ref
end end
def unescape_html_entities(text)
CGI.unescapeHTML(text.to_s)
end
def escape_html_entities(text)
CGI.escapeHTML(text.to_s)
end
end end
end end
end end
...@@ -104,14 +104,6 @@ module Banzai ...@@ -104,14 +104,6 @@ module Banzai
matches[:namespace] && matches[:project] matches[:namespace] && matches[:project]
end end
def unescape_html_entities(text)
CGI.unescapeHTML(text.to_s)
end
def escape_html_entities(text)
CGI.escapeHTML(text.to_s)
end
def object_link_title(object, matches) def object_link_title(object, matches)
# use title of wrapped element instead # use title of wrapped element instead
nil nil
......
...@@ -51,13 +51,13 @@ module Banzai ...@@ -51,13 +51,13 @@ module Banzai
# default implementation. # default implementation.
return super(text, pattern) if pattern != Milestone.reference_pattern return super(text, pattern) if pattern != Milestone.reference_pattern
text.gsub(pattern) do |match| unescape_html_entities(text).gsub(pattern) do |match|
milestone = find_milestone($~[:project], $~[:namespace], $~[:milestone_iid], $~[:milestone_name]) milestone = find_milestone($~[:project], $~[:namespace], $~[:milestone_iid], $~[:milestone_name])
if milestone if milestone
yield match, milestone.id, $~[:project], $~[:namespace], $~ yield match, milestone.id, $~[:project], $~[:namespace], $~
else else
match escape_html_entities(match)
end end
end end
end end
......
...@@ -40,6 +40,7 @@ sast: ...@@ -40,6 +40,7 @@ sast:
SAST_BRAKEMAN_LEVEL \ SAST_BRAKEMAN_LEVEL \
SAST_GOSEC_LEVEL \ SAST_GOSEC_LEVEL \
SAST_FLAWFINDER_LEVEL \ SAST_FLAWFINDER_LEVEL \
SAST_GITLEAKS_ENTROPY_LEVEL \
SAST_DOCKER_CLIENT_NEGOTIATION_TIMEOUT \ SAST_DOCKER_CLIENT_NEGOTIATION_TIMEOUT \
SAST_PULL_ANALYZER_IMAGE_TIMEOUT \ SAST_PULL_ANALYZER_IMAGE_TIMEOUT \
SAST_RUN_ANALYZER_TIMEOUT \ SAST_RUN_ANALYZER_TIMEOUT \
......
# frozen_string_literal: true
module Gitlab
module Kubernetes
module Errors
CONNECTION = [
SocketError,
OpenSSL::SSL::SSLError,
Errno::ECONNRESET,
Errno::ENETUNREACH,
Errno::ECONNREFUSED,
Errno::EHOSTUNREACH,
Net::OpenTimeout,
Net::ReadTimeout,
IPAddr::InvalidAddressError
].freeze
AUTHENTICATION = [
OpenSSL::X509::CertificateError
].freeze
end
end
end
...@@ -102,7 +102,7 @@ module Gitlab ...@@ -102,7 +102,7 @@ module Gitlab
@updates[:milestone_id] = nil @updates[:milestone_id] = nil
end end
desc _('Copy labels and milestone from other issue or merge request') desc _('Copy labels and milestone from other issue or merge request in this project')
explanation do |source_issuable| explanation do |source_issuable|
_("Copy labels and milestone from %{source_issuable_reference}.") % { source_issuable_reference: source_issuable.to_reference } _("Copy labels and milestone from %{source_issuable_reference}.") % { source_issuable_reference: source_issuable.to_reference }
end end
......
# frozen_string_literal: true
module Quality
class TestLevel
UnknownTestLevelError = Class.new(StandardError)
TEST_LEVEL_FOLDERS = {
unit: %w[
bin
config
db
dependencies
factories
finders
frontend
graphql
helpers
initializers
javascripts
lib
migrations
models
policies
presenters
rack_servers
routing
rubocop
serializers
services
sidekiq
tasks
uploaders
validators
views
workers
elastic_integration
],
integration: %w[
controllers
mailers
requests
],
system: ['features']
}.freeze
attr_reader :prefix
def initialize(prefix = nil)
@prefix = prefix
@patterns = {}
@regexps = {}
end
def pattern(level)
@patterns[level] ||= "#{prefix}spec/{#{TEST_LEVEL_FOLDERS.fetch(level).join(',')}}{,/**/}*_spec.rb".freeze
end
def regexp(level)
@regexps[level] ||= Regexp.new("#{prefix}spec/(#{TEST_LEVEL_FOLDERS.fetch(level).join('|')})").freeze
end
def level_for(file_path)
case file_path
when regexp(:unit)
:unit
when regexp(:integration)
:integration
when regexp(:system)
:system
else
raise UnknownTestLevelError, "Test level for #{file_path} couldn't be set. Please rename the file properly or change the test level detection regexes in #{__FILE__}."
end
end
end
end
# frozen_string_literal: true
return if Rails.env.production?
Rake::Task["spec"].clear if Rake::Task.task_defined?('spec') Rake::Task["spec"].clear if Rake::Task.task_defined?('spec')
namespace :spec do namespace :spec do
desc 'GitLab | Rspec | Run request specs' desc 'GitLab | RSpec | Run unit tests'
RSpec::Core::RakeTask.new(:unit, :rspec_opts) do |t, args|
require_dependency 'quality/test_level'
t.pattern = Quality::TestLevel.new.pattern(:unit)
t.rspec_opts = args[:rspec_opts]
end
desc 'GitLab | RSpec | Run integration tests'
RSpec::Core::RakeTask.new(:integration, :rspec_opts) do |t, args|
require_dependency 'quality/test_level'
t.pattern = Quality::TestLevel.new.pattern(:integration)
t.rspec_opts = args[:rspec_opts]
end
desc 'GitLab | RSpec | Run system tests'
RSpec::Core::RakeTask.new(:system, :rspec_opts) do |t, args|
require_dependency 'quality/test_level'
t.pattern = Quality::TestLevel.new.pattern(:system)
t.rspec_opts = args[:rspec_opts]
end
desc '[Deprecated] Use the "bin/rspec --tag api" instead'
task :api do task :api do
cmds = [ cmds = [
%w(rake gitlab:setup), %w(rake gitlab:setup),
...@@ -10,7 +35,7 @@ namespace :spec do ...@@ -10,7 +35,7 @@ namespace :spec do
run_commands(cmds) run_commands(cmds)
end end
desc 'GitLab | Rspec | Run feature specs' desc '[Deprecated] Use the "spec:system" task instead'
task :feature do task :feature do
cmds = [ cmds = [
%w(rake gitlab:setup), %w(rake gitlab:setup),
...@@ -19,7 +44,7 @@ namespace :spec do ...@@ -19,7 +44,7 @@ namespace :spec do
run_commands(cmds) run_commands(cmds)
end end
desc 'GitLab | Rspec | Run model specs' desc '[Deprecated] Use "bin/rspec spec/models" instead'
task :models do task :models do
cmds = [ cmds = [
%w(rake gitlab:setup), %w(rake gitlab:setup),
...@@ -28,7 +53,7 @@ namespace :spec do ...@@ -28,7 +53,7 @@ namespace :spec do
run_commands(cmds) run_commands(cmds)
end end
desc 'GitLab | Rspec | Run service specs' desc '[Deprecated] Use "bin/rspec spec/services" instead'
task :services do task :services do
cmds = [ cmds = [
%w(rake gitlab:setup), %w(rake gitlab:setup),
...@@ -37,7 +62,7 @@ namespace :spec do ...@@ -37,7 +62,7 @@ namespace :spec do
run_commands(cmds) run_commands(cmds)
end end
desc 'GitLab | Rspec | Run lib specs' desc '[Deprecated] Use "bin/rspec spec/lib" instead'
task :lib do task :lib do
cmds = [ cmds = [
%w(rake gitlab:setup), %w(rake gitlab:setup),
...@@ -45,15 +70,6 @@ namespace :spec do ...@@ -45,15 +70,6 @@ namespace :spec do
] ]
run_commands(cmds) run_commands(cmds)
end end
desc 'GitLab | Rspec | Run other specs'
task :other do
cmds = [
%w(rake gitlab:setup),
%w(rspec spec --tag ~@api --tag ~@feature --tag ~@models --tag ~@lib --tag ~@services)
]
run_commands(cmds)
end
end end
desc "GitLab | Run specs" desc "GitLab | Run specs"
......
...@@ -835,6 +835,9 @@ msgstr "" ...@@ -835,6 +835,9 @@ msgstr ""
msgid "An error has occurred" msgid "An error has occurred"
msgstr "" msgstr ""
msgid "An error occurding while fetching folder content."
msgstr ""
msgid "An error occurred creating the new branch." msgid "An error occurred creating the new branch."
msgstr "" msgstr ""
...@@ -2499,6 +2502,9 @@ msgstr "" ...@@ -2499,6 +2502,9 @@ msgstr ""
msgid "ClusterIntegration|The endpoint is in the process of being assigned. Please check your Kubernetes cluster or Quotas on Google Kubernetes Engine if it takes a long time." msgid "ClusterIntegration|The endpoint is in the process of being assigned. Please check your Kubernetes cluster or Quotas on Google Kubernetes Engine if it takes a long time."
msgstr "" msgstr ""
msgid "ClusterIntegration|There was a problem authenticating with your cluster. Please ensure your CA Certificate and Token are valid."
msgstr ""
msgid "ClusterIntegration|This account must have permissions to create a Kubernetes cluster in the %{link_to_container_project} specified below" msgid "ClusterIntegration|This account must have permissions to create a Kubernetes cluster in the %{link_to_container_project} specified below"
msgstr "" msgstr ""
...@@ -2553,6 +2559,9 @@ msgstr "" ...@@ -2553,6 +2559,9 @@ msgstr ""
msgid "ClusterIntegration|Your account must have %{link_to_kubernetes_engine}" msgid "ClusterIntegration|Your account must have %{link_to_kubernetes_engine}"
msgstr "" msgstr ""
msgid "ClusterIntegration|Your cluster API is unreachable. Please ensure your API URL is correct."
msgstr ""
msgid "ClusterIntegration|Zone" msgid "ClusterIntegration|Zone"
msgstr "" msgstr ""
...@@ -2900,7 +2909,7 @@ msgstr "" ...@@ -2900,7 +2909,7 @@ msgstr ""
msgid "Copy labels and milestone from %{source_issuable_reference}." msgid "Copy labels and milestone from %{source_issuable_reference}."
msgstr "" msgstr ""
msgid "Copy labels and milestone from other issue or merge request" msgid "Copy labels and milestone from other issue or merge request in this project"
msgstr "" msgstr ""
msgid "Copy link" msgid "Copy link"
...@@ -4641,6 +4650,9 @@ msgstr "" ...@@ -4641,6 +4650,9 @@ msgstr ""
msgid "Go to %{link_to_google_takeout}." msgid "Go to %{link_to_google_takeout}."
msgstr "" msgstr ""
msgid "Go to parent"
msgstr ""
msgid "Go to project" msgid "Go to project"
msgstr "" msgstr ""
...@@ -5261,6 +5273,9 @@ msgstr "" ...@@ -5261,6 +5273,9 @@ msgstr ""
msgid "Invite" msgid "Invite"
msgstr "" msgstr ""
msgid "Invite \"%{trimmed}\" by email"
msgstr ""
msgid "Invite group" msgid "Invite group"
msgstr "" msgstr ""
...@@ -5808,6 +5823,9 @@ msgstr "" ...@@ -5808,6 +5823,9 @@ msgstr ""
msgid "Mark as resolved" msgid "Mark as resolved"
msgstr "" msgstr ""
msgid "Mark comment as resolved"
msgstr ""
msgid "Mark this issue as a duplicate of another issue" msgid "Mark this issue as a duplicate of another issue"
msgstr "" msgstr ""
...@@ -6452,6 +6470,9 @@ msgstr "" ...@@ -6452,6 +6470,9 @@ msgstr ""
msgid "Not found." msgid "Not found."
msgstr "" msgstr ""
msgid "Not implemented!"
msgstr ""
msgid "Not now" msgid "Not now"
msgstr "" msgstr ""
...@@ -8214,6 +8235,9 @@ msgstr "" ...@@ -8214,6 +8235,9 @@ msgstr ""
msgid "Resolved all discussions." msgid "Resolved all discussions."
msgstr "" msgstr ""
msgid "Resolved by %{name}"
msgstr ""
msgid "Resolved by %{resolvedByName}" msgid "Resolved by %{resolvedByName}"
msgstr "" msgstr ""
...@@ -8441,6 +8465,9 @@ msgstr "" ...@@ -8441,6 +8465,9 @@ msgstr ""
msgid "Search for a group" msgid "Search for a group"
msgstr "" msgstr ""
msgid "Search for a user"
msgstr ""
msgid "Search for projects, issues, etc." msgid "Search for projects, issues, etc."
msgstr "" msgstr ""
...@@ -8935,6 +8962,9 @@ msgstr "" ...@@ -8935,6 +8962,9 @@ msgstr ""
msgid "Something went wrong. Please try again." msgid "Something went wrong. Please try again."
msgstr "" msgstr ""
msgid "Something went wrong. Try again later."
msgstr ""
msgid "Sorry, no projects matched your search" msgid "Sorry, no projects matched your search"
msgstr "" msgstr ""
...@@ -10744,6 +10774,21 @@ msgstr "" ...@@ -10744,6 +10774,21 @@ msgstr ""
msgid "Users were successfully added." msgid "Users were successfully added."
msgstr "" msgstr ""
msgid "UsersSelect|%{name} + %{length} more"
msgstr ""
msgid "UsersSelect|Any User"
msgstr ""
msgid "UsersSelect|Assignee"
msgstr ""
msgid "UsersSelect|No assignee - %{openingTag} assign yourself %{closingTag}"
msgstr ""
msgid "UsersSelect|Unassigned"
msgstr ""
msgid "Using required encryption strategy when encrypted field is missing!" msgid "Using required encryption strategy when encrypted field is missing!"
msgstr "" msgstr ""
......
...@@ -374,12 +374,12 @@ function clearNote (inputId) { ...@@ -374,12 +374,12 @@ function clearNote (inputId) {
} }
} }
function confirmAndClear (discussionId) { function confirmAndClear (mergeRequestId) {
const commentButton = document.getElementById('gitlab-comment-button'); const commentButton = document.getElementById('gitlab-comment-button');
const note = document.getElementById('gitlab-validation-note'); const note = document.getElementById('gitlab-validation-note');
commentButton.innerText = 'Feedback sent'; commentButton.innerText = 'Feedback sent';
note.innerText = `Your comment was successfully posted to issue #${discussionId}`; note.innerText = `Your comment was successfully posted to merge request #${mergeRequestId}`;
setTimeout(resetCommentButton, 1000); setTimeout(resetCommentButton, 1000);
} }
...@@ -412,7 +412,7 @@ function getProjectDetails () { ...@@ -412,7 +412,7 @@ function getProjectDetails () {
const browser = getBrowserId(userAgent); const browser = getBrowserId(userAgent);
const scriptEl = document.getElementById('review-app-toolbar-script') const scriptEl = document.getElementById('review-app-toolbar-script')
const { projectId, discussionId, mrUrl } = scriptEl.dataset; const { projectId, mergeRequestId, mrUrl } = scriptEl.dataset;
return { return {
href, href,
...@@ -422,7 +422,7 @@ function getProjectDetails () { ...@@ -422,7 +422,7 @@ function getProjectDetails () {
innerWidth, innerWidth,
innerHeight, innerHeight,
projectId, projectId,
discussionId, mergeRequestId,
mrUrl, mrUrl,
}; };
} }
...@@ -449,7 +449,7 @@ function postComment ({ ...@@ -449,7 +449,7 @@ function postComment ({
innerWidth, innerWidth,
innerHeight, innerHeight,
projectId, projectId,
discussionId, mergeRequestId,
mrUrl, mrUrl,
}) { }) {
// Clear any old errors // Clear any old errors
...@@ -466,18 +466,20 @@ function postComment ({ ...@@ -466,18 +466,20 @@ function postComment ({
} }
const detailText = ` const detailText = `
<details> \n
<summary>Metadata</summary> <details>
Posted from ${href} | ${platform} | ${browser} | ${innerWidth} x ${innerHeight}. <summary>Metadata</summary>
<br /><br /> Posted from ${href} | ${platform} | ${browser} | ${innerWidth} x ${innerHeight}.
*User agent: ${userAgent}* <br /><br />
</details> <em>User agent: ${userAgent}</em>
</details>
`; `;
const url = ` const url = `
${mrUrl}/api/v4/projects/${projectId}/issues/${discussionId}/discussions`; ${mrUrl}/api/v4/projects/${projectId}/merge_requests/${mergeRequestId}/discussions`;
const body = `${commentText}${detailText}`;
const body = `${commentText} ${detailText}`;
fetch(url, { fetch(url, {
method: 'POST', method: 'POST',
...@@ -489,7 +491,7 @@ function postComment ({ ...@@ -489,7 +491,7 @@ function postComment ({
}) })
.then((response) => { .then((response) => {
if (response.ok) { if (response.ok) {
confirmAndClear(discussionId); confirmAndClear(mergeRequestId);
return; return;
} }
......
...@@ -11,6 +11,7 @@ module QA ...@@ -11,6 +11,7 @@ module QA
element :variable_row, '.ci-variable-row-body' # rubocop:disable QA/ElementWithPattern element :variable_row, '.ci-variable-row-body' # rubocop:disable QA/ElementWithPattern
element :variable_key, '.qa-ci-variable-input-key' # rubocop:disable QA/ElementWithPattern element :variable_key, '.qa-ci-variable-input-key' # rubocop:disable QA/ElementWithPattern
element :variable_value, '.qa-ci-variable-input-value' # rubocop:disable QA/ElementWithPattern element :variable_value, '.qa-ci-variable-input-value' # rubocop:disable QA/ElementWithPattern
element :variable_masked
end end
view 'app/views/ci/variables/_index.html.haml' do view 'app/views/ci/variables/_index.html.haml' do
...@@ -18,7 +19,7 @@ module QA ...@@ -18,7 +19,7 @@ module QA
element :reveal_values, '.js-secret-value-reveal-button' # rubocop:disable QA/ElementWithPattern element :reveal_values, '.js-secret-value-reveal-button' # rubocop:disable QA/ElementWithPattern
end end
def fill_variable(key, value) def fill_variable(key, value, masked)
keys = all_elements(:ci_variable_input_key) keys = all_elements(:ci_variable_input_key)
index = keys.size - 1 index = keys.size - 1
...@@ -32,6 +33,9 @@ module QA ...@@ -32,6 +33,9 @@ module QA
# The code was inspired from: # The code was inspired from:
# https://github.com/teamcapybara/capybara/blob/679548cea10773d45e32808f4d964377cfe5e892/lib/capybara/selenium/node.rb#L217 # https://github.com/teamcapybara/capybara/blob/679548cea10773d45e32808f4d964377cfe5e892/lib/capybara/selenium/node.rb#L217
execute_script("arguments[0].value = #{value.to_json}", node) execute_script("arguments[0].value = #{value.to_json}", node)
masked_node = all_elements(:variable_masked)[index]
toggle_masked(masked_node, masked)
end end
def save_variables def save_variables
...@@ -47,6 +51,24 @@ module QA ...@@ -47,6 +51,24 @@ module QA
find('.qa-ci-variable-input-value').value find('.qa-ci-variable-input-value').value
end end
end end
private
def toggle_masked(masked_node, masked)
wait(reload: false) do
masked_node.click
masked ? masked_enabled?(masked_node) : masked_disabled?(masked_node)
end
end
def masked_enabled?(masked_node)
masked_node[:class].include?('is-checked')
end
def masked_disabled?(masked_node)
!masked_enabled?(masked_node)
end
end end
end end
end end
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
module QA module QA
module Resource module Resource
class CiVariable < Base class CiVariable < Base
attr_accessor :key, :value attr_accessor :key, :value, :masked
attribute :project do attribute :project do
Project.fabricate! do |resource| Project.fabricate! do |resource|
...@@ -19,7 +19,7 @@ module QA ...@@ -19,7 +19,7 @@ module QA
Page::Project::Settings::CICD.perform do |setting| Page::Project::Settings::CICD.perform do |setting|
setting.expand_ci_variables do |page| setting.expand_ci_variables do |page|
page.fill_variable(key, value) page.fill_variable(key, value, masked)
page.save_variables page.save_variables
end end
...@@ -49,7 +49,8 @@ module QA ...@@ -49,7 +49,8 @@ module QA
def api_post_body def api_post_body
{ {
key: key, key: key,
value: value value: value,
masked: masked
} }
end end
end end
......
...@@ -16,6 +16,7 @@ module QA ...@@ -16,6 +16,7 @@ module QA
resource.project = project resource.project = project
resource.key = 'VARIABLE_KEY' resource.key = 'VARIABLE_KEY'
resource.value = 'some_CI_variable' resource.value = 'some_CI_variable'
resource.masked = false
end end
project.visit! project.visit!
......
...@@ -60,6 +60,7 @@ module QA ...@@ -60,6 +60,7 @@ module QA
resource.project = @project resource.project = @project
resource.key = deploy_key_name resource.key = deploy_key_name
resource.value = key.private_key resource.value = key.private_key
resource.masked = false
end end
gitlab_ci = <<~YAML gitlab_ci = <<~YAML
......
...@@ -34,6 +34,7 @@ module QA ...@@ -34,6 +34,7 @@ module QA
resource.project = @project resource.project = @project
resource.key = 'CODE_QUALITY_DISABLED' resource.key = 'CODE_QUALITY_DISABLED'
resource.value = '1' resource.value = '1'
resource.masked = false
end end
# Set an application secret CI variable (prefixed with K8S_SECRET_) # Set an application secret CI variable (prefixed with K8S_SECRET_)
...@@ -41,6 +42,7 @@ module QA ...@@ -41,6 +42,7 @@ module QA
resource.project = @project resource.project = @project
resource.key = 'K8S_SECRET_OPTIONAL_MESSAGE' resource.key = 'K8S_SECRET_OPTIONAL_MESSAGE'
resource.value = 'you_can_see_this_variable' resource.value = 'you_can_see_this_variable'
resource.masked = false
end end
# Connect K8s cluster # Connect K8s cluster
......
...@@ -5,6 +5,7 @@ export USE_BUNDLE_INSTALL=${USE_BUNDLE_INSTALL:-true} ...@@ -5,6 +5,7 @@ export USE_BUNDLE_INSTALL=${USE_BUNDLE_INSTALL:-true}
export BUNDLE_INSTALL_FLAGS="--without=production --jobs=$(nproc) --path=vendor --retry=3 --quiet" export BUNDLE_INSTALL_FLAGS="--without=production --jobs=$(nproc) --path=vendor --retry=3 --quiet"
if [ "$USE_BUNDLE_INSTALL" != "false" ]; then if [ "$USE_BUNDLE_INSTALL" != "false" ]; then
bundle --version
bundle install --clean $BUNDLE_INSTALL_FLAGS && bundle check bundle install --clean $BUNDLE_INSTALL_FLAGS && bundle check
fi fi
...@@ -16,12 +17,10 @@ cp config/gitlab.yml.example config/gitlab.yml ...@@ -16,12 +17,10 @@ cp config/gitlab.yml.example config/gitlab.yml
sed -i 's/bin_path: \/usr\/bin\/git/bin_path: \/usr\/local\/bin\/git/' config/gitlab.yml sed -i 's/bin_path: \/usr\/bin\/git/bin_path: \/usr\/local\/bin\/git/' config/gitlab.yml
# Determine the database by looking at the job name. # Determine the database by looking at the job name.
# For example, we'll get pg if the job is `rspec-pg 19 20` # This would make the default database postgresql.
export GITLAB_DATABASE=$(echo $CI_JOB_NAME | cut -f1 -d' ' | cut -f2 -d-) if [[ "${CI_JOB_NAME#*mysql}" != "$CI_JOB_NAME" ]]; then
export GITLAB_DATABASE='mysql'
# This would make the default database postgresql, and we could also use else
# pg to mean postgresql.
if [ "$GITLAB_DATABASE" != 'mysql' ]; then
export GITLAB_DATABASE='postgresql' export GITLAB_DATABASE='postgresql'
fi fi
......
...@@ -3,6 +3,8 @@ ...@@ -3,6 +3,8 @@
require 'spec_helper' require 'spec_helper'
describe 'Clusterable > Show page' do describe 'Clusterable > Show page' do
include KubernetesHelpers
let(:current_user) { create(:user) } let(:current_user) { create(:user) }
let(:cluster_ingress_help_text_selector) { '.js-ingress-domain-help-text' } let(:cluster_ingress_help_text_selector) { '.js-ingress-domain-help-text' }
let(:hide_modifier_selector) { '.hide' } let(:hide_modifier_selector) { '.hide' }
...@@ -83,6 +85,7 @@ describe 'Clusterable > Show page' do ...@@ -83,6 +85,7 @@ describe 'Clusterable > Show page' do
shared_examples 'editing a user-provided cluster' do shared_examples 'editing a user-provided cluster' do
before do before do
stub_kubeclient_discover(cluster.platform.api_url)
clusterable.add_maintainer(current_user) clusterable.add_maintainer(current_user)
visit cluster_path visit cluster_path
end end
......
...@@ -14,6 +14,7 @@ describe 'User Cluster', :js do ...@@ -14,6 +14,7 @@ describe 'User Cluster', :js do
allow(Groups::ClustersController).to receive(:STATUS_POLLING_INTERVAL) { 100 } allow(Groups::ClustersController).to receive(:STATUS_POLLING_INTERVAL) { 100 }
allow_any_instance_of(Clusters::Gcp::Kubernetes::CreateOrUpdateNamespaceService).to receive(:execute) allow_any_instance_of(Clusters::Gcp::Kubernetes::CreateOrUpdateNamespaceService).to receive(:execute)
allow_any_instance_of(Clusters::Cluster).to receive(:retrieve_connection_status).and_return(:connected)
end end
context 'when user does not have a cluster and visits cluster index page' do context 'when user does not have a cluster and visits cluster index page' do
......
...@@ -12,6 +12,7 @@ describe 'User Cluster', :js do ...@@ -12,6 +12,7 @@ describe 'User Cluster', :js do
allow(Projects::ClustersController).to receive(:STATUS_POLLING_INTERVAL) { 100 } allow(Projects::ClustersController).to receive(:STATUS_POLLING_INTERVAL) { 100 }
allow_any_instance_of(Clusters::Gcp::Kubernetes::CreateOrUpdateNamespaceService).to receive(:execute) allow_any_instance_of(Clusters::Gcp::Kubernetes::CreateOrUpdateNamespaceService).to receive(:execute)
allow_any_instance_of(Clusters::Cluster).to receive(:retrieve_connection_status).and_return(:connected)
end end
context 'when user does not have a cluster and visits cluster index page' do context 'when user does not have a cluster and visits cluster index page' do
......
...@@ -46,6 +46,9 @@ describe 'Projects > Settings > For a forked project', :js do ...@@ -46,6 +46,9 @@ describe 'Projects > Settings > For a forked project', :js do
wait_for_requests wait_for_requests
within '.js-error-tracking-settings' do
click_button('Expand')
end
expect(page).to have_content('Sentry API URL') expect(page).to have_content('Sentry API URL')
expect(page.body).to include('Error Tracking') expect(page.body).to include('Error Tracking')
expect(page).to have_button('Connect') expect(page).to have_button('Connect')
...@@ -86,6 +89,9 @@ describe 'Projects > Settings > For a forked project', :js do ...@@ -86,6 +89,9 @@ describe 'Projects > Settings > For a forked project', :js do
wait_for_requests wait_for_requests
within '.js-error-tracking-settings' do
click_button('Expand')
end
check('Active') check('Active')
fill_in('error-tracking-api-host', with: 'http://sentry.example.com') fill_in('error-tracking-api-host', with: 'http://sentry.example.com')
fill_in('error-tracking-token', with: 'token') fill_in('error-tracking-token', with: 'token')
......
...@@ -209,6 +209,22 @@ describe('Clusters', () => { ...@@ -209,6 +209,22 @@ describe('Clusters', () => {
expect(cluster.errorContainer.classList.contains('hidden')).toBeFalsy(); expect(cluster.errorContainer.classList.contains('hidden')).toBeFalsy();
}); });
}); });
describe('when cluster is unreachable', () => {
it('should show the unreachable warning container', () => {
cluster.updateContainer(null, 'unreachable');
expect(cluster.unreachableContainer.classList.contains('hidden')).toBe(false);
});
});
describe('when cluster has an authentication failure', () => {
it('should show the authentication failure warning container', () => {
cluster.updateContainer(null, 'authentication_failure');
expect(cluster.authenticationFailureContainer.classList.contains('hidden')).toBe(false);
});
});
}); });
describe('installApplication', () => { describe('installApplication', () => {
......
...@@ -16,7 +16,9 @@ exports[`Repository table row component renders table row 1`] = ` ...@@ -16,7 +16,9 @@ exports[`Repository table row component renders table row 1`] = `
<a <a
class="str-truncated" class="str-truncated"
> >
test test
</a> </a>
<!----> <!---->
......
...@@ -3,18 +3,19 @@ import { GlLoadingIcon } from '@gitlab/ui'; ...@@ -3,18 +3,19 @@ import { GlLoadingIcon } from '@gitlab/ui';
import Table from '~/repository/components/table/index.vue'; import Table from '~/repository/components/table/index.vue';
let vm; let vm;
let $apollo;
function factory(path, data = () => ({})) {
$apollo = {
query: jest.fn().mockReturnValue(Promise.resolve({ data: data() })),
};
function factory(path, loading = false) {
vm = shallowMount(Table, { vm = shallowMount(Table, {
propsData: { propsData: {
path, path,
}, },
mocks: { mocks: {
$apollo: { $apollo,
queries: {
files: { loading },
},
},
}, },
}); });
} }
...@@ -39,9 +40,41 @@ describe('Repository table component', () => { ...@@ -39,9 +40,41 @@ describe('Repository table component', () => {
); );
}); });
it('renders loading icon', () => { it('shows loading icon', () => {
factory('/', true); factory('/');
vm.setData({ isLoadingFiles: true });
expect(vm.find(GlLoadingIcon).isVisible()).toBe(true);
});
describe('normalizeData', () => {
it('normalizes edge nodes', () => {
const output = vm.vm.normalizeData('blobs', [{ node: '1' }, { node: '2' }]);
expect(output).toEqual(['1', '2']);
});
});
describe('hasNextPage', () => {
it('returns undefined when hasNextPage is false', () => {
const output = vm.vm.hasNextPage({
trees: { pageInfo: { hasNextPage: false } },
submodules: { pageInfo: { hasNextPage: false } },
blobs: { pageInfo: { hasNextPage: false } },
});
expect(output).toBe(undefined);
});
it('returns pageInfo object when hasNextPage is true', () => {
const output = vm.vm.hasNextPage({
trees: { pageInfo: { hasNextPage: false } },
submodules: { pageInfo: { hasNextPage: false } },
blobs: { pageInfo: { hasNextPage: true, nextCursor: 'test' } },
});
expect(vm.find(GlLoadingIcon).exists()).toBe(true); expect(output).toEqual({ hasNextPage: true, nextCursor: 'test' });
});
}); });
}); });
...@@ -29,9 +29,10 @@ describe('Repository table row component', () => { ...@@ -29,9 +29,10 @@ describe('Repository table row component', () => {
it('renders table row', () => { it('renders table row', () => {
factory({ factory({
id: 1, id: '1',
path: 'test', path: 'test',
type: 'file', type: 'file',
currentPath: '/',
}); });
expect(vm.element).toMatchSnapshot(); expect(vm.element).toMatchSnapshot();
...@@ -39,14 +40,15 @@ describe('Repository table row component', () => { ...@@ -39,14 +40,15 @@ describe('Repository table row component', () => {
it.each` it.each`
type | component | componentName type | component | componentName
${'folder'} | ${RouterLinkStub} | ${'RouterLink'} ${'tree'} | ${RouterLinkStub} | ${'RouterLink'}
${'file'} | ${'a'} | ${'hyperlink'} ${'file'} | ${'a'} | ${'hyperlink'}
${'commit'} | ${'a'} | ${'hyperlink'} ${'commit'} | ${'a'} | ${'hyperlink'}
`('renders a $componentName for type $type', ({ type, component }) => { `('renders a $componentName for type $type', ({ type, component }) => {
factory({ factory({
id: 1, id: '1',
path: 'test', path: 'test',
type, type,
currentPath: '/',
}); });
expect(vm.find(component).exists()).toBe(true); expect(vm.find(component).exists()).toBe(true);
...@@ -54,14 +56,15 @@ describe('Repository table row component', () => { ...@@ -54,14 +56,15 @@ describe('Repository table row component', () => {
it.each` it.each`
type | pushes type | pushes
${'folder'} | ${true} ${'tree'} | ${true}
${'file'} | ${false} ${'file'} | ${false}
${'commit'} | ${false} ${'commit'} | ${false}
`('pushes new router if type $type is folder', ({ type, pushes }) => { `('pushes new router if type $type is tree', ({ type, pushes }) => {
factory({ factory({
id: 1, id: '1',
path: 'test', path: 'test',
type, type,
currentPath: '/',
}); });
vm.trigger('click'); vm.trigger('click');
...@@ -75,9 +78,10 @@ describe('Repository table row component', () => { ...@@ -75,9 +78,10 @@ describe('Repository table row component', () => {
it('renders commit ID for submodule', () => { it('renders commit ID for submodule', () => {
factory({ factory({
id: 1, id: '1',
path: 'test', path: 'test',
type: 'commit', type: 'commit',
currentPath: '/',
}); });
expect(vm.find('.commit-sha').text()).toContain('1'); expect(vm.find('.commit-sha').text()).toContain('1');
......
...@@ -6,7 +6,7 @@ describe('getIconName', () => { ...@@ -6,7 +6,7 @@ describe('getIconName', () => {
// file types // file types
it.each` it.each`
type | path | icon type | path | icon
${'folder'} | ${''} | ${'folder'} ${'tree'} | ${''} | ${'folder'}
${'commit'} | ${''} | ${'archive'} ${'commit'} | ${''} | ${'archive'}
${'file'} | ${'test.pdf'} | ${'file-pdf-o'} ${'file'} | ${'test.pdf'} | ${'file-pdf-o'}
${'file'} | ${'test.jpg'} | ${'file-image-o'} ${'file'} | ${'test.jpg'} | ${'file-image-o'}
......
...@@ -90,9 +90,12 @@ describe('Job App ', () => { ...@@ -90,9 +90,12 @@ describe('Job App ', () => {
describe('triggered job', () => { describe('triggered job', () => {
beforeEach(() => { beforeEach(() => {
const aYearAgo = new Date();
aYearAgo.setFullYear(aYearAgo.getFullYear() - 1);
mock mock
.onGet(props.endpoint) .onGet(props.endpoint)
.replyOnce(200, Object.assign({}, job, { started: '2017-05-24T10:59:52.000+01:00' })); .replyOnce(200, Object.assign({}, job, { started: aYearAgo.toISOString() }));
vm = mountComponentWithStore(Component, { props, store }); vm = mountComponentWithStore(Component, { props, store });
}); });
......
...@@ -295,6 +295,25 @@ describe Banzai::Filter::MilestoneReferenceFilter do ...@@ -295,6 +295,25 @@ describe Banzai::Filter::MilestoneReferenceFilter do
end end
end end
shared_examples 'references with HTML entities' do
before do
milestone.update!(title: '&lt;html&gt;')
end
it 'links to a valid reference' do
doc = reference_filter('See %"&lt;html&gt;"')
expect(doc.css('a').first.attr('href')).to eq urls.milestone_url(milestone)
expect(doc.text).to eq 'See %<html>'
end
it 'ignores invalid milestone names and escapes entities' do
act = %(Milestone %"&lt;non valid&gt;")
expect(reference_filter(act).to_html).to eq act
end
end
shared_context 'project milestones' do shared_context 'project milestones' do
let(:reference) { milestone.to_reference(format: :iid) } let(:reference) { milestone.to_reference(format: :iid) }
...@@ -307,6 +326,7 @@ describe Banzai::Filter::MilestoneReferenceFilter do ...@@ -307,6 +326,7 @@ describe Banzai::Filter::MilestoneReferenceFilter do
it_behaves_like 'cross-project / cross-namespace complete reference' it_behaves_like 'cross-project / cross-namespace complete reference'
it_behaves_like 'cross-project / same-namespace complete reference' it_behaves_like 'cross-project / same-namespace complete reference'
it_behaves_like 'cross project shorthand reference' it_behaves_like 'cross project shorthand reference'
it_behaves_like 'references with HTML entities'
end end
shared_context 'group milestones' do shared_context 'group milestones' do
...@@ -317,6 +337,7 @@ describe Banzai::Filter::MilestoneReferenceFilter do ...@@ -317,6 +337,7 @@ describe Banzai::Filter::MilestoneReferenceFilter do
it_behaves_like 'String-based single-word references' it_behaves_like 'String-based single-word references'
it_behaves_like 'String-based multi-word references in quotes' it_behaves_like 'String-based multi-word references in quotes'
it_behaves_like 'referencing a milestone in a link href' it_behaves_like 'referencing a milestone in a link href'
it_behaves_like 'references with HTML entities'
it 'does not support references by IID' do it 'does not support references by IID' do
doc = reference_filter("See #{Milestone.reference_prefix}#{milestone.iid}") doc = reference_filter("See #{Milestone.reference_prefix}#{milestone.iid}")
......
# frozen_string_literal: true
require 'fast_spec_helper'
RSpec.describe Quality::TestLevel do
describe '#pattern' do
context 'when level is unit' do
it 'returns a pattern' do
expect(subject.pattern(:unit))
.to eq("spec/{bin,config,db,dependencies,factories,finders,frontend,graphql,helpers,initializers,javascripts,lib,migrations,models,policies,presenters,rack_servers,routing,rubocop,serializers,services,sidekiq,tasks,uploaders,validators,views,workers,elastic_integration}{,/**/}*_spec.rb")
end
end
context 'when level is integration' do
it 'returns a pattern' do
expect(subject.pattern(:integration))
.to eq("spec/{controllers,mailers,requests}{,/**/}*_spec.rb")
end
end
context 'when level is system' do
it 'returns a pattern' do
expect(subject.pattern(:system))
.to eq("spec/{features}{,/**/}*_spec.rb")
end
end
context 'with a prefix' do
it 'returns a pattern' do
expect(described_class.new('ee/').pattern(:system))
.to eq("ee/spec/{features}{,/**/}*_spec.rb")
end
end
describe 'performance' do
it 'memoizes the pattern for a given level' do
expect(subject.pattern(:system).object_id).to eq(subject.pattern(:system).object_id)
end
it 'freezes the pattern for a given level' do
expect(subject.pattern(:system)).to be_frozen
end
end
end
describe '#regexp' do
context 'when level is unit' do
it 'returns a regexp' do
expect(subject.regexp(:unit))
.to eq(%r{spec/(bin|config|db|dependencies|factories|finders|frontend|graphql|helpers|initializers|javascripts|lib|migrations|models|policies|presenters|rack_servers|routing|rubocop|serializers|services|sidekiq|tasks|uploaders|validators|views|workers|elastic_integration)})
end
end
context 'when level is integration' do
it 'returns a regexp' do
expect(subject.regexp(:integration))
.to eq(%r{spec/(controllers|mailers|requests)})
end
end
context 'when level is system' do
it 'returns a regexp' do
expect(subject.regexp(:system))
.to eq(%r{spec/(features)})
end
end
context 'with a prefix' do
it 'returns a regexp' do
expect(described_class.new('ee/').regexp(:system))
.to eq(%r{ee/spec/(features)})
end
end
describe 'performance' do
it 'memoizes the regexp for a given level' do
expect(subject.regexp(:system).object_id).to eq(subject.regexp(:system).object_id)
end
it 'freezes the regexp for a given level' do
expect(subject.regexp(:system)).to be_frozen
end
end
end
describe '#level_for' do
it 'returns the correct level for a unit test' do
expect(subject.level_for('spec/models/abuse_report_spec.rb')).to eq(:unit)
end
it 'returns the correct level for an integration test' do
expect(subject.level_for('spec/mailers/abuse_report_mailer_spec.rb')).to eq(:integration)
end
it 'returns the correct level for a system test' do
expect(subject.level_for('spec/features/abuse_report_spec.rb')).to eq(:system)
end
it 'raises an error for an unknown level' do
expect { subject.level_for('spec/unknown/foo_spec.rb') }
.to raise_error(described_class::UnknownTestLevelError,
%r{Test level for spec/unknown/foo_spec.rb couldn't be set. Please rename the file properly or change the test level detection regexes in .+/lib/quality/test_level.rb.})
end
end
end
...@@ -2,7 +2,10 @@ ...@@ -2,7 +2,10 @@
require 'spec_helper' require 'spec_helper'
describe Clusters::Cluster do describe Clusters::Cluster, :use_clean_rails_memory_store_caching do
include ReactiveCachingHelpers
include KubernetesHelpers
it_behaves_like 'having unique enum values' it_behaves_like 'having unique enum values'
subject { build(:cluster) } subject { build(:cluster) }
...@@ -23,7 +26,6 @@ describe Clusters::Cluster do ...@@ -23,7 +26,6 @@ describe Clusters::Cluster do
it { is_expected.to delegate_method(:status).to(:provider) } it { is_expected.to delegate_method(:status).to(:provider) }
it { is_expected.to delegate_method(:status_reason).to(:provider) } it { is_expected.to delegate_method(:status_reason).to(:provider) }
it { is_expected.to delegate_method(:status_name).to(:provider) }
it { is_expected.to delegate_method(:on_creation?).to(:provider) } it { is_expected.to delegate_method(:on_creation?).to(:provider) }
it { is_expected.to delegate_method(:active?).to(:platform_kubernetes).with_prefix } it { is_expected.to delegate_method(:active?).to(:platform_kubernetes).with_prefix }
it { is_expected.to delegate_method(:rbac?).to(:platform_kubernetes).with_prefix } it { is_expected.to delegate_method(:rbac?).to(:platform_kubernetes).with_prefix }
...@@ -501,28 +503,6 @@ describe Clusters::Cluster do ...@@ -501,28 +503,6 @@ describe Clusters::Cluster do
end end
end end
describe '#created?' do
let(:cluster) { create(:cluster, :provided_by_gcp) }
subject { cluster.created? }
context 'when status_name is :created' do
before do
allow(cluster).to receive_message_chain(:provider, :status_name).and_return(:created)
end
it { is_expected.to eq(true) }
end
context 'when status_name is not :created' do
before do
allow(cluster).to receive_message_chain(:provider, :status_name).and_return(:creating)
end
it { is_expected.to eq(false) }
end
end
describe '#allow_user_defined_namespace?' do describe '#allow_user_defined_namespace?' do
let(:cluster) { create(:cluster, :provided_by_gcp) } let(:cluster) { create(:cluster, :provided_by_gcp) }
...@@ -617,4 +597,139 @@ describe Clusters::Cluster do ...@@ -617,4 +597,139 @@ describe Clusters::Cluster do
it { is_expected.to be_truthy } it { is_expected.to be_truthy }
end end
end end
describe '#status_name' do
subject { cluster.status_name }
context 'the cluster has a provider' do
let(:cluster) { create(:cluster, :provided_by_gcp) }
before do
cluster.provider.make_errored!
end
it { is_expected.to eq :errored }
end
context 'there is a cached connection status' do
let(:cluster) { create(:cluster, :provided_by_user) }
before do
allow(cluster).to receive(:connection_status).and_return(:connected)
end
it { is_expected.to eq :connected }
end
context 'there is no connection status in the cache' do
let(:cluster) { create(:cluster, :provided_by_user) }
before do
allow(cluster).to receive(:connection_status).and_return(nil)
end
it { is_expected.to eq :created }
end
end
describe '#connection_status' do
let(:cluster) { create(:cluster) }
let(:status) { :connected }
subject { cluster.connection_status }
it { is_expected.to be_nil }
context 'with a cached status' do
before do
stub_reactive_cache(cluster, connection_status: status)
end
it { is_expected.to eq(status) }
end
end
describe '#calculate_reactive_cache' do
subject { cluster.calculate_reactive_cache }
context 'cluster is disabled' do
let(:cluster) { create(:cluster, :disabled) }
it 'does not populate the cache' do
expect(cluster).not_to receive(:retrieve_connection_status)
is_expected.to be_nil
end
end
context 'cluster is enabled' do
let(:cluster) { create(:cluster, :provided_by_user, :group) }
context 'connection to the cluster is successful' do
before do
stub_kubeclient_discover(cluster.platform.api_url)
end
it { is_expected.to eq(connection_status: :connected) }
end
context 'cluster cannot be reached' do
before do
allow(cluster.kubeclient.core_client).to receive(:discover)
.and_raise(SocketError)
end
it { is_expected.to eq(connection_status: :unreachable) }
end
context 'cluster cannot be authenticated to' do
before do
allow(cluster.kubeclient.core_client).to receive(:discover)
.and_raise(OpenSSL::X509::CertificateError.new("Certificate error"))
end
it { is_expected.to eq(connection_status: :authentication_failure) }
end
describe 'Kubeclient::HttpError' do
let(:error_code) { 403 }
let(:error_message) { "Forbidden" }
before do
allow(cluster.kubeclient.core_client).to receive(:discover)
.and_raise(Kubeclient::HttpError.new(error_code, error_message, nil))
end
it { is_expected.to eq(connection_status: :authentication_failure) }
context 'generic timeout' do
let(:error_message) { 'Timed out connecting to server'}
it { is_expected.to eq(connection_status: :unreachable) }
end
context 'gateway timeout' do
let(:error_message) { '504 Gateway Timeout for GET https://kubernetes.example.com/api/v1'}
it { is_expected.to eq(connection_status: :unreachable) }
end
end
context 'an uncategorised error is raised' do
before do
allow(cluster.kubeclient.core_client).to receive(:discover)
.and_raise(StandardError)
end
it { is_expected.to eq(connection_status: :unknown_failure) }
it 'notifies Sentry' do
expect(Gitlab::Sentry).to receive(:track_acceptable_exception)
.with(instance_of(StandardError), hash_including(extra: { cluster_id: cluster.id }))
subject
end
end
end
end
end end
...@@ -158,46 +158,6 @@ describe Clusters::ClusterPresenter do ...@@ -158,46 +158,6 @@ describe Clusters::ClusterPresenter do
it { is_expected.to include(cluster.name) } it { is_expected.to include(cluster.name) }
end end
describe '#can_toggle_cluster' do
let(:user) { create(:user) }
before do
allow(cluster).to receive(:current_user).and_return(user)
end
subject { described_class.new(cluster).can_toggle_cluster? }
context 'when user can update' do
before do
allow_any_instance_of(described_class).to receive(:can?).with(user, :update_cluster, cluster).and_return(true)
end
context 'when cluster is created' do
before do
allow(cluster).to receive(:created?).and_return(true)
end
it { is_expected.to eq(true) }
end
context 'when cluster is not created' do
before do
allow(cluster).to receive(:created?).and_return(false)
end
it { is_expected.to eq(false) }
end
end
context 'when user can not update' do
before do
allow_any_instance_of(described_class).to receive(:can?).with(user, :update_cluster, cluster).and_return(false)
end
it { is_expected.to eq(false) }
end
end
describe '#cluster_type_description' do describe '#cluster_type_description' do
subject { described_class.new(cluster).cluster_type_description } subject { described_class.new(cluster).cluster_type_description }
......
require 'spec_helper'
describe API::CircuitBreakers do
set(:user) { create(:user) }
set(:admin) { create(:admin) }
describe 'GET circuit_breakers/repository_storage' do
it 'returns a 401 for anonymous users' do
get api('/circuit_breakers/repository_storage')
expect(response).to have_gitlab_http_status(401)
end
it 'returns a 403 for users' do
get api('/circuit_breakers/repository_storage', user)
expect(response).to have_gitlab_http_status(403)
end
it 'returns an Array of storages' do
get api('/circuit_breakers/repository_storage', admin)
expect(response).to have_gitlab_http_status(200)
expect(json_response).to be_kind_of(Array)
expect(json_response).to be_empty
end
describe 'GET circuit_breakers/repository_storage/failing' do
it 'returns an array of failing storages' do
get api('/circuit_breakers/repository_storage/failing', admin)
expect(response).to have_gitlab_http_status(200)
expect(json_response).to be_kind_of(Array)
expect(json_response).to be_empty
end
end
end
describe 'DELETE circuit_breakers/repository_storage' do
it 'clears all circuit_breakers' do
delete api('/circuit_breakers/repository_storage', admin)
expect(response).to have_gitlab_http_status(204)
end
end
end
...@@ -44,6 +44,8 @@ Dir[Rails.root.join("spec/support/shared_contexts/*.rb")].each { |f| require f } ...@@ -44,6 +44,8 @@ Dir[Rails.root.join("spec/support/shared_contexts/*.rb")].each { |f| require f }
Dir[Rails.root.join("spec/support/shared_examples/*.rb")].each { |f| require f } Dir[Rails.root.join("spec/support/shared_examples/*.rb")].each { |f| require f }
Dir[Rails.root.join("spec/support/**/*.rb")].each { |f| require f } Dir[Rails.root.join("spec/support/**/*.rb")].each { |f| require f }
quality_level = Quality::TestLevel.new
RSpec.configure do |config| RSpec.configure do |config|
config.use_transactional_fixtures = false config.use_transactional_fixtures = false
config.use_instantiated_fixtures = false config.use_instantiated_fixtures = false
...@@ -55,9 +57,10 @@ RSpec.configure do |config| ...@@ -55,9 +57,10 @@ RSpec.configure do |config|
config.infer_spec_type_from_file_location! config.infer_spec_type_from_file_location!
config.full_backtrace = !!ENV['CI'] config.full_backtrace = !!ENV['CI']
config.define_derived_metadata(file_path: %r{/spec/}) do |metadata| config.define_derived_metadata(file_path: %r{(ee)?/spec/.+_spec\.rb\z}) do |metadata|
location = metadata[:location] location = metadata[:location]
metadata[:level] = quality_level.level_for(location)
metadata[:api] = true if location =~ %r{/spec/requests/api/} metadata[:api] = true if location =~ %r{/spec/requests/api/}
# do not overwrite type if it's already set # do not overwrite type if it's already set
......
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