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:
BUILD_ASSETS_IMAGE: "false"
before_script:
- bundle --version
- date
- source scripts/utils.sh
- date
- source scripts/prepare_build.sh
- date
......
......@@ -6,8 +6,9 @@
.use-pg-10: &use-pg-10
services:
- postgres:10.7
- redis:alpine
- name: postgres:10.7
command: ["postgres", "-c", "fsync=off", "-c", "synchronous_commit=off", "-c", "full_page_writes=off"]
- name: redis:alpine
.use-mysql: &use-mysql
services:
......@@ -52,8 +53,10 @@
script:
- JOB_NAME=( $CI_JOB_NAME )
- TEST_TOOL=${JOB_NAME[0]}
- export KNAPSACK_REPORT_PATH=knapsack/${CI_PROJECT_NAME}/${TEST_TOOL}_node_${CI_NODE_INDEX}_${CI_NODE_TOTAL}_report.json
- export KNAPSACK_GENERATE_REPORT=true
- TEST_LEVEL=${JOB_NAME[1]}
- 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 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
......@@ -63,7 +66,10 @@
- '[[ -f $FLAKY_RSPEC_REPORT_PATH ]] || echo "{}" > ${FLAKY_RSPEC_REPORT_PATH}'
- '[[ -f $NEW_FLAKY_RSPEC_REPORT_PATH ]] || echo "{}" > ${NEW_FLAKY_RSPEC_REPORT_PATH}'
- 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:
expire_in: 31d
when: always
......@@ -140,19 +146,68 @@ setup-test-env:
except:
- /(^docs[\/-].*|.*-docs$)/
rspec-pg:
rspec unit pg:
<<: *rspec-metadata-pg
parallel: 20
rspec integration 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
<<: *only-schedules-master
parallel: 50
parallel: 24
rspec-mysql:
rspec unit mysql:
<<: *rspec-metadata-mysql
<<: *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-metadata-pg
......@@ -164,16 +219,17 @@ rspec-fast-spec-helper:
script:
- export CACHE_CLASSES=true
- 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-quarantine
allow_failure: true
rspec-mysql-quarantine:
rspec quarantine mysql:
<<: *rspec-metadata-mysql
<<: *rspec-quarantine
<<: *only-schedules-master
allow_failure: true
static-analysis:
......
......@@ -40,12 +40,12 @@ update-tests-metadata:
policy: push
script:
- 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
- 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'
- rm -f knapsack/${CI_PROJECT_NAME}/*_node_*.json
- rm -f rspec_flaky/all_*.json rspec_flaky/new_*.json
- scripts/insert-rspec-profiling-data
only:
......
......@@ -361,7 +361,7 @@ group :development, :test do
gem 'scss_lint', '~> 0.56.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 'benchmark-ips', '~> 2.3.0', require: false
......@@ -372,6 +372,7 @@ group :development, :test do
gem 'activerecord_sane_schema_dumper', '1.0'
gem 'stackprof', '~> 0.2.10', require: false
gem 'derailed_benchmarks', require: false
gem 'simple_po_parser', '~> 1.1.2', require: false
......@@ -399,6 +400,8 @@ gem 'html2text'
gem 'ruby-prof', '~> 0.17.0'
gem 'rbtrace', '~> 0.4', require: false
gem 'memory_profiler', '~> 0.9', require: false
gem 'benchmark-memory', '~> 0.1', require: false
# OAuth
gem 'oauth2', '~> 1.4'
......
......@@ -82,6 +82,8 @@ GEM
bcrypt (3.1.12)
bcrypt_pbkdf (1.0.0)
benchmark-ips (2.3.0)
benchmark-memory (0.1.2)
memory_profiler (~> 0.9)
better_errors (2.5.0)
coderay (>= 1.0.0)
erubi (>= 1.0.0)
......@@ -155,6 +157,14 @@ GEM
html-pipeline
declarative (0.0.10)
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)
thread_safe (~> 0.3, >= 0.3.1)
device_detector (1.0.0)
......@@ -174,7 +184,7 @@ GEM
diffy (3.1.0)
discordrb-webhooks-blackst0ne (3.3.0)
rest-client (~> 2.0)
docile (1.1.5)
docile (1.3.1)
domain_name (0.5.20180417)
unf (>= 0.0.5, < 1.0.0)
doorkeeper (4.3.2)
......@@ -377,6 +387,7 @@ GEM
hashie (>= 3.0)
health_check (2.6.0)
rails (>= 4.0)
heapy (0.1.4)
hipchat (1.5.2)
httparty
mimemagic
......@@ -478,6 +489,7 @@ GEM
memoist (0.16.0)
memoizable (0.4.2)
thread_safe (~> 0.3, >= 0.3.1)
memory_profiler (0.9.13)
method_source (0.9.2)
mime-types (3.2.2)
mime-types-data (~> 3.2015)
......@@ -873,11 +885,11 @@ GEM
jwt (>= 1.5, < 3.0)
multi_json (~> 1.10)
simple_po_parser (1.1.2)
simplecov (0.14.1)
docile (~> 1.1.0)
simplecov (0.16.1)
docile (~> 1.1)
json (>= 1.8, < 3)
simplecov-html (~> 0.10.0)
simplecov-html (0.10.0)
simplecov-html (0.10.2)
slack-notifier (1.5.1)
spring (2.0.2)
activesupport (>= 4.2)
......@@ -1014,6 +1026,7 @@ DEPENDENCIES
batch-loader (~> 1.4.0)
bcrypt_pbkdf (~> 1.0)
benchmark-ips (~> 2.3.0)
benchmark-memory (~> 0.1)
better_errors (~> 2.5.0)
binding_of_caller (~> 0.8.0)
bootsnap (~> 1.4)
......@@ -1034,6 +1047,7 @@ DEPENDENCIES
creole (~> 0.5.0)
database_cleaner (~> 1.7.0)
deckar01-task_list (= 2.2.0)
derailed_benchmarks
device_detector
devise (~> 4.6)
devise-two-factor (~> 3.0.0)
......@@ -1111,6 +1125,7 @@ DEPENDENCIES
lograge (~> 0.5)
loofah (~> 2.2)
mail_room (~> 0.9.1)
memory_profiler (~> 0.9)
method_source (~> 0.8)
mimemagic (~> 0.3.2)
mini_magick
......@@ -1203,7 +1218,7 @@ DEPENDENCIES
sidekiq (~> 5.2.7)
sidekiq-cron (~> 1.0)
simple_po_parser (~> 1.1.2)
simplecov (~> 0.14.0)
simplecov (~> 0.16.1)
slack-notifier (~> 1.5.1)
spring (~> 2.0.0)
spring-commands-rspec (~> 1.0.4)
......
import { sprintf, __ } from '~/locale';
export default {
computed: {
resolveButtonTitle() {
let title = 'Mark comment as resolved';
let title = __('Mark comment as resolved');
if (this.resolvedBy) {
title = `Resolved by ${this.resolvedBy.name}`;
title = sprintf(__('Resolved by %{name}'), { name: this.resolvedBy.name });
}
return title;
......
import { __ } from '~/locale';
const notImplemented = () => {
throw new Error('Not implemented!');
throw new Error(__('Not implemented!'));
};
export default {
......
import * as mutationTypes from './mutation_types';
import { __ } from '~/locale';
const notImplemented = () => {
throw new Error('Not implemented!');
throw new Error(__('Not implemented!'));
};
export default {
......
import Visibility from 'visibilityjs';
import Vue from 'vue';
import AccessorUtilities from '~/lib/utils/accessor';
import { GlToast } from '@gitlab/ui';
import PersistentUserCallout from '../persistent_user_callout';
import { s__, sprintf } from '../locale';
......@@ -43,8 +44,10 @@ export default class Clusters {
helpPath,
ingressHelpPath,
ingressDnsHelpPath,
clusterId,
} = document.querySelector('.js-edit-cluster-form').dataset;
this.clusterId = clusterId;
this.store = new ClustersStore();
this.store.setHelpPaths(helpPath, ingressHelpPath, ingressDnsHelpPath);
this.store.setManagePrometheusPath(managePrometheusPath);
......@@ -69,6 +72,10 @@ export default class Clusters {
this.errorContainer = document.querySelector('.js-cluster-error');
this.successContainer = document.querySelector('.js-cluster-success');
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.successApplicationContainer = document.querySelector('.js-cluster-application-notice');
this.showTokenButton = document.querySelector('.js-show-cluster-token');
......@@ -125,6 +132,13 @@ export default class Clusters {
PersistentUserCallout.factory(callout);
}
addBannerCloseHandler(el, status) {
el.querySelector('.js-close-banner').addEventListener('click', () => {
el.classList.add('hidden');
this.setBannerDismissedState(status, true);
});
}
addListeners() {
if (this.showTokenButton) this.showTokenButton.addEventListener('click', this.showToken);
eventHub.$on('installApplication', this.installApplication);
......@@ -133,6 +147,9 @@ export default class Clusters {
eventHub.$on('saveKnativeDomain', data => this.saveKnativeDomain(data));
eventHub.$on('setKnativeHostname', data => this.setKnativeHostname(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() {
......@@ -205,6 +222,8 @@ export default class Clusters {
this.errorContainer.classList.add('hidden');
this.successContainer.classList.add('hidden');
this.creatingContainer.classList.add('hidden');
this.unreachableContainer.classList.add('hidden');
this.authenticationFailureContainer.classList.add('hidden');
}
checkForNewInstalls(prevApplicationMap, newApplicationMap) {
......@@ -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) {
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
if (this.store.state.status !== 'created' || prevStatus !== this.store.state.status) {
switch (status) {
......@@ -241,6 +283,12 @@ export default class Clusters {
this.errorContainer.classList.remove('hidden');
this.errorReasonContainer.textContent = error;
break;
case 'unreachable':
this.unreachableContainer.classList.remove('hidden');
break;
case 'authentication_failure':
this.authenticationFailureContainer.classList.remove('hidden');
break;
case 'scheduled':
case 'creating':
this.creatingContainer.classList.remove('hidden');
......
......@@ -6,6 +6,7 @@ import 'core-js/fn/array/from';
import 'core-js/fn/array/includes';
import 'core-js/fn/object/assign';
import 'core-js/fn/object/values';
import 'core-js/fn/object/entries';
import 'core-js/fn/promise';
import 'core-js/fn/promise/finally';
import 'core-js/fn/string/code-point-at';
......
import Vue from 'vue';
import ErrorTrackingSettings from './components/app.vue';
import createStore from './store';
import initSettingsPanels from '~/settings_panels';
export default () => {
initSettingsPanels();
const formContainerEl = document.querySelector('.js-error-tracking-form');
const {
dataset: { apiHost, enabled, project, token, listProjectsEndpoint, operationsSettingsEndpoint },
......
......@@ -3,12 +3,12 @@ import { InMemoryCache } from 'apollo-cache-inmemory';
import { createUploadLink } from 'apollo-upload-client';
import csrf from '~/lib/utils/csrf';
export default (resolvers = {}, baseUrl = '') => {
export default (resolvers = {}, config = {}) => {
let uri = `${gon.relative_url_root}/api/graphql`;
if (baseUrl) {
if (config.baseUrl) {
// Prepend baseUrl and ensure that `///` are replaced with `/`
uri = `${baseUrl}${uri}`.replace(/\/{3,}/g, '/');
uri = `${config.baseUrl}${uri}`.replace(/\/{3,}/g, '/');
}
return new ApolloClient({
......@@ -18,7 +18,7 @@ export default (resolvers = {}, baseUrl = '') => {
[csrf.headerKey]: csrf.token,
},
}),
cache: new InMemoryCache(),
cache: new InMemoryCache(config.cacheConfig),
resolvers,
});
};
<script>
import { GlLoadingIcon } from '@gitlab/ui';
import createFlash from '~/flash';
import { sprintf, __ } from '../../../locale';
import getRefMixin from '../../mixins/get_ref';
import getFiles from '../../queries/getFiles.graphql';
import getProjectPath from '../../queries/getProjectPath.graphql';
import TableHeader from './header.vue';
import TableRow from './row.vue';
import ParentRow from './parent_row.vue';
const PAGE_SIZE = 100;
export default {
components: {
GlLoadingIcon,
TableHeader,
TableRow,
ParentRow,
},
mixins: [getRefMixin],
apollo: {
files: {
query: getFiles,
variables() {
return {
ref: this.ref,
path: this.path,
};
},
projectPath: {
query: getProjectPath,
},
},
props: {
......@@ -32,7 +32,14 @@ export default {
},
data() {
return {
files: [],
projectPath: '',
nextPageCursor: '',
entries: {
trees: [],
submodules: [],
blobs: [],
},
isLoadingFiles: false,
};
},
computed: {
......@@ -42,8 +49,66 @@ export default {
{ path: this.path, ref: this.ref },
);
},
isLoadingFiles() {
return this.$apollo.queries.files.loading;
showParentRow() {
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 {
tableCaption
}}
</caption>
<table-header />
<table-header v-once />
<tbody>
<table-row
v-for="entry in files"
:id="entry.id"
:key="entry.id"
:path="entry.flatPath"
:type="entry.type"
/>
<parent-row v-show="showParentRow" :commit-ref="ref" :path="path" />
<template v-for="val in entries">
<table-row
v-for="entry in val"
:id="entry.id"
:key="`${entry.flatPath}-${entry.id}`"
:current-path="path"
:path="entry.flatPath"
:type="entry.type"
/>
</template>
</tbody>
</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>
</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 {
mixins: [getRefMixin],
props: {
id: {
type: Number,
type: String,
required: true,
},
currentPath: {
type: String,
required: true,
},
path: {
......@@ -26,7 +30,7 @@ export default {
return `fa-${getIconName(this.type, this.path)}`;
},
isFolder() {
return this.type === 'folder';
return this.type === 'tree';
},
isSubmodule() {
return this.type === 'commit';
......@@ -34,6 +38,12 @@ export default {
linkComponent() {
return this.isFolder ? 'router-link' : 'a';
},
fullPath() {
return this.path.replace(new RegExp(`^${this.currentPath}/`), '');
},
shortSha() {
return this.id.slice(0, 8);
},
},
methods: {
openRow() {
......@@ -49,9 +59,11 @@ export default {
<tr v-once :class="`file_${id}`" class="tree-item" @click="openRow">
<td class="tree-item-file-name">
<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">
@ <a href="#" class="commit-sha">{{ id }}</a>
@ <a href="#" class="commit-sha">{{ shortSha }}</a>
</template>
</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 VueApollo from 'vue-apollo';
import { IntrospectionFragmentMatcher } from 'apollo-cache-inmemory';
import createDefaultClient from '~/lib/graphql';
import introspectionQueryResultData from './fragmentTypes.json';
Vue.use(VueApollo);
const defaultClient = createDefaultClient({
Query: {
files() {
return [
{
__typename: 'file',
id: 1,
name: 'app',
flatPath: 'app',
type: 'folder',
},
{
__typename: 'file',
id: 2,
name: 'gitlab-svg',
flatPath: 'gitlab-svg',
type: 'commit',
},
{
__typename: 'file',
id: 3,
name: 'index.js',
flatPath: 'index.js',
type: 'blob',
},
{
__typename: 'file',
id: 4,
name: 'test.pdf',
flatPath: 'fixtures/test.pdf',
type: 'blob',
},
];
// We create a fragment matcher so that we can create a fragment from an interface
// Without this, Apollo throws a heuristic fragment matcher warning
const fragmentMatcher = new IntrospectionFragmentMatcher({
introspectionQueryResultData,
});
const defaultClient = createDefaultClient(
{},
{
cacheConfig: {
fragmentMatcher,
dataIdFromObject: obj => {
// eslint-disable-next-line no-underscore-dangle
switch (obj.__typename) {
// We need to create a dynamic ID for each entry
// Each entry can have the same ID as the ID is a commit ID
// So we create a unique cache ID with the path and the ID
case 'TreeEntry':
case 'Submodule':
case 'Blob':
return `${obj.flatPath}-${obj.id}`;
default:
// If the type doesn't match any of the above we fallback
// to using the default Apollo ID
// eslint-disable-next-line no-underscore-dangle
return obj.id || obj._id;
}
},
},
},
});
);
export default new VueApollo({
defaultClient,
......
query getFiles($path: String!, $ref: String!) {
files(path: $path, ref: $ref) @client {
id
flatPath
type
fragment TreeEntry on Entry {
id
flatPath
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) {
mode: 'history',
base: joinPaths(gon.relative_url_root || '', base),
routes: [
{
path: '/',
name: 'projectRoot',
component: IndexPage,
},
{
path: `/tree/${baseRef}(/.*)?`,
name: 'treePath',
component: TreePage,
props: route => ({
path: route.params.pathMatch,
path: route.params.pathMatch.replace(/^\//, ''),
}),
beforeEnter(to, from, next) {
document
......@@ -31,6 +26,11 @@ export default function createRouter(base, baseRef) {
next();
},
},
{
path: '/',
name: 'projectRoot',
component: IndexPage,
},
],
});
}
const entryTypeIcons = {
folder: 'folder',
tree: 'folder',
commit: 'archive',
};
......
......@@ -2,6 +2,7 @@ import $ from 'jquery';
import axios from './lib/utils/axios_utils';
import Flash, { hideFlash } from './flash';
import { parseBoolean } from './lib/utils/common_utils';
import { __ } from './locale';
export default () => {
$('body').on('click', '.js-usage-consent-action', e => {
......@@ -25,7 +26,7 @@ export default () => {
})
.catch(() => {
hideConsentMessage();
Flash('Something went wrong. Try again later.');
Flash(__('Something went wrong. Try again later.'));
});
});
};
......@@ -5,7 +5,7 @@
import $ from 'jquery';
import _ from 'underscore';
import axios from './lib/utils/axios_utils';
import { __ } from './locale';
import { s__, __, sprintf } from './locale';
import ModalStore from './boards/stores/modal_store';
// TODO: remove eventHub hack after code splitting refactor
......@@ -157,14 +157,20 @@ function UsersSelect(currentUser, els, options = {}) {
.get(0);
if (selectedUsers.length === 0) {
return 'Unassigned';
return s__('UsersSelect|Unassigned');
} else if (selectedUsers.length === 1) {
return firstUser.name;
} else if (isSelected) {
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 {
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 = {}) {
tooltipTitle = _.escape(user.name);
} else {
user = {
name: 'Unassigned',
name: s__('UsersSelect|Unassigned'),
username: '',
avatar: '',
};
tooltipTitle = __('Assignee');
tooltipTitle = s__('UsersSelect|Assignee');
}
$value.html(assigneeTemplate(user));
$collapsedSidebar.attr('title', tooltipTitle).tooltip('_fixTitle');
......@@ -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> <% } %>',
);
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({
showMenuAbove: showMenuAbove,
......@@ -302,7 +312,7 @@ function UsersSelect(currentUser, els, options = {}) {
showDivider += 1;
users.unshift({
beforeDivider: true,
name: 'Unassigned',
name: s__('UsersSelect|Unassigned'),
id: 0,
});
}
......@@ -310,7 +320,7 @@ function UsersSelect(currentUser, els, options = {}) {
showDivider += 1;
name = showAnyUser;
if (name === true) {
name = 'Any User';
name = s__('UsersSelect|Any User');
}
anyUser = {
beforeDivider: true,
......@@ -596,7 +606,7 @@ function UsersSelect(currentUser, els, options = {}) {
showEmailUser = $(select).data('emailUser');
firstUser = $(select).data('firstUser');
return $(select).select2({
placeholder: 'Search for a user',
placeholder: __('Search for a user'),
multiple: $(select).hasClass('multiselect'),
minimumInputLength: 0,
query: function(query) {
......@@ -621,7 +631,7 @@ function UsersSelect(currentUser, els, options = {}) {
}
if (showNullUser) {
nullUser = {
name: 'Unassigned',
name: s__('UsersSelect|Unassigned'),
id: 0,
};
data.results.unshift(nullUser);
......@@ -629,7 +639,7 @@ function UsersSelect(currentUser, els, options = {}) {
if (showAnyUser) {
name = showAnyUser;
if (name === true) {
name = 'Any User';
name = s__('UsersSelect|Any User');
}
anyUser = {
name: name,
......@@ -645,7 +655,7 @@ function UsersSelect(currentUser, els, options = {}) {
) {
var trimmed = query.term.trim();
emailUser = {
name: 'Invite "' + trimmed + '" by email',
name: sprintf(__('Invite "%{trimmed}" by email'), { trimmed }),
username: trimmed,
id: trimmed,
invite: true,
......@@ -688,7 +698,7 @@ UsersSelect.prototype.initSelection = function(element, callback) {
id = $(element).val();
if (id === '0') {
nullUser = {
name: 'Unassigned',
name: s__('UsersSelect|Unassigned'),
};
return callback(nullUser);
} else if (id !== '') {
......
......@@ -49,7 +49,7 @@ export default {
required: false,
default: () => ({
sourceProjectId: '',
issueId: '',
mergeRequestId: '',
appUrl: '',
}),
},
......
......@@ -48,7 +48,7 @@ export default {
visualReviewAppMeta() {
return {
appUrl: this.mr.appUrl,
issueId: this.mr.iid,
mergeRequestId: this.mr.iid,
sourceProjectId: this.mr.sourceProjectId,
};
},
......
......@@ -14,7 +14,7 @@ export default {
</script>
<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>
<i v-tooltip :title="tooltipTitle" :aria-label="tooltipTitle" class="fa fa-question-circle">
</i>
......
......@@ -333,41 +333,45 @@ export default {
<div class="mr-widget-section">
<component :is="componentName" :mr="mr" :service="service" />
<section v-if="shouldRenderCollaborationStatus" class="mr-info-list mr-links">
{{ s__('mrWidget|Allows commits from members who can merge to the target branch') }}
</section>
<div class="mr-widget-info">
<section v-if="shouldRenderCollaborationStatus" class="mr-info-list mr-links">
<p>
{{ s__('mrWidget|Allows commits from members who can merge to the target branch') }}
</p>
</section>
<mr-widget-related-links
v-if="shouldRenderRelatedLinks"
:state="mr.state"
:related-links="mr.relatedLinks"
/>
<mr-widget-related-links
v-if="shouldRenderRelatedLinks"
:state="mr.state"
:related-links="mr.relatedLinks"
/>
<mr-widget-alert-message
v-if="showMergePipelineForkWarning"
type="warning"
:help-path="mr.mergeRequestPipelinesHelpPath"
>
{{
s__(
'mrWidget|Fork merge requests do not create merge request pipelines which validate a post merge result',
)
}}
</mr-widget-alert-message>
<mr-widget-alert-message
v-if="showMergePipelineForkWarning"
type="warning"
:help-path="mr.mergeRequestPipelinesHelpPath"
>
{{
s__(
'mrWidget|Fork merge requests do not create merge request pipelines which validate a post merge result',
)
}}
</mr-widget-alert-message>
<mr-widget-alert-message
v-if="showTargetBranchAdvancedError"
type="danger"
:help-path="mr.mergeRequestPipelinesHelpPath"
>
{{
s__(
'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
v-if="showTargetBranchAdvancedError"
type="danger"
:help-path="mr.mergeRequestPipelinesHelpPath"
>
{{
s__(
'mrWidget|The target branch has advanced, which invalidates the merge request pipeline. Please update the source branch and retry merging',
)
}}
</mr-widget-alert-message>
<source-branch-removal-status v-if="shouldRenderSourceBranchRemovalStatus" />
<source-branch-removal-status v-if="shouldRenderSourceBranchRemovalStatus" />
</div>
</div>
<div v-if="shouldRenderMergeHelp" class="mr-widget-footer"><mr-widget-merge-help /></div>
</div>
......
......@@ -71,11 +71,15 @@ export default {
</div>
<div class="text-secondary">
<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>
</div>
<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>
</div>
<gl-skeleton-loading
......@@ -88,7 +92,7 @@ export default {
<icon
v-show="!locationIsLoading && user.location"
name="location"
css-classes="category-icon"
css-classes="category-icon flex-shrink-0"
/>
<span class="ml-1">{{ user.location }}</span>
<gl-skeleton-loading
......
@mixin avatar-size($size, $margin-right) {
width: $size;
height: $size;
margin-right: $margin-right;
}
.avatar-circle {
float: left;
margin-right: 15px;
border-radius: $avatar-radius;
border: 1px solid $gray-normal;
&.s16 { @include avatar-size(16px, 6px); }
&.s18 { @include avatar-size(18px, 6px); }
&.s19 { @include avatar-size(19px, 6px); }
&.s20 { @include avatar-size(20px, 7px); }
&.s16 { @include avatar-size(16px, 8px); }
&.s18 { @include avatar-size(18px, 8px); }
&.s19 { @include avatar-size(19px, 8px); }
&.s20 { @include avatar-size(20px, 8px); }
&.s24 { @include avatar-size(24px, 8px); }
&.s26 { @include avatar-size(26px, 8px); }
&.s32 { @include avatar-size(32px, 10px); }
&.s36 { @include avatar-size(36px, 10px); }
&.s40 { @include avatar-size(40px, 10px); }
&.s46 { @include avatar-size(46px, 15px); }
&.s48 { @include avatar-size(48px, 10px); }
&.s60 { @include avatar-size(60px, 12px); }
&.s64 { @include avatar-size(64px, 14px); }
&.s70 { @include avatar-size(70px, 14px); }
&.s90 { @include avatar-size(90px, 15px); }
&.s100 { @include avatar-size(100px, 15px); }
&.s110 { @include avatar-size(110px, 15px); }
&.s140 { @include avatar-size(140px, 15px); }
&.s160 { @include avatar-size(160px, 20px); }
&.s32 { @include avatar-size(32px, 8px); }
&.s36 { @include avatar-size(36px, 16px); }
&.s40 { @include avatar-size(40px, 16px); }
&.s46 { @include avatar-size(46px, 16px); }
&.s48 { @include avatar-size(48px, 16px); }
&.s60 { @include avatar-size(60px, 16px); }
&.s64 { @include avatar-size(64px, 16px); }
&.s70 { @include avatar-size(70px, 16px); }
&.s90 { @include avatar-size(90px, 16px); }
&.s96 { @include avatar-size(96px, 16px); }
&.s100 { @include avatar-size(100px, 16px); }
&.s110 { @include avatar-size(110px, 16px); }
&.s140 { @include avatar-size(140px, 16px); }
&.s160 { @include avatar-size(160px, 16px); }
}
.avatar {
......@@ -39,6 +34,7 @@
padding: 0;
background: $gray-lightest;
overflow: hidden;
border-color: rgba($black, $gl-avatar-border-opacity);
&.avatar-inline {
float: none;
......@@ -64,41 +60,37 @@
&.avatar-placeholder {
border: 0;
}
&:not([href]):hover {
border-color: darken($gray-normal, 10%);
}
}
.identicon {
text-align: center;
vertical-align: top;
color: $gl-gray-700;
color: $gray-800;
background-color: $gray-darker;
// Sizes
&.s16 { font-size: 12px;
line-height: 1.33; }
&.s16 { font-size: 10px;
line-height: 16px; }
&.s24 { font-size: 13px;
line-height: 1.8; }
&.s24 { font-size: 12px;
line-height: 24px; }
&.s26 { font-size: 20px;
line-height: 1.33; }
&.s32 { font-size: 20px;
line-height: 30px; }
&.s32 { font-size: 14px;
line-height: 32px; }
&.s40 { font-size: 16px;
line-height: 38px; }
&.s48 { font-size: 20px;
line-height: 46px; }
line-height: 48px; }
&.s60 { font-size: 32px;
line-height: 58px; }
&.s64 { font-size: 32px;
&.s64 { font-size: 28px;
line-height: 64px; }
&.s70 { font-size: 34px;
......@@ -107,6 +99,9 @@
&.s90 { font-size: 36px;
line-height: 88px; }
&.s96 { font-size: 48px;
line-height: 96px; }
&.s100 { font-size: 36px;
line-height: 98px; }
......@@ -144,7 +139,6 @@
.avatar {
border-radius: 0;
border: 0;
height: auto;
width: 100%;
margin: 0;
......
......@@ -8,7 +8,6 @@
@import 'framework/animations';
@import 'framework/vue_transitions';
@import 'framework/avatar';
@import 'framework/asciidoctor';
@import 'framework/banner';
@import 'framework/blocks';
......
......@@ -21,7 +21,7 @@
}
@mixin btn-default {
border-radius: 3px;
border-radius: $border-radius-default;
font-size: $gl-font-size;
font-weight: $gl-font-weight-normal;
padding: $gl-vert-padding $gl-btn-padding;
......@@ -37,7 +37,7 @@
@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;
color: $text;
border-color: $border;
......@@ -61,13 +61,22 @@
}
}
&:focus {
box-shadow: 0 0 4px 1px $blue-300;
}
&:active {
background-color: $active-background;
border-color: $active-border;
color: $hover-text;
box-shadow: inset 0 2px 4px 0 rgba($black, 0.2);
color: $active-text;
> .icon {
color: $hover-text;
color: $active-text;
}
&:focus {
box-shadow: inset 0 2px 4px 0 rgba($black, 0.2);
}
}
}
......@@ -164,21 +173,21 @@
&.btn-inverted {
&.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-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 {
@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-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 @@
&.btn-close,
&.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 {
@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,
......@@ -402,7 +411,7 @@
.btn-inverted {
&-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 @@
*/
&.code {
padding: 0;
border-radius: 0 0 $border-radius-default $border-radius-default;
}
.list-inline.previews {
......
......@@ -280,3 +280,7 @@ label {
max-width: $input-lg-width;
width: 100%;
}
.input-group-text {
max-height: $input-height;
}
......@@ -8,7 +8,7 @@
pre {
padding: 10px 0;
border: 0;
border-radius: 0;
border-radius: 0 0 $border-radius-default $border-radius-default;
font-family: $monospace-font;
font-size: $code-font-size;
line-height: 19px;
......@@ -42,6 +42,7 @@
padding: 10px;
text-align: right;
float: left;
border-bottom-left-radius: $border-radius-default;
a {
font-family: $monospace-font;
......
......@@ -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
*/
$avatar-radius: 50%;
$gl-avatar-size: 40px;
$gl-avatar-border-opacity: 0.1;
/*
* Blame
......
......@@ -7,6 +7,7 @@ $secondary: $gray-light;
$input-disabled-bg: $gray-light;
$input-border-color: $gray-200;
$input-color: $gl-text-color;
$input-font-size: $gl-font-size;
$font-family-sans-serif: $regular-font;
$font-family-monospace: $monospace-font;
$btn-line-height: 20px;
......
......@@ -69,6 +69,8 @@
align-self: flex-start;
font-weight: 500;
font-size: 20px;
color: $orange-900;
opacity: 1;
margin: $gl-padding-8 14px 0 0;
}
......
......@@ -494,6 +494,12 @@ table.code {
}
}
.line_holder:last-of-type {
td:first-child {
border-bottom-left-radius: $border-radius-default;
}
}
&.left-side-selected {
td.line_content.parallel.right-side {
user-select: none;
......
......@@ -21,13 +21,6 @@
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 {
font-size: 22px;
}
......
......@@ -87,6 +87,11 @@
padding: $gl-padding;
}
.mr-widget-info {
padding-left: $gl-padding-50 - $gl-padding-32;
padding-right: $gl-padding;
}
.mr-state-widget {
color: $gl-text-color;
......@@ -560,6 +565,10 @@
.mr-links {
padding-left: $status-icon-size + $gl-btn-padding;
&:last-child {
padding-bottom: $gl-padding;
}
}
.mr-info-list {
......@@ -1030,11 +1039,6 @@
background: $black-transparent;
}
.source-branch-removal-status {
padding-left: 50px;
padding-bottom: $gl-padding;
}
.mr-compare {
.diff-file .file-title-flex-parent {
top: $header-height + 51px;
......
......@@ -5,8 +5,10 @@ module Clusters
include Presentable
include Gitlab::Utils::StrongMemoize
include FromUnion
include ReactiveCaching
self.table_name = 'clusters'
self.reactive_cache_key = -> (cluster) { [cluster.class.model_name.singular, cluster.id] }
PROJECT_ONLY_APPLICATIONS = {
Applications::Jupyter.application_name => Applications::Jupyter,
......@@ -57,6 +59,8 @@ module Clusters
validate :no_groups, unless: :group_type?
validate :no_projects, unless: :project_type?
after_save :clear_reactive_cache!
delegate :status, to: :provider, allow_nil: true
delegate :status_reason, to: :provider, allow_nil: true
delegate :on_creation?, to: :provider, allow_nil: true
......@@ -123,15 +127,19 @@ module Clusters
end
def status_name
if provider
provider.status_name
else
:created
provider&.status_name || connection_status.presence || :created
end
def connection_status
with_reactive_cache do |data|
data[:connection_status]
end
end
def created?
status_name == :created
def calculate_reactive_cache
return unless enabled?
{ connection_status: retrieve_connection_status }
end
def applications
......@@ -221,6 +229,51 @@ module Clusters
@instance_domain ||= Gitlab::CurrentSettings.auto_devops_domain
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
if provider&.on_creation?
errors.add(:base, "cannot modify during creation")
......
......@@ -13,6 +13,7 @@ class Identity < ApplicationRecord
before_save :ensure_normalized_extern_uid, if: :extern_uid_changed?
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_extern_uid, ->(provider, extern_uid) do
iwhere(extern_uid: normalize_uid(provider, extern_uid)).with_provider(provider)
......
......@@ -22,10 +22,6 @@ module Clusters
"https://console.cloud.google.com/kubernetes/clusters/details/#{provider.zone}/#{name}" if gcp?
end
def can_toggle_cluster?
can?(current_user, :update_cluster, cluster) && created?
end
def can_read_cluster?
can?(current_user, :read_cluster, cluster)
end
......
......@@ -11,12 +11,14 @@
= f.hidden_field :user_id
.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
- name = "#{@abuse_report.user.name} (@#{@abuse_report.user.username})"
= text_field_tag :user_name, name, class: "form-control", readonly: true
.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
= f.text_area :message, class: "form-control", rows: 2, required: true, value: sanitize(@ref_url)
.form-text.text-muted
......
......@@ -2,13 +2,15 @@
= form_errors(application)
= 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
= f.text_field :name, class: 'form-control'
= doorkeeper_errors_for application, :name
= 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
= f.text_area :redirect_uri, class: 'form-control'
= doorkeeper_errors_for application, :redirect_uri
......@@ -21,14 +23,16 @@
for local tests
= 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
= f.check_box :trusted
%span.form-text.text-muted
Trusted applications are automatically authorized on GitLab OAuth flow.
.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
= render 'shared/tokens/scopes_form', prefix: 'doorkeeper_application', token: application, scopes: @scopes
......
......@@ -10,7 +10,8 @@
= form_errors(@broadcast_message)
.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
= f.text_area :message, class: "form-control js-autosize",
required: true,
......@@ -20,19 +21,23 @@
.col-sm-10.offset-sm-2
= link_to 'Customize colors', '#', class: 'js-toggle-colors-link'
.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
= f.color_field :color, class: "form-control"
.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
= f.color_field :font, class: "form-control"
.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
= f.datetime_select :starts_at, {}, class: 'form-control form-control-inline'
.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
= f.datetime_select :ends_at, {}, class: 'form-control form-control-inline'
.form-actions
......
......@@ -6,7 +6,8 @@
= render_if_exists 'admin/namespace_plan', f: f
.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
= render 'shared/choose_avatar_button', f: f
......
......@@ -2,12 +2,14 @@
= form_errors(@identity)
.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
- 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'
.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
= f.text_field :extern_uid, class: 'form-control', required: true
......
......@@ -2,15 +2,18 @@
= form_errors(@label)
.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
= f.text_field :title, class: "form-control", required: true
.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
= f.text_field :description, class: "form-control js-quick-submit"
.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
.input-group
.input-group-prepend
......
......@@ -117,7 +117,8 @@
.card-body
= form_for @project, url: transfer_admin_project_path(@project), method: :put do |f|
.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
.dropdown
= dropdown_toggle('Search for Namespace', { toggle: 'dropdown', field_name: 'new_namespace_id' }, { toggle_class: 'js-namespace-select large' })
......
%fieldset
%legend Access
.form-group.row
.col-sm-2.text-right
= f.label :projects_limit, class: 'col-form-label'
.col-sm-10= f.number_field :projects_limit, min: 0, max: Gitlab::Database::MAX_INT_VALUE, class: 'form-control'
.col-sm-2.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'
.form-group.row
.col-sm-2.text-right
= f.label :can_create_group, class: 'col-form-label'
.col-sm-10= f.check_box :can_create_group
.col-sm-2.col-form-label
= f.label :can_create_group
.col-sm-10
= f.check_box :can_create_group
.form-group.row
.col-sm-2.text-right
= f.label :access_level, class: 'col-form-label'
.col-sm-2.col-form-label
= f.label :access_level
.col-sm-10
- editing_current_user = (current_user == @user)
......@@ -34,8 +36,8 @@
You cannot remove your own admin rights.
.form-group.row
.col-sm-2.text-right
= f.label :external, class: 'col-form-label'
.col-sm-2.col-form-label
= f.label :external
.hidden{ data: user_internal_regex_data }
.col-sm-10
= f.check_box :external do
......
......@@ -5,20 +5,20 @@
%fieldset
%legend Account
.form-group.row
.col-sm-2.text-right
= f.label :name, class: 'col-form-label'
.col-sm-2.col-form-label
= f.label :name
.col-sm-10
= f.text_field :name, required: true, autocomplete: 'off', class: 'form-control'
%span.help-inline * required
.form-group.row
.col-sm-2.text-right
= f.label :username, class: 'col-form-label'
.col-sm-2.col-form-label
= f.label :username
.col-sm-10
= f.text_field :username, required: true, autocomplete: 'off', autocorrect: 'off', autocapitalize: 'off', spellcheck: false, class: 'form-control'
%span.help-inline * required
.form-group.row
.col-sm-2.text-right
= f.label :email, class: 'col-form-label'
.col-sm-2.col-form-label
= f.label :email
.col-sm-10
= f.text_field :email, required: true, autocomplete: 'off', class: 'form-control'
%span.help-inline * required
......@@ -27,8 +27,8 @@
%fieldset
%legend Password
.form-group.row
.col-sm-2.text-right
= f.label :password, class: 'col-form-label'
.col-sm-2.col-form-label
= f.label :password
.col-sm-10
%strong
Reset link will be generated and sent to the user.
......@@ -38,13 +38,15 @@
%fieldset
%legend Password
.form-group.row
.col-sm-2.text-right
= f.label :password, class: 'col-form-label'
.col-sm-10= f.password_field :password, disabled: f.object.force_random_password, class: 'form-control'
.col-sm-2.col-form-label
= f.label :password
.col-sm-10
= f.password_field :password, disabled: f.object.force_random_password, class: 'form-control'
.form-group.row
.col-sm-2.text-right
= f.label :password_confirmation, class: 'col-form-label'
.col-sm-10= f.password_field :password_confirmation, disabled: f.object.force_random_password, class: 'form-control'
.col-sm-2.col-form-label
= f.label :password_confirmation
.col-sm-10
= f.password_field :password_confirmation, disabled: f.object.force_random_password, class: 'form-control'
= render partial: 'access_levels', locals: { f: f }
......@@ -55,27 +57,31 @@
%fieldset
%legend Profile
.form-group.row
.col-sm-2.text-right
= f.label :avatar, class: 'col-form-label'
.col-sm-2.col-form-label
= f.label :avatar
.col-sm-10
= f.file_field :avatar
.form-group.row
.col-sm-2.text-right
= f.label :skype, class: 'col-form-label'
.col-sm-10= f.text_field :skype, class: 'form-control'
.col-sm-2.col-form-label
= f.label :skype
.col-sm-10
= f.text_field :skype, class: 'form-control'
.form-group.row
.col-sm-2.text-right
= f.label :linkedin, class: 'col-form-label'
.col-sm-10= f.text_field :linkedin, class: 'form-control'
.col-sm-2.col-form-label
= f.label :linkedin
.col-sm-10
= f.text_field :linkedin, class: 'form-control'
.form-group.row
.col-sm-2.text-right
= f.label :twitter, class: 'col-form-label'
.col-sm-10= f.text_field :twitter, class: 'form-control'
.col-sm-2.col-form-label
= f.label :twitter
.col-sm-10
= f.text_field :twitter, class: 'form-control'
.form-group.row
.col-sm-2.text-right
= f.label :website_url, 'Website', class: 'col-form-label'
.col-sm-10= f.text_field :website_url, class: 'form-control'
.col-sm-2.col-form-label
= f.label :website_url
.col-sm-10
= f.text_field :website_url, class: 'form-control'
= render_if_exists 'admin/users/admin_notes', f: f
......
......@@ -59,7 +59,7 @@
.append-right-default
= s_("CiVariable|Masked")
%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") }
%input{ type: "hidden",
class: 'js-ci-variable-input-masked js-project-feature-toggle-input',
......
......@@ -5,5 +5,17 @@
.hidden.js-cluster-creating.bs-callout.bs-callout-info{ role: 'alert' }
= 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' }
= s_("ClusterIntegration|Kubernetes cluster was successfully created on Google Kubernetes Engine. Refresh the page to see Kubernetes cluster's details")
......@@ -24,7 +24,8 @@
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_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
.flash-container
......
.form-group
= f.label :create_chat_team, class: 'col-form-label' do
%span.mattermost-icon
= custom_icon('icon_mattermost')
Mattermost
.col-sm-2.col-form-label
= f.label :create_chat_team do
%span.mattermost-icon
= custom_icon('icon_mattermost')
Mattermost
.col-sm-10
.form-check.js-toggle-container
.js-toggle-button.form-check-input= f.check_box(:create_chat_team, { checked: true }, true, false)
......
.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
.form-check
= f.check_box :lfs_enabled, checked: @group.lfs_enabled?, class: 'form-check-input'
......@@ -10,12 +11,14 @@
%br/
%span.descr This setting can be overridden in each project.
.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
= f.select :project_creation_level, options_for_select(::Gitlab::Access.project_creation_options, @group.project_creation_level), {}, class: 'form-control'
.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
.form-check
= f.check_box :require_two_factor_authentication, class: 'form-check-input'
......
......@@ -10,15 +10,17 @@
.container.navless-container
.content
= render "layouts/flash"
.row.append-bottom-15
.col-sm-7.brand-holder
%h1
.row.mt-3
.col-sm-12
%h1.mb-3.font-weight-normal
= brand_title
.row.mb-3
.col-sm-7.order-12.order-sm-1.brand-holder
= brand_image
- if current_appearance&.description?
= brand_text
- else
%h3
%h3.mt-sm-0
= _('Open source software to collaborate on code')
%p
......@@ -29,7 +31,7 @@
= 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
%hr.footer-fixed
......
......@@ -13,13 +13,18 @@
- unless @user.password_automatically_set?
.form-group.row
= f.label :current_password, class: 'col-form-label col-sm-2'
.col-sm-10= f.password_field :current_password, required: true, class: 'form-control'
.col-sm-2.col-form-label
= f.label :current_password
.col-sm-10
= f.password_field :current_password, required: true, class: 'form-control'
.form-group.row
= f.label :password, class: 'col-form-label col-sm-2'
.col-sm-10= f.password_field :password, required: true, class: 'form-control'
.col-sm-2.col-form-label
= f.label :password
.col-sm-10
= f.password_field :password, required: true, class: 'form-control'
.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
= f.password_field :password_confirmation, required: true, class: 'form-control'
.form-actions
......
......@@ -5,22 +5,22 @@
%p= msg
.form-group.row
= f.label :domain, class: 'col-form-label col-sm-2' do
= _("Domain")
.col-sm-2.col-form-label
= f.label :domain, _("Domain")
.col-sm-10
= f.text_field :domain, required: true, autocomplete: 'off', class: 'form-control', disabled: @domain.persisted?
- if Gitlab.config.pages.external_https
.form-group.row
= f.label :certificate, class: 'col-form-label col-sm-2' do
= _("Certificate (PEM)")
.col-sm-2.col-form-label
= f.label :certificate, _("Certificate (PEM)")
.col-sm-10
= f.text_area :certificate, rows: 5, class: 'form-control'
%span.help-inline= _("Upload a certificate for your domain with all intermediates")
.form-group.row
= f.label :key, class: 'col-form-label col-sm-2' do
= _("Key (PEM)")
.col-sm-2.col-form-label
= f.label :key, _("Key (PEM)")
.col-sm-10
= f.text_area :key, rows: 5, class: 'form-control'
%span.help-inline= _("Upload a private key for your certificate")
......
......@@ -6,8 +6,8 @@
.card-body
= form_errors(@protected_branch)
.form-group.row
= f.label :name, class: 'col-md-2 text-right' do
Branch:
.col-md-2.text-right
= f.label :name, 'Branch:'
.col-md-10
= render partial: "projects/protected_branches/shared/dropdown", locals: { f: f }
.form-text.text-muted
......
......@@ -6,8 +6,8 @@
.card-body
= form_errors(@protected_tag)
.form-group.row
= f.label :name, class: 'col-md-2 text-right' do
Tag:
.col-md-2.text-right
= f.label :name, 'Tag:'
.col-md-10.protected-tags-dropdown
= render partial: "projects/protected_tags/shared/dropdown", locals: { f: f }
.form-text.text-muted
......
......@@ -2,10 +2,12 @@
- setting = error_tracking_setting
%section.settings.expanded.no-animate
%section.settings.no-animate.js-error-tracking-settings
.settings-header
%h4
= _('Error Tracking')
%button.btn.js-settings-toggle{ type: 'button' }
= _('Expand')
%p
= _('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'
......
......@@ -3,6 +3,6 @@
- breadcrumb_title _('Operations Settings')
= 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_if_exists 'projects/settings/operations/tracing'
......@@ -2,17 +2,20 @@
= form_errors(@label)
.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
= 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'
.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
= f.text_field :description, class: "form-control js-quick-submit qa-label-description"
.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
.input-group
.input-group-prepend
......
......@@ -7,7 +7,8 @@
= form_errors(@snippet)
.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
= f.text_field :title, class: 'form-control qa-snippet-title', required: true, autofocus: true
......@@ -17,7 +18,8 @@
.file-editor
.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
.file-holder.snippet
.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:
![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
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**
......@@ -40,7 +42,8 @@ Checking Geo ...
GitLab Geo is available ... yes
GitLab Geo is enabled ... 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 Foreign Data Wrapper schema is up-to-date? ... yes
GitLab Geo HTTP(S) connectivity ...
......@@ -68,22 +71,22 @@ Example output:
```
http://secondary.example.com/
-----------------------------------------------------
GitLab Version: 11.8.1-ee
GitLab Version: 11.10.4-ee
Geo Role: Secondary
Health Status: Healthy
Repositories: 190/190 (100%)
Verified Repositories: 190/190 (100%)
Wikis: 190/190 (100%)
Verified Wikis: 190/190 (100%)
LFS Objects: 35/35 (100%)
Attachments: 528/528 (100%)
CI job artifacts: 477/477 (100%)
Repositories Checked: 0/190 (0%)
Repositories: 289/289 (100%)
Verified Repositories: 289/289 (100%)
Wikis: 289/289 (100%)
Verified Wikis: 289/289 (100%)
LFS Objects: 8/8 (100%)
Attachments: 5/5 (100%)
CI job artifacts: 0/0 (0%)
Repositories Checked: 0/289 (0%)
Sync Settings: Full
Database replication lag: 0 seconds
Last event ID seen from primary: 2158 (about 2 minute ago)
Last event ID processed by cursor: 2158 (about 2 minute ago)
Last status report was: 4 minutes ago
Last event ID seen from primary: 10215 (about 2 minutes ago)
Last event ID processed by cursor: 10215 (about 2 minutes ago)
Last status report was: 2 minutes ago
```
## Is Postgres replication working?
......@@ -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-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.
- 2 or more GitLab application nodes (Unicorn, Workhorse, Sidekiq, PGBouncer)
- 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
......@@ -153,7 +153,7 @@ contention due to certain workloads.
- 1 or more NFS/Gitaly servers
- 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
......@@ -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 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
separately:
......
......@@ -134,7 +134,7 @@ otherwise the networks will become a single point of failure.
#### Architecture
![PG HA Architecture](pg_ha_architecture.png)
![PG HA Architecture](img/pg_ha_architecture.png)
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.'
- [DeclarativePolicy framework](policies.md)
- [How Git object deduplication works in GitLab](git_object_deduplication.md)
- [Geo development](geo.md)
- [Routing](routing.md)
## Performance guides
......
......@@ -106,43 +106,43 @@ Component statuses are linked to configuration documentation for each component.
### 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 |
| --------- | ----------- |:--------------------:|:------------------:|:-----:|:--------:|:--------:|
| [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 |
| [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 |
| [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 |
| [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 |
| [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 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 Pages](#gitlab-pages) | Hosts static websites | [][pages-omnibus] | [][pages-charts] | [][pages-charts] | [](https://docs.gitlab.com/ee/user/gitlab_com/#gitlab-pages) | 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 |
| [Redis](#redis) | Caching service | [][redis-omnibus] | [][redis-omnibus] | [][redis-charts] | [](https://about.gitlab.com/handbook/engineering/infrastructure/production-architecture/#service-architecture) | CE & EE |
| [PostgreSQL](#postgresql) | Database | [][postgres-omnibus] | [][postgres-charts] | [][postgres-charts] | [](https://docs.gitlab.com/ee/user/gitlab_com/#postgresql) | 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 |
| [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: 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: 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: 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 |
| [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 |
| [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 |
| [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 |
| [Minio](#minio) | Object storage service | [][minio-omnibus] | [][minio-charts] | [][minio-charts] | [](https://about.gitlab.com/handbook/engineering/infrastructure/production-architecture/#storage-architecture) | 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 |
| [Database Migrations](#database-migrations) | Database migrations | [][database-migrations-omnibus] | []() | [][database-migrations-charts] | [][database-migrations-charts] | 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 |
| [GitLab Geo Node](#gitlab-geo) | Geographically distributed GitLab nodes | [][geo-omnibus] | [][geo-charts] | [][geo-charts] | ✅ | 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 |
| [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 |
| [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 |
| [ElasticSearch](#elasticsearch) | Improved search within GitLab | [][elasticsearch-omnibus] | [][elasticsearch-charts] | [][elasticsearch-charts] | [](https://gitlab.com/groups/gitlab-org/-/epics/153) | EE Only |
| [Sentry integration](#sentry) | Error tracking for deployed apps | [][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 |
| [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 |
| 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) | [][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) | [][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) | [][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) | [][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) | [][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) | [][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) | [][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) | [][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) | [][redis-source] | ✅ | 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 |
| [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: 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: 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) | [][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 |
| [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 |
| [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 |
| [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) | ❌ | [][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) | [][runner-source] | [][runner-gdk] | 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) | [][certificate-management-source] | [][certificate-management-gdk] | CE & EE |
| [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) | [][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) | [][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) | [][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) | [][elasticsearch-source] | [][elasticsearch-gdk] | EE Only |
| [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] | [][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] | [][managed-k8s-apps] | [][managed-k8s-apps] | CE & EE |
### Component details
......@@ -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)
- Configuration: [Omnibus][alertmanager-omnibus], [Charts][alertmanager-charts]
- 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.
#### 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)
- 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)
#### Consul
......@@ -183,13 +184,13 @@ Consul is a tool for service discovery and configuration. Consul is distributed,
#### Database migrations
- Configuration: [Omnibus][registry-omnibus], [Charts][registry-charts]
- Configuration: [Omnibus][registry-omnibus], [Charts][registry-charts], [Source][database-migrations-source]
- Layer: Core Service (Data)
#### Elasticsearch
- [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)
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
- [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)
- 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).
#### Gitlab Geo
- Configuration: [Omnibus][geo-omnibus], [Charts][geo-charts]
- Configuration: [Omnibus][geo-omnibus], [Charts][geo-charts], [GDK][geo-gdk]
- Layer: Core Service (Processor)
#### Gitlab Monitor
......@@ -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)
- Configuration: [Omnibus][gitlab-monitor-omnibus], [Charts][gitlab-monitor-charts]
- 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 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)
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
#### Gitlab Runner
- [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)
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
#### Gitlab Shell
- [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)
[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
#### Gitlab Workhorse
- [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)
- 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.
......@@ -261,7 +265,7 @@ Grafana is an open source, feature rich metrics dashboard and graph editor for G
#### Jaeger
- [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
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
- [Project page](https://github.com/logrotate/logrotate/blob/master/README.md)
- Configuration: [Omnibus](https://docs.gitlab.com/omnibus/settings/logs.html#logrotate)
- 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.
......@@ -285,7 +290,7 @@ Mattermost is an open source, private cloud, Slack-alternative from https://matt
#### MinIO
- [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)
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
#### NGINX
- 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)
- 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.
......@@ -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)
- Configuration: [Omnibus][node-exporter-omnibus], [Charts][node-exporter-charts]
- 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.
......@@ -325,8 +332,9 @@ Prometheus exporter for PgBouncer. Exports metrics at 9127/metrics.
#### Postgresql
- [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)
- Process: `postgresql`
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
- [Project page](https://github.com/wrouesnel/postgres_exporter/blob/master/README.md)
- Configuration: [Omnibus][postgres-exporter-omnibus], [Charts][postgres-exporter-charts]
- 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.
......@@ -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)
- Configuration: [Omnibus][prometheus-omnibus], [Charts][prometheus-charts]
- 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.
#### Redis
- [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)
- Process: `redis`
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)
- Configuration: [Omnibus][redis-exporter-omnibus], [Charts][redis-exporter-charts]
- 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.
#### Registry
- [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)
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.
#### Sentry
- [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
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
#### Sidekiq
- [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)
- 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.
#### Unicorn
- [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)
- 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.
#### 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)
#### 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)
#### 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)
#### 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)
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
[alertmanager-charts]: https://github.com/helm/charts/tree/master/stable/prometheus
[nginx-omnibus]: https://docs.gitlab.com/omnibus/settings/nginx.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-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-charts]: https://docs.gitlab.com/charts/charts/gitlab/sidekiq/index.html
[gitaly-omnibus]: https://docs.gitlab.com/ee/administration/gitaly/
[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-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-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-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-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-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-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-charts]: https://docs.gitlab.com/charts/installation/deployment.html#postgresql
[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
[sentry-charts]: https://gitlab.com/charts/gitlab/issues/1319
[jaeger-omnibus]: https://gitlab.com/gitlab-org/omnibus-gitlab/issues/4104
[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-charts]: https://docs.gitlab.com/charts/charts/redis/index.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
[mattermost-charts]: https://docs.mattermost.com/install/install-mmte-helm-gitlab-helm.html
[minio-omnibus]: https://min.io/download
[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-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-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-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-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-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-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-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-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
[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
......@@ -27,7 +27,7 @@ are very appreciative of the work done by translators and proofreaders!
- Czech
- Proofreaders needed.
- Danish
- Proofreaders needed.
- Saederup92 - [GitLab](https://gitlab.com/Saederup92), [Crowdin](https://crowdin.com/profile/Saederup92)
- Dutch
- Emily Hendle - [GitLab](https://gitlab.com/pundachan), [Crowdin](https://crowdin.com/profile/pandachan)
- Esperanto
......
......@@ -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:
- `rake spec` to run the rspec suite
- `rake karma` to run the karma test suite
- `rake gitlab:test` to run all the tests
- `bin/rake spec` to run the rspec suite
- `bin/rake spec:unit` to run the only the unit 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
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
......@@ -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`
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:
- `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 @@
## Test Design
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.
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.
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
the right tests, but then cover _all_ the important ways the test may fail, which can quickly widen our scope to
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
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.
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
our test design. We can find some helpful heuristics documented in the Handbook in the
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
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.
## 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
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`.
### `let` variables
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:
- `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.
- 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
......@@ -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.
- Don't define a `let` variable that's only used by the definition of another.
Use a helper method instead.
- `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
- `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
be evaluated until it is referenced.
[lets-not]: https://robots.thoughtbot.com/lets-not
......
......@@ -4,12 +4,14 @@
_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).
- 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).
- 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).
| Test level | Community Edition | Enterprise Edition | Community + Enterprise Edition |
| --------- | ---------- | -------------- | ----- |
| Black-box tests at the system level (aka end-to-end or QA tests) | 68 (0.14%) | 31 (0.2%) | 99 (0.17%) |
| 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
......
# 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 get the credentials (a pair of Client ID and Client Secret), you must register an application on SalesForces.
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 [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:
- **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.
![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. 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.
![SalesForce Oauth App Details](img/salesforce_oauth_app_details.png)
![Salesforce Oauth App Details](img/salesforce_oauth_app_details.png)
1. Click **Save**.
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
app_secret: 'SALESFORCE_CLIENT_SECRET'
}
```
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.
![SalesForce App Secret Details](img/salesforce_app_secret_details.png)
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.
![Salesforce App Secret Details](img/salesforce_app_secret_details.png)
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
installed GitLab via Omnibus or from source respectively.
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.
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.
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.
If everything goes well, the user will be returned to GitLab and will be signed in.
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 @@
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:
......
---
type: reference
---
# GitLab Admin Area **[CORE ONLY]**
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:
| 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. |
| 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. |
......@@ -90,3 +94,23 @@ You can combine the filter options. For example, to list only public projects wi
1. Click the **Public** tab.
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]**
## 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)
<!-- ## 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]**
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.
[free trial]: https://about.gitlab.com/free-trial/
[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.
> - The `health_check` endpoint was [introduced][ce-3888] in GitLab 8.8 and was
> be deprecated in GitLab 9.1.
> - [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
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.
## IP whitelist
To access monitoring resources, the requesting client IP needs to be included in a whitelist.
[Read how to add IPs to a whitelist for the monitoring endpoints][admin].
For details, see [how to add IPs to a whitelist for the monitoring endpoints](../../../administration/monitoring/ip_whitelist.md).
## Using the endpoints
......@@ -87,9 +90,8 @@ will return a valid successful HTTP status code, and a `success` message.
## Access token (Deprecated)
>**Note:**
Access token has been deprecated in GitLab 9.4
in favor of [IP whitelist](#ip-whitelist)
> NOTE: **Note:**
> Access token has been deprecated in GitLab 9.4 in favor of [IP whitelist](#ip-whitelist).
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**
......@@ -103,10 +105,21 @@ The access token can be passed as a URL parameter:
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-3888]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/3888
[pingdom]: https://www.pingdom.com
[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
[kubernetes]: https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-probes/
[admin]: ../../../administration/monitoring/ip_whitelist.md
[kubernetes]: https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-probes/
\ No newline at end of file
---
type: reference
---
# Account and limit settings
## 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
using LFS. Their size can grow exponentially and eat up your storage device quite
fast.
using LFS. Their size can grow exponentially, rapidly consuming available storage.
In order to avoid this from happening, you can set a hard limit for your
repositories' size. This limit can be set globally, per group, or per project,
with per project limits taking the highest priority.
To avoid this from happening, you can set a hard limit for your repositories' size.
This limit can be set globally, per group, or per project, with per project limits
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:
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.
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.
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.
### How it works
......@@ -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
allowed repository size.
For more manually purging the files, read the docs on
[reducing the repository size using Git][repo-size].
For details on manually purging files, see [reducing the repository size using Git](../../project/repository/reducing_the_repo_size_using_git.md).
NOTE: **Note:**
For GitLab.com, the repository size limit is 10 GB.
<!-- ## Troubleshooting
> **Note:**
> For GitLab.com, the repository size limit is 10 GB.
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.
[ee-740]: https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/740
[repo-size]: ../../project/repository/reducing_the_repo_size_using_git.md
[ee-8.12]: https://about.gitlab.com/2016/09/22/gitlab-8-12-released/#limit-project-size-ee
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. -->
......@@ -4,7 +4,7 @@ GitLab Inc. will periodically collect information about your instance in order
to perform various actions.
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]**
......
......@@ -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 |
| Node.js | [NodeJsScan](https://github.com/ajinabraham/NodeJsScan) | 11.1 |
| 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 |
| 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 |
......
# 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.
## Installing applications
GitLab provides a one-click install for various applications that can be
added directly to your cluster.
NOTE: **Note:**
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)
GitLab can install and manage some applications in your group-level
cluster. For more information on installing, upgrading, uninstalling,
and troubleshooting applications for your group cluster, see
[Gitlab Managed Apps](../../clusters/applications.md).
## RBAC compatibility
......
......@@ -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
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 required assertions using the [table below](#assertions).
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:
| First Name | `first_name`, `firstname`, `firstName` | |
| 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
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.
## Installing applications
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](#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
```
GitLab can install and manage some applications in your project-level
cluster. For more information on installing, upgrading, uninstalling,
and troubleshooting applications for your project cluster, see
[Gitlab Managed Apps](../../clusters/applications.md).
## Getting the external endpoint
......
......@@ -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. | ✓ | ✓ |
| `/unlabel ~label1 ~label2` | Remove all or specific label(s)| ✓ | ✓ |
| `/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 | ✓ | ✓ |
| `/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 | ✓ | ✓ |
......
......@@ -2,10 +2,11 @@
>**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
> 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
> application settings (`/admin/application_settings`) under 'Import sources'.
> Ask your administrator if you don't see the **GitLab export** button when
......@@ -14,10 +15,9 @@
> on the GitLab instance in application settings (`/admin/application_settings`)
> under 'Visibility and Access Controls'.
> - You can find some useful raketasks if you are an administrator in the
> [import_export](../../../administration/raketasks/project_import_export.md)
> raketask.
> - The exports are stored in a temporary [shared directory][tmp] and are deleted
> every 24 hours by a specific worker.
> [import_export](../../../administration/raketasks/project_import_export.md) raketask.
> - The exports are stored in a temporary [shared directory](../../../development/shared_files.md)
> and are deleted every 24 hours by a specific worker.
> - 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
> 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:
## 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:
......@@ -98,19 +98,14 @@ The following items will NOT be exported:
## Importing the project
1. The new GitLab project import feature is at the far right of the import
options when creating a New Project. Make sure you are in the right namespace
and you have entered a project name. Click on **GitLab export**:
1. The GitLab project import feature is the first import option when creating a
new project. Click on **GitLab export**:
![New project](img/import_export_new_project.png)
1. You can see where the project will be imported to. You can now select file
exported previously:
1. Enter your project name and URL. Then select the file you exported previously:
![Select file](img/import_export_select_file.png)
1. Click on **Import project** to begin importing. Your newly imported project
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.
## Pulling from a remote repository **[STARTER]**
> [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
upstream repository.
......
......@@ -98,7 +98,6 @@ module API
mount ::API::Boards
mount ::API::Branches
mount ::API::BroadcastMessages
mount ::API::CircuitBreakers
mount ::API::Commits
mount ::API::CommitStatuses
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
requires :key, type: String, desc: 'The key of the variable'
requires :value, type: String, desc: 'The value of the variable'
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'
if Gitlab.ee?
......@@ -82,7 +82,7 @@ module API
optional :key, type: String, desc: 'The key of the variable'
optional :value, type: String, desc: 'The value of the variable'
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'
if Gitlab.ee?
......
......@@ -363,6 +363,14 @@ module Banzai
group_ref
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
......@@ -104,14 +104,6 @@ module Banzai
matches[:namespace] && matches[:project]
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)
# use title of wrapped element instead
nil
......
......@@ -51,13 +51,13 @@ module Banzai
# default implementation.
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])
if milestone
yield match, milestone.id, $~[:project], $~[:namespace], $~
else
match
escape_html_entities(match)
end
end
end
......
......@@ -40,6 +40,7 @@ sast:
SAST_BRAKEMAN_LEVEL \
SAST_GOSEC_LEVEL \
SAST_FLAWFINDER_LEVEL \
SAST_GITLEAKS_ENTROPY_LEVEL \
SAST_DOCKER_CLIENT_NEGOTIATION_TIMEOUT \
SAST_PULL_ANALYZER_IMAGE_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
@updates[:milestone_id] = nil
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|
_("Copy labels and milestone from %{source_issuable_reference}.") % { source_issuable_reference: source_issuable.to_reference }
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')
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
cmds = [
%w(rake gitlab:setup),
......@@ -10,7 +35,7 @@ namespace :spec do
run_commands(cmds)
end
desc 'GitLab | Rspec | Run feature specs'
desc '[Deprecated] Use the "spec:system" task instead'
task :feature do
cmds = [
%w(rake gitlab:setup),
......@@ -19,7 +44,7 @@ namespace :spec do
run_commands(cmds)
end
desc 'GitLab | Rspec | Run model specs'
desc '[Deprecated] Use "bin/rspec spec/models" instead'
task :models do
cmds = [
%w(rake gitlab:setup),
......@@ -28,7 +53,7 @@ namespace :spec do
run_commands(cmds)
end
desc 'GitLab | Rspec | Run service specs'
desc '[Deprecated] Use "bin/rspec spec/services" instead'
task :services do
cmds = [
%w(rake gitlab:setup),
......@@ -37,7 +62,7 @@ namespace :spec do
run_commands(cmds)
end
desc 'GitLab | Rspec | Run lib specs'
desc '[Deprecated] Use "bin/rspec spec/lib" instead'
task :lib do
cmds = [
%w(rake gitlab:setup),
......@@ -45,15 +70,6 @@ namespace :spec do
]
run_commands(cmds)
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
desc "GitLab | Run specs"
......
......@@ -835,6 +835,9 @@ msgstr ""
msgid "An error has occurred"
msgstr ""
msgid "An error occurding while fetching folder content."
msgstr ""
msgid "An error occurred creating the new branch."
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."
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"
msgstr ""
......@@ -2553,6 +2559,9 @@ msgstr ""
msgid "ClusterIntegration|Your account must have %{link_to_kubernetes_engine}"
msgstr ""
msgid "ClusterIntegration|Your cluster API is unreachable. Please ensure your API URL is correct."
msgstr ""
msgid "ClusterIntegration|Zone"
msgstr ""
......@@ -2900,7 +2909,7 @@ msgstr ""
msgid "Copy labels and milestone from %{source_issuable_reference}."
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 ""
msgid "Copy link"
......@@ -4641,6 +4650,9 @@ msgstr ""
msgid "Go to %{link_to_google_takeout}."
msgstr ""
msgid "Go to parent"
msgstr ""
msgid "Go to project"
msgstr ""
......@@ -5261,6 +5273,9 @@ msgstr ""
msgid "Invite"
msgstr ""
msgid "Invite \"%{trimmed}\" by email"
msgstr ""
msgid "Invite group"
msgstr ""
......@@ -5808,6 +5823,9 @@ msgstr ""
msgid "Mark as resolved"
msgstr ""
msgid "Mark comment as resolved"
msgstr ""
msgid "Mark this issue as a duplicate of another issue"
msgstr ""
......@@ -6452,6 +6470,9 @@ msgstr ""
msgid "Not found."
msgstr ""
msgid "Not implemented!"
msgstr ""
msgid "Not now"
msgstr ""
......@@ -8214,6 +8235,9 @@ msgstr ""
msgid "Resolved all discussions."
msgstr ""
msgid "Resolved by %{name}"
msgstr ""
msgid "Resolved by %{resolvedByName}"
msgstr ""
......@@ -8441,6 +8465,9 @@ msgstr ""
msgid "Search for a group"
msgstr ""
msgid "Search for a user"
msgstr ""
msgid "Search for projects, issues, etc."
msgstr ""
......@@ -8935,6 +8962,9 @@ msgstr ""
msgid "Something went wrong. Please try again."
msgstr ""
msgid "Something went wrong. Try again later."
msgstr ""
msgid "Sorry, no projects matched your search"
msgstr ""
......@@ -10744,6 +10774,21 @@ msgstr ""
msgid "Users were successfully added."
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!"
msgstr ""
......
......@@ -374,12 +374,12 @@ function clearNote (inputId) {
}
}
function confirmAndClear (discussionId) {
function confirmAndClear (mergeRequestId) {
const commentButton = document.getElementById('gitlab-comment-button');
const note = document.getElementById('gitlab-validation-note');
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);
}
......@@ -412,7 +412,7 @@ function getProjectDetails () {
const browser = getBrowserId(userAgent);
const scriptEl = document.getElementById('review-app-toolbar-script')
const { projectId, discussionId, mrUrl } = scriptEl.dataset;
const { projectId, mergeRequestId, mrUrl } = scriptEl.dataset;
return {
href,
......@@ -422,7 +422,7 @@ function getProjectDetails () {
innerWidth,
innerHeight,
projectId,
discussionId,
mergeRequestId,
mrUrl,
};
}
......@@ -449,7 +449,7 @@ function postComment ({
innerWidth,
innerHeight,
projectId,
discussionId,
mergeRequestId,
mrUrl,
}) {
// Clear any old errors
......@@ -466,18 +466,20 @@ function postComment ({
}
const detailText = `
<details>
<summary>Metadata</summary>
Posted from ${href} | ${platform} | ${browser} | ${innerWidth} x ${innerHeight}.
<br /><br />
*User agent: ${userAgent}*
</details>
\n
<details>
<summary>Metadata</summary>
Posted from ${href} | ${platform} | ${browser} | ${innerWidth} x ${innerHeight}.
<br /><br />
<em>User agent: ${userAgent}</em>
</details>
`;
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, {
method: 'POST',
......@@ -489,7 +491,7 @@ function postComment ({
})
.then((response) => {
if (response.ok) {
confirmAndClear(discussionId);
confirmAndClear(mergeRequestId);
return;
}
......
......@@ -11,6 +11,7 @@ module QA
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_value, '.qa-ci-variable-input-value' # rubocop:disable QA/ElementWithPattern
element :variable_masked
end
view 'app/views/ci/variables/_index.html.haml' do
......@@ -18,7 +19,7 @@ module QA
element :reveal_values, '.js-secret-value-reveal-button' # rubocop:disable QA/ElementWithPattern
end
def fill_variable(key, value)
def fill_variable(key, value, masked)
keys = all_elements(:ci_variable_input_key)
index = keys.size - 1
......@@ -32,6 +33,9 @@ module QA
# The code was inspired from:
# https://github.com/teamcapybara/capybara/blob/679548cea10773d45e32808f4d964377cfe5e892/lib/capybara/selenium/node.rb#L217
execute_script("arguments[0].value = #{value.to_json}", node)
masked_node = all_elements(:variable_masked)[index]
toggle_masked(masked_node, masked)
end
def save_variables
......@@ -47,6 +51,24 @@ module QA
find('.qa-ci-variable-input-value').value
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
......
......@@ -3,7 +3,7 @@
module QA
module Resource
class CiVariable < Base
attr_accessor :key, :value
attr_accessor :key, :value, :masked
attribute :project do
Project.fabricate! do |resource|
......@@ -19,7 +19,7 @@ module QA
Page::Project::Settings::CICD.perform do |setting|
setting.expand_ci_variables do |page|
page.fill_variable(key, value)
page.fill_variable(key, value, masked)
page.save_variables
end
......@@ -49,7 +49,8 @@ module QA
def api_post_body
{
key: key,
value: value
value: value,
masked: masked
}
end
end
......
......@@ -16,6 +16,7 @@ module QA
resource.project = project
resource.key = 'VARIABLE_KEY'
resource.value = 'some_CI_variable'
resource.masked = false
end
project.visit!
......
......@@ -60,6 +60,7 @@ module QA
resource.project = @project
resource.key = deploy_key_name
resource.value = key.private_key
resource.masked = false
end
gitlab_ci = <<~YAML
......
......@@ -34,6 +34,7 @@ module QA
resource.project = @project
resource.key = 'CODE_QUALITY_DISABLED'
resource.value = '1'
resource.masked = false
end
# Set an application secret CI variable (prefixed with K8S_SECRET_)
......@@ -41,6 +42,7 @@ module QA
resource.project = @project
resource.key = 'K8S_SECRET_OPTIONAL_MESSAGE'
resource.value = 'you_can_see_this_variable'
resource.masked = false
end
# Connect K8s cluster
......
......@@ -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"
if [ "$USE_BUNDLE_INSTALL" != "false" ]; then
bundle --version
bundle install --clean $BUNDLE_INSTALL_FLAGS && bundle check
fi
......@@ -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
# Determine the database by looking at the job name.
# For example, we'll get pg if the job is `rspec-pg 19 20`
export GITLAB_DATABASE=$(echo $CI_JOB_NAME | cut -f1 -d' ' | cut -f2 -d-)
# This would make the default database postgresql, and we could also use
# pg to mean postgresql.
if [ "$GITLAB_DATABASE" != 'mysql' ]; then
# This would make the default database postgresql.
if [[ "${CI_JOB_NAME#*mysql}" != "$CI_JOB_NAME" ]]; then
export GITLAB_DATABASE='mysql'
else
export GITLAB_DATABASE='postgresql'
fi
......
......@@ -3,6 +3,8 @@
require 'spec_helper'
describe 'Clusterable > Show page' do
include KubernetesHelpers
let(:current_user) { create(:user) }
let(:cluster_ingress_help_text_selector) { '.js-ingress-domain-help-text' }
let(:hide_modifier_selector) { '.hide' }
......@@ -83,6 +85,7 @@ describe 'Clusterable > Show page' do
shared_examples 'editing a user-provided cluster' do
before do
stub_kubeclient_discover(cluster.platform.api_url)
clusterable.add_maintainer(current_user)
visit cluster_path
end
......
......@@ -14,6 +14,7 @@ describe 'User Cluster', :js do
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::Cluster).to receive(:retrieve_connection_status).and_return(:connected)
end
context 'when user does not have a cluster and visits cluster index page' do
......
......@@ -12,6 +12,7 @@ describe 'User Cluster', :js do
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::Cluster).to receive(:retrieve_connection_status).and_return(:connected)
end
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
wait_for_requests
within '.js-error-tracking-settings' do
click_button('Expand')
end
expect(page).to have_content('Sentry API URL')
expect(page.body).to include('Error Tracking')
expect(page).to have_button('Connect')
......@@ -86,6 +89,9 @@ describe 'Projects > Settings > For a forked project', :js do
wait_for_requests
within '.js-error-tracking-settings' do
click_button('Expand')
end
check('Active')
fill_in('error-tracking-api-host', with: 'http://sentry.example.com')
fill_in('error-tracking-token', with: 'token')
......
......@@ -209,6 +209,22 @@ describe('Clusters', () => {
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', () => {
......
......@@ -16,7 +16,9 @@ exports[`Repository table row component renders table row 1`] = `
<a
class="str-truncated"
>
test
</a>
<!---->
......
......@@ -3,18 +3,19 @@ import { GlLoadingIcon } from '@gitlab/ui';
import Table from '~/repository/components/table/index.vue';
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, {
propsData: {
path,
},
mocks: {
$apollo: {
queries: {
files: { loading },
},
},
$apollo,
},
});
}
......@@ -39,9 +40,41 @@ describe('Repository table component', () => {
);
});
it('renders loading icon', () => {
factory('/', true);
it('shows loading icon', () => {
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', () => {
it('renders table row', () => {
factory({
id: 1,
id: '1',
path: 'test',
type: 'file',
currentPath: '/',
});
expect(vm.element).toMatchSnapshot();
......@@ -39,14 +40,15 @@ describe('Repository table row component', () => {
it.each`
type | component | componentName
${'folder'} | ${RouterLinkStub} | ${'RouterLink'}
${'tree'} | ${RouterLinkStub} | ${'RouterLink'}
${'file'} | ${'a'} | ${'hyperlink'}
${'commit'} | ${'a'} | ${'hyperlink'}
`('renders a $componentName for type $type', ({ type, component }) => {
factory({
id: 1,
id: '1',
path: 'test',
type,
currentPath: '/',
});
expect(vm.find(component).exists()).toBe(true);
......@@ -54,14 +56,15 @@ describe('Repository table row component', () => {
it.each`
type | pushes
${'folder'} | ${true}
${'tree'} | ${true}
${'file'} | ${false}
${'commit'} | ${false}
`('pushes new router if type $type is folder', ({ type, pushes }) => {
`('pushes new router if type $type is tree', ({ type, pushes }) => {
factory({
id: 1,
id: '1',
path: 'test',
type,
currentPath: '/',
});
vm.trigger('click');
......@@ -75,9 +78,10 @@ describe('Repository table row component', () => {
it('renders commit ID for submodule', () => {
factory({
id: 1,
id: '1',
path: 'test',
type: 'commit',
currentPath: '/',
});
expect(vm.find('.commit-sha').text()).toContain('1');
......
......@@ -6,7 +6,7 @@ describe('getIconName', () => {
// file types
it.each`
type | path | icon
${'folder'} | ${''} | ${'folder'}
${'tree'} | ${''} | ${'folder'}
${'commit'} | ${''} | ${'archive'}
${'file'} | ${'test.pdf'} | ${'file-pdf-o'}
${'file'} | ${'test.jpg'} | ${'file-image-o'}
......
......@@ -90,9 +90,12 @@ describe('Job App ', () => {
describe('triggered job', () => {
beforeEach(() => {
const aYearAgo = new Date();
aYearAgo.setFullYear(aYearAgo.getFullYear() - 1);
mock
.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 });
});
......
......@@ -295,6 +295,25 @@ describe Banzai::Filter::MilestoneReferenceFilter do
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
let(:reference) { milestone.to_reference(format: :iid) }
......@@ -307,6 +326,7 @@ describe Banzai::Filter::MilestoneReferenceFilter do
it_behaves_like 'cross-project / cross-namespace complete reference'
it_behaves_like 'cross-project / same-namespace complete reference'
it_behaves_like 'cross project shorthand reference'
it_behaves_like 'references with HTML entities'
end
shared_context 'group milestones' do
......@@ -317,6 +337,7 @@ describe Banzai::Filter::MilestoneReferenceFilter do
it_behaves_like 'String-based single-word references'
it_behaves_like 'String-based multi-word references in quotes'
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
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 @@
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'
subject { build(:cluster) }
......@@ -23,7 +26,6 @@ describe Clusters::Cluster do
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_name).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(:rbac?).to(:platform_kubernetes).with_prefix }
......@@ -501,28 +503,6 @@ describe Clusters::Cluster do
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
let(:cluster) { create(:cluster, :provided_by_gcp) }
......@@ -617,4 +597,139 @@ describe Clusters::Cluster do
it { is_expected.to be_truthy }
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
......@@ -158,46 +158,6 @@ describe Clusters::ClusterPresenter do
it { is_expected.to include(cluster.name) }
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
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 }
Dir[Rails.root.join("spec/support/shared_examples/*.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|
config.use_transactional_fixtures = false
config.use_instantiated_fixtures = false
......@@ -55,9 +57,10 @@ RSpec.configure do |config|
config.infer_spec_type_from_file_location!
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]
metadata[:level] = quality_level.level_for(location)
metadata[:api] = true if location =~ %r{/spec/requests/api/}
# 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