Commit a3e92224 authored by Stan Hu's avatar Stan Hu

Merge branch 'master' into ce-to-ee-2018-09-07

parents 6b2a3fd2 bef4c774
......@@ -48,6 +48,16 @@ export const dasherize = str => str.replace(/[_\s]+/g, '-');
*/
export const slugify = str => str.trim().toLowerCase();
/**
* Replaces whitespaces with hyphens and converts to lower case
* @param {String} str
* @returns {String}
*/
export const slugifyWithHyphens = str => {
const regex = new RegExp(/\s+/, 'g');
return str.toLowerCase().replace(regex, '-');
};
/**
* Truncates given text
*
......
import $ from 'jquery';
import { getParameterValues } from '../lib/utils/url_utility';
import projectNew from './project_new';
export default () => {
const path = getParameterValues('path')[0];
const pathParam = getParameterValues('path')[0];
const nameParam = getParameterValues('name')[0];
const $projectPath = $('.js-path-name');
const $projectName = $('.js-project-name');
// get the path url and append it in the inputS
$('.js-path-name').val(path);
// get the path url and append it in the input
$projectPath.val(pathParam);
// get the project name from the URL and set it as input value
$projectName.val(nameParam);
// generate slug when project name changes
$projectName.keyup(() => projectNew.onProjectNameChange($projectName, $projectPath));
};
import $ from 'jquery';
import { addSelectOnFocusBehaviour } from '../lib/utils/common_utils';
import { slugifyWithHyphens } from '../lib/utils/text_utility';
let hasUserDefinedProjectPath = false;
......@@ -29,18 +30,23 @@ const deriveProjectPathFromUrl = ($projectImportUrl) => {
}
};
const onProjectNameChange = ($projectNameInput, $projectPathInput) => {
const slug = slugifyWithHyphens($projectNameInput.val());
$projectPathInput.val(slug);
};
const bindEvents = () => {
const $newProjectForm = $('#new_project');
const $projectImportUrl = $('#project_import_url');
const $projectPath = $('#project_path');
const $projectPath = $('.tab-pane.active #project_path');
const $useTemplateBtn = $('.template-button > input');
const $projectFieldsForm = $('.project-fields-form');
const $selectedTemplateText = $('.selected-template');
const $changeTemplateBtn = $('.change-template');
const $selectedIcon = $('.selected-icon');
const $templateProjectNameInput = $('#template-project-name #project_path');
const $pushNewProjectTipTrigger = $('.push-new-project-tip');
const $projectTemplateButtons = $('.project-templates-buttons');
const $projectName = $('.tab-pane.active #project_name');
if ($newProjectForm.length !== 1) {
return;
......@@ -57,7 +63,8 @@ const bindEvents = () => {
$('.btn_import_gitlab_project').on('click', () => {
const importHref = $('a.btn_import_gitlab_project').attr('href');
$('.btn_import_gitlab_project').attr('href', `${importHref}?namespace_id=${$('#project_namespace_id').val()}&path=${$projectPath.val()}`);
$('.btn_import_gitlab_project')
.attr('href', `${importHref}?namespace_id=${$('#project_namespace_id').val()}&name=${$projectName.val()}&path=${$projectPath.val()}`);
});
if ($pushNewProjectTipTrigger) {
......@@ -111,7 +118,15 @@ const bindEvents = () => {
const selectedTemplate = templates[value];
$selectedTemplateText.text(selectedTemplate.text);
$(selectedTemplate.icon).clone().addClass('d-block').appendTo($selectedIcon);
$templateProjectNameInput.focus();
const $activeTabProjectName = $('.tab-pane.active #project_name');
const $activeTabProjectPath = $('.tab-pane.active #project_path');
$activeTabProjectName.focus();
$activeTabProjectName
.keyup(() => {
onProjectNameChange($activeTabProjectName, $activeTabProjectPath);
hasUserDefinedProjectPath = $activeTabProjectPath.val().trim().length > 0;
});
}
$useTemplateBtn.on('change', chooseTemplate);
......@@ -137,9 +152,15 @@ const bindEvents = () => {
$projectMirror.attr('disabled', !$projectMirror.attr('disabled'));
});
$projectName.keyup(() => {
onProjectNameChange($projectName, $projectPath);
hasUserDefinedProjectPath = $projectPath.val().trim().length > 0;
});
};
export default {
bindEvents,
deriveProjectPathFromUrl,
onProjectNameChange,
};
......@@ -221,6 +221,7 @@ module ApplicationSettingsHelper
:recaptcha_enabled,
:recaptcha_private_key,
:recaptcha_site_key,
:receive_max_input_size,
:repository_checks_enabled,
:repository_storages,
:require_two_factor_authentication,
......
......@@ -17,7 +17,10 @@
= render 'repository_size_limit_setting', form: f
.form-group
= f.label :session_expire_delay, 'Session duration (minutes)', class: 'label-bold'
= f.label :receive_max_input_size, 'Maximum push size (MB)', class: 'label-light'
= f.number_field :receive_max_input_size, class: 'form-control'
.form-group
= f.label :session_expire_delay, 'Session duration (minutes)', class: 'label-light'
= f.number_field :session_expire_delay, class: 'form-control'
%span.form-text.text-muted#session_expire_delay_help_block GitLab restart is required to apply changes
.form-group
......
......@@ -8,8 +8,11 @@
= form_tag import_gitlab_project_path, class: 'new_project', multipart: true do
.row
.form-group.project-name.col-sm-12
= label_tag :name, _('Project name'), class: 'label-bold'
= text_field_tag :name, @name, placeholder: "My awesome project", class: "js-project-name form-control input-lg", autofocus: true, required: true
.form-group.col-12.col-sm-6
= label_tag :namespace_id, 'Project path', class: 'label-bold'
= label_tag :namespace_id, _('Project URL'), class: 'label-bold'
.form-group
.input-group
- if current_user.can_select_namespace?
......@@ -24,8 +27,8 @@
#{user_url(current_user.username)}/
= hidden_field_tag :namespace_id, value: current_user.namespace_id
.form-group.col-12.col-sm-6.project-path
= label_tag :path, _('Project name'), class: 'label-bold'
= text_field_tag :path, @path, placeholder: "my-awesome-project", class: "js-path-name form-control", tabindex: 2, autofocus: true, required: true
= label_tag :path, _('Project slug'), class: 'label-bold'
= text_field_tag :path, @path, placeholder: "my-awesome-project", class: "js-path-name form-control", tabindex: 2, required: true
.row
.form-group.col-md-12
......
......@@ -3,10 +3,13 @@
.row{ id: project_name_id }
= f.hidden_field :ci_cd_only, value: ci_cd_only
.form-group.project-name.col-sm-12
= f.label :name, class: 'label-bold' do
%span= _("Project name")
= f.text_field :name, placeholder: "My awesome project", class: "form-control input-lg", autofocus: true, required: true
.form-group.project-path.col-sm-6
= f.label :namespace_id, class: 'label-bold' do
%span
Project path
%span= s_("Project URL")
.input-group
- if current_user.can_select_namespace?
.input-group-prepend.has-tooltip{ title: root_url }
......@@ -27,13 +30,12 @@
= f.hidden_field :namespace_id, value: current_user.namespace_id
.form-group.project-path.col-sm-6
= f.label :path, class: 'label-bold' do
%span
Project name
= f.text_field :path, placeholder: "my-awesome-project", class: "form-control", tabindex: 2, autofocus: true, required: true
%span= _("Project slug")
= f.text_field :path, placeholder: "my-awesome-project", class: "form-control", tabindex: 2, required: true
- if current_user.can_create_group?
.form-text.text-muted
Want to house several dependent projects under the same namespace?
= link_to "Create a group", new_group_path
= link_to "Create a group.", new_group_path
.form-group
= f.label :description, class: 'label-bold' do
......
---
title: Re-add project name field on "Create new project" page
merge_request: 21386
author:
type: other
---
title: Allow admins to configure the maximum Git push size
merge_request: 20758
author:
type: added
# frozen_string_literal: true
# See http://doc.gitlab.com/ce/development/migration_style_guide.html
# for more information on how to write migrations for GitLab.
class AddReceiveMaxInputSizeToApplicationSettings < ActiveRecord::Migration
DOWNTIME = false
def change
add_column :application_settings, :receive_max_input_size, :integer
end
end
......@@ -219,6 +219,7 @@ ActiveRecord::Schema.define(version: 20180906101639) do
t.boolean "user_show_add_ssh_key_message", default: true, null: false
t.integer "custom_project_templates_group_id"
t.integer "usage_stats_set_by_user_id"
t.integer "receive_max_input_size"
end
create_table "approvals", force: :cascade do |t|
......
# Custom Git Hooks
> **Note:** Custom Git hooks must be configured on the filesystem of the GitLab
server. Only GitLab server administrators will be able to complete these tasks.
Please explore [webhooks] and [CI] as an option if you do not
have filesystem access. For a user configurable Git hook interface, see
[Push Rules](https://docs.gitlab.com/ee/push_rules/push_rules.html),
available in GitLab Enterprise Edition.
> server. Only GitLab server administrators will be able to complete these tasks.
> Please explore [webhooks] and [CI] as an option if you do not
> have filesystem access. For a user configurable Git hook interface, see
> [Push Rules](https://docs.gitlab.com/ee/push_rules/push_rules.html),
> available in GitLab Enterprise Edition.
>
**Note:** Custom Git hooks won't be replicated to secondary nodes if you use [GitLab Geo][gitlab-geo]
> **Note:** Custom Git hooks won't be replicated to secondary nodes if you use [GitLab Geo][gitlab-geo]
Git natively supports hooks that are executed on different actions.
Examples of server-side git hooks include pre-receive, post-receive, and update.
......
......@@ -25,16 +25,16 @@ To bring the former primary up to date:
sudo gitlab-ctl start
```
>**Note 1:** If you [disabled primary permanently][disaster-recovery-disable-primary],
you need to undo those steps now. For Debian/Ubuntu you just need to run
`sudo systemctl enable gitlab-runsvdir`. For CentOS 6, you need to install
the GitLab instance from scratch and setup it as a secondary node by
following [Setup instructions][setup-geo]. In this case you don't need to follow the next step.
>**Note 2:** If you [changed the DNS records](index.md#step-4-optional-updating-the-primary-domains-dns-record)
for this node during disaster recovery procedure you may need to [block
all the writes to this node](https://gitlab.com/gitlab-org/gitlab-ee/blob/master/doc/gitlab-geo/planned-failover.md#block-primary-traffic)
during this procedure.
> **Note 1:** If you [disabled primary permanently][disaster-recovery-disable-primary],
> you need to undo those steps now. For Debian/Ubuntu you just need to run
> `sudo systemctl enable gitlab-runsvdir`. For CentOS 6, you need to install
> the GitLab instance from scratch and setup it as a secondary node by
> following [Setup instructions][setup-geo]. In this case you don't need to follow the next step.
>
> **Note 2:** If you [changed the DNS records](index.md#step-4-optional-updating-the-primary-domains-dns-record)
> for this node during disaster recovery procedure you may need to [block
> all the writes to this node](https://gitlab.com/gitlab-org/gitlab-ee/blob/master/doc/gitlab-geo/planned-failover.md#block-primary-traffic)
> during this procedure.
1. [Setup database replication][database-replication]. Note that in this
case, primary refers to the current primary, and secondary refers to the
......
......@@ -143,28 +143,28 @@ access to the primary for the duration of the maintenance window.
all HTTP, HTTPS and SSH traffic to/from the primary, **except** for your IP and
the secondary's IP.
For instance, if your secondary originates all its traffic from `5.6.7.8` and
your IP is `100.0.0.1`, you might run the following commands on the server(s)
making up your primary node:
```
sudo iptables -A INPUT -p tcp -s 5.6.7.8 --destination-port 22 -j ACCEPT
sudo iptables -A INPUT -p tcp -s 100.0.0.1 --destination-port 22 -j ACCEPT
sudo iptables -A INPUT --destination-port 22 -j REJECT
sudo iptables -A INPUT -p tcp -s 5.6.7.8 --destination-port 80 -j ACCEPT
sudo iptables -A INPUT -p tcp -s 100.0.0.1 --destination-port 80 -j ACCEPT
sudo iptables -A INPUT --tcp-dport 80 -j REJECT
sudo iptables -A INPUT -p tcp -s 5.6.7.8 --destination-port 443 -j ACCEPT
sudo iptables -A INPUT -p tcp -s 100.0.0.1 --destination-port 443 -j ACCEPT
sudo iptables -A INPUT --tcp-dport 443 -j REJECT
```
From this point, users will be unable to view their data or make changes on the
**primary** node. They will also be unable to log in to the **secondary** node,
but existing sessions will work for the remainder of the maintenance period, and
public data will be accessible throughout.
For instance, if your secondary originates all its traffic from `5.6.7.8` and
your IP is `100.0.0.1`, you might run the following commands on the server(s)
making up your primary node:
```
sudo iptables -A INPUT -p tcp -s 5.6.7.8 --destination-port 22 -j ACCEPT
sudo iptables -A INPUT -p tcp -s 100.0.0.1 --destination-port 22 -j ACCEPT
sudo iptables -A INPUT --destination-port 22 -j REJECT
sudo iptables -A INPUT -p tcp -s 5.6.7.8 --destination-port 80 -j ACCEPT
sudo iptables -A INPUT -p tcp -s 100.0.0.1 --destination-port 80 -j ACCEPT
sudo iptables -A INPUT --tcp-dport 80 -j REJECT
sudo iptables -A INPUT -p tcp -s 5.6.7.8 --destination-port 443 -j ACCEPT
sudo iptables -A INPUT -p tcp -s 100.0.0.1 --destination-port 443 -j ACCEPT
sudo iptables -A INPUT --tcp-dport 443 -j REJECT
```
From this point, users will be unable to view their data or make changes on the
**primary** node. They will also be unable to log in to the **secondary** node,
but existing sessions will work for the remainder of the maintenance period, and
public data will be accessible throughout.
1. Verify the primary is blocked to HTTP traffic by visiting it in browser via
another IP. The server should refuse connection.
......
......@@ -289,13 +289,13 @@ It is important to note that selective synchronization does not:
1. Restrict permissions from secondary nodes.
1. Hide project metadata from secondary nodes.
* Since Geo currently relies on PostgreSQL replication, all project metadata
gets replicated to secondary nodes, but repositories that have not been
selected will be empty.
* Since Geo currently relies on PostgreSQL replication, all project metadata
gets replicated to secondary nodes, but repositories that have not been
selected will be empty.
1. Reduce the number of events generated for the Geo event log
* The primary generates events as long as any secondaries are present.
Selective synchronization restrictions are implemented on the secondaries,
not the primary.
* The primary generates events as long as any secondaries are present.
Selective synchronization restrictions are implemented on the secondaries,
not the primary.
A subset of projects can be chosen, either by group or by storage shard. The
former is ideal for replicating data belonging to a subset of users, while the
......
......@@ -403,24 +403,24 @@ data before running `pg_basebackup`.
This command also takes a number of additional options. You can use `--help`
to list them all, but here are a couple of tips:
- If PostgreSQL is listening on a non-standard port, add `--port=` as well.
- If your database is too large to be transferred in 30 minutes, you will need
to increase the timeout, e.g., `--backup-timeout=3600` if you expect the
initial replication to take under an hour.
- Pass `--sslmode=disable` to skip PostgreSQL TLS authentication altogether
(e.g., you know the network path is secure, or you are using a site-to-site
VPN). This is **not** safe over the public Internet!
- You can read more details about each `sslmode` in the
[PostgreSQL documentation][pg-docs-ssl];
the instructions above are carefully written to ensure protection against
both passive eavesdroppers and active "man-in-the-middle" attackers.
- Change the `--slot-name` to the name of the replication slot
to be used on the primary database. The script will attempt to create the
replication slot automatically if it does not exist.
- If you're repurposing an old server into a Geo secondary, you'll need to
add `--force` to the command line.
- When not in a production machine you can disable backup step if you
really sure this is what you want by adding `--skip-backup`
- If PostgreSQL is listening on a non-standard port, add `--port=` as well.
- If your database is too large to be transferred in 30 minutes, you will need
to increase the timeout, e.g., `--backup-timeout=3600` if you expect the
initial replication to take under an hour.
- Pass `--sslmode=disable` to skip PostgreSQL TLS authentication altogether
(e.g., you know the network path is secure, or you are using a site-to-site
VPN). This is **not** safe over the public Internet!
- You can read more details about each `sslmode` in the
[PostgreSQL documentation][pg-docs-ssl];
the instructions above are carefully written to ensure protection against
both passive eavesdroppers and active "man-in-the-middle" attackers.
- Change the `--slot-name` to the name of the replication slot
to be used on the primary database. The script will attempt to create the
replication slot automatically if it does not exist.
- If you're repurposing an old server into a Geo secondary, you'll need to
add `--force` to the command line.
- When not in a production machine you can disable backup step if you
really sure this is what you want by adding `--skip-backup`
1. Verify that the secondary is configured correctly and that the primary is
reachable:
......
......@@ -151,7 +151,6 @@ be specified.
gitlab_rails['redis_host'] = '10.1.2.1'
gitlab_rails['redis_password'] = 'Redis password'
##
## Enable the geo secondary role and configure the
## geo tracking database
......
# Geo (Geo Replication) **[PREMIUM ONLY]**
> **Notes:**
- Geo is part of [GitLab Premium][ee]
- Introduced in GitLab Enterprise Edition 8.9
We recommend you use it with at least GitLab Enterprise Edition 10.0 for
basic Geo features, or latest version for a better experience
- You should make sure that all nodes run the same GitLab version
- Geo requires PostgreSQL 9.6 and Git 2.9 in addition to GitLab's usual
[minimum requirements][install-requirements]
- Using Geo in combination with High Availability (HA) is considered **Generally Available** (GA) in GitLab Enterprise Edition 10.4
>**Note:**
Geo changes significantly from release to release. Upgrades **are**
supported and [documented](#updating-the-geo-nodes), but you should ensure that
you're following the right version of the documentation for your installation!
The best way to do this is to follow the documentation from the `/help` endpoint
on your **primary** node, but you can also navigate to [this page on GitLab.com](https://gitlab.com/gitlab-org/gitlab-ee/blob/master/doc/gitlab-geo/README.md)
and choose the appropriate release from the `tags` dropdown, e.g., `v10.0.0-ee`.
> - Geo is part of [GitLab Premium][ee]
> - Introduced in GitLab Enterprise Edition 8.9
> We recommend you use it with at least GitLab Enterprise Edition 10.0 for
> basic Geo features, or latest version for a better experience
> - You should make sure that all nodes run the same GitLab version
> - Geo requires PostgreSQL 9.6 and Git 2.9 in addition to GitLab's usual
> [minimum requirements][install-requirements]
> - Using Geo in combination with High Availability (HA) is considered **Generally Available** (GA) in GitLab Enterprise Edition 10.4
>
> **Note:**
> Geo changes significantly from release to release. Upgrades **are**
> supported and [documented](#updating-the-geo-nodes), but you should ensure that
> you're following the right version of the documentation for your installation!
> The best way to do this is to follow the documentation from the `/help` endpoint
> on your **primary** node, but you can also navigate to [this page on GitLab.com](https://gitlab.com/gitlab-org/gitlab-ee/blob/master/doc/gitlab-geo/README.md)
> and choose the appropriate release from the `tags` dropdown, e.g., `v10.0.0-ee`.
Geo allows you to replicate your GitLab instance to other geographical
locations as a read-only fully operational version.
......@@ -202,9 +202,8 @@ Read how to [replicate the Container Registry][docker-registry].
extra limitations may be in place.
- Pushing code to a secondary redirects the request to the primary instead of handling it directly [gitlab-ee#1381](https://gitlab.com/gitlab-org/gitlab-ee/issues/1381):
* Only push via HTTP is currently supported
* Git LFS is supported
* Pushing via SSH is currently not supported: [gitlab-ee#5387](https://gitlab.com/gitlab-org/gitlab-ee/issues/5387)
* Push via HTTP and SSH supported
* Git LFS also supported
- The primary node has to be online for OAuth login to happen (existing sessions and Git are not affected)
- The installation takes multiple manual steps that together can take about an hour depending on circumstances; we are
working on improving this experience, see [gitlab-org/omnibus-gitlab#2978] for details.
......
......@@ -105,9 +105,9 @@ log data to build up in `pg_xlog`. Removing the unused slots can reduce the amou
2. View your replication slots with
```sql
SELECT * FROM pg_replication_slots;
```
```sql
SELECT * FROM pg_replication_slots;
```
Slots where `active` is `f` are not active.
......
......@@ -49,7 +49,8 @@ authentication method.
# Every node that runs Unicorn or Sidekiq needs to have the database
# password specified as below. If you have a high-availability setup, this
# must be present in all application nodes.
gitlab_rails['db_password'] = 'mypassword' ```
gitlab_rails['db_password'] = 'mypassword'
```
Still in the configuration file, locate and remove the `trust_auth_cidr_address`:
......
......@@ -10,14 +10,14 @@ there are a few things to consider:
1. Clone the repository as you would normally do, but from the secondary node:
```bash
git clone git@secondary.gitlab.example.com:user/repo.git
```
```bash
git clone git@secondary.gitlab.example.com:user/repo.git
```
1. Change the remote push URL to always push to primary, following this example:
```bash
git remote set-url --push origin git@primary.gitlab.example.com:user/repo.git
```
```bash
git remote set-url --push origin git@primary.gitlab.example.com:user/repo.git
```
[req]: index.md#setup-instructions
......@@ -123,7 +123,7 @@ separately:
1. [Configure Redis](redis.md)
1. [Configure Redis for GitLab source installations](redis_source.md)
1. [Configure NFS](nfs.md)
1. [NFS Client and Host setup](nfs_host_client_setup.md)
1. [NFS Client and Host setup](nfs_host_client_setup.md)
1. [Configure the GitLab application servers](gitlab.md)
1. [Configure the load balancers](load_balancer.md)
......@@ -52,12 +52,12 @@ To fix this:
1. Pick an address on each node that all of the other nodes can reach this node through.
1. Update your `/etc/gitlab/gitlab.rb`
```ruby
consul['configuration'] = {
...
bind_addr: 'IP ADDRESS'
}
```
```ruby
consul['configuration'] = {
...
bind_addr: 'IP ADDRESS'
}
```
1. Run `gitlab-ctl reconfigure`
If you still see the errors, you may have to [erase the consul database and reinitialize](#recreate-from-scratch) on the affected node.
......@@ -78,12 +78,12 @@ To fix this:
1. Pick an address on the node that all of the other nodes can reach this node through.
1. Update your `/etc/gitlab/gitlab.rb`
```ruby
consul['configuration'] = {
...
bind_addr: 'IP ADDRESS'
}
```
```ruby
consul['configuration'] = {
...
bind_addr: 'IP ADDRESS'
}
```
1. Run `gitlab-ctl reconfigure`
### Outage recovery
......
......@@ -6,13 +6,12 @@ PostgreSQL. This is the database that will be installed if you use the
Omnibus package to manage your database.
> Important notes:
- This document will focus only on configuration supported with [GitLab Premium](https://about.gitlab.com/pricing/), using the Omnibus GitLab package.
- If you are a Community Edition or Starter user, consider using a cloud hosted solution.
- This document will not cover installations from source.
> - This document will focus only on configuration supported with [GitLab Premium](https://about.gitlab.com/pricing/), using the Omnibus GitLab package.
> - If you are a Community Edition or Starter user, consider using a cloud hosted solution.
> - This document will not cover installations from source.
>
- If HA setup is not what you were looking for, see the [database configuration document](http://docs.gitlab.com/omnibus/settings/database.html)
for the Omnibus GitLab packages.
> - If HA setup is not what you were looking for, see the [database configuration document](http://docs.gitlab.com/omnibus/settings/database.html)
> for the Omnibus GitLab packages.
## Configure your own database server
......@@ -31,20 +30,18 @@ If you use a cloud-managed service, or provide your own PostgreSQL:
## Configure using Omnibus for High Availability
> Please read this document fully before attempting to configure PostgreSQL HA
> for GitLab.
>
Please read this document fully before attempting to configure PostgreSQL HA
for GitLab.
>
This configuration is GA in EE 10.2.
> This configuration is GA in EE 10.2.
The recommended configuration for a PostgreSQL HA requires:
- A minimum of three database nodes
- Each node will run the following services:
- `PostgreSQL` - The database itself
- `repmgrd` - A service to monitor, and handle failover in case of a failure
- `Consul` agent - Used for service discovery, to alert other nodes when failover occurs
- `PostgreSQL` - The database itself
- `repmgrd` - A service to monitor, and handle failover in case of a failure
- `Consul` agent - Used for service discovery, to alert other nodes when failover occurs
- A minimum of three `Consul` server nodes
- A minimum of one `pgbouncer` service node
......@@ -59,12 +56,12 @@ otherwise the networks will become a single point of failure.
Database nodes run two services besides PostgreSQL
1. Repmgrd -- monitors the cluster and handles failover in case of an issue with the master
The failover consists of
* Selecting a new master for the cluster
* Promoting the new node to master
* Instructing remaining servers to follow the new master node
The failover consists of
* Selecting a new master for the cluster
* Promoting the new node to master
* Instructing remaining servers to follow the new master node
On failure, the old master node is automatically evicted from the cluster, and should be rejoined manually once recovered.
On failure, the old master node is automatically evicted from the cluster, and should be rejoined manually once recovered.
1. Consul -- Monitors the status of each node in the database cluster, and tracks its health in a service definiton on the consul cluster.
......@@ -94,12 +91,12 @@ Similarly, PostgreSQL access is controlled based on the network source.
This is why you will need:
> IP address of each nodes network interface
- This can be set to `0.0.0.0` to listen on all interfaces. It cannot
be set to the loopack address `127.0.0.1`
> - This can be set to `0.0.0.0` to listen on all interfaces. It cannot
> be set to the loopack address `127.0.0.1`
>
> Network Address
- This can be in subnet (i.e. `192.168.0.0/255.255.255.0`) or CIDR (i.e.
`192.168.0.0/24`) form.
> - This can be in subnet (i.e. `192.168.0.0/255.255.255.0`) or CIDR (i.e.
> `192.168.0.0/24`) form.
#### User information
......@@ -115,10 +112,12 @@ When using default setup, minimum configuration requires:
- `CONSUL_USERNAME`. Defaults to `gitlab-consul`
- `CONSUL_DATABASE_PASSWORD`. Password for the database user.
- `CONSUL_PASSWORD_HASH`. This is a hash generated out of consul username/password pair.
Can be generated with:
Can be generated with:
```sh
sudo gitlab-ctl pg-password-md5 CONSUL_USERNAME
```
- `CONSUL_SERVER_NODES`. The IP addresses or DNS records of the Consul server nodes.
Few notes on the service itself:
......@@ -141,7 +140,7 @@ This is used to prevent replication from using up all of the
available database connections.
> Note:
- In this document we are assuming 3 database nodes, which makes this configuration:
> - In this document we are assuming 3 database nodes, which makes this configuration:
```
postgresql['max_wal_senders'] = 4
......@@ -157,7 +156,8 @@ We will need the following password information for the application's database u
- `POSTGRESQL_USERNAME`. Defaults to `gitlab`
- `POSTGRESQL_USER_PASSWORD`. The password for the database user
- `POSTGRESQL_PASSWORD_HASH`. This is a hash generated out of the username/password pair.
Can be generated with:
Can be generated with:
```sh
sudo gitlab-ctl pg-password-md5 POSTGRESQL_USERNAME
```
......@@ -169,10 +169,12 @@ When using default setup, minimum configuration requires:
- `PGBOUNCER_USERNAME`. Defaults to `pgbouncer`
- `PGBOUNCER_PASSWORD`. This is a password for pgbouncer service.
- `PGBOUNCER_PASSWORD_HASH`. This is a hash generated out of pgbouncer username/password pair.
Can be generated with:
Can be generated with:
```sh
sudo gitlab-ctl pg-password-md5 PGBOUNCER_USERNAME
```
- `PGBOUNCER_NODE`, is the IP address or a FQDN of the node running Pgbouncer.
Few notes on the service itself:
......@@ -321,11 +323,11 @@ check the [Troubleshooting section](#troubleshooting) before proceeding.
1. [Reconfigure GitLab] for the changes to take effect.
> Please note:
- If you want your database to listen on a specific interface, change the config:
`postgresql['listen_address'] = '0.0.0.0'`
- If your Pgbouncer service runs under a different user account,
you also need to specify: `postgresql['pgbouncer_user'] = PGBOUNCER_USERNAME` in
your configuration
> - If you want your database to listen on a specific interface, change the config:
> `postgresql['listen_address'] = '0.0.0.0'`
> - If your Pgbouncer service runs under a different user account,
> you also need to specify: `postgresql['pgbouncer_user'] = PGBOUNCER_USERNAME` in
> your configuration
#### Database nodes post-configuration
......@@ -349,17 +351,17 @@ Select one node as a primary node.
1. Verify the cluster is initialized with one node:
```sh
gitlab-ctl repmgr cluster show
```
```sh
gitlab-ctl repmgr cluster show
```
The output should be similar to the following:
The output should be similar to the following:
```
Role | Name | Upstream | Connection String
----------+----------|----------|----------------------------------------
* master | HOSTNAME | | host=HOSTNAME user=gitlab_repmgr dbname=gitlab_repmgr
```
```
Role | Name | Upstream | Connection String
----------+----------|----------|----------------------------------------
* master | HOSTNAME | | host=HOSTNAME user=gitlab_repmgr dbname=gitlab_repmgr
```
1. Note down the hostname/ip in the connection string: `host=HOSTNAME`. We will
refer to the hostname in the next section as `MASTER_NODE_NAME`. If the value
......@@ -374,6 +376,7 @@ Select one node as a primary node.
```sh
gitlab-ctl repmgr standby setup MASTER_NODE_NAME
```
Do note that this will remove the existing data on the node. The command
has a wait time.
......@@ -395,18 +398,18 @@ Select one node as a primary node.
1. Verify the node now appears in the cluster:
```sh
gitlab-ctl repmgr cluster show
```
```sh
gitlab-ctl repmgr cluster show
```
The output should be similar to the following:
The output should be similar to the following:
```
Role | Name | Upstream | Connection String
----------+---------|-----------|------------------------------------------------
* master | MASTER | | host=MASTER_NODE_NAME user=gitlab_repmgr dbname=gitlab_repmgr
standby | STANDBY | MASTER | host=STANDBY_HOSTNAME user=gitlab_repmgr dbname=gitlab_repmgr
```
```
Role | Name | Upstream | Connection String
----------+---------|-----------|------------------------------------------------
* master | MASTER | | host=MASTER_NODE_NAME user=gitlab_repmgr dbname=gitlab_repmgr
standby | STANDBY | MASTER | host=STANDBY_HOSTNAME user=gitlab_repmgr dbname=gitlab_repmgr
```
Repeat the above steps on all secondary nodes.
......@@ -490,44 +493,44 @@ Check the [Troubleshooting section](#troubleshooting) before proceeding.
1. Create a `.pgpass` file so Consule is able to
reload pgbouncer. Enter the `PGBOUNCER_PASSWORD` twice when asked:
```sh
gitlab-ctl write-pgpass --host 127.0.0.1 --database pgbouncer --user pgbouncer --hostuser gitlab-consul
```
```sh
gitlab-ctl write-pgpass --host 127.0.0.1 --database pgbouncer --user pgbouncer --hostuser gitlab-consul
```
#### PGBouncer Checkpoint
1. Ensure the node is talking to the current master:
```sh
gitlab-ctl pgb-console # You will be prompted for PGBOUNCER_PASSWORD
```
```sh
gitlab-ctl pgb-console # You will be prompted for PGBOUNCER_PASSWORD
```
If there is an error `psql: ERROR: Auth failed` after typing in the
password, ensure you previously generated the MD5 password hashes with the correct
format. The correct format is to concatenate the password and the username:
`PASSWORDUSERNAME`. For example, `Sup3rS3cr3tpgbouncer` would be the text
needed to generate an MD5 password hash for the `pgbouncer` user.
If there is an error `psql: ERROR: Auth failed` after typing in the
password, ensure you previously generated the MD5 password hashes with the correct
format. The correct format is to concatenate the password and the username:
`PASSWORDUSERNAME`. For example, `Sup3rS3cr3tpgbouncer` would be the text
needed to generate an MD5 password hash for the `pgbouncer` user.
1. Once the console prompt is available, run the following queries:
```sh
show databases ; show clients ;
```
The output should be similar to the following:
```sh
show databases ; show clients ;
```
```
name | host | port | database | force_user | pool_size | reserve_pool | pool_mode | max_connections | current_connections
---------------------+-------------+------+---------------------+------------+-----------+--------------+-----------+-----------------+---------------------
gitlabhq_production | MASTER_HOST | 5432 | gitlabhq_production | | 20 | 0 | | 0 | 0
pgbouncer | | 6432 | pgbouncer | pgbouncer | 2 | 0 | statement | 0 | 0
(2 rows)
The output should be similar to the following:
type | user | database | state | addr | port | local_addr | local_port | connect_time | request_time | ptr | link | remote_pid | tls
------+-----------+---------------------+---------+----------------+-------+------------+------------+---------------------+---------------------+-----------+------+------------+-----
C | pgbouncer | pgbouncer | active | 127.0.0.1 | 56846 | 127.0.0.1 | 6432 | 2017-08-21 18:09:59 | 2017-08-21 18:10:48 | 0x22b3880 | | 0 |
(2 rows)
```
```
name | host | port | database | force_user | pool_size | reserve_pool | pool_mode | max_connections | current_connections
---------------------+-------------+------+---------------------+------------+-----------+--------------+-----------+-----------------+---------------------
gitlabhq_production | MASTER_HOST | 5432 | gitlabhq_production | | 20 | 0 | | 0 | 0
pgbouncer | | 6432 | pgbouncer | pgbouncer | 2 | 0 | statement | 0 | 0
(2 rows)
type | user | database | state | addr | port | local_addr | local_port | connect_time | request_time | ptr | link | remote_pid | tls
------+-----------+---------------------+---------+----------------+-------+------------+------------+---------------------+---------------------+-----------+------+------------+-----
C | pgbouncer | pgbouncer | active | 127.0.0.1 | 56846 | 127.0.0.1 | 6432 | 2017-08-21 18:09:59 | 2017-08-21 18:10:48 | 0x22b3880 | | 0 |
(2 rows)
```
### Configuring the Application nodes
......@@ -986,24 +989,24 @@ the previous section:
1. On each database node:
1. Edit `/etc/gitlab/gitlab.rb`:
1. Ensure `repmgr['trust_auth_cidr_addresses']` is **not** set
1. Set `postgresql['md5_auth_cidr_addresses']` to the desired value
1. Set `postgresql['sql_replication_user'] = 'gitlab_repmgr'`
1. Reconfigure with `gitlab-ctl reconfigure`
1. Restart postgresql with `gitlab-ctl restart postgresql`
1. Edit `/etc/gitlab/gitlab.rb`:
1. Ensure `repmgr['trust_auth_cidr_addresses']` is **not** set
1. Set `postgresql['md5_auth_cidr_addresses']` to the desired value
1. Set `postgresql['sql_replication_user'] = 'gitlab_repmgr'`
1. Reconfigure with `gitlab-ctl reconfigure`
1. Restart postgresql with `gitlab-ctl restart postgresql`
1. Create a `.pgpass` file. Enter the `gitlab_repmgr` password twice to
when asked:
1. Create a `.pgpass` file. Enter the `gitlab_repmgr` password twice to
when asked:
```sh
gitlab-ctl write-pgpass --user gitlab_repmgr --hostuser gitlab-psql --database '*'
```
1. On each pgbouncer node, edit `/etc/gitlab/gitlab.rb`:
1. Ensure `gitlab_rails['db_password']` is set to the plaintext password for
the `gitlab` database user
1. [Reconfigure GitLab] for the changes to take effect
1. Ensure `gitlab_rails['db_password']` is set to the plaintext password for
the `gitlab` database user
1. [Reconfigure GitLab] for the changes to take effect
### Troubleshooting
......@@ -1032,7 +1035,7 @@ steps to fix the problem:
Now there should not be errors. If errors still occur then there is another problem.
#### PGBouncer error `ERROR: pgbouncer cannot connect to server`
#### PGBouncer error `ERROR: pgbouncer cannot connect to server`
You may get this error when running `gitlab-rake gitlab:db:configure` or you
may see the error in the PGBouncer log file.
......
......@@ -91,7 +91,7 @@ for each GitLab application server in your environment.
certificates are not present, Nginx will fail to start. See
[Nginx documentation](http://docs.gitlab.com/omnibus/settings/nginx.html#enable-https)
for more information.
>
> **Note:** It is best to set the `uid` and `gid`s prior to the initial reconfigure of GitLab. Omnibus will not recursively `chown` directories if set after the initial reconfigure.
## First GitLab application server
......
......@@ -22,39 +22,40 @@ See our [HA documentation for PostgreSQL](database.md) for information on runnin
1. Generate SQL_USER_PASSWORD_HASH with the command `gitlab-ctl pg-password-md5 gitlab`. We'll also need to enter the plaintext SQL_USER_PASSWORD later
1. On your database node, ensure the following is set in your `/etc/gitlab/gitlab.rb`
```ruby
postgresql['pgbouncer_user_password'] = 'PGBOUNCER_USER_PASSWORD_HASH'
postgresql['sql_user_password'] = 'SQL_USER_PASSWORD_HASH'
postgresql['listen_address'] = 'XX.XX.XX.Y' # Where XX.XX.XX.Y is the ip address on the node postgresql should listen on
postgresql['md5_auth_cidr_addresses'] = %w(AA.AA.AA.B/32) # Where AA.AA.AA.B is the IP address of the pgbouncer node
```
```ruby
postgresql['pgbouncer_user_password'] = 'PGBOUNCER_USER_PASSWORD_HASH'
postgresql['sql_user_password'] = 'SQL_USER_PASSWORD_HASH'
postgresql['listen_address'] = 'XX.XX.XX.Y' # Where XX.XX.XX.Y is the ip address on the node postgresql should listen on
postgresql['md5_auth_cidr_addresses'] = %w(AA.AA.AA.B/32) # Where AA.AA.AA.B is the IP address of the pgbouncer node
```
1. Run `gitlab-ctl reconfigure`
**Note:** If the database was already running, it will need to be restarted after reconfigure by running `gitlab-ctl restart postgresql`.
**Note:** If the database was already running, it will need to be restarted after reconfigure by running `gitlab-ctl restart postgresql`.
1. On the node you are running pgbouncer on, make sure the following is set in `/etc/gitlab/gitlab.rb`
```ruby
pgbouncer['enable'] = true
pgbouncer['databases'] = {
gitlabhq_production: {
host: 'DATABASE_HOST',
user: 'pgbouncer',
password: 'PGBOUNCER_USER_PASSWORD_HASH'
}
}
```
```ruby
pgbouncer['enable'] = true
pgbouncer['databases'] = {
gitlabhq_production: {
host: 'DATABASE_HOST',
user: 'pgbouncer',
password: 'PGBOUNCER_USER_PASSWORD_HASH'
}
}
```
1. Run `gitlab-ctl reconfigure`
1. On the node running unicorn, make sure the following is set in `/etc/gitlab/gitlab.rb`
```ruby
gitlab_rails['db_host'] = 'PGBOUNCER_HOST'
gitlab_rails['db_port'] = '6432'
gitlab_rails['db_password'] = 'SQL_USER_PASSWORD'
```
```ruby
gitlab_rails['db_host'] = 'PGBOUNCER_HOST'
gitlab_rails['db_port'] = '6432'
gitlab_rails['db_password'] = 'SQL_USER_PASSWORD'
```
1. Run `gitlab-ctl reconfigure`
......
# GitLab private Maven repository administration
> **Notes:**
- [Introduced][ee-5811] in GitLab 11.3.
- This document is about the admin guide. Learn how to use GitLab Maven
repository from [user documentation](../user/project/maven_packages.md).
> - [Introduced][ee-5811] in GitLab 11.3.
> - This document is about the admin guide. Learn how to use GitLab Maven
> repository from [user documentation](../user/project/maven_packages.md).
When enabled, every project in GitLab will have its own space to store Maven packages.
......
# GitLab ChatOps **[ULTIMATE]**
> **Notes:**
> **Notes:**
>
> * [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/4466) in [GitLab Ultimate](https://about.gitlab.com/pricing/) 10.6.
>
> * ChatOps is currently in alpha, with some important features missing like access control.
GitLab ChatOps provides a method to interact with CI/CD jobs through chat services like Slack. Many organizations' discussion, collaboration, and troubleshooting is taking place in chat services these days, and having a method to run CI/CD jobs with output posted back to the channel can significantly augment a team's workflow.
......
......@@ -9,7 +9,7 @@ you may need to enable pipeline triggering in your project's
## Pipelines
A pipeline is a group of [jobs][] that get executed in [stages][](batches).
A pipeline is a group of [jobs] that get executed in [stages].
All of the jobs in a stage are executed in parallel (if there are enough
concurrent [Runners]), and if they all succeed, the pipeline moves on to the
next stage. If one of the jobs fails, the next stage is not (usually)
......@@ -29,17 +29,17 @@ There are three types of pipelines that often use the single shorthand of "pipel
![Types of Pipelines](img/types-of-pipelines.svg)
1. **CI Pipeline**: Build and test stages defined in `.gitlab-ci.yml`
2. **Deploy Pipeline**: Deploy stage(s) defined in `.gitlab-ci.yml` The flow of deploying code to servers through various stages: e.g. development to staging to production
3. **Project Pipeline**: Cross-project CI dependencies [triggered via API][triggers], particularly for micro-services, but also for complicated build dependencies: e.g. api -> front-end, ce/ee -> omnibus.
1. **CI Pipeline**: Build and test stages defined in `.gitlab-ci.yml`.
1. **Deploy Pipeline**: Deploy stage(s) defined in `.gitlab-ci.yml` The flow of deploying code to servers through various stages: e.g. development to staging to production.
1. **Project Pipeline**: Cross-project CI dependencies [triggered via API][triggers], particularly for micro-services, but also for complicated build dependencies: e.g. api -> front-end, ce/ee -> omnibus.
## Development workflows
Pipelines accommodate several development workflows:
1. **Branch Flow** (e.g. different branch for dev, qa, staging, production)
2. **Trunk-based Flow** (e.g. feature branches and single master branch, possibly with tags for releases)
3. **Fork-based Flow** (e.g. merge requests come from forks)
1. **Branch Flow** (e.g. different branch for dev, qa, staging, production).
1. **Trunk-based Flow** (e.g. feature branches and single master branch, possibly with tags for releases).
1. **Fork-based Flow** (e.g. merge requests come from forks).
Example continuous delivery flow:
......@@ -57,6 +57,16 @@ Pipelines are defined in `.gitlab-ci.yml` by specifying [jobs] that run in
See the reference [documentation for jobs](yaml/README.md#jobs).
## Manually executing pipelines
Pipelines can be manually executed, with predefined or manually-specified [variables](variables/README.md).
To execute a pipeline manually:
1. Navigate to your project's **CI/CD > Pipelines**.
1. Click on the **Run Pipeline** button.
1. Select the branch to run the pipeline for and enter any environment variables required for the pipeline run.
## Seeing pipeline status
You can find the current and historical pipeline runs under your project's
......@@ -112,9 +122,9 @@ Then, there is the pipeline mini graph which takes less space and can give you a
quick glance if all jobs pass or something failed. The pipeline mini graph can
be found when you visit:
- the pipelines index page
- a single commit page
- a merge request page
- The pipelines index page.
- A single commit page.
- A merge request page.
That way, you can see all related jobs for a single commit and the net result
of each stage of your pipeline. This allows you to quickly see what failed and
......@@ -142,9 +152,9 @@ jobs. Click to expand them.
The basic requirements is that there are two numbers separated with one of
the following (you can even use them interchangeably):
- a space
- a slash (`/`)
- a colon (`:`)
- A space (` `)
- A slash (`/`)
- A colon (`:`)
>**Note:**
More specifically, [it uses][regexp] this regular expression: `\d+[\s:\/\\]+\d+\s*`.
......@@ -257,11 +267,12 @@ A strict security model is enforced when pipelines are executed on
The following actions are allowed on protected branches only if the user is
[allowed to merge or push](../user/project/protected_branches.md#using-the-allowed-to-merge-and-allowed-to-push-settings)
on that specific branch:
- run **manual pipelines** (using Web UI or Pipelines API)
- run **scheduled pipelines**
- run pipelines using **triggers**
- trigger **manual actions** on existing pipelines
- **retry/cancel** existing jobs (using Web UI or Pipelines API)
- Run **manual pipelines** (using [Web UI](#manually-executing-pipelines) or Pipelines API).
- Run **scheduled pipelines**.
- Run pipelines using **triggers**.
- Trigger **manual actions** on existing pipelines.
- **Retry/cancel** existing jobs (using Web UI or Pipelines API).
**Variables** marked as **protected** are accessible only to jobs that
run on protected branches, avoiding untrusted users to get unintended access to
......
......@@ -34,7 +34,7 @@ Some of the predefined environment variables are available only if a minimum
version of [GitLab Runner][runner] is used. Consult the table below to find the
version of Runner required.
>**Note:**
NOTE: **Note:**
Starting with GitLab 9.0, we have deprecated some variables. Read the
[9.0 Renaming](#9-0-renaming) section to find out their replacements. **You are
strongly advised to use the new variables as we will remove the old ones in
......@@ -112,7 +112,7 @@ To follow conventions of naming across GitLab, and to further move away from the
`build` term and toward `job` CI variables have been renamed for the 9.0
release.
>**Note:**
NOTE: **Note:**
Starting with GitLab 9.0, we have deprecated the `$CI_BUILD_*` variables. **You are
strongly advised to use the new variables as we will remove the old ones in
future GitLab releases.**
......@@ -134,7 +134,7 @@ future GitLab releases.**
## `.gitlab-ci.yml` defined variables
>**Note:**
NOTE **Note:**
This feature requires GitLab Runner 0.5.0 or higher and GitLab CI 7.14 or higher.
GitLab CI allows you to add to `.gitlab-ci.yml` variables that are set in the
......@@ -236,9 +236,15 @@ Wildcards (`*`) can be used along with the environment name, therefore if the
environment scope is `review/*` then any jobs with environment names starting
with `review/` would have that particular variable.
### Manually-specified variables
> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/issues/44059) in GitLab 10.8.
Variables can be specified for a single pipeline run when a [manual pipeline](../pipelines.md#manually-executing-pipelines) is created.
## Deployment variables
>**Note:**
NOTE: **Note:**
This feature requires GitLab CI 8.15 or higher.
[Project services](../../user/project/integrations/project_services.md) that are
......
......@@ -8,10 +8,10 @@ This document describes where and how the different types of variables can be us
## Variables usage
There are basically two places where you can use any defined variables:
There are two places defined variables can be used. On the:
1. On GitLab's side there's `.gitlab-ci.yml`
1. On the Runner's side there's `config.toml`
1. GitLab side, in `.gitlab-ci.yml`.
1. The runner side, in `config.toml`.
### `.gitlab-ci.yml` file
......@@ -56,8 +56,8 @@ since the expansion is done in GitLab before any Runner will get the job.
### GitLab Runner internal variable expansion mechanism
- **Supported:** project/group variables, `.gitlab-ci.yml` variables, `config.toml` variables, and
variables from triggers and pipeline schedules
- **Not supported:** variables defined inside of scripts (e.g., `export MY_VARIABLE="test"`)
variables from triggers, pipeline schedules, and manual pipelines.
- **Not supported:** variables defined inside of scripts (e.g., `export MY_VARIABLE="test"`).
The Runner uses Go's `os.Expand()` method for variable expansion. It means that it will handle
only variables defined as `$variable` and `${variable}`. What's also important, is that
......@@ -80,11 +80,10 @@ are using a different variables syntax.
`.gitlab-ci.yml` variables, `config.toml` variables, and variables from triggers and pipeline schedules).
- The `script` may also use all variables defined in the lines before. So, for example, if you define
a variable `export MY_VARIABLE="test"`:
- in `before_script`, it will work in the following lines of `before_script` and
all lines of the related `script`
- in `script`, it will work in the following lines of `script`
- in `after_script`, it will work in following lines of `after_script`
- In `before_script`, it will work in the following lines of `before_script` and
all lines of the related `script`.
- In `script`, it will work in the following lines of `script`.
- In `after_script`, it will work in following lines of `after_script`.
## Persisted variables
......@@ -107,10 +106,10 @@ The following variables are known as "persisted":
They are:
- **supported** for all definitions as [described in the table](#gitlab-ci-yml-file) where the "Expansion place" is "Runner"
- **not supported:**
- by the definitions [described in the table](#gitlab-ci-yml-file) where the "Expansion place" is "GitLab"
- in the `only` and `except` [variables expressions](README.md#variables-expressions)
- **Supported** for all definitions as [described in the table](#gitlab-ci-yml-file) where the "Expansion place" is "Runner".
- **Not supported:**
- By the definitions [described in the table](#gitlab-ci-yml-file) where the "Expansion place" is "GitLab".
- In the `only` and `except` [variables expressions](README.md#variables-expressions).
## Variables with an environment scope
......
# Elasticsearch integration **[STARTER ONLY]**
>
[Introduced][ee-109] in GitLab [Starter][ee] 8.4. Support
for [Amazon Elasticsearch][aws-elastic] was [introduced][ee-1305] in GitLab
[Starter][ee] 9.0.
> [Introduced][ee-109] in GitLab [Starter][ee] 8.4. Support
> for [Amazon Elasticsearch][aws-elastic] was [introduced][ee-1305] in GitLab
> [Starter][ee] 9.0.
This document describes how to set up Elasticsearch with GitLab. Once enabled,
you'll have the benefit of fast search response times and the advantage of two
......
......@@ -93,11 +93,11 @@ from the left navigation menu. Click `Link GitHub account` to start creating a n
for all the projects in the GitLab group you specified in the previous step. These are refreshed
every 60 minutes.
>**Note:**
In the future, we plan on implementating real-time integration. If you need
to refresh the data manually, you can do this from the `Applications -> DVCS
accounts` screen where you initially set up the integration:
> **Note:**
> In the future, we plan on implementating real-time integration. If you need
> to refresh the data manually, you can do this from the `Applications -> DVCS
> accounts` screen where you initially set up the integration:
>
> ![Refresh GitLab information in JIRA](img/jira_dev_panel_manual_refresh.png)
To connect additional GitLab projects from other GitLab top-level groups (or personal namespaces), repeat the above
......
# External authorization control **[PREMIUM]**
>
[Introduced](https://gitlab.com/gitlab-org/gitlab-ee/issues/4216) in
[GitLab Premium](https://about.gitlab.com/pricing) 10.6.
> [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/issues/4216) in
> [GitLab Premium](https://about.gitlab.com/pricing) 10.6.
In highly controlled environments, it may be necessary for access policy to be
controlled by an external service that permits access based on project
......
......@@ -83,14 +83,14 @@ To add an existing Kubernetes cluster to your project:
- **Kubernetes cluster name** (required) - The name you wish to give the cluster.
- **Environment scope** (required)- The
[associated environment](#setting-the-environment-scope) to this cluster.
* **API URL** (required) -
- **API URL** (required) -
It's the URL that GitLab uses to access the Kubernetes API. Kubernetes
exposes several APIs, we want the "base" URL that is common to all of them,
e.g., `https://kubernetes.example.com` rather than `https://kubernetes.example.com/api/v1`.
* **CA certificate** (optional) -
- **CA certificate** (optional) -
If the API is using a self-signed TLS certificate, you'll also need to include
the `ca.crt` contents here.
* **Token** -
- **Token** -
GitLab authenticates against Kubernetes using service tokens, which are
scoped to a particular `namespace`. If you don't have a service token yet,
you can follow the
......@@ -101,13 +101,14 @@ To add an existing Kubernetes cluster to your project:
must have admin privileges on the cluster.**
- **Project namespace** (optional) - You don't have to fill it in; by leaving
it blank, GitLab will create one for you. Also:
- Each project should have a unique namespace.
- The project namespace is not necessarily the namespace of the secret, if
you're using a secret with broader permissions, like the secret from `default`.
* You should **not** use `default` as the project namespace.
* If you or someone created a secret specifically for the project, usually
with limited permissions, the secret's namespace and project namespace may
be the same.
- Each project should have a unique namespace.
- The project namespace is not necessarily the namespace of the secret, if
you're using a secret with broader permissions, like the secret from `default`.
- You should **not** use `default` as the project namespace.
- If you or someone created a secret specifically for the project, usually
with limited permissions, the secret's namespace and project namespace may
be the same.
1. Finally, click the **Create Kubernetes cluster** button.
After a couple of minutes, your cluster will be ready to go. You can now proceed
......
......@@ -57,13 +57,12 @@ changes you made after picking the template and return it to its initial status.
## Setting a default template for issues and merge requests **[STARTER]**
>
**Notes:**
- This feature was introduced before [description templates](#overview) and is
available in [GitLab Starter][products]. It can be enabled
in the project's settings.
- Templates for issues were [introduced][ee-28] in GitLab EE 8.1.
- Templates for merge requests were [introduced][ee-7478ece] in GitLab EE 6.9.
> **Notes:**
> - This feature was introduced before [description templates](#overview) and is
> available in [GitLab Starter][products]. It can be enabled
> in the project's settings.
> - Templates for issues were [introduced][ee-28] in GitLab EE 8.1.
> - Templates for merge requests were [introduced][ee-7478ece] in GitLab EE 6.9.
The visibility of issues and/or merge requests should be set to either "Everyone
with access" or "Only team members" in your project's **Settings** otherwise the
......
# File Locking **[PREMIUM]**
>**Notes:**
- [Introduced][ee-440] in [GitLab Premium][ee] 8.9.
- This feature needs to have a license with the "File Lock" option enabled. If
you are using Premium but you don't see the "Lock" button,
ask your GitLab administrator.
> **Notes:**
> - [Introduced][ee-440] in [GitLab Premium][ee] 8.9.
> - This feature needs to have a license with the "File Lock" option enabled. If
> you are using Premium but you don't see the "Lock" button,
> ask your GitLab administrator.
File Locking helps you avoid merge conflicts and better manage your binary files.
Lock any file or directory, make your changes, and then unlock it so another
......
......@@ -289,7 +289,7 @@ Each security vulnerability in the report is actionable. Clicking on an entry,
a detailed information will pop up with two different possible options:
- **Dismiss vulnerability** - Dismissing a vulnerability will place a
~~strikethrough~~ styling on it.
<s>strikethrough</s> styling on it.
- **Create issue** - The new issue will have the title and description
pre-populated with the information of the vulnerability report.
......
......@@ -75,17 +75,16 @@ the following is possible:
- **They are not an eligible approver**: They cannot do anything with respect
to approving this merge request.
- **They have not approved this merge request**:
- If the required number of approvals has _not_ been yet met, they can approve
it by clicking the displayed **Approve** button.
![Approve](img/approve.png)
- If the required number of approvals has already been met, they can still
approve it by clicking the displayed **Add approval** button.
![Add approval](img/approve_additionally.png)
---
- **They have already approved this merge request**: They can remove their approval.
![Remove approval](img/remove_approval.png)
......
# Burndown Charts **[STARTER]**
>**Notes:**
- [Introduced][ee-1540] in [GitLab Starter 9.1][ee-9.1] for project milestones.
- [Introduced][ee-5354] in [GitLab Premium 10.8][ee-10.8] for group milestones.
- [Added][ee-6495] to [GitLab Starter 11.2][ee-11.2] for group milestones.
- Closed or reopened issues prior to GitLab 9.1 won't have a `closed_at`
value, so the burndown chart considers them as closed on the milestone
`start_date`. In that case, a warning will be displayed.
> **Notes:**
> - [Introduced][ee-1540] in [GitLab Starter 9.1][ee-9.1] for project milestones.
> - [Introduced][ee-5354] in [GitLab Premium 10.8][ee-10.8] for group milestones.
> - [Added][ee-6495] to [GitLab Starter 11.2][ee-11.2] for group milestones.
> - Closed or reopened issues prior to GitLab 9.1 won't have a `closed_at`
> value, so the burndown chart considers them as closed on the milestone
> `start_date`. In that case, a warning will be displayed.
## Overview
......@@ -51,9 +51,8 @@ Find your project's **Burndown Chart** under **Project > Issues > Milestones**,
and select a milestone from your current ones, while for group's, access the **Groups** dashboard,
select a group, and go through **Issues > Milestones** on the sidebar.
>
**Note:** You're able to [promote project][promote-milestone] to group milestones and still
see the **Burndown Chart** for them, respecting license limitations.
> **Note:** You're able to [promote project][promote-milestone] to group milestones and still
> see the **Burndown Chart** for them, respecting license limitations.
The chart indicates the project's progress throughout that milestone (for issues assigned to it).
......
......@@ -157,6 +157,10 @@ into your `README.md`:
![coverage](https://gitlab.com/gitlab-org/gitlab-ce/badges/master/coverage.svg?job=coverage)
```
### Environment Variables
[Environment variables](../../../ci/variables/README.html#variables) can be set in an environment to be available to a runner.
[var]: ../../../ci/yaml/README.md#git-strategy
[coverage report]: #test-coverage-parsing
[timeout overriding]: ../../../ci/runners/README.html#setting-maximum-job-timeout-for-a-runner
......
# Advanced Global Search **[STARTER]**
>
- [Introduced][ee-109] in GitLab [Starter][ee] 8.4.
- This is the user documentation. To install and configure Elasticsearch,
visit the [admin docs](../../integration/elasticsearch.md).
> - [Introduced][ee-109] in GitLab [Starter][ee] 8.4.
> - This is the user documentation. To install and configure Elasticsearch,
> visit the [admin docs](../../integration/elasticsearch.md).
Leverage Elasticsearch for faster, more advanced code search across your entire
GitLab instance.
......
# Advanced Syntax Search **[STARTER]**
>**Notes:**
- Introduced in [GitLab Enterprise Starter][ee] 9.2
- This is the user documentation. To install and configure Elasticsearch,
visit the [admin docs](../../integration/elasticsearch.md).
> **Notes:**
> - Introduced in [GitLab Enterprise Starter][ee] 9.2
> - This is the user documentation. To install and configure Elasticsearch,
> visit the [admin docs](../../integration/elasticsearch.md).
Use advanced queries for more targeted search results.
......
......@@ -86,9 +86,8 @@ one is located in `config.yml` of gitlab-shell.
## Using GitLab git-annex
>
**Note:**
Your Git remotes must be using the SSH protocol, not HTTP(S).
> **Note:**
> Your Git remotes must be using the SSH protocol, not HTTP(S).
Here is an example workflow of uploading a very large file and then checking it
into your Git repository:
......
......@@ -152,6 +152,7 @@ if the server also has Git Annex 6 installed. Read more in the
indirect ok
ok
```
---
At this point, you have two options. Either add, commit and push the files
......
import $ from 'jquery';
import projectNew from '~/projects/project_new';
const bindEvents = () => {
const $newProjectForm = $('#new_project');
......@@ -41,6 +42,12 @@ const bindEvents = () => {
$templateProjectNameInput.focus();
enableCustomTemplate();
const $activeTabProjectName = $('.tab-pane.active #project_name');
const $activeTabProjectPath = $('.tab-pane.active #project_path');
$activeTabProjectName.focus();
$activeTabProjectName
.keyup(() => projectNew.onProjectNameChange($activeTabProjectName, $activeTabProjectPath));
}
$useCustomTemplateBtn.on('change', chooseTemplate);
......
......@@ -6,11 +6,24 @@ module EE
override :render_ok
def render_ok
set_workhorse_internal_api_content_type
render json: ::Gitlab::Workhorse.git_http_ok(repository, wiki?, user, action_name, show_all_refs: geo_request?)
end
private
def user
super || geo_push_user&.user
end
def geo_push_user
@geo_push_user ||= ::Geo::PushUser.new_from_headers(request.headers)
end
def geo_push_user_headers_provided?
::Geo::PushUser.needed_headers_provided?(request.headers)
end
def geo_request?
::Gitlab::Geo::JwtRequestDecoder.geo_auth_attempt?(request.headers['Authorization'])
end
......@@ -21,9 +34,11 @@ module EE
override :access_actor
def access_actor
return :geo if geo?
return super unless geo?
return :geo unless geo_push_user_headers_provided?
return geo_push_user.user if geo_push_user.user
super
raise ::Gitlab::GitAccess::UnauthorizedError, 'Geo push user is invalid.'
end
override :authenticate_user
......@@ -32,7 +47,7 @@ module EE
payload = ::Gitlab::Geo::JwtRequestDecoder.new(request.headers['Authorization']).decode
if payload
@authentication_result = ::Gitlab::Auth::Result.new(nil, project, :geo, [:download_code]) # rubocop:disable Gitlab/ModuleWithInstanceVariables
@authentication_result = ::Gitlab::Auth::Result.new(nil, project, :geo, [:download_code, :push_code]) # rubocop:disable Gitlab/ModuleWithInstanceVariables
return # grant access
end
......
# frozen_string_literal: true
class Geo::PushUser
include ::Gitlab::Identifier
def initialize(gl_id)
@gl_id = gl_id
end
def self.needed_headers_provided?(headers)
headers['Geo-GL-Id'].present?
end
def self.new_from_headers(headers)
return nil unless needed_headers_provided?(headers)
new(headers['Geo-GL-Id'])
end
def user
@user ||= identify_using_ssh_key(gl_id)
end
private
attr_reader :gl_id
end
---
title: Allow push_code when auth'd via Geo JWT
merge_request: 6455
author:
type: changed
---
title: 'Geo: SSH git push to secondary -> proxy to Primary'
merge_request: 6456
author:
type: added
require 'base64'
module API
class Geo < Grape::API
resource :geo do
......@@ -40,6 +42,51 @@ module API
render_validation_error!(db_status)
end
end
# git push over SSH secondary -> primary related proxying logic
#
resource 'proxy_git_push_ssh' do
format :json
# Responsible for making HTTP GET /repo.git/info/refs?service=git-receive-pack
# request *from* secondary gitlab-shell to primary
#
params do
requires :secret_token, type: String
requires :data, type: Hash do
requires :gl_id, type: String
requires :primary_repo, type: String
end
end
post 'info_refs' do
authenticate_by_gitlab_shell_token!
params.delete(:secret_token)
resp = Gitlab::Geo::GitPushSSHProxy.new(params['data']).info_refs
status(resp.code.to_i)
{ status: true, message: nil, result: Base64.encode64(resp.body.to_s) }
end
# Responsible for making HTTP POST /repo.git/git-receive-pack
# request *from* secondary gitlab-shell to primary
#
params do
requires :secret_token, type: String
requires :data, type: Hash do
requires :gl_id, type: String
requires :primary_repo, type: String
end
requires :output, type: String, desc: 'Output from git-receive-pack'
end
post 'push' do
authenticate_by_gitlab_shell_token!
params.delete(:secret_token)
resp = Gitlab::Geo::GitPushSSHProxy.new(params['data']).push(Base64.decode64(params['output']))
status(resp.code.to_i)
{ status: true, message: nil, result: Base64.encode64(resp.body.to_s) }
end
end
end
end
end
......@@ -3,9 +3,19 @@ module EE
module GeoGitAccess
include ::Gitlab::ConfigHelper
include ::EE::GitlabRoutingHelper
include GrapePathHelpers::NamedRouteMatcher
extend ::Gitlab::Utils::Override
GEO_SERVER_DOCS_URL = 'https://docs.gitlab.com/ee/administration/geo/replication/using_a_geo_server.html'.freeze
override :check_custom_action
def check_custom_action(cmd)
custom_action = custom_action_for(cmd)
return custom_action if custom_action
super
end
protected
def project_or_wiki
......@@ -14,6 +24,27 @@ module EE
private
def custom_action_for?(cmd)
return unless receive_pack?(cmd)
return unless ::Gitlab::Database.read_only?
::Gitlab::Geo.secondary_with_primary?
end
def custom_action_for(cmd)
return unless custom_action_for?(cmd)
payload = {
'action' => 'geo_proxy_to_primary',
'data' => {
'api_endpoints' => [api_v4_geo_proxy_git_push_ssh_info_refs_path, api_v4_geo_proxy_git_push_ssh_push_path],
'primary_repo' => geo_primary_http_url_to_repo(project_or_wiki)
}
}
::Gitlab::GitAccessResult::CustomAction.new(payload, 'Attempting to proxy to primary.')
end
def push_to_read_only_message
message = super
......
......@@ -17,7 +17,7 @@ module EE
override :whitelisted_routes
def whitelisted_routes
super || geo_node_update_route
super || geo_node_update_route || geo_proxy_git_push_ssh_route
end
def geo_node_update_route
......@@ -33,6 +33,14 @@ module EE
WHITELISTED_GEO_ROUTES_TRACKING_DB[controller]&.include?(action)
end
end
def geo_proxy_git_push_ssh_route
routes = ::Gitlab::Middleware::ReadOnly::API_VERSIONS.map do |version|
["/api/v#{version}/geo/proxy_git_push_ssh/info_refs",
"/api/v#{version}/geo/proxy_git_push_ssh/push"]
end
routes.flatten.include?(request.path)
end
end
end
end
......
# frozen_string_literal: true
module Gitlab
module Geo
class GitPushSSHProxy
HTTP_READ_TIMEOUT = 10
HTTP_SUCCESS_CODE = '200'.freeze
MustBeASecondaryNode = Class.new(StandardError)
def initialize(data)
@data = data
end
def info_refs
ensure_secondary!
url = "#{primary_repo}/info/refs?service=git-receive-pack"
headers = {
'Content-Type' => 'application/x-git-upload-pack-request'
}
resp = get(url, headers)
return resp unless resp.code == HTTP_SUCCESS_CODE
resp.body = remove_http_service_fragment_from(resp.body)
resp
end
def push(info_refs_response)
ensure_secondary!
url = "#{primary_repo}/git-receive-pack"
headers = {
'Content-Type' => 'application/x-git-receive-pack-request',
'Accept' => 'application/x-git-receive-pack-result'
}
post(url, info_refs_response, headers)
end
private
attr_reader :data
def primary_repo
@primary_repo ||= data['primary_repo']
end
def gl_id
@gl_id ||= data['gl_id']
end
def base_headers
@base_headers ||= {
'Geo-GL-Id' => gl_id,
'Authorization' => Gitlab::Geo::BaseRequest.new.authorization
}
end
def get(url, headers)
request(url, Net::HTTP::Get, headers)
end
def post(url, body, headers)
request(url, Net::HTTP::Post, headers, body: body)
end
def request(url, klass, headers, body: nil)
headers = base_headers.merge(headers)
uri = URI.parse(url)
req = klass.new(uri, headers)
req.body = body if body
http = Net::HTTP.new(uri.hostname, uri.port)
http.read_timeout = HTTP_READ_TIMEOUT
http.use_ssl = true if uri.is_a?(URI::HTTPS)
http.start { http.request(req) }
end
def remove_http_service_fragment_from(body)
# HTTP(S) and SSH responses are very similar, except for the fragment below.
# As we're performing a git HTTP(S) request here, we'll get a HTTP(s)
# suitable git response. However, we're executing in the context of an
# SSH session so we need to make the response suitable for what git over
# SSH expects.
#
# See Downloading Data > HTTP(S) section at:
# https://git-scm.com/book/en/v2/Git-Internals-Transfer-Protocols
body.gsub(/\A001f# service=git-receive-pack\n0000/, '')
end
def ensure_secondary!
raise MustBeASecondaryNode, 'Node is not a secondary' unless Gitlab::Geo.secondary_with_primary?
end
end
end
end
......@@ -48,6 +48,7 @@ describe 'Project' do
page.within '.project-fields-form' do
fill_in("project_path", with: new_name)
fill_in("project_name", with: new_name)
Sidekiq::Testing.inline! do
click_button "Create project"
......
......@@ -90,6 +90,7 @@ describe 'New project' do
first('.js-import-git-toggle-button').click
fill_in 'project_import_url', with: 'http://foo.git'
fill_in 'project_name', with: 'import-project-with-features1'
fill_in 'project_path', with: 'import-project-with-features1'
choose 'project_visibility_level_20'
click_button 'Create project'
......@@ -114,6 +115,7 @@ describe 'New project' do
find('.js-import-git-toggle-button').click
fill_in 'project_import_url', with: 'http://foo.git'
fill_in 'project_name', with: 'CI CD Project1'
fill_in 'project_path', with: 'ci-cd-project1'
choose 'project_visibility_level_20'
click_button 'Create project'
......
......@@ -18,6 +18,7 @@ describe 'User creates a project', :js do
it 'creates a new project' do
visit(new_project_path)
fill_in :project_name, with: 'a-new-project'
fill_in :project_path, with: 'a-new-project'
page.find('.js-select-namespace').click
......
# frozen_string_literal: true
require 'spec_helper'
describe Gitlab::Geo::GitPushSSHProxy, :geo do
include ::EE::GeoHelpers
set(:primary_node) { create(:geo_node, :primary) }
set(:secondary_node) { create(:geo_node) }
let(:current_node) { nil }
let(:project) { create(:project, :repository) }
let(:user) { project.creator }
let(:key) { create(:key, user: user) }
let(:base_request) { double(Gitlab::Geo::BaseRequest.new.authorization) }
let(:info_refs_body_short) do
"008f43ba78b7912f7bf7ef1d7c3b8a0e5ae14a759dfa refs/heads/masterreport-status delete-refs side-band-64k quiet atomic ofs-delta agent=git/2.18.0
0000"
end
let(:base_headers) do
{
'Geo-GL-Id' => "key-#{key.id}",
'Authorization' => 'secret'
}
end
let(:data) do
{
'gl_id' => "key-#{key.id}",
'primary_repo' => "#{primary_node.url}#{project.repository.full_path}.git"
}
end
subject { described_class.new(data) }
before do
stub_current_geo_node(current_node)
allow(Gitlab::Geo::BaseRequest).to receive(:new).and_return(base_request)
allow(base_request).to receive(:authorization).and_return('secret')
end
describe '#info_refs' do
context 'against primary node' do
let(:current_node) { primary_node }
it 'raises an exception' do
expect do
subject.info_refs
end.to raise_error(described_class::MustBeASecondaryNode)
end
end
context 'against secondary node' do
let(:current_node) { secondary_node }
let(:full_info_refs_url) { "#{primary_node.url}#{project.full_path}.git/info/refs?service=git-receive-pack" }
let(:info_refs_headers) { base_headers.merge('Content-Type' => 'application/x-git-upload-pack-request') }
let(:info_refs_http_body_full) do
"001f# service=git-receive-pack
0000#{info_refs_body_short}"
end
before do
stub_request(:get, full_info_refs_url).to_return(status: 200, body: info_refs_http_body_full, headers: info_refs_headers)
end
it 'returns a Net::HTTPOK' do
expect(subject.info_refs).to be_a(Net::HTTPOK)
end
it 'returns a modified body' do
expect(subject.info_refs.body).to eql(info_refs_body_short)
end
end
end
describe '#push' do
context 'against primary node' do
let(:current_node) { primary_node }
it 'raises an exception' do
expect do
subject.push(info_refs_body_short)
end.to raise_error(described_class::MustBeASecondaryNode)
end
end
context 'against secondary node' do
let(:current_node) { secondary_node }
let(:full_git_receive_pack_url) { "#{primary_node.url}#{project.full_path}.git/git-receive-pack" }
let(:push_headers) do
base_headers.merge(
'Content-Type' => 'application/x-git-receive-pack-request',
'Accept' => 'application/x-git-receive-pack-result'
)
end
before do
stub_request(:post, full_git_receive_pack_url).to_return(status: 201, body: info_refs_body_short, headers: push_headers)
end
it 'returns a Net::HTTPCreated' do
expect(subject.push(info_refs_body_short)).to be_a(Net::HTTPCreated)
end
end
end
end
......@@ -18,25 +18,9 @@ describe Gitlab::GitAccess do
allow(Gitlab::Database).to receive(:read_only?) { true }
end
it 'denies push access' do
project.add_maintainer(user)
let(:primary_repo_url) { "https://localhost:3000/gitlab/#{project.full_path}.git" }
expect { push_changes }.to raise_unauthorized("You can't push code to a read-only GitLab instance.")
end
it 'denies push access with primary present' do
error_message = "You can't push code to a read-only GitLab instance."\
"\nPlease use the primary node URL instead: https://localhost:3000/gitlab/#{project.full_path}.git.
For more information: #{EE::Gitlab::GeoGitAccess::GEO_SERVER_DOCS_URL}"
primary_node = create(:geo_node, :primary, url: 'https://localhost:3000/gitlab')
allow(Gitlab::Geo).to receive(:primary).and_return(primary_node)
allow(Gitlab::Geo).to receive(:secondary_with_primary?).and_return(true)
project.add_maintainer(user)
expect { push_changes }.to raise_unauthorized(error_message)
end
it_behaves_like 'a read-only GitLab instance'
end
describe "push_rule_check" do
......@@ -264,12 +248,23 @@ For more information: #{EE::Gitlab::GeoGitAccess::GEO_SERVER_DOCS_URL}"
end
end
describe 'Geo system permissions' do
let(:actor) { :geo }
it { expect { pull_changes }.not_to raise_error }
it { expect { push_changes }.to raise_unauthorized(Gitlab::GitAccess::ERROR_MESSAGES[:push_code]) }
end
private
def push_changes(changes = '_any')
access.check('git-receive-pack', changes)
end
def pull_changes(changes = '_any')
access.check('git-upload-pack', changes)
end
def raise_unauthorized(message)
raise_error(Gitlab::GitAccess::UnauthorizedError, message)
end
......
require 'spec_helper'
describe Gitlab::GitAccessWiki do
let(:access) { described_class.new(user, project, 'web', authentication_abilities: authentication_abilities, redirected_path: redirected_path) }
let(:project) { create(:project, :repository) }
let(:user) { create(:user) }
let(:project) { create(:project, :repository) }
let(:changes) { ['6f6d7e7ed 570e7b2ab refs/heads/master'] }
let(:authentication_abilities) { %i[read_project download_code push_code] }
let(:redirected_path) { nil }
let(:authentication_abilities) do
[
:read_project,
:download_code,
:push_code
]
end
let(:access) { described_class.new(user, project, 'web', authentication_abilities: authentication_abilities, redirected_path: redirected_path) }
context "when in a read-only GitLab instance" do
subject { access.check('git-receive-pack', changes) }
......@@ -22,26 +17,9 @@ describe Gitlab::GitAccessWiki do
allow(Gitlab::Database).to receive(:read_only?) { true }
end
it 'denies push access' do
project.add_maintainer(user)
expect { subject }.to raise_unauthorized("You can't push code to a read-only GitLab instance.")
end
it 'denies push access with primary present' do
error_message = "You can't push code to a read-only GitLab instance.
Please use the primary node URL instead: "\
"https://localhost:3000/gitlab/#{project.full_path}.wiki.git.
For more information: #{EE::Gitlab::GeoGitAccess::GEO_SERVER_DOCS_URL}"
primary_node = create(:geo_node, :primary, url: 'https://localhost:3000/gitlab')
allow(Gitlab::Geo).to receive(:primary).and_return(primary_node)
allow(Gitlab::Geo).to receive(:secondary_with_primary?).and_return(true)
project.add_maintainer(user)
let(:primary_repo_url) { "https://localhost:3000/gitlab/#{project.full_path}.wiki.git" }
expect { subject }.to raise_unauthorized(error_message)
end
it_behaves_like 'a read-only GitLab instance'
end
context 'when wiki is disabled' do
......@@ -58,6 +36,10 @@ For more information: #{EE::Gitlab::GeoGitAccess::GEO_SERVER_DOCS_URL}"
private
def push_changes(changes = '_any')
access.check('git-receive-pack', changes)
end
def raise_unauthorized(message)
raise_error(Gitlab::GitAccess::UnauthorizedError, message)
end
......
# frozen_string_literal: true
require 'spec_helper'
describe Geo::PushUser do
let!(:user) { create(:user) }
let!(:key) { create(:key, user: user) }
let(:gl_id) { "key-#{key.id}" }
subject { described_class.new(gl_id) }
describe '.needed_headers_provided?' do
where(:headers) do
[
{},
{ 'Geo-GL-Id' => nil },
{ 'Geo-GL-Id' => '' }
]
end
with_them do
it 'returns false' do
expect(described_class.needed_headers_provided?(headers)).to be(false)
end
end
context 'where gl_id is not nil' do
let(:headers) do
{ 'Geo-GL-Id' => gl_id }
end
it 'returns true' do
expect(described_class.needed_headers_provided?(headers)).to be(true)
end
end
end
describe '.new_from_headers' do
where(:headers) do
[
{},
{ 'Geo-GL-Id' => nil },
{ 'Geo-GL-Id' => '' }
]
end
with_them do
it 'returns false' do
expect(described_class.new_from_headers(headers)).to be_nil
end
end
context 'where gl_id is not nil' do
let(:headers) do
{ 'Geo-GL-Id' => gl_id }
end
it 'returns an instance of Geo::PushUser' do
expect(described_class.new_from_headers(headers)).to be_a(described_class)
end
end
end
describe '#user' do
context 'with a junk gl_id' do
let(:gl_id) { "test" }
it 'returns nil' do
expect(subject.user).to be_nil
end
end
context 'with an unsupported gl_id type' do
let(:gl_id) { "user-#{user.id}" }
it 'returns nil' do
expect(subject.user).to be_nil
end
end
context 'when the User associated to gl_id matches the User associated to gl_username' do
it 'returns a User' do
expect(subject.user).to be_a(User)
end
end
end
end
......@@ -25,7 +25,7 @@ describe API::Geo do
end
end
describe '/geo/transfers' do
describe 'GET /geo/transfers' do
before do
stub_current_geo_node(secondary_node)
end
......@@ -287,4 +287,120 @@ describe API::Geo do
it_behaves_like 'with terms enforced'
end
end
describe '/geo/proxy_git_push_ssh' do
let(:secret_token) { Gitlab::Shell.secret_token }
let(:data) { { primary_repo: 'http://localhost:3001/testuser/repo.git', gl_id: 'key-1', gl_username: 'testuser' } }
before do
stub_current_geo_node(secondary_node)
end
describe 'POST /geo/proxy_git_push_ssh/info_refs' do
context 'with all required params missing' do
it 'responds with 400' do
post api('/geo/proxy_git_push_ssh/info_refs'), nil
expect(response).to have_gitlab_http_status(400)
expect(json_response['error']).to eql('secret_token is missing, data is missing, data[gl_id] is missing, data[primary_repo] is missing')
end
end
context 'with all required params' do
let(:git_push_ssh_proxy) { double(Gitlab::Geo::GitPushSSHProxy) }
before do
allow(Gitlab::Geo::GitPushSSHProxy).to receive(:new).with(data).and_return(git_push_ssh_proxy)
end
context 'with an invalid secret_token' do
it 'responds with 401' do
post(api('/geo/proxy_git_push_ssh/info_refs'), { secret_token: 'invalid', data: data })
expect(response).to have_gitlab_http_status(401)
expect(json_response['error']).to be_nil
end
end
context 'where an exception occurs' do
it 'responds with 500' do
expect(git_push_ssh_proxy).to receive(:info_refs).and_raise('deliberate exception raised')
post api('/geo/proxy_git_push_ssh/info_refs'), { secret_token: secret_token, data: data }
expect(response).to have_gitlab_http_status(500)
expect(json_response['message']).to include('RuntimeError (deliberate exception raised)')
expect(json_response['result']).to be_nil
end
end
context 'with a valid secret token' do
let(:http_response) { double(Net::HTTPResponse, code: 200, body: 'something here') }
it 'responds with 200' do
expect(git_push_ssh_proxy).to receive(:info_refs).and_return(http_response)
post api('/geo/proxy_git_push_ssh/info_refs'), { secret_token: secret_token, data: data }
expect(response).to have_gitlab_http_status(200)
expect(Base64.decode64(json_response['result'])).to eql('something here')
end
end
end
end
describe 'POST /geo/proxy_git_push_ssh/push' do
context 'with all required params missing' do
it 'responds with 400' do
post api('/geo/proxy_git_push_ssh/push'), nil
expect(response).to have_gitlab_http_status(400)
expect(json_response['error']).to eql('secret_token is missing, data is missing, data[gl_id] is missing, data[primary_repo] is missing, output is missing')
end
end
context 'with all required params' do
let(:text) { 'output text' }
let(:output) { Base64.encode64(text) }
let(:git_push_ssh_proxy) { double(Gitlab::Geo::GitPushSSHProxy) }
before do
allow(Gitlab::Geo::GitPushSSHProxy).to receive(:new).with(data).and_return(git_push_ssh_proxy)
end
context 'with an invalid secret_token' do
it 'responds with 401' do
post(api('/geo/proxy_git_push_ssh/push'), { secret_token: 'invalid', data: data, output: output })
expect(response).to have_gitlab_http_status(401)
expect(json_response['error']).to be_nil
end
end
context 'where an exception occurs' do
it 'responds with 500' do
expect(git_push_ssh_proxy).to receive(:push).and_raise('deliberate exception raised')
post api('/geo/proxy_git_push_ssh/push'), { secret_token: secret_token, data: data, output: output }
expect(response).to have_gitlab_http_status(500)
expect(json_response['message']).to include('RuntimeError (deliberate exception raised)')
expect(json_response['result']).to be_nil
end
end
context 'with a valid secret token' do
let(:http_response) { double(Net::HTTPResponse, code: 201, body: 'something here') }
it 'responds with 201' do
expect(git_push_ssh_proxy).to receive(:push).with(text).and_return(http_response)
post api('/geo/proxy_git_push_ssh/push'), { secret_token: secret_token, data: data, output: output }
expect(response).to have_gitlab_http_status(201)
expect(Base64.decode64(json_response['result'])).to eql('something here')
end
end
end
end
end
end
......@@ -13,15 +13,24 @@ describe "Git HTTP requests (Geo)" do
# Ensure the token always comes from the real time of the request
let!(:auth_token) { Gitlab::Geo::BaseRequest.new.authorization }
let!(:user) { create(:user) }
let!(:user_without_any_access) { create(:user) }
let!(:user_without_push_access) { create(:user) }
let!(:key) { create(:key, user: user) }
let!(:key_for_user_without_any_access) { create(:key, user: user_without_any_access) }
let!(:key_for_user_without_push_access) { create(:key, user: user_without_push_access) }
let(:env) { valid_geo_env }
before do
project.add_maintainer(user)
project.add_guest(user_without_push_access)
stub_licensed_features(geo: true)
stub_current_geo_node(secondary)
stub_current_geo_node(current_node)
end
shared_examples_for 'Geo sync request' do
shared_examples_for 'Geo request' do
subject do
make_request
response
......@@ -64,180 +73,275 @@ describe "Git HTTP requests (Geo)" do
end
end
describe 'GET info_refs' do
context 'git pull' do
def make_request
get "/#{project.full_path}.git/info/refs", { service: 'git-upload-pack' }, env
context 'when current node is a secondary' do
let(:current_node) { secondary }
set(:project) { create(:project, :repository, :private) }
describe 'GET info_refs' do
context 'git pull' do
def make_request
get "/#{project.full_path}.git/info/refs", { service: 'git-upload-pack' }, env
end
it_behaves_like 'Geo request'
context 'when terms are enforced' do
before do
enforce_terms
end
it_behaves_like 'Geo request'
end
end
it_behaves_like 'Geo sync request'
context 'git push' do
def make_request
get url, { service: 'git-receive-pack' }, env
end
context 'when terms are enforced' do
before do
enforce_terms
let(:url) { "/#{project.full_path}.git/info/refs" }
subject do
make_request
response
end
it_behaves_like 'Geo sync request'
it 'redirects to the primary' do
is_expected.to have_gitlab_http_status(:redirect)
redirect_location = "#{primary.url.chomp('/')}#{url}?service=git-receive-pack"
expect(subject.header['Location']).to eq(redirect_location)
end
end
end
context 'git push' do
describe 'POST git_upload_pack' do
def make_request
get url, { service: 'git-receive-pack' }, env
post "/#{project.full_path}.git/git-upload-pack", {}, env
end
let(:url) { "/#{project.full_path}.git/info/refs" }
it_behaves_like 'Geo request'
subject do
make_request
response
end
context 'when terms are enforced' do
before do
enforce_terms
end
it 'redirects to the primary' do
is_expected.to have_gitlab_http_status(:redirect)
redirect_location = "#{primary.url.chomp('/')}#{url}?service=git-receive-pack"
expect(subject.header['Location']).to eq(redirect_location)
it_behaves_like 'Geo request'
end
end
end
describe 'POST upload_pack' do
def make_request
post "/#{project.full_path}.git/git-upload-pack", {}, env
end
context 'git-lfs' do
context 'API' do
describe 'POST batch' do
def make_request
post url, args, env
end
it_behaves_like 'Geo sync request'
let(:args) { {} }
let(:url) { "/#{project.full_path}.git/info/lfs/objects/batch" }
context 'when terms are enforced' do
before do
enforce_terms
end
subject do
make_request
response
end
it_behaves_like 'Geo sync request'
end
end
before do
allow(Gitlab.config.lfs).to receive(:enabled).and_return(true)
project.update_attribute(:lfs_enabled, true)
env['Content-Type'] = LfsRequest::CONTENT_TYPE
end
context 'git-lfs' do
context 'API' do
describe 'POST batch' do
def make_request
post url, args, env
end
context 'operation upload' do
let(:args) { { 'operation' => 'upload' }.to_json }
let(:args) { {} }
let(:url) { "/#{project.full_path}.git/info/lfs/objects/batch" }
context 'with the correct git-lfs version' do
before do
env['User-Agent'] = 'git-lfs/2.4.2 (GitHub; darwin amd64; go 1.10.2)'
end
subject do
make_request
response
end
it 'redirects to the primary' do
is_expected.to have_gitlab_http_status(:redirect)
redirect_location = "#{primary.url.chomp('/')}#{url}"
expect(subject.header['Location']).to eq(redirect_location)
end
end
before do
allow(Gitlab.config.lfs).to receive(:enabled).and_return(true)
project.update_attribute(:lfs_enabled, true)
env['Content-Type'] = LfsRequest::CONTENT_TYPE
end
context 'with an incorrect git-lfs version' do
where(:description, :version) do
'outdated' | 'git-lfs/2.4.1'
'unknown' | 'git-lfs'
end
context 'operation upload' do
let(:args) { { 'operation' => 'upload' }.to_json }
with_them do
context "that is #{description}" do
before do
env['User-Agent'] = "#{version} (GitHub; darwin amd64; go 1.10.2)"
end
it 'is forbidden' do
is_expected.to have_gitlab_http_status(:forbidden)
expect(json_response['message']).to match(/You need git-lfs version 2.4.2/)
end
end
end
end
end
context 'operation download' do
let(:user) { create(:user) }
let(:authorization) { ActionController::HttpAuthentication::Basic.encode_credentials(user.username, user.password) }
let(:lfs_object) { create(:lfs_object, :with_file) }
let(:args) do
{
'operation' => 'download',
'objects' => [{ 'oid' => lfs_object.oid, 'size' => lfs_object.size }]
}.to_json
end
context 'with the correct git-lfs version' do
before do
env['User-Agent'] = 'git-lfs/2.4.2 (GitHub; darwin amd64; go 1.10.2)'
project.add_maintainer(user)
env['Authorization'] = authorization
end
it 'redirects to the primary' do
is_expected.to have_gitlab_http_status(:redirect)
redirect_location = "#{primary.url.chomp('/')}#{url}"
expect(subject.header['Location']).to eq(redirect_location)
it 'is handled by the secondary' do
is_expected.to have_gitlab_http_status(:ok)
end
end
context 'with an incorrect git-lfs version' do
where(:description, :version) do
'outdated' | 'git-lfs/2.4.1'
'unknown' | 'git-lfs'
end
with_them do
context "that is #{description}" do
context "with an #{description} git-lfs version" do
before do
env['User-Agent'] = "#{version} (GitHub; darwin amd64; go 1.10.2)"
end
it 'is forbidden' do
is_expected.to have_gitlab_http_status(:forbidden)
expect(json_response['message']).to match(/You need git-lfs version 2.4.2/)
it 'is handled by the secondary' do
is_expected.to have_gitlab_http_status(:ok)
end
end
end
end
end
end
context 'operation download' do
let(:user) { create(:user) }
let(:authorization) { ActionController::HttpAuthentication::Basic.encode_credentials(user.username, user.password) }
let(:lfs_object) { create(:lfs_object, :with_file) }
let(:args) do
{
'operation' => 'download',
'objects' => [{ 'oid' => lfs_object.oid, 'size' => lfs_object.size }]
}.to_json
end
context 'Locks API' do
where(:description, :path, :args) do
'create' | 'info/lfs/locks' | {}
'verify' | 'info/lfs/locks/verify' | {}
'unlock' | 'info/lfs/locks/1/unlock' | { id: 1 }
end
before do
project.add_maintainer(user)
env['Authorization'] = authorization
with_them do
describe "POST #{description}" do
def make_request
post url, args, env
end
let(:url) { "/#{project.full_path}.git/#{path}" }
subject do
make_request
response
end
it 'redirects to the primary' do
is_expected.to have_gitlab_http_status(:redirect)
redirect_location = "#{primary.url.chomp('/')}#{url}"
expect(subject.header['Location']).to eq(redirect_location)
end
end
end
end
end
end
context 'when current node is the primary' do
let(:current_node) { primary }
describe 'POST git_receive_pack' do
def make_request
post url, {}, env
end
let(:url) { "/#{project.full_path}.git/git-receive-pack" }
before do
env['Geo-GL-Id'] = geo_gl_id
end
it 'is handled by the secondary' do
is_expected.to have_gitlab_http_status(:ok)
subject do
make_request
response
end
context 'when gl_id is incorrectly provided via HTTP headers' do
where(:geo_gl_id) do
[
nil,
''
]
end
with_them do
it 'returns a 403' do
is_expected.to have_gitlab_http_status(:forbidden)
expect(response.body).to eql('You are not allowed to upload code for this project.')
end
end
end
where(:description, :version) do
'outdated' | 'git-lfs/2.4.1'
'unknown' | 'git-lfs'
context 'when gl_id is provided via HTTP headers' do
context 'but is invalid' do
where(:geo_gl_id) do
[
'key-999',
'key-1',
'key-999',
'junk',
'junk-1',
'kkey-1'
]
end
with_them do
context "with an #{description} git-lfs version" do
before do
env['User-Agent'] = "#{version} (GitHub; darwin amd64; go 1.10.2)"
end
it 'is handled by the secondary' do
is_expected.to have_gitlab_http_status(:ok)
end
it 'returns a 403' do
is_expected.to have_gitlab_http_status(:forbidden)
expect(response.body).to eql('Geo push user is invalid.')
end
end
end
end
end
context 'Locks API' do
where(:description, :path, :args) do
'create' | 'info/lfs/locks' | {}
'verify' | 'info/lfs/locks/verify' | {}
'unlock' | 'info/lfs/locks/1/unlock' | { id: 1 }
end
context 'and is valid' do
context 'but the user has no access' do
let(:geo_gl_id) { "key-#{key_for_user_without_any_access.id}" }
with_them do
describe "POST #{description}" do
def make_request
post url, args, env
it 'returns a 404' do
is_expected.to have_gitlab_http_status(:not_found)
expect(response.body).to eql('The project you were looking for could not be found.')
end
end
let(:url) { "/#{project.full_path}.git/#{path}" }
context 'but the user does not have push access' do
let(:geo_gl_id) { "key-#{key_for_user_without_push_access.id}" }
subject do
make_request
response
it 'returns a 403' do
is_expected.to have_gitlab_http_status(:forbidden)
expect(response.body).to eql('You are not allowed to push code to this project.')
end
end
it 'redirects to the primary' do
is_expected.to have_gitlab_http_status(:redirect)
redirect_location = "#{primary.url.chomp('/')}#{url}"
expect(subject.header['Location']).to eq(redirect_location)
context 'and the user has push access' do
let(:geo_gl_id) { "key-#{key.id}" }
it 'returns a 200' do
is_expected.to have_gitlab_http_status(:ok)
expect(json_response['GL_ID']).to match("user-#{user.id}")
expect(json_response['GL_REPOSITORY']).to match(Gitlab::GlRepository.gl_repository(project, false))
end
end
end
end
......
# frozen_string_literal: true
shared_examples 'a read-only GitLab instance' do
it 'denies push access' do
project.add_maintainer(user)
expect { push_changes }.to raise_unauthorized("You can't push code to a read-only GitLab instance.")
end
context 'for a Geo setup' do
before do
primary_node = create(:geo_node, :primary, url: 'https://localhost:3000/gitlab')
allow(Gitlab::Geo).to receive(:primary).and_return(primary_node)
allow(Gitlab::Geo).to receive(:secondary_with_primary?).and_return(secondary_with_primary)
end
context 'that is incorrectly setup' do
let(:secondary_with_primary) { false }
let(:error_message) { "You can't push code to a read-only GitLab instance." }
it 'denies push access with primary present' do
project.add_maintainer(user)
expect { push_changes }.to raise_unauthorized(error_message)
end
end
context 'that is correctly setup' do
let(:secondary_with_primary) { true }
let(:payload) do
{
'action' => 'geo_proxy_to_primary',
'data' => {
'api_endpoints' => %w{/api/v4/geo/proxy_git_push_ssh/info_refs /api/v4/geo/proxy_git_push_ssh/push},
'primary_repo' => primary_repo_url
}
}
end
it 'attempts to proxy to the primary' do
project.add_maintainer(user)
expect(push_changes).to be_a(Gitlab::GitAccessResult::CustomAction)
expect(push_changes.message).to eql('Attempting to proxy to primary.')
expect(push_changes.payload).to eql(payload)
end
end
end
end
......@@ -74,6 +74,7 @@ module API
gl_repository: gl_repository,
gl_id: Gitlab::GlId.gl_id(user),
gl_username: user&.username,
git_config_options: [],
# This repository_path is a bogus value but gitlab-shell still requires
# its presence. https://gitlab.com/gitlab-org/gitlab-shell/issues/135
......@@ -81,6 +82,13 @@ module API
gitaly: gitaly_payload(params[:action])
}
# Custom option for git-receive-pack command
receive_max_input_size = Gitlab::CurrentSettings.receive_max_input_size.to_i
if receive_max_input_size > 0
payload[:git_config_options] << "receive.maxInputSize=#{receive_max_input_size.megabytes}"
end
response_with_status(**payload)
when ::Gitlab::GitAccessResult::CustomAction
response_with_status(code: 300, message: check_result.message, payload: check_result.payload)
......
......@@ -50,6 +50,7 @@ module Gitlab
def push_checks
unless can_push?
# You are not allowed to push code to this project.
raise GitAccess::UnauthorizedError, ERROR_MESSAGES[:push_code]
end
end
......
......@@ -236,11 +236,13 @@ module Gitlab
# TODO: please clean this up
def check_push_access!
if project.repository_read_only?
# The repository is temporarily read-only. Please try again later.
raise UnauthorizedError, ERROR_MESSAGES[:read_only]
end
if deploy_key?
unless deploy_key.can_push_to?(project)
# This deploy key does not have write access to this project.
raise UnauthorizedError, ERROR_MESSAGES[:deploy_key_upload]
end
elsif user
......@@ -248,6 +250,7 @@ module Gitlab
elsif authed_via_jwt?
# Authenticated via JWT
else
# You are not allowed to upload code for this project.
raise UnauthorizedError, ERROR_MESSAGES[:upload]
end
......
......@@ -22,18 +22,27 @@ module Gitlab
project = repository.project
{
attrs = {
GL_ID: Gitlab::GlId.gl_id(user),
GL_REPOSITORY: Gitlab::GlRepository.gl_repository(project, is_wiki),
GL_USERNAME: user&.username,
ShowAllRefs: show_all_refs,
Repository: repository.gitaly_repository.to_h,
RepoPath: 'ignored but not allowed to be empty in gitlab-workhorse',
GitConfigOptions: [],
GitalyServer: {
address: Gitlab::GitalyClient.address(project.repository_storage),
token: Gitlab::GitalyClient.token(project.repository_storage)
}
}
# Custom option for git-receive-pack command
receive_max_input_size = Gitlab::CurrentSettings.receive_max_input_size.to_i
if receive_max_input_size > 0
attrs[:GitConfigOptions] << "receive.maxInputSize=#{receive_max_input_size.megabytes}"
end
attrs
end
def send_git_blob(repository, blob)
......
......@@ -5819,6 +5819,9 @@ msgstr ""
msgid "Project Badges"
msgstr ""
msgid "Project URL"
msgstr ""
msgid "Project access must be granted explicitly to each user."
msgstr ""
......@@ -5846,6 +5849,9 @@ msgstr ""
msgid "Project name"
msgstr ""
msgid "Project slug"
msgstr ""
msgid "ProjectActivityRSS|Subscribe"
msgstr ""
......
......@@ -140,5 +140,12 @@ describe Admin::ApplicationSettingsController do
expect(body).to include('counts')
expect(response.status).to eq(200)
end
it 'updates the receive_max_input_size setting' do
put :update, application_setting: { receive_max_input_size: "1024" }
expect(response).to redirect_to(admin_application_settings_path)
expect(ApplicationSetting.current.receive_max_input_size).to eq(1024)
end
end
end
......@@ -20,7 +20,7 @@ describe 'Top Plus Menu', :js do
click_topmenuitem("New project")
expect(page).to have_content('Project path')
expect(page).to have_content('Project URL')
expect(page).to have_content('Project name')
end
......@@ -92,7 +92,7 @@ describe 'Top Plus Menu', :js do
find('.header-new-group-project a').click
end
expect(page).to have_content('Project path')
expect(page).to have_content('Project URL')
expect(page).to have_content('Project name')
end
end
......
......@@ -3,6 +3,8 @@ require 'rails_helper'
describe 'Cohorts page' do
before do
sign_in(create(:admin))
stub_application_setting(usage_ping_enabled: true)
end
it 'See users count per month' do
......
......@@ -20,6 +20,7 @@ describe 'Import/Export - project import integration test', :js do
context 'when selecting the namespace' do
let(:user) { create(:admin) }
let!(:namespace) { user.namespace }
let(:project_name) { 'Test Project Name' + SecureRandom.hex }
let(:project_path) { 'test-project-path' + SecureRandom.hex }
context 'prefilled the path' do
......@@ -27,12 +28,13 @@ describe 'Import/Export - project import integration test', :js do
visit new_project_path
select2(namespace.id, from: '#project_namespace_id')
fill_in :project_name, with: project_name, visible: true
fill_in :project_path, with: project_path, visible: true
click_import_project_tab
click_link 'GitLab export'
expect(page).to have_content('Import an exported GitLab project')
expect(URI.parse(current_url).query).to eq("namespace_id=#{namespace.id}&path=#{project_path}")
expect(URI.parse(current_url).query).to eq("namespace_id=#{namespace.id}&name=#{ERB::Util.url_encode(project_name)}&path=#{project_path}")
attach_file('file', file)
click_on 'Import project'
......@@ -56,6 +58,7 @@ describe 'Import/Export - project import integration test', :js do
click_import_project_tab
click_link 'GitLab export'
fill_in :name, with: 'Test Project Name', visible: true
fill_in :path, with: 'test-project-path', visible: true
attach_file('file', file)
......@@ -74,7 +77,8 @@ describe 'Import/Export - project import integration test', :js do
visit new_project_path
select2(user.namespace.id, from: '#project_namespace_id')
fill_in :project_path, with: project.name, visible: true
fill_in :project_name, with: project.name, visible: true
fill_in :project_path, with: project.path, visible: true
click_import_project_tab
click_link 'GitLab export'
attach_file('file', file)
......
......@@ -12,8 +12,9 @@ describe 'New project' do
it 'shows "New project" page', :js do
visit new_project_path
expect(page).to have_content('Project path')
expect(page).to have_content('Project name')
expect(page).to have_content('Project URL')
expect(page).to have_content('Project slug')
find('#import-project-tab').click
......@@ -187,7 +188,7 @@ describe 'New project' do
collision_project = create(:project, name: 'test-name-collision', namespace: user.namespace)
fill_in 'project_import_url', with: collision_project.http_url_to_repo
fill_in 'project_path', with: collision_project.path
fill_in 'project_name', with: collision_project.name
click_on 'Create project'
......
......@@ -11,7 +11,7 @@ describe 'User creates a project', :js do
it 'creates a new project' do
visit(new_project_path)
fill_in(:project_path, with: 'Empty')
fill_in(:project_name, with: 'Empty')
page.within('#content-body') do
click_button('Create project')
......@@ -37,6 +37,7 @@ describe 'User creates a project', :js do
it 'creates a new project' do
visit(new_project_path)
fill_in :project_name, with: 'A Subgroup Project'
fill_in :project_path, with: 'a-subgroup-project'
page.find('.js-select-namespace').click
......@@ -46,7 +47,7 @@ describe 'User creates a project', :js do
click_button('Create project')
end
expect(page).to have_content("Project 'a-subgroup-project' was successfully created")
expect(page).to have_content("Project 'A Subgroup Project' was successfully created")
project = Project.last
......
......@@ -16,7 +16,7 @@ describe 'Project' do
it "allows creation from templates", :js do
find('#create-from-template-tab').click
find("label[for=#{template.name}]").click
fill_in("project_path", with: template.name)
fill_in("project_name", with: template.name)
page.within '#content-body' do
click_button "Create project"
......
......@@ -61,6 +61,12 @@ describe('text_utility', () => {
});
});
describe('slugifyWithHyphens', () => {
it('should replaces whitespaces with hyphens and convert to lower case', () => {
expect(textUtils.slugifyWithHyphens('My Input String')).toEqual('my-input-string');
});
});
describe('stripHtml', () => {
it('replaces html tag with the default replacement', () => {
expect(textUtils.stripHtml('This is a text with <p>html</p>.')).toEqual(
......
......@@ -4,12 +4,14 @@ import projectNew from '~/projects/project_new';
describe('New Project', () => {
let $projectImportUrl;
let $projectPath;
let $projectName;
beforeEach(() => {
setFixtures(`
<div class='toggle-import-form'>
<div class='import-url-data'>
<input id="project_import_url" />
<input id="project_name" />
<input id="project_path" />
</div>
</div>
......@@ -17,6 +19,7 @@ describe('New Project', () => {
$projectImportUrl = $('#project_import_url');
$projectPath = $('#project_path');
$projectName = $('#project_name');
});
describe('deriveProjectPathFromUrl', () => {
......@@ -129,4 +132,31 @@ describe('New Project', () => {
});
});
});
describe('deriveSlugFromProjectName', () => {
beforeEach(() => {
projectNew.bindEvents();
$projectName.val('').keyup();
});
it('converts project name to lower case and dash-limited slug', () => {
const dummyProjectName = 'My Awesome Project';
$projectName.val(dummyProjectName);
projectNew.onProjectNameChange($projectName, $projectPath);
expect($projectPath.val()).toEqual('my-awesome-project');
});
it('does not add additional dashes in the slug if the project name already contains dashes', () => {
const dummyProjectName = 'My-Dash-Delimited Awesome Project';
$projectName.val(dummyProjectName);
projectNew.onProjectNameChange($projectName, $projectPath);
expect($projectPath.val()).toEqual('my-dash-delimited-awesome-project');
});
});
});
......@@ -1165,13 +1165,6 @@ describe Gitlab::GitAccess do
end
end
describe 'Geo system permissions' do
let(:actor) { :geo }
it { expect { pull_access_check }.not_to raise_error }
it { expect { push_access_check }.to raise_unauthorized(Gitlab::GitAccess::ERROR_MESSAGES[:upload]) }
end
context 'terms are enforced' do
before do
enforce_terms
......
......@@ -336,6 +336,22 @@ describe Gitlab::Workhorse do
it { expect { subject }.to raise_exception('Unsupported action: download') }
end
end
context 'when receive_max_input_size has been updated' do
it 'returns custom git config' do
allow(Gitlab::CurrentSettings).to receive(:receive_max_input_size) { 1 }
expect(subject[:GitConfigOptions]).to be_present
end
end
context 'when receive_max_input_size is empty' do
it 'returns an empty git config' do
allow(Gitlab::CurrentSettings).to receive(:receive_max_input_size) { nil }
expect(subject[:GitConfigOptions]).to be_empty
end
end
end
describe '.set_key_and_notify' do
......
......@@ -369,6 +369,26 @@ describe API::Internal do
expect(user.reload.last_activity_on).to be_nil
end
end
context 'when receive_max_input_size has been updated' do
it 'returns custom git config' do
allow(Gitlab::CurrentSettings).to receive(:receive_max_input_size) { 1 }
push(key, project)
expect(json_response["git_config_options"]).to be_present
end
end
context 'when receive_max_input_size is empty' do
it 'returns an empty git config' do
allow(Gitlab::CurrentSettings).to receive(:receive_max_input_size) { nil }
push(key, project)
expect(json_response["git_config_options"]).to be_empty
end
end
end
end
......
......@@ -10,9 +10,9 @@ shared_examples 'label note created from events' do
end
def label_refs(events)
sorted_labels = events.map(&:label).compact.sort_by(&:title)
labels = events.map(&:label).compact
sorted_labels.map { |l| l.to_reference}.join(' ')
labels.map { |l| l.to_reference}.sort.join(' ')
end
let(:time) { Time.now }
......
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