From 685072f5889f60e9ced0511fd72ce0d3d6be6230 Mon Sep 17 00:00:00 2001 From: Jarka Kadlecova Date: Sat, 5 Nov 2016 14:59:08 +0100 Subject: simplify url generation --- app/models/project_services/jira_service.rb | 18 ++++++++---------- changelogs/unreleased/jira_service_simplify.yml | 4 ++++ spec/models/project_services/jira_service_spec.rb | 7 +++++-- 3 files changed, 17 insertions(+), 12 deletions(-) create mode 100644 changelogs/unreleased/jira_service_simplify.yml diff --git a/app/models/project_services/jira_service.rb b/app/models/project_services/jira_service.rb index 2dbe0075465..7ce274b5dca 100644 --- a/app/models/project_services/jira_service.rb +++ b/app/models/project_services/jira_service.rb @@ -256,16 +256,14 @@ class JiraService < IssueTrackerService end def build_entity_url(entity_name, entity_id) - resource_url( - polymorphic_url( - [ - self.project.namespace.becomes(Namespace), - self.project, - entity_name - ], - id: entity_id, - routing_type: :path - ) + polymorphic_url( + [ + self.project.namespace.becomes(Namespace), + self.project, + entity_name + ], + id: entity_id, + host: Settings.gitlab.base_url ) end end diff --git a/changelogs/unreleased/jira_service_simplify.yml b/changelogs/unreleased/jira_service_simplify.yml new file mode 100644 index 00000000000..51cedd8ce5e --- /dev/null +++ b/changelogs/unreleased/jira_service_simplify.yml @@ -0,0 +1,4 @@ +--- +title: simplify url generation +merge_request: +author: Jarka Kadlecova diff --git a/spec/models/project_services/jira_service_spec.rb b/spec/models/project_services/jira_service_spec.rb index 05ee4a08391..ed5abcf7fc0 100644 --- a/spec/models/project_services/jira_service_spec.rb +++ b/spec/models/project_services/jira_service_spec.rb @@ -69,6 +69,7 @@ describe JiraService, models: true do end describe "Execute" do + let(:custom_base_url) { 'http://custom_url' } let(:user) { create(:user) } let(:project) { create(:project) } let(:merge_request) { create(:merge_request) } @@ -107,10 +108,12 @@ describe JiraService, models: true do end it "references the GitLab commit/merge request" do - @jira_service.execute(merge_request, ExternalIssue.new("JIRA-123", project)) + stub_config_setting(base_url: custom_base_url) + stub_config_setting(url: custom_base_url) + @jira_service.execute(merge_request, ExternalIssue.new("JIRA-123", project)) expect(WebMock).to have_requested(:post, @comment_url).with( - body: /#{Gitlab.config.gitlab.url}\/#{project.path_with_namespace}\/commit\/#{merge_request.diff_head_sha}/ + body: /#{custom_base_url}\/#{project.path_with_namespace}\/commit\/#{merge_request.diff_head_sha}/ ).once end -- cgit v1.2.1 From c76ff8cf32801198e93122fdcf689f3ac1987e41 Mon Sep 17 00:00:00 2001 From: Gabriel Mazetto Date: Thu, 22 Sep 2016 06:05:28 -0300 Subject: Improved redis sentinel documentation for CE --- doc/administration/high_availability/redis.md | 62 +++++++++++---------------- 1 file changed, 26 insertions(+), 36 deletions(-) diff --git a/doc/administration/high_availability/redis.md b/doc/administration/high_availability/redis.md index bc424330656..e347959ebbc 100644 --- a/doc/administration/high_availability/redis.md +++ b/doc/administration/high_availability/redis.md @@ -143,6 +143,7 @@ the master, and `masterauth` in slaves. redis['port'] = 6379 ## Slave redis instance + redis['master'] = false redis['master_ip'] = '10.10.10.10' # IP of master Redis server redis['master_port'] = 6379 # Port of master Redis server redis['master_password'] = "" @@ -157,31 +158,19 @@ servers. ### Sentinel setup -We don't provide yet an automated way to setup and run the Sentinel daemon -from Omnibus installation method. You must follow the instructions below and -run it by yourself. +We provide an automated way to setup and run the Sentinel daemon +with GitLab EE. -The support for Sentinel in Ruby has some [caveats](https://github.com/redis/redis-rb/issues/531). -While you can give any name for the `master-group-name` part of the -configuration, as in this example: - -```conf -sentinel monitor -``` - -,for it to work in Ruby, you have to use the "hostname" of the master Redis -server, otherwise you will get an error message like: -`Redis::CannotConnectError: No sentinels available.`. Read -[Sentinel troubleshooting](#sentinel-troubleshooting) for more information. +See the instructions below how to setup it by yourself. Here is an example configuration file (`sentinel.conf`) for a Sentinel node: ```conf port 26379 -sentinel monitor master-redis.example.com 10.10.10.10 6379 1 -sentinel down-after-milliseconds master-redis.example.com 10000 -sentinel config-epoch master-redis.example.com 0 -sentinel leader-epoch master-redis.example.com 0 +sentinel monitor gitlab-redis 10.0.0.1 6379 1 +sentinel down-after-milliseconds gitlab-redis 10000 +sentinel config-epoch gitlab-redis 0 +sentinel leader-epoch gitlab-redis 0 ``` --- @@ -213,10 +202,11 @@ The following steps should be performed in the [GitLab application server](gitla 1. Edit `/etc/gitlab/gitlab.rb` and add/change the following lines: ```ruby - gitlab-rails['redis_host'] = "master-redis.example.com" - gitlab-rails['redis_port'] = 6379 - gitlab-rails['redis_password'] = '' - gitlab-rails['redis_sentinels'] = [ + redis['master_name'] = "gitlab-redis" + redis['master_ip'] = "10.0.0.1" + redis['master_port'] = 6379 + redis['master_password'] = '' + gitlab_rails['redis_sentinels'] = [ {'host' => '10.10.10.1', 'port' => 26379}, {'host' => '10.10.10.2', 'port' => 26379}, {'host' => '10.10.10.3', 'port' => 26379} @@ -229,33 +219,33 @@ The following steps should be performed in the [GitLab application server](gitla If you get an error like: `Redis::CannotConnectError: No sentinels available.`, there may be something wrong with your configuration files or it can be related -to [this issue][gh-531] ([pull request][gh-534] that should make things better). +to [this issue][gh-531]. -It's a bit rigid the way you have to config `resque.yml` and `sentinel.conf`, -otherwise `redis-rb` will not work properly. +It's a bit non-intuitive the way you have to config `resque.yml` and +`sentinel.conf`, otherwise `redis-rb` will not work properly. -The hostname ('my-primary-redis') of the primary Redis server (`sentinel.conf`) -**must** match the one configured in GitLab (`resque.yml` for source installations -or `gitlab-rails['redis_*']` in Omnibus) and it must be valid ex: +The `master-group-name` ('gitlab-redis') defined in (`sentinel.conf`) +**must** be used as the hostname in GitLab (`resque.yml` for source installations +or `gitlab-rails['redis_*']` in Omnibus): ```conf # sentinel.conf: -sentinel monitor my-primary-redis 10.10.10.10 6379 1 -sentinel down-after-milliseconds my-primary-redis 10000 -sentinel config-epoch my-primary-redis 0 -sentinel leader-epoch my-primary-redis 0 +sentinel monitor gitlab-redis 10.10.10.10 6379 1 +sentinel down-after-milliseconds gitlab-redis 10000 +sentinel config-epoch gitlab-redis 0 +sentinel leader-epoch gitlab-redis 0 ``` ```yaml # resque.yaml production: - url: redis://my-primary-redis:6378 + url: redis://:myredispassword@gitlab-redis/ sentinels: - - host: slave1 + host: slave1.example.com # or use ip port: 26380 # point to sentinel, not to redis port - - host: slave2 + host: slave2.exampl.com # or use ip port: 26381 # point to sentinel, not to redis port ``` -- cgit v1.2.1 From f6d29583b02ffbb35b3c344f78b5a3575bf9c656 Mon Sep 17 00:00:00 2001 From: Gabriel Mazetto Date: Fri, 7 Oct 2016 01:16:32 +0200 Subject: Small fixes on Sentinel documentation for CE --- doc/administration/high_availability/redis.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/doc/administration/high_availability/redis.md b/doc/administration/high_availability/redis.md index e347959ebbc..69184ae9723 100644 --- a/doc/administration/high_availability/redis.md +++ b/doc/administration/high_availability/redis.md @@ -203,8 +203,6 @@ The following steps should be performed in the [GitLab application server](gitla ```ruby redis['master_name'] = "gitlab-redis" - redis['master_ip'] = "10.0.0.1" - redis['master_port'] = 6379 redis['master_password'] = '' gitlab_rails['redis_sentinels'] = [ {'host' => '10.10.10.1', 'port' => 26379}, -- cgit v1.2.1 From f54d60b41df3d30181a371d3799fa8b9451d4c5b Mon Sep 17 00:00:00 2001 From: Gabriel Mazetto Date: Fri, 7 Oct 2016 01:47:22 +0200 Subject: Updated password examples and improved omnibus troubleshooting --- doc/administration/high_availability/redis.md | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/doc/administration/high_availability/redis.md b/doc/administration/high_availability/redis.md index 69184ae9723..9fca7bfb8b4 100644 --- a/doc/administration/high_availability/redis.md +++ b/doc/administration/high_availability/redis.md @@ -45,7 +45,7 @@ Redis. redis['bind'] = '0.0.0.0' # If you wish to use Redis authentication (recommended) - redis['password'] = 'Redis Password' + redis['password'] = 'redis-password-goes-here' ``` 1. Run `sudo gitlab-ctl reconfigure` to install and configure PostgreSQL. @@ -132,7 +132,7 @@ the master, and `masterauth` in slaves. redis['port'] = 6379 ## Master redis instance - redis['password'] = '' + redis['password'] = 'redis-password-goes-here' ``` 1. Edit `/etc/gitlab/gitlab.rb` of a slave Redis machine (should be one or more machines): @@ -146,7 +146,7 @@ the master, and `masterauth` in slaves. redis['master'] = false redis['master_ip'] = '10.10.10.10' # IP of master Redis server redis['master_port'] = 6379 # Port of master Redis server - redis['master_password'] = "" + redis['master_password'] = "redis-password-goes-here" ``` 1. Reconfigure the GitLab for the changes to take effect: `sudo gitlab-ctl reconfigure` @@ -203,7 +203,7 @@ The following steps should be performed in the [GitLab application server](gitla ```ruby redis['master_name'] = "gitlab-redis" - redis['master_password'] = '' + redis['master_password'] = 'redis-password-goes-here' gitlab_rails['redis_sentinels'] = [ {'host' => '10.10.10.1', 'port' => 26379}, {'host' => '10.10.10.2', 'port' => 26379}, @@ -215,6 +215,21 @@ The following steps should be performed in the [GitLab application server](gitla ### Sentinel troubleshooting +#### Omnibus install + +If you get an error like: `Redis::CannotConnectError: No sentinels available.`, +there may be something wrong with your configuration files or it can be related +to [this issue][gh-531]. + +You must make sure you are defining the same value in `redis['master_name']` +and `redis['master_pasword']` as you defined for your sentinel node. + +The way the redis connector `redis-rb` works with sentinel is a bit +non-intuitive. We try to hide the complexity in omnibus, but it still requires +a few extra configs. + +#### Source install + If you get an error like: `Redis::CannotConnectError: No sentinels available.`, there may be something wrong with your configuration files or it can be related to [this issue][gh-531]. -- cgit v1.2.1 From c4d3c0de1f489639e1e2f1a12b7b4d88384d3e06 Mon Sep 17 00:00:00 2001 From: Gabriel Mazetto Date: Sat, 15 Oct 2016 05:40:15 +0200 Subject: Improved documentation on HA sentinel part and Redis replication troubleshooting. --- doc/administration/high_availability/redis.md | 316 +++++++++++++++++++++----- 1 file changed, 256 insertions(+), 60 deletions(-) diff --git a/doc/administration/high_availability/redis.md b/doc/administration/high_availability/redis.md index 9fca7bfb8b4..840f5896bd7 100644 --- a/doc/administration/high_availability/redis.md +++ b/doc/administration/high_availability/redis.md @@ -8,6 +8,27 @@ that comes bundled with GitLab Omnibus packages. information. We recommend using a combination of a Redis password and tight firewall rules to secure your Redis service. + + +**Table of Contents** + +- [Configure your own Redis server](#configure-your-own-redis-server) +- [Configure Redis using Omnibus](#configure-redis-using-omnibus) +- [Experimental Redis Sentinel support](#experimental-redis-sentinel-support) + - [Redis setup](#redis-setup) + - [Source install](#source-install) + - [Omnibus Install](#omnibus-install) + - [Troubleshooting Replication](#troubleshooting-replication) + - [Sentinel](#sentinel) + - [Sentinel setup (Community Edition)](#sentinel-setup-community-edition) + - [Sentinel setup (EE Only)](#sentinel-setup-ee-only) + - [GitLab setup](#gitlab-setup) + - [Sentinel troubleshooting](#sentinel-troubleshooting) + - [Omnibus install](#omnibus-install) + - [Source install](#source-install-1) + + + ## Configure your own Redis server If you're hosting GitLab on a cloud provider, you can optionally use a @@ -37,6 +58,7 @@ Redis. unicorn['enable'] = false sidekiq['enable'] = false postgresql['enable'] = false + gitlab_rails['enable'] = false gitlab_workhorse['enable'] = false mailroom['enable'] = false @@ -59,120 +81,294 @@ Redis. ## Experimental Redis Sentinel support -> [Introduced][ce-1877] in GitLab 8.11. +> [Introduced][ce-1877] in GitLab 8.11, improved in 8.13. Since GitLab 8.11, you can configure a list of Redis Sentinel servers that will monitor a group of Redis servers to provide you with a standard failover support. -There is currently one exception to the Sentinel support: `mail_room`, the -component that processes incoming emails. It doesn't support Sentinel yet, but -we hope to integrate a future release that does support it. - To get a better understanding on how to correctly setup Sentinel, please read the [Redis Sentinel documentation](http://redis.io/topics/sentinel) first, as failing to configure it correctly can lead to data loss. +Redis Sentinel can handle the most important tasks in a HA environment to help +keep servers online with minimal to no downtime: + +- Monitors master and slave instances to see if they are available +- Promote a slave to master when the master fails. +- Demote a master to slave when failed master comes back online (to prevent + data-partitioning). +- Can be queried by clients to always connect to the correct master server. + +There is currently one exception to the Sentinel support: `mail_room`, the +component that processes incoming emails. It doesn't support Sentinel yet, but +we hope to integrate a future release that does support it soon. + The configuration consists of three parts: -- Redis setup -- Sentinel setup -- GitLab setup +- Setup Redis Master and Slave nodes +- Setup Sentinel nodes +- Setup GitLab + +> **IMPORTANT**: You need at least 3 independent machines: physical, or VMs +running into distinct physical machines. If you fail to provision the +machines in that specific way, any issue with the shared environment can +bring your entire setup down. Read carefully how to configure those components below. ### Redis setup -You must have at least 2 Redis servers: 1 Master, 1 or more Slaves. +You must have at least `3` Redis servers: `1` Master, `2` Slaves, and they need to +be each in a independent machine (see explanation above). + They should be configured the same way and with similar server specs, as -in a failover situation, any Slave can be elected as the new Master by +in a failover situation, any `Slave` can be elected as the new `Master` by the Sentinel servers. -In a minimal setup, the only required change for the slaves in `redis.conf` -is the addition of a `slaveof` line pointing to the initial master. -You can increase the security by defining a `requirepass` configuration in -the master, and `masterauth` in slaves. +With Sentinel, you must define a password to protect the access as both +Sentinel instances and other redis instances should be able to talk to +each other over the network. ---- +You'll need to define both `requirepass` and `masterauth` in all +nodes because they can be re-configured at any time by the Sentinels +during a failover, and change it's status as `Master` or `Slave`. -**Configuring your own Redis server** +Initial `Slave` nodes will have in `redis.conf` an additional `slaveof` line +pointing to the initial `Master`. -1. Add to the slaves' `redis.conf`: +#### Source install - ```conf - # IP and port of the master Redis server - slaveof 10.10.10.10 6379 - ``` +**Master Redis instance** -1. Optionally, set up password authentication for increased security. - Add the following to master's `redis.conf`: +You need to make the following changes in `redis.conf`: - ```conf - # Optional password authentication for increased security - requirepass "" - ``` +1. Define a `bind` address pointing to a local IP that your other machines + can reach you. If you really need to bind to an external acessible IP, make + sure you add extra firewall rules to prevent unauthorized access: -1. Then add this line to all the slave servers' `redis.conf`: + ```conf + # By default, if no "bind" configuration directive is specified, Redis listens + # for connections from all the network interfaces available on the server. + # It is possible to listen to just one or multiple selected interfaces using + # the "bind" configuration directive, followed by one or more IP addresses. + # + # Examples: + # + # bind 192.168.1.100 10.0.0.1 + # bind 127.0.0.1 ::1 + bind 0.0.0.0 # This will bind to all interfaces + ``` + +1. Define a `port` to force redis to listin on TCP so other machines can + connect to it: + + ```conf + # Accept connections on the specified port, default is 6379 (IANA #815344). + # If port 0 is specified Redis will not listen on a TCP socket. + port 6379 + ``` + +1. Set up password authentication (use the same password in all nodes) ```conf - masterauth "" + requirepass "redis-password-goes-here" + masterauth "redis-password-goes-here" ``` 1. Restart the Redis services for the changes to take effect. ---- +**Slave Redis instance** -**Using Redis via Omnibus** +1. Follow same instructions from master with the extra change in `redis.conf`: -1. Edit `/etc/gitlab/gitlab.rb` of a master Redis machine (usualy a single machine): + ```conf + # IP and port of the master Redis server + slaveof 10.10.10.10 6379 + ``` - ```ruby - ## Redis TCP support (will disable UNIX socket transport) - redis['bind'] = '0.0.0.0' # or specify an IP to bind to a single one - redis['port'] = 6379 +1. Restart the Redis services for the changes to take effect. - ## Master redis instance - redis['password'] = 'redis-password-goes-here' - ``` +#### Omnibus Install -1. Edit `/etc/gitlab/gitlab.rb` of a slave Redis machine (should be one or more machines): +You need to install the omnibus package in 3 different and independent machines. +We will elect one as the initial `Master` and the other 2 as `Slaves`. - ```ruby - ## Redis TCP support (will disable UNIX socket transport) - redis['bind'] = '0.0.0.0' # or specify an IP to bind to a single one - redis['port'] = 6379 +If you are migrating from a single machine install, you may want to setup the +machines as Slaves, pointing to the original machine as `Master`, to migrate +the data first, and than switch to this setup. - ## Slave redis instance - redis['master'] = false - redis['master_ip'] = '10.10.10.10' # IP of master Redis server - redis['master_port'] = 6379 # Port of master Redis server - redis['master_password'] = "redis-password-goes-here" - ``` +To disable redis in the single install, edit `/etc/gitlab/gitlab.rb`: + +```ruby +redis['enable'] = false +``` + +**Master Redis instances** + +You need to make the following changes in `/etc/gitlab/gitlab.rb`: + +1. Define a `redis['bind']` address pointing to a local IP that your other machines + can reach you. If you really need to bind to an external acessible IP, make + sure you add extra firewall rules to prevent unauthorized access. +1. Define a `redis['port']` to force redis to listin on TCP so other machines can + connect to it. +1. Set up password authentication with `redis['master_password']` (use the same + password in all nodes). -1. Reconfigure the GitLab for the changes to take effect: `sudo gitlab-ctl reconfigure` +```ruby +## Redis TCP support (will disable UNIX socket transport) +redis['bind'] = '0.0.0.0' # or specify an IP to bind to a single one +redis['port'] = 6379 +redis['requirepass'] = 'redis-password-goes-here' +redis['master_password'] = 'redis-password-goes-here' +``` + +Reconfigure GitLab Omnibus for the changes to take effect: `sudo gitlab-ctl reconfigure` + +**Slave Redis instances** + +You need to make the same changes listed for the `Master` instance, +with an additional `Slave` section as in the example below: + +```ruby +## Redis TCP support (will disable UNIX socket transport) +redis['bind'] = '0.0.0.0' # or specify an IP to bind to a single one +redis['port'] = 6379 +redis['requirepass'] = 'redis-password-goes-here' +redis['master_password'] = 'redis-password-goes-here' + +## Slave redis instance +redis['master'] = false +redis['master_ip'] = '10.10.10.10' # IP of master Redis server +redis['master_port'] = 6379 # Port of master Redis server +``` + +Reconfigure GitLab Omnibus for the changes to take effect: `sudo gitlab-ctl reconfigure` + +#### Troubleshooting Replication + +You can check if everything is correct by connecting to each server using +`redis-cli` application, and sending the `INFO` command. + +If authentication was correctly defined, it should fail with: +`NOAUTH Authentication required` error. Try to authenticate with the +previous defined password with `AUTH redis-password-goes-here` and +try the `INFO` command again. + +Look for the `# Replication` section where you should see some important +information like the `role` of the server. + +When connected to a `master` redis, you will see the number of connected +`slaves`, and a list of each with connection details. + +When it's a `slave`, you will see details of the master connection and if +its `up` or `down`. --- Now that the Redis servers are all set up, let's configure the Sentinel servers. -### Sentinel setup +If you are not sure if your Redis servers are working and replicating +correctly, please read the [Troubleshooting Replication](#troubleshooting-replication) +and fix it before proceeding with Sentinel setup. + +### Sentinel + +You must have at least `3` Redis Sentinel servers, and they need to +be each in a independent machine. You can install them in the same +machines you installed the other `3` Redis servers. + +This number is required for the consensus algorithm to be effective +in the case of a failure. You should always have and `odd` number +of Sentinel nodes provisioned. -We provide an automated way to setup and run the Sentinel daemon -with GitLab EE. +Here is a simple explanation on how Sentinel handles a failover: -See the instructions below how to setup it by yourself. +When a number of Sentinels (`quorum` value) agree the fact the `master` is +not reachable, the **majority** of the sentinels must elect a temporary +Sentinel `leader`, that will be responsible to start the failover proceedings. -Here is an example configuration file (`sentinel.conf`) for a Sentinel node: +As an example, for a cluster of `3` Sentinels, at least `2` must agree on a +`leader`. If you have total of `5` at least `3` must agree on the leader. + +The `quorum` is only used to detect failure, not to elect the `leader`. + +Official [Sentinel documentation](http://redis.io/topics/sentinel#example-sentinel-deployments) +also lists different network topologies and warns againts situations like +network partition and how it can affect the state of the HA solution. Make +sure you read it carefully and understand the implications in your current +setup. + +To make Sentinel setup easier, ee provide an [automated way to setup and run](#sentinel-setup-ee-only) +the Sentinel daemon with GitLab EE. + +#### Sentinel setup (Community Edition) + +For GitLab CE, you need to install, configure, execute and monitor Sentinel +by yourself. + +Here is an example configuration file (`sentinel.conf`) for a minimal Sentinel +node: ```conf -port 26379 -sentinel monitor gitlab-redis 10.0.0.1 6379 1 +bind 0.0.0.0 # bind to all interfaces or change to a specific IP +port 26379 # default sentinel port +sentinel auth-pass gitlab-redis redis-password-goes-here +sentinel monitor gitlab-redis 10.0.0.1 6379 2 sentinel down-after-milliseconds gitlab-redis 10000 sentinel config-epoch gitlab-redis 0 sentinel leader-epoch gitlab-redis 0 ``` +#### Sentinel setup (EE Only) + +To setup sentinel, you must edit `/etc/gitlab/gitlab.rb` file. +This is a minimal configuration required to run the daemon: + +```ruby +redis['master_name'] = 'gitlab-redis' # must be the same in every sentinel node +redis['master_ip'] = '10.0.0.1' # ip of the initial master redis instance +redis['master_port'] = 6379 # port of the initial master redis instance +redis['master_password'] = 'your-secure-password-here' # the same value defined in redis['password'] in the master instance + +sentinel['enable'] = true +# sentinel['port'] = 26379 + +## Quorum must reflect the amount of voting sentinels it take to start a failover. +sentinel['quorum'] = 2 + +## Consider unresponsive server down after x amount of ms. +# sentinel['down_after_milliseconds'] = 10000 + +# sentinel['failover_timeout'] = 60000 +``` + +When you install Sentinel in a separate machine, you need to control which +other services will be running in it. Take a look at the following variables +and enable or disable whenever it fits your strategy: + +```ruby +# Enabled Redis and Sentinel services +redis['enable'] = true +sentinel['enable'] = true + +# Disabled all other services +redis['enable'] = false +bootstrap['enable'] = false +nginx['enable'] = false +unicorn['enable'] = false +sidekiq['enable'] = false +postgresql['enable'] = false +gitlab_workhorse['enable'] = false +gitlab_rails['enable'] = false +mailroom['enable'] = false +``` + +Remember that enabling a new service may also require additional configuration +params (like `redis` for example). + --- The final part is to inform the main GitLab application server of the Redis @@ -243,7 +439,7 @@ or `gitlab-rails['redis_*']` in Omnibus): ```conf # sentinel.conf: -sentinel monitor gitlab-redis 10.10.10.10 6379 1 +sentinel monitor gitlab-redis 10.10.10.10 6379 2 sentinel down-after-milliseconds gitlab-redis 10000 sentinel config-epoch gitlab-redis 0 sentinel leader-epoch gitlab-redis 0 @@ -276,7 +472,7 @@ To make sure your configuration is correct: sudo gitlab-rails console # For source installations - sudo -u git rails console RAILS_ENV=production + sudo -u git rails console production ``` 1. Run in the console: -- cgit v1.2.1 From 3242ea6d40442f19c3932715521864ac5eb3700b Mon Sep 17 00:00:00 2001 From: Gabriel Mazetto Date: Tue, 18 Oct 2016 19:45:57 +0200 Subject: Improved Redis HA and Sentinel documentation. --- doc/administration/high_availability/redis.md | 327 +++++++++++++++++--------- 1 file changed, 210 insertions(+), 117 deletions(-) diff --git a/doc/administration/high_availability/redis.md b/doc/administration/high_availability/redis.md index 840f5896bd7..29bf2c54a08 100644 --- a/doc/administration/high_availability/redis.md +++ b/doc/administration/high_availability/redis.md @@ -1,7 +1,7 @@ # Configuring Redis for GitLab HA You can choose to install and manage Redis yourself, or you can use the one -that comes bundled with GitLab Omnibus packages. +that comes bundled with Omnibus GitLab packages. > **Note:** Redis does not require authentication by default. See [Redis Security](http://redis.io/topics/security) documentation for more @@ -15,17 +15,22 @@ that comes bundled with GitLab Omnibus packages. - [Configure your own Redis server](#configure-your-own-redis-server) - [Configure Redis using Omnibus](#configure-redis-using-omnibus) - [Experimental Redis Sentinel support](#experimental-redis-sentinel-support) +- [Redis Sentinel support](#redis-sentinel-support) - [Redis setup](#redis-setup) - - [Source install](#source-install) - - [Omnibus Install](#omnibus-install) - - [Troubleshooting Replication](#troubleshooting-replication) - - [Sentinel](#sentinel) - - [Sentinel setup (Community Edition)](#sentinel-setup-community-edition) - - [Sentinel setup (EE Only)](#sentinel-setup-ee-only) + - [Existing single-machine installation](#existing-single-machine-installation) + - [Installation from source](#installation-from-source) + - [Omnibus packages](#omnibus-packages) + - [Configuring Sentinel](#configuring-sentinel) + - [How sentinel handles a failover](#how-sentinel-handles-a-failover) + - [Sentinel setup](#sentinel-setup) + - [Community Edition](#community-edition) + - [Enterprise Edition](#enterprise-edition) - [GitLab setup](#gitlab-setup) - - [Sentinel troubleshooting](#sentinel-troubleshooting) +- [Troubleshooting](#troubleshooting) + - [Redis replication](#redis-replication) + - [Sentinel](#sentinel) - [Omnibus install](#omnibus-install) - - [Source install](#source-install-1) + - [Source install](#source-install) @@ -41,7 +46,7 @@ If you don't want to bother setting up your own Redis server, you can use the one bundled with Omnibus. In this case, you should disable all services except Redis. -1. Download/install GitLab Omnibus using **steps 1 and 2** from +1. Download/install Omnibus GitLab using **steps 1 and 2** from [GitLab downloads](https://about.gitlab.com/downloads). Do not complete other steps on the download page. 1. Create/edit `/etc/gitlab/gitlab.rb` and use the following configuration. @@ -70,7 +75,7 @@ Redis. redis['password'] = 'redis-password-goes-here' ``` -1. Run `sudo gitlab-ctl reconfigure` to install and configure PostgreSQL. +1. Run `sudo gitlab-ctl reconfigure` to install and configure Redis. > **Note**: This `reconfigure` step will result in some errors. That's OK - don't be alarmed. @@ -81,7 +86,12 @@ Redis. ## Experimental Redis Sentinel support -> [Introduced][ce-1877] in GitLab 8.11, improved in 8.13. + > Experimental Redis Sentinel support was [Introduced][ce-1877] in GitLab 8.11. + Starting with 8.13, Redis Sentinel is no longer experimental. + If you used with versions `< 8.13` before, please check the updated + documentation below. + +## Redis Sentinel support Since GitLab 8.11, you can configure a list of Redis Sentinel servers that will monitor a group of Redis servers to provide you with a standard failover @@ -100,20 +110,19 @@ keep servers online with minimal to no downtime: data-partitioning). - Can be queried by clients to always connect to the correct master server. -There is currently one exception to the Sentinel support: `mail_room`, the -component that processes incoming emails. It doesn't support Sentinel yet, but -we hope to integrate a future release that does support it soon. - The configuration consists of three parts: - Setup Redis Master and Slave nodes - Setup Sentinel nodes - Setup GitLab -> **IMPORTANT**: You need at least 3 independent machines: physical, or VMs -running into distinct physical machines. If you fail to provision the -machines in that specific way, any issue with the shared environment can -bring your entire setup down. +### Prerequisites + +You need at least `3` independent machines: physical, or VMs running into +distinct physical machines. + +If you fail to provision the machines in that specific way, any issue with +the shared environment can bring your entire setup down. Read carefully how to configure those components below. @@ -131,15 +140,35 @@ Sentinel instances and other redis instances should be able to talk to each other over the network. You'll need to define both `requirepass` and `masterauth` in all -nodes because they can be re-configured at any time by the Sentinels -during a failover, and change it's status as `Master` or `Slave`. +nodes. At any time during a failover the Sentinels can reconfigure a node +and change it's status from `Master` to `Slave` and vice versa. -Initial `Slave` nodes will have in `redis.conf` an additional `slaveof` line +Initial `Slave` nodes require an additional `slaveof` setting in `redis.conf` pointing to the initial `Master`. -#### Source install +#### Existing single-machine installation + +If you already have a single-machine GitLab install running, you will need to +replicate from this machine first, before de-activating the Redis instance +inside it. + +Your single-machine install will be the initial `Master`, and the `3` others +should be configured as `Slave` pointing to this machine. + +After replication catchs-up, you will need to stop services in the +single-machine install, to rotate the `Master` to one of the new nodes. + +Make the required changes in configuration and restart the new nodes again. + +To disable redis in the single install, edit `/etc/gitlab/gitlab.rb`: -**Master Redis instance** +```ruby +redis['enable'] = false +``` + +#### Installation from source + +**Configuring Master Redis instance** You need to make the following changes in `redis.conf`: @@ -178,9 +207,9 @@ You need to make the following changes in `redis.conf`: 1. Restart the Redis services for the changes to take effect. -**Slave Redis instance** +**Configuring Slave Redis instance** -1. Follow same instructions from master with the extra change in `redis.conf`: +1. Follow same instructions from master, with the extra change in `redis.conf`: ```conf # IP and port of the master Redis server @@ -189,33 +218,24 @@ You need to make the following changes in `redis.conf`: 1. Restart the Redis services for the changes to take effect. -#### Omnibus Install - -You need to install the omnibus package in 3 different and independent machines. -We will elect one as the initial `Master` and the other 2 as `Slaves`. +#### Omnibus packages -If you are migrating from a single machine install, you may want to setup the -machines as Slaves, pointing to the original machine as `Master`, to migrate -the data first, and than switch to this setup. - -To disable redis in the single install, edit `/etc/gitlab/gitlab.rb`: - -```ruby -redis['enable'] = false -``` +You need to install the Omnibus GitLab package in `3` independent machines. -**Master Redis instances** +**Configuring Master Redis instance** -You need to make the following changes in `/etc/gitlab/gitlab.rb`: +You will need to configure the following: 1. Define a `redis['bind']` address pointing to a local IP that your other machines can reach you. If you really need to bind to an external acessible IP, make sure you add extra firewall rules to prevent unauthorized access. -1. Define a `redis['port']` to force redis to listin on TCP so other machines can - connect to it. -1. Set up password authentication with `redis['master_password']` (use the same +1. Define a `redis['port']` so redis can listen for TCP requests which will + allow other machines to connect to it. +1. Set up a password authentication with `redis['master_password']` (use the same password in all nodes). +In `/etc/gitlab/gitlab.rb`: + ```ruby ## Redis TCP support (will disable UNIX socket transport) redis['bind'] = '0.0.0.0' # or specify an IP to bind to a single one @@ -224,15 +244,14 @@ redis['requirepass'] = 'redis-password-goes-here' redis['master_password'] = 'redis-password-goes-here' ``` -Reconfigure GitLab Omnibus for the changes to take effect: `sudo gitlab-ctl reconfigure` +Reconfigure Omnibus GitLab for the changes to take effect: `sudo gitlab-ctl reconfigure` -**Slave Redis instances** +**Configuring Slave Redis instances** You need to make the same changes listed for the `Master` instance, with an additional `Slave` section as in the example below: ```ruby -## Redis TCP support (will disable UNIX socket transport) redis['bind'] = '0.0.0.0' # or specify an IP to bind to a single one redis['port'] = 6379 redis['requirepass'] = 'redis-password-goes-here' @@ -244,26 +263,7 @@ redis['master_ip'] = '10.10.10.10' # IP of master Redis server redis['master_port'] = 6379 # Port of master Redis server ``` -Reconfigure GitLab Omnibus for the changes to take effect: `sudo gitlab-ctl reconfigure` - -#### Troubleshooting Replication - -You can check if everything is correct by connecting to each server using -`redis-cli` application, and sending the `INFO` command. - -If authentication was correctly defined, it should fail with: -`NOAUTH Authentication required` error. Try to authenticate with the -previous defined password with `AUTH redis-password-goes-here` and -try the `INFO` command again. - -Look for the `# Replication` section where you should see some important -information like the `role` of the server. - -When connected to a `master` redis, you will see the number of connected -`slaves`, and a list of each with connection details. - -When it's a `slave`, you will see details of the master connection and if -its `up` or `down`. +Reconfigure Omnibus GitLab for the changes to take effect: `sudo gitlab-ctl reconfigure` --- @@ -274,26 +274,29 @@ If you are not sure if your Redis servers are working and replicating correctly, please read the [Troubleshooting Replication](#troubleshooting-replication) and fix it before proceeding with Sentinel setup. -### Sentinel +### Configuring Sentinel You must have at least `3` Redis Sentinel servers, and they need to -be each in a independent machine. You can install them in the same -machines you installed the other `3` Redis servers. +be each in a independent machine. You can configure them in the same +machines where you've configured the other Redis servers. This number is required for the consensus algorithm to be effective -in the case of a failure. You should always have and `odd` number -of Sentinel nodes provisioned. +in the case of a failure. **You should always have and `odd` number +of Sentinel nodes provisioned**. + +#### How sentinel handles a failover -Here is a simple explanation on how Sentinel handles a failover: +If (`quorum` value of) Sentinels agree the fact the `master` is not reachable, +Sentinels will try to elect a temporary `Leader`. The **Majority** of the +Sentinels must agree to start a failover. -When a number of Sentinels (`quorum` value) agree the fact the `master` is -not reachable, the **majority** of the sentinels must elect a temporary -Sentinel `leader`, that will be responsible to start the failover proceedings. +If you don't have the **Majority** of the Sentinels online (for example if you +are under a network partitioning), a failover **will not be started**. -As an example, for a cluster of `3` Sentinels, at least `2` must agree on a -`leader`. If you have total of `5` at least `3` must agree on the leader. +For example, for a cluster of `3` Sentinels, at least `2` must agree on a +`Leader`. If you have total of `5` at least `3` must agree on a `Leader`. -The `quorum` is only used to detect failure, not to elect the `leader`. +The `quorum` is only used to detect failure, not to elect the `Leader`. Official [Sentinel documentation](http://redis.io/topics/sentinel#example-sentinel-deployments) also lists different network topologies and warns againts situations like @@ -301,16 +304,16 @@ network partition and how it can affect the state of the HA solution. Make sure you read it carefully and understand the implications in your current setup. -To make Sentinel setup easier, ee provide an [automated way to setup and run](#sentinel-setup-ee-only) -the Sentinel daemon with GitLab EE. +GitLab Enterprise Edition provides [automated way to setup and run](#sentinel-setup-ee-only) the Sentinel daemon. -#### Sentinel setup (Community Edition) +#### Sentinel setup -For GitLab CE, you need to install, configure, execute and monitor Sentinel -by yourself. +##### Community Edition +With GitLab Community Edition, you need to install, configure, execute and +monitor Sentinel from source. Omnibus GitLab Community Edition package does +not support Sentinel configuration. -Here is an example configuration file (`sentinel.conf`) for a minimal Sentinel -node: +A minimal configuration file (`sentinel.conf`) should contain the following: ```conf bind 0.0.0.0 # bind to all interfaces or change to a specific IP @@ -322,35 +325,17 @@ sentinel config-epoch gitlab-redis 0 sentinel leader-epoch gitlab-redis 0 ``` -#### Sentinel setup (EE Only) +##### Enterprise Edition -To setup sentinel, you must edit `/etc/gitlab/gitlab.rb` file. -This is a minimal configuration required to run the daemon: +To setup sentinel, you edit `/etc/gitlab/gitlab.rb` file: ```ruby -redis['master_name'] = 'gitlab-redis' # must be the same in every sentinel node -redis['master_ip'] = '10.0.0.1' # ip of the initial master redis instance -redis['master_port'] = 6379 # port of the initial master redis instance -redis['master_password'] = 'your-secure-password-here' # the same value defined in redis['password'] in the master instance -sentinel['enable'] = true -# sentinel['port'] = 26379 +## When you install Sentinel in a separate machine, you need to control which +## other services will be running in it. Take a look at the following variables +## and enable or disable whenever it fits your strategy: -## Quorum must reflect the amount of voting sentinels it take to start a failover. -sentinel['quorum'] = 2 - -## Consider unresponsive server down after x amount of ms. -# sentinel['down_after_milliseconds'] = 10000 - -# sentinel['failover_timeout'] = 60000 -``` - -When you install Sentinel in a separate machine, you need to control which -other services will be running in it. Take a look at the following variables -and enable or disable whenever it fits your strategy: - -```ruby -# Enabled Redis and Sentinel services +## Enabled Redis and Sentinel services redis['enable'] = true sentinel['enable'] = true @@ -364,10 +349,53 @@ postgresql['enable'] = false gitlab_workhorse['enable'] = false gitlab_rails['enable'] = false mailroom['enable'] = false -``` -Remember that enabling a new service may also require additional configuration -params (like `redis` for example). +## Configure Redis +redis['master_name'] = 'gitlab-redis' # must be the same in every sentinel node +redis['master_ip'] = '10.0.0.1' # ip of the initial master redis instance +redis['master_port'] = 6379 # port of the initial master redis instance +redis['master_password'] = 'your-secure-password-here' # the same value defined in redis['password'] in the master instance + +## Configure Sentinel +# sentinel['port'] = 26379 # uncomment to change default port + +## Quorum must reflect the amount of voting sentinels it take to start a failover. +## Value must NOT be greater then the ammount of sentinels. +## +## The quorum can be used to tune Sentinel in two ways: +## 1. If a the quorum is set to a value smaller than the majority of Sentinels +## we deploy, we are basically making Sentinel more sensible to master failures, +## triggering a failover as soon as even just a minority of Sentinels is no longer +## able to talk with the master. +## 1. If a quorum is set to a value greater than the majority of Sentinels, we are +## making Sentinel able to failover only when there are a very large number (larger +## than majority) of well connected Sentinels which agree about the master being down.s +sentinel['quorum'] = 2 + +## Consider unresponsive server down after x amount of ms. +# sentinel['down_after_milliseconds'] = 10000 + +## Specifies the failover timeout in milliseconds. It is used in many ways: +## +## - The time needed to re-start a failover after a previous failover was +## already tried against the same master by a given Sentinel, is two +## times the failover timeout. +## +## - The time needed for a slave replicating to a wrong master according +## to a Sentinel current configuration, to be forced to replicate +## with the right master, is exactly the failover timeout (counting since +## the moment a Sentinel detected the misconfiguration). +## +## - The time needed to cancel a failover that is already in progress but +## did not produced any configuration change (SLAVEOF NO ONE yet not +## acknowledged by the promoted slave). +## +## - The maximum time a failover in progress waits for all the slaves to be +## reconfigured as slaves of the new master. However even after this time +## the slaves will be reconfigured by the Sentinels anyway, but not with +## the exact parallel-syncs progression as specified. +# sentinel['failover_timeout'] = 60000 +``` --- @@ -409,9 +437,74 @@ The following steps should be performed in the [GitLab application server](gitla 1. [Reconfigure] the GitLab for the changes to take effect. -### Sentinel troubleshooting +## Troubleshooting + +There are a lot of moving parts that needs to be taken care carefully +in order for the HA setup to work as expected. + +Before proceeding with the troubleshooting below, check your firewall +rules: +- Redis machines + - Accept TCP connection in `6379` + - Connect to the other Redis machines via TCP in `6379` +- Sentinel machines + - Accept TCP connection in `26379` + - Connect to other Sentinel machines via TCP in `26379` + - Connect to the Redis machines via TCP in `6379` + +### Redis replication + +You can check if everything is correct by connecting to each server using +`redis-cli` application, and sending the `INFO` command. + +If authentication was correctly defined, it should fail with: +`NOAUTH Authentication required` error. Try to authenticate with the +previous defined password with `AUTH redis-password-goes-here` and +try the `INFO` command again. + +Look for the `# Replication` section where you should see some important +information like the `role` of the server. + +When connected to a `master` redis, you will see the number of connected +`slaves`, and a list of each with connection details: + +``` +# Replication +role:master +connected_slaves:1 +slave0:ip=10.133.5.21,port=6379,state=online,offset=208037514,lag=1 +master_repl_offset:208037658 +repl_backlog_active:1 +repl_backlog_size:1048576 +repl_backlog_first_byte_offset:206989083 +repl_backlog_histlen:1048576 +``` + +When it's a `slave`, you will see details of the master connection and if +its `up` or `down`: + +``` +# Replication +role:slave +master_host:10.133.1.58 +master_port:6379 +master_link_status:up +master_last_io_seconds_ago:1 +master_sync_in_progress:0 +slave_repl_offset:208096498 +slave_priority:100 +slave_read_only:1 +connected_slaves:0 +master_repl_offset:0 +repl_backlog_active:0 +repl_backlog_size:1048576 +repl_backlog_first_byte_offset:0 +repl_backlog_histlen:0 +``` + +### Sentinel -#### Omnibus install +#### Omnibus GitLab If you get an error like: `Redis::CannotConnectError: No sentinels available.`, there may be something wrong with your configuration files or it can be related @@ -424,7 +517,7 @@ The way the redis connector `redis-rb` works with sentinel is a bit non-intuitive. We try to hide the complexity in omnibus, but it still requires a few extra configs. -#### Source install +#### Install from Source If you get an error like: `Redis::CannotConnectError: No sentinels available.`, there may be something wrong with your configuration files or it can be related -- cgit v1.2.1 From 0ca14544df6f8053a998009043290a4242d0362c Mon Sep 17 00:00:00 2001 From: Gabriel Mazetto Date: Tue, 18 Oct 2016 19:52:19 +0200 Subject: Reduce the ammount of lines to disable services and update TOC --- doc/administration/high_availability/redis.md | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/doc/administration/high_availability/redis.md b/doc/administration/high_availability/redis.md index 29bf2c54a08..aa334a8a0f6 100644 --- a/doc/administration/high_availability/redis.md +++ b/doc/administration/high_availability/redis.md @@ -16,6 +16,7 @@ that comes bundled with Omnibus GitLab packages. - [Configure Redis using Omnibus](#configure-redis-using-omnibus) - [Experimental Redis Sentinel support](#experimental-redis-sentinel-support) - [Redis Sentinel support](#redis-sentinel-support) + - [Prerequisites](#prerequisites) - [Redis setup](#redis-setup) - [Existing single-machine installation](#existing-single-machine-installation) - [Installation from source](#installation-from-source) @@ -29,8 +30,8 @@ that comes bundled with Omnibus GitLab packages. - [Troubleshooting](#troubleshooting) - [Redis replication](#redis-replication) - [Sentinel](#sentinel) - - [Omnibus install](#omnibus-install) - - [Source install](#source-install) + - [Omnibus GitLab](#omnibus-gitlab) + - [Install from Source](#install-from-source) @@ -60,11 +61,8 @@ Redis. redis['enable'] = true bootstrap['enable'] = false nginx['enable'] = false - unicorn['enable'] = false - sidekiq['enable'] = false postgresql['enable'] = false gitlab_rails['enable'] = false - gitlab_workhorse['enable'] = false mailroom['enable'] = false # Redis configuration @@ -340,13 +338,9 @@ redis['enable'] = true sentinel['enable'] = true # Disabled all other services -redis['enable'] = false bootstrap['enable'] = false nginx['enable'] = false -unicorn['enable'] = false -sidekiq['enable'] = false postgresql['enable'] = false -gitlab_workhorse['enable'] = false gitlab_rails['enable'] = false mailroom['enable'] = false -- cgit v1.2.1 From 1dcbff1c4b07a0893f6d4a676895e8a01b8cfb14 Mon Sep 17 00:00:00 2001 From: Gabriel Mazetto Date: Thu, 20 Oct 2016 18:11:00 +0200 Subject: Few more fixes to Sentinel documentation to address MR feedback --- doc/administration/high_availability/redis.md | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/doc/administration/high_availability/redis.md b/doc/administration/high_availability/redis.md index aa334a8a0f6..d60852814bb 100644 --- a/doc/administration/high_availability/redis.md +++ b/doc/administration/high_availability/redis.md @@ -63,13 +63,10 @@ Redis. nginx['enable'] = false postgresql['enable'] = false gitlab_rails['enable'] = false - mailroom['enable'] = false # Redis configuration redis['port'] = 6379 redis['bind'] = '0.0.0.0' - - # If you wish to use Redis authentication (recommended) redis['password'] = 'redis-password-goes-here' ``` @@ -85,8 +82,8 @@ Redis. ## Experimental Redis Sentinel support > Experimental Redis Sentinel support was [Introduced][ce-1877] in GitLab 8.11. - Starting with 8.13, Redis Sentinel is no longer experimental. - If you used with versions `< 8.13` before, please check the updated + Starting with 8.14, Redis Sentinel is no longer experimental. + If you used with versions `< 8.14` before, please check the updated documentation below. ## Redis Sentinel support @@ -279,7 +276,7 @@ be each in a independent machine. You can configure them in the same machines where you've configured the other Redis servers. This number is required for the consensus algorithm to be effective -in the case of a failure. **You should always have and `odd` number +in the case of a failure. **You should always have an `odd` number of Sentinel nodes provisioned**. #### How sentinel handles a failover @@ -400,7 +397,7 @@ master and the new sentinels servers. You can enable or disable sentinel support at any time in new or existing installations. From the GitLab application perspective, all it requires is -the correct credentials for the master Redis and for a few Sentinel nodes. +the correct credentials for the master Redis and for all Sentinel nodes. It doesn't require a list of all Sentinel nodes, as in case of a failure, the application will need to query only one of them. -- cgit v1.2.1 From 92e603727fd93ab862f7c6b5b46dfa2b1dd3a44e Mon Sep 17 00:00:00 2001 From: Gabriel Mazetto Date: Sat, 29 Oct 2016 00:40:28 +0200 Subject: Fixed documentation and added redis/sentinel roles instruction --- doc/administration/high_availability/redis.md | 68 +++++++++++++++++++++------ 1 file changed, 54 insertions(+), 14 deletions(-) diff --git a/doc/administration/high_availability/redis.md b/doc/administration/high_availability/redis.md index d60852814bb..bfad3047385 100644 --- a/doc/administration/high_availability/redis.md +++ b/doc/administration/high_availability/redis.md @@ -235,7 +235,7 @@ In `/etc/gitlab/gitlab.rb`: ## Redis TCP support (will disable UNIX socket transport) redis['bind'] = '0.0.0.0' # or specify an IP to bind to a single one redis['port'] = 6379 -redis['requirepass'] = 'redis-password-goes-here' +redis['password'] = 'redis-password-goes-here' redis['master_password'] = 'redis-password-goes-here' ``` @@ -249,7 +249,7 @@ with an additional `Slave` section as in the example below: ```ruby redis['bind'] = '0.0.0.0' # or specify an IP to bind to a single one redis['port'] = 6379 -redis['requirepass'] = 'redis-password-goes-here' +redis['password'] = 'redis-password-goes-here' redis['master_password'] = 'redis-password-goes-here' ## Slave redis instance @@ -327,27 +327,25 @@ To setup sentinel, you edit `/etc/gitlab/gitlab.rb` file: ```ruby ## When you install Sentinel in a separate machine, you need to control which -## other services will be running in it. Take a look at the following variables -## and enable or disable whenever it fits your strategy: +## other services will be running in it. +## We've simplified the choice using special "roles" settings: -## Enabled Redis and Sentinel services -redis['enable'] = true -sentinel['enable'] = true +## Enabled Sentinel and Redis Master services +redis_sentinel_role['enable'] = true +redis_master_role['enable'] = true -# Disabled all other services -bootstrap['enable'] = false -nginx['enable'] = false -postgresql['enable'] = false -gitlab_rails['enable'] = false -mailroom['enable'] = false +## Enabled Sentinel and Redis Slave services +redis_sentinel_role['enable'] = true +redis_master_role['enable'] = true ## Configure Redis redis['master_name'] = 'gitlab-redis' # must be the same in every sentinel node redis['master_ip'] = '10.0.0.1' # ip of the initial master redis instance redis['master_port'] = 6379 # port of the initial master redis instance -redis['master_password'] = 'your-secure-password-here' # the same value defined in redis['password'] in the master instance +redis['master_password'] = 'redis-password-goes-here' # the same value defined in redis['password'] in the master instance ## Configure Sentinel +sentinel['bind'] = '0.0.0.0' # or specify an IP to bind to a single one # sentinel['port'] = 26379 # uncomment to change default port ## Quorum must reflect the amount of voting sentinels it take to start a failover. @@ -388,6 +386,48 @@ sentinel['quorum'] = 2 # sentinel['failover_timeout'] = 60000 ``` +In the example above we've used `redis_sentinel_role` and `redis_master_role` +which simplify the ammount of configuration changes. + +If you want more control, here is what each one sets for you automatically +when enabled: + +```ruby +## Redis Sentinel Role +redis_sentinel_role['enable'] = true + +# When Sentinel Role is enabled, the following services are enabled/disabled: +sentinel['enable'] = true + +# This others are disabled: +redis['enable'] = false +bootstrap['enable'] = false +nginx['enable'] = false +postgresql['enable'] = false +gitlab_rails['enable'] = false +mailroom['enable'] = false + +## Redis master/slave Role: +redis_master_role['enable'] = true # enable only one of them +redis_slave_role['enable'] = true # enable only one of them + +# When Redis Master or Slave role are enabled, the following services are enabled/disabled: +# (Note that if redis and sentinel roles are combined both services will be enabled) + +# When Sentinel Role is enabled, the following services are enabled/disabled: +redis['enable'] = true + +# This others are disabled: +sentinel['enable'] = false +bootstrap['enable'] = false +nginx['enable'] = false +postgresql['enable'] = false +gitlab_rails['enable'] = false +mailroom['enable'] = false + +# Redis Slave role also change this setting from default 'true' to 'false': +redis['master'] = false +``` --- The final part is to inform the main GitLab application server of the Redis -- cgit v1.2.1 From 494c2785fdc6a2f67edf20cbfc2106ffdba3ef28 Mon Sep 17 00:00:00 2001 From: Gabriel Mazetto Date: Tue, 8 Nov 2016 06:24:43 +0100 Subject: Fixed some documentation and moved Source install specific to other file. --- doc/administration/high_availability/redis.md | 135 +--------- .../high_availability/redis_source.md | 286 +++++++++++++++++++++ 2 files changed, 300 insertions(+), 121 deletions(-) create mode 100644 doc/administration/high_availability/redis_source.md diff --git a/doc/administration/high_availability/redis.md b/doc/administration/high_availability/redis.md index bfad3047385..f49bf1d4ab5 100644 --- a/doc/administration/high_availability/redis.md +++ b/doc/administration/high_availability/redis.md @@ -19,7 +19,6 @@ that comes bundled with Omnibus GitLab packages. - [Prerequisites](#prerequisites) - [Redis setup](#redis-setup) - [Existing single-machine installation](#existing-single-machine-installation) - - [Installation from source](#installation-from-source) - [Omnibus packages](#omnibus-packages) - [Configuring Sentinel](#configuring-sentinel) - [How sentinel handles a failover](#how-sentinel-handles-a-failover) @@ -31,7 +30,6 @@ that comes bundled with Omnibus GitLab packages. - [Redis replication](#redis-replication) - [Sentinel](#sentinel) - [Omnibus GitLab](#omnibus-gitlab) - - [Install from Source](#install-from-source) @@ -161,58 +159,6 @@ To disable redis in the single install, edit `/etc/gitlab/gitlab.rb`: redis['enable'] = false ``` -#### Installation from source - -**Configuring Master Redis instance** - -You need to make the following changes in `redis.conf`: - -1. Define a `bind` address pointing to a local IP that your other machines - can reach you. If you really need to bind to an external acessible IP, make - sure you add extra firewall rules to prevent unauthorized access: - - ```conf - # By default, if no "bind" configuration directive is specified, Redis listens - # for connections from all the network interfaces available on the server. - # It is possible to listen to just one or multiple selected interfaces using - # the "bind" configuration directive, followed by one or more IP addresses. - # - # Examples: - # - # bind 192.168.1.100 10.0.0.1 - # bind 127.0.0.1 ::1 - bind 0.0.0.0 # This will bind to all interfaces - ``` - -1. Define a `port` to force redis to listin on TCP so other machines can - connect to it: - - ```conf - # Accept connections on the specified port, default is 6379 (IANA #815344). - # If port 0 is specified Redis will not listen on a TCP socket. - port 6379 - ``` - -1. Set up password authentication (use the same password in all nodes) - - ```conf - requirepass "redis-password-goes-here" - masterauth "redis-password-goes-here" - ``` - -1. Restart the Redis services for the changes to take effect. - -**Configuring Slave Redis instance** - -1. Follow same instructions from master, with the extra change in `redis.conf`: - - ```conf - # IP and port of the master Redis server - slaveof 10.10.10.10 6379 - ``` - -1. Restart the Redis services for the changes to take effect. - #### Omnibus packages You need to install the Omnibus GitLab package in `3` independent machines. @@ -304,25 +250,16 @@ GitLab Enterprise Edition provides [automated way to setup and run](#sentinel-se #### Sentinel setup ##### Community Edition + With GitLab Community Edition, you need to install, configure, execute and monitor Sentinel from source. Omnibus GitLab Community Edition package does not support Sentinel configuration. -A minimal configuration file (`sentinel.conf`) should contain the following: - -```conf -bind 0.0.0.0 # bind to all interfaces or change to a specific IP -port 26379 # default sentinel port -sentinel auth-pass gitlab-redis redis-password-goes-here -sentinel monitor gitlab-redis 10.0.0.1 6379 2 -sentinel down-after-milliseconds gitlab-redis 10000 -sentinel config-epoch gitlab-redis 0 -sentinel leader-epoch gitlab-redis 0 -``` +See documentation for Source Install [here](redis_source.md). ##### Enterprise Edition -To setup sentinel, you edit `/etc/gitlab/gitlab.rb` file: +To setup sentinel, edit `/etc/gitlab/gitlab.rb` file: ```ruby @@ -336,7 +273,7 @@ redis_master_role['enable'] = true ## Enabled Sentinel and Redis Slave services redis_sentinel_role['enable'] = true -redis_master_role['enable'] = true +redis_slave_role['enable'] = true ## Configure Redis redis['master_name'] = 'gitlab-redis' # must be the same in every sentinel node @@ -345,7 +282,7 @@ redis['master_port'] = 6379 # port of the initial master redis instance redis['master_password'] = 'redis-password-goes-here' # the same value defined in redis['password'] in the master instance ## Configure Sentinel -sentinel['bind'] = '0.0.0.0' # or specify an IP to bind to a single one +# sentinel['bind'] = '0.0.0.0' # bind to all interfaces, uncomment to specify an IP and bind to a single one # sentinel['port'] = 26379 # uncomment to change default port ## Quorum must reflect the amount of voting sentinels it take to start a failover. @@ -435,24 +372,16 @@ master and the new sentinels servers. ### GitLab setup -You can enable or disable sentinel support at any time in new or existing +You can enable or disable Sentinel support at any time in new or existing installations. From the GitLab application perspective, all it requires is -the correct credentials for the master Redis and for all Sentinel nodes. +the correct credentials for the Sentinel nodes. -It doesn't require a list of all Sentinel nodes, as in case of a failure, -the application will need to query only one of them. +While it doesn't require a list of all Sentinel nodes, in case of a failure, +it needs to access at one of listed ones. >**Note:** -The following steps should be performed in the [GitLab application server](gitlab.md). - -**For source based installations** - -1. Edit `/home/git/gitlab/config/resque.yml` following the example in - `/home/git/gitlab/config/resque.yml.example`, and uncomment the sentinels - line, changing to the correct server credentials. -1. Restart GitLab for the changes to take effect. - -**For Omnibus installations** +The following steps should be performed in the [GitLab application server](gitlab.md) +which ideally should not have Redis or Sentinels in the same machine for a HA setup. 1. Edit `/etc/gitlab/gitlab.rb` and add/change the following lines: @@ -466,7 +395,7 @@ The following steps should be performed in the [GitLab application server](gitla ] ``` -1. [Reconfigure] the GitLab for the changes to take effect. +1. [Reconfigure] GitLab for the changes to take effect. ## Troubleshooting @@ -548,42 +477,6 @@ The way the redis connector `redis-rb` works with sentinel is a bit non-intuitive. We try to hide the complexity in omnibus, but it still requires a few extra configs. -#### Install from Source - -If you get an error like: `Redis::CannotConnectError: No sentinels available.`, -there may be something wrong with your configuration files or it can be related -to [this issue][gh-531]. - -It's a bit non-intuitive the way you have to config `resque.yml` and -`sentinel.conf`, otherwise `redis-rb` will not work properly. - -The `master-group-name` ('gitlab-redis') defined in (`sentinel.conf`) -**must** be used as the hostname in GitLab (`resque.yml` for source installations -or `gitlab-rails['redis_*']` in Omnibus): - -```conf -# sentinel.conf: -sentinel monitor gitlab-redis 10.10.10.10 6379 2 -sentinel down-after-milliseconds gitlab-redis 10000 -sentinel config-epoch gitlab-redis 0 -sentinel leader-epoch gitlab-redis 0 -``` - -```yaml -# resque.yaml -production: - url: redis://:myredispassword@gitlab-redis/ - sentinels: - - - host: slave1.example.com # or use ip - port: 26380 # point to sentinel, not to redis port - - - host: slave2.exampl.com # or use ip - port: 26381 # point to sentinel, not to redis port -``` - -When in doubt, please read [Redis Sentinel documentation](http://redis.io/topics/sentinel) - --- To make sure your configuration is correct: @@ -611,8 +504,8 @@ To make sure your configuration is correct: 1. To simulate a failover on master Redis, SSH into the Redis server and run: ```bash - # port must match your master redis port - redis-cli -h localhost -p 6379 DEBUG sleep 60 + # port must match your master redis port, and the sleep time must be a few seconds bigger than defined one + redis-cli -h localhost -p 6379 DEBUG sleep 20 ``` 1. Then back in the Rails console from the first step, run: diff --git a/doc/administration/high_availability/redis_source.md b/doc/administration/high_availability/redis_source.md new file mode 100644 index 00000000000..7e8c8c2b4b9 --- /dev/null +++ b/doc/administration/high_availability/redis_source.md @@ -0,0 +1,286 @@ +# Configuring Redis for GitLab HA (Source Install) + +We highly recommend that you use Omnibus GitLab packages, as we can optimize +required packages specifically for GitLab, and we will take care of upgrading +to the latest supported version. + +If you are building packages for a specific distro, or trying to build some +internal automation, you can check this documentation to learn about the +minimal setup, required changes, etc. + +If you want to see the documentation for Omnibus GitLab Install, please [read it here](redis.md). + + + +**Table of Contents** + +- [Configure your own Redis server](#configure-your-own-redis-server) + - [Configuring Master Redis instance](#configuring-master-redis-instance) + - [Configuring Slave Redis instances](#configuring-slave-redis-instances) + - [Configuring Redis Sentinel instances](#configuring-redis-sentinel-instances) +- [GitLab setup](#gitlab-setup) +- [Example configurations](#example-configurations) + - [Configuring Redis Master](#configuring-redis-master) + - [Configuring Redis Slaves](#configuring-redis-slaves) + - [Configuring Redis Sentinel](#configuring-redis-sentinel) +- [Troubleshooting](#troubleshooting) + + + +## Configure your own Redis server + +Redis server must be configured to use TCP connection instead of socket, +and since Redis `3.2`, you must define a password to receive external +connections (`requirepass`). + +You will also need to define equal password for slave password definition +(`masterauth`), in the same instance, if you are using Redis with Sentinel. + +To configure Redis to use TCP connection you need to define both +`bind` and `port`. You can bind to all interfaces (`0.0.0.0`) or specify the +ip of the desired interface (for ex. one from an internal network). + + +### Configuring Master Redis instance + +You need to make the following changes in `redis.conf`: + +1. Define a `bind` address pointing to a local IP that your other machines + can reach you. If you really need to bind to an external acessible IP, make + sure you add extra firewall rules to prevent unauthorized access: + +1. Define a `port` to force redis to listen on TCP so other machines can + connect to it (default port is `6379`). + +1. Set up password authentication (use the same password in all nodes). + The password should be defined equal for both `requirepass` and `masterauth` + when setting up Redis to use with Sentinel. + +1. Restart the Redis services for the changes to take effect. + +See [example configuration](#configuring-redis-master) below. + +### Configuring Slave Redis instances + +1. Follow same instructions for Redis Master + +1. Define `slaveof` pointing to the Redis master instance with **IP** and **port**. + +1. Restart the Redis services for the changes to take effect. + +See [example configuration](#configuring-redis-slaves) below. + +### Configuring Redis Sentinel instances + +Sentinel is a special type of Redis server. It inherits most of the basic +configuration options you can define in `redis.conf`, with specific ones +starting with `sentinel` prefix. + +You will need to define the initial configs to enable connectivity: + +1. Define a `bind` address pointing to a local IP that your other machines + can reach you. If you really need to bind to an external acessible IP, make + sure you add extra firewall rules to prevent unauthorized access: + +1. Define a `port` to force sentinel to listen on TCP so other machines can + connect to it (default port is `26379`). + +And the sentinel specific ones: + +1. Define with `sentinel auth-pass` the same shared password you have + defined for both Redis **Master** and **Slaves** instances. + +1. Define with `sentinel monitor` the **IP** and **port** of the Redis + **Master** node, and the **quorum** required to start a failover. + If you need more information to understand about quorum, please + read the detailed explanation in the [HA documentation for Omnibus Installs](redis.md). + +1. Define with `sentinel down-after-milliseconds` the ammount in `ms` of time + that an unresponsive server will be considered down. + +1. Define a value for `sentinel failover_timeout` in `ms`. This has multiple + meanings: + + * The time needed to re-start a failover after a previous failover was + already tried against the same master by a given Sentinel, is two + times the failover timeout. + + * The time needed for a slave replicating to a wrong master according + to a Sentinel current configuration, to be forced to replicate + with the right master, is exactly the failover timeout (counting since + the moment a Sentinel detected the misconfiguration). + + * The time needed to cancel a failover that is already in progress but + did not produced any configuration change (SLAVEOF NO ONE yet not + acknowledged by the promoted slave). + + * The maximum time a failover in progress waits for all the slaves to be + reconfigured as slaves of the new master. However even after this time + the slaves will be reconfigured by the Sentinels anyway, but not with + the exact parallel-syncs progression as specified. + +See [example configuration](#configuring-redis-sentinel) below. + +## GitLab setup + +You can enable or disable Sentinel support at any time in new or existing +installations. From the GitLab application perspective, all it requires is +the correct credentials for the Sentinel nodes. + +While it doesn't require a list of all Sentinel nodes, in case of a failure, +it needs to access at one of listed ones. + +>**Note:** +The following steps should be performed in the [GitLab application server](gitlab.md) +which ideally should not have Redis or Sentinels in the same machine for a HA setup. + +1. Edit `/home/git/gitlab/config/resque.yml` following the example in + `/home/git/gitlab/config/resque.yml.example`, and uncomment the sentinels + lines, pointing to the correct server credentials. + +1. Restart GitLab for the changes to take effect. + +## Example configurations + +In this example we consider that all servers have an internal network +interface with IPs in the `10.0.0.x` range, and that they can connect +to each other using these IPs. + +In a real world usage, you would also setup firewall rules to prevent +unauthorized access from other machines, and block traffic from the +outside (Internet). + +We will use the same `3` nodes with **Redis** + **Sentinel** topology +discussed in the [Configuring Redis for GitLab HA](redis.md) documentation. + +Here is a list and description of each **machine** and the assined **ip**: + +* `10.0.0.1`: Redis Master + Sentinel 1 +* `10.0.0.2`: Redis Slave 1 + Sentinel 2 +* `10.0.0.2`: Redis Slave 2 + Sentinel 3 + +Please note that after the initial configuration, if a failover is initiated +by the Sentinel nodes, the Redis nodes will be reconfigured and the **Master** +will change permanently (including in `redis.conf`) from one node to the other, +until a new failover is initiated again. + +The same thing will happen with `sentinel.conf` that will be overriten after the +initial execution, after any new sentinel node starts watching the **Master**, +or a failover promotes a different **Master** node. + +### Configuring Redis Master + +`redis.conf`: + +```conf +bind 10.0.0.1 +port 6379 +requirepass redis-password-goes-here +masterauth redis-password-goes-here +``` + +### Configuring Redis Slaves + +**Slave 1 - `redis.conf`:** + +```conf +bind 10.0.0.2 +port 6379 +requirepass redis-password-goes-here +masterauth redis-password-goes-here + +# IP and port of the master Redis server +slaveof 10.0.0.1 6379 +``` + +**Slave 2 - `redis.conf`:** + +```conf +bind 10.0.0.3 +port 6379 +requirepass redis-password-goes-here +masterauth redis-password-goes-here + +# IP and port of the master Redis server +slaveof 10.0.0.1 6379 +``` + +### Configuring Redis Sentinel + +For this example, **Sentinel 1** will be configured in the same machine as the +**Redis Master**, **Sentinel 2** and **Sentinel 3** in the same machines as the +**Slave 1** and **Slave 2** respectively. + +Sentinel 1 - `sentinel.conf` + +```conf +bind 10.0.0.1 +port 26379 +sentinel auth-pass gitlab-redis redis-password-goes-here +sentinel monitor gitlab-redis 10.0.0.1 6379 2 +sentinel down-after-milliseconds gitlab-redis 10000 +sentinel failover_timeout 30000 +``` + +Sentinel 2 - `sentinel.conf` + +```conf +bind 10.0.0.2 +port 26379 +sentinel auth-pass gitlab-redis redis-password-goes-here +sentinel monitor gitlab-redis 10.0.0.1 6379 2 +sentinel down-after-milliseconds gitlab-redis 10000 +sentinel failover_timeout 30000 +``` + +Sentinel 3 - `sentinel.conf` + +```conf +bind 10.0.0.3 +port 26379 +sentinel auth-pass gitlab-redis redis-password-goes-here +sentinel monitor gitlab-redis 10.0.0.1 6379 2 +sentinel down-after-milliseconds gitlab-redis 10000 +sentinel failover_timeout 30000 +``` + +## Troubleshooting + +We have a more detailed [Troubleshooting](redis.md#troubleshooting) explained in the documentation for Omnibus +Install. Here we will list only the things that are specific to a **Source** install. + +If you get an error in GitLab like: `Redis::CannotConnectError: No sentinels available.`, +there may be something wrong with your configuration files or it can be related +to [this issue][gh-531]. + +It's a bit non-intuitive the way you have to config `resque.yml` and +`sentinel.conf`, otherwise `redis-rb` will not work properly. + +The `master-group-name` ('gitlab-redis') defined in (`sentinel.conf`) +**must** be used as the hostname in GitLab (`resque.yml`): + +```conf +# sentinel.conf: +sentinel monitor gitlab-redis 10.0.0.1 6379 2 +sentinel down-after-milliseconds gitlab-redis 10000 +sentinel config-epoch gitlab-redis 0 +sentinel leader-epoch gitlab-redis 0 +``` + +```yaml +# resque.yaml +production: + url: redis://:myredispassword@gitlab-redis/ + sentinels: + - + host: 10.0.0.1 + port: 26379 # point to sentinel, not to redis port + - + host: 10.0.0.2 + port: 26379 # point to sentinel, not to redis port + - + host: 10.0.0.3 + port: 26379 # point to sentinel, not to redis port +``` + +When in doubt, please read [Redis Sentinel documentation](http://redis.io/topics/sentinel) -- cgit v1.2.1 From 903431e8895ba196edc7ce67d46279d0d06cba1d Mon Sep 17 00:00:00 2001 From: the-undefined Date: Mon, 7 Nov 2016 08:01:27 +0000 Subject: Move 'Explore Snippets' Spinach feature to Rspec This commit moves the `snippets/discover.feature` Spinach test to a Rspec feature, as part of deprecating the Spinach test suite. The original feature was called 'Discover Snippets', but the UI no longer reflects this wording. The new Rspec feature is called 'Explore Snippets' to reflect UI/Controller/View naming in use. - Remove Spinach discover snippets feature and steps - Add Rspec feature test --- features/snippets/discover.feature | 13 ------------- features/steps/snippets/discover.rb | 21 --------------------- spec/features/snippets/explore_spec.rb | 16 ++++++++++++++++ 3 files changed, 16 insertions(+), 34 deletions(-) delete mode 100644 features/snippets/discover.feature delete mode 100644 features/steps/snippets/discover.rb create mode 100644 spec/features/snippets/explore_spec.rb diff --git a/features/snippets/discover.feature b/features/snippets/discover.feature deleted file mode 100644 index 1a7e132ea25..00000000000 --- a/features/snippets/discover.feature +++ /dev/null @@ -1,13 +0,0 @@ -@snippets -Feature: Snippets Discover - Background: - Given I sign in as a user - And I have public "Personal snippet one" snippet - And I have private "Personal snippet private" snippet - And I have internal "Personal snippet internal" snippet - - Scenario: I should see snippets - Given I visit snippets page - Then I should see "Personal snippet one" in snippets - And I should see "Personal snippet internal" in snippets - And I should not see "Personal snippet private" in snippets diff --git a/features/steps/snippets/discover.rb b/features/steps/snippets/discover.rb deleted file mode 100644 index 76379d09d02..00000000000 --- a/features/steps/snippets/discover.rb +++ /dev/null @@ -1,21 +0,0 @@ -class Spinach::Features::SnippetsDiscover < Spinach::FeatureSteps - include SharedAuthentication - include SharedPaths - include SharedSnippet - - step 'I should see "Personal snippet one" in snippets' do - expect(page).to have_content "Personal snippet one" - end - - step 'I should see "Personal snippet internal" in snippets' do - expect(page).to have_content "Personal snippet internal" - end - - step 'I should not see "Personal snippet private" in snippets' do - expect(page).not_to have_content "Personal snippet private" - end - - def snippet - @snippet ||= PersonalSnippet.find_by!(title: "Personal snippet one") - end -end diff --git a/spec/features/snippets/explore_spec.rb b/spec/features/snippets/explore_spec.rb new file mode 100644 index 00000000000..10a4597e467 --- /dev/null +++ b/spec/features/snippets/explore_spec.rb @@ -0,0 +1,16 @@ +require 'rails_helper' + +feature 'Explore Snippets', feature: true do + scenario 'User should see snippets that are not private' do + public_snippet = create(:personal_snippet, :public) + internal_snippet = create(:personal_snippet, :internal) + private_snippet = create(:personal_snippet, :private) + + login_as create(:user) + visit explore_snippets_path + + expect(page).to have_content(public_snippet.title) + expect(page).to have_content(internal_snippet.title) + expect(page).not_to have_content(private_snippet.title) + end +end -- cgit v1.2.1 From 04a8372cdfff501fe67c17612a0b5e098f7d56c9 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Tue, 8 Nov 2016 14:20:58 +0100 Subject: Add environment teardown service --- app/services/ci/stop_environment_service.rb | 31 ++++++++++++++++++++ app/services/delete_branch_service.rb | 2 ++ spec/services/ci/stop_environment_service_spec.rb | 35 +++++++++++++++++++++++ 3 files changed, 68 insertions(+) create mode 100644 app/services/ci/stop_environment_service.rb create mode 100644 spec/services/ci/stop_environment_service_spec.rb diff --git a/app/services/ci/stop_environment_service.rb b/app/services/ci/stop_environment_service.rb new file mode 100644 index 00000000000..5c208b738f7 --- /dev/null +++ b/app/services/ci/stop_environment_service.rb @@ -0,0 +1,31 @@ +module Ci + class StopEnvironmentService < BaseService + def execute(ref) + @ref = ref + @commit = project.commit(ref) + + return unless has_ref_sha_pair? + return unless has_environments? + + environments.each do |environment| + next unless environment.stoppable? + + environment.stop!(current_user) + end + end + + private + + def has_ref_sha_pair? + @ref && @commit + end + + def has_environments? + environments.any? + end + + def environments + @environments ||= project.environments_for(@ref, @commit) + end + end +end diff --git a/app/services/delete_branch_service.rb b/app/services/delete_branch_service.rb index 3e5dd4ebb86..ec8ee7452d6 100644 --- a/app/services/delete_branch_service.rb +++ b/app/services/delete_branch_service.rb @@ -21,6 +21,8 @@ class DeleteBranchService < BaseService return error('You dont have push access to repo', 405) end + # StopEnvironmentService + if repository.rm_branch(current_user, branch_name) success('Branch was removed') else diff --git a/spec/services/ci/stop_environment_service_spec.rb b/spec/services/ci/stop_environment_service_spec.rb new file mode 100644 index 00000000000..40b2a028f67 --- /dev/null +++ b/spec/services/ci/stop_environment_service_spec.rb @@ -0,0 +1,35 @@ +require 'spec_helper' + +describe Ci::StopEnvironmentService, services: true do + let(:project) { create(:project) } + let(:user) { create(:user) } + + let(:service) { described_class.new(project, user) } + + describe '#execute' do + context 'when environment exists' do + let(:environment) { create(:environment, project: project) } + let(:deployable) { create(:ci_build) } + + let(:stop_build) do + create(:ci_build, :manual, name: 'environment/teardown', + pipeline: deployable.pipeline) + end + + before do + create(:deployment, environment: environment, + deployable: deployable, + on_stop: stop_build.name, + user: user, + project: project, + sha: project.commit.id) + end + + it 'stops environment' do + expect_any_instance_of(Environment).to receive(:stop!) + + service.execute('master') + end + end + end +end -- cgit v1.2.1 From f7ef7bea11012bef6d373ce3b3c43fac08b047fd Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Wed, 9 Nov 2016 09:42:09 +0100 Subject: Add environment factory review app trait --- app/services/ci/stop_environment_service.rb | 19 ++++++++++++------- spec/factories/deployments.rb | 3 ++- spec/factories/environments.rb | 23 +++++++++++++++++++++++ spec/services/ci/stop_environment_service_spec.rb | 15 +-------------- 4 files changed, 38 insertions(+), 22 deletions(-) diff --git a/app/services/ci/stop_environment_service.rb b/app/services/ci/stop_environment_service.rb index 5c208b738f7..2ac0b3d885a 100644 --- a/app/services/ci/stop_environment_service.rb +++ b/app/services/ci/stop_environment_service.rb @@ -1,10 +1,11 @@ module Ci class StopEnvironmentService < BaseService - def execute(ref) - @ref = ref - @commit = project.commit(ref) + attr_reader :ref - return unless has_ref_sha_pair? + def execute(branch_name) + @ref = branch_name + + return unless has_ref_commit_pair? return unless has_environments? environments.each do |environment| @@ -16,8 +17,12 @@ module Ci private - def has_ref_sha_pair? - @ref && @commit + def has_ref_commit_pair? + ref && commit + end + + def commit + @commit ||= project.commit(ref) end def has_environments? @@ -25,7 +30,7 @@ module Ci end def environments - @environments ||= project.environments_for(@ref, @commit) + @environments ||= project.environments_for(ref, commit) end end end diff --git a/spec/factories/deployments.rb b/spec/factories/deployments.rb index 6f24bf58d14..29ad1af9fd9 100644 --- a/spec/factories/deployments.rb +++ b/spec/factories/deployments.rb @@ -3,8 +3,9 @@ FactoryGirl.define do sha '97de212e80737a608d939f648d959671fb0a0142' ref 'master' tag false + user project nil - + deployable factory: :ci_build environment factory: :environment after(:build) do |deployment, evaluator| diff --git a/spec/factories/environments.rb b/spec/factories/environments.rb index 846cccfc7fa..bb6558a403f 100644 --- a/spec/factories/environments.rb +++ b/spec/factories/environments.rb @@ -4,5 +4,28 @@ FactoryGirl.define do project factory: :empty_project sequence(:external_url) { |n| "https://env#{n}.example.gitlab.com" } + + trait :with_review_app do |environment| + project + + # At this point `review app` is an ephemeral concept related to + # deployments being deployed for given environment. There is no + # first-class `review app` available so we need to create set of + # interconnected objects to simulate a review app. + # + after(:create) do |environment| + deployment = create(:deployment, + environment: environment, + project: environment.project, + sha: environment.project.commit.id) + + teardown_build = create(:ci_build, :manual, + name: "#{deployment.environment.name}:teardown", + pipeline: deployment.deployable.pipeline) + + deployment.update_column(:on_stop, teardown_build.name) + environment.update_attribute(:deployments, [deployment]) + end + end end end diff --git a/spec/services/ci/stop_environment_service_spec.rb b/spec/services/ci/stop_environment_service_spec.rb index 40b2a028f67..05e9e2d84cf 100644 --- a/spec/services/ci/stop_environment_service_spec.rb +++ b/spec/services/ci/stop_environment_service_spec.rb @@ -8,21 +8,8 @@ describe Ci::StopEnvironmentService, services: true do describe '#execute' do context 'when environment exists' do - let(:environment) { create(:environment, project: project) } - let(:deployable) { create(:ci_build) } - - let(:stop_build) do - create(:ci_build, :manual, name: 'environment/teardown', - pipeline: deployable.pipeline) - end - before do - create(:deployment, environment: environment, - deployable: deployable, - on_stop: stop_build.name, - user: user, - project: project, - sha: project.commit.id) + create(:environment, :with_review_app, project: project) end it 'stops environment' do -- cgit v1.2.1 From ae8a461d875b29d7492230df1a3ca419707e154d Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Wed, 9 Nov 2016 13:16:21 +0100 Subject: Fix environment feature specs after changes in factory --- spec/features/environments_spec.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/spec/features/environments_spec.rb b/spec/features/environments_spec.rb index b565586ee14..10ca835e6ca 100644 --- a/spec/features/environments_spec.rb +++ b/spec/features/environments_spec.rb @@ -152,7 +152,9 @@ feature 'Environments', feature: true do end context 'with deployments' do - given(:deployment) { create(:deployment, environment: environment) } + given(:deployment) do + create(:deployment, environment: environment, deployable: nil) + end scenario 'does show deployment SHA' do expect(page).to have_link(deployment.short_sha) -- cgit v1.2.1 From 45bd5391d7f20715ae4de7a4a5cd9233ab598c28 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Wed, 9 Nov 2016 13:31:33 +0100 Subject: Extend tests for service that stops environment --- spec/services/ci/stop_environment_service_spec.rb | 50 ++++++++++++++++++++++- 1 file changed, 49 insertions(+), 1 deletion(-) diff --git a/spec/services/ci/stop_environment_service_spec.rb b/spec/services/ci/stop_environment_service_spec.rb index 05e9e2d84cf..047f9b0b2ca 100644 --- a/spec/services/ci/stop_environment_service_spec.rb +++ b/spec/services/ci/stop_environment_service_spec.rb @@ -7,7 +7,7 @@ describe Ci::StopEnvironmentService, services: true do let(:service) { described_class.new(project, user) } describe '#execute' do - context 'when environment exists' do + context 'when environment with review app exists' do before do create(:environment, :with_review_app, project: project) end @@ -17,6 +17,54 @@ describe Ci::StopEnvironmentService, services: true do service.execute('master') end + + context 'when specified branch does not exist' do + it 'does not stop environment' do + expect_any_instance_of(Environment).not_to receive(:stop!) + + service.execute('non/existent/branch') + end + end + + context 'when no branch not specified' do + it 'does not stop environment' do + expect_any_instance_of(Environment).not_to receive(:stop!) + + service.execute(nil) + end + end + + context 'when environment is not stoppable' do + before do + allow_any_instance_of(Environment) + .to receive(:stoppable?).and_return(false) + end + + it 'does not stop environment' do + expect_any_instance_of(Environment).not_to receive(:stop!) + + service.execute('master') + end + end + end + + context 'when there is no environment associated with review app' do + before do + create(:environment, project: project) + end + + it 'does not stop environment' do + expect_any_instance_of(Environment).not_to receive(:stop!) + + service.execute('master') + end + end + + context 'when environment does not exist' do + it 'does not raise error' do + expect { service.execute('master') } + .not_to raise_error + end end end end -- cgit v1.2.1 From 5328e3b1276d8eef15b6636a1d5b1c7a57d31ea6 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Wed, 9 Nov 2016 14:46:36 +0100 Subject: Make commit an optional arg for environments search --- app/models/merge_request.rb | 15 +++++++++------ app/models/project.rb | 23 ++++++++++++----------- app/services/ci/stop_environment_service.rb | 12 ++++-------- spec/models/project_spec.rb | 23 +++++++++++++++++------ 4 files changed, 42 insertions(+), 31 deletions(-) diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb index d76feb9680e..9d3eab52189 100644 --- a/app/models/merge_request.rb +++ b/app/models/merge_request.rb @@ -692,12 +692,15 @@ class MergeRequest < ActiveRecord::Base def environments return [] unless diff_head_commit - @environments ||= - begin - envs = target_project.environments_for(target_branch, diff_head_commit, with_tags: true) - envs.concat(source_project.environments_for(source_branch, diff_head_commit)) if source_project - envs.uniq - end + @environments ||= begin + target_envs = target_project.environments_for( + target_branch, commit: diff_head_commit, with_tags: true) + + source_envs = source_project.environments_for( + source_branch, commit: diff_head_commit) if source_project + + (target_envs.to_a + source_envs.to_a).uniq + end end def state_human_name diff --git a/app/models/project.rb b/app/models/project.rb index 4c9c7c001dd..64e707233f4 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -1288,19 +1288,20 @@ class Project < ActiveRecord::Base Gitlab::Redis.with { |redis| redis.del(pushes_since_gc_redis_key) } end - def environments_for(ref, commit, with_tags: false) - environment_ids = deployments.group(:environment_id). - select(:environment_id) + def environments_for(ref, commit: nil, with_tags: false) + environments_query = with_tags ? 'ref=? OR tag IS TRUE' : 'ref=?' - environment_ids = - if with_tags - environment_ids.where('ref=? OR tag IS TRUE', ref) - else - environment_ids.where(ref: ref) - end + environment_ids = deployments + .group(:environment_id) + .select(:environment_id) + .where(environments_query, ref) - environments.available.where(id: environment_ids).select do |environment| - environment.includes_commit?(commit) + envs = environments.available.where(id: environment_ids) + + if commit + envs.select { |env| env.includes_commit?(commit) } + else + envs.to_a end end diff --git a/app/services/ci/stop_environment_service.rb b/app/services/ci/stop_environment_service.rb index 2ac0b3d885a..cb49f71c5a3 100644 --- a/app/services/ci/stop_environment_service.rb +++ b/app/services/ci/stop_environment_service.rb @@ -5,7 +5,7 @@ module Ci def execute(branch_name) @ref = branch_name - return unless has_ref_commit_pair? + return unless has_ref? return unless has_environments? environments.each do |environment| @@ -17,12 +17,8 @@ module Ci private - def has_ref_commit_pair? - ref && commit - end - - def commit - @commit ||= project.commit(ref) + def has_ref? + @ref.present? end def has_environments? @@ -30,7 +26,7 @@ module Ci end def environments - @environments ||= project.environments_for(ref, commit) + @environments ||= project.environments_for(@ref) end end end diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index 0810d06b50f..d835eac34c2 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -1646,15 +1646,18 @@ describe Project, models: true do end it 'returns environment when with_tags is set' do - expect(project.environments_for('master', project.commit, with_tags: true)).to contain_exactly(environment) + expect(project.environments_for('master', commit: project.commit, with_tags: true)) + .to contain_exactly(environment) end it 'does not return environment when no with_tags is set' do - expect(project.environments_for('master', project.commit)).to be_empty + expect(project.environments_for('master', commit: project.commit)) + .to be_empty end it 'does not return environment when commit is not part of deployment' do - expect(project.environments_for('master', project.commit('feature'))).to be_empty + expect(project.environments_for('master', commit: project.commit('feature'))) + .to be_empty end end @@ -1664,15 +1667,23 @@ describe Project, models: true do end it 'returns environment when ref is set' do - expect(project.environments_for('master', project.commit)).to contain_exactly(environment) + expect(project.environments_for('master', commit: project.commit)) + .to contain_exactly(environment) end it 'does not environment when ref is different' do - expect(project.environments_for('feature', project.commit)).to be_empty + expect(project.environments_for('feature', commit: project.commit)) + .to be_empty end it 'does not return environment when commit is not part of deployment' do - expect(project.environments_for('master', project.commit('feature'))).to be_empty + expect(project.environments_for('master', commit: project.commit('feature'))) + .to be_empty + end + + it 'returns environment when commit constraint is not set' do + expect(project.environments_for('master')) + .to contain_exactly(environment) end end end -- cgit v1.2.1 From 4a7fcc2af6eba65dff48b25c81d5925311fa933d Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Wed, 9 Nov 2016 15:29:19 +0100 Subject: Stop environments for branch after branch removal --- app/models/project.rb | 4 ++-- app/models/repository.rb | 15 +++++++++++++-- app/services/delete_branch_service.rb | 2 -- app/services/git_push_service.rb | 2 +- lib/gitlab/github_import/importer.rb | 2 +- spec/models/repository_spec.rb | 13 ++++++++++++- 6 files changed, 29 insertions(+), 9 deletions(-) diff --git a/app/models/project.rb b/app/models/project.rb index 64e707233f4..2f4fb0d082d 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -1289,12 +1289,12 @@ class Project < ActiveRecord::Base end def environments_for(ref, commit: nil, with_tags: false) - environments_query = with_tags ? 'ref=? OR tag IS TRUE' : 'ref=?' + environments_query = with_tags ? 'ref = ? OR tag IS TRUE' : 'ref = ?' environment_ids = deployments .group(:environment_id) .select(:environment_id) - .where(environments_query, ref) + .where(environments_query, ref.to_s) envs = environments.available.where(id: environment_ids) diff --git a/app/models/repository.rb b/app/models/repository.rb index 30be7262438..38e5cd3faa9 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -203,7 +203,7 @@ class Repository update_ref!(ref, newrev, oldrev) end - after_remove_branch + after_remove_branch(user, branch_name) true end @@ -524,7 +524,12 @@ class Repository end # Runs code after an existing branch has been removed. - def after_remove_branch + def after_remove_branch(user, branch_name) + expire_branch_cache_after_removal + stop_environments_for_branch(user, branch_name) + end + + def expire_branch_cache_after_removal expire_has_visible_content_cache expire_branch_count_cache expire_branches_cache @@ -1165,4 +1170,10 @@ class Repository def repository_event(event, tags = {}) Gitlab::Metrics.add_event(event, { path: path_with_namespace }.merge(tags)) end + + def stop_environments_for_branch(user, branch_name) + Ci::StopEnvironmentService + .new(@project, user) + .execute(branch_name) + end end diff --git a/app/services/delete_branch_service.rb b/app/services/delete_branch_service.rb index ec8ee7452d6..3e5dd4ebb86 100644 --- a/app/services/delete_branch_service.rb +++ b/app/services/delete_branch_service.rb @@ -21,8 +21,6 @@ class DeleteBranchService < BaseService return error('You dont have push access to repo', 405) end - # StopEnvironmentService - if repository.rm_branch(current_user, branch_name) success('Branch was removed') else diff --git a/app/services/git_push_service.rb b/app/services/git_push_service.rb index de313095bed..ec1c2f61c27 100644 --- a/app/services/git_push_service.rb +++ b/app/services/git_push_service.rb @@ -21,7 +21,7 @@ class GitPushService < BaseService @project.repository.after_push_commit(branch_name, params[:newrev]) if push_remove_branch? - @project.repository.after_remove_branch + @project.repository.after_remove_branch(current_user, branch_name) @push_commits = [] elsif push_to_new_branch? @project.repository.after_create_branch diff --git a/lib/gitlab/github_import/importer.rb b/lib/gitlab/github_import/importer.rb index 90cf38a8513..c724577ae89 100644 --- a/lib/gitlab/github_import/importer.rb +++ b/lib/gitlab/github_import/importer.rb @@ -111,7 +111,7 @@ module Gitlab end end - project.repository.after_remove_branch + project.repository.expire_branch_cache_after_removal end def restore_source_branch(pull_request) diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb index 04b7d19d414..6d65f6ead12 100644 --- a/spec/models/repository_spec.rb +++ b/spec/models/repository_spec.rb @@ -1182,7 +1182,18 @@ describe Repository, models: true do it 'flushes the visible content cache' do expect(repository).to receive(:expire_has_visible_content_cache) - repository.after_remove_branch + repository.after_remove_branch(user, 'master') + end + + context 'when there is environment with review app available for branch' do + before do + create(:environment, :with_review_app, project: project) + end + + it 'stops environment' do + expect_any_instance_of(Environment).to receive(:stop!) + repository.after_remove_branch(user, 'master') + end end end -- cgit v1.2.1 From 1e4f86744627fbf0d0190c7d76f30cd77480db37 Mon Sep 17 00:00:00 2001 From: Gabriel Mazetto Date: Thu, 10 Nov 2016 07:07:30 +0100 Subject: Remade documentation for Redis HA with Omnibus --- doc/administration/high_availability/redis.md | 648 +++++++++++++++------ .../high_availability/redis_source.md | 14 +- 2 files changed, 480 insertions(+), 182 deletions(-) diff --git a/doc/administration/high_availability/redis.md b/doc/administration/high_availability/redis.md index f49bf1d4ab5..6b16f9e791f 100644 --- a/doc/administration/high_availability/redis.md +++ b/doc/administration/high_availability/redis.md @@ -1,9 +1,14 @@ # Configuring Redis for GitLab HA -You can choose to install and manage Redis yourself, or you can use the one +High Availability with Redis is possible using a **Master** x **Slave** +topology with **Sentinel** service to watch and automatically start +failover proceedings. + +You can choose to install and manage Redis and Sentinel yourself, use +a hosted, managed clouse solution or you can use or you can use the one that comes bundled with Omnibus GitLab packages. -> **Note:** Redis does not require authentication by default. See +> **Note:** Redis requires authentication for High Availability. See [Redis Security](http://redis.io/topics/security) documentation for more information. We recommend using a combination of a Redis password and tight firewall rules to secure your Redis service. @@ -12,20 +17,26 @@ that comes bundled with Omnibus GitLab packages. **Table of Contents** -- [Configure your own Redis server](#configure-your-own-redis-server) -- [Configure Redis using Omnibus](#configure-redis-using-omnibus) -- [Experimental Redis Sentinel support](#experimental-redis-sentinel-support) -- [Redis Sentinel support](#redis-sentinel-support) +- [Using an external Redis server](#using-an-external-redis-server) +- [High Availability with Sentinel](#high-availability-with-sentinel) - [Prerequisites](#prerequisites) - [Redis setup](#redis-setup) - - [Existing single-machine installation](#existing-single-machine-installation) - - [Omnibus packages](#omnibus-packages) - - [Configuring Sentinel](#configuring-sentinel) - - [How sentinel handles a failover](#how-sentinel-handles-a-failover) - - [Sentinel setup](#sentinel-setup) + - [Experimental Redis Sentinel support](#experimental-redis-sentinel-support) + - [Sentinel setup](#sentinel-setup) + - [Recommended setup](#recommended-setup) +- [Configuring instances using Omnibus](#configuring-instances-using-omnibus) + - [Existing single-machine installation](#existing-single-machine-installation) + - [Configuring Master Redis instance](#configuring-master-redis-instance) + - [Configuring Slave Redis instances](#configuring-slave-redis-instances) + - [Configuring Sentinel instances](#configuring-sentinel-instances) - [Community Edition](#community-edition) - [Enterprise Edition](#enterprise-edition) - [GitLab setup](#gitlab-setup) +- [Example Configurations](#example-configurations) + - [Configuration for Redis Master](#configuration-for-redis-master) + - [Configuration for Redis Slave](#configuration-for-redis-slave) + - [Configuration for Sentinel (EE only)](#configuration-for-sentinel-ee-only) + - [Control running services](#control-running-services) - [Troubleshooting](#troubleshooting) - [Redis replication](#redis-replication) - [Sentinel](#sentinel) @@ -33,75 +44,57 @@ that comes bundled with Omnibus GitLab packages. -## Configure your own Redis server +## Using an external Redis server If you're hosting GitLab on a cloud provider, you can optionally use a managed service for Redis. For example, AWS offers a managed ElastiCache service that runs Redis. -## Configure Redis using Omnibus +Managed services can provide High Availability using their own proprietary +technology and provide a transparent proxy, which means that GitLab doesn't +need any additional change, or will use Sentinel and manage it for you. -If you don't want to bother setting up your own Redis server, you can use the -one bundled with Omnibus. In this case, you should disable all services except -Redis. +If your provider, uses Sentinel method, see [GitLab Setup](#gitlab-setup) +to understant where you need to provide the list of servers and credentials. -1. Download/install Omnibus GitLab using **steps 1 and 2** from - [GitLab downloads](https://about.gitlab.com/downloads). Do not complete other - steps on the download page. -1. Create/edit `/etc/gitlab/gitlab.rb` and use the following configuration. - Be sure to change the `external_url` to match your eventual GitLab front-end - URL: +If you want to setup Redis by yourself, without using Omnibus, you can +read our documentation: [Configuring Redis for GitLab HA (Source Install)](redis_source.md) - ```ruby - external_url 'https://gitlab.example.com' - - # Disable all services except Redis - redis['enable'] = true - bootstrap['enable'] = false - nginx['enable'] = false - postgresql['enable'] = false - gitlab_rails['enable'] = false - - # Redis configuration - redis['port'] = 6379 - redis['bind'] = '0.0.0.0' - redis['password'] = 'redis-password-goes-here' - ``` +## High Availability with Sentinel -1. Run `sudo gitlab-ctl reconfigure` to install and configure Redis. +> Since GitLab `8.11`, you can configure a list of Redis Sentinel servers that +will monitor a group of Redis servers to provide failover support. - > **Note**: This `reconfigure` step will result in some errors. - That's OK - don't be alarmed. +> With GitLab `8.14`, we bundled Redis Sentinel as part of Omnibus package and +improved the way you use and configure it. -1. Run `touch /etc/gitlab/skip-auto-migrations` to prevent database migrations - from running on upgrade. Only the primary GitLab application server should - handle migrations. +High Availability with Redis requires a few things: -## Experimental Redis Sentinel support +- Multiple Redis instances +- Run Redis in a **Master** x **Slave** topology +- Multiple Sentinel instances +- Application support and visiblity to all Sentinel and Redis instances - > Experimental Redis Sentinel support was [Introduced][ce-1877] in GitLab 8.11. - Starting with 8.14, Redis Sentinel is no longer experimental. - If you used with versions `< 8.14` before, please check the updated - documentation below. +Redis Sentinel can handle the most important tasks in a HA environment to help +keep servers online with minimal to no downtime: -## Redis Sentinel support +- Monitors **Master** and **Slaves** instances to see if they are available +- Promote a **Slave** to **Master** when the **Master** fails. +- Demote a **Master** to **Slave** when failed **Master** comes back online (to prevent + data-partitioning). +- Can be queried by clients to always connect to the current **Master** server. -Since GitLab 8.11, you can configure a list of Redis Sentinel servers that -will monitor a group of Redis servers to provide you with a standard failover -support. +When a **Master** fails to respond, it's the client responsability to handle timeout +and reconnect (querying a **Sentinel** for a new **Master**). To get a better understanding on how to correctly setup Sentinel, please read the [Redis Sentinel documentation](http://redis.io/topics/sentinel) first, as -failing to configure it correctly can lead to data loss. +failing to configure it correctly can lead to data loss, or can bring your +whole cluster down, invalidating the failover effort. -Redis Sentinel can handle the most important tasks in a HA environment to help -keep servers online with minimal to no downtime: - -- Monitors master and slave instances to see if they are available -- Promote a slave to master when the master fails. -- Demote a master to slave when failed master comes back online (to prevent - data-partitioning). -- Can be queried by clients to always connect to the correct master server. +This documentation will provide you with a minimal and a recommended topology +that can resist to some levels of failure. Usually the more Redis and Sentinel +instances you have provisioned, the better will be your availability. The configuration consists of three parts: @@ -112,44 +105,191 @@ The configuration consists of three parts: ### Prerequisites You need at least `3` independent machines: physical, or VMs running into -distinct physical machines. +distinct physical machines. They must be believed to fail in an +independent way. If you fail to provision the machines in that specific way, any issue with the shared environment can bring your entire setup down. -Read carefully how to configure those components below. +You also need to take in consideration the underlying network topology, +making sure you have redundant connectivity between Redis / Sentinel and +GitLab instances, otherwhise the networks will become a single point of +failure. + +Read carefully how to configure the components below. ### Redis setup -You must have at least `3` Redis servers: `1` Master, `2` Slaves, and they need to -be each in a independent machine (see explanation above). +You must have at least `3` Redis servers: `1` Master, `2` Slaves, and they +need to be each in a independent machine (see explanation above). -They should be configured the same way and with similar server specs, as -in a failover situation, any `Slave` can be elected as the new `Master` by +You can have additional Redis nodes, that will help survive a situation +where more nodes goes down. Whenever there is only `2` nodes online, a failover +will not be initiated. + +As an example, if you have `6` Redis nodes, a maximum of `3` can be +simultaneously down. + +Please note that there are different requirements for Sentinel nodes. +If you host them in the same Redis machines, you may need to take +that restrictions into consideration when calculating the ammount of +nodes to be provisioned. See [Sentinel setup](#sentinel-setup) +documentation for more information. + +All Redis nodes should be configured the same way and with similar server specs, as +in a failover situation, any **Slave** can be promoted as the new **Master** by the Sentinel servers. -With Sentinel, you must define a password to protect the access as both -Sentinel instances and other redis instances should be able to talk to +The replication requires authentication, so you need to define a password to +protect all Redis nodes and the Sentinels. They will all share the same +password, and all instances must be able to talk to each other over the network. -You'll need to define both `requirepass` and `masterauth` in all -nodes. At any time during a failover the Sentinels can reconfigure a node -and change it's status from `Master` to `Slave` and vice versa. +Redis nodes will need the same password defined in `redis['password']` and +`redis['master_password']`, no matter if **Master** or **Slave**. At any time +during a failover the Sentinels can reconfigure a node and change it's status +from **Master** to **Slave** and vice versa. + +Initial **Slave** nodes requires `redis['master']` defined to `false` and +`redis['master_ip']` pointing to the initial **Master**. If you use the +simplified configuration by enabling `redis_slave_role['enable']`, you +just need to fill in the `redis['master_ip']`. + +This values doesn't have to be changed again in `/etc/gitlab/gitlab.rb` after +a failover, as the nodes will be managed by the Sentinels, and even after a +`gitlab-ctl reconfigure`, they will get their configuration restored by +the same Sentinels. + +### Experimental Redis Sentinel support + + > Experimental Redis Sentinel support was [Introduced][ce-1877] in GitLab 8.11. + Starting with 8.14, Redis Sentinel is no longer experimental. + If you used with versions `< 8.14` before, please check the updated + documentation here. + +### Sentinel setup + +Sentinels watches both other sentinels and Redis nodes. Whenever a Sentinel +detects that a Redis node is not responding, it will announce that to the +other sentinels. You have to reach the **quorum**, the minimum ammount of +sentinels that agrees that a node is down, to be able to start a failover. + +Whenver the **quorum** is met, you need the **majority** of all known +Sentinel nodes to be available and reachable, to elect the Sentinel **leader** +who will take all the decisions to restore the service availability by: + +- Promoting a new **Master** +- Reconfiguring the other **Slaves** and make them point to the new **Master** +- Announce the new **Master** to every other Sentinel peer +- Reconfigure the old **Master** and demote to **Slave** when it comes back online + +You must have at least `3` Redis Sentinel servers, and they need to +be each in a independent machine (that are believed to fail independently). + +You can configure them in the same machines where you've configured the other +Redis servers, but understand that if a whole node goes down, you loose both +a Sentinel and a Redis instance. -Initial `Slave` nodes require an additional `slaveof` setting in `redis.conf` -pointing to the initial `Master`. +The number of sentinels should ideally always be an **odd** number, for the +consensus algorithm to be effective in the case of a failure. -#### Existing single-machine installation +In a `3` nodes topology, you can only afford `1` Sentinel node going down. +Whenever the **majority** of the Sentinels goes down, the network partition +protection prevents destructive actions and a failover **will not be started**. + +Here are some examples: + +- With `5` or `6` sentinels, a maximum of `2` can go down for a failover begin. +- With `7` sentinels, a maximum of `3` nodes can go down. + +The **Leader** election can sometimes fail the voting round when **consensus**, +is not achieved (see the odd number of nodes requirement above). In that case, +a new attempt will be made after the amount of time defined in +`sentinel['failover_timeout']` (in milliseconds). + +The `failover_time` variable have a lot of different usages, according to +official documentation: + +- The time needed to re-start a failover after a previous failover was + already tried against the same master by a given Sentinel, is two + times the failover timeout. + +- The time needed for a slave replicating to a wrong master according + to a Sentinel current configuration, to be forced to replicate + with the right master, is exactly the failover timeout (counting since + the moment a Sentinel detected the misconfiguration). + +- The time needed to cancel a failover that is already in progress but + did not produced any configuration change (SLAVEOF NO ONE yet not + acknowledged by the promoted slave). + +- The maximum time a failover in progress waits for all the slaves to be + reconfigured as slaves of the new master. However even after this time + the slaves will be reconfigured by the Sentinels anyway, but not with + the exact parallel-syncs progression as specified. + +### Recommended setup + +For a minimal setup, you will install the Omnibus GitLab package in `3` +independent machines, both with **Redis** and **Sentinel**: + +- Redis Master + Sentinel +- Redis Slave + Sentinel +- Redis Slave + Sentinel + +Make sure you've read [Redis Setup](#redis-setup) and [Sentinel Setup](#sentinel-setup) +before, to understant how and why the ammount of nodes came from. + +For a recommended setup, that can resist more failures, you will install +the Omnibus GitLab package in `5` independent machines, both with +**Redis** and **Sentinel**: + +- Redis Master + Sentinel +- Redis Slave + Sentinel +- Redis Slave + Sentinel +- Redis Slave + Sentinel +- Redis Slave + Sentinel + +## Configuring instances using Omnibus + +This is a summary of what are we going to do: + +1. Provision the required number of instances specified previously + - You can opt to install Redis and Sentinel in the same machine or each in + independent ones. + - Don't install Redis and Sentinel in the same machines your GitLab instance + is running on. + - All machines must be able to talk to each other and accept incomming + connection over Redis (`6379`) and Sentinel (`26379`) ports. + - GitLab machines must be able to access these machines and with the same + permissions. + - Protected them from indiscriminated access from external networks (Internet), + to harden the security. + +1. Download/install Omnibus GitLab using **steps 1 and 2** from + [GitLab downloads](https://about.gitlab.com/downloads) in each node. + - Do not complete other steps on the download page. + - Make sure you select the correct Omnibus package, with the same version + and type (Community, Enterprise editions) of your current install. + +1. Run `touch /etc/gitlab/skip-auto-migrations` to prevent database migrations + from running on upgrade. Only the primary GitLab application server should + handle migrations. + +1. Create/edit `/etc/gitlab/gitlab.rb` and make the changes based on the + [Example Configurations](#example-configurations). + +### Existing single-machine installation If you already have a single-machine GitLab install running, you will need to replicate from this machine first, before de-activating the Redis instance inside it. -Your single-machine install will be the initial `Master`, and the `3` others -should be configured as `Slave` pointing to this machine. +Your single-machine install will be the initial **Master**, and the `3` others +should be configured as **Slave** pointing to this machine. After replication catchs-up, you will need to stop services in the -single-machine install, to rotate the `Master` to one of the new nodes. +single-machine install, to rotate the **Master** to one of the new nodes. Make the required changes in configuration and restart the new nodes again. @@ -159,130 +299,317 @@ To disable redis in the single install, edit `/etc/gitlab/gitlab.rb`: redis['enable'] = false ``` -#### Omnibus packages +If you fail to replicate first, you may loose data (unprocessed background jobs). + +### Configuring Master Redis instance + +You will need to configure the following in `/etc/gitlab/gitlab.rb`: + +1. Define `redis_master_role['enable']` to `true`, to disable other services + in the machine (you can still enable Sentinel) + +1. Define a `redis['bind']` address pointing to a local IP that your other machines + can reach you. + - If you really need to bind to an external acessible IP, make + sure you add extra firewall rules to prevent unauthorized access. + - You can also set bind to `0.0.0.0` which listen in all interfaces. + +1. Define a `redis['port']` so redis can listen for TCP requests which will + allow other machines to connect to it. + +1. Set up a password authentication with `redis['password']` and + `redis['master_password']` (use the same password in all nodes). + +Reconfigure Omnibus GitLab for the changes to take effect: `sudo gitlab-ctl reconfigure` -You need to install the Omnibus GitLab package in `3` independent machines. +### Configuring Slave Redis instances -**Configuring Master Redis instance** +You will need to configure the following in `/etc/gitlab/gitlab.rb`: -You will need to configure the following: +1. Define `redis_slaves_role['enable']` to `true`, to disable other services + in the machine (you can still enable Sentinel) + - This will also set automatically `redis['master'] = false`. 1. Define a `redis['bind']` address pointing to a local IP that your other machines - can reach you. If you really need to bind to an external acessible IP, make + can reach you. + - If you really need to bind to an external acessible IP, make sure you add extra firewall rules to prevent unauthorized access. + - You can also set bind to `0.0.0.0` which listen in all interfaces. + 1. Define a `redis['port']` so redis can listen for TCP requests which will allow other machines to connect to it. -1. Set up a password authentication with `redis['master_password']` (use the same - password in all nodes). + +1. Set up a password authentication with `redis['password']` and + `redis['master_password']` (use the same password in all nodes). + +1. Define `redis['master_ip']` with the IP of the **Master** Redis. + +1. Define `redis['master_port']` with the port of the **Master** Redis (default to `6379`). + +### Configuring Sentinel instances + +Now that the Redis servers are all set up, let's configure the Sentinel +servers. + +If you are not sure if your Redis servers are working and replicating +correctly, please read the [Troubleshooting Replication](#troubleshooting-replication) +and fix it before proceeding with Sentinel setup. + +You must have at least `3` Redis Sentinel servers, and they need to +be each in a independent machine. You can configure them in the same +machines where you've configured the other Redis servers. + +##### Community Edition + +With GitLab Community Edition, you need to install, configure, execute and +monitor Sentinel from source. Omnibus GitLab Community Edition package does +not support Sentinel configuration. + +See [documentation for Source Install](redis_source.md). + +##### Enterprise Edition + +With GitLab Enterprise Edition, you can use Omnibus package to setup multiple +machines with Sentinel daemon. + +See [example configuration](#configuration-for-sentinel-ee-only) below. + +### GitLab setup + +The final part is to inform the main GitLab application server of the Redis +Sentinels servers and authentication credentials. + +You can enable or disable Sentinel support at any time in new or existing +installations. From the GitLab application perspective, all it requires is +the correct credentials for the Sentinel nodes. + +While it doesn't require a list of all Sentinel nodes, in case of a failure, +it needs to access at least one of listeds. + +>**Note:** +The following steps should be performed in the [GitLab application server](gitlab.md) +which ideally should not have Redis or Sentinels in the same machine for a HA setup. + +1. Edit `/etc/gitlab/gitlab.rb` and add/change the following lines: + + - `redis['master_name']` - this is the `master-group-name` from sentinel (default: `gitlab-redis`) + - `redis['master_password']` - the same password you've defined before for Redis and Sentinels + - `gitlab_rails['redis_sentinels']` - a list of sentinels with `host` and `port` + +1. [Reconfigure] GitLab for the changes to take effect. + +See [example configuration](#configuration-for-gitlab) below. + +## Example Configurations + +In this example we consider that all servers have an internal network +interface with IPs in the `10.0.0.x` range, and that they can connect +to each other using these IPs. + +In a real world usage, you would also setup firewall rules to prevent +unauthorized access from other machines, and block traffic from the +outside (Internet). + +We will use the same `3` nodes with **Redis** + **Sentinel** topology +discussed in [Redis Setup](#redis-setup) and [Sentinel Setup](#sentinel-setup) +documentation. + +Here is a list and description of each **machine** and the assigned **IP**: + +* `10.0.0.1`: Redis Master + Sentinel 1 +* `10.0.0.2`: Redis Slave 1 + Sentinel 2 +* `10.0.0.2`: Redis Slave 2 + Sentinel 3 + +Please note that after the initial configuration, if a failover is initiated +by the Sentinel nodes, the Redis nodes will be reconfigured and the **Master** +will change permanently (including in `redis.conf`) from one node to the other, +until a new failover is initiated again. + +The same thing will happen with `sentinel.conf` that will be overriten after the +initial execution, after any new sentinel node starts watching the **Master**, +or a failover promotes a different **Master** node. + +### Configuration for Redis Master + +**Example configation for Redis Master:** In `/etc/gitlab/gitlab.rb`: ```ruby -## Redis TCP support (will disable UNIX socket transport) -redis['bind'] = '0.0.0.0' # or specify an IP to bind to a single one +redis_master_role['enable'] = true + +redis['bind'] = '10.0.0.1' redis['port'] = 6379 redis['password'] = 'redis-password-goes-here' redis['master_password'] = 'redis-password-goes-here' ``` - Reconfigure Omnibus GitLab for the changes to take effect: `sudo gitlab-ctl reconfigure` -**Configuring Slave Redis instances** +### Configuration for Redis Slave + +**Example configation for Slave 1:** -You need to make the same changes listed for the `Master` instance, -with an additional `Slave` section as in the example below: +In `/etc/gitlab/gitlab.rb`: ```ruby -redis['bind'] = '0.0.0.0' # or specify an IP to bind to a single one +redis_slave_role['enable'] = true + +redis['bind'] = '10.0.0.2' redis['port'] = 6379 redis['password'] = 'redis-password-goes-here' redis['master_password'] = 'redis-password-goes-here' -## Slave redis instance -redis['master'] = false -redis['master_ip'] = '10.10.10.10' # IP of master Redis server -redis['master_port'] = 6379 # Port of master Redis server +redis['master_ip'] = '10.0.0.1' # IP of master Redis server +#redis['master_port'] = 6379 # Port of master Redis server, uncomment to change to non default ``` Reconfigure Omnibus GitLab for the changes to take effect: `sudo gitlab-ctl reconfigure` ---- - -Now that the Redis servers are all set up, let's configure the Sentinel -servers. +**Example configation for Slave 2:** -If you are not sure if your Redis servers are working and replicating -correctly, please read the [Troubleshooting Replication](#troubleshooting-replication) -and fix it before proceeding with Sentinel setup. +In `/etc/gitlab/gitlab.rb`: -### Configuring Sentinel +```ruby +redis_slave_role['enable'] = true -You must have at least `3` Redis Sentinel servers, and they need to -be each in a independent machine. You can configure them in the same -machines where you've configured the other Redis servers. +redis['bind'] = '10.0.0.3' +redis['port'] = 6379 +redis['password'] = 'redis-password-goes-here' +redis['master_password'] = 'redis-password-goes-here' -This number is required for the consensus algorithm to be effective -in the case of a failure. **You should always have an `odd` number -of Sentinel nodes provisioned**. +redis['master_ip'] = '10.0.0.1' # IP of master Redis server +#redis['master_port'] = 6379 # Port of master Redis server, uncomment to change to non default +``` -#### How sentinel handles a failover +Reconfigure Omnibus GitLab for the changes to take effect: `sudo gitlab-ctl reconfigure` -If (`quorum` value of) Sentinels agree the fact the `master` is not reachable, -Sentinels will try to elect a temporary `Leader`. The **Majority** of the -Sentinels must agree to start a failover. +### Configuration for Sentinel (EE only) -If you don't have the **Majority** of the Sentinels online (for example if you -are under a network partitioning), a failover **will not be started**. +Please note that some of the variables are already configured previously +as they are required for Redis replication. -For example, for a cluster of `3` Sentinels, at least `2` must agree on a -`Leader`. If you have total of `5` at least `3` must agree on a `Leader`. +**Example configation for Sentinel 1:** -The `quorum` is only used to detect failure, not to elect the `Leader`. +In `/etc/gitlab/gitlab.rb`: -Official [Sentinel documentation](http://redis.io/topics/sentinel#example-sentinel-deployments) -also lists different network topologies and warns againts situations like -network partition and how it can affect the state of the HA solution. Make -sure you read it carefully and understand the implications in your current -setup. +```ruby +redis_sentinel_role['enable'] = true -GitLab Enterprise Edition provides [automated way to setup and run](#sentinel-setup-ee-only) the Sentinel daemon. +redis['master_name'] = 'gitlab-redis' # must be the same in every sentinel node +redis['master_ip'] = '10.0.0.1' # ip of the initial master redis instance +#redis['master_port'] = 6379 # port of the initial master redis instance, uncomment to change to non default +redis['master_password'] = 'redis-password-goes-here' # the same value defined in redis['password'] in the master instance -#### Sentinel setup +## Configure Sentinel +sentinel['bind'] = '10.0.0.1' +# sentinel['port'] = 26379 # uncomment to change default port -##### Community Edition +## Quorum must reflect the amount of voting sentinels it take to start a failover. +## Value must NOT be greater then the ammount of sentinels. +## +## The quorum can be used to tune Sentinel in two ways: +## 1. If a the quorum is set to a value smaller than the majority of Sentinels +## we deploy, we are basically making Sentinel more sensible to master failures, +## triggering a failover as soon as even just a minority of Sentinels is no longer +## able to talk with the master. +## 1. If a quorum is set to a value greater than the majority of Sentinels, we are +## making Sentinel able to failover only when there are a very large number (larger +## than majority) of well connected Sentinels which agree about the master being down.s +sentinel['quorum'] = 2 -With GitLab Community Edition, you need to install, configure, execute and -monitor Sentinel from source. Omnibus GitLab Community Edition package does -not support Sentinel configuration. +## Consider unresponsive server down after x amount of ms. +# sentinel['down_after_milliseconds'] = 10000 -See documentation for Source Install [here](redis_source.md). +## Specifies the failover timeout in milliseconds. It is used in many ways: +## +## - The time needed to re-start a failover after a previous failover was +## already tried against the same master by a given Sentinel, is two +## times the failover timeout. +## +## - The time needed for a slave replicating to a wrong master according +## to a Sentinel current configuration, to be forced to replicate +## with the right master, is exactly the failover timeout (counting since +## the moment a Sentinel detected the misconfiguration). +## +## - The time needed to cancel a failover that is already in progress but +## did not produced any configuration change (SLAVEOF NO ONE yet not +## acknowledged by the promoted slave). +## +## - The maximum time a failover in progress waits for all the slaves to be +## reconfigured as slaves of the new master. However even after this time +## the slaves will be reconfigured by the Sentinels anyway, but not with +## the exact parallel-syncs progression as specified. +# sentinel['failover_timeout'] = 60000 +``` -##### Enterprise Edition +**Example configation for Sentinel 2:** -To setup sentinel, edit `/etc/gitlab/gitlab.rb` file: +In `/etc/gitlab/gitlab.rb`: ```ruby +redis_sentinel_role['enable'] = true -## When you install Sentinel in a separate machine, you need to control which -## other services will be running in it. -## We've simplified the choice using special "roles" settings: +redis['master_name'] = 'gitlab-redis' # must be the same in every sentinel node +redis['master_ip'] = '10.0.0.1' # ip of the initial master redis instance +#redis['master_port'] = 6379 # port of the initial master redis instance, uncomment to change to non default +redis['master_password'] = 'redis-password-goes-here' # the same value defined in redis['password'] in the master instance -## Enabled Sentinel and Redis Master services -redis_sentinel_role['enable'] = true -redis_master_role['enable'] = true +## Configure Sentinel +sentinel['bind'] = '10.0.0.2' +# sentinel['port'] = 26379 # uncomment to change default port + +## Quorum must reflect the amount of voting sentinels it take to start a failover. +## Value must NOT be greater then the ammount of sentinels. +## +## The quorum can be used to tune Sentinel in two ways: +## 1. If a the quorum is set to a value smaller than the majority of Sentinels +## we deploy, we are basically making Sentinel more sensible to master failures, +## triggering a failover as soon as even just a minority of Sentinels is no longer +## able to talk with the master. +## 1. If a quorum is set to a value greater than the majority of Sentinels, we are +## making Sentinel able to failover only when there are a very large number (larger +## than majority) of well connected Sentinels which agree about the master being down.s +sentinel['quorum'] = 2 -## Enabled Sentinel and Redis Slave services +## Consider unresponsive server down after x amount of ms. +# sentinel['down_after_milliseconds'] = 10000 + +## Specifies the failover timeout in milliseconds. It is used in many ways: +## +## - The time needed to re-start a failover after a previous failover was +## already tried against the same master by a given Sentinel, is two +## times the failover timeout. +## +## - The time needed for a slave replicating to a wrong master according +## to a Sentinel current configuration, to be forced to replicate +## with the right master, is exactly the failover timeout (counting since +## the moment a Sentinel detected the misconfiguration). +## +## - The time needed to cancel a failover that is already in progress but +## did not produced any configuration change (SLAVEOF NO ONE yet not +## acknowledged by the promoted slave). +## +## - The maximum time a failover in progress waits for all the slaves to be +## reconfigured as slaves of the new master. However even after this time +## the slaves will be reconfigured by the Sentinels anyway, but not with +## the exact parallel-syncs progression as specified. +# sentinel['failover_timeout'] = 60000 +``` + +**Example configation for Sentinel 3:** + +In `/etc/gitlab/gitlab.rb`: + +```ruby redis_sentinel_role['enable'] = true -redis_slave_role['enable'] = true -## Configure Redis redis['master_name'] = 'gitlab-redis' # must be the same in every sentinel node redis['master_ip'] = '10.0.0.1' # ip of the initial master redis instance -redis['master_port'] = 6379 # port of the initial master redis instance +#redis['master_port'] = 6379 # port of the initial master redis instance, uncomment to change to non default redis['master_password'] = 'redis-password-goes-here' # the same value defined in redis['password'] in the master instance ## Configure Sentinel -# sentinel['bind'] = '0.0.0.0' # bind to all interfaces, uncomment to specify an IP and bind to a single one +sentinel['bind'] = '10.0.0.3' # sentinel['port'] = 26379 # uncomment to change default port ## Quorum must reflect the amount of voting sentinels it take to start a failover. @@ -323,6 +650,8 @@ sentinel['quorum'] = 2 # sentinel['failover_timeout'] = 60000 ``` +### Control running services + In the example above we've used `redis_sentinel_role` and `redis_master_role` which simplify the ammount of configuration changes. @@ -365,37 +694,6 @@ mailroom['enable'] = false # Redis Slave role also change this setting from default 'true' to 'false': redis['master'] = false ``` ---- - -The final part is to inform the main GitLab application server of the Redis -master and the new sentinels servers. - -### GitLab setup - -You can enable or disable Sentinel support at any time in new or existing -installations. From the GitLab application perspective, all it requires is -the correct credentials for the Sentinel nodes. - -While it doesn't require a list of all Sentinel nodes, in case of a failure, -it needs to access at one of listed ones. - ->**Note:** -The following steps should be performed in the [GitLab application server](gitlab.md) -which ideally should not have Redis or Sentinels in the same machine for a HA setup. - -1. Edit `/etc/gitlab/gitlab.rb` and add/change the following lines: - - ```ruby - redis['master_name'] = "gitlab-redis" - redis['master_password'] = 'redis-password-goes-here' - gitlab_rails['redis_sentinels'] = [ - {'host' => '10.10.10.1', 'port' => 26379}, - {'host' => '10.10.10.2', 'port' => 26379}, - {'host' => '10.10.10.3', 'port' => 26379} - ] - ``` - -1. [Reconfigure] GitLab for the changes to take effect. ## Troubleshooting diff --git a/doc/administration/high_availability/redis_source.md b/doc/administration/high_availability/redis_source.md index 7e8c8c2b4b9..ab5bb13f070 100644 --- a/doc/administration/high_availability/redis_source.md +++ b/doc/administration/high_availability/redis_source.md @@ -153,7 +153,7 @@ outside (Internet). We will use the same `3` nodes with **Redis** + **Sentinel** topology discussed in the [Configuring Redis for GitLab HA](redis.md) documentation. -Here is a list and description of each **machine** and the assined **ip**: +Here is a list and description of each **machine** and the assigned **IP**: * `10.0.0.1`: Redis Master + Sentinel 1 * `10.0.0.2`: Redis Slave 1 + Sentinel 2 @@ -170,7 +170,7 @@ or a failover promotes a different **Master** node. ### Configuring Redis Master -`redis.conf`: +**Example configation for Redis Master - `redis.conf`:** ```conf bind 10.0.0.1 @@ -181,7 +181,7 @@ masterauth redis-password-goes-here ### Configuring Redis Slaves -**Slave 1 - `redis.conf`:** +**Example configation for Slave 1 - `redis.conf`:** ```conf bind 10.0.0.2 @@ -193,7 +193,7 @@ masterauth redis-password-goes-here slaveof 10.0.0.1 6379 ``` -**Slave 2 - `redis.conf`:** +**Example configation for Slave 2 - `redis.conf`:** ```conf bind 10.0.0.3 @@ -211,7 +211,7 @@ For this example, **Sentinel 1** will be configured in the same machine as the **Redis Master**, **Sentinel 2** and **Sentinel 3** in the same machines as the **Slave 1** and **Slave 2** respectively. -Sentinel 1 - `sentinel.conf` +**Example configation for Sentinel 1 - `sentinel.conf`:** ```conf bind 10.0.0.1 @@ -222,7 +222,7 @@ sentinel down-after-milliseconds gitlab-redis 10000 sentinel failover_timeout 30000 ``` -Sentinel 2 - `sentinel.conf` +**Example configation for Sentinel 2 - `sentinel.conf`:** ```conf bind 10.0.0.2 @@ -233,7 +233,7 @@ sentinel down-after-milliseconds gitlab-redis 10000 sentinel failover_timeout 30000 ``` -Sentinel 3 - `sentinel.conf` +**Example configation for Sentinel 3 - `sentinel.conf`:** ```conf bind 10.0.0.3 -- cgit v1.2.1 From 646f81afac23b74b604c77af61254f90e205505f Mon Sep 17 00:00:00 2001 From: Achilleas Pipinellis Date: Thu, 10 Nov 2016 13:14:22 +0100 Subject: Fix typos of Redis sentinel docs --- doc/administration/high_availability/redis.md | 44 +++++++++++++-------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/doc/administration/high_availability/redis.md b/doc/administration/high_availability/redis.md index 6b16f9e791f..ca773ea20aa 100644 --- a/doc/administration/high_availability/redis.md +++ b/doc/administration/high_availability/redis.md @@ -5,7 +5,7 @@ topology with **Sentinel** service to watch and automatically start failover proceedings. You can choose to install and manage Redis and Sentinel yourself, use -a hosted, managed clouse solution or you can use or you can use the one +a hosted, managed cloud solution or you can use or you can use the one that comes bundled with Omnibus GitLab packages. > **Note:** Redis requires authentication for High Availability. See @@ -55,10 +55,10 @@ technology and provide a transparent proxy, which means that GitLab doesn't need any additional change, or will use Sentinel and manage it for you. If your provider, uses Sentinel method, see [GitLab Setup](#gitlab-setup) -to understant where you need to provide the list of servers and credentials. +to understand where you need to provide the list of servers and credentials. If you want to setup Redis by yourself, without using Omnibus, you can -read our documentation: [Configuring Redis for GitLab HA (Source Install)](redis_source.md) +read our documentation: [Configuring Redis for GitLab HA (source install)](redis_source.md). ## High Availability with Sentinel @@ -73,19 +73,19 @@ High Availability with Redis requires a few things: - Multiple Redis instances - Run Redis in a **Master** x **Slave** topology - Multiple Sentinel instances -- Application support and visiblity to all Sentinel and Redis instances +- Application support and visibility to all Sentinel and Redis instances Redis Sentinel can handle the most important tasks in a HA environment to help keep servers online with minimal to no downtime: - Monitors **Master** and **Slaves** instances to see if they are available -- Promote a **Slave** to **Master** when the **Master** fails. +- Promote a **Slave** to **Master** when the **Master** fails - Demote a **Master** to **Slave** when failed **Master** comes back online (to prevent - data-partitioning). -- Can be queried by clients to always connect to the current **Master** server. + data-partitioning) +- Can be queried by clients to always connect to the current **Master** server -When a **Master** fails to respond, it's the client responsability to handle timeout -and reconnect (querying a **Sentinel** for a new **Master**). +When a **Master** fails to respond, it's the client's responsibility to handle +timeout and reconnect (querying a **Sentinel** for a new **Master**). To get a better understanding on how to correctly setup Sentinel, please read the [Redis Sentinel documentation](http://redis.io/topics/sentinel) first, as @@ -113,7 +113,7 @@ the shared environment can bring your entire setup down. You also need to take in consideration the underlying network topology, making sure you have redundant connectivity between Redis / Sentinel and -GitLab instances, otherwhise the networks will become a single point of +GitLab instances, otherwise the networks will become a single point of failure. Read carefully how to configure the components below. @@ -132,7 +132,7 @@ simultaneously down. Please note that there are different requirements for Sentinel nodes. If you host them in the same Redis machines, you may need to take -that restrictions into consideration when calculating the ammount of +that restrictions into consideration when calculating the amount of nodes to be provisioned. See [Sentinel setup](#sentinel-setup) documentation for more information. @@ -171,10 +171,10 @@ the same Sentinels. Sentinels watches both other sentinels and Redis nodes. Whenever a Sentinel detects that a Redis node is not responding, it will announce that to the -other sentinels. You have to reach the **quorum**, the minimum ammount of +other sentinels. You have to reach the **quorum**, the minimum amount of sentinels that agrees that a node is down, to be able to start a failover. -Whenver the **quorum** is met, you need the **majority** of all known +Whenever the **quorum** is met, you need the **majority** of all known Sentinel nodes to be available and reachable, to elect the Sentinel **leader** who will take all the decisions to restore the service availability by: @@ -238,7 +238,7 @@ independent machines, both with **Redis** and **Sentinel**: - Redis Slave + Sentinel Make sure you've read [Redis Setup](#redis-setup) and [Sentinel Setup](#sentinel-setup) -before, to understant how and why the ammount of nodes came from. +before, to understand how and why the amount of nodes came from. For a recommended setup, that can resist more failures, you will install the Omnibus GitLab package in `5` independent machines, both with @@ -259,11 +259,11 @@ This is a summary of what are we going to do: independent ones. - Don't install Redis and Sentinel in the same machines your GitLab instance is running on. - - All machines must be able to talk to each other and accept incomming + - All machines must be able to talk to each other and accept incoming connection over Redis (`6379`) and Sentinel (`26379`) ports. - GitLab machines must be able to access these machines and with the same permissions. - - Protected them from indiscriminated access from external networks (Internet), + - Protected them from indiscriminating access from external networks (Internet), to harden the security. 1. Download/install Omnibus GitLab using **steps 1 and 2** from @@ -288,7 +288,7 @@ inside it. Your single-machine install will be the initial **Master**, and the `3` others should be configured as **Slave** pointing to this machine. -After replication catchs-up, you will need to stop services in the +After replication catches up, you will need to stop services in the single-machine install, to rotate the **Master** to one of the new nodes. Make the required changes in configuration and restart the new nodes again. @@ -310,7 +310,7 @@ You will need to configure the following in `/etc/gitlab/gitlab.rb`: 1. Define a `redis['bind']` address pointing to a local IP that your other machines can reach you. - - If you really need to bind to an external acessible IP, make + - If you really need to bind to an external accessible IP, make sure you add extra firewall rules to prevent unauthorized access. - You can also set bind to `0.0.0.0` which listen in all interfaces. @@ -332,7 +332,7 @@ You will need to configure the following in `/etc/gitlab/gitlab.rb`: 1. Define a `redis['bind']` address pointing to a local IP that your other machines can reach you. - - If you really need to bind to an external acessible IP, make + - If you really need to bind to an external accessible IP, make sure you add extra firewall rules to prevent unauthorized access. - You can also set bind to `0.0.0.0` which listen in all interfaces. @@ -384,7 +384,7 @@ installations. From the GitLab application perspective, all it requires is the correct credentials for the Sentinel nodes. While it doesn't require a list of all Sentinel nodes, in case of a failure, -it needs to access at least one of listeds. +it needs to access at least one of the listed. >**Note:** The following steps should be performed in the [GitLab application server](gitlab.md) @@ -425,7 +425,7 @@ by the Sentinel nodes, the Redis nodes will be reconfigured and the **Master** will change permanently (including in `redis.conf`) from one node to the other, until a new failover is initiated again. -The same thing will happen with `sentinel.conf` that will be overriten after the +The same thing will happen with `sentinel.conf` that will be overridden after the initial execution, after any new sentinel node starts watching the **Master**, or a failover promotes a different **Master** node. @@ -653,7 +653,7 @@ sentinel['quorum'] = 2 ### Control running services In the example above we've used `redis_sentinel_role` and `redis_master_role` -which simplify the ammount of configuration changes. +which simplify the amount of configuration changes. If you want more control, here is what each one sets for you automatically when enabled: -- cgit v1.2.1 From c43b540095b6de03e679dc4dd56df97f08a6aed0 Mon Sep 17 00:00:00 2001 From: Achilleas Pipinellis Date: Thu, 10 Nov 2016 13:21:34 +0100 Subject: Move experimental heading at the bottom under changelog --- doc/administration/high_availability/redis.md | 59 +++++++++++++++------------ 1 file changed, 33 insertions(+), 26 deletions(-) diff --git a/doc/administration/high_availability/redis.md b/doc/administration/high_availability/redis.md index ca773ea20aa..3837a8a0a88 100644 --- a/doc/administration/high_availability/redis.md +++ b/doc/administration/high_availability/redis.md @@ -19,28 +19,29 @@ that comes bundled with Omnibus GitLab packages. - [Using an external Redis server](#using-an-external-redis-server) - [High Availability with Sentinel](#high-availability-with-sentinel) - - [Prerequisites](#prerequisites) - - [Redis setup](#redis-setup) - - [Experimental Redis Sentinel support](#experimental-redis-sentinel-support) - - [Sentinel setup](#sentinel-setup) - - [Recommended setup](#recommended-setup) + - [Prerequisites](#prerequisites) + - [Redis setup](#redis-setup) + - [Sentinel setup](#sentinel-setup) + - [Recommended setup](#recommended-setup) - [Configuring instances using Omnibus](#configuring-instances-using-omnibus) - - [Existing single-machine installation](#existing-single-machine-installation) - - [Configuring Master Redis instance](#configuring-master-redis-instance) - - [Configuring Slave Redis instances](#configuring-slave-redis-instances) - - [Configuring Sentinel instances](#configuring-sentinel-instances) - - [Community Edition](#community-edition) - - [Enterprise Edition](#enterprise-edition) - - [GitLab setup](#gitlab-setup) + - [Existing single-machine installation](#existing-single-machine-installation) + - [Configuring Master Redis instance](#configuring-master-redis-instance) + - [Configuring Slave Redis instances](#configuring-slave-redis-instances) + - [Configuring Sentinel instances](#configuring-sentinel-instances) + - [Community Edition](#community-edition) + - [Enterprise Edition](#enterprise-edition) + - [GitLab setup](#gitlab-setup) - [Example Configurations](#example-configurations) - - [Configuration for Redis Master](#configuration-for-redis-master) - - [Configuration for Redis Slave](#configuration-for-redis-slave) - - [Configuration for Sentinel (EE only)](#configuration-for-sentinel-ee-only) - - [Control running services](#control-running-services) + - [Configuration for Redis Master](#configuration-for-redis-master) + - [Configuration for Redis Slave](#configuration-for-redis-slave) + - [Configuration for Sentinel (EE only)](#configuration-for-sentinel-ee-only) + - [Control running services](#control-running-services) - [Troubleshooting](#troubleshooting) - - [Redis replication](#redis-replication) - - [Sentinel](#sentinel) - - [Omnibus GitLab](#omnibus-gitlab) + - [Redis replication](#redis-replication) + - [Sentinel](#sentinel) + - [Omnibus GitLab](#omnibus-gitlab) +- [Changelog](#changelog) + - [Experimental Redis Sentinel support](#experimental-redis-sentinel-support) @@ -160,13 +161,6 @@ a failover, as the nodes will be managed by the Sentinels, and even after a `gitlab-ctl reconfigure`, they will get their configuration restored by the same Sentinels. -### Experimental Redis Sentinel support - - > Experimental Redis Sentinel support was [Introduced][ce-1877] in GitLab 8.11. - Starting with 8.14, Redis Sentinel is no longer experimental. - If you used with versions `< 8.14` before, please check the updated - documentation here. - ### Sentinel setup Sentinels watches both other sentinels and Redis nodes. Whenever a Sentinel @@ -815,6 +809,19 @@ To make sure your configuration is correct: You should see a different port after a few seconds delay (the failover/reconnect time). + +## Changelog + +Changes to Redis HA over time. + +### Experimental Redis Sentinel support + +> +Experimental Redis Sentinel support was [Introduced][ce-1877] in GitLab 8.11. +Starting with 8.14, Redis Sentinel is no longer experimental. +If you used with versions `< 8.14` before, please check the updated +documentation here. + --- Read more on high-availability configuration: -- cgit v1.2.1 From fb0dff6ea848f58a068030f93e8728ce0143c592 Mon Sep 17 00:00:00 2001 From: Achilleas Pipinellis Date: Thu, 10 Nov 2016 15:51:13 +0100 Subject: Move some things over --- doc/administration/high_availability/redis.md | 195 +++++++++++++++----------- 1 file changed, 112 insertions(+), 83 deletions(-) diff --git a/doc/administration/high_availability/redis.md b/doc/administration/high_availability/redis.md index 3837a8a0a88..5d4dfe22779 100644 --- a/doc/administration/high_availability/redis.md +++ b/doc/administration/high_availability/redis.md @@ -1,73 +1,105 @@ # Configuring Redis for GitLab HA -High Availability with Redis is possible using a **Master** x **Slave** -topology with **Sentinel** service to watch and automatically start -failover proceedings. +High Availability with [Redis] is possible using a **Master** x **Slave** +topology with a [Redis Sentinel][sentinel] service to watch and automatically +start failover proceedings. You can choose to install and manage Redis and Sentinel yourself, use -a hosted, managed cloud solution or you can use or you can use the one -that comes bundled with Omnibus GitLab packages. +a hosted cloud solution or you can use the one that comes bundled with +Omnibus GitLab packages. -> **Note:** Redis requires authentication for High Availability. See +> **Notes:** +- Redis requires authentication for High Availability. See [Redis Security](http://redis.io/topics/security) documentation for more information. We recommend using a combination of a Redis password and tight firewall rules to secure your Redis service. +- You are highly encouraged to read the [Redis Sentinel][sentinel] documentation + before configuring Redis HA with GitLab to fully understand the topology and + architecture. **Table of Contents** -- [Using an external Redis server](#using-an-external-redis-server) -- [High Availability with Sentinel](#high-availability-with-sentinel) - - [Prerequisites](#prerequisites) - - [Redis setup](#redis-setup) - - [Sentinel setup](#sentinel-setup) - - [Recommended setup](#recommended-setup) +- [Overview](#overview) + - [Available setups](#available-setups) + - [Using a non-Omnibus external Redis server](#using-a-non-omnibus-external-redis-server) + - [High Availability with Sentinel](#high-availability-with-sentinel) + - [Prerequisites](#prerequisites) + - [Recommended setup](#recommended-setup) +- [Redis HA configuration](#redis-ha-configuration) + - [Redis setup](#redis-setup) + - [Sentinel setup](#sentinel-setup) - [Configuring instances using Omnibus](#configuring-instances-using-omnibus) - - [Existing single-machine installation](#existing-single-machine-installation) - - [Configuring Master Redis instance](#configuring-master-redis-instance) - - [Configuring Slave Redis instances](#configuring-slave-redis-instances) - - [Configuring Sentinel instances](#configuring-sentinel-instances) - - [Community Edition](#community-edition) - - [Enterprise Edition](#enterprise-edition) - - [GitLab setup](#gitlab-setup) -- [Example Configurations](#example-configurations) - - [Configuration for Redis Master](#configuration-for-redis-master) - - [Configuration for Redis Slave](#configuration-for-redis-slave) - - [Configuration for Sentinel (EE only)](#configuration-for-sentinel-ee-only) - - [Control running services](#control-running-services) + - [Existing single-machine installation](#existing-single-machine-installation) + - [Configuring Master Redis instance](#configuring-master-redis-instance) + - [Configuring Slave Redis instances](#configuring-slave-redis-instances) + - [Configuring Sentinel instances](#configuring-sentinel-instances) + - [Community Edition](#community-edition) + - [Enterprise Edition](#enterprise-edition) + - [GitLab setup](#gitlab-setup) +- [Minimal example configuration with 1 master, 2 slaves and 3 sentinels](#minimal-example-configuration-with-1-master-2-slaves-and-3-sentinels) + - [Configuration for Redis Master](#configuration-for-redis-master) + - [Configuration for Redis Slave](#configuration-for-redis-slave) + - [Configuration for Sentinel (EE only)](#configuration-for-sentinel-ee-only) + - [Control running services](#control-running-services) - [Troubleshooting](#troubleshooting) - - [Redis replication](#redis-replication) - - [Sentinel](#sentinel) - - [Omnibus GitLab](#omnibus-gitlab) + - [Redis replication](#redis-replication) + - [Sentinel](#sentinel) + - [Omnibus GitLab](#omnibus-gitlab) - [Changelog](#changelog) - - [Experimental Redis Sentinel support](#experimental-redis-sentinel-support) + - [Experimental Redis Sentinel support](#experimental-redis-sentinel-support) -## Using an external Redis server + +## Overview + +Before diving into the details of setting up Redis and Redis Sentinel for HA, +make sure you read this section to better understand the underline architecture. + +### Available setups + +Based on your infrastructure setup, there are multiple ways to setup Redis HA +with GitLab. Omnibus GitLab packages have Redis and Redis Sentinel bundled with +them to save you the hassle to install it yourself. Pick the one that suits your +needs. + +- **Installations from source:** You need to install Redis and Redis Sentinel + yourself. Use the [Redis HA source install](redis_source.md) guide. +- **Omnibus Community Edition (CE):** Redis is bundled so you can use the + package with only the Redis service enabled (works for both master and slave + setups). +- **Omnibus Enterprise Edition (EE):** Both Redis and Redis Sentinel are bundled + in the Omnibus package so you can use only them to setup the whole Redis HA + infrastructure (master, slave and Sentinel). + +Note that with the Omnibus packages (both CE and EE), you can also use an +[external Redis server](#using-a-non-omnibus-external-redis-server). + +### Using a non-Omnibus external Redis server If you're hosting GitLab on a cloud provider, you can optionally use a managed service for Redis. For example, AWS offers a managed ElastiCache service that runs Redis. Managed services can provide High Availability using their own proprietary -technology and provide a transparent proxy, which means that GitLab doesn't -need any additional change, or will use Sentinel and manage it for you. +technology and provide a transparent proxy (which means that GitLab doesn't +need any additional change) or they will use Sentinel and manage it for you. -If your provider, uses Sentinel method, see [GitLab Setup](#gitlab-setup) +If your provider, uses Sentinel, see [GitLab Setup](#gitlab-setup) to understand where you need to provide the list of servers and credentials. If you want to setup Redis by yourself, without using Omnibus, you can -read our documentation: [Configuring Redis for GitLab HA (source install)](redis_source.md). - -## High Availability with Sentinel +read the documentation on [configuring Redis HA for source installs](redis_source.md). -> Since GitLab `8.11`, you can configure a list of Redis Sentinel servers that -will monitor a group of Redis servers to provide failover support. +### High Availability with Sentinel -> With GitLab `8.14`, we bundled Redis Sentinel as part of Omnibus package and -improved the way you use and configure it. +> +- Since GitLab `8.11`, you can configure a list of Redis Sentinel servers that + will monitor a group of Redis servers to provide failover support. +- With GitLab `8.14`, we bundled Redis Sentinel as part of Omnibus package and + improved the way you use and configure it. High Availability with Redis requires a few things: @@ -77,12 +109,12 @@ High Availability with Redis requires a few things: - Application support and visibility to all Sentinel and Redis instances Redis Sentinel can handle the most important tasks in a HA environment to help -keep servers online with minimal to no downtime: +keep servers online with minimal to no downtime. Redis Sentinel: - Monitors **Master** and **Slaves** instances to see if they are available -- Promote a **Slave** to **Master** when the **Master** fails -- Demote a **Master** to **Slave** when failed **Master** comes back online (to prevent - data-partitioning) +- Promotes a **Slave** to **Master** when the **Master** fails +- Demotes a **Master** to **Slave** when failed **Master** comes back online + (to prevent data-partitioning) - Can be queried by clients to always connect to the current **Master** server When a **Master** fails to respond, it's the client's responsibility to handle @@ -93,24 +125,16 @@ the [Redis Sentinel documentation](http://redis.io/topics/sentinel) first, as failing to configure it correctly can lead to data loss, or can bring your whole cluster down, invalidating the failover effort. -This documentation will provide you with a minimal and a recommended topology -that can resist to some levels of failure. Usually the more Redis and Sentinel -instances you have provisioned, the better will be your availability. - -The configuration consists of three parts: - -- Setup Redis Master and Slave nodes -- Setup Sentinel nodes -- Setup GitLab - ### Prerequisites You need at least `3` independent machines: physical, or VMs running into -distinct physical machines. They must be believed to fail in an -independent way. +distinct physical machines. It is essential that all master and Redis slaves +run in different machines. If you fail to provision the machines in that +specific way, any issue with the shared environment can bring your entire setup +down. -If you fail to provision the machines in that specific way, any issue with -the shared environment can bring your entire setup down. +It is OK to run a Sentinel along with a master or slave Redis instance. +No more than one though. You also need to take in consideration the underlying network topology, making sure you have redundant connectivity between Redis / Sentinel and @@ -119,6 +143,30 @@ failure. Read carefully how to configure the components below. +### Recommended setup + +For a minimal setup, you will install the Omnibus GitLab package in `3` +independent machines, both with **Redis** and **Sentinel**: + +- Redis Master + Sentinel +- Redis Slave + Sentinel +- Redis Slave + Sentinel + +Make sure you've read [Redis Setup](#redis-setup) and [Sentinel Setup](#sentinel-setup) +before, to understand how and why the amount of nodes came from. + +For a recommended setup, that can resist more failures, you will install +the Omnibus GitLab package in `5` independent machines, both with +**Redis** and **Sentinel**: + +- Redis Master + Sentinel +- Redis Slave + Sentinel +- Redis Slave + Sentinel +- Redis Slave + Sentinel +- Redis Slave + Sentinel + +## Redis HA configuration + ### Redis setup You must have at least `3` Redis servers: `1` Master, `2` Slaves, and they @@ -156,14 +204,14 @@ Initial **Slave** nodes requires `redis['master']` defined to `false` and simplified configuration by enabling `redis_slave_role['enable']`, you just need to fill in the `redis['master_ip']`. -This values doesn't have to be changed again in `/etc/gitlab/gitlab.rb` after +This values don't have to be changed again in `/etc/gitlab/gitlab.rb` after a failover, as the nodes will be managed by the Sentinels, and even after a `gitlab-ctl reconfigure`, they will get their configuration restored by the same Sentinels. ### Sentinel setup -Sentinels watches both other sentinels and Redis nodes. Whenever a Sentinel +Sentinels watch both other sentinels and Redis nodes. Whenever a Sentinel detects that a Redis node is not responding, it will announce that to the other sentinels. You have to reach the **quorum**, the minimum amount of sentinels that agrees that a node is down, to be able to start a failover. @@ -222,33 +270,12 @@ official documentation: the slaves will be reconfigured by the Sentinels anyway, but not with the exact parallel-syncs progression as specified. -### Recommended setup - -For a minimal setup, you will install the Omnibus GitLab package in `3` -independent machines, both with **Redis** and **Sentinel**: - -- Redis Master + Sentinel -- Redis Slave + Sentinel -- Redis Slave + Sentinel - -Make sure you've read [Redis Setup](#redis-setup) and [Sentinel Setup](#sentinel-setup) -before, to understand how and why the amount of nodes came from. - -For a recommended setup, that can resist more failures, you will install -the Omnibus GitLab package in `5` independent machines, both with -**Redis** and **Sentinel**: - -- Redis Master + Sentinel -- Redis Slave + Sentinel -- Redis Slave + Sentinel -- Redis Slave + Sentinel -- Redis Slave + Sentinel - ## Configuring instances using Omnibus This is a summary of what are we going to do: 1. Provision the required number of instances specified previously + - You can opt to install Redis and Sentinel in the same machine or each in independent ones. - Don't install Redis and Sentinel in the same machines your GitLab instance @@ -257,7 +284,7 @@ This is a summary of what are we going to do: connection over Redis (`6379`) and Sentinel (`26379`) ports. - GitLab machines must be able to access these machines and with the same permissions. - - Protected them from indiscriminating access from external networks (Internet), + - Protect them from access from external networks (Internet), to harden the security. 1. Download/install Omnibus GitLab using **steps 1 and 2** from @@ -394,7 +421,7 @@ which ideally should not have Redis or Sentinels in the same machine for a HA se See [example configuration](#configuration-for-gitlab) below. -## Example Configurations +## Minimal example configuration with 1 master, 2 slaves and 3 sentinels In this example we consider that all servers have an internal network interface with IPs in the `10.0.0.x` range, and that they can connect @@ -836,3 +863,5 @@ Read more on high-availability configuration: [reconfigure]: ../restart_gitlab.md#omnibus-gitlab-reconfigure [gh-531]: https://github.com/redis/redis-rb/issues/531 [gh-534]: https://github.com/redis/redis-rb/issues/534 +[redis]: http://redis.io/ +[sentinel]: http://redis.io/topics/sentinel -- cgit v1.2.1 From b575b00402da92d15f8edcaab702039e6e92eddc Mon Sep 17 00:00:00 2001 From: Achilleas Pipinellis Date: Thu, 10 Nov 2016 19:00:06 +0100 Subject: Rearrange sections in Sentinel docs [ci skip] --- doc/administration/high_availability/redis.md | 343 ++++++++++++++------------ 1 file changed, 181 insertions(+), 162 deletions(-) diff --git a/doc/administration/high_availability/redis.md b/doc/administration/high_availability/redis.md index 5d4dfe22779..3eaa0ffdcec 100644 --- a/doc/administration/high_availability/redis.md +++ b/doc/administration/high_availability/redis.md @@ -16,82 +16,67 @@ Omnibus GitLab packages. - You are highly encouraged to read the [Redis Sentinel][sentinel] documentation before configuring Redis HA with GitLab to fully understand the topology and architecture. +- This is the documentation for the Omnibus packages. For installations from + source, follow the [Redis HA source install](redis_source.md) guide. +- Redis Sentinel is bundled with Omnibus GitLab Enterprise Edition only. For the + Omnibus Community Edition and installations from source, follow the + [Redis HA source install](redis_source.md) guide. **Table of Contents** - [Overview](#overview) - - [Available setups](#available-setups) - - [Using a non-Omnibus external Redis server](#using-a-non-omnibus-external-redis-server) - - [High Availability with Sentinel](#high-availability-with-sentinel) - [Prerequisites](#prerequisites) + - [High Availability with Sentinel](#high-availability-with-sentinel) - [Recommended setup](#recommended-setup) + - [Available configuration setups](#available-configuration-setups) + - [Using a non-Omnibus external Redis server](#using-a-non-omnibus-external-redis-server) + - [Redis setup overview](#redis-setup-overview) + - [Sentinel setup overview](#sentinel-setup-overview) - [Redis HA configuration](#redis-ha-configuration) - - [Redis setup](#redis-setup) - - [Sentinel setup](#sentinel-setup) -- [Configuring instances using Omnibus](#configuring-instances-using-omnibus) - - [Existing single-machine installation](#existing-single-machine-installation) - - [Configuring Master Redis instance](#configuring-master-redis-instance) - - [Configuring Slave Redis instances](#configuring-slave-redis-instances) - - [Configuring Sentinel instances](#configuring-sentinel-instances) - - [Community Edition](#community-edition) - - [Enterprise Edition](#enterprise-edition) - - [GitLab setup](#gitlab-setup) + - [Configuring the Master Redis instance](#configuring-the-master-redis-instance) + - [Configuring the Slave Redis instances](#configuring-the-slave-redis-instances) + - [Configuring the Sentinel instances](#configuring-the-sentinel-instances) + - [Configuring the GitLab application](#configuring-the-gitlab-application) +- [Switching from an existing single-machine installation to Redis HA](#switching-from-an-existing-single-machine-installation-to-redis-ha) - [Minimal example configuration with 1 master, 2 slaves and 3 sentinels](#minimal-example-configuration-with-1-master-2-slaves-and-3-sentinels) - - [Configuration for Redis Master](#configuration-for-redis-master) - - [Configuration for Redis Slave](#configuration-for-redis-slave) - - [Configuration for Sentinel (EE only)](#configuration-for-sentinel-ee-only) + - [Configuration for Redis master](#configuration-for-redis-master) + - [Configuration for Redis slaves](#configuration-for-redis-slaves) + - [Configuration for Sentinels](#configuration-for-sentinels) +- [Advanced configuration](#advanced-configuration) - [Control running services](#control-running-services) - [Troubleshooting](#troubleshooting) - - [Redis replication](#redis-replication) - - [Sentinel](#sentinel) - - [Omnibus GitLab](#omnibus-gitlab) + - [Troubleshooting Redis replication](#troubleshooting-redis-replication) + - [Troubleshooting Sentinel](#troubleshooting-sentinel) - [Changelog](#changelog) - [Experimental Redis Sentinel support](#experimental-redis-sentinel-support) - ## Overview Before diving into the details of setting up Redis and Redis Sentinel for HA, -make sure you read this section to better understand the underline architecture. - -### Available setups - -Based on your infrastructure setup, there are multiple ways to setup Redis HA -with GitLab. Omnibus GitLab packages have Redis and Redis Sentinel bundled with -them to save you the hassle to install it yourself. Pick the one that suits your -needs. - -- **Installations from source:** You need to install Redis and Redis Sentinel - yourself. Use the [Redis HA source install](redis_source.md) guide. -- **Omnibus Community Edition (CE):** Redis is bundled so you can use the - package with only the Redis service enabled (works for both master and slave - setups). -- **Omnibus Enterprise Edition (EE):** Both Redis and Redis Sentinel are bundled - in the Omnibus package so you can use only them to setup the whole Redis HA - infrastructure (master, slave and Sentinel). - -Note that with the Omnibus packages (both CE and EE), you can also use an -[external Redis server](#using-a-non-omnibus-external-redis-server). +make sure you read this Overview section to better understand how the components +are tied together. -### Using a non-Omnibus external Redis server +### Prerequisites -If you're hosting GitLab on a cloud provider, you can optionally use a -managed service for Redis. For example, AWS offers a managed ElastiCache service -that runs Redis. +You need at least `3` independent machines: physical, or VMs running into +distinct physical machines. It is essential that all master and slaves Redis +instances run in different machines. If you fail to provision the machines in +that specific way, any issue with the shared environment can bring your entire +setup down. -Managed services can provide High Availability using their own proprietary -technology and provide a transparent proxy (which means that GitLab doesn't -need any additional change) or they will use Sentinel and manage it for you. +It is OK to run a Sentinel along with a master or slave Redis instance. +No more than one Sentinel in the same machine though. -If your provider, uses Sentinel, see [GitLab Setup](#gitlab-setup) -to understand where you need to provide the list of servers and credentials. +You also need to take in consideration the underlying network topology, +making sure you have redundant connectivity between Redis / Sentinel and +GitLab instances, otherwise the networks will become a single point of +failure. -If you want to setup Redis by yourself, without using Omnibus, you can -read the documentation on [configuring Redis HA for source installs](redis_source.md). +Read carefully how to configure the components below. ### High Availability with Sentinel @@ -108,12 +93,12 @@ High Availability with Redis requires a few things: - Multiple Sentinel instances - Application support and visibility to all Sentinel and Redis instances -Redis Sentinel can handle the most important tasks in a HA environment to help -keep servers online with minimal to no downtime. Redis Sentinel: +Redis Sentinel can handle the most important tasks in a HA environment and that's +to help keep servers online with minimal to no downtime. Redis Sentinel: - Monitors **Master** and **Slaves** instances to see if they are available - Promotes a **Slave** to **Master** when the **Master** fails -- Demotes a **Master** to **Slave** when failed **Master** comes back online +- Demotes a **Master** to **Slave** when the failed **Master** comes back online (to prevent data-partitioning) - Can be queried by clients to always connect to the current **Master** server @@ -122,41 +107,23 @@ timeout and reconnect (querying a **Sentinel** for a new **Master**). To get a better understanding on how to correctly setup Sentinel, please read the [Redis Sentinel documentation](http://redis.io/topics/sentinel) first, as -failing to configure it correctly can lead to data loss, or can bring your +failing to configure it correctly can lead to data loss or can bring your whole cluster down, invalidating the failover effort. -### Prerequisites - -You need at least `3` independent machines: physical, or VMs running into -distinct physical machines. It is essential that all master and Redis slaves -run in different machines. If you fail to provision the machines in that -specific way, any issue with the shared environment can bring your entire setup -down. - -It is OK to run a Sentinel along with a master or slave Redis instance. -No more than one though. - -You also need to take in consideration the underlying network topology, -making sure you have redundant connectivity between Redis / Sentinel and -GitLab instances, otherwise the networks will become a single point of -failure. - -Read carefully how to configure the components below. - ### Recommended setup For a minimal setup, you will install the Omnibus GitLab package in `3` -independent machines, both with **Redis** and **Sentinel**: +**independent** machines, both with **Redis** and **Sentinel**: - Redis Master + Sentinel - Redis Slave + Sentinel - Redis Slave + Sentinel -Make sure you've read [Redis Setup](#redis-setup) and [Sentinel Setup](#sentinel-setup) -before, to understand how and why the amount of nodes came from. +If you are not sure or don't understand why and where the amount of nodes come +from, read [Redis Setup](#redis-setup) and [Sentinel Setup](#sentinel-setup). -For a recommended setup, that can resist more failures, you will install -the Omnibus GitLab package in `5` independent machines, both with +For a recommended setup that can resist more failures, you will install +the Omnibus GitLab package in `5` **independent** machines, both with **Redis** and **Sentinel**: - Redis Master + Sentinel @@ -165,9 +132,42 @@ the Omnibus GitLab package in `5` independent machines, both with - Redis Slave + Sentinel - Redis Slave + Sentinel -## Redis HA configuration +### Available configuration setups + +Based on your infrastructure setup and how you have installed GitLab, there are +multiple ways to configure Redis HA. Omnibus GitLab packages have Redis and/or +Redis Sentinel bundled with them to save you the hassle to install them yourself. +Pick the one that suits your needs. + +- [Installations from source][source]: You need to install Redis and Sentinel + yourself. Use the [Redis HA source install](redis_source.md) guide. +- [Omnibus package Community Edition (CE)][ce]: Redis is bundled, so you can use the + package with only the Redis service enabled (works for both master and slave + setups). +- [Omnibus package Enterprise Edition (EE)][ee]: Both Redis and Sentinel are bundled, + so you can use the EE package to setup the whole Redis HA infrastructure + (master, slave and Sentinel). + +Note that if you have installed GitLab using the Omnibus packages (both CE and EE), +you can also use an [external Redis server](#using-a-non-omnibus-external-redis-server). + +### Using a non-Omnibus external Redis server + +If you're hosting GitLab on a cloud provider, you can optionally use a +managed service for Redis. For example, AWS offers a managed ElastiCache service +that runs Redis. + +Managed services can provide High Availability using their own proprietary +technology and provide a transparent proxy (which means that GitLab doesn't +need any additional change) or they will use Sentinel and manage it for you. + +If your provider uses Sentinel, see [GitLab Setup](#gitlab-setup) +to understand where you need to provide the list of servers and credentials. + +If you want to setup Redis by yourself, without using Omnibus, you can +read the documentation on [configuring Redis HA for source installs](redis_source.md). -### Redis setup +### Redis setup overview You must have at least `3` Redis servers: `1` Master, `2` Slaves, and they need to be each in a independent machine (see explanation above). @@ -194,22 +194,7 @@ protect all Redis nodes and the Sentinels. They will all share the same password, and all instances must be able to talk to each other over the network. -Redis nodes will need the same password defined in `redis['password']` and -`redis['master_password']`, no matter if **Master** or **Slave**. At any time -during a failover the Sentinels can reconfigure a node and change it's status -from **Master** to **Slave** and vice versa. - -Initial **Slave** nodes requires `redis['master']` defined to `false` and -`redis['master_ip']` pointing to the initial **Master**. If you use the -simplified configuration by enabling `redis_slave_role['enable']`, you -just need to fill in the `redis['master_ip']`. - -This values don't have to be changed again in `/etc/gitlab/gitlab.rb` after -a failover, as the nodes will be managed by the Sentinels, and even after a -`gitlab-ctl reconfigure`, they will get their configuration restored by -the same Sentinels. - -### Sentinel setup +### Sentinel setup overview Sentinels watch both other sentinels and Redis nodes. Whenever a Sentinel detects that a Redis node is not responding, it will announce that to the @@ -249,8 +234,11 @@ is not achieved (see the odd number of nodes requirement above). In that case, a new attempt will be made after the amount of time defined in `sentinel['failover_timeout']` (in milliseconds). -The `failover_time` variable have a lot of different usages, according to -official documentation: +>**Note:** +We will see where `sentinel['failover_timeout']` is defined later. + +The `failover_timeout` variable has a lot of different use cases, according to +the official documentation: - The time needed to re-start a failover after a previous failover was already tried against the same master by a given Sentinel, is two @@ -270,18 +258,29 @@ official documentation: the slaves will be reconfigured by the Sentinels anyway, but not with the exact parallel-syncs progression as specified. -## Configuring instances using Omnibus +## Redis HA configuration + +This is the section where we install and setup the new Redis instances. -This is a summary of what are we going to do: +>**Notes:** +- We assume that you install GitLab and all HA components from scratch. If you + already have it installed and running, read how to + [switch from a single-machine installation to Redis HA](#switching-from-an-existing-single-machine-installation-to-redis-ha). +- Redis nodes (both master and slaves) will need the same password defined in + `redis['password']` and `redis['master_password']`. At any time during a + failover the Sentinels can reconfigure a node and change its status + from master to slave and vice versa. -1. Provision the required number of instances specified previously +A summary of what are we going to do: +1. Provision the required number of instances specified previously: - You can opt to install Redis and Sentinel in the same machine or each in independent ones. - - Don't install Redis and Sentinel in the same machines your GitLab instance + - Don't install Redis and Sentinel in the same machines your GitLab application is running on. - All machines must be able to talk to each other and accept incoming - connection over Redis (`6379`) and Sentinel (`26379`) ports. + connections over Redis (`6379`) and Sentinel (`26379`) ports (unless you + change the default ports). - GitLab machines must be able to access these machines and with the same permissions. - Protect them from access from external networks (Internet), @@ -297,32 +296,10 @@ This is a summary of what are we going to do: from running on upgrade. Only the primary GitLab application server should handle migrations. -1. Create/edit `/etc/gitlab/gitlab.rb` and make the changes based on the +1. Edit `/etc/gitlab/gitlab.rb` and make the changes based on the [Example Configurations](#example-configurations). -### Existing single-machine installation - -If you already have a single-machine GitLab install running, you will need to -replicate from this machine first, before de-activating the Redis instance -inside it. - -Your single-machine install will be the initial **Master**, and the `3` others -should be configured as **Slave** pointing to this machine. - -After replication catches up, you will need to stop services in the -single-machine install, to rotate the **Master** to one of the new nodes. - -Make the required changes in configuration and restart the new nodes again. - -To disable redis in the single install, edit `/etc/gitlab/gitlab.rb`: - -```ruby -redis['enable'] = false -``` - -If you fail to replicate first, you may loose data (unprocessed background jobs). - -### Configuring Master Redis instance +### Configuring the Master Redis instance You will need to configure the following in `/etc/gitlab/gitlab.rb`: @@ -341,9 +318,9 @@ You will need to configure the following in `/etc/gitlab/gitlab.rb`: 1. Set up a password authentication with `redis['password']` and `redis['master_password']` (use the same password in all nodes). -Reconfigure Omnibus GitLab for the changes to take effect: `sudo gitlab-ctl reconfigure` +1. [Reconfigure Omnibus GitLab][reocnfigure] for the changes to take effect. -### Configuring Slave Redis instances +### Configuring the Slave Redis instances You will need to configure the following in `/etc/gitlab/gitlab.rb`: @@ -367,7 +344,22 @@ You will need to configure the following in `/etc/gitlab/gitlab.rb`: 1. Define `redis['master_port']` with the port of the **Master** Redis (default to `6379`). -### Configuring Sentinel instances +Initial **Slave** nodes require `redis['master']` defined to `false` and +`redis['master_ip']` pointing to the initial **Master**. If you use the +simplified configuration by enabling `redis_slave_role['enable']`, you +just need to fill in the `redis['master_ip']`. + +This values don't have to be changed again in `/etc/gitlab/gitlab.rb` after +a failover, as the nodes will be managed by the Sentinels, and even after a +`gitlab-ctl reconfigure`, they will get their configuration restored by +the same Sentinels. + +### Configuring the Sentinel instances + +>**Note:** +- Redis Sentinel is bundled with Omnibus GitLab Enterprise Edition only. For the + Omnibus Community Edition and installations from source, follow the + [Redis HA source install](redis_source.md) guide. Now that the Redis servers are all set up, let's configure the Sentinel servers. @@ -380,22 +372,12 @@ You must have at least `3` Redis Sentinel servers, and they need to be each in a independent machine. You can configure them in the same machines where you've configured the other Redis servers. -##### Community Edition - -With GitLab Community Edition, you need to install, configure, execute and -monitor Sentinel from source. Omnibus GitLab Community Edition package does -not support Sentinel configuration. - -See [documentation for Source Install](redis_source.md). - -##### Enterprise Edition - -With GitLab Enterprise Edition, you can use Omnibus package to setup multiple -machines with Sentinel daemon. +With GitLab Enterprise Edition, you can use the Omnibus package to setup multiple +machines with the Sentinel daemon. See [example configuration](#configuration-for-sentinel-ee-only) below. -### GitLab setup +### Configuring the GitLab application The final part is to inform the main GitLab application server of the Redis Sentinels servers and authentication credentials. @@ -409,7 +391,7 @@ it needs to access at least one of the listed. >**Note:** The following steps should be performed in the [GitLab application server](gitlab.md) -which ideally should not have Redis or Sentinels in the same machine for a HA setup. +which ideally should not have Redis or Sentinels on it for a HA setup. 1. Edit `/etc/gitlab/gitlab.rb` and add/change the following lines: @@ -417,10 +399,32 @@ which ideally should not have Redis or Sentinels in the same machine for a HA se - `redis['master_password']` - the same password you've defined before for Redis and Sentinels - `gitlab_rails['redis_sentinels']` - a list of sentinels with `host` and `port` -1. [Reconfigure] GitLab for the changes to take effect. +1. [Reconfigure Omnibus GitLab][reconfigure] for the changes to take effect. See [example configuration](#configuration-for-gitlab) below. +## Switching from an existing single-machine installation to Redis HA + +If you already have a single-machine GitLab install running, you will need to +replicate from this machine first, before de-activating the Redis instance +inside it. + +Your single-machine install will be the initial **Master**, and the `3` others +should be configured as **Slave** pointing to this machine. + +After replication catches up, you will need to stop services in the +single-machine install, to rotate the **Master** to one of the new nodes. + +Make the required changes in configuration and restart the new nodes again. + +To disable redis in the single install, edit `/etc/gitlab/gitlab.rb`: + +```ruby +redis['enable'] = false +``` + +If you fail to replicate first, you may loose data (unprocessed background jobs). + ## Minimal example configuration with 1 master, 2 slaves and 3 sentinels In this example we consider that all servers have an internal network @@ -428,7 +432,7 @@ interface with IPs in the `10.0.0.x` range, and that they can connect to each other using these IPs. In a real world usage, you would also setup firewall rules to prevent -unauthorized access from other machines, and block traffic from the +unauthorized access from other machines and block traffic from the outside (Internet). We will use the same `3` nodes with **Redis** + **Sentinel** topology @@ -450,7 +454,7 @@ The same thing will happen with `sentinel.conf` that will be overridden after th initial execution, after any new sentinel node starts watching the **Master**, or a failover promotes a different **Master** node. -### Configuration for Redis Master +### Configuration for Redis master **Example configation for Redis Master:** @@ -464,9 +468,10 @@ redis['port'] = 6379 redis['password'] = 'redis-password-goes-here' redis['master_password'] = 'redis-password-goes-here' ``` -Reconfigure Omnibus GitLab for the changes to take effect: `sudo gitlab-ctl reconfigure` -### Configuration for Redis Slave +[Reconfigure Omnibus GitLab][reconfigure] for the changes to take effect. + +### Configuration for Redis slaves **Example configation for Slave 1:** @@ -502,9 +507,14 @@ redis['master_ip'] = '10.0.0.1' # IP of master Redis server #redis['master_port'] = 6379 # Port of master Redis server, uncomment to change to non default ``` -Reconfigure Omnibus GitLab for the changes to take effect: `sudo gitlab-ctl reconfigure` +[Reconfigure Omnibus GitLab][reconfigure] for the changes to take effect. + +### Configuration for Sentinels -### Configuration for Sentinel (EE only) +>**Note:** +Redis Sentinel is bundled with Omnibus GitLab Enterprise Edition only. For the +Omnibus Community Edition and installations from source, follow the +[Redis HA source install](redis_source.md) guide. Please note that some of the variables are already configured previously as they are required for Redis replication. @@ -517,9 +527,9 @@ In `/etc/gitlab/gitlab.rb`: redis_sentinel_role['enable'] = true redis['master_name'] = 'gitlab-redis' # must be the same in every sentinel node +redis['master_password'] = 'redis-password-goes-here' # the same value defined in redis['password'] in the master instance redis['master_ip'] = '10.0.0.1' # ip of the initial master redis instance #redis['master_port'] = 6379 # port of the initial master redis instance, uncomment to change to non default -redis['master_password'] = 'redis-password-goes-here' # the same value defined in redis['password'] in the master instance ## Configure Sentinel sentinel['bind'] = '10.0.0.1' @@ -571,9 +581,9 @@ In `/etc/gitlab/gitlab.rb`: redis_sentinel_role['enable'] = true redis['master_name'] = 'gitlab-redis' # must be the same in every sentinel node +redis['master_password'] = 'redis-password-goes-here' # the same value defined in redis['password'] in the master instance redis['master_ip'] = '10.0.0.1' # ip of the initial master redis instance #redis['master_port'] = 6379 # port of the initial master redis instance, uncomment to change to non default -redis['master_password'] = 'redis-password-goes-here' # the same value defined in redis['password'] in the master instance ## Configure Sentinel sentinel['bind'] = '10.0.0.2' @@ -625,9 +635,9 @@ In `/etc/gitlab/gitlab.rb`: redis_sentinel_role['enable'] = true redis['master_name'] = 'gitlab-redis' # must be the same in every sentinel node +redis['master_password'] = 'redis-password-goes-here' # the same value defined in redis['password'] in the master instance redis['master_ip'] = '10.0.0.1' # ip of the initial master redis instance #redis['master_port'] = 6379 # port of the initial master redis instance, uncomment to change to non default -redis['master_password'] = 'redis-password-goes-here' # the same value defined in redis['password'] in the master instance ## Configure Sentinel sentinel['bind'] = '10.0.0.3' @@ -671,10 +681,15 @@ sentinel['quorum'] = 2 # sentinel['failover_timeout'] = 60000 ``` +## Advanced configuration + +Omnibus GitLab configures some things behind the curtains to make the sysadmins' +lives easier. If you want to know what happens underneath keep reading. + ### Control running services -In the example above we've used `redis_sentinel_role` and `redis_master_role` -which simplify the amount of configuration changes. +In the previous example above we've used `redis_sentinel_role` and +`redis_master_role` which simplify the amount of configuration changes. If you want more control, here is what each one sets for you automatically when enabled: @@ -716,13 +731,15 @@ mailroom['enable'] = false redis['master'] = false ``` +You can find the relevant attributes defined in [gitlab_rails.rb][omnifile]. + ## Troubleshooting There are a lot of moving parts that needs to be taken care carefully in order for the HA setup to work as expected. -Before proceeding with the troubleshooting below, check your firewall -rules: +Before proceeding with the troubleshooting below, check your firewall rules: + - Redis machines - Accept TCP connection in `6379` - Connect to the other Redis machines via TCP in `6379` @@ -731,7 +748,7 @@ rules: - Connect to other Sentinel machines via TCP in `26379` - Connect to the Redis machines via TCP in `6379` -### Redis replication +### Troubleshooting Redis replication You can check if everything is correct by connecting to each server using `redis-cli` application, and sending the `INFO` command. @@ -781,9 +798,7 @@ repl_backlog_first_byte_offset:0 repl_backlog_histlen:0 ``` -### Sentinel - -#### Omnibus GitLab +### Troubleshooting Sentinel If you get an error like: `Redis::CannotConnectError: No sentinels available.`, there may be something wrong with your configuration files or it can be related @@ -865,3 +880,7 @@ Read more on high-availability configuration: [gh-534]: https://github.com/redis/redis-rb/issues/534 [redis]: http://redis.io/ [sentinel]: http://redis.io/topics/sentinel +[omnifile]: https://gitlab.com/gitlab-org/omnibus-gitlab/blob/82b7345b150f072c8673c79738ce893f92d0d652/files/gitlab-cookbooks/gitlab/libraries/gitlab_rails.rb#L134-159 +[source]: ../../install/installation.md +[ce]: https://about.gitlab.com/downloads +[ee]: https://about.gitlab.com/downloads-ee -- cgit v1.2.1 From be3d74e096f6b5cb46b5d2440c16383633f1fc06 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Fri, 11 Nov 2016 10:16:03 +0100 Subject: Do not call environments service in repository model --- app/models/repository.rb | 15 ++------------- app/services/git_push_service.rb | 2 +- lib/gitlab/github_import/importer.rb | 2 +- spec/models/repository_spec.rb | 13 +------------ 4 files changed, 5 insertions(+), 27 deletions(-) diff --git a/app/models/repository.rb b/app/models/repository.rb index 38e5cd3faa9..30be7262438 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -203,7 +203,7 @@ class Repository update_ref!(ref, newrev, oldrev) end - after_remove_branch(user, branch_name) + after_remove_branch true end @@ -524,12 +524,7 @@ class Repository end # Runs code after an existing branch has been removed. - def after_remove_branch(user, branch_name) - expire_branch_cache_after_removal - stop_environments_for_branch(user, branch_name) - end - - def expire_branch_cache_after_removal + def after_remove_branch expire_has_visible_content_cache expire_branch_count_cache expire_branches_cache @@ -1170,10 +1165,4 @@ class Repository def repository_event(event, tags = {}) Gitlab::Metrics.add_event(event, { path: path_with_namespace }.merge(tags)) end - - def stop_environments_for_branch(user, branch_name) - Ci::StopEnvironmentService - .new(@project, user) - .execute(branch_name) - end end diff --git a/app/services/git_push_service.rb b/app/services/git_push_service.rb index ec1c2f61c27..de313095bed 100644 --- a/app/services/git_push_service.rb +++ b/app/services/git_push_service.rb @@ -21,7 +21,7 @@ class GitPushService < BaseService @project.repository.after_push_commit(branch_name, params[:newrev]) if push_remove_branch? - @project.repository.after_remove_branch(current_user, branch_name) + @project.repository.after_remove_branch @push_commits = [] elsif push_to_new_branch? @project.repository.after_create_branch diff --git a/lib/gitlab/github_import/importer.rb b/lib/gitlab/github_import/importer.rb index c724577ae89..90cf38a8513 100644 --- a/lib/gitlab/github_import/importer.rb +++ b/lib/gitlab/github_import/importer.rb @@ -111,7 +111,7 @@ module Gitlab end end - project.repository.expire_branch_cache_after_removal + project.repository.after_remove_branch end def restore_source_branch(pull_request) diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb index 6d65f6ead12..04b7d19d414 100644 --- a/spec/models/repository_spec.rb +++ b/spec/models/repository_spec.rb @@ -1182,18 +1182,7 @@ describe Repository, models: true do it 'flushes the visible content cache' do expect(repository).to receive(:expire_has_visible_content_cache) - repository.after_remove_branch(user, 'master') - end - - context 'when there is environment with review app available for branch' do - before do - create(:environment, :with_review_app, project: project) - end - - it 'stops environment' do - expect_any_instance_of(Environment).to receive(:stop!) - repository.after_remove_branch(user, 'master') - end + repository.after_remove_branch end end -- cgit v1.2.1 From ec0daedbc45104324387804ef7c3c9337fde1fda Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Fri, 11 Nov 2016 10:33:16 +0100 Subject: Add service that runs after branch removed hooks --- app/services/after_branch_delete_service.rb | 23 +++++++++++++++++++++++ app/services/delete_branch_service.rb | 9 +++++++++ app/services/git_push_service.rb | 19 +++++++++++++------ 3 files changed, 45 insertions(+), 6 deletions(-) create mode 100644 app/services/after_branch_delete_service.rb diff --git a/app/services/after_branch_delete_service.rb b/app/services/after_branch_delete_service.rb new file mode 100644 index 00000000000..c157cb68715 --- /dev/null +++ b/app/services/after_branch_delete_service.rb @@ -0,0 +1,23 @@ +require_relative 'base_service' + +## +# Branch can be deleted either by DeleteBranchService +# or by GitPushService. +# +class AfterBranchDeleteService < BaseService + attr_reader :branch_name + + def execute(branch_name) + @branch_name = branch_name + + stop_environments + end + + private + + def stop_environments + Ci::StopEnvironmentService + .new(project, current_user) + .execute(branch_name) + end +end diff --git a/app/services/delete_branch_service.rb b/app/services/delete_branch_service.rb index 3e5dd4ebb86..a9fe8198172 100644 --- a/app/services/delete_branch_service.rb +++ b/app/services/delete_branch_service.rb @@ -22,6 +22,7 @@ class DeleteBranchService < BaseService end if repository.rm_branch(current_user, branch_name) + execute_after_branch_delete_hooks(branch_name) success('Branch was removed') else error('Failed to remove branch') @@ -47,4 +48,12 @@ class DeleteBranchService < BaseService "#{Gitlab::Git::BRANCH_REF_PREFIX}#{branch.name}", []) end + + private + + def execute_after_branch_delete_hooks(branch_name) + AfterBranchDeleteService + .new(project, current_user) + .execute(branch_name) + end end diff --git a/app/services/git_push_service.rb b/app/services/git_push_service.rb index de313095bed..77c6c81cc1b 100644 --- a/app/services/git_push_service.rb +++ b/app/services/git_push_service.rb @@ -49,10 +49,7 @@ class GitPushService < BaseService update_gitattributes if is_default_branch? end - # Update merge requests that may be affected by this push. A new branch - # could cause the last commit of a merge request to change. - update_merge_requests - + execute_related_hooks perform_housekeeping end @@ -62,14 +59,24 @@ class GitPushService < BaseService protected - def update_merge_requests - UpdateMergeRequestsWorker.perform_async(@project.id, current_user.id, params[:oldrev], params[:newrev], params[:ref]) + def execute_related_hooks + # Update merge requests that may be affected by this push. A new branch + # could cause the last commit of a merge request to change. + # + UpdateMergeRequestsWorker + .perform_async(@project.id, current_user.id, params[:oldrev], params[:newrev], params[:ref]) EventCreateService.new.push(@project, current_user, build_push_data) @project.execute_hooks(build_push_data.dup, :push_hooks) @project.execute_services(build_push_data.dup, :push_hooks) Ci::CreatePipelineService.new(@project, current_user, build_push_data).execute ProjectCacheWorker.perform_async(@project.id) + + if push_remove_branch? + AfterBranchDeleteService + .new(project, current_user) + .execute(branch_name) + end end def perform_housekeeping -- cgit v1.2.1 From 64d2eff9ccf716395d101f2ac91db44c47d7394e Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Thu, 20 Oct 2016 08:20:12 +0000 Subject: Set 'request_access_enabled' false by default Set request access for projects/groups to false by default. Request access implemented in !5286 8.10 was enabled by default. --- db/migrate/20161020075734_default_request_access_groups.rb | 12 ++++++++++++ db/migrate/20161020075830_default_request_access_projects.rb | 12 ++++++++++++ db/schema.rb | 4 ++-- 3 files changed, 26 insertions(+), 2 deletions(-) create mode 100644 db/migrate/20161020075734_default_request_access_groups.rb create mode 100644 db/migrate/20161020075830_default_request_access_projects.rb diff --git a/db/migrate/20161020075734_default_request_access_groups.rb b/db/migrate/20161020075734_default_request_access_groups.rb new file mode 100644 index 00000000000..9721cc88724 --- /dev/null +++ b/db/migrate/20161020075734_default_request_access_groups.rb @@ -0,0 +1,12 @@ +class DefaultRequestAccessGroups < ActiveRecord::Migration + include Gitlab::Database::MigrationHelpers + DOWNTIME = false + + def up + change_column_default :namespaces, :request_access_enabled, false + end + + def down + change_column_default :namespaces, :request_access_enabled, true + end +end diff --git a/db/migrate/20161020075830_default_request_access_projects.rb b/db/migrate/20161020075830_default_request_access_projects.rb new file mode 100644 index 00000000000..cb790291b24 --- /dev/null +++ b/db/migrate/20161020075830_default_request_access_projects.rb @@ -0,0 +1,12 @@ +class DefaultRequestAccessProjects < ActiveRecord::Migration + include Gitlab::Database::MigrationHelpers + DOWNTIME = false + + def up + change_column_default :projects, :request_access_enabled, false + end + + def down + change_column_default :projects, :request_access_enabled, true + end +end diff --git a/db/schema.rb b/db/schema.rb index 62c325a52d7..1c982fc5799 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -720,7 +720,7 @@ ActiveRecord::Schema.define(version: 20161106185620) do t.string "avatar" t.boolean "share_with_group_lock", default: false t.integer "visibility_level", default: 20, null: false - t.boolean "request_access_enabled", default: true, null: false + t.boolean "request_access_enabled", default: false, null: false t.datetime "deleted_at" t.boolean "lfs_enabled" t.text "description_html" @@ -908,7 +908,7 @@ ActiveRecord::Schema.define(version: 20161106185620) do t.boolean "only_allow_merge_if_build_succeeds", default: false, null: false t.boolean "has_external_issue_tracker" t.string "repository_storage", default: "default", null: false - t.boolean "request_access_enabled", default: true, null: false + t.boolean "request_access_enabled", default: false, null: false t.boolean "has_external_wiki" t.boolean "lfs_enabled" t.text "description_html" -- cgit v1.2.1 From d211011698016bd4f04bc32e7450d6df470ea0c2 Mon Sep 17 00:00:00 2001 From: Nick Thomas Date: Fri, 11 Nov 2016 12:51:50 +0000 Subject: Make access request specs explicitly enable or disable access requests as required --- .../21992-disable-access-requests-by-default.yml | 4 +++ .../groups/group_members_controller_spec.rb | 2 +- .../projects/project_members_controller_spec.rb | 2 +- spec/factories/groups.rb | 4 +++ spec/factories/projects.rb | 4 +++ .../members/owner_manages_access_requests_spec.rb | 2 +- .../groups/members/user_requests_access_spec.rb | 2 +- ...uester_cannot_request_access_to_project_spec.rb | 4 +-- .../members/master_manages_access_requests_spec.rb | 2 +- .../projects/members/user_requests_access_spec.rb | 2 +- spec/finders/access_requests_finder_spec.rb | 15 +++++++---- spec/helpers/members_helper_spec.rb | 8 +++--- spec/mailers/notify_spec.rb | 15 +++++++---- spec/models/concerns/access_requestable_spec.rb | 8 +++--- spec/models/group_spec.rb | 2 +- spec/models/member_spec.rb | 4 +-- spec/models/project_spec.rb | 2 +- spec/models/project_team_spec.rb | 8 +++--- spec/models/user_spec.rb | 4 +-- spec/requests/api/access_requests_spec.rb | 22 +++++++-------- spec/requests/api/members_spec.rb | 20 +++++++------- .../members/approve_access_request_service_spec.rb | 4 +-- spec/services/members/destroy_service_spec.rb | 1 + .../members/request_access_service_spec.rb | 31 ++++++++++------------ 24 files changed, 96 insertions(+), 76 deletions(-) create mode 100644 changelogs/unreleased/21992-disable-access-requests-by-default.yml diff --git a/changelogs/unreleased/21992-disable-access-requests-by-default.yml b/changelogs/unreleased/21992-disable-access-requests-by-default.yml new file mode 100644 index 00000000000..ddcb2169407 --- /dev/null +++ b/changelogs/unreleased/21992-disable-access-requests-by-default.yml @@ -0,0 +1,4 @@ +--- +title: Disable "Request Access" functionality by default for new projects and groups +merge_request: 7425 +author: diff --git a/spec/controllers/groups/group_members_controller_spec.rb b/spec/controllers/groups/group_members_controller_spec.rb index c7db84dd5f9..60db0192dfd 100644 --- a/spec/controllers/groups/group_members_controller_spec.rb +++ b/spec/controllers/groups/group_members_controller_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' describe Groups::GroupMembersController do let(:user) { create(:user) } - let(:group) { create(:group, :public) } + let(:group) { create(:group, :public, :access_requestable) } describe 'GET index' do it 'renders index with 200 status code' do diff --git a/spec/controllers/projects/project_members_controller_spec.rb b/spec/controllers/projects/project_members_controller_spec.rb index 2a7523c6512..b52137fbe7e 100644 --- a/spec/controllers/projects/project_members_controller_spec.rb +++ b/spec/controllers/projects/project_members_controller_spec.rb @@ -2,7 +2,7 @@ require('spec_helper') describe Projects::ProjectMembersController do let(:user) { create(:user) } - let(:project) { create(:project, :public) } + let(:project) { create(:empty_project, :public, :access_requestable) } describe 'GET index' do it 'renders index with 200 status code' do diff --git a/spec/factories/groups.rb b/spec/factories/groups.rb index 2d47a6f6c4c..ebd3595ea64 100644 --- a/spec/factories/groups.rb +++ b/spec/factories/groups.rb @@ -15,5 +15,9 @@ FactoryGirl.define do trait :private do visibility_level Gitlab::VisibilityLevel::PRIVATE end + + trait :access_requestable do + request_access_enabled true + end end end diff --git a/spec/factories/projects.rb b/spec/factories/projects.rb index bfd88a254f1..1166498ddff 100644 --- a/spec/factories/projects.rb +++ b/spec/factories/projects.rb @@ -24,6 +24,10 @@ FactoryGirl.define do visibility_level Gitlab::VisibilityLevel::PRIVATE end + trait :access_requestable do + request_access_enabled true + end + trait :empty_repo do after(:create) do |project| project.create_repository diff --git a/spec/features/groups/members/owner_manages_access_requests_spec.rb b/spec/features/groups/members/owner_manages_access_requests_spec.rb index d811b05b0c3..dbe150823ba 100644 --- a/spec/features/groups/members/owner_manages_access_requests_spec.rb +++ b/spec/features/groups/members/owner_manages_access_requests_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' feature 'Groups > Members > Owner manages access requests', feature: true do let(:user) { create(:user) } let(:owner) { create(:user) } - let(:group) { create(:group, :public) } + let(:group) { create(:group, :public, :access_requestable) } background do group.request_access(user) diff --git a/spec/features/groups/members/user_requests_access_spec.rb b/spec/features/groups/members/user_requests_access_spec.rb index b3baa2ab57c..d8c9c487996 100644 --- a/spec/features/groups/members/user_requests_access_spec.rb +++ b/spec/features/groups/members/user_requests_access_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' feature 'Groups > Members > User requests access', feature: true do let(:user) { create(:user) } let(:owner) { create(:user) } - let(:group) { create(:group, :public) } + let(:group) { create(:group, :public, :access_requestable) } let!(:project) { create(:project, :private, namespace: group) } background do diff --git a/spec/features/projects/members/group_requester_cannot_request_access_to_project_spec.rb b/spec/features/projects/members/group_requester_cannot_request_access_to_project_spec.rb index c4ed92d2780..4973e0aee85 100644 --- a/spec/features/projects/members/group_requester_cannot_request_access_to_project_spec.rb +++ b/spec/features/projects/members/group_requester_cannot_request_access_to_project_spec.rb @@ -3,8 +3,8 @@ require 'spec_helper' feature 'Projects > Members > Group requester cannot request access to project', feature: true do let(:user) { create(:user) } let(:owner) { create(:user) } - let(:group) { create(:group, :public) } - let(:project) { create(:project, :public, namespace: group) } + let(:group) { create(:group, :public, :access_requestable) } + let(:project) { create(:project, :public, :access_requestable, namespace: group) } background do group.add_owner(owner) diff --git a/spec/features/projects/members/master_manages_access_requests_spec.rb b/spec/features/projects/members/master_manages_access_requests_spec.rb index d15376931c3..143390b71cd 100644 --- a/spec/features/projects/members/master_manages_access_requests_spec.rb +++ b/spec/features/projects/members/master_manages_access_requests_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' feature 'Projects > Members > Master manages access requests', feature: true do let(:user) { create(:user) } let(:master) { create(:user) } - let(:project) { create(:project, :public) } + let(:project) { create(:empty_project, :public, :access_requestable) } background do project.request_access(user) diff --git a/spec/features/projects/members/user_requests_access_spec.rb b/spec/features/projects/members/user_requests_access_spec.rb index 56ede8eb5be..97c42bd7f01 100644 --- a/spec/features/projects/members/user_requests_access_spec.rb +++ b/spec/features/projects/members/user_requests_access_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' feature 'Projects > Members > User requests access', feature: true do let(:user) { create(:user) } let(:master) { create(:user) } - let(:project) { create(:project, :public) } + let(:project) { create(:project, :public, :access_requestable) } background do project.team << [master, :master] diff --git a/spec/finders/access_requests_finder_spec.rb b/spec/finders/access_requests_finder_spec.rb index 8cfea9659cb..c7278e971ae 100644 --- a/spec/finders/access_requests_finder_spec.rb +++ b/spec/finders/access_requests_finder_spec.rb @@ -3,12 +3,17 @@ require 'spec_helper' describe AccessRequestsFinder, services: true do let(:user) { create(:user) } let(:access_requester) { create(:user) } - let(:project) { create(:project, :public) } - let(:group) { create(:group, :public) } - before do - project.request_access(access_requester) - group.request_access(access_requester) + let(:project) do + create(:empty_project, :public, :access_requestable) do |project| + project.request_access(access_requester) + end + end + + let(:group) do + create(:group, :public, :access_requestable) do |group| + group.request_access(access_requester) + end end shared_examples 'a finder returning access requesters' do |method_name| diff --git a/spec/helpers/members_helper_spec.rb b/spec/helpers/members_helper_spec.rb index 6703d88e357..ffca1c94da1 100644 --- a/spec/helpers/members_helper_spec.rb +++ b/spec/helpers/members_helper_spec.rb @@ -11,11 +11,11 @@ describe MembersHelper do describe '#remove_member_message' do let(:requester) { build(:user) } - let(:project) { create(:empty_project, :public) } + let(:project) { create(:empty_project, :public, :access_requestable) } let(:project_member) { build(:project_member, project: project) } let(:project_member_invite) { build(:project_member, project: project).tap { |m| m.generate_invite_token! } } let(:project_member_request) { project.request_access(requester) } - let(:group) { create(:group) } + let(:group) { create(:group, :access_requestable) } let(:group_member) { build(:group_member, group: group) } let(:group_member_invite) { build(:group_member, group: group).tap { |m| m.generate_invite_token! } } let(:group_member_request) { group.request_access(requester) } @@ -32,10 +32,10 @@ describe MembersHelper do describe '#remove_member_title' do let(:requester) { build(:user) } - let(:project) { create(:empty_project, :public) } + let(:project) { create(:empty_project, :public, :access_requestable) } let(:project_member) { build(:project_member, project: project) } let(:project_member_request) { project.request_access(requester) } - let(:group) { create(:group) } + let(:group) { create(:group, :access_requestable) } let(:group_member) { build(:group_member, group: group) } let(:group_member_request) { group.request_access(requester) } diff --git a/spec/mailers/notify_spec.rb b/spec/mailers/notify_spec.rb index f5f3f58613d..932a5dc4862 100644 --- a/spec/mailers/notify_spec.rb +++ b/spec/mailers/notify_spec.rb @@ -401,7 +401,12 @@ describe Notify do describe 'project access requested' do context 'for a project in a user namespace' do - let(:project) { create(:project, :public).tap { |p| p.team << [p.owner, :master, p.owner] } } + let(:project) do + create(:empty_project, :public, :access_requestable) do |project| + project.team << [project.owner, :master, project.owner] + end + end + let(:user) { create(:user) } let(:project_member) do project.request_access(user) @@ -428,7 +433,7 @@ describe Notify do context 'for a project in a group' do let(:group_owner) { create(:user) } let(:group) { create(:group).tap { |g| g.add_owner(group_owner) } } - let(:project) { create(:project, :public, namespace: group) } + let(:project) { create(:empty_project, :public, :access_requestable, namespace: group) } let(:user) { create(:user) } let(:project_member) do project.request_access(user) @@ -454,7 +459,7 @@ describe Notify do end describe 'project access denied' do - let(:project) { create(:project) } + let(:project) { create(:empty_project, :public, :access_requestable) } let(:user) { create(:user) } let(:project_member) do project.request_access(user) @@ -474,7 +479,7 @@ describe Notify do end describe 'project access changed' do - let(:project) { create(:project) } + let(:project) { create(:empty_project, :public, :access_requestable) } let(:user) { create(:user) } let(:project_member) { create(:project_member, project: project, user: user) } subject { Notify.member_access_granted_email('project', project_member.id) } @@ -685,7 +690,7 @@ describe Notify do context 'for a group' do describe 'group access requested' do - let(:group) { create(:group) } + let(:group) { create(:group, :public, :access_requestable) } let(:user) { create(:user) } let(:group_member) do group.request_access(user) diff --git a/spec/models/concerns/access_requestable_spec.rb b/spec/models/concerns/access_requestable_spec.rb index 96eee0e8bdd..4829ef17a20 100644 --- a/spec/models/concerns/access_requestable_spec.rb +++ b/spec/models/concerns/access_requestable_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' describe AccessRequestable do describe 'Group' do describe '#request_access' do - let(:group) { create(:group, :public) } + let(:group) { create(:group, :public, :access_requestable) } let(:user) { create(:user) } it { expect(group.request_access(user)).to be_a(GroupMember) } @@ -11,7 +11,7 @@ describe AccessRequestable do end describe '#access_requested?' do - let(:group) { create(:group, :public) } + let(:group) { create(:group, :public, :access_requestable) } let(:user) { create(:user) } before { group.request_access(user) } @@ -22,14 +22,14 @@ describe AccessRequestable do describe 'Project' do describe '#request_access' do - let(:project) { create(:empty_project, :public) } + let(:project) { create(:empty_project, :public, :access_requestable) } let(:user) { create(:user) } it { expect(project.request_access(user)).to be_a(ProjectMember) } end describe '#access_requested?' do - let(:project) { create(:empty_project, :public) } + let(:project) { create(:empty_project, :public, :access_requestable) } let(:user) { create(:user) } before { project.request_access(user) } diff --git a/spec/models/group_spec.rb b/spec/models/group_spec.rb index 47f89f744cb..1613a586a2c 100644 --- a/spec/models/group_spec.rb +++ b/spec/models/group_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' describe Group, models: true do - let!(:group) { create(:group) } + let!(:group) { create(:group, :access_requestable) } describe 'associations' do it { is_expected.to have_many :projects } diff --git a/spec/models/member_spec.rb b/spec/models/member_spec.rb index 485121701af..12419d6fd5a 100644 --- a/spec/models/member_spec.rb +++ b/spec/models/member_spec.rb @@ -57,7 +57,7 @@ describe Member, models: true do describe 'Scopes & finders' do before do - project = create(:empty_project, :public) + project = create(:empty_project, :public, :access_requestable) group = create(:group) @owner_user = create(:user).tap { |u| group.add_owner(u) } @owner = group.members.find_by(user_id: @owner_user.id) @@ -174,7 +174,7 @@ describe Member, models: true do describe '.add_user' do %w[project group].each do |source_type| context "when source is a #{source_type}" do - let!(:source) { create(source_type, :public) } + let!(:source) { create(source_type, :public, :access_requestable) } let!(:user) { create(:user) } let!(:admin) { create(:admin) } diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index 0810d06b50f..3a0c3ec21cd 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -76,7 +76,7 @@ describe Project, models: true do end describe '#members & #requesters' do - let(:project) { create(:project, :public) } + let(:project) { create(:empty_project, :public, :access_requestable) } let(:requester) { create(:user) } let(:developer) { create(:user) } before do diff --git a/spec/models/project_team_spec.rb b/spec/models/project_team_spec.rb index e0f2dadf189..12228425579 100644 --- a/spec/models/project_team_spec.rb +++ b/spec/models/project_team_spec.rb @@ -137,7 +137,7 @@ describe ProjectTeam, models: true do describe '#find_member' do context 'personal project' do - let(:project) { create(:empty_project, :public) } + let(:project) { create(:empty_project, :public, :access_requestable) } let(:requester) { create(:user) } before do @@ -155,7 +155,7 @@ describe ProjectTeam, models: true do end context 'group project' do - let(:group) { create(:group) } + let(:group) { create(:group, :access_requestable) } let(:project) { create(:empty_project, group: group) } let(:requester) { create(:user) } @@ -200,7 +200,7 @@ describe ProjectTeam, models: true do let(:requester) { create(:user) } context 'personal project' do - let(:project) { create(:empty_project, :public) } + let(:project) { create(:empty_project, :public, :access_requestable) } context 'when project is not shared with group' do before do @@ -243,7 +243,7 @@ describe ProjectTeam, models: true do end context 'group project' do - let(:group) { create(:group) } + let(:group) { create(:group, :access_requestable) } let(:project) { create(:empty_project, group: group) } before do diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index 3b152e15b61..54b23b02003 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -37,7 +37,7 @@ describe User, models: true do describe '#group_members' do it 'does not include group memberships for which user is a requester' do user = create(:user) - group = create(:group, :public) + group = create(:group, :public, :access_requestable) group.request_access(user) expect(user.group_members).to be_empty @@ -47,7 +47,7 @@ describe User, models: true do describe '#project_members' do it 'does not include project memberships for which user is a requester' do user = create(:user) - project = create(:project, :public) + project = create(:project, :public, :access_requestable) project.request_access(user) expect(user.project_members).to be_empty diff --git a/spec/requests/api/access_requests_spec.rb b/spec/requests/api/access_requests_spec.rb index b467890a403..1a771b3c87a 100644 --- a/spec/requests/api/access_requests_spec.rb +++ b/spec/requests/api/access_requests_spec.rb @@ -9,19 +9,19 @@ describe API::AccessRequests, api: true do let(:stranger) { create(:user) } let(:project) do - project = create(:project, :public, creator_id: master.id, namespace: master.namespace) - project.team << [developer, :developer] - project.team << [master, :master] - project.request_access(access_requester) - project + create(:project, :public, :access_requestable, creator_id: master.id, namespace: master.namespace) do |project| + project.team << [developer, :developer] + project.team << [master, :master] + project.request_access(access_requester) + end end let(:group) do - group = create(:group, :public) - group.add_developer(developer) - group.add_owner(master) - group.request_access(access_requester) - group + create(:group, :public, :access_requestable) do |group| + group.add_developer(developer) + group.add_owner(master) + group.request_access(access_requester) + end end shared_examples 'GET /:sources/:id/access_requests' do |source_type| @@ -89,7 +89,7 @@ describe API::AccessRequests, api: true do context 'when authenticated as a stranger' do context "when access request is disabled for the #{source_type}" do before do - source.update(request_access_enabled: false) + source.update_attributes(request_access_enabled: false) end it 'returns 403' do diff --git a/spec/requests/api/members_spec.rb b/spec/requests/api/members_spec.rb index 493c0a893d1..2c94c86ccfa 100644 --- a/spec/requests/api/members_spec.rb +++ b/spec/requests/api/members_spec.rb @@ -9,19 +9,19 @@ describe API::Members, api: true do let(:stranger) { create(:user) } let(:project) do - project = create(:project, :public, creator_id: master.id, namespace: master.namespace) - project.team << [developer, :developer] - project.team << [master, :master] - project.request_access(access_requester) - project + create(:project, :public, :access_requestable, creator_id: master.id, namespace: master.namespace) do |project| + project.team << [developer, :developer] + project.team << [master, :master] + project.request_access(access_requester) + end end let!(:group) do - group = create(:group, :public) - group.add_developer(developer) - group.add_owner(master) - group.request_access(access_requester) - group + create(:group, :public, :access_requestable) do |group| + group.add_developer(developer) + group.add_owner(master) + group.request_access(access_requester) + end end shared_examples 'GET /:sources/:id/members' do |source_type| diff --git a/spec/services/members/approve_access_request_service_spec.rb b/spec/services/members/approve_access_request_service_spec.rb index 7b090343a3e..7d5a66801db 100644 --- a/spec/services/members/approve_access_request_service_spec.rb +++ b/spec/services/members/approve_access_request_service_spec.rb @@ -3,8 +3,8 @@ require 'spec_helper' describe Members::ApproveAccessRequestService, services: true do let(:user) { create(:user) } let(:access_requester) { create(:user) } - let(:project) { create(:project, :public) } - let(:group) { create(:group, :public) } + let(:project) { create(:empty_project, :public, :access_requestable) } + let(:group) { create(:group, :public, :access_requestable) } let(:opts) { {} } shared_examples 'a service raising ActiveRecord::RecordNotFound' do diff --git a/spec/services/members/destroy_service_spec.rb b/spec/services/members/destroy_service_spec.rb index 9995f3488af..574df6e0f42 100644 --- a/spec/services/members/destroy_service_spec.rb +++ b/spec/services/members/destroy_service_spec.rb @@ -26,6 +26,7 @@ describe Members::DestroyService, services: true do context 'when the given member is an access requester' do before do source.members.find_by(user_id: member_user).destroy + source.update_attributes(request_access_enabled: true) source.request_access(member_user) end let(:access_requester) { source.requesters.find_by(user_id: member_user) } diff --git a/spec/services/members/request_access_service_spec.rb b/spec/services/members/request_access_service_spec.rb index 0d2d5f03199..853c125dadb 100644 --- a/spec/services/members/request_access_service_spec.rb +++ b/spec/services/members/request_access_service_spec.rb @@ -2,8 +2,6 @@ require 'spec_helper' describe Members::RequestAccessService, services: true do let(:user) { create(:user) } - let(:project) { create(:project, :private) } - let(:group) { create(:group, :private) } shared_examples 'a service raising Gitlab::Access::AccessDeniedError' do it 'raises Gitlab::Access::AccessDeniedError' do @@ -31,27 +29,26 @@ describe Members::RequestAccessService, services: true do end context 'when current user cannot request access to the project' do - it_behaves_like 'a service raising Gitlab::Access::AccessDeniedError' do - let(:source) { project } + %i[project group].each do |source_type| + it_behaves_like 'a service raising Gitlab::Access::AccessDeniedError' do + let(:source) { create(source_type, :private) } + end end + end - it_behaves_like 'a service raising Gitlab::Access::AccessDeniedError' do - let(:source) { group } + context 'when access requests are disabled' do + %i[project group].each do |source_type| + it_behaves_like 'a service raising Gitlab::Access::AccessDeniedError' do + let(:source) { create(source_type, :public) } + end end end context 'when current user can request access to the project' do - before do - project.update(visibility_level: Gitlab::VisibilityLevel::PUBLIC) - group.update(visibility_level: Gitlab::VisibilityLevel::PUBLIC) - end - - it_behaves_like 'a service creating a access request' do - let(:source) { project } - end - - it_behaves_like 'a service creating a access request' do - let(:source) { group } + %i[project group].each do |source_type| + it_behaves_like 'a service creating a access request' do + let(:source) { create(source_type, :public, :access_requestable) } + end end end end -- cgit v1.2.1 From cc2bf503dbeb23561302bb77ff824e0c30a560b6 Mon Sep 17 00:00:00 2001 From: Achilleas Pipinellis Date: Fri, 11 Nov 2016 17:53:26 +0100 Subject: Add missing link to GH issue --- doc/administration/high_availability/redis_source.md | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/doc/administration/high_availability/redis_source.md b/doc/administration/high_availability/redis_source.md index ab5bb13f070..20630b070c0 100644 --- a/doc/administration/high_availability/redis_source.md +++ b/doc/administration/high_availability/redis_source.md @@ -8,7 +8,8 @@ If you are building packages for a specific distro, or trying to build some internal automation, you can check this documentation to learn about the minimal setup, required changes, etc. -If you want to see the documentation for Omnibus GitLab Install, please [read it here](redis.md). +If you want to see the documentation for Omnibus GitLab Install, please +[read it here](redis.md). @@ -38,7 +39,7 @@ You will also need to define equal password for slave password definition To configure Redis to use TCP connection you need to define both `bind` and `port`. You can bind to all interfaces (`0.0.0.0`) or specify the -ip of the desired interface (for ex. one from an internal network). +IP of the desired interface (for ex. one from an internal network). ### Configuring Master Redis instance @@ -46,7 +47,7 @@ ip of the desired interface (for ex. one from an internal network). You need to make the following changes in `redis.conf`: 1. Define a `bind` address pointing to a local IP that your other machines - can reach you. If you really need to bind to an external acessible IP, make + can reach you. If you really need to bind to an external accessible IP, make sure you add extra firewall rules to prevent unauthorized access: 1. Define a `port` to force redis to listen on TCP so other machines can @@ -79,7 +80,7 @@ starting with `sentinel` prefix. You will need to define the initial configs to enable connectivity: 1. Define a `bind` address pointing to a local IP that your other machines - can reach you. If you really need to bind to an external acessible IP, make + can reach you. If you really need to bind to an external accessible IP, make sure you add extra firewall rules to prevent unauthorized access: 1. Define a `port` to force sentinel to listen on TCP so other machines can @@ -95,7 +96,7 @@ And the sentinel specific ones: If you need more information to understand about quorum, please read the detailed explanation in the [HA documentation for Omnibus Installs](redis.md). -1. Define with `sentinel down-after-milliseconds` the ammount in `ms` of time +1. Define with `sentinel down-after-milliseconds` the amount in `ms` of time that an unresponsive server will be considered down. 1. Define a value for `sentinel failover_timeout` in `ms`. This has multiple @@ -164,7 +165,7 @@ by the Sentinel nodes, the Redis nodes will be reconfigured and the **Master** will change permanently (including in `redis.conf`) from one node to the other, until a new failover is initiated again. -The same thing will happen with `sentinel.conf` that will be overriten after the +The same thing will happen with `sentinel.conf` that will be overridden after the initial execution, after any new sentinel node starts watching the **Master**, or a failover promotes a different **Master** node. @@ -284,3 +285,5 @@ production: ``` When in doubt, please read [Redis Sentinel documentation](http://redis.io/topics/sentinel) + +[gh-531]: https://github.com/redis/redis-rb/issues/531 -- cgit v1.2.1 From 0527ea09920d49bb9e1574e7a61958ba70dbe24e Mon Sep 17 00:00:00 2001 From: tiagonbotelho Date: Tue, 8 Nov 2016 17:43:19 +0000 Subject: adds guard clause for readme model method and initializes a new view with proper stylesheets --- app/helpers/preferences_helper.rb | 4 +++- app/models/repository.rb | 2 ++ .../23990-project-show-error-when-empty-repo.yml | 4 ++++ spec/helpers/preferences_helper_spec.rb | 21 +++++++++++++++++++++ 4 files changed, 30 insertions(+), 1 deletion(-) create mode 100644 changelogs/unreleased/23990-project-show-error-when-empty-repo.yml diff --git a/app/helpers/preferences_helper.rb b/app/helpers/preferences_helper.rb index a46f2c6e17d..a721f8957cb 100644 --- a/app/helpers/preferences_helper.rb +++ b/app/helpers/preferences_helper.rb @@ -50,7 +50,9 @@ module PreferencesHelper end def default_project_view - return 'readme' unless current_user + unless current_user + return @repository.head_commit ? 'readme' : 'activity' + end user_view = current_user.project_view diff --git a/app/models/repository.rb b/app/models/repository.rb index 063dc74021d..12748fe5d76 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -554,6 +554,8 @@ class Repository end def readme + return unless head_commit + cache.fetch(:readme) { tree(:head).readme } end diff --git a/changelogs/unreleased/23990-project-show-error-when-empty-repo.yml b/changelogs/unreleased/23990-project-show-error-when-empty-repo.yml new file mode 100644 index 00000000000..040737f917c --- /dev/null +++ b/changelogs/unreleased/23990-project-show-error-when-empty-repo.yml @@ -0,0 +1,4 @@ +--- +title: 500 error on project show when user is not logged in and project is still empty +merge_request: 7376 +author: diff --git a/spec/helpers/preferences_helper_spec.rb b/spec/helpers/preferences_helper_spec.rb index 2f9291afc3f..3830fe26158 100644 --- a/spec/helpers/preferences_helper_spec.rb +++ b/spec/helpers/preferences_helper_spec.rb @@ -85,4 +85,25 @@ describe PreferencesHelper do and_return(double('user', messages)) end end + + describe 'default_project_view' do + let(:project) { create(:project) } + + context 'user not signed in' do + before do + stub_user + @repository = project.repository + end + + it 'returns readme view if repository is not empty' do + expect(helper.default_project_view).to eq('readme') + end + + it 'returns activity if repository is empty' do + expect(@repository).to receive(:head_commit).and_return(nil) + + expect(helper.default_project_view).to eq('activity') + end + end + end end -- cgit v1.2.1 From 062c9a08c44f2e178476cace0ebd8a55aa27e866 Mon Sep 17 00:00:00 2001 From: tiagonbotelho Date: Fri, 11 Nov 2016 22:57:23 +0000 Subject: change empty project view to annonymous user from activity to empty partial --- app/helpers/preferences_helper.rb | 8 +++++--- app/models/repository.rb | 2 -- app/views/projects/_empty.html.haml | 3 +++ spec/helpers/preferences_helper_spec.rb | 9 +++------ 4 files changed, 11 insertions(+), 11 deletions(-) create mode 100644 app/views/projects/_empty.html.haml diff --git a/app/helpers/preferences_helper.rb b/app/helpers/preferences_helper.rb index a721f8957cb..f7189e0c5a1 100644 --- a/app/helpers/preferences_helper.rb +++ b/app/helpers/preferences_helper.rb @@ -50,9 +50,7 @@ module PreferencesHelper end def default_project_view - unless current_user - return @repository.head_commit ? 'readme' : 'activity' - end + return annonymous_project_view unless current_user user_view = current_user.project_view @@ -68,4 +66,8 @@ module PreferencesHelper "customize_workflow" end end + + def annonymous_project_view + @project.empty_repo? ? 'empty' : 'readme' + end end diff --git a/app/models/repository.rb b/app/models/repository.rb index 12748fe5d76..063dc74021d 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -554,8 +554,6 @@ class Repository end def readme - return unless head_commit - cache.fetch(:readme) { tree(:head).readme } end diff --git a/app/views/projects/_empty.html.haml b/app/views/projects/_empty.html.haml new file mode 100644 index 00000000000..0e4f73cbefc --- /dev/null +++ b/app/views/projects/_empty.html.haml @@ -0,0 +1,3 @@ +.row-content-block.second-block.center + %h3.page-title + The repository for this project is empty diff --git a/spec/helpers/preferences_helper_spec.rb b/spec/helpers/preferences_helper_spec.rb index 3830fe26158..2bd4eace89f 100644 --- a/spec/helpers/preferences_helper_spec.rb +++ b/spec/helpers/preferences_helper_spec.rb @@ -90,19 +90,16 @@ describe PreferencesHelper do let(:project) { create(:project) } context 'user not signed in' do - before do - stub_user - @repository = project.repository - end + before { stub_user } it 'returns readme view if repository is not empty' do expect(helper.default_project_view).to eq('readme') end it 'returns activity if repository is empty' do - expect(@repository).to receive(:head_commit).and_return(nil) + expect(project).to receive(:empty_repo?).and_return(true) - expect(helper.default_project_view).to eq('activity') + expect(helper.default_project_view).to eq('empty') end end end -- cgit v1.2.1 From d04961a2353c31eb80fe4431f01065a6f65b214a Mon Sep 17 00:00:00 2001 From: Jarka Kadlecova Date: Sat, 12 Nov 2016 18:22:39 +0100 Subject: add blank line --- spec/models/project_services/jira_service_spec.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/spec/models/project_services/jira_service_spec.rb b/spec/models/project_services/jira_service_spec.rb index ed5abcf7fc0..af58d041260 100644 --- a/spec/models/project_services/jira_service_spec.rb +++ b/spec/models/project_services/jira_service_spec.rb @@ -112,6 +112,7 @@ describe JiraService, models: true do stub_config_setting(url: custom_base_url) @jira_service.execute(merge_request, ExternalIssue.new("JIRA-123", project)) + expect(WebMock).to have_requested(:post, @comment_url).with( body: /#{custom_base_url}\/#{project.path_with_namespace}\/commit\/#{merge_request.diff_head_sha}/ ).once -- cgit v1.2.1 From 7cc9d0f3b12a4eb2d5809558a88b3a6e989c47d9 Mon Sep 17 00:00:00 2001 From: Jarka Kadlecova Date: Sat, 12 Nov 2016 18:29:28 +0100 Subject: stub not needed --- spec/models/project_services/jira_service_spec.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/spec/models/project_services/jira_service_spec.rb b/spec/models/project_services/jira_service_spec.rb index af58d041260..2a87a411e9d 100644 --- a/spec/models/project_services/jira_service_spec.rb +++ b/spec/models/project_services/jira_service_spec.rb @@ -109,7 +109,6 @@ describe JiraService, models: true do it "references the GitLab commit/merge request" do stub_config_setting(base_url: custom_base_url) - stub_config_setting(url: custom_base_url) @jira_service.execute(merge_request, ExternalIssue.new("JIRA-123", project)) -- cgit v1.2.1 From d945300b8e49c927da8a12ba3a8bac68dbc4b9fc Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Mon, 14 Nov 2016 13:54:23 +0100 Subject: Check permissions before stopping CI environments --- app/services/ci/stop_environment_service.rb | 1 + spec/services/ci/stop_environment_service_spec.rb | 72 +++++++++++++++-------- 2 files changed, 50 insertions(+), 23 deletions(-) diff --git a/app/services/ci/stop_environment_service.rb b/app/services/ci/stop_environment_service.rb index cb49f71c5a3..79a0b1369c9 100644 --- a/app/services/ci/stop_environment_service.rb +++ b/app/services/ci/stop_environment_service.rb @@ -10,6 +10,7 @@ module Ci environments.each do |environment| next unless environment.stoppable? + next unless can?(current_user, :create_deployment, project) environment.stop!(current_user) end diff --git a/spec/services/ci/stop_environment_service_spec.rb b/spec/services/ci/stop_environment_service_spec.rb index 047f9b0b2ca..e044b40679a 100644 --- a/spec/services/ci/stop_environment_service_spec.rb +++ b/spec/services/ci/stop_environment_service_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' describe Ci::StopEnvironmentService, services: true do - let(:project) { create(:project) } + let(:project) { create(:project, :private) } let(:user) { create(:user) } let(:service) { described_class.new(project, user) } @@ -12,38 +12,46 @@ describe Ci::StopEnvironmentService, services: true do create(:environment, :with_review_app, project: project) end - it 'stops environment' do - expect_any_instance_of(Environment).to receive(:stop!) + context 'when user has permission to stop environment' do + before do + project.team << [user, :developer] + end - service.execute('master') - end + it 'stops environment' do + expect_environment_stopped_on('master') + end - context 'when specified branch does not exist' do - it 'does not stop environment' do - expect_any_instance_of(Environment).not_to receive(:stop!) + context 'when specified branch does not exist' do + it 'does not stop environment' do + expect_environment_not_stopped_on('non/existent/branch') + end + end - service.execute('non/existent/branch') + context 'when no branch not specified' do + it 'does not stop environment' do + expect_environment_not_stopped_on(nil) + end end - end - context 'when no branch not specified' do - it 'does not stop environment' do - expect_any_instance_of(Environment).not_to receive(:stop!) + context 'when environment is not stoppable' do + before do + allow_any_instance_of(Environment) + .to receive(:stoppable?).and_return(false) + end - service.execute(nil) + it 'does not stop environment' do + expect_environment_not_stopped_on('master') + end end end - context 'when environment is not stoppable' do + context 'when user does not have permission to stop environment' do before do - allow_any_instance_of(Environment) - .to receive(:stoppable?).and_return(false) + project.team << [user, :guest] end it 'does not stop environment' do - expect_any_instance_of(Environment).not_to receive(:stop!) - - service.execute('master') + expect_environment_not_stopped_on('master') end end end @@ -53,10 +61,14 @@ describe Ci::StopEnvironmentService, services: true do create(:environment, project: project) end - it 'does not stop environment' do - expect_any_instance_of(Environment).not_to receive(:stop!) + context 'when user has permission to stop environments' do + before do + project.team << [user, :master] + end - service.execute('master') + it 'does not stop environment' do + expect_environment_not_stopped_on('master') + end end end @@ -67,4 +79,18 @@ describe Ci::StopEnvironmentService, services: true do end end end + + def expect_environment_stopped_on(branch) + expect_any_instance_of(Environment) + .to receive(:stop!) + + service.execute(branch) + end + + def expect_environment_not_stopped_on(branch) + expect_any_instance_of(Environment) + .not_to receive(:stop!) + + service.execute(branch) + end end -- cgit v1.2.1 From 990765edf354a7bbb39d3897780fb85bf8d6fa48 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Mon, 14 Nov 2016 13:57:09 +0100 Subject: Improve name of service that stops CI environments --- app/services/after_branch_delete_service.rb | 2 +- app/services/ci/stop_environment_service.rb | 33 -------- app/services/ci/stop_environments_service.rb | 33 ++++++++ spec/services/ci/stop_environment_service_spec.rb | 96 ---------------------- spec/services/ci/stop_environments_service_spec.rb | 96 ++++++++++++++++++++++ 5 files changed, 130 insertions(+), 130 deletions(-) delete mode 100644 app/services/ci/stop_environment_service.rb create mode 100644 app/services/ci/stop_environments_service.rb delete mode 100644 spec/services/ci/stop_environment_service_spec.rb create mode 100644 spec/services/ci/stop_environments_service_spec.rb diff --git a/app/services/after_branch_delete_service.rb b/app/services/after_branch_delete_service.rb index c157cb68715..2be4d3e6ab5 100644 --- a/app/services/after_branch_delete_service.rb +++ b/app/services/after_branch_delete_service.rb @@ -16,7 +16,7 @@ class AfterBranchDeleteService < BaseService private def stop_environments - Ci::StopEnvironmentService + Ci::StopEnvironmentsService .new(project, current_user) .execute(branch_name) end diff --git a/app/services/ci/stop_environment_service.rb b/app/services/ci/stop_environment_service.rb deleted file mode 100644 index 79a0b1369c9..00000000000 --- a/app/services/ci/stop_environment_service.rb +++ /dev/null @@ -1,33 +0,0 @@ -module Ci - class StopEnvironmentService < BaseService - attr_reader :ref - - def execute(branch_name) - @ref = branch_name - - return unless has_ref? - return unless has_environments? - - environments.each do |environment| - next unless environment.stoppable? - next unless can?(current_user, :create_deployment, project) - - environment.stop!(current_user) - end - end - - private - - def has_ref? - @ref.present? - end - - def has_environments? - environments.any? - end - - def environments - @environments ||= project.environments_for(@ref) - end - end -end diff --git a/app/services/ci/stop_environments_service.rb b/app/services/ci/stop_environments_service.rb new file mode 100644 index 00000000000..b7a49a424ce --- /dev/null +++ b/app/services/ci/stop_environments_service.rb @@ -0,0 +1,33 @@ +module Ci + class StopEnvironmentsService < BaseService + attr_reader :ref + + def execute(branch_name) + @ref = branch_name + + return unless has_ref? + return unless has_environments? + + environments.each do |environment| + next unless environment.stoppable? + next unless can?(current_user, :create_deployment, project) + + environment.stop!(current_user) + end + end + + private + + def has_ref? + @ref.present? + end + + def has_environments? + environments.any? + end + + def environments + @environments ||= project.environments_for(@ref) + end + end +end diff --git a/spec/services/ci/stop_environment_service_spec.rb b/spec/services/ci/stop_environment_service_spec.rb deleted file mode 100644 index e044b40679a..00000000000 --- a/spec/services/ci/stop_environment_service_spec.rb +++ /dev/null @@ -1,96 +0,0 @@ -require 'spec_helper' - -describe Ci::StopEnvironmentService, services: true do - let(:project) { create(:project, :private) } - let(:user) { create(:user) } - - let(:service) { described_class.new(project, user) } - - describe '#execute' do - context 'when environment with review app exists' do - before do - create(:environment, :with_review_app, project: project) - end - - context 'when user has permission to stop environment' do - before do - project.team << [user, :developer] - end - - it 'stops environment' do - expect_environment_stopped_on('master') - end - - context 'when specified branch does not exist' do - it 'does not stop environment' do - expect_environment_not_stopped_on('non/existent/branch') - end - end - - context 'when no branch not specified' do - it 'does not stop environment' do - expect_environment_not_stopped_on(nil) - end - end - - context 'when environment is not stoppable' do - before do - allow_any_instance_of(Environment) - .to receive(:stoppable?).and_return(false) - end - - it 'does not stop environment' do - expect_environment_not_stopped_on('master') - end - end - end - - context 'when user does not have permission to stop environment' do - before do - project.team << [user, :guest] - end - - it 'does not stop environment' do - expect_environment_not_stopped_on('master') - end - end - end - - context 'when there is no environment associated with review app' do - before do - create(:environment, project: project) - end - - context 'when user has permission to stop environments' do - before do - project.team << [user, :master] - end - - it 'does not stop environment' do - expect_environment_not_stopped_on('master') - end - end - end - - context 'when environment does not exist' do - it 'does not raise error' do - expect { service.execute('master') } - .not_to raise_error - end - end - end - - def expect_environment_stopped_on(branch) - expect_any_instance_of(Environment) - .to receive(:stop!) - - service.execute(branch) - end - - def expect_environment_not_stopped_on(branch) - expect_any_instance_of(Environment) - .not_to receive(:stop!) - - service.execute(branch) - end -end diff --git a/spec/services/ci/stop_environments_service_spec.rb b/spec/services/ci/stop_environments_service_spec.rb new file mode 100644 index 00000000000..c3e1af3afe2 --- /dev/null +++ b/spec/services/ci/stop_environments_service_spec.rb @@ -0,0 +1,96 @@ +require 'spec_helper' + +describe Ci::StopEnvironmentsService, services: true do + let(:project) { create(:project, :private) } + let(:user) { create(:user) } + + let(:service) { described_class.new(project, user) } + + describe '#execute' do + context 'when environment with review app exists' do + before do + create(:environment, :with_review_app, project: project) + end + + context 'when user has permission to stop environment' do + before do + project.team << [user, :developer] + end + + it 'stops environment' do + expect_environment_stopped_on('master') + end + + context 'when specified branch does not exist' do + it 'does not stop environment' do + expect_environment_not_stopped_on('non/existent/branch') + end + end + + context 'when no branch not specified' do + it 'does not stop environment' do + expect_environment_not_stopped_on(nil) + end + end + + context 'when environment is not stoppable' do + before do + allow_any_instance_of(Environment) + .to receive(:stoppable?).and_return(false) + end + + it 'does not stop environment' do + expect_environment_not_stopped_on('master') + end + end + end + + context 'when user does not have permission to stop environment' do + before do + project.team << [user, :guest] + end + + it 'does not stop environment' do + expect_environment_not_stopped_on('master') + end + end + end + + context 'when there is no environment associated with review app' do + before do + create(:environment, project: project) + end + + context 'when user has permission to stop environments' do + before do + project.team << [user, :master] + end + + it 'does not stop environment' do + expect_environment_not_stopped_on('master') + end + end + end + + context 'when environment does not exist' do + it 'does not raise error' do + expect { service.execute('master') } + .not_to raise_error + end + end + end + + def expect_environment_stopped_on(branch) + expect_any_instance_of(Environment) + .to receive(:stop!) + + service.execute(branch) + end + + def expect_environment_not_stopped_on(branch) + expect_any_instance_of(Environment) + .not_to receive(:stop!) + + service.execute(branch) + end +end -- cgit v1.2.1 From ceb06983ec186bcc1c6fce28613bdb92297e5bf1 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Mon, 14 Nov 2016 14:10:54 +0100 Subject: Make it possible to fabricate environment on branch --- spec/factories/environments.rb | 9 +++++++-- spec/services/ci/stop_environments_service_spec.rb | 17 +++++++++++++---- 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/spec/factories/environments.rb b/spec/factories/environments.rb index bb6558a403f..0852dda6b29 100644 --- a/spec/factories/environments.rb +++ b/spec/factories/environments.rb @@ -8,16 +8,21 @@ FactoryGirl.define do trait :with_review_app do |environment| project + transient do + ref 'master' + end + # At this point `review app` is an ephemeral concept related to # deployments being deployed for given environment. There is no # first-class `review app` available so we need to create set of # interconnected objects to simulate a review app. # - after(:create) do |environment| + after(:create) do |environment, evaluator| deployment = create(:deployment, environment: environment, project: environment.project, - sha: environment.project.commit.id) + ref: evaluator.ref, + sha: environment.project.commit(evaluator.ref).id) teardown_build = create(:ci_build, :manual, name: "#{deployment.environment.name}:teardown", diff --git a/spec/services/ci/stop_environments_service_spec.rb b/spec/services/ci/stop_environments_service_spec.rb index c3e1af3afe2..6f7d1a5d28d 100644 --- a/spec/services/ci/stop_environments_service_spec.rb +++ b/spec/services/ci/stop_environments_service_spec.rb @@ -9,7 +9,8 @@ describe Ci::StopEnvironmentsService, services: true do describe '#execute' do context 'when environment with review app exists' do before do - create(:environment, :with_review_app, project: project) + create(:environment, :with_review_app, project: project, + ref: 'feature') end context 'when user has permission to stop environment' do @@ -17,8 +18,16 @@ describe Ci::StopEnvironmentsService, services: true do project.team << [user, :developer] end - it 'stops environment' do - expect_environment_stopped_on('master') + context 'when environment is associated with removed branch' do + it 'stops environment' do + expect_environment_stopped_on('feature') + end + end + + context 'when environment is associated with different branch' do + it 'does not stop environment' do + expect_environment_not_stopped_on('master') + end end context 'when specified branch does not exist' do @@ -40,7 +49,7 @@ describe Ci::StopEnvironmentsService, services: true do end it 'does not stop environment' do - expect_environment_not_stopped_on('master') + expect_environment_not_stopped_on('feature') end end end -- cgit v1.2.1 From 84ac742fa6caf6e525e690d6f929f8931902fb9b Mon Sep 17 00:00:00 2001 From: Francesco Coda Zabetta Date: Mon, 14 Nov 2016 15:52:43 +0100 Subject: fix labels API adding missing parameter (current_user) --- lib/api/entities.rb | 13 ++++++++++++- spec/requests/api/labels_spec.rb | 28 ++++++++++++++++++++-------- 2 files changed, 32 insertions(+), 9 deletions(-) diff --git a/lib/api/entities.rb b/lib/api/entities.rb index 1942aeea656..a186cfd94c1 100644 --- a/lib/api/entities.rb +++ b/lib/api/entities.rb @@ -437,7 +437,18 @@ module API end class Label < LabelBasic - expose :open_issues_count, :closed_issues_count, :open_merge_requests_count + expose :open_issues_count do |label, options| + label.open_issues_count(options[:current_user]) + end + + expose :closed_issues_count do |label, options| + label.closed_issues_count(options[:current_user]) + end + + expose :open_merge_requests_count do |label, options| + label.open_merge_requests_count(options[:current_user]) + end + expose :priority do |label, options| label.priority(options[:project]) end diff --git a/spec/requests/api/labels_spec.rb b/spec/requests/api/labels_spec.rb index 5d84976c9c3..39fe6e64653 100644 --- a/spec/requests/api/labels_spec.rb +++ b/spec/requests/api/labels_spec.rb @@ -17,6 +17,8 @@ describe API::API, api: true do group = create(:group) group_label = create(:group_label, title: 'feature', group: group) project.update(group: group) + create(:labeled_issue, project: project, labels: [group_label], author: user) + create(:labeled_issue, project: project, labels: [label1], author: user, state: :closed) expected_keys = [ 'id', 'name', 'color', 'description', 'open_issues_count', 'closed_issues_count', 'open_merge_requests_count', @@ -30,14 +32,24 @@ describe API::API, api: true do expect(json_response.size).to eq(3) expect(json_response.first.keys).to match_array expected_keys expect(json_response.map { |l| l['name'] }).to match_array([group_label.name, priority_label.name, label1.name]) - expect(json_response.last['name']).to eq(label1.name) - expect(json_response.last['color']).to be_present - expect(json_response.last['description']).to be_nil - expect(json_response.last['open_issues_count']).to eq(0) - expect(json_response.last['closed_issues_count']).to eq(0) - expect(json_response.last['open_merge_requests_count']).to eq(0) - expect(json_response.last['priority']).to be_nil - expect(json_response.last['subscribed']).to be_falsey + + label1_response = json_response.select{|l| l['name'] == label1.title}.first + group_label_response = json_response.select{|l| l['name'] == group_label.title}.first + priority_label_response = json_response.select{|l| l['name'] == priority_label.title}.first + + expect(label1_response['open_issues_count']).to eq(0) + expect(label1_response['closed_issues_count']).to eq(1) + expect(group_label_response['open_issues_count']).to eq(1) + expect(group_label_response['closed_issues_count']).to eq(0) + expect(priority_label_response['open_issues_count']).to eq(0) + expect(priority_label_response['closed_issues_count']).to eq(0) + + expect(label1_response['name']).to eq(label1.name) + expect(label1_response['color']).to be_present + expect(label1_response['description']).to be_nil + expect(label1_response['open_merge_requests_count']).to eq(0) + expect(label1_response['priority']).to be_nil + expect(label1_response['subscribed']).to be_falsey end end -- cgit v1.2.1 From 81c03db9dcda549c740172de819b963a6c9272d0 Mon Sep 17 00:00:00 2001 From: Francesco Coda Zabetta Date: Mon, 14 Nov 2016 15:59:40 +0100 Subject: add changelog entry --- changelogs/unreleased/fix_labels_api_adding_missing_parameter.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 changelogs/unreleased/fix_labels_api_adding_missing_parameter.yml diff --git a/changelogs/unreleased/fix_labels_api_adding_missing_parameter.yml b/changelogs/unreleased/fix_labels_api_adding_missing_parameter.yml new file mode 100644 index 00000000000..a237c96f774 --- /dev/null +++ b/changelogs/unreleased/fix_labels_api_adding_missing_parameter.yml @@ -0,0 +1,4 @@ +--- +title: fix labesl API adding missing current_user parameter Francesco Coda Zabetta +merge_request: 7458 +author: Francesco Coda Zabetta -- cgit v1.2.1 From 471ef6cb2760717b917227ae545cc8ec8efa0aa2 Mon Sep 17 00:00:00 2001 From: Francesco Coda Zabetta Date: Mon, 14 Nov 2016 16:04:07 +0100 Subject: fix indentation --- lib/api/entities.rb | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/lib/api/entities.rb b/lib/api/entities.rb index a186cfd94c1..e5d641adccc 100644 --- a/lib/api/entities.rb +++ b/lib/api/entities.rb @@ -438,15 +438,15 @@ module API class Label < LabelBasic expose :open_issues_count do |label, options| - label.open_issues_count(options[:current_user]) - end + label.open_issues_count(options[:current_user]) + end - expose :closed_issues_count do |label, options| - label.closed_issues_count(options[:current_user]) - end + expose :closed_issues_count do |label, options| + label.closed_issues_count(options[:current_user]) + end - expose :open_merge_requests_count do |label, options| - label.open_merge_requests_count(options[:current_user]) + expose :open_merge_requests_count do |label, options| + label.open_merge_requests_count(options[:current_user]) end expose :priority do |label, options| -- cgit v1.2.1 From d5981dd44db4b36ad3d6b5005cd942bc295ace58 Mon Sep 17 00:00:00 2001 From: Francesco Coda Zabetta Date: Mon, 14 Nov 2016 16:05:39 +0100 Subject: fix style --- spec/requests/api/labels_spec.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/spec/requests/api/labels_spec.rb b/spec/requests/api/labels_spec.rb index 39fe6e64653..9f0500bd443 100644 --- a/spec/requests/api/labels_spec.rb +++ b/spec/requests/api/labels_spec.rb @@ -33,9 +33,9 @@ describe API::API, api: true do expect(json_response.first.keys).to match_array expected_keys expect(json_response.map { |l| l['name'] }).to match_array([group_label.name, priority_label.name, label1.name]) - label1_response = json_response.select{|l| l['name'] == label1.title}.first - group_label_response = json_response.select{|l| l['name'] == group_label.title}.first - priority_label_response = json_response.select{|l| l['name'] == priority_label.title}.first + label1_response = json_response.select{ |l| l['name'] == label1.title }.first + group_label_response = json_response.select{ |l| l['name'] == group_label.title }.first + priority_label_response = json_response.select{ |l| l['name'] == priority_label.title }.first expect(label1_response['open_issues_count']).to eq(0) expect(label1_response['closed_issues_count']).to eq(1) -- cgit v1.2.1 From c57c83336d8dc8b4cb82e51cd40b72795bdf01bf Mon Sep 17 00:00:00 2001 From: Francesco Coda Zabetta Date: Mon, 14 Nov 2016 16:15:53 +0100 Subject: fix style --- spec/requests/api/labels_spec.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/spec/requests/api/labels_spec.rb b/spec/requests/api/labels_spec.rb index 9f0500bd443..64a99e42c28 100644 --- a/spec/requests/api/labels_spec.rb +++ b/spec/requests/api/labels_spec.rb @@ -33,9 +33,9 @@ describe API::API, api: true do expect(json_response.first.keys).to match_array expected_keys expect(json_response.map { |l| l['name'] }).to match_array([group_label.name, priority_label.name, label1.name]) - label1_response = json_response.select{ |l| l['name'] == label1.title }.first - group_label_response = json_response.select{ |l| l['name'] == group_label.title }.first - priority_label_response = json_response.select{ |l| l['name'] == priority_label.title }.first + label1_response = json_response.select { |l| l['name'] == label1.title }.first + group_label_response = json_response.select { |l| l['name'] == group_label.title }.first + priority_label_response = json_response.select { |l| l['name'] == priority_label.title }.first expect(label1_response['open_issues_count']).to eq(0) expect(label1_response['closed_issues_count']).to eq(1) -- cgit v1.2.1 From 8fc771ab3ef86d77fe8a91774aa3e49333492ee5 Mon Sep 17 00:00:00 2001 From: Francesco Coda Zabetta Date: Mon, 14 Nov 2016 17:17:38 +0100 Subject: fix changelog typos --- changelogs/unreleased/fix_labels_api_adding_missing_parameter.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/changelogs/unreleased/fix_labels_api_adding_missing_parameter.yml b/changelogs/unreleased/fix_labels_api_adding_missing_parameter.yml index a237c96f774..01b191a8c5a 100644 --- a/changelogs/unreleased/fix_labels_api_adding_missing_parameter.yml +++ b/changelogs/unreleased/fix_labels_api_adding_missing_parameter.yml @@ -1,4 +1,4 @@ --- -title: fix labesl API adding missing current_user parameter Francesco Coda Zabetta +title: Fix labels API by adding missing current_user parameter merge_request: 7458 author: Francesco Coda Zabetta -- cgit v1.2.1 From 067da6224ef2cc53ae4ac38e3f3d1c99d1a97f96 Mon Sep 17 00:00:00 2001 From: Brian Neel Date: Thu, 10 Nov 2016 20:14:54 -0500 Subject: fix shibboleth misconfigurations resulting in authentication bypass --- changelogs/unreleased/fix-shibboleth-auth-with-no-uid.yml | 4 ++++ config/initializers/devise.rb | 4 ++++ 2 files changed, 8 insertions(+) create mode 100644 changelogs/unreleased/fix-shibboleth-auth-with-no-uid.yml diff --git a/changelogs/unreleased/fix-shibboleth-auth-with-no-uid.yml b/changelogs/unreleased/fix-shibboleth-auth-with-no-uid.yml new file mode 100644 index 00000000000..56fa2170be3 --- /dev/null +++ b/changelogs/unreleased/fix-shibboleth-auth-with-no-uid.yml @@ -0,0 +1,4 @@ +--- +title: fix shibboleth misconfigurations resulting in authentication bypass +merge_request: 7428 +author: diff --git a/config/initializers/devise.rb b/config/initializers/devise.rb index a0a8f88584c..a5b415457db 100644 --- a/config/initializers/devise.rb +++ b/config/initializers/devise.rb @@ -254,6 +254,10 @@ Devise.setup do |config| end end + if provider['name'] == 'shibboleth' + provider['args'][:fail_with_empty_uid] = true + end + # A Hash from the configuration will be passed as is. provider_arguments << provider['args'].symbolize_keys end -- cgit v1.2.1 From a88b2d3b019cd696404d20f8042a555ec0081143 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Mon, 14 Nov 2016 14:35:29 +0100 Subject: Add feature tests for environments auto-close --- app/models/environment.rb | 1 + spec/features/environments_spec.rb | 42 ++++++++++++++++++++++++++++++++++---- 2 files changed, 39 insertions(+), 4 deletions(-) diff --git a/app/models/environment.rb b/app/models/environment.rb index 73f415c0ef0..67812b12993 100644 --- a/app/models/environment.rb +++ b/app/models/environment.rb @@ -92,6 +92,7 @@ class Environment < ActiveRecord::Base def stop!(current_user) return unless stoppable? + stop stop_action.play(current_user) end end diff --git a/spec/features/environments_spec.rb b/spec/features/environments_spec.rb index 10ca835e6ca..8b34cb13b9d 100644 --- a/spec/features/environments_spec.rb +++ b/spec/features/environments_spec.rb @@ -6,8 +6,8 @@ feature 'Environments', feature: true do given(:role) { :developer } background do - login_as(user) project.team << [user, role] + login_as(user) end describe 'when showing environments' do @@ -16,7 +16,7 @@ feature 'Environments', feature: true do given!(:manual) { } before do - visit namespace_project_environments_path(project.namespace, project) + visit_environments(project) end context 'shows two tabs' do @@ -142,7 +142,7 @@ feature 'Environments', feature: true do given!(:manual) { } before do - visit namespace_project_environment_path(project.namespace, project, environment) + visit_environment(environment) end context 'without deployments' do @@ -234,7 +234,7 @@ feature 'Environments', feature: true do describe 'when creating a new environment' do before do - visit namespace_project_environments_path(project.namespace, project) + visit_environments(project) end context 'when logged as developer' do @@ -273,4 +273,38 @@ feature 'Environments', feature: true do end end end + + feature 'auto-close environment when branch deleted' do + given(:project) { create(:project) } + + given!(:environment) do + create(:environment, :with_review_app, project: project, + ref: 'feature') + end + + scenario 'user visits environment page' do + visit_environment(environment) + + expect(page).to have_link('Stop') + end + + scenario 'user deletes the branch with running environment' do + visit namespace_project_branches_path(project.namespace, project) + + page.within('.js-branch-feature') { find('a.btn-remove').click } + visit_environment(environment) + + expect(page).to have_no_link('Stop') + end + end + + def visit_environments(project) + visit namespace_project_environments_path(project.namespace, project) + end + + def visit_environment(environment) + visit namespace_project_environment_path(environment.project.namespace, + environment.project, + environment) + end end -- cgit v1.2.1 From a58e2f4762d8f0fc52b0a4dc5a65c28258603c11 Mon Sep 17 00:00:00 2001 From: Francesco Coda Zabetta Date: Tue, 15 Nov 2016 09:50:39 +0100 Subject: use Enumerable#find insted of select + first --- spec/requests/api/labels_spec.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/spec/requests/api/labels_spec.rb b/spec/requests/api/labels_spec.rb index 64a99e42c28..151b5c33e3a 100644 --- a/spec/requests/api/labels_spec.rb +++ b/spec/requests/api/labels_spec.rb @@ -33,9 +33,9 @@ describe API::API, api: true do expect(json_response.first.keys).to match_array expected_keys expect(json_response.map { |l| l['name'] }).to match_array([group_label.name, priority_label.name, label1.name]) - label1_response = json_response.select { |l| l['name'] == label1.title }.first - group_label_response = json_response.select { |l| l['name'] == group_label.title }.first - priority_label_response = json_response.select { |l| l['name'] == priority_label.title }.first + label1_response = json_response.find { |l| l['name'] == label1.title } + group_label_response = json_response.find { |l| l['name'] == group_label.title } + priority_label_response = json_response.find { |l| l['name'] == priority_label.title } expect(label1_response['open_issues_count']).to eq(0) expect(label1_response['closed_issues_count']).to eq(1) -- cgit v1.2.1 From 2a53d6c21aed285ce08a4c4431154e9182beb069 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Tue, 15 Nov 2016 10:27:40 +0100 Subject: Add minimial test coverage for delete branch service --- spec/services/delete_branch_service_spec.rb | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 spec/services/delete_branch_service_spec.rb diff --git a/spec/services/delete_branch_service_spec.rb b/spec/services/delete_branch_service_spec.rb new file mode 100644 index 00000000000..2603a930a08 --- /dev/null +++ b/spec/services/delete_branch_service_spec.rb @@ -0,0 +1,28 @@ +require 'spec_helper' + +describe DeleteBranchService, services: true do + let(:project) { create(:project) } + let(:user) { create(:user) } + let(:service) { described_class.new(project, user) } + + describe '#execute' do + let(:result) { service.execute('feature') } + + context 'when user has access to push to repository' do + before do + project.team << [user, :developer] + end + + it 'removes the branch' do + expect(result[:status]).to eq :success + end + end + + context 'when user does not have access to push to repository' do + it 'does not remove branch' do + expect(result[:status]).to eq :error + expect(result[:message]).to eq 'You dont have push access to repo' + end + end + end +end -- cgit v1.2.1 From e552e1fc22c66c593c4479d85b7a770fda09e5d0 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Tue, 15 Nov 2016 10:32:37 +0100 Subject: Extend tests for delete branch service --- spec/services/delete_branch_service_spec.rb | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/spec/services/delete_branch_service_spec.rb b/spec/services/delete_branch_service_spec.rb index 2603a930a08..336f5dafb5b 100644 --- a/spec/services/delete_branch_service_spec.rb +++ b/spec/services/delete_branch_service_spec.rb @@ -2,27 +2,40 @@ require 'spec_helper' describe DeleteBranchService, services: true do let(:project) { create(:project) } + let(:repository) { project.repository } let(:user) { create(:user) } let(:service) { described_class.new(project, user) } describe '#execute' do - let(:result) { service.execute('feature') } - context 'when user has access to push to repository' do before do project.team << [user, :developer] end it 'removes the branch' do + expect(branch_exists?('feature')).to be true + + result = service.execute('feature') + expect(result[:status]).to eq :success + expect(branch_exists?('feature')).to be false end end context 'when user does not have access to push to repository' do it 'does not remove branch' do + expect(branch_exists?('feature')).to be true + + result = service.execute('feature') + expect(result[:status]).to eq :error expect(result[:message]).to eq 'You dont have push access to repo' + expect(branch_exists?('feature')).to be true end end end + + def branch_exists?(branch_name) + repository.ref_exists?("refs/heads/#{branch_name}") + end end -- cgit v1.2.1 From 94a9effeec183894123f4ea7ad9faa19da3e6b0b Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Tue, 15 Nov 2016 10:34:27 +0100 Subject: Test call to after branch delete hooks in service --- spec/services/delete_branch_service_spec.rb | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/spec/services/delete_branch_service_spec.rb b/spec/services/delete_branch_service_spec.rb index 336f5dafb5b..3ca4eb14518 100644 --- a/spec/services/delete_branch_service_spec.rb +++ b/spec/services/delete_branch_service_spec.rb @@ -20,6 +20,12 @@ describe DeleteBranchService, services: true do expect(result[:status]).to eq :success expect(branch_exists?('feature')).to be false end + + it 'calls after branch delete hooks' do + expect(service).to receive(:execute_after_branch_delete_hooks) + + service.execute('feature') + end end context 'when user does not have access to push to repository' do @@ -32,6 +38,12 @@ describe DeleteBranchService, services: true do expect(result[:message]).to eq 'You dont have push access to repo' expect(branch_exists?('feature')).to be true end + + it 'does not call after branch delete hooks' do + expect(service).not_to receive(:execute_after_branch_delete_hooks) + + service.execute('feature') + end end end -- cgit v1.2.1 From 54c2e234f491fa4b94f40cc997544a029b28d7b8 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Tue, 15 Nov 2016 10:38:08 +0100 Subject: Add test example for after branch delete service --- spec/services/after_branch_delete_service_spec.rb | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 spec/services/after_branch_delete_service_spec.rb diff --git a/spec/services/after_branch_delete_service_spec.rb b/spec/services/after_branch_delete_service_spec.rb new file mode 100644 index 00000000000..d29e0addb53 --- /dev/null +++ b/spec/services/after_branch_delete_service_spec.rb @@ -0,0 +1,15 @@ +require 'spec_helper' + +describe AfterBranchDeleteService, services: true do + let(:project) { create(:project) } + let(:user) { create(:user) } + let(:service) { described_class.new(project, user) } + + describe '#execute' do + it 'stops environments attached to branch' do + expect(service).to receive(:stop_environments) + + service.execute('feature') + end + end +end -- cgit v1.2.1 From 8966c6e33daec5480f19149d2a6102107833afe0 Mon Sep 17 00:00:00 2001 From: Francesco Coda Zabetta Date: Tue, 15 Nov 2016 11:45:57 +0100 Subject: add tests --- spec/requests/api/labels_spec.rb | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/spec/requests/api/labels_spec.rb b/spec/requests/api/labels_spec.rb index 151b5c33e3a..77dfebf1a98 100644 --- a/spec/requests/api/labels_spec.rb +++ b/spec/requests/api/labels_spec.rb @@ -19,6 +19,8 @@ describe API::API, api: true do project.update(group: group) create(:labeled_issue, project: project, labels: [group_label], author: user) create(:labeled_issue, project: project, labels: [label1], author: user, state: :closed) + create(:labeled_merge_request, labels: [priority_label], author: user, source_project: project ) + expected_keys = [ 'id', 'name', 'color', 'description', 'open_issues_count', 'closed_issues_count', 'open_merge_requests_count', @@ -39,17 +41,30 @@ describe API::API, api: true do expect(label1_response['open_issues_count']).to eq(0) expect(label1_response['closed_issues_count']).to eq(1) - expect(group_label_response['open_issues_count']).to eq(1) - expect(group_label_response['closed_issues_count']).to eq(0) - expect(priority_label_response['open_issues_count']).to eq(0) - expect(priority_label_response['closed_issues_count']).to eq(0) - + expect(label1_response['open_merge_requests_count']).to eq(0) expect(label1_response['name']).to eq(label1.name) expect(label1_response['color']).to be_present expect(label1_response['description']).to be_nil - expect(label1_response['open_merge_requests_count']).to eq(0) expect(label1_response['priority']).to be_nil expect(label1_response['subscribed']).to be_falsey + + expect(group_label_response['open_issues_count']).to eq(1) + expect(group_label_response['closed_issues_count']).to eq(0) + expect(group_label_response['open_merge_requests_count']).to eq(0) + expect(group_label_response['name']).to eq(group_label.name) + expect(group_label_response['color']).to be_present + expect(group_label_response['description']).to be_nil + expect(group_label_response['priority']).to be_nil + expect(group_label_response['subscribed']).to be_falsey + + expect(priority_label_response['open_issues_count']).to eq(0) + expect(priority_label_response['closed_issues_count']).to eq(0) + expect(priority_label_response['open_merge_requests_count']).to eq(1) + expect(priority_label_response['name']).to eq(priority_label.name) + expect(priority_label_response['color']).to be_present + expect(priority_label_response['description']).to be_nil + expect(priority_label_response['priority']).to eq(3) + expect(priority_label_response['subscribed']).to be_falsey end end -- cgit v1.2.1 From 4dab79c5c6aa1eed1295663fe91c62ea0062f8d2 Mon Sep 17 00:00:00 2001 From: Tomasz Maczukin Date: Thu, 27 Oct 2016 17:05:02 +0200 Subject: Update the updated_at of a build while patching the trace --- app/models/ci/build.rb | 5 +++ spec/requests/ci/api/builds_spec.rb | 86 ++++++++++++++++++++++++++++++++++--- 2 files changed, 86 insertions(+), 5 deletions(-) diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb index bf5f92f8462..33612256540 100644 --- a/app/models/ci/build.rb +++ b/app/models/ci/build.rb @@ -271,6 +271,7 @@ module Ci def append_trace(trace_part, offset) recreate_trace_dir + touch if needs_touch? trace_part = hide_secrets(trace_part) @@ -280,6 +281,10 @@ module Ci end end + def needs_touch? + Time.now - updated_at > 15.minutes.to_i + end + def trace_file_path if has_old_trace_file? old_path_to_trace diff --git a/spec/requests/ci/api/builds_spec.rb b/spec/requests/ci/api/builds_spec.rb index 6d49c42c215..a611c5e3823 100644 --- a/spec/requests/ci/api/builds_spec.rb +++ b/spec/requests/ci/api/builds_spec.rb @@ -213,26 +213,102 @@ describe Ci::API::API do let(:build) { create(:ci_build, :pending, :trace, runner_id: runner.id) } let(:headers) { { Ci::API::Helpers::BUILD_TOKEN_HEADER => build.token, 'Content-Type' => 'text/plain' } } let(:headers_with_range) { headers.merge({ 'Content-Range' => '11-20' }) } + let(:update_interval) { 10.seconds.to_i } + + def patch_the_trace(content = ' appended', request_headers = nil) + unless request_headers + offset = build.trace_length + limit = offset + content.length - 1 + request_headers = headers.merge({ 'Content-Range' => "#{offset}-#{limit}" }) + end + + Timecop.travel(build.updated_at + update_interval) do + patch ci_api("/builds/#{build.id}/trace.txt"), content, request_headers + build.reload + end + end + + def initial_patch_the_trace + patch_the_trace(' appended', headers_with_range) + end + + def force_patch_the_trace + 2.times { patch_the_trace('') } + end before do build.run! - patch ci_api("/builds/#{build.id}/trace.txt"), ' appended', headers_with_range + initial_patch_the_trace end context 'when request is valid' do it 'gets correct response' do expect(response.status).to eq 202 + expect(build.reload.trace).to eq 'BUILD TRACE appended' expect(response.header).to have_key 'Range' expect(response.header).to have_key 'Build-Status' end - it { expect(build.reload.trace).to eq 'BUILD TRACE appended' } + context 'when build has been updated recently' do + it { expect{ patch_the_trace }.not_to change { build.updated_at }} + + it 'changes the build trace' do + patch_the_trace + + expect(build.reload.trace).to eq 'BUILD TRACE appended appended' + end + + context 'when Runner makes a force-patch' do + it { expect{ force_patch_the_trace }.not_to change { build.updated_at }} + + it "doesn't change the build.trace" do + force_patch_the_trace + + expect(build.reload.trace).to eq 'BUILD TRACE appended' + end + end + end + + context 'when build was not updated recently' do + let(:update_interval) { 15.minutes.to_i } + + it { expect { patch_the_trace }.to change { build.updated_at } } + + it 'changes the build.trace' do + patch_the_trace + + expect(build.reload.trace).to eq 'BUILD TRACE appended appended' + end + + context 'when Runner makes a force-patch' do + it { expect { force_patch_the_trace }.to change { build.updated_at } } + + it "doesn't change the build.trace" do + force_patch_the_trace + + expect(build.reload.trace).to eq 'BUILD TRACE appended' + end + end + end + end + + context 'when Runner makes a force-patch' do + before do + force_patch_the_trace + end + + it 'gets correct response' do + expect(response.status).to eq 202 + expect(build.reload.trace).to eq 'BUILD TRACE appended' + expect(response.header).to have_key 'Range' + expect(response.header).to have_key 'Build-Status' + end end context 'when content-range start is too big' do let(:headers_with_range) { headers.merge({ 'Content-Range' => '15-20' }) } - it 'gets correct response' do + it 'gets 416 error response with range headers' do expect(response.status).to eq 416 expect(response.header).to have_key 'Range' expect(response.header['Range']).to eq '0-11' @@ -242,7 +318,7 @@ describe Ci::API::API do context 'when content-range start is too small' do let(:headers_with_range) { headers.merge({ 'Content-Range' => '8-20' }) } - it 'gets correct response' do + it 'gets 416 error response with range headers' do expect(response.status).to eq 416 expect(response.header).to have_key 'Range' expect(response.header['Range']).to eq '0-11' @@ -250,7 +326,7 @@ describe Ci::API::API do end context 'when Content-Range header is missing' do - let(:headers_with_range) { headers.merge({}) } + let(:headers_with_range) { headers } it { expect(response.status).to eq 400 } end -- cgit v1.2.1 From 39a9af1594bf2365ba0da6457efef238d7e42e24 Mon Sep 17 00:00:00 2001 From: Achilleas Pipinellis Date: Tue, 15 Nov 2016 14:07:38 +0100 Subject: Swap HA setups --- doc/administration/high_availability/README.md | 33 +++++++++------- doc/administration/high_availability/redis.md | 54 +++++++++++++------------- 2 files changed, 45 insertions(+), 42 deletions(-) diff --git a/doc/administration/high_availability/README.md b/doc/administration/high_availability/README.md index d74a786ac24..d5a5aef7ec0 100644 --- a/doc/administration/high_availability/README.md +++ b/doc/administration/high_availability/README.md @@ -7,19 +7,10 @@ highly available. ## Architecture -### Active/Passive - -For pure high-availability/failover with no scaling you can use an -active/passive configuration. This utilizes DRBD (Distributed Replicated -Block Device) to keep all data in sync. DRBD requires a low latency link to -remain in sync. It is not advisable to attempt to run DRBD between data centers -or in different cloud availability zones. +There are two kinds of setups: -Components/Servers Required: - -- 2 servers/virtual machines (one active/one passive) - -![Active/Passive HA Diagram](../img/high_availability/active-passive-diagram.png) +- active/active +- active/passive ### Active/Active @@ -28,12 +19,24 @@ user requests simultaneously. The database, Redis, and GitLab application are all deployed on separate servers. The configuration is **only** highly-available if the database, Redis and storage are also configured as such. -![Active/Active HA Diagram](../img/high_availability/active-active-diagram.png) - -**Steps to configure active/active:** +Follow the steps below to configure an active/active setup: 1. [Configure the database](database.md) 1. [Configure Redis](redis.md) 1. [Configure NFS](nfs.md) 1. [Configure the GitLab application servers](gitlab.md) 1. [Configure the load balancers](load_balancer.md) + +![Active/Active HA Diagram](../img/high_availability/active-active-diagram.png) + +### Active/Passive + +For pure high-availability/failover with no scaling you can use an +active/passive configuration. This utilizes DRBD (Distributed Replicated +Block Device) to keep all data in sync. DRBD requires a low latency link to +remain in sync. It is not advisable to attempt to run DRBD between data centers +or in different cloud availability zones. + +Components/Servers Required: 2 servers/virtual machines (one active/one passive) + +![Active/Passive HA Diagram](../img/high_availability/active-passive-diagram.png) diff --git a/doc/administration/high_availability/redis.md b/doc/administration/high_availability/redis.md index 3eaa0ffdcec..bb46de65e3c 100644 --- a/doc/administration/high_availability/redis.md +++ b/doc/administration/high_availability/redis.md @@ -16,10 +16,10 @@ Omnibus GitLab packages. - You are highly encouraged to read the [Redis Sentinel][sentinel] documentation before configuring Redis HA with GitLab to fully understand the topology and architecture. -- This is the documentation for the Omnibus packages. For installations from - source, follow the [Redis HA source install](redis_source.md) guide. -- Redis Sentinel is bundled with Omnibus GitLab Enterprise Edition only. For the - Omnibus Community Edition and installations from source, follow the +- This is the documentation for the Omnibus GitLab packages. For installations + from source, follow the [Redis HA source install](redis_source.md) guide. +- Redis Sentinel daemon is bundled with Omnibus GitLab Enterprise Edition only. + For the Omnibus Community Edition and installations from source, follow the [Redis HA source install](redis_source.md) guide. @@ -50,7 +50,6 @@ Omnibus GitLab packages. - [Troubleshooting Redis replication](#troubleshooting-redis-replication) - [Troubleshooting Sentinel](#troubleshooting-sentinel) - [Changelog](#changelog) - - [Experimental Redis Sentinel support](#experimental-redis-sentinel-support) @@ -76,15 +75,16 @@ making sure you have redundant connectivity between Redis / Sentinel and GitLab instances, otherwise the networks will become a single point of failure. -Read carefully how to configure the components below. +Make sure that you read this document once as a whole before configuring the +components below. ### High Availability with Sentinel > -- Since GitLab `8.11`, you can configure a list of Redis Sentinel servers that - will monitor a group of Redis servers to provide failover support. -- With GitLab `8.14`, we bundled Redis Sentinel as part of Omnibus package and - improved the way you use and configure it. +- Starting with GitLab `8.11`, you can configure a list of Redis Sentinel + servers that will monitor a group of Redis servers to provide failover support. +- Starting with GitLab `8.14`, the Omnibus GitLab Enterprise Edition package + comes with Redis sentinel daemon support. High Availability with Redis requires a few things: @@ -136,20 +136,21 @@ the Omnibus GitLab package in `5` **independent** machines, both with Based on your infrastructure setup and how you have installed GitLab, there are multiple ways to configure Redis HA. Omnibus GitLab packages have Redis and/or -Redis Sentinel bundled with them to save you the hassle to install them yourself. +Redis Sentinel bundled with them so you only need to focus on configuration. Pick the one that suits your needs. - [Installations from source][source]: You need to install Redis and Sentinel - yourself. Use the [Redis HA source install](redis_source.md) guide. -- [Omnibus package Community Edition (CE)][ce]: Redis is bundled, so you can use the - package with only the Redis service enabled (works for both master and slave - setups). -- [Omnibus package Enterprise Edition (EE)][ee]: Both Redis and Sentinel are bundled, - so you can use the EE package to setup the whole Redis HA infrastructure - (master, slave and Sentinel). - -Note that if you have installed GitLab using the Omnibus packages (both CE and EE), -you can also use an [external Redis server](#using-a-non-omnibus-external-redis-server). + yourself. Use the [Redis HA installation from source](redis_source.md) guide. +- [Omnibus package Community Edition (CE) package][ce]: Redis is bundled, so you + can use the package with only the Redis service enabled (works for both master + and slave setups). To install and configure Sentinel, you can use the + [Redis HA installation from source](redis_source.md) guide. +- [Omnibus package Enterprise Edition (EE) package][ee]: Both Redis and Sentinel + are bundled, so you can use the EE package to setup the whole Redis HA + infrastructure (master, slave and Sentinel). + +Note that if you have installed GitLab using the Omnibus GitLab packages (both +CE and EE), you can also use an [external Redis server](#using-a-non-omnibus-external-redis-server). ### Using a non-Omnibus external Redis server @@ -198,7 +199,7 @@ each other over the network. Sentinels watch both other sentinels and Redis nodes. Whenever a Sentinel detects that a Redis node is not responding, it will announce that to the -other sentinels. You have to reach the **quorum**, the minimum amount of +other sentinels. They have to reach the **quorum**, the minimum amount of sentinels that agrees that a node is down, to be able to start a failover. Whenever the **quorum** is met, you need the **majority** of all known @@ -267,9 +268,8 @@ This is the section where we install and setup the new Redis instances. already have it installed and running, read how to [switch from a single-machine installation to Redis HA](#switching-from-an-existing-single-machine-installation-to-redis-ha). - Redis nodes (both master and slaves) will need the same password defined in - `redis['password']` and `redis['master_password']`. At any time during a - failover the Sentinels can reconfigure a node and change its status - from master to slave and vice versa. + `redis['password']`. At any time during a failover the Sentinels can + reconfigure a node and change its status from master to slave and vice versa. A summary of what are we going to do: @@ -354,7 +354,7 @@ a failover, as the nodes will be managed by the Sentinels, and even after a `gitlab-ctl reconfigure`, they will get their configuration restored by the same Sentinels. -### Configuring the Sentinel instances +### Configuring the Redis Sentinel instances >**Note:** - Redis Sentinel is bundled with Omnibus GitLab Enterprise Edition only. For the @@ -425,7 +425,7 @@ redis['enable'] = false If you fail to replicate first, you may loose data (unprocessed background jobs). -## Minimal example configuration with 1 master, 2 slaves and 3 sentinels +## Example of a minimal configuration with 1 master, 2 slaves and 3 sentinels In this example we consider that all servers have an internal network interface with IPs in the `10.0.0.x` range, and that they can connect -- cgit v1.2.1 From c7350f0c62dee6ffc43cf24004ed95e399d72832 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Tue, 15 Nov 2016 14:48:14 +0100 Subject: Select recently updated environments for auto-close --- app/models/environment.rb | 4 ++++ app/models/project.rb | 4 ++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/app/models/environment.rb b/app/models/environment.rb index 67812b12993..fe3df473d73 100644 --- a/app/models/environment.rb +++ b/app/models/environment.rb @@ -37,6 +37,10 @@ class Environment < ActiveRecord::Base state :stopped end + def recently_updated_on?(ref) + ref.to_s == last_deployment.ref + end + def last_deployment deployments.last end diff --git a/app/models/project.rb b/app/models/project.rb index 2f4fb0d082d..1aad749c77e 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -1299,9 +1299,9 @@ class Project < ActiveRecord::Base envs = environments.available.where(id: environment_ids) if commit - envs.select { |env| env.includes_commit?(commit) } + envs.select { |environment| env.includes_commit?(commit) } else - envs.to_a + envs.select { |environment| env.recently_updated_on?(ref) } end end -- cgit v1.2.1 From c9d93f645aed1fbb9196616afb0110a585882fc1 Mon Sep 17 00:00:00 2001 From: tiagonbotelho Date: Tue, 15 Nov 2016 14:21:05 +0000 Subject: moves empty view logic onto empty partial to make it reusable and fixes tests --- app/views/projects/_empty.html.haml | 55 ++++++++++++++++++++++++++++++ app/views/projects/empty.html.haml | 60 +-------------------------------- spec/helpers/preferences_helper_spec.rb | 9 ++--- 3 files changed, 61 insertions(+), 63 deletions(-) diff --git a/app/views/projects/_empty.html.haml b/app/views/projects/_empty.html.haml index 0e4f73cbefc..56276e164de 100644 --- a/app/views/projects/_empty.html.haml +++ b/app/views/projects/_empty.html.haml @@ -1,3 +1,58 @@ .row-content-block.second-block.center %h3.page-title The repository for this project is empty + - if can?(current_user, :push_code, @project) + %p + If you already have files you can push them using command line instructions below. + %p + Otherwise you can start with adding a + = succeed ',' do + = link_to "README", new_readme_path, class: 'underlined-link' + a + = succeed ',' do + = link_to "LICENSE", add_special_file_path(@project, file_name: 'LICENSE'), class: 'underlined-link' + or a + = link_to '.gitignore', add_special_file_path(@project, file_name: '.gitignore'), class: 'underlined-link' + to this project. + %p + You will need to be owner or have the master permission level for the initial push, as the master branch is automatically protected. + +- if can?(current_user, :push_code, @project) + %div{ class: container_class } + .prepend-top-20 + .empty_wrapper + %h3.page-title-empty + Command line instructions + %div.git-empty + %fieldset + %h5 Git global setup + %pre.light-well + :preserve + git config --global user.name "#{h git_user_name}" + git config --global user.email "#{h git_user_email}" + + %fieldset + %h5 Create a new repository + %pre.light-well + :preserve + git clone #{ content_tag(:span, default_url_to_repo, class: 'clone')} + cd #{h @project.path} + touch README.md + git add README.md + git commit -m "add README" + git push -u origin master + + %fieldset + %h5 Existing folder or Git repository + %pre.light-well + :preserve + cd existing_folder + git init + git remote add origin #{ content_tag(:span, default_url_to_repo, class: 'clone')} + git add . + git commit + git push -u origin master + + - if can? current_user, :remove_project, @project + .prepend-top-20 + = link_to 'Remove project', [@project.namespace.becomes(Namespace), @project], data: { confirm: remove_project_message(@project)}, method: :delete, class: "btn btn-remove pull-right" diff --git a/app/views/projects/empty.html.haml b/app/views/projects/empty.html.haml index 7a39064adc5..94895699453 100644 --- a/app/views/projects/empty.html.haml +++ b/app/views/projects/empty.html.haml @@ -6,62 +6,4 @@ = render 'shared/no_password' = render "home_panel" - -.row-content-block.second-block.center - %h3.page-title - The repository for this project is empty - - if can?(current_user, :push_code, @project) - %p - If you already have files you can push them using command line instructions below. - %p - Otherwise you can start with adding a - = succeed ',' do - = link_to "README", new_readme_path, class: 'underlined-link' - a - = succeed ',' do - = link_to "LICENSE", add_special_file_path(@project, file_name: 'LICENSE'), class: 'underlined-link' - or a - = link_to '.gitignore', add_special_file_path(@project, file_name: '.gitignore'), class: 'underlined-link' - to this project. - %p - You will need to be owner or have the master permission level for the initial push, as the master branch is automatically protected. - -- if can?(current_user, :push_code, @project) - %div{ class: container_class } - .prepend-top-20 - .empty_wrapper - %h3.page-title-empty - Command line instructions - %div.git-empty - %fieldset - %h5 Git global setup - %pre.light-well - :preserve - git config --global user.name "#{h git_user_name}" - git config --global user.email "#{h git_user_email}" - - %fieldset - %h5 Create a new repository - %pre.light-well - :preserve - git clone #{ content_tag(:span, default_url_to_repo, class: 'clone')} - cd #{h @project.path} - touch README.md - git add README.md - git commit -m "add README" - git push -u origin master - - %fieldset - %h5 Existing folder or Git repository - %pre.light-well - :preserve - cd existing_folder - git init - git remote add origin #{ content_tag(:span, default_url_to_repo, class: 'clone')} - git add . - git commit - git push -u origin master - - - if can? current_user, :remove_project, @project - .prepend-top-20 - = link_to 'Remove project', [@project.namespace.becomes(Namespace), @project], data: { confirm: remove_project_message(@project)}, method: :delete, class: "btn btn-remove pull-right" += render "empty" diff --git a/spec/helpers/preferences_helper_spec.rb b/spec/helpers/preferences_helper_spec.rb index 2bd4eace89f..02b464f7e07 100644 --- a/spec/helpers/preferences_helper_spec.rb +++ b/spec/helpers/preferences_helper_spec.rb @@ -87,17 +87,18 @@ describe PreferencesHelper do end describe 'default_project_view' do - let(:project) { create(:project) } - context 'user not signed in' do - before { stub_user } + before do + @project = create(:project) + stub_user + end it 'returns readme view if repository is not empty' do expect(helper.default_project_view).to eq('readme') end it 'returns activity if repository is empty' do - expect(project).to receive(:empty_repo?).and_return(true) + expect(@project).to receive(:empty_repo?).and_return(true) expect(helper.default_project_view).to eq('empty') end -- cgit v1.2.1 From 59189438388191c044c0697d5bfb571693d95978 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Tue, 15 Nov 2016 17:04:33 +0200 Subject: Remove gitattribute entry for CHANGELOG.md [ci skip] --- .gitattributes | 1 - 1 file changed, 1 deletion(-) diff --git a/.gitattributes b/.gitattributes index ab791a4cd6c..70cce05d2b5 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,2 +1 @@ -CHANGELOG.md merge=union *.js.es6 gitlab-language=javascript -- cgit v1.2.1 From 7cefaea876da9e25a1c3ea870f4e4b070a196905 Mon Sep 17 00:00:00 2001 From: "Luke \"Jared\" Bennett" Date: Tue, 15 Nov 2016 14:08:06 +0000 Subject: explicitly disable eslint inline --- app/assets/javascripts/activities.js | 2 +- app/assets/javascripts/admin.js | 2 +- app/assets/javascripts/api.js | 2 +- app/assets/javascripts/application.js | 2 +- app/assets/javascripts/aside.js | 2 +- app/assets/javascripts/autosave.js | 2 +- app/assets/javascripts/awards_handler.js | 2 +- app/assets/javascripts/behaviors/autosize.js | 2 +- app/assets/javascripts/behaviors/details_behavior.js | 2 +- app/assets/javascripts/behaviors/quick_submit.js | 2 +- app/assets/javascripts/behaviors/requires_input.js | 2 +- app/assets/javascripts/behaviors/toggler_behavior.js | 2 +- app/assets/javascripts/blob/blob_file_dropzone.js | 2 +- app/assets/javascripts/blob/blob_gitignore_selector.js | 2 +- app/assets/javascripts/blob/blob_gitignore_selectors.js | 2 +- app/assets/javascripts/blob/blob_license_selector.js | 2 +- app/assets/javascripts/blob_edit/blob_edit_bundle.js | 2 +- app/assets/javascripts/blob_edit/edit_blob.js | 2 +- app/assets/javascripts/boards/test_utils/simulate_drag.js | 2 +- app/assets/javascripts/breakpoints.js | 2 +- app/assets/javascripts/broadcast_message.js | 2 +- app/assets/javascripts/build.js | 2 +- app/assets/javascripts/build_artifacts.js | 2 +- app/assets/javascripts/commit.js | 2 +- app/assets/javascripts/commit/file.js | 2 +- app/assets/javascripts/commit/image_file.js | 2 +- app/assets/javascripts/commits.js | 2 +- app/assets/javascripts/compare.js | 2 +- app/assets/javascripts/confirm_danger_modal.js | 2 +- app/assets/javascripts/copy_to_clipboard.js | 2 +- app/assets/javascripts/diff.js | 2 +- app/assets/javascripts/dropzone_input.js | 2 +- app/assets/javascripts/extensions/array.js | 2 +- app/assets/javascripts/extensions/jquery.js | 2 +- app/assets/javascripts/files_comment_button.js | 2 +- app/assets/javascripts/flash.js | 2 +- app/assets/javascripts/gl_dropdown.js | 2 +- app/assets/javascripts/gl_form.js | 2 +- app/assets/javascripts/graphs/graphs_bundle.js | 2 +- app/assets/javascripts/graphs/stat_graph.js | 2 +- app/assets/javascripts/graphs/stat_graph_contributors.js | 2 +- app/assets/javascripts/graphs/stat_graph_contributors_graph.js | 2 +- app/assets/javascripts/graphs/stat_graph_contributors_util.js | 2 +- app/assets/javascripts/group_avatar.js | 2 +- app/assets/javascripts/groups_select.js | 2 +- app/assets/javascripts/header.js | 2 +- app/assets/javascripts/importer_status.js | 2 +- app/assets/javascripts/issuable_context.js | 2 +- app/assets/javascripts/issuable_form.js | 2 +- app/assets/javascripts/issue.js | 2 +- app/assets/javascripts/issue_status_select.js | 2 +- app/assets/javascripts/labels.js | 2 +- app/assets/javascripts/labels_select.js | 2 +- app/assets/javascripts/layout_nav.js | 2 +- app/assets/javascripts/lib/chart.js | 2 +- app/assets/javascripts/lib/cropper.js | 2 +- app/assets/javascripts/lib/d3.js | 2 +- app/assets/javascripts/lib/raphael.js | 2 +- app/assets/javascripts/lib/utils/animate.js | 2 +- app/assets/javascripts/lib/utils/common_utils.js | 2 +- app/assets/javascripts/lib/utils/datetime_utility.js | 2 +- app/assets/javascripts/lib/utils/notify.js | 2 +- app/assets/javascripts/lib/utils/text_utility.js | 2 +- app/assets/javascripts/lib/utils/timeago.js | 2 ++ app/assets/javascripts/lib/utils/type_utility.js | 2 +- app/assets/javascripts/lib/utils/url_utility.js | 2 +- app/assets/javascripts/line_highlighter.js | 2 +- app/assets/javascripts/logo.js | 2 +- app/assets/javascripts/member_expiration_date.js | 2 +- app/assets/javascripts/merge_request.js | 2 +- app/assets/javascripts/merge_request_tabs.js | 2 +- app/assets/javascripts/merged_buttons.js | 2 +- app/assets/javascripts/milestone.js | 2 +- app/assets/javascripts/milestone_select.js | 2 +- app/assets/javascripts/namespace_select.js | 2 +- app/assets/javascripts/network/branch_graph.js | 2 +- app/assets/javascripts/network/network.js | 2 +- app/assets/javascripts/network/network_bundle.js | 2 +- app/assets/javascripts/new_branch_form.js | 2 +- app/assets/javascripts/new_commit_form.js | 2 +- app/assets/javascripts/notes.js | 2 +- app/assets/javascripts/notifications_dropdown.js | 2 +- app/assets/javascripts/notifications_form.js | 2 +- app/assets/javascripts/pager.js | 2 +- app/assets/javascripts/preview_markdown.js | 2 +- app/assets/javascripts/profile/profile_bundle.js | 2 +- app/assets/javascripts/project.js | 2 +- app/assets/javascripts/project_avatar.js | 2 +- app/assets/javascripts/project_find_file.js | 2 +- app/assets/javascripts/project_fork.js | 2 +- app/assets/javascripts/project_import.js | 2 +- app/assets/javascripts/project_new.js | 2 +- app/assets/javascripts/project_select.js | 2 +- app/assets/javascripts/project_show.js | 2 +- app/assets/javascripts/projects_list.js | 2 +- app/assets/javascripts/right_sidebar.js | 2 +- app/assets/javascripts/search.js | 2 +- app/assets/javascripts/shortcuts.js | 2 +- app/assets/javascripts/shortcuts_blob.js | 2 +- app/assets/javascripts/shortcuts_dashboard_navigation.js | 2 +- app/assets/javascripts/shortcuts_find_file.js | 2 +- app/assets/javascripts/shortcuts_issuable.js | 2 +- app/assets/javascripts/shortcuts_navigation.js | 2 +- app/assets/javascripts/shortcuts_network.js | 2 +- app/assets/javascripts/single_file_diff.js | 2 +- app/assets/javascripts/snippet/snippet_bundle.js | 2 +- app/assets/javascripts/star.js | 2 +- app/assets/javascripts/subscription.js | 2 +- app/assets/javascripts/subscription_select.js | 2 +- app/assets/javascripts/syntax_highlight.js | 2 +- app/assets/javascripts/tree.js | 2 +- app/assets/javascripts/u2f/authenticate.js | 2 +- app/assets/javascripts/u2f/error.js | 2 +- app/assets/javascripts/u2f/register.js | 2 +- app/assets/javascripts/u2f/util.js | 2 +- app/assets/javascripts/users/calendar.js | 2 +- app/assets/javascripts/users/users_bundle.js | 2 +- app/assets/javascripts/users_select.js | 2 +- app/assets/javascripts/wikis.js | 2 +- app/assets/javascripts/zen_mode.js | 2 +- spec/javascripts/application_spec.js | 2 +- spec/javascripts/awards_handler_spec.js | 2 +- spec/javascripts/behaviors/autosize_spec.js | 2 +- spec/javascripts/behaviors/quick_submit_spec.js | 2 +- spec/javascripts/behaviors/requires_input_spec.js | 2 +- spec/javascripts/extensions/array_spec.js | 2 +- spec/javascripts/extensions/jquery_spec.js | 2 +- spec/javascripts/fixtures/emoji_menu.js | 2 +- spec/javascripts/graphs/stat_graph_contributors_graph_spec.js | 2 +- spec/javascripts/graphs/stat_graph_contributors_util_spec.js | 2 +- spec/javascripts/graphs/stat_graph_spec.js | 2 +- spec/javascripts/header_spec.js | 2 +- spec/javascripts/issue_spec.js | 2 +- spec/javascripts/line_highlighter_spec.js | 2 +- spec/javascripts/merge_request_spec.js | 2 +- spec/javascripts/merge_request_tabs_spec.js | 2 +- spec/javascripts/merge_request_widget_spec.js | 2 +- spec/javascripts/new_branch_spec.js | 2 +- spec/javascripts/notes_spec.js | 2 +- spec/javascripts/project_title_spec.js | 2 +- spec/javascripts/right_sidebar_spec.js | 2 +- spec/javascripts/search_autocomplete_spec.js | 2 +- spec/javascripts/shortcuts_issuable_spec.js | 2 +- spec/javascripts/spec_helper.js | 2 +- spec/javascripts/syntax_highlight_spec.js | 2 +- spec/javascripts/u2f/authenticate_spec.js | 2 +- spec/javascripts/u2f/mock_u2f_device.js | 2 +- spec/javascripts/u2f/register_spec.js | 2 +- spec/javascripts/zen_mode_spec.js | 2 +- 149 files changed, 150 insertions(+), 148 deletions(-) diff --git a/app/assets/javascripts/activities.js b/app/assets/javascripts/activities.js index 919107b8cb9..906a1a69d93 100644 --- a/app/assets/javascripts/activities.js +++ b/app/assets/javascripts/activities.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-undef, quotes, no-var, padded-blocks, max-len */ (function() { this.Activities = (function() { function Activities() { diff --git a/app/assets/javascripts/admin.js b/app/assets/javascripts/admin.js index 1ef340e4ca1..31852e4750c 100644 --- a/app/assets/javascripts/admin.js +++ b/app/assets/javascripts/admin.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, wrap-iife, one-var, no-var, one-var-declaration-per-line, no-unused-vars, no-else-return, prefer-arrow-callback, camelcase, quotes, comma-dangle, no-undef, padded-blocks, max-len */ (function() { this.Admin = (function() { function Admin() { diff --git a/app/assets/javascripts/api.js b/app/assets/javascripts/api.js index 1cab66e109e..1c625e2f2b1 100644 --- a/app/assets/javascripts/api.js +++ b/app/assets/javascripts/api.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, quotes, object-shorthand, camelcase, no-var, no-undef, comma-dangle, prefer-arrow-callback, indent, object-curly-spacing, quote-props, no-param-reassign, padded-blocks, max-len */ (function() { this.Api = { groupsPath: "/api/:version/groups.json", diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js index 5c047dd4481..251533edc4d 100644 --- a/app/assets/javascripts/application.js +++ b/app/assets/javascripts/application.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, no-var, no-undef, quotes, consistent-return, prefer-arrow-callback, comma-dangle, object-shorthand, no-new, max-len */ // This is a manifest file that'll be compiled into including all the files listed below. // Add new JavaScript code in separate files in this directory and they'll automatically // be included in the compiled file accessible from http://example.com/assets/application.js diff --git a/app/assets/javascripts/aside.js b/app/assets/javascripts/aside.js index c7eff27f971..9417afc2ea7 100644 --- a/app/assets/javascripts/aside.js +++ b/app/assets/javascripts/aside.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, wrap-iife, quotes, prefer-arrow-callback, no-var, one-var, one-var-declaration-per-line, no-else-return, padded-blocks, max-len */ (function() { this.Aside = (function() { function Aside() { diff --git a/app/assets/javascripts/autosave.js b/app/assets/javascripts/autosave.js index ab09e4475e6..f45dbe4cbf2 100644 --- a/app/assets/javascripts/autosave.js +++ b/app/assets/javascripts/autosave.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-param-reassign, quotes, prefer-template, no-var, one-var, no-unused-vars, one-var-declaration-per-line, no-void, consistent-return, no-empty, padded-blocks, max-len */ (function() { this.Autosave = (function() { function Autosave(field, key) { diff --git a/app/assets/javascripts/awards_handler.js b/app/assets/javascripts/awards_handler.js index d7cda977845..f4302e2e9f6 100644 --- a/app/assets/javascripts/awards_handler.js +++ b/app/assets/javascripts/awards_handler.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, wrap-iife, max-len, no-var, spaced-comment, prefer-arrow-callback, consistent-return, one-var, one-var-declaration-per-line, no-unused-vars, no-else-return, prefer-template, quotes, comma-dangle, no-param-reassign, no-void, radix, keyword-spacing, space-before-blocks, brace-style, no-underscore-dangle, no-undef, no-plusplus, no-return-assign, camelcase, padded-blocks, max-len */ (function() { this.AwardsHandler = (function() { var FROM_SENTENCE_REGEX = /(?:, and | and |, )/; //For separating lists produced by ruby's Array#toSentence diff --git a/app/assets/javascripts/behaviors/autosize.js b/app/assets/javascripts/behaviors/autosize.js index 074378b9e52..a5d62f881fe 100644 --- a/app/assets/javascripts/behaviors/autosize.js +++ b/app/assets/javascripts/behaviors/autosize.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, prefer-arrow-callback, no-var, consistent-return, no-undef, padded-blocks, max-len */ /*= require jquery.ba-resize */ /*= require autosize */ diff --git a/app/assets/javascripts/behaviors/details_behavior.js b/app/assets/javascripts/behaviors/details_behavior.js index a64cefb62bd..3998ee9a0a0 100644 --- a/app/assets/javascripts/behaviors/details_behavior.js +++ b/app/assets/javascripts/behaviors/details_behavior.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, prefer-arrow-callback, quotes, no-var, vars-on-top, padded-blocks, max-len */ (function() { $(function() { $("body").on("click", ".js-details-target", function() { diff --git a/app/assets/javascripts/behaviors/quick_submit.js b/app/assets/javascripts/behaviors/quick_submit.js index 7ff88ecdcaf..4edcaa15fe5 100644 --- a/app/assets/javascripts/behaviors/quick_submit.js +++ b/app/assets/javascripts/behaviors/quick_submit.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, one-var, no-var, one-var-declaration-per-line, no-undef, prefer-arrow-callback, camelcase, max-len, consistent-return, quotes, object-shorthand, comma-dangle, padded-blocks, max-len */ // Quick Submit behavior // // When a child field of a form with a `js-quick-submit` class receives a diff --git a/app/assets/javascripts/behaviors/requires_input.js b/app/assets/javascripts/behaviors/requires_input.js index 4ac343f876c..72362988b2e 100644 --- a/app/assets/javascripts/behaviors/requires_input.js +++ b/app/assets/javascripts/behaviors/requires_input.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, one-var, no-var, one-var-declaration-per-line, quotes, prefer-template, prefer-arrow-callback, no-else-return, consistent-return, padded-blocks, max-len */ // Requires Input behavior // // When called on a form with input fields with the `required` attribute, the diff --git a/app/assets/javascripts/behaviors/toggler_behavior.js b/app/assets/javascripts/behaviors/toggler_behavior.js index 05b213fe3fb..6a49715590c 100644 --- a/app/assets/javascripts/behaviors/toggler_behavior.js +++ b/app/assets/javascripts/behaviors/toggler_behavior.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable wrap-iife, func-names, space-before-function-paren, prefer-arrow-callback, vars-on-top, no-var, max-len */ (function(w) { $(function() { // Toggle button. Show/hide content inside parent container. diff --git a/app/assets/javascripts/blob/blob_file_dropzone.js b/app/assets/javascripts/blob/blob_file_dropzone.js index 33fb4f8185c..e0a2e8ac12e 100644 --- a/app/assets/javascripts/blob/blob_file_dropzone.js +++ b/app/assets/javascripts/blob/blob_file_dropzone.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, wrap-iife, one-var, no-var, one-var-declaration-per-line, camelcase, no-undef, object-shorthand, quotes, comma-dangle, prefer-arrow-callback, no-unused-vars, prefer-template, no-useless-escape, no-alert, padded-blocks, max-len */ (function() { this.BlobFileDropzone = (function() { function BlobFileDropzone(form, method) { diff --git a/app/assets/javascripts/blob/blob_gitignore_selector.js b/app/assets/javascripts/blob/blob_gitignore_selector.js index 344fe5dcd94..7e8f1062ab3 100644 --- a/app/assets/javascripts/blob/blob_gitignore_selector.js +++ b/app/assets/javascripts/blob/blob_gitignore_selector.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, max-len, one-var, no-var, no-restricted-syntax, vars-on-top, no-use-before-define, no-param-reassign, new-cap, no-underscore-dangle, wrap-iife, prefer-rest-params, no-undef, padded-blocks, max-len */ /*= require blob/template_selector */ diff --git a/app/assets/javascripts/blob/blob_gitignore_selectors.js b/app/assets/javascripts/blob/blob_gitignore_selectors.js index 9e992f7913c..9a694daa010 100644 --- a/app/assets/javascripts/blob/blob_gitignore_selectors.js +++ b/app/assets/javascripts/blob/blob_gitignore_selectors.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-var, no-unused-expressions, no-cond-assign, no-sequences, no-undef, comma-dangle, padded-blocks, max-len */ (function() { this.BlobGitignoreSelectors = (function() { function BlobGitignoreSelectors(opts) { diff --git a/app/assets/javascripts/blob/blob_license_selector.js b/app/assets/javascripts/blob/blob_license_selector.js index 41a83a56146..9a77fe35d55 100644 --- a/app/assets/javascripts/blob/blob_license_selector.js +++ b/app/assets/javascripts/blob/blob_license_selector.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, max-len, one-var, no-var, no-restricted-syntax, vars-on-top, no-use-before-define, no-param-reassign, new-cap, no-underscore-dangle, wrap-iife, prefer-rest-params, comma-dangle, no-undef, padded-blocks, max-len */ /*= require blob/template_selector */ diff --git a/app/assets/javascripts/blob_edit/blob_edit_bundle.js b/app/assets/javascripts/blob_edit/blob_edit_bundle.js index b801c10f168..b8eb0f60a8e 100644 --- a/app/assets/javascripts/blob_edit/blob_edit_bundle.js +++ b/app/assets/javascripts/blob_edit/blob_edit_bundle.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, prefer-arrow-callback, no-var, quotes, vars-on-top, no-unused-vars, no-undef, no-new, padded-blocks, max-len */ /*= require_tree . */ (function() { diff --git a/app/assets/javascripts/blob_edit/edit_blob.js b/app/assets/javascripts/blob_edit/edit_blob.js index 60840560dd3..0c74aaaa852 100644 --- a/app/assets/javascripts/blob_edit/edit_blob.js +++ b/app/assets/javascripts/blob_edit/edit_blob.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, no-var, space-before-blocks, prefer-rest-params, wrap-iife, camelcase, no-param-reassign, no-undef, quotes, prefer-template, no-new, comma-dangle, one-var, one-var-declaration-per-line, prefer-arrow-callback, no-else-return, no-unused-vars, padded-blocks, max-len */ (function() { var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }; diff --git a/app/assets/javascripts/boards/test_utils/simulate_drag.js b/app/assets/javascripts/boards/test_utils/simulate_drag.js index 039ca491cf5..01e09ec482e 100644 --- a/app/assets/javascripts/boards/test_utils/simulate_drag.js +++ b/app/assets/javascripts/boards/test_utils/simulate_drag.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable wrap-iife, func-names, strict, indent, no-tabs, no-var, vars-on-top, no-param-reassign, object-shorthand, no-shadow, comma-dangle, prefer-template, consistent-return, no-mixed-operators, no-unused-vars, object-curly-spacing, no-unused-expressions, prefer-arrow-callback, max-len */ (function () { 'use strict'; diff --git a/app/assets/javascripts/breakpoints.js b/app/assets/javascripts/breakpoints.js index 5d4d23e26c6..e7ceb602601 100644 --- a/app/assets/javascripts/breakpoints.js +++ b/app/assets/javascripts/breakpoints.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, wrap-iife, one-var, no-var, one-var-declaration-per-line, quotes, no-shadow, prefer-arrow-callback, prefer-template, consistent-return, padded-blocks, no-return-assign, new-parens, no-param-reassign, no-undef, max-len */ (function() { this.Breakpoints = (function() { var BreakpointInstance, instance; diff --git a/app/assets/javascripts/broadcast_message.js b/app/assets/javascripts/broadcast_message.js index 576f4c76c1e..30432dae278 100644 --- a/app/assets/javascripts/broadcast_message.js +++ b/app/assets/javascripts/broadcast_message.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, prefer-arrow-callback, no-var, quotes, no-else-return, object-shorthand, comma-dangle, padded-blocks, max-len */ (function() { $(function() { var previewPath; diff --git a/app/assets/javascripts/build.js b/app/assets/javascripts/build.js index 5133e361001..68012e8cf42 100644 --- a/app/assets/javascripts/build.js +++ b/app/assets/javascripts/build.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, no-var, space-before-blocks, prefer-rest-params, wrap-iife, no-use-before-define, no-param-reassign, no-undef, quotes, yoda, no-else-return, consistent-return, comma-dangle, semi, object-shorthand, prefer-template, one-var, one-var-declaration-per-line, no-unused-vars, max-len, vars-on-top, padded-blocks, max-len */ (function() { var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }; diff --git a/app/assets/javascripts/build_artifacts.js b/app/assets/javascripts/build_artifacts.js index 49f84581650..c423a548a30 100644 --- a/app/assets/javascripts/build_artifacts.js +++ b/app/assets/javascripts/build_artifacts.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, wrap-iife, prefer-arrow-callback, no-unused-vars, no-return-assign, padded-blocks, max-len */ (function() { this.BuildArtifacts = (function() { function BuildArtifacts() { diff --git a/app/assets/javascripts/commit.js b/app/assets/javascripts/commit.js index fac5b4f17da..67509ea7d91 100644 --- a/app/assets/javascripts/commit.js +++ b/app/assets/javascripts/commit.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-undef, padded-blocks */ (function() { this.Commit = (function() { function Commit() { diff --git a/app/assets/javascripts/commit/file.js b/app/assets/javascripts/commit/file.js index 16d63729d31..3f29826fa9b 100644 --- a/app/assets/javascripts/commit/file.js +++ b/app/assets/javascripts/commit/file.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-new, no-undef, padded-blocks, max-len */ (function() { this.CommitFile = (function() { function CommitFile(file) { diff --git a/app/assets/javascripts/commit/image_file.js b/app/assets/javascripts/commit/image_file.js index ffddce1297b..4c2ae595319 100644 --- a/app/assets/javascripts/commit/image_file.js +++ b/app/assets/javascripts/commit/image_file.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-var, no-use-before-define, prefer-arrow-callback, no-else-return, consistent-return, prefer-template, quotes, one-var, one-var-declaration-per-line, no-unused-vars, no-return-assign, comma-dangle, quote-props, no-unused-expressions, no-sequences, object-shorthand, padded-blocks, max-len */ (function() { this.ImageFile = (function() { var prepareFrames; diff --git a/app/assets/javascripts/commits.js b/app/assets/javascripts/commits.js index c765d233831..951fb338f9d 100644 --- a/app/assets/javascripts/commits.js +++ b/app/assets/javascripts/commits.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, wrap-iife, quotes, consistent-return, no-undef, no-return-assign, no-param-reassign, one-var, no-var, one-var-declaration-per-line, no-unused-vars, prefer-template, object-shorthand, comma-dangle, padded-blocks, max-len */ (function() { this.CommitsList = (function() { function CommitsList() {} diff --git a/app/assets/javascripts/compare.js b/app/assets/javascripts/compare.js index 61cc91c524b..d4243baadb5 100644 --- a/app/assets/javascripts/compare.js +++ b/app/assets/javascripts/compare.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, wrap-iife, quotes, no-var, object-shorthand, consistent-return, no-unused-vars, comma-dangle, vars-on-top, prefer-template, padded-blocks, max-len */ (function() { this.Compare = (function() { function Compare(opts) { diff --git a/app/assets/javascripts/confirm_danger_modal.js b/app/assets/javascripts/confirm_danger_modal.js index 143d21adb37..686a48486f3 100644 --- a/app/assets/javascripts/confirm_danger_modal.js +++ b/app/assets/javascripts/confirm_danger_modal.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, wrap-iife, one-var, no-var, camelcase, one-var-declaration-per-line, no-else-return, padded-blocks, max-len */ (function() { this.ConfirmDangerModal = (function() { function ConfirmDangerModal(form, text) { diff --git a/app/assets/javascripts/copy_to_clipboard.js b/app/assets/javascripts/copy_to_clipboard.js index 7808d7fe313..1cc34e490c2 100644 --- a/app/assets/javascripts/copy_to_clipboard.js +++ b/app/assets/javascripts/copy_to_clipboard.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, one-var, no-var, one-var-declaration-per-line, no-undef, prefer-template, quotes, no-unused-vars, prefer-arrow-callback, padded-blocks, max-len */ /*= require clipboard */ diff --git a/app/assets/javascripts/diff.js b/app/assets/javascripts/diff.js index 82bfdcea0ca..00da5f17f9f 100644 --- a/app/assets/javascripts/diff.js +++ b/app/assets/javascripts/diff.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-var, max-len, one-var, camelcase, one-var-declaration-per-line, no-unused-vars, no-unused-expressions, no-sequences, object-shorthand, comma-dangle, prefer-arrow-callback, semi, radix, padded-blocks, max-len */ (function() { this.Diff = (function() { var UNFOLD_COUNT; diff --git a/app/assets/javascripts/dropzone_input.js b/app/assets/javascripts/dropzone_input.js index 1a0aa9757ba..e1e76bca6ad 100644 --- a/app/assets/javascripts/dropzone_input.js +++ b/app/assets/javascripts/dropzone_input.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, wrap-iife, max-len, one-var, no-var, one-var-declaration-per-line, no-unused-vars, camelcase, no-undef, quotes, no-useless-concat, prefer-template, quote-props, comma-dangle, object-shorthand, consistent-return, no-plusplus, prefer-arrow-callback, padded-blocks, max-len */ /*= require preview_markdown */ diff --git a/app/assets/javascripts/extensions/array.js b/app/assets/javascripts/extensions/array.js index 4c9e219aa43..fc6c130113d 100644 --- a/app/assets/javascripts/extensions/array.js +++ b/app/assets/javascripts/extensions/array.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable no-extend-native, func-names, space-before-function-paren, semi, space-infix-ops, max-len */ Array.prototype.first = function() { return this[0]; } diff --git a/app/assets/javascripts/extensions/jquery.js b/app/assets/javascripts/extensions/jquery.js index 623a80b7053..cdedc865d1b 100644 --- a/app/assets/javascripts/extensions/jquery.js +++ b/app/assets/javascripts/extensions/jquery.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, object-shorthand, comma-dangle, padded-blocks, max-len */ // Disable an element and add the 'disabled' Bootstrap class (function() { $.fn.extend({ diff --git a/app/assets/javascripts/files_comment_button.js b/app/assets/javascripts/files_comment_button.js index 732136f1f2c..0122e847161 100644 --- a/app/assets/javascripts/files_comment_button.js +++ b/app/assets/javascripts/files_comment_button.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, no-var, space-before-blocks, prefer-rest-params, wrap-iife, max-len, one-var, one-var-declaration-per-line, quotes, prefer-template, newline-per-chained-call, comma-dangle, new-cap, no-else-return, padded-blocks, consistent-return, no-undef, max-len */ (function() { var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }; diff --git a/app/assets/javascripts/flash.js b/app/assets/javascripts/flash.js index 46e272c3311..804d7d9c4ab 100644 --- a/app/assets/javascripts/flash.js +++ b/app/assets/javascripts/flash.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-var, one-var, one-var-declaration-per-line, no-param-reassign, quotes, quote-props, prefer-template, comma-dangle, padded-blocks, max-len */ (function() { this.Flash = (function() { var hideFlash; diff --git a/app/assets/javascripts/gl_dropdown.js b/app/assets/javascripts/gl_dropdown.js index 98e43c4d088..669c5f0e284 100644 --- a/app/assets/javascripts/gl_dropdown.js +++ b/app/assets/javascripts/gl_dropdown.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, no-var, one-var, one-var-declaration-per-line, space-before-blocks, prefer-rest-params, max-len, vars-on-top, no-plusplus, wrap-iife, no-unused-vars, quotes, no-shadow, no-cond-assign, prefer-arrow-callback, semi, no-return-assign, no-else-return, camelcase, no-undef, comma-dangle, no-lonely-if, guard-for-in, no-restricted-syntax, consistent-return, padded-blocks, prefer-template, no-param-reassign, no-loop-func, no-extra-semi, keyword-spacing, no-mixed-operators, max-len */ (function() { var GitLabDropdown, GitLabDropdownFilter, GitLabDropdownRemote, bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }, diff --git a/app/assets/javascripts/gl_form.js b/app/assets/javascripts/gl_form.js index ce54c34492d..db5d9e75b3a 100644 --- a/app/assets/javascripts/gl_form.js +++ b/app/assets/javascripts/gl_form.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-var, no-undef, no-new, padded-blocks, max-len */ (function() { this.GLForm = (function() { function GLForm(form) { diff --git a/app/assets/javascripts/graphs/graphs_bundle.js b/app/assets/javascripts/graphs/graphs_bundle.js index e103748d499..32c26349da0 100644 --- a/app/assets/javascripts/graphs/graphs_bundle.js +++ b/app/assets/javascripts/graphs/graphs_bundle.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren */ // This is a manifest file that'll be compiled into including all the files listed below. // Add new JavaScript code in separate files in this directory and they'll automatically // be included in the compiled file accessible from http://example.com/assets/application.js diff --git a/app/assets/javascripts/graphs/stat_graph.js b/app/assets/javascripts/graphs/stat_graph.js index b796a9abb49..3273bf3a263 100644 --- a/app/assets/javascripts/graphs/stat_graph.js +++ b/app/assets/javascripts/graphs/stat_graph.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-return-assign, padded-blocks, max-len */ (function() { this.StatGraph = (function() { function StatGraph() {} diff --git a/app/assets/javascripts/graphs/stat_graph_contributors.js b/app/assets/javascripts/graphs/stat_graph_contributors.js index 818bff0c413..c3a132b3c75 100644 --- a/app/assets/javascripts/graphs/stat_graph_contributors.js +++ b/app/assets/javascripts/graphs/stat_graph_contributors.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-var, one-var, camelcase, one-var-declaration-per-line, no-undef, quotes, no-param-reassign, quote-props, comma-dangle, prefer-template, max-len, no-return-assign, padded-blocks, max-len */ /*= require d3 */ diff --git a/app/assets/javascripts/graphs/stat_graph_contributors_graph.js b/app/assets/javascripts/graphs/stat_graph_contributors_graph.js index dea26a3f1e1..cb2448e8cc7 100644 --- a/app/assets/javascripts/graphs/stat_graph_contributors_graph.js +++ b/app/assets/javascripts/graphs/stat_graph_contributors_graph.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, one-var, no-var, space-before-blocks, prefer-rest-params, max-len, no-restricted-syntax, vars-on-top, no-use-before-define, no-param-reassign, new-cap, no-underscore-dangle, wrap-iife, comma-dangle, no-return-assign, prefer-arrow-callback, quotes, prefer-template, padded-blocks, no-undef, newline-per-chained-call, no-else-return, max-len */ /*= require d3 */ diff --git a/app/assets/javascripts/graphs/stat_graph_contributors_util.js b/app/assets/javascripts/graphs/stat_graph_contributors_util.js index 362a77e868f..051ff98c774 100644 --- a/app/assets/javascripts/graphs/stat_graph_contributors_util.js +++ b/app/assets/javascripts/graphs/stat_graph_contributors_util.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, object-shorthand, no-var, one-var, camelcase, one-var-declaration-per-line, no-plusplus, comma-dangle, no-param-reassign, no-return-assign, quotes, prefer-arrow-callback, wrap-iife, consistent-return, no-unused-vars, max-len, no-cond-assign, no-else-return, padded-blocks, max-len */ (function() { window.ContributorsStatGraphUtil = { parse_log: function(log) { diff --git a/app/assets/javascripts/group_avatar.js b/app/assets/javascripts/group_avatar.js index 774477dc7a9..17a76168a79 100644 --- a/app/assets/javascripts/group_avatar.js +++ b/app/assets/javascripts/group_avatar.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, wrap-iife, quotes, no-var, one-var, one-var-declaration-per-line, no-useless-escape, padded-blocks, max-len */ (function() { this.GroupAvatar = (function() { function GroupAvatar() { diff --git a/app/assets/javascripts/groups_select.js b/app/assets/javascripts/groups_select.js index e3c39c895ba..3dc6f05ca20 100644 --- a/app/assets/javascripts/groups_select.js +++ b/app/assets/javascripts/groups_select.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, no-var, wrap-iife, one-var, camelcase, one-var-declaration-per-line, quotes, object-shorthand, no-undef, prefer-arrow-callback, comma-dangle, consistent-return, yoda, prefer-rest-params, prefer-spread, no-unused-vars, prefer-template, padded-blocks, max-len */ (function() { var slice = [].slice; diff --git a/app/assets/javascripts/header.js b/app/assets/javascripts/header.js index 81fcaf06430..c7cbf9ca44b 100644 --- a/app/assets/javascripts/header.js +++ b/app/assets/javascripts/header.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable wrap-iife, func-names, space-before-function-paren, padded-blocks, prefer-arrow-callback, no-var, max-len */ (function() { $(document).on('todo:toggle', function(e, count) { diff --git a/app/assets/javascripts/importer_status.js b/app/assets/javascripts/importer_status.js index c53f7c88aa2..9425b6ed9d4 100644 --- a/app/assets/javascripts/importer_status.js +++ b/app/assets/javascripts/importer_status.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, wrap-iife, camelcase, no-var, one-var, one-var-declaration-per-line, prefer-template, quotes, object-shorthand, comma-dangle, no-unused-vars, prefer-arrow-callback, no-else-return, padded-blocks, vars-on-top, no-new, no-undef, max-len */ (function() { this.ImporterStatus = (function() { function ImporterStatus(jobs_url, import_url) { diff --git a/app/assets/javascripts/issuable_context.js b/app/assets/javascripts/issuable_context.js index fae49ee6144..317818951fd 100644 --- a/app/assets/javascripts/issuable_context.js +++ b/app/assets/javascripts/issuable_context.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-new, no-undef, comma-dangle, quotes, prefer-arrow-callback, consistent-return, one-var, no-var, one-var-declaration-per-line, no-underscore-dangle, padded-blocks, max-len */ (function() { this.IssuableContext = (function() { function IssuableContext(currentUser) { diff --git a/app/assets/javascripts/issuable_form.js b/app/assets/javascripts/issuable_form.js index 849b45756ee..50fdbc89c7c 100644 --- a/app/assets/javascripts/issuable_form.js +++ b/app/assets/javascripts/issuable_form.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, no-var, space-before-blocks, prefer-rest-params, wrap-iife, no-use-before-define, no-useless-escape, no-undef, no-new, quotes, object-shorthand, no-unused-vars, comma-dangle, radix, no-alert, consistent-return, no-else-return, prefer-template, one-var, one-var-declaration-per-line, curly, padded-blocks, max-len */ (function() { var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }; diff --git a/app/assets/javascripts/issue.js b/app/assets/javascripts/issue.js index 67ace697936..8540b199aba 100644 --- a/app/assets/javascripts/issue.js +++ b/app/assets/javascripts/issue.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, no-var, space-before-blocks, prefer-rest-params, wrap-iife, one-var, no-underscore-dangle, one-var-declaration-per-line, object-shorthand, no-unused-vars, no-undef, no-new, comma-dangle, consistent-return, quotes, dot-notation, quote-props, prefer-arrow-callback, padded-blocks, max-len */ /*= require flash */ /*= require jquery.waitforimages */ diff --git a/app/assets/javascripts/issue_status_select.js b/app/assets/javascripts/issue_status_select.js index d7262e5eb74..b39d8274e13 100644 --- a/app/assets/javascripts/issue_status_select.js +++ b/app/assets/javascripts/issue_status_select.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-var, quotes, object-shorthand, no-unused-vars, no-shadow, one-var, one-var-declaration-per-line, comma-dangle, padded-blocks, max-len */ (function() { this.IssueStatusSelect = (function() { function IssueStatusSelect() { diff --git a/app/assets/javascripts/labels.js b/app/assets/javascripts/labels.js index 3033e8ca5c2..10de13c9a8a 100644 --- a/app/assets/javascripts/labels.js +++ b/app/assets/javascripts/labels.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, no-var, space-before-blocks, prefer-rest-params, wrap-iife, vars-on-top, no-unused-vars, padded-blocks, max-len */ (function() { var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }; diff --git a/app/assets/javascripts/labels_select.js b/app/assets/javascripts/labels_select.js index c334e3e0c02..812d5cde685 100644 --- a/app/assets/javascripts/labels_select.js +++ b/app/assets/javascripts/labels_select.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-var, no-underscore-dangle, prefer-arrow-callback, max-len, one-var, no-unused-vars, one-var-declaration-per-line, prefer-template, no-new, consistent-return, object-shorthand, comma-dangle, no-shadow, no-param-reassign, brace-style, vars-on-top, quotes, no-lonely-if, no-else-return, no-undef, semi, dot-notation, no-empty, no-return-assign, camelcase, prefer-spread, padded-blocks, max-len */ (function() { this.LabelsSelect = (function() { function LabelsSelect() { diff --git a/app/assets/javascripts/layout_nav.js b/app/assets/javascripts/layout_nav.js index 6b4edf02f4d..2b700539c2b 100644 --- a/app/assets/javascripts/layout_nav.js +++ b/app/assets/javascripts/layout_nav.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, no-var, prefer-arrow-callback, no-unused-vars, one-var, one-var-declaration-per-line, indent, vars-on-top, padded-blocks, max-len */ (function() { var hideEndFade; diff --git a/app/assets/javascripts/lib/chart.js b/app/assets/javascripts/lib/chart.js index e1dfdae97de..d8ad5aaeffe 100644 --- a/app/assets/javascripts/lib/chart.js +++ b/app/assets/javascripts/lib/chart.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren */ /*= require Chart */ diff --git a/app/assets/javascripts/lib/cropper.js b/app/assets/javascripts/lib/cropper.js index 155e30cc462..5221f85ba7a 100644 --- a/app/assets/javascripts/lib/cropper.js +++ b/app/assets/javascripts/lib/cropper.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren */ /*= require cropper */ diff --git a/app/assets/javascripts/lib/d3.js b/app/assets/javascripts/lib/d3.js index 0c9c2787077..57e7986576c 100644 --- a/app/assets/javascripts/lib/d3.js +++ b/app/assets/javascripts/lib/d3.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren */ /*= require d3 */ diff --git a/app/assets/javascripts/lib/raphael.js b/app/assets/javascripts/lib/raphael.js index cc445db274b..5a9a501efe3 100644 --- a/app/assets/javascripts/lib/raphael.js +++ b/app/assets/javascripts/lib/raphael.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren */ /*= require raphael */ /*= require g.raphael */ diff --git a/app/assets/javascripts/lib/utils/animate.js b/app/assets/javascripts/lib/utils/animate.js index a68edab2aad..83957af94f3 100644 --- a/app/assets/javascripts/lib/utils/animate.js +++ b/app/assets/javascripts/lib/utils/animate.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-param-reassign, no-void, prefer-template, no-var, new-cap, prefer-arrow-callback, consistent-return, padded-blocks, max-len */ (function() { (function(w) { if (w.gl == null) { diff --git a/app/assets/javascripts/lib/utils/common_utils.js b/app/assets/javascripts/lib/utils/common_utils.js index 6cb3d95f984..2a38ac28172 100644 --- a/app/assets/javascripts/lib/utils/common_utils.js +++ b/app/assets/javascripts/lib/utils/common_utils.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-var, no-unused-expressions, no-param-reassign, no-else-return, quotes, object-shorthand, comma-dangle, camelcase, one-var, vars-on-top, one-var-declaration-per-line, no-return-assign, consistent-return, padded-blocks, max-len */ (function() { (function(w) { var base; diff --git a/app/assets/javascripts/lib/utils/datetime_utility.js b/app/assets/javascripts/lib/utils/datetime_utility.js index 3965109dd65..d480fdc882b 100644 --- a/app/assets/javascripts/lib/utils/datetime_utility.js +++ b/app/assets/javascripts/lib/utils/datetime_utility.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-var, no-param-reassign, no-cond-assign, no-undef, comma-dangle, no-unused-expressions, prefer-template, padded-blocks, max-len */ (function() { (function(w) { var base; diff --git a/app/assets/javascripts/lib/utils/notify.js b/app/assets/javascripts/lib/utils/notify.js index dafc006d2e5..d0fe69260a5 100644 --- a/app/assets/javascripts/lib/utils/notify.js +++ b/app/assets/javascripts/lib/utils/notify.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-var, one-var, one-var-declaration-per-line, consistent-return, no-undef, prefer-arrow-callback, no-return-assign, object-shorthand, comma-dangle, no-param-reassign, padded-blocks, max-len */ (function() { (function(w) { var notificationGranted, notifyMe, notifyPermissions; diff --git a/app/assets/javascripts/lib/utils/text_utility.js b/app/assets/javascripts/lib/utils/text_utility.js index 98f9815ff05..5b4123a483b 100644 --- a/app/assets/javascripts/lib/utils/text_utility.js +++ b/app/assets/javascripts/lib/utils/text_utility.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-var, no-param-reassign, no-cond-assign, quotes, semi, one-var, one-var-declaration-per-line, operator-assignment, no-else-return, prefer-template, prefer-arrow-callback, no-empty, max-len, consistent-return, no-unused-vars, no-return-assign, padded-blocks, max-len */ (function() { (function(w) { var base; diff --git a/app/assets/javascripts/lib/utils/timeago.js b/app/assets/javascripts/lib/utils/timeago.js index 42606dd2d46..edf0a612374 100644 --- a/app/assets/javascripts/lib/utils/timeago.js +++ b/app/assets/javascripts/lib/utils/timeago.js @@ -1,3 +1,5 @@ +/* eslint-disable no-unused-expressions, wrap-iife, func-names, curly, no-param-reassign, no-trailing-spaces, prefer-arrow-callback, no-var, one-var, quote-props, space-before-function-paren, vars-on-top, radix, prefer-template, space-infix-ops, no-use-before-define, newline-per-chained-call, no-useless-escape, no-nested-ternary, indent, no-undef, no-plusplus, one-var-declaration-per-line, operator-assignment, consistent-return, keyword-spacing, max-len, space-unary-ops, no-shadow, no-restricted-syntax, guard-for-in, eol-last, max-len */ + /** * Copyright (c) 2016 hustcc * License: MIT diff --git a/app/assets/javascripts/lib/utils/type_utility.js b/app/assets/javascripts/lib/utils/type_utility.js index 4fd1e3fc1d3..961859dfb5b 100644 --- a/app/assets/javascripts/lib/utils/type_utility.js +++ b/app/assets/javascripts/lib/utils/type_utility.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-var, no-param-reassign, no-cond-assign, no-return-assign, padded-blocks, max-len */ (function() { (function(w) { var base; diff --git a/app/assets/javascripts/lib/utils/url_utility.js b/app/assets/javascripts/lib/utils/url_utility.js index 44a66a915e3..6872186cd7f 100644 --- a/app/assets/javascripts/lib/utils/url_utility.js +++ b/app/assets/javascripts/lib/utils/url_utility.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-var, no-param-reassign, no-cond-assign, one-var, one-var-declaration-per-line, no-void, no-plusplus, guard-for-in, no-restricted-syntax, prefer-template, quotes, padded-blocks, max-len */ (function() { (function(w) { var base; diff --git a/app/assets/javascripts/line_highlighter.js b/app/assets/javascripts/line_highlighter.js index ea5a60bb78e..b0f834705c3 100644 --- a/app/assets/javascripts/line_highlighter.js +++ b/app/assets/javascripts/line_highlighter.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, no-var, space-before-blocks, prefer-rest-params, wrap-iife, no-use-before-define, no-underscore-dangle, no-param-reassign, no-undef, prefer-template, quotes, comma-dangle, prefer-arrow-callback, consistent-return, one-var, one-var-declaration-per-line, spaced-comment, radix, no-else-return, max-len, no-plusplus, padded-blocks, max-len */ // LineHighlighter // // Handles single- and multi-line selection and highlight for blob views. diff --git a/app/assets/javascripts/logo.js b/app/assets/javascripts/logo.js index d4f86534f0c..9404b2c3a8c 100644 --- a/app/assets/javascripts/logo.js +++ b/app/assets/javascripts/logo.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, prefer-arrow-callback, padded-blocks */ (function() { Turbolinks.enableProgressBar(); diff --git a/app/assets/javascripts/member_expiration_date.js b/app/assets/javascripts/member_expiration_date.js index 0bd90c57396..7741cd29793 100644 --- a/app/assets/javascripts/member_expiration_date.js +++ b/app/assets/javascripts/member_expiration_date.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, vars-on-top, no-var, object-shorthand, comma-dangle, max-len */ (function() { // Add datepickers to all `js-access-expiration-date` elements. If those elements are // children of an element with the `clearable-input` class, and have a sibling diff --git a/app/assets/javascripts/merge_request.js b/app/assets/javascripts/merge_request.js index d3bd1e846c1..a4b4db14db8 100644 --- a/app/assets/javascripts/merge_request.js +++ b/app/assets/javascripts/merge_request.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, no-var, space-before-blocks, prefer-rest-params, wrap-iife, quotes, no-undef, no-underscore-dangle, one-var, one-var-declaration-per-line, consistent-return, dot-notation, quote-props, comma-dangle, object-shorthand, padded-blocks, max-len */ /*= require jquery.waitforimages */ /*= require task_list */ diff --git a/app/assets/javascripts/merge_request_tabs.js b/app/assets/javascripts/merge_request_tabs.js index 5ca4b34909a..5e0257c09a6 100644 --- a/app/assets/javascripts/merge_request_tabs.js +++ b/app/assets/javascripts/merge_request_tabs.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable max-len, func-names, space-before-function-paren, no-var, space-before-blocks, prefer-rest-params, wrap-iife, no-use-before-define, no-underscore-dangle, no-undef, one-var, one-var-declaration-per-line, quotes, comma-dangle, consistent-return, prefer-template, no-param-reassign, camelcase, vars-on-top, space-in-parens, curly, prefer-arrow-callback, no-unused-vars, no-return-assign, semi, object-shorthand, operator-assignment, padded-blocks, max-len */ // MergeRequestTabs // // Handles persisting and restoring the current tab selection and lazily-loading diff --git a/app/assets/javascripts/merged_buttons.js b/app/assets/javascripts/merged_buttons.js index 7ad86d8c084..15a12c3d985 100644 --- a/app/assets/javascripts/merged_buttons.js +++ b/app/assets/javascripts/merged_buttons.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, no-var, space-before-blocks, prefer-rest-params, wrap-iife, no-undef, padded-blocks, max-len */ (function() { var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }; diff --git a/app/assets/javascripts/milestone.js b/app/assets/javascripts/milestone.js index 9299c96e8ea..db7561a3a75 100644 --- a/app/assets/javascripts/milestone.js +++ b/app/assets/javascripts/milestone.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-use-before-define, camelcase, quotes, object-shorthand, no-shadow, no-unused-vars, no-undef, comma-dangle, no-var, prefer-template, no-underscore-dangle, consistent-return, one-var, one-var-declaration-per-line, default-case, prefer-arrow-callback, padded-blocks, max-len */ (function() { this.Milestone = (function() { Milestone.updateIssue = function(li, issue_url, data) { diff --git a/app/assets/javascripts/milestone_select.js b/app/assets/javascripts/milestone_select.js index d1cd38ad110..67796083790 100644 --- a/app/assets/javascripts/milestone_select.js +++ b/app/assets/javascripts/milestone_select.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-var, no-underscore-dangle, prefer-arrow-callback, max-len, one-var, one-var-declaration-per-line, no-unused-vars, object-shorthand, comma-dangle, no-else-return, no-self-compare, consistent-return, no-undef, no-param-reassign, no-shadow, padded-blocks, max-len */ (function() { this.MilestoneSelect = (function() { function MilestoneSelect(currentProject) { diff --git a/app/assets/javascripts/namespace_select.js b/app/assets/javascripts/namespace_select.js index d1168227b77..87c903ec576 100644 --- a/app/assets/javascripts/namespace_select.js +++ b/app/assets/javascripts/namespace_select.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, no-var, space-before-blocks, prefer-rest-params, wrap-iife, one-var, vars-on-top, one-var-declaration-per-line, comma-dangle, object-shorthand, no-else-return, prefer-template, quotes, no-undef, prefer-arrow-callback, padded-blocks, no-param-reassign, no-cond-assign, max-len */ (function() { var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }; diff --git a/app/assets/javascripts/network/branch_graph.js b/app/assets/javascripts/network/branch_graph.js index 74dbeb94741..e3dc599b90a 100644 --- a/app/assets/javascripts/network/branch_graph.js +++ b/app/assets/javascripts/network/branch_graph.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, no-var, space-before-blocks, prefer-rest-params, wrap-iife, quotes, comma-dangle, one-var, one-var-declaration-per-line, no-mixed-operators, new-cap, no-undef, no-plusplus, no-loop-func, no-floating-decimal, consistent-return, no-unused-vars, prefer-template, prefer-arrow-callback, camelcase, max-len, padded-blocks, max-len */ (function() { var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }; diff --git a/app/assets/javascripts/network/network.js b/app/assets/javascripts/network/network.js index 8898e7ace43..5a8f723a27b 100644 --- a/app/assets/javascripts/network/network.js +++ b/app/assets/javascripts/network/network.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-var, quotes, no-undef, quote-props, prefer-template, comma-dangle, padded-blocks, max-len */ (function() { this.Network = (function() { function Network(opts) { diff --git a/app/assets/javascripts/network/network_bundle.js b/app/assets/javascripts/network/network_bundle.js index a192273a180..732d92845cb 100644 --- a/app/assets/javascripts/network/network_bundle.js +++ b/app/assets/javascripts/network/network_bundle.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, prefer-arrow-callback, quotes, no-var, vars-on-top, camelcase, no-undef, comma-dangle, consistent-return, padded-blocks, max-len */ // This is a manifest file that'll be compiled into including all the files listed below. // Add new JavaScript code in separate files in this directory and they'll automatically // be included in the compiled file accessible from http://example.com/assets/application.js diff --git a/app/assets/javascripts/new_branch_form.js b/app/assets/javascripts/new_branch_form.js index 0e643b0ff14..29a323dd4c6 100644 --- a/app/assets/javascripts/new_branch_form.js +++ b/app/assets/javascripts/new_branch_form.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, no-var, one-var, space-before-blocks, prefer-rest-params, max-len, vars-on-top, no-plusplus, wrap-iife, consistent-return, comma-dangle, one-var-declaration-per-line, quotes, no-return-assign, prefer-arrow-callback, prefer-template, no-shadow, no-else-return, padded-blocks, max-len */ (function() { var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }, indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; }; diff --git a/app/assets/javascripts/new_commit_form.js b/app/assets/javascripts/new_commit_form.js index acb529023fa..8fb8f3e4a5f 100644 --- a/app/assets/javascripts/new_commit_form.js +++ b/app/assets/javascripts/new_commit_form.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, no-var, space-before-blocks, prefer-rest-params, wrap-iife, no-return-assign, padded-blocks, max-len */ (function() { var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }; diff --git a/app/assets/javascripts/notes.js b/app/assets/javascripts/notes.js index df7e316ca6c..44079bc3ca3 100644 --- a/app/assets/javascripts/notes.js +++ b/app/assets/javascripts/notes.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, no-var, space-before-blocks, prefer-rest-params, wrap-iife, no-use-before-define, camelcase, no-unused-expressions, quotes, max-len, one-var, one-var-declaration-per-line, default-case, prefer-template, no-undef, consistent-return, no-alert, no-return-assign, no-param-reassign, prefer-arrow-callback, no-else-return, comma-dangle, no-new, brace-style, no-lonely-if, vars-on-top, no-unused-vars, semi, indent, no-sequences, no-shadow, newline-per-chained-call, no-useless-escape, radix, padded-blocks, max-len */ /*= require autosave */ /*= require autosize */ diff --git a/app/assets/javascripts/notifications_dropdown.js b/app/assets/javascripts/notifications_dropdown.js index ef3f2c6ae73..b152d26733f 100644 --- a/app/assets/javascripts/notifications_dropdown.js +++ b/app/assets/javascripts/notifications_dropdown.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, wrap-iife, one-var, no-var, one-var-declaration-per-line, no-unused-vars, consistent-return, prefer-arrow-callback, no-else-return, no-undef, padded-blocks, max-len */ (function() { this.NotificationsDropdown = (function() { function NotificationsDropdown() { diff --git a/app/assets/javascripts/notifications_form.js b/app/assets/javascripts/notifications_form.js index 6fbec8efe9b..2034f9a748a 100644 --- a/app/assets/javascripts/notifications_form.js +++ b/app/assets/javascripts/notifications_form.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, no-var, space-before-blocks, prefer-rest-params, wrap-iife, one-var, one-var-declaration-per-line, newline-per-chained-call, comma-dangle, consistent-return, prefer-arrow-callback, padded-blocks, max-len */ (function() { var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }; diff --git a/app/assets/javascripts/pager.js b/app/assets/javascripts/pager.js index 2e4dc62273e..d22d2d9dbae 100644 --- a/app/assets/javascripts/pager.js +++ b/app/assets/javascripts/pager.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, object-shorthand, quotes, no-undef, prefer-template, wrap-iife, comma-dangle, no-return-assign, no-else-return, consistent-return, no-unused-vars, padded-blocks, max-len */ (function() { this.Pager = { init: function(limit, preload, disable, callback) { diff --git a/app/assets/javascripts/preview_markdown.js b/app/assets/javascripts/preview_markdown.js index f2a45a18bed..3723aa24942 100644 --- a/app/assets/javascripts/preview_markdown.js +++ b/app/assets/javascripts/preview_markdown.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, no-var, one-var, one-var-declaration-per-line, wrap-iife, no-else-return, consistent-return, object-shorthand, comma-dangle, no-param-reassign, padded-blocks, no-undef, camelcase, prefer-arrow-callback, max-len */ // MarkdownPreview // // Handles toggling the "Write" and "Preview" tab clicks, rendering the preview, diff --git a/app/assets/javascripts/profile/profile_bundle.js b/app/assets/javascripts/profile/profile_bundle.js index 22bee0f6187..f50802bdf2e 100644 --- a/app/assets/javascripts/profile/profile_bundle.js +++ b/app/assets/javascripts/profile/profile_bundle.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren */ /*= require_tree . */ diff --git a/app/assets/javascripts/project.js b/app/assets/javascripts/project.js index 2d0c6b16699..016d999d77e 100644 --- a/app/assets/javascripts/project.js +++ b/app/assets/javascripts/project.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-var, quotes, consistent-return, no-undef, no-new, prefer-arrow-callback, no-return-assign, one-var, one-var-declaration-per-line, object-shorthand, comma-dangle, no-else-return, newline-per-chained-call, no-shadow, semi, vars-on-top, indent, prefer-template, padded-blocks, max-len */ (function() { this.Project = (function() { function Project() { diff --git a/app/assets/javascripts/project_avatar.js b/app/assets/javascripts/project_avatar.js index 61877c6616d..84f28ede4bf 100644 --- a/app/assets/javascripts/project_avatar.js +++ b/app/assets/javascripts/project_avatar.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-var, one-var, one-var-declaration-per-line, no-useless-escape, padded-blocks, max-len */ (function() { this.ProjectAvatar = (function() { function ProjectAvatar() { diff --git a/app/assets/javascripts/project_find_file.js b/app/assets/javascripts/project_find_file.js index ddac5ed83e1..804306a3293 100644 --- a/app/assets/javascripts/project_find_file.js +++ b/app/assets/javascripts/project_find_file.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, no-var, space-before-blocks, prefer-rest-params, wrap-iife, quotes, consistent-return, one-var, one-var-declaration-per-line, no-cond-assign, max-len, no-undef, object-shorthand, no-param-reassign, comma-dangle, no-plusplus, prefer-template, no-unused-vars, no-return-assign, padded-blocks, max-len */ (function() { var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }; diff --git a/app/assets/javascripts/project_fork.js b/app/assets/javascripts/project_fork.js index fd95f8f2c19..4aedc9a2330 100644 --- a/app/assets/javascripts/project_fork.js +++ b/app/assets/javascripts/project_fork.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, wrap-iife, prefer-arrow-callback, padded-blocks, max-len */ (function() { this.ProjectFork = (function() { function ProjectFork() { diff --git a/app/assets/javascripts/project_import.js b/app/assets/javascripts/project_import.js index f1c4a9fe542..c99e55234cf 100644 --- a/app/assets/javascripts/project_import.js +++ b/app/assets/javascripts/project_import.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, wrap-iife, prefer-arrow-callback, no-undef, padded-blocks, max-len */ (function() { this.ProjectImport = (function() { function ProjectImport() { diff --git a/app/assets/javascripts/project_new.js b/app/assets/javascripts/project_new.js index 0d3fb31a9cf..7fc611d0dad 100644 --- a/app/assets/javascripts/project_new.js +++ b/app/assets/javascripts/project_new.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, no-var, space-before-blocks, prefer-rest-params, wrap-iife, no-unused-vars, one-var, indent, no-underscore-dangle, prefer-template, no-else-return, prefer-arrow-callback, radix, padded-blocks, max-len */ (function() { var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }; diff --git a/app/assets/javascripts/project_select.js b/app/assets/javascripts/project_select.js index e1acf3c8232..fe1f96872f3 100644 --- a/app/assets/javascripts/project_select.js +++ b/app/assets/javascripts/project_select.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, wrap-iife, prefer-arrow-callback, no-var, comma-dangle, object-shorthand, one-var, one-var-declaration-per-line, no-undef, no-else-return, quotes, padded-blocks, max-len */ (function() { this.ProjectSelect = (function() { function ProjectSelect() { diff --git a/app/assets/javascripts/project_show.js b/app/assets/javascripts/project_show.js index 21650f5f67a..eaf4c03d573 100644 --- a/app/assets/javascripts/project_show.js +++ b/app/assets/javascripts/project_show.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, wrap-iife, padded-blocks */ (function() { this.ProjectShow = (function() { function ProjectShow() {} diff --git a/app/assets/javascripts/projects_list.js b/app/assets/javascripts/projects_list.js index 3458cd89ae2..dbf530bed41 100644 --- a/app/assets/javascripts/projects_list.js +++ b/app/assets/javascripts/projects_list.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, object-shorthand, quotes, no-var, one-var, one-var-declaration-per-line, no-undef, prefer-arrow-callback, consistent-return, no-unused-vars, camelcase, prefer-template, comma-dangle, padded-blocks, max-len */ (function() { this.ProjectsList = { init: function() { diff --git a/app/assets/javascripts/right_sidebar.js b/app/assets/javascripts/right_sidebar.js index df38937858f..440b5da756d 100644 --- a/app/assets/javascripts/right_sidebar.js +++ b/app/assets/javascripts/right_sidebar.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, no-var, space-before-blocks, prefer-rest-params, wrap-iife, no-unused-vars, semi, consistent-return, one-var, one-var-declaration-per-line, no-undef, quotes, prefer-template, object-shorthand, comma-dangle, no-else-return, no-param-reassign, padded-blocks, max-len */ (function() { var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }; diff --git a/app/assets/javascripts/search.js b/app/assets/javascripts/search.js index d79e6f014f6..1d208f1494c 100644 --- a/app/assets/javascripts/search.js +++ b/app/assets/javascripts/search.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-var, one-var, one-var-declaration-per-line, object-shorthand, no-undef, prefer-arrow-callback, comma-dangle, prefer-template, quotes, no-else-return, padded-blocks, max-len */ (function() { this.Search = (function() { function Search() { diff --git a/app/assets/javascripts/shortcuts.js b/app/assets/javascripts/shortcuts.js index 8d8ab6dda5e..fa2168723be 100644 --- a/app/assets/javascripts/shortcuts.js +++ b/app/assets/javascripts/shortcuts.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, no-var, space-before-blocks, prefer-rest-params, wrap-iife, quotes, no-undef, prefer-arrow-callback, consistent-return, object-shorthand, no-unused-vars, one-var, one-var-declaration-per-line, no-plusplus, no-else-return, comma-dangle, padded-blocks, max-len */ (function() { var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }; diff --git a/app/assets/javascripts/shortcuts_blob.js b/app/assets/javascripts/shortcuts_blob.js index 704a8bd3a57..65305b8c22f 100644 --- a/app/assets/javascripts/shortcuts_blob.js +++ b/app/assets/javascripts/shortcuts_blob.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, max-len, one-var, no-var, no-restricted-syntax, vars-on-top, no-use-before-define, no-param-reassign, new-cap, no-underscore-dangle, wrap-iife, consistent-return, padded-blocks, no-undef, max-len */ /*= require shortcuts */ diff --git a/app/assets/javascripts/shortcuts_dashboard_navigation.js b/app/assets/javascripts/shortcuts_dashboard_navigation.js index befe4eccdba..1b9a265ba39 100644 --- a/app/assets/javascripts/shortcuts_dashboard_navigation.js +++ b/app/assets/javascripts/shortcuts_dashboard_navigation.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, max-len, one-var, no-var, no-restricted-syntax, vars-on-top, no-use-before-define, no-param-reassign, new-cap, no-underscore-dangle, wrap-iife, prefer-arrow-callback, consistent-return, no-return-assign, padded-blocks, no-undef, max-len */ /*= require shortcuts */ diff --git a/app/assets/javascripts/shortcuts_find_file.js b/app/assets/javascripts/shortcuts_find_file.js index 90ed4267661..68cd6fad04e 100644 --- a/app/assets/javascripts/shortcuts_find_file.js +++ b/app/assets/javascripts/shortcuts_find_file.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, max-len, one-var, no-var, no-restricted-syntax, vars-on-top, no-use-before-define, no-param-reassign, new-cap, no-underscore-dangle, wrap-iife, padded-blocks, no-undef, max-len */ /*= require shortcuts_navigation */ diff --git a/app/assets/javascripts/shortcuts_issuable.js b/app/assets/javascripts/shortcuts_issuable.js index 25ec7dbc067..c4899f3566a 100644 --- a/app/assets/javascripts/shortcuts_issuable.js +++ b/app/assets/javascripts/shortcuts_issuable.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, max-len, no-var, one-var, no-restricted-syntax, vars-on-top, no-use-before-define, no-param-reassign, new-cap, no-underscore-dangle, wrap-iife, one-var-declaration-per-line, quotes, prefer-arrow-callback, consistent-return, prefer-template, no-mixed-operators, no-undef, padded-blocks, max-len */ /*= require mousetrap */ /*= require shortcuts_navigation */ diff --git a/app/assets/javascripts/shortcuts_navigation.js b/app/assets/javascripts/shortcuts_navigation.js index 19c6b7d30ab..7d4d6364c70 100644 --- a/app/assets/javascripts/shortcuts_navigation.js +++ b/app/assets/javascripts/shortcuts_navigation.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, max-len, no-var, one-var, no-restricted-syntax, vars-on-top, no-use-before-define, no-param-reassign, new-cap, no-underscore-dangle, wrap-iife, prefer-arrow-callback, consistent-return, no-return-assign, padded-blocks, no-undef, max-len */ /*= require shortcuts */ diff --git a/app/assets/javascripts/shortcuts_network.js b/app/assets/javascripts/shortcuts_network.js index 002e979a2c6..a4095d2c06b 100644 --- a/app/assets/javascripts/shortcuts_network.js +++ b/app/assets/javascripts/shortcuts_network.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, max-len, no-var, one-var, no-restricted-syntax, vars-on-top, no-use-before-define, no-param-reassign, new-cap, no-underscore-dangle, wrap-iife, padded-blocks, no-undef, max-len */ /*= require shortcuts_navigation */ diff --git a/app/assets/javascripts/single_file_diff.js b/app/assets/javascripts/single_file_diff.js index 01ccbe5b987..2767849e673 100644 --- a/app/assets/javascripts/single_file_diff.js +++ b/app/assets/javascripts/single_file_diff.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, no-var, space-before-blocks, prefer-rest-params, wrap-iife, one-var, one-var-declaration-per-line, consistent-return, no-param-reassign, padded-blocks, no-undef, max-len */ (function() { var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }; diff --git a/app/assets/javascripts/snippet/snippet_bundle.js b/app/assets/javascripts/snippet/snippet_bundle.js index 083dc23c796..2c8ecba7de4 100644 --- a/app/assets/javascripts/snippet/snippet_bundle.js +++ b/app/assets/javascripts/snippet/snippet_bundle.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, prefer-arrow-callback, no-var, no-undef, quotes, semi, padded-blocks, max-len */ /*= require_tree . */ (function() { diff --git a/app/assets/javascripts/star.js b/app/assets/javascripts/star.js index cfd1e2204d5..32803fa790b 100644 --- a/app/assets/javascripts/star.js +++ b/app/assets/javascripts/star.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-unused-vars, one-var, no-var, one-var-declaration-per-line, prefer-arrow-callback, no-new, no-undef, padded-blocks, max-len */ (function() { this.Star = (function() { function Star() { diff --git a/app/assets/javascripts/subscription.js b/app/assets/javascripts/subscription.js index f9915593657..6d75688deeb 100644 --- a/app/assets/javascripts/subscription.js +++ b/app/assets/javascripts/subscription.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, no-var, space-before-blocks, prefer-rest-params, wrap-iife, vars-on-top, no-unused-vars, one-var, one-var-declaration-per-line, camelcase, consistent-return, no-undef, padded-blocks, max-len */ (function() { var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }; diff --git a/app/assets/javascripts/subscription_select.js b/app/assets/javascripts/subscription_select.js index 2ca65cb762d..185d20775d0 100644 --- a/app/assets/javascripts/subscription_select.js +++ b/app/assets/javascripts/subscription_select.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-var, quotes, object-shorthand, no-unused-vars, no-shadow, one-var, one-var-declaration-per-line, comma-dangle, padded-blocks, max-len */ (function() { this.SubscriptionSelect = (function() { function SubscriptionSelect() { diff --git a/app/assets/javascripts/syntax_highlight.js b/app/assets/javascripts/syntax_highlight.js index 77ad4f30b7a..bd37d69165f 100644 --- a/app/assets/javascripts/syntax_highlight.js +++ b/app/assets/javascripts/syntax_highlight.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, consistent-return, no-var, no-undef, no-else-return, prefer-arrow-callback, padded-blocks, max-len */ // Syntax Highlighter // // Applies a syntax highlighting color scheme CSS class to any element with the diff --git a/app/assets/javascripts/tree.js b/app/assets/javascripts/tree.js index 70aff4b9a2f..54c473d936d 100644 --- a/app/assets/javascripts/tree.js +++ b/app/assets/javascripts/tree.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, wrap-iife, max-len, quotes, consistent-return, no-var, one-var, one-var-declaration-per-line, no-else-return, prefer-arrow-callback, padded-blocks, max-len */ (function() { this.TreeView = (function() { function TreeView() { diff --git a/app/assets/javascripts/u2f/authenticate.js b/app/assets/javascripts/u2f/authenticate.js index 35f2b1e2b25..5d991542b51 100644 --- a/app/assets/javascripts/u2f/authenticate.js +++ b/app/assets/javascripts/u2f/authenticate.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, no-var, space-before-blocks, prefer-rest-params, wrap-iife, prefer-arrow-callback, no-undef, no-else-return, quotes, quote-props, comma-dangle, one-var, one-var-declaration-per-line, padded-blocks, max-len */ // Authenticate U2F (universal 2nd factor) devices for users to authenticate with. // // State Flow #1: setup -> in_progress -> authenticated -> POST to server diff --git a/app/assets/javascripts/u2f/error.js b/app/assets/javascripts/u2f/error.js index aff605169e4..4c70a6e9bb6 100644 --- a/app/assets/javascripts/u2f/error.js +++ b/app/assets/javascripts/u2f/error.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, no-var, space-before-blocks, prefer-rest-params, wrap-iife, no-console, quotes, prefer-template, no-undef, padded-blocks, max-len */ (function() { var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }; diff --git a/app/assets/javascripts/u2f/register.js b/app/assets/javascripts/u2f/register.js index 22fbf9f3a91..97d8993cac2 100644 --- a/app/assets/javascripts/u2f/register.js +++ b/app/assets/javascripts/u2f/register.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, no-var, space-before-blocks, prefer-rest-params, wrap-iife, no-undef, no-else-return, quotes, quote-props, comma-dangle, one-var, one-var-declaration-per-line, padded-blocks, max-len */ // Register U2F (universal 2nd factor) devices for users to authenticate with. // // State Flow #1: setup -> in_progress -> registered -> POST to server diff --git a/app/assets/javascripts/u2f/util.js b/app/assets/javascripts/u2f/util.js index 2eab2d5ae23..eedd3bcd5a1 100644 --- a/app/assets/javascripts/u2f/util.js +++ b/app/assets/javascripts/u2f/util.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, wrap-iife, padded-blocks */ (function() { this.U2FUtil = (function() { function U2FUtil() {} diff --git a/app/assets/javascripts/users/calendar.js b/app/assets/javascripts/users/calendar.js index 0ec878e7e60..6d739039a5b 100644 --- a/app/assets/javascripts/users/calendar.js +++ b/app/assets/javascripts/users/calendar.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, no-var, space-before-blocks, prefer-rest-params, wrap-iife, camelcase, vars-on-top, semi, keyword-spacing, no-plusplus, no-undef, object-shorthand, comma-dangle, eqeqeq, no-mixed-operators, no-return-assign, newline-per-chained-call, prefer-arrow-callback, consistent-return, one-var, one-var-declaration-per-line, prefer-template, quotes, no-unused-vars, no-else-return, padded-blocks, max-len */ (function() { var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }; diff --git a/app/assets/javascripts/users/users_bundle.js b/app/assets/javascripts/users/users_bundle.js index 22bee0f6187..f50802bdf2e 100644 --- a/app/assets/javascripts/users/users_bundle.js +++ b/app/assets/javascripts/users/users_bundle.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren */ /*= require_tree . */ diff --git a/app/assets/javascripts/users_select.js b/app/assets/javascripts/users_select.js index 7a2221dbaf5..c6e18fad832 100644 --- a/app/assets/javascripts/users_select.js +++ b/app/assets/javascripts/users_select.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, one-var, no-var, space-before-blocks, prefer-rest-params, wrap-iife, quotes, max-len, one-var-declaration-per-line, vars-on-top, prefer-arrow-callback, consistent-return, no-undef, comma-dangle, object-shorthand, no-shadow, no-unused-vars, no-plusplus, no-else-return, no-self-compare, prefer-template, no-unused-expressions, no-lonely-if, yoda, prefer-spread, no-void, camelcase, keyword-spacing, no-param-reassign, padded-blocks, max-len */ (function() { var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }, slice = [].slice; diff --git a/app/assets/javascripts/wikis.js b/app/assets/javascripts/wikis.js index ad9b842db3c..5dd853389c2 100644 --- a/app/assets/javascripts/wikis.js +++ b/app/assets/javascripts/wikis.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, no-var, space-before-blocks, prefer-rest-params, wrap-iife, consistent-return, one-var, one-var-declaration-per-line, no-undef, prefer-template, padded-blocks, max-len */ /*= require latinise */ diff --git a/app/assets/javascripts/zen_mode.js b/app/assets/javascripts/zen_mode.js index fa124e7052d..82eb761442a 100644 --- a/app/assets/javascripts/zen_mode.js +++ b/app/assets/javascripts/zen_mode.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable func-names, space-before-function-paren, wrap-iife, prefer-arrow-callback, no-unused-vars, consistent-return, no-undef, camelcase, comma-dangle, padded-blocks, max-len */ // Zen Mode (full screen) textarea // /*= provides zen_mode:enter */ diff --git a/spec/javascripts/application_spec.js b/spec/javascripts/application_spec.js index 16e908f3a81..7e38abc608e 100644 --- a/spec/javascripts/application_spec.js +++ b/spec/javascripts/application_spec.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable space-before-function-paren, one-var, no-var, one-var-declaration-per-line, no-return-assign, padded-blocks, max-len */ /*= require lib/utils/common_utils */ diff --git a/spec/javascripts/awards_handler_spec.js b/spec/javascripts/awards_handler_spec.js index 3d705e1cb2e..ac1404f6e1c 100644 --- a/spec/javascripts/awards_handler_spec.js +++ b/spec/javascripts/awards_handler_spec.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable space-before-function-paren, no-var, one-var, one-var-declaration-per-line, no-unused-expressions, comma-dangle, no-undef, new-parens, no-unused-vars, quotes, jasmine/no-spec-dupes, prefer-template, padded-blocks, max-len */ /*= require awards_handler */ /*= require jquery */ diff --git a/spec/javascripts/behaviors/autosize_spec.js b/spec/javascripts/behaviors/autosize_spec.js index 36254a7370e..b4573e53a4e 100644 --- a/spec/javascripts/behaviors/autosize_spec.js +++ b/spec/javascripts/behaviors/autosize_spec.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable space-before-function-paren, no-var, comma-dangle, no-return-assign, padded-blocks, max-len */ /*= require behaviors/autosize */ diff --git a/spec/javascripts/behaviors/quick_submit_spec.js b/spec/javascripts/behaviors/quick_submit_spec.js index 7370ccb4a08..efb1203eb2f 100644 --- a/spec/javascripts/behaviors/quick_submit_spec.js +++ b/spec/javascripts/behaviors/quick_submit_spec.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable space-before-function-paren, no-var, no-return-assign, comma-dangle, no-undef, jasmine/no-spec-dupes, new-cap, padded-blocks, max-len */ /*= require behaviors/quick_submit */ diff --git a/spec/javascripts/behaviors/requires_input_spec.js b/spec/javascripts/behaviors/requires_input_spec.js index 32469a4fd1f..c3f4c867d6a 100644 --- a/spec/javascripts/behaviors/requires_input_spec.js +++ b/spec/javascripts/behaviors/requires_input_spec.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable space-before-function-paren, no-var, padded-blocks */ /*= require behaviors/requires_input */ diff --git a/spec/javascripts/extensions/array_spec.js b/spec/javascripts/extensions/array_spec.js index f28983d7764..c56e6c7789b 100644 --- a/spec/javascripts/extensions/array_spec.js +++ b/spec/javascripts/extensions/array_spec.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable space-before-function-paren, no-var, padded-blocks */ /*= require extensions/array */ diff --git a/spec/javascripts/extensions/jquery_spec.js b/spec/javascripts/extensions/jquery_spec.js index 9c361bb0867..76309930f27 100644 --- a/spec/javascripts/extensions/jquery_spec.js +++ b/spec/javascripts/extensions/jquery_spec.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable space-before-function-paren, no-var, padded-blocks */ /*= require extensions/jquery */ diff --git a/spec/javascripts/fixtures/emoji_menu.js b/spec/javascripts/fixtures/emoji_menu.js index 41cf40c29cf..3d776bb9277 100644 --- a/spec/javascripts/fixtures/emoji_menu.js +++ b/spec/javascripts/fixtures/emoji_menu.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable space-before-function-paren, padded-blocks */ (function() { window.emojiMenu = "
\n \n
\n
\n Emoticons\n
\n
    \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
\n
\n
"; diff --git a/spec/javascripts/graphs/stat_graph_contributors_graph_spec.js b/spec/javascripts/graphs/stat_graph_contributors_graph_spec.js index 8c66c45ba79..a406e6cc36a 100644 --- a/spec/javascripts/graphs/stat_graph_contributors_graph_spec.js +++ b/spec/javascripts/graphs/stat_graph_contributors_graph_spec.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable quotes, no-undef, indent, semi, object-curly-spacing, jasmine/no-suite-dupes, vars-on-top, no-var, padded-blocks, spaced-comment, max-len */ //= require graphs/stat_graph_contributors_graph describe("ContributorsGraph", function () { diff --git a/spec/javascripts/graphs/stat_graph_contributors_util_spec.js b/spec/javascripts/graphs/stat_graph_contributors_util_spec.js index 920e4ee0892..96f39abe13e 100644 --- a/spec/javascripts/graphs/stat_graph_contributors_util_spec.js +++ b/spec/javascripts/graphs/stat_graph_contributors_util_spec.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable quotes, padded-blocks, no-var, camelcase, object-curly-spacing, semi, indent, object-property-newline, comma-dangle, comma-spacing, no-undef, spaced-comment, max-len, key-spacing, vars-on-top, quote-props, no-multi-spaces, max-len */ //= require graphs/stat_graph_contributors_util describe("ContributorsStatGraphUtil", function () { diff --git a/spec/javascripts/graphs/stat_graph_spec.js b/spec/javascripts/graphs/stat_graph_spec.js index ae2821ecad9..f78573b992b 100644 --- a/spec/javascripts/graphs/stat_graph_spec.js +++ b/spec/javascripts/graphs/stat_graph_spec.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable quotes, padded-blocks, no-undef, semi */ //= require graphs/stat_graph describe("StatGraph", function () { diff --git a/spec/javascripts/header_spec.js b/spec/javascripts/header_spec.js index 9a859655d8b..d2bcbc37b64 100644 --- a/spec/javascripts/header_spec.js +++ b/spec/javascripts/header_spec.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable space-before-function-paren, padded-blocks, no-var */ /*= require header */ /*= require lib/utils/text_utility */ /*= require jquery */ diff --git a/spec/javascripts/issue_spec.js b/spec/javascripts/issue_spec.js index 949114185cf..beef46122ab 100644 --- a/spec/javascripts/issue_spec.js +++ b/spec/javascripts/issue_spec.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable space-before-function-paren, no-var, one-var, one-var-declaration-per-line, no-use-before-define, indent, no-undef, no-trailing-spaces, comma-dangle, padded-blocks, max-len */ /*= require lib/utils/text_utility */ /*= require issue */ diff --git a/spec/javascripts/line_highlighter_spec.js b/spec/javascripts/line_highlighter_spec.js index e0192a2d624..b8b174a2e53 100644 --- a/spec/javascripts/line_highlighter_spec.js +++ b/spec/javascripts/line_highlighter_spec.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable space-before-function-paren, no-var, no-param-reassign, quotes, prefer-template, no-else-return, new-cap, dot-notation, no-undef, no-return-assign, comma-dangle, no-new, one-var, one-var-declaration-per-line, no-plusplus, jasmine/no-spec-dupes, no-underscore-dangle, padded-blocks, max-len */ /*= require line_highlighter */ diff --git a/spec/javascripts/merge_request_spec.js b/spec/javascripts/merge_request_spec.js index 83d279ab414..cbe2634d3a4 100644 --- a/spec/javascripts/merge_request_spec.js +++ b/spec/javascripts/merge_request_spec.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable space-before-function-paren, no-return-assign, no-undef, padded-blocks */ /*= require merge_request */ diff --git a/spec/javascripts/merge_request_tabs_spec.js b/spec/javascripts/merge_request_tabs_spec.js index 6a53c6aa6ac..971222c44e1 100644 --- a/spec/javascripts/merge_request_tabs_spec.js +++ b/spec/javascripts/merge_request_tabs_spec.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable space-before-function-paren, no-var, comma-dangle, dot-notation, quotes, no-undef, no-return-assign, no-underscore-dangle, camelcase, padded-blocks, max-len */ /*= require merge_request_tabs */ //= require breakpoints diff --git a/spec/javascripts/merge_request_widget_spec.js b/spec/javascripts/merge_request_widget_spec.js index 91f19aca719..8c5afc2ff3c 100644 --- a/spec/javascripts/merge_request_widget_spec.js +++ b/spec/javascripts/merge_request_widget_spec.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable space-before-function-paren, quotes, comma-dangle, dot-notation, indent, quote-props, no-var, padded-blocks, max-len */ /*= require merge_request_widget */ /*= require lib/utils/timeago.js */ diff --git a/spec/javascripts/new_branch_spec.js b/spec/javascripts/new_branch_spec.js index c092424ec32..8828970d984 100644 --- a/spec/javascripts/new_branch_spec.js +++ b/spec/javascripts/new_branch_spec.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable space-before-function-paren, one-var, no-var, one-var-declaration-per-line, no-return-assign, no-undef, quotes, padded-blocks, max-len */ /*= require jquery-ui/autocomplete */ /*= require new_branch_form */ diff --git a/spec/javascripts/notes_spec.js b/spec/javascripts/notes_spec.js index 2e3a4b66e2d..51f2ae8bcbd 100644 --- a/spec/javascripts/notes_spec.js +++ b/spec/javascripts/notes_spec.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable space-before-function-paren, no-unused-expressions, no-undef, no-var, object-shorthand, comma-dangle, semi, padded-blocks, max-len */ /*= require notes */ /*= require autosize */ /*= require gl_form */ diff --git a/spec/javascripts/project_title_spec.js b/spec/javascripts/project_title_spec.js index 1963857bba3..49211a6b852 100644 --- a/spec/javascripts/project_title_spec.js +++ b/spec/javascripts/project_title_spec.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable space-before-function-paren, no-unused-expressions, no-return-assign, no-undef, no-param-reassign, no-var, new-cap, wrap-iife, no-unused-vars, quotes, padded-blocks, max-len */ /*= require bootstrap */ /*= require select2 */ diff --git a/spec/javascripts/right_sidebar_spec.js b/spec/javascripts/right_sidebar_spec.js index ef03d1147de..83ebbd63f3a 100644 --- a/spec/javascripts/right_sidebar_spec.js +++ b/spec/javascripts/right_sidebar_spec.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable space-before-function-paren, no-var, one-var, one-var-declaration-per-line, new-parens, no-undef, no-return-assign, new-cap, vars-on-top, semi, padded-blocks, max-len */ /*= require right_sidebar */ /*= require jquery */ diff --git a/spec/javascripts/search_autocomplete_spec.js b/spec/javascripts/search_autocomplete_spec.js index 29080804960..1b7f642d59e 100644 --- a/spec/javascripts/search_autocomplete_spec.js +++ b/spec/javascripts/search_autocomplete_spec.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable space-before-function-paren, max-len, no-var, one-var, one-var-declaration-per-line, no-unused-expressions, consistent-return, no-param-reassign, default-case, no-return-assign, comma-dangle, object-shorthand, prefer-template, quotes, new-parens, vars-on-top, new-cap, padded-blocks, max-len */ /*= require gl_dropdown */ /*= require search_autocomplete */ diff --git a/spec/javascripts/shortcuts_issuable_spec.js b/spec/javascripts/shortcuts_issuable_spec.js index 1f36a048153..7d36d79b687 100644 --- a/spec/javascripts/shortcuts_issuable_spec.js +++ b/spec/javascripts/shortcuts_issuable_spec.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable space-before-function-paren, no-return-assign, no-undef, no-var, quotes, padded-blocks, max-len */ /*= require shortcuts_issuable */ diff --git a/spec/javascripts/spec_helper.js b/spec/javascripts/spec_helper.js index 9cb8243ee2c..8a64de4dd43 100644 --- a/spec/javascripts/spec_helper.js +++ b/spec/javascripts/spec_helper.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable space-before-function-paren */ // PhantomJS (Teaspoons default driver) doesn't have support for // Function.prototype.bind, which has caused confusion. Use this polyfill to // avoid the confusion. diff --git a/spec/javascripts/syntax_highlight_spec.js b/spec/javascripts/syntax_highlight_spec.js index 498f0f06797..ac411f6c306 100644 --- a/spec/javascripts/syntax_highlight_spec.js +++ b/spec/javascripts/syntax_highlight_spec.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable space-before-function-paren, no-var, no-return-assign, quotes, padded-blocks */ /*= require syntax_highlight */ diff --git a/spec/javascripts/u2f/authenticate_spec.js b/spec/javascripts/u2f/authenticate_spec.js index 024a91f0a80..944df6d23f7 100644 --- a/spec/javascripts/u2f/authenticate_spec.js +++ b/spec/javascripts/u2f/authenticate_spec.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable space-before-function-paren, new-parens, no-undef, quotes, comma-dangle, no-var, one-var, one-var-declaration-per-line, padded-blocks, max-len */ /*= require u2f/authenticate */ /*= require u2f/util */ diff --git a/spec/javascripts/u2f/mock_u2f_device.js b/spec/javascripts/u2f/mock_u2f_device.js index ad133682fb1..1459f968c3d 100644 --- a/spec/javascripts/u2f/mock_u2f_device.js +++ b/spec/javascripts/u2f/mock_u2f_device.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable space-before-function-paren, no-var, space-before-blocks, prefer-rest-params, wrap-iife, no-unused-expressions, no-return-assign, no-param-reassign, padded-blocks, max-len */ (function() { var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }; diff --git a/spec/javascripts/u2f/register_spec.js b/spec/javascripts/u2f/register_spec.js index abea76f622f..0c73c5772bd 100644 --- a/spec/javascripts/u2f/register_spec.js +++ b/spec/javascripts/u2f/register_spec.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable space-before-function-paren, new-parens, no-undef, quotes, no-var, one-var, one-var-declaration-per-line, comma-dangle, padded-blocks, max-len */ /*= require u2f/register */ /*= require u2f/util */ diff --git a/spec/javascripts/zen_mode_spec.js b/spec/javascripts/zen_mode_spec.js index 65b6e3dce33..a18e8aee9b1 100644 --- a/spec/javascripts/zen_mode_spec.js +++ b/spec/javascripts/zen_mode_spec.js @@ -1,4 +1,4 @@ -/* eslint-disable */ +/* eslint-disable space-before-function-paren, no-var, one-var, one-var-declaration-per-line, no-undef, object-shorthand, comma-dangle, no-return-assign, new-cap, padded-blocks, max-len */ /*= require zen_mode */ -- cgit v1.2.1 From 16674d9b8226d9ce353789ba24a57c176721ee62 Mon Sep 17 00:00:00 2001 From: Nick Thomas Date: Tue, 15 Nov 2016 17:07:58 +0000 Subject: Fix a badly-performing migration --- ..._fix_project_records_with_invalid_visibility.rb | 46 +++++++++++----------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/db/post_migrate/20161109150329_fix_project_records_with_invalid_visibility.rb b/db/post_migrate/20161109150329_fix_project_records_with_invalid_visibility.rb index bea1cfa4c5d..df38591a333 100644 --- a/db/post_migrate/20161109150329_fix_project_records_with_invalid_visibility.rb +++ b/db/post_migrate/20161109150329_fix_project_records_with_invalid_visibility.rb @@ -1,7 +1,7 @@ class FixProjectRecordsWithInvalidVisibility < ActiveRecord::Migration include Gitlab::Database::MigrationHelpers - BATCH_SIZE = 1000 + BATCH_SIZE = 500 DOWNTIME = false # This migration is idempotent and there's no sense in throwing away the @@ -12,34 +12,34 @@ class FixProjectRecordsWithInvalidVisibility < ActiveRecord::Migration projects = Arel::Table.new(:projects) namespaces = Arel::Table.new(:namespaces) - finder = + finder_sql = projects. join(namespaces, Arel::Nodes::InnerJoin). on(projects[:namespace_id].eq(namespaces[:id])). where(projects[:visibility_level].gt(namespaces[:visibility_level])). - project(projects[:id]). - take(BATCH_SIZE) + project(projects[:id], namespaces[:visibility_level]). + take(BATCH_SIZE). + to_sql - # MySQL requires a derived table to perform this query - nested_finder = - projects. - from(finder.as("AS projects_inner")). - project(projects[:id]) - - valuer = - namespaces. - where(namespaces[:id].eq(projects[:namespace_id])). - project(namespaces[:visibility_level]) - - # Update matching rows until none remain. The finder contains a limit. + # Update matching rows in batches. Each batch can cause up to 3 UPDATE + # statements, in addition to the SELECT: one per visibility_level loop do - updater = Arel::UpdateManager.new(ActiveRecord::Base). - table(projects). - set(projects[:visibility_level] => Arel::Nodes::SqlLiteral.new("(#{valuer.to_sql})")). - where(projects[:id].in(nested_finder)) - - num_updated = connection.exec_update(updater.to_sql, self.class.name, []) - break if num_updated == 0 + to_update = connection.exec_query(finder_sql) + break if to_update.rows.count == 0 + + # row[0] is projects.id, row[1] is namespaces.visibility_level + updates = to_update.rows.each_with_object(Hash.new {|h, k| h[k] = [] }) do |row, obj| + obj[row[1]] << row[0] + end + + updates.each do |visibility_level, project_ids| + updater = Arel::UpdateManager.new(ActiveRecord::Base). + table(projects). + set(projects[:visibility_level] => visibility_level). + where(projects[:id].in(project_ids)) + + ActiveRecord::Base.connection.exec_update(updater.to_sql, self.class.name, []) + end end end -- cgit v1.2.1 From 5b876592b47209e431d37b281aacb187df870ea3 Mon Sep 17 00:00:00 2001 From: Yar Date: Sun, 13 Nov 2016 18:18:04 +0300 Subject: Fix double event and xhr request call on MR page !7298 On page merge_requests/n/diffs and merge_requests/n/commits 'shown.bs.tab' event triggers twice when tab is loading which lead to extra ajax request for data each time. This commit prevent this event from triggering twice when tab is loading. --- app/assets/javascripts/merge_request_tabs.js | 3 ++- changelogs/unreleased/24010-double-event-trigger.yml | 4 ++++ 2 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 changelogs/unreleased/24010-double-event-trigger.yml diff --git a/app/assets/javascripts/merge_request_tabs.js b/app/assets/javascripts/merge_request_tabs.js index 860ee5df57e..ccc28766911 100644 --- a/app/assets/javascripts/merge_request_tabs.js +++ b/app/assets/javascripts/merge_request_tabs.js @@ -145,7 +145,8 @@ if (action === 'show') { action = 'notes'; } - $(".merge-request-tabs a[data-action='" + action + "']").tab('show').trigger('shown.bs.tab'); + // important note: the .tab('show') method triggers 'shown.bs.tab' event itself + $(".merge-request-tabs a[data-action='" + action + "']").tab('show'); }; // Replaces the current Merge Request-specific action in the URL with a new one diff --git a/changelogs/unreleased/24010-double-event-trigger.yml b/changelogs/unreleased/24010-double-event-trigger.yml new file mode 100644 index 00000000000..3c2f20d391f --- /dev/null +++ b/changelogs/unreleased/24010-double-event-trigger.yml @@ -0,0 +1,4 @@ +--- +title: Fix double event and ajax request call on MR page +merge_request: 7298 +author: YarNayar -- cgit v1.2.1 From 8782bb96cca3ade2b367f588fd8332906dcc2d1b Mon Sep 17 00:00:00 2001 From: Yar Date: Sun, 13 Nov 2016 21:18:03 +0300 Subject: Unify anchor link format for MR diff files !7298 Right now, the following naming scheme for diff files is used: diff-1, diff-2, ... and also we have "internal" format which is file-path-HASH, where HASH is sha1 of file path. Besides, we have HASH_lineA_lineB format to link exact line number in MR diff. It makes sence to unify the way we link diff from outside, while leave "file-path-HASH" format for internal (js) usage. Changes in this commit allow to link diff just by HASH, if we don't want specify exact lines, also it changes "file-path-HASH" and "diff-NUMBER" links in code to this unified format. Inspired by #24010 and !7298 --- app/controllers/projects/blob_controller.rb | 2 +- app/views/notify/repository_push_email.html.haml | 11 ++++++----- app/views/projects/diffs/_diffs.html.haml | 5 +++-- app/views/projects/diffs/_file.html.haml | 4 ++-- app/views/projects/diffs/_stats.html.haml | 11 ++++++----- .../unreleased/24010-change-anchor-link-to-mr-diff.yml | 4 ++++ features/steps/project/merge_requests.rb | 18 +++++++++--------- 7 files changed, 31 insertions(+), 24 deletions(-) create mode 100644 changelogs/unreleased/24010-change-anchor-link-to-mr-diff.yml diff --git a/app/controllers/projects/blob_controller.rb b/app/controllers/projects/blob_controller.rb index b78cc6585ba..56ced786311 100644 --- a/app/controllers/projects/blob_controller.rb +++ b/app/controllers/projects/blob_controller.rb @@ -42,7 +42,7 @@ class Projects::BlobController < Projects::ApplicationController after_edit_path = if from_merge_request && @target_branch == @ref diffs_namespace_project_merge_request_path(from_merge_request.target_project.namespace, from_merge_request.target_project, from_merge_request) + - "#file-path-#{hexdigest(@path)}" + "##{hexdigest(@path)}" else namespace_project_blob_path(@project.namespace, @project, File.join(@target_branch, @path)) end diff --git a/app/views/notify/repository_push_email.html.haml b/app/views/notify/repository_push_email.html.haml index c0c07d65daa..307c5a11206 100644 --- a/app/views/notify/repository_push_email.html.haml +++ b/app/views/notify/repository_push_email.html.haml @@ -27,9 +27,9 @@ %h4 #{pluralize @message.diffs_count, "changed file"}: %ul - - @message.diffs.each_with_index do |diff, i| + - @message.diffs.each do |diff| %li.file-stats - %a{href: "#{@message.target_url if @message.disable_diffs?}#diff-#{i}" } + %a{href: "#{@message.target_url if @message.disable_diffs?}##{hexdigest(diff.file_path)}" } - if diff.deleted_file %span.deleted-file − @@ -52,9 +52,10 @@ %h5 The diff was not included because it is too large. - else %h4 Changes: - - diff_files.each_with_index do |diff_file, i| - %li{id: "diff-#{i}"} - %a{href: @message.target_url + "#diff-#{i}"}< + - diff_files.each do |diff_file| + - file_hash = hexdigest(diff_file.file_path) + %li{id: file_hash} + %a{href: @message.target_url + "##{file_hash}"}< - if diff_file.deleted_file %strong< = diff_file.old_path diff --git a/app/views/projects/diffs/_diffs.html.haml b/app/views/projects/diffs/_diffs.html.haml index 067cf595da3..ab4a2dc36e5 100644 --- a/app/views/projects/diffs/_diffs.html.haml +++ b/app/views/projects/diffs/_diffs.html.haml @@ -22,11 +22,12 @@ = render 'projects/diffs/warning', diff_files: diff_files .files{ data: { can_create_note: can_create_note } } - - diff_files.each_with_index do |diff_file, index| + - diff_files.each_with_index do |diff_file| - diff_commit = commit_for_diff(diff_file) - blob = diff_file.blob(diff_commit) - next unless blob - blob.load_all_data!(diffs.project.repository) unless blob.only_display_raw? + - file_hash = hexdigest(diff_file.file_path) - = render 'projects/diffs/file', index: index, project: diffs.project, + = render 'projects/diffs/file', file_hash: file_hash, project: diffs.project, diff_file: diff_file, diff_commit: diff_commit, blob: blob diff --git a/app/views/projects/diffs/_file.html.haml b/app/views/projects/diffs/_file.html.haml index 8f4f9ad4a80..120ba9ffcd2 100644 --- a/app/views/projects/diffs/_file.html.haml +++ b/app/views/projects/diffs/_file.html.haml @@ -1,6 +1,6 @@ -.diff-file.file-holder{id: "diff-#{index}", data: diff_file_html_data(project, diff_file.file_path, diff_commit.id)} +.diff-file.file-holder{id: file_hash, data: diff_file_html_data(project, diff_file.file_path, diff_commit.id)} .file-title{id: "file-path-#{hexdigest(diff_file.file_path)}"} - = render "projects/diffs/file_header", diff_file: diff_file, blob: blob, diff_commit: diff_commit, project: project, url: "#diff-#{index}" + = render "projects/diffs/file_header", diff_file: diff_file, blob: blob, diff_commit: diff_commit, project: project, url: "##{file_hash}" - unless diff_file.submodule? .file-actions.hidden-xs diff --git a/app/views/projects/diffs/_stats.html.haml b/app/views/projects/diffs/_stats.html.haml index e751dabdf99..66d6254aa1e 100644 --- a/app/views/projects/diffs/_stats.html.haml +++ b/app/views/projects/diffs/_stats.html.haml @@ -9,28 +9,29 @@ %strong.cred #{diff_files.sum(&:removed_lines)} deletions .file-stats.js-toggle-content.hide %ul - - diff_files.each_with_index do |diff_file, i| + - diff_files.each do |diff_file| + - file_hash = hexdigest(diff_file.file_path) %li - if diff_file.deleted_file %span.deleted-file - %a{href: "#diff-#{i}"} + %a{href: "##{file_hash}"} %i.fa.fa-minus = diff_file.old_path - elsif diff_file.renamed_file %span.renamed-file - %a{href: "#diff-#{i}"} + %a{href: "##{file_hash}"} %i.fa.fa-minus = diff_file.old_path → = diff_file.new_path - elsif diff_file.new_file %span.new-file - %a{href: "#diff-#{i}"} + %a{href: "##{file_hash}"} %i.fa.fa-plus = diff_file.new_path - else %span.edit-file - %a{href: "#diff-#{i}"} + %a{href: "##{file_hash}"} %i.fa.fa-adjust = diff_file.new_path diff --git a/changelogs/unreleased/24010-change-anchor-link-to-mr-diff.yml b/changelogs/unreleased/24010-change-anchor-link-to-mr-diff.yml new file mode 100644 index 00000000000..33ce18b2141 --- /dev/null +++ b/changelogs/unreleased/24010-change-anchor-link-to-mr-diff.yml @@ -0,0 +1,4 @@ +--- +title: Unify anchor link format for MR diff files +merge_request: 7298 +author: YarNayar diff --git a/features/steps/project/merge_requests.rb b/features/steps/project/merge_requests.rb index 2ccab4334eb..f728d243cdc 100644 --- a/features/steps/project/merge_requests.rb +++ b/features/steps/project/merge_requests.rb @@ -413,37 +413,37 @@ class Spinach::Features::ProjectMergeRequests < Spinach::FeatureSteps end step 'I click link "Hide inline discussion" of the third file' do - page.within '.files [id^=diff]:nth-child(3)' do + page.within '.files>div:nth-child(3)' do find('.js-toggle-diff-comments').trigger('click') end end step 'I click link "Show inline discussion" of the third file' do - page.within '.files [id^=diff]:nth-child(3)' do + page.within '.files>div:nth-child(3)' do find('.js-toggle-diff-comments').trigger('click') end end step 'I should not see a comment like "Line is wrong" in the third file' do - page.within '.files [id^=diff]:nth-child(3)' do + page.within '.files>div:nth-child(3)' do expect(page).not_to have_visible_content "Line is wrong" end end step 'I should see a comment like "Line is wrong" in the third file' do - page.within '.files [id^=diff]:nth-child(3) .note-body > .note-text' do + page.within '.files>div:nth-child(3) .note-body > .note-text' do expect(page).to have_visible_content "Line is wrong" end end step 'I should not see a comment like "Line is wrong here" in the third file' do - page.within '.files [id^=diff]:nth-child(3)' do + page.within '.files>div:nth-child(3)' do expect(page).not_to have_visible_content "Line is wrong here" end end step 'I should see a comment like "Line is wrong here" in the third file' do - page.within '.files [id^=diff]:nth-child(3) .note-body > .note-text' do + page.within '.files>div:nth-child(3) .note-body > .note-text' do expect(page).to have_visible_content "Line is wrong here" end end @@ -456,7 +456,7 @@ class Spinach::Features::ProjectMergeRequests < Spinach::FeatureSteps click_button "Comment" end - page.within ".files [id^=diff]:nth-child(2) .note-body > .note-text" do + page.within ".files>div:nth-child(2) .note-body > .note-text" do expect(page).to have_content "Line is correct" end end @@ -471,7 +471,7 @@ class Spinach::Features::ProjectMergeRequests < Spinach::FeatureSteps end step 'I should still see a comment like "Line is correct" in the second file' do - page.within '.files [id^=diff]:nth-child(2) .note-body > .note-text' do + page.within '.files>div:nth-child(2) .note-body > .note-text' do expect(page).to have_visible_content "Line is correct" end end @@ -494,7 +494,7 @@ class Spinach::Features::ProjectMergeRequests < Spinach::FeatureSteps end step 'I should see comments on the side-by-side diff page' do - page.within '.files [id^=diff]:nth-child(2) .parallel .note-body > .note-text' do + page.within '.files>div:nth-child(2) .parallel .note-body > .note-text' do expect(page).to have_visible_content "Line is correct" end end -- cgit v1.2.1 From 34a86120ed52b5e77ef2cd51a60bed1208671639 Mon Sep 17 00:00:00 2001 From: Semyon Pupkov Date: Wed, 16 Nov 2016 00:16:45 +0500 Subject: Use setter for key instead AR callback ref: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/6763 --- app/models/key.rb | 7 ++++--- changelogs/unreleased/setter-for-key.yml | 4 ++++ spec/models/key_spec.rb | 10 ++++++++++ 3 files changed, 18 insertions(+), 3 deletions(-) create mode 100644 changelogs/unreleased/setter-for-key.yml diff --git a/app/models/key.rb b/app/models/key.rb index 568a60b8af3..ff8dda2dc89 100644 --- a/app/models/key.rb +++ b/app/models/key.rb @@ -6,7 +6,7 @@ class Key < ActiveRecord::Base belongs_to :user - before_validation :strip_white_space, :generate_fingerprint + before_validation :generate_fingerprint validates :title, presence: true, length: { within: 0..255 } validates :key, presence: true, length: { within: 0..5000 }, format: { with: /\A(ssh|ecdsa)-.*\Z/ } @@ -21,8 +21,9 @@ class Key < ActiveRecord::Base after_destroy :remove_from_shell after_destroy :post_destroy_hook - def strip_white_space - self.key = key.strip unless key.blank? + def key=(value) + value.strip! unless value.blank? + write_attribute(:key, value) end def publishable_key diff --git a/changelogs/unreleased/setter-for-key.yml b/changelogs/unreleased/setter-for-key.yml new file mode 100644 index 00000000000..15167904ed5 --- /dev/null +++ b/changelogs/unreleased/setter-for-key.yml @@ -0,0 +1,4 @@ +--- +title: Use setter for key instead AR callback +merge_request: 7488 +author: Semyon Pupkov diff --git a/spec/models/key_spec.rb b/spec/models/key_spec.rb index 7fc6ed1dd54..ba9cdf7dddb 100644 --- a/spec/models/key_spec.rb +++ b/spec/models/key_spec.rb @@ -82,4 +82,14 @@ describe Key, models: true do @key.destroy end end + + describe '#key=' do + let(:valid_key) do + "ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAIEAiPWx6WM4lhHNedGfBpPJNPpZ7yKu+dnn1SJejgt4596k6YjzGGphH2TUxwKzxcKDKKezwkpfnxPkSMkuEspGRt/aZZ9wa++Oi7Qkr8prgHc4soW6NUlfDzpvZK2H5E7eQaSeP3SAwGmQKUFHCddNaP0L+hM7zhFNzjFvpaMgJw0= dummy@gitlab.com" + end + + it 'strips white spaces' do + expect(described_class.new(key: " #{valid_key} ").key).to eq(valid_key) + end + end end -- cgit v1.2.1 From 2a951840451cf86f3ddfa4675cb3a5e360b89867 Mon Sep 17 00:00:00 2001 From: Semyon Pupkov Date: Wed, 16 Nov 2016 00:27:49 +0500 Subject: Remove instance vars from key model tests --- spec/models/key_spec.rb | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/spec/models/key_spec.rb b/spec/models/key_spec.rb index ba9cdf7dddb..1a26cee9f3d 100644 --- a/spec/models/key_spec.rb +++ b/spec/models/key_spec.rb @@ -71,15 +71,15 @@ describe Key, models: true do context 'callbacks' do it 'adds new key to authorized_file' do - @key = build(:personal_key, id: 7) - expect(GitlabShellWorker).to receive(:perform_async).with(:add_key, @key.shell_id, @key.key) - @key.save + key = build(:personal_key, id: 7) + expect(GitlabShellWorker).to receive(:perform_async).with(:add_key, key.shell_id, key.key) + key.save! end it 'removes key from authorized_file' do - @key = create(:personal_key) - expect(GitlabShellWorker).to receive(:perform_async).with(:remove_key, @key.shell_id, @key.key) - @key.destroy + key = create(:personal_key) + expect(GitlabShellWorker).to receive(:perform_async).with(:remove_key, key.shell_id, key.key) + key.destroy end end -- cgit v1.2.1 From 9b70c09d07d180229501799f2d440191853aba57 Mon Sep 17 00:00:00 2001 From: Achilleas Pipinellis Date: Tue, 15 Nov 2016 16:08:53 +0100 Subject: Refactor Redis HA docs [ci skip] --- doc/administration/high_availability/redis.md | 547 +++++++++++++++----------- 1 file changed, 314 insertions(+), 233 deletions(-) diff --git a/doc/administration/high_availability/redis.md b/doc/administration/high_availability/redis.md index bb46de65e3c..6de92ae3741 100644 --- a/doc/administration/high_availability/redis.md +++ b/doc/administration/high_availability/redis.md @@ -17,10 +17,11 @@ Omnibus GitLab packages. before configuring Redis HA with GitLab to fully understand the topology and architecture. - This is the documentation for the Omnibus GitLab packages. For installations - from source, follow the [Redis HA source install](redis_source.md) guide. + from source, follow the [Redis HA source installation](redis_source.md) guide. - Redis Sentinel daemon is bundled with Omnibus GitLab Enterprise Edition only. - For the Omnibus Community Edition and installations from source, follow the - [Redis HA source install](redis_source.md) guide. + For configuring Sentinel with the Omnibus GitLab Community Edition and + installations from source, follow the + [Redis HA source installation](redis_source.md) guide. @@ -31,16 +32,15 @@ Omnibus GitLab packages. - [High Availability with Sentinel](#high-availability-with-sentinel) - [Recommended setup](#recommended-setup) - [Available configuration setups](#available-configuration-setups) - - [Using a non-Omnibus external Redis server](#using-a-non-omnibus-external-redis-server) - [Redis setup overview](#redis-setup-overview) - [Sentinel setup overview](#sentinel-setup-overview) -- [Redis HA configuration](#redis-ha-configuration) - - [Configuring the Master Redis instance](#configuring-the-master-redis-instance) - - [Configuring the Slave Redis instances](#configuring-the-slave-redis-instances) - - [Configuring the Sentinel instances](#configuring-the-sentinel-instances) - - [Configuring the GitLab application](#configuring-the-gitlab-application) +- [Configuring Redis HA](#configuring-redis-ha) + - [Step 1. Configuring the Master Redis instance](#step-1-configuring-the-master-redis-instance) + - [Step 2. Configuring the Slave Redis instances](#step-2-configuring-the-slave-redis-instances) + - [Step 3. Configuring the Redis Sentinel instances](#step-3-configuring-the-redis-sentinel-instances) + - [Step 4. Configuring the GitLab application](#step-4-configuring-the-gitlab-application) - [Switching from an existing single-machine installation to Redis HA](#switching-from-an-existing-single-machine-installation-to-redis-ha) -- [Minimal example configuration with 1 master, 2 slaves and 3 sentinels](#minimal-example-configuration-with-1-master-2-slaves-and-3-sentinels) +- [Example of a minimal configuration with 1 master, 2 slaves and 3 sentinels](#example-of-a-minimal-configuration-with-1-master-2-slaves-and-3-sentinels) - [Configuration for Redis master](#configuration-for-redis-master) - [Configuration for Redis slaves](#configuration-for-redis-slaves) - [Configuration for Sentinels](#configuration-for-sentinels) @@ -50,6 +50,7 @@ Omnibus GitLab packages. - [Troubleshooting Redis replication](#troubleshooting-redis-replication) - [Troubleshooting Sentinel](#troubleshooting-sentinel) - [Changelog](#changelog) + - [Experimental Redis Sentinel support](#experimental-redis-sentinel-support) @@ -59,8 +60,6 @@ Before diving into the details of setting up Redis and Redis Sentinel for HA, make sure you read this Overview section to better understand how the components are tied together. -### Prerequisites - You need at least `3` independent machines: physical, or VMs running into distinct physical machines. It is essential that all master and slaves Redis instances run in different machines. If you fail to provision the machines in @@ -84,16 +83,16 @@ components below. - Starting with GitLab `8.11`, you can configure a list of Redis Sentinel servers that will monitor a group of Redis servers to provide failover support. - Starting with GitLab `8.14`, the Omnibus GitLab Enterprise Edition package - comes with Redis sentinel daemon support. + comes with Redis Sentinel daemon support. High Availability with Redis requires a few things: - Multiple Redis instances -- Run Redis in a **Master** x **Slave** topology -- Multiple Sentinel instances + - Run Redis in a **Master** x **Slave** topology + - Multiple Sentinel instances - Application support and visibility to all Sentinel and Redis instances -Redis Sentinel can handle the most important tasks in a HA environment and that's +Redis Sentinel can handle the most important tasks in an HA environment and that's to help keep servers online with minimal to no downtime. Redis Sentinel: - Monitors **Master** and **Slaves** instances to see if they are available @@ -120,7 +119,8 @@ For a minimal setup, you will install the Omnibus GitLab package in `3` - Redis Slave + Sentinel If you are not sure or don't understand why and where the amount of nodes come -from, read [Redis Setup](#redis-setup) and [Sentinel Setup](#sentinel-setup). +from, read [Redis setup overview](#redis-setup-overview) and +[Sentinel setup overview](#sentinel-setup-overview). For a recommended setup that can resist more failures, you will install the Omnibus GitLab package in `5` **independent** machines, both with @@ -149,24 +149,9 @@ Pick the one that suits your needs. are bundled, so you can use the EE package to setup the whole Redis HA infrastructure (master, slave and Sentinel). -Note that if you have installed GitLab using the Omnibus GitLab packages (both -CE and EE), you can also use an [external Redis server](#using-a-non-omnibus-external-redis-server). - -### Using a non-Omnibus external Redis server - -If you're hosting GitLab on a cloud provider, you can optionally use a -managed service for Redis. For example, AWS offers a managed ElastiCache service -that runs Redis. - -Managed services can provide High Availability using their own proprietary -technology and provide a transparent proxy (which means that GitLab doesn't -need any additional change) or they will use Sentinel and manage it for you. - -If your provider uses Sentinel, see [GitLab Setup](#gitlab-setup) -to understand where you need to provide the list of servers and credentials. - -If you want to setup Redis by yourself, without using Omnibus, you can -read the documentation on [configuring Redis HA for source installs](redis_source.md). +Note that even if you have installed GitLab using the Omnibus GitLab packages +(both CE and EE), you can still use an +[external Redis server](#using-a-non-omnibus-external-redis-server). ### Redis setup overview @@ -197,13 +182,13 @@ each other over the network. ### Sentinel setup overview -Sentinels watch both other sentinels and Redis nodes. Whenever a Sentinel +Sentinels watch both other Sentinels and Redis nodes. Whenever a Sentinel detects that a Redis node is not responding, it will announce that to the -other sentinels. They have to reach the **quorum**, the minimum amount of -sentinels that agrees that a node is down, to be able to start a failover. +other Sentinels. They have to reach the **quorum**, that is the minimum amount +of Sentinels that agrees a node is down, in order to be able to start a failover. -Whenever the **quorum** is met, you need the **majority** of all known -Sentinel nodes to be available and reachable, to elect the Sentinel **leader** +Whenever the **quorum** is met, the **majority** of all known Sentinel nodes +need to be available and reachable, so that they can elect the Sentinel **leader** who will take all the decisions to restore the service availability by: - Promoting a new **Master** @@ -212,7 +197,8 @@ who will take all the decisions to restore the service availability by: - Reconfigure the old **Master** and demote to **Slave** when it comes back online You must have at least `3` Redis Sentinel servers, and they need to -be each in a independent machine (that are believed to fail independently). +be each in a independent machine (that are believed to fail independently), +ideally in different geographical areas. You can configure them in the same machines where you've configured the other Redis servers, but understand that if a whole node goes down, you loose both @@ -230,7 +216,7 @@ Here are some examples: - With `5` or `6` sentinels, a maximum of `2` can go down for a failover begin. - With `7` sentinels, a maximum of `3` nodes can go down. -The **Leader** election can sometimes fail the voting round when **consensus**, +The **Leader** election can sometimes fail the voting round when **consensus** is not achieved (see the odd number of nodes requirement above). In that case, a new attempt will be made after the amount of time defined in `sentinel['failover_timeout']` (in milliseconds). @@ -238,7 +224,7 @@ a new attempt will be made after the amount of time defined in >**Note:** We will see where `sentinel['failover_timeout']` is defined later. -The `failover_timeout` variable has a lot of different use cases, according to +The `failover_timeout` variable has a lot of different use cases. According to the official documentation: - The time needed to re-start a failover after a previous failover was @@ -259,7 +245,7 @@ the official documentation: the slaves will be reconfigured by the Sentinels anyway, but not with the exact parallel-syncs progression as specified. -## Redis HA configuration +## Configuring Redis HA This is the section where we install and setup the new Redis instances. @@ -271,95 +257,138 @@ This is the section where we install and setup the new Redis instances. `redis['password']`. At any time during a failover the Sentinels can reconfigure a node and change its status from master to slave and vice versa. -A summary of what are we going to do: +### Prerequisites -1. Provision the required number of instances specified previously: - - You can opt to install Redis and Sentinel in the same machine or each in - independent ones. - - Don't install Redis and Sentinel in the same machines your GitLab application - is running on. - - All machines must be able to talk to each other and accept incoming - connections over Redis (`6379`) and Sentinel (`26379`) ports (unless you - change the default ports). - - GitLab machines must be able to access these machines and with the same - permissions. - - Protect them from access from external networks (Internet), - to harden the security. +The prerequisites for a HA Redis setup are the following: -1. Download/install Omnibus GitLab using **steps 1 and 2** from - [GitLab downloads](https://about.gitlab.com/downloads) in each node. - - Do not complete other steps on the download page. - - Make sure you select the correct Omnibus package, with the same version - and type (Community, Enterprise editions) of your current install. +1. Provision the minimum required number of instances as specified in the + [recommended setup](#recommended-setup) section. +1. **Do NOT** install Redis or Redis Sentinel in the same machines your + GitLab application is running on. You can however opt in to install Redis + and Sentinel in the same machine (each in independent ones is recommended + though). +1. All Redis nodes must be able to talk to each other and accept incoming + connections over Redis (`6379`) and Sentinel (`26379`) ports (unless you + change the default ones). +1. The server that hosts the GitLab application must be able to access the + Redis nodes. +1. Protect the nodes from access from external networks (Internet), using + firewall. -1. Run `touch /etc/gitlab/skip-auto-migrations` to prevent database migrations - from running on upgrade. Only the primary GitLab application server should - handle migrations. +### Step 1. Configuring the master Redis instance -1. Edit `/etc/gitlab/gitlab.rb` and make the changes based on the - [Example Configurations](#example-configurations). +1. SSH into the **master** Redis server and login as root: -### Configuring the Master Redis instance + ``` + sudo -i + ``` -You will need to configure the following in `/etc/gitlab/gitlab.rb`: +1. [Download/install](https://about.gitlab.com/installation) the Omnibus GitLab + package using **steps 1 and 2** from the GitLab downloads page. + - Make sure you select the correct Omnibus package, with the same version + and type (Community, Enterprise editions) of your current install. + - Do not complete any other steps on the download page. -1. Define `redis_master_role['enable']` to `true`, to disable other services - in the machine (you can still enable Sentinel) +1. Edit `/etc/gitlab/gitlab.rb` and add the contents: -1. Define a `redis['bind']` address pointing to a local IP that your other machines - can reach you. - - If you really need to bind to an external accessible IP, make - sure you add extra firewall rules to prevent unauthorized access. - - You can also set bind to `0.0.0.0` which listen in all interfaces. + ```ruby + # Enable the master role and disable all other services in the machine + # (you can still enable Sentinel). + redis_master_role['enable'] = true + + # IP address pointing to a local IP that the other machines can reach to. + # You can also set bind to '0.0.0.0' which listen in all interfaces. + # If you really need to bind to an external accessible IP, make + # sure you add extra firewall rules to prevent unauthorized access. + redis['bind'] = '10.0.0.1' + + # Define a port so Redis can listen for TCP requests which will allow other + # machines to connect to it. + redis['port'] = 6379 + + # Set up password authentication for Redis (use the same password in all nodes). + redis['password'] = 'redis-password-goes-here' + ``` + +1. To prevent database migrations from running on upgrade, run: + + ``` + touch /etc/gitlab/skip-auto-migrations + ``` -1. Define a `redis['port']` so redis can listen for TCP requests which will - allow other machines to connect to it. + Only the primary GitLab application server should handle migrations. -1. Set up a password authentication with `redis['password']` and - `redis['master_password']` (use the same password in all nodes). +1. [Reconfigure Omnibus GitLab][reconfigure] for the changes to take effect. -1. [Reconfigure Omnibus GitLab][reocnfigure] for the changes to take effect. +### Step 2. Configuring the slave Redis instances -### Configuring the Slave Redis instances +1. SSH into the **slave** Redis server and login as root: -You will need to configure the following in `/etc/gitlab/gitlab.rb`: + ``` + sudo -i + ``` -1. Define `redis_slaves_role['enable']` to `true`, to disable other services - in the machine (you can still enable Sentinel) - - This will also set automatically `redis['master'] = false`. +1. [Download/install](https://about.gitlab.com/installation) the Omnibus GitLab + package using **steps 1 and 2** from the GitLab downloads page. + - Make sure you select the correct Omnibus package, with the same version + and type (Community, Enterprise editions) of your current install. + - Do not complete any other steps on the download page. -1. Define a `redis['bind']` address pointing to a local IP that your other machines - can reach you. - - If you really need to bind to an external accessible IP, make - sure you add extra firewall rules to prevent unauthorized access. - - You can also set bind to `0.0.0.0` which listen in all interfaces. +1. Edit `/etc/gitlab/gitlab.rb` and add the contents: + + ```ruby + # Enable the slave role and disable all other services in the machine + # (you can still enable Sentinel). This will also set automatically + # `redis['master'] = false`. + redis_slave_role['enable'] = true -1. Define a `redis['port']` so redis can listen for TCP requests which will - allow other machines to connect to it. + # IP address pointing to a local IP that the other machines can reach to. + # You can also set bind to '0.0.0.0' which listen in all interfaces. + # If you really need to bind to an external accessible IP, make + # sure you add extra firewall rules to prevent unauthorized access. + redis['bind'] = '10.0.0.2' -1. Set up a password authentication with `redis['password']` and - `redis['master_password']` (use the same password in all nodes). -1. Define `redis['master_ip']` with the IP of the **Master** Redis. + # Define a port so Redis can listen for TCP requests which will allow other + # machines to connect to it. + redis['port'] = 6379 -1. Define `redis['master_port']` with the port of the **Master** Redis (default to `6379`). + # The same password for Redeis authentication you set up for the master node. + redis['password'] = 'redis-password-goes-here' -Initial **Slave** nodes require `redis['master']` defined to `false` and -`redis['master_ip']` pointing to the initial **Master**. If you use the -simplified configuration by enabling `redis_slave_role['enable']`, you -just need to fill in the `redis['master_ip']`. + # The IP of the master Redis node. + redis['master_ip'] = '10.0.0.1' -This values don't have to be changed again in `/etc/gitlab/gitlab.rb` after + # Port of master Redis server, uncomment to change to non default. Defaults + # to `6379`. + #redis['master_port'] = 6379 + ``` + +1. To prevent database migrations from running on upgrade, run: + + ``` + touch /etc/gitlab/skip-auto-migrations + ``` + + Only the primary GitLab application server should handle migrations. + +1. [Reconfigure Omnibus GitLab][reconfigure] for the changes to take effect. +1. Go through the steps again for all the other slave nodes. + +--- + +These values don't have to be changed again in `/etc/gitlab/gitlab.rb` after a failover, as the nodes will be managed by the Sentinels, and even after a `gitlab-ctl reconfigure`, they will get their configuration restored by the same Sentinels. -### Configuring the Redis Sentinel instances +### Step 3. Configuring the Redis Sentinel instances >**Note:** -- Redis Sentinel is bundled with Omnibus GitLab Enterprise Edition only. For the - Omnibus Community Edition and installations from source, follow the - [Redis HA source install](redis_source.md) guide. +Redis Sentinel is bundled with Omnibus GitLab Enterprise Edition only. The +following section assumes you are using Omnibus GitLab Enterprise Edition. +For the Omnibus Community Edition and installations from source, follow the +[Redis HA source install](redis_source.md) guide. Now that the Redis servers are all set up, let's configure the Sentinel servers. @@ -369,15 +398,112 @@ correctly, please read the [Troubleshooting Replication](#troubleshooting-replic and fix it before proceeding with Sentinel setup. You must have at least `3` Redis Sentinel servers, and they need to -be each in a independent machine. You can configure them in the same +be each in an independent machine. You can configure them in the same machines where you've configured the other Redis servers. -With GitLab Enterprise Edition, you can use the Omnibus package to setup multiple -machines with the Sentinel daemon. +With GitLab Enterprise Edition, you can use the Omnibus package to setup +multiple machines with the Sentinel daemon. + +--- + +1. SSH into the server that will host Redis Sentinel and login as root: + + ``` + sudo -i + ``` + +1. **You can omit this step if the Sentinels will be hosted in the same node as + the other Redis instances.** + + [Download/install](https://about.gitlab.com/downloads-ee) the + Omnibus GitLab Enterprise Edition package using **steps 1 and 2** from the + GitLab downloads page. + - Make sure you select the correct Omnibus package, with the same version + the GitLab application is running. + - Do not complete any other steps on the download page. -See [example configuration](#configuration-for-sentinel-ee-only) below. +1. Edit `/etc/gitlab/gitlab.rb` and add the contents (if you are installing the + Sentinels in the same node as the other Redis instances, some values might + be duplicate below): -### Configuring the GitLab application + + ```ruby + redis_sentinel_role['enable'] = true + + # Must be the same in every sentinel node + redis['master_name'] = 'gitlab-redis' + + # The same password for Redis authentication you set up for the master node. + redis['password'] = 'redis-password-goes-here' + + # The IP of the master Redis node. + redis['master_ip'] = '10.0.0.1' + + # Define a port so Redis can listen for TCP requests which will allow other + # machines to connect to it. + redis['port'] = 6379 + + # Port of master Redis server, uncomment to change to non default. Defaults + # to `6379`. + #redis['master_port'] = 6379 + + ## Configure Sentinel + sentinel['bind'] = '10.0.0.1' + + # Port that Sentinel listens on, uncomment to change to non default. Defaults + # to `26379`. + # sentinel['port'] = 26379 + + ## Quorum must reflect the amount of voting sentinels it take to start a failover. + ## Value must NOT be greater then the amount of sentinels. + ## + ## The quorum can be used to tune Sentinel in two ways: + ## 1. If a the quorum is set to a value smaller than the majority of Sentinels + ## we deploy, we are basically making Sentinel more sensible to master failures, + ## triggering a failover as soon as even just a minority of Sentinels is no longer + ## able to talk with the master. + ## 1. If a quorum is set to a value greater than the majority of Sentinels, we are + ## making Sentinel able to failover only when there are a very large number (larger + ## than majority) of well connected Sentinels which agree about the master being down.s + sentinel['quorum'] = 2 + + ## Consider unresponsive server down after x amount of ms. + # sentinel['down_after_milliseconds'] = 10000 + + ## Specifies the failover timeout in milliseconds. It is used in many ways: + ## + ## - The time needed to re-start a failover after a previous failover was + ## already tried against the same master by a given Sentinel, is two + ## times the failover timeout. + ## + ## - The time needed for a slave replicating to a wrong master according + ## to a Sentinel current configuration, to be forced to replicate + ## with the right master, is exactly the failover timeout (counting since + ## the moment a Sentinel detected the misconfiguration). + ## + ## - The time needed to cancel a failover that is already in progress but + ## did not produced any configuration change (SLAVEOF NO ONE yet not + ## acknowledged by the promoted slave). + ## + ## - The maximum time a failover in progress waits for all the slaves to be + ## reconfigured as slaves of the new master. However even after this time + ## the slaves will be reconfigured by the Sentinels anyway, but not with + ## the exact parallel-syncs progression as specified. + # sentinel['failover_timeout'] = 60000 + ``` + +1. To prevent database migrations from running on upgrade, run: + + ``` + touch /etc/gitlab/skip-auto-migrations + ``` + + Only the primary GitLab application server should handle migrations. + +1. [Reconfigure Omnibus GitLab][reconfigure] for the changes to take effect. +1. Go through the steps again for all the other Sentinel nodes. + +### Step 4. Configuring the GitLab application The final part is to inform the main GitLab application server of the Redis Sentinels servers and authentication credentials. @@ -395,14 +521,23 @@ which ideally should not have Redis or Sentinels on it for a HA setup. 1. Edit `/etc/gitlab/gitlab.rb` and add/change the following lines: - - `redis['master_name']` - this is the `master-group-name` from sentinel (default: `gitlab-redis`) - - `redis['master_password']` - the same password you've defined before for Redis and Sentinels - - `gitlab_rails['redis_sentinels']` - a list of sentinels with `host` and `port` + ``` + # Must be the same in every sentinel node + redis['master_name'] = 'gitlab-redis' + + # The same password for Redis authentication you set up for the master node. + redis['password'] = 'redis-password-goes-here' + + # A list of sentinels with `host` and `port` + gitlab_rails['redis_sentinels'] = [ + {'host' => '10.0.0.1', 'port' => 26379}, + {'host' => '10.0.0.2', 'port' => 26379}, + {'host' => '10.0.0.3', 'port' => 26379} + ] + ``` 1. [Reconfigure Omnibus GitLab][reconfigure] for the changes to take effect. -See [example configuration](#configuration-for-gitlab) below. - ## Switching from an existing single-machine installation to Redis HA If you already have a single-machine GitLab install running, you will need to @@ -436,14 +571,15 @@ unauthorized access from other machines and block traffic from the outside (Internet). We will use the same `3` nodes with **Redis** + **Sentinel** topology -discussed in [Redis Setup](#redis-setup) and [Sentinel Setup](#sentinel-setup) -documentation. +discussed in [Redis setup overview](#redis-setup-overview) and +[Sentinel setup overview](#sentinel-setup-overview) documentation. Here is a list and description of each **machine** and the assigned **IP**: * `10.0.0.1`: Redis Master + Sentinel 1 * `10.0.0.2`: Redis Slave 1 + Sentinel 2 -* `10.0.0.2`: Redis Slave 2 + Sentinel 3 +* `10.0.0.3`: Redis Slave 2 + Sentinel 3 +* `10.0.0.4`: GitLab application Please note that after the initial configuration, if a failover is initiated by the Sentinel nodes, the Redis nodes will be reconfigured and the **Master** @@ -454,7 +590,7 @@ The same thing will happen with `sentinel.conf` that will be overridden after th initial execution, after any new sentinel node starts watching the **Master**, or a failover promotes a different **Master** node. -### Configuration for Redis master +### Example configuration for Redis master **Example configation for Redis Master:** @@ -466,14 +602,13 @@ redis_master_role['enable'] = true redis['bind'] = '10.0.0.1' redis['port'] = 6379 redis['password'] = 'redis-password-goes-here' -redis['master_password'] = 'redis-password-goes-here' ``` [Reconfigure Omnibus GitLab][reconfigure] for the changes to take effect. -### Configuration for Redis slaves +### Example configuration for Redis slaves -**Example configation for Slave 1:** +**Example configuration for Slave 1:** In `/etc/gitlab/gitlab.rb`: @@ -489,9 +624,9 @@ redis['master_ip'] = '10.0.0.1' # IP of master Redis server #redis['master_port'] = 6379 # Port of master Redis server, uncomment to change to non default ``` -Reconfigure Omnibus GitLab for the changes to take effect: `sudo gitlab-ctl reconfigure` +[Reconfigure Omnibus GitLab][reconfigure] for the changes to take effect. -**Example configation for Slave 2:** +**Example configuration for Slave 2:** In `/etc/gitlab/gitlab.rb`: @@ -509,7 +644,7 @@ redis['master_ip'] = '10.0.0.1' # IP of master Redis server [Reconfigure Omnibus GitLab][reconfigure] for the changes to take effect. -### Configuration for Sentinels +### Example configuration for Sentinels >**Note:** Redis Sentinel is bundled with Omnibus GitLab Enterprise Edition only. For the @@ -519,7 +654,7 @@ Omnibus Community Edition and installations from source, follow the Please note that some of the variables are already configured previously as they are required for Redis replication. -**Example configation for Sentinel 1:** +**Example configuration for Sentinel 1:** In `/etc/gitlab/gitlab.rb`: @@ -534,46 +669,17 @@ redis['master_ip'] = '10.0.0.1' # ip of the initial master redis instance ## Configure Sentinel sentinel['bind'] = '10.0.0.1' # sentinel['port'] = 26379 # uncomment to change default port - -## Quorum must reflect the amount of voting sentinels it take to start a failover. -## Value must NOT be greater then the ammount of sentinels. -## -## The quorum can be used to tune Sentinel in two ways: -## 1. If a the quorum is set to a value smaller than the majority of Sentinels -## we deploy, we are basically making Sentinel more sensible to master failures, -## triggering a failover as soon as even just a minority of Sentinels is no longer -## able to talk with the master. -## 1. If a quorum is set to a value greater than the majority of Sentinels, we are -## making Sentinel able to failover only when there are a very large number (larger -## than majority) of well connected Sentinels which agree about the master being down.s sentinel['quorum'] = 2 ## Consider unresponsive server down after x amount of ms. # sentinel['down_after_milliseconds'] = 10000 -## Specifies the failover timeout in milliseconds. It is used in many ways: -## -## - The time needed to re-start a failover after a previous failover was -## already tried against the same master by a given Sentinel, is two -## times the failover timeout. -## -## - The time needed for a slave replicating to a wrong master according -## to a Sentinel current configuration, to be forced to replicate -## with the right master, is exactly the failover timeout (counting since -## the moment a Sentinel detected the misconfiguration). -## -## - The time needed to cancel a failover that is already in progress but -## did not produced any configuration change (SLAVEOF NO ONE yet not -## acknowledged by the promoted slave). -## -## - The maximum time a failover in progress waits for all the slaves to be -## reconfigured as slaves of the new master. However even after this time -## the slaves will be reconfigured by the Sentinels anyway, but not with -## the exact parallel-syncs progression as specified. # sentinel['failover_timeout'] = 60000 ``` -**Example configation for Sentinel 2:** +[Reconfigure Omnibus GitLab][reconfigure] for the changes to take effect. + +**Example configuration for Sentinel 2:** In `/etc/gitlab/gitlab.rb`: @@ -589,45 +695,17 @@ redis['master_ip'] = '10.0.0.1' # ip of the initial master redis instance sentinel['bind'] = '10.0.0.2' # sentinel['port'] = 26379 # uncomment to change default port -## Quorum must reflect the amount of voting sentinels it take to start a failover. -## Value must NOT be greater then the ammount of sentinels. -## -## The quorum can be used to tune Sentinel in two ways: -## 1. If a the quorum is set to a value smaller than the majority of Sentinels -## we deploy, we are basically making Sentinel more sensible to master failures, -## triggering a failover as soon as even just a minority of Sentinels is no longer -## able to talk with the master. -## 1. If a quorum is set to a value greater than the majority of Sentinels, we are -## making Sentinel able to failover only when there are a very large number (larger -## than majority) of well connected Sentinels which agree about the master being down.s sentinel['quorum'] = 2 ## Consider unresponsive server down after x amount of ms. # sentinel['down_after_milliseconds'] = 10000 -## Specifies the failover timeout in milliseconds. It is used in many ways: -## -## - The time needed to re-start a failover after a previous failover was -## already tried against the same master by a given Sentinel, is two -## times the failover timeout. -## -## - The time needed for a slave replicating to a wrong master according -## to a Sentinel current configuration, to be forced to replicate -## with the right master, is exactly the failover timeout (counting since -## the moment a Sentinel detected the misconfiguration). -## -## - The time needed to cancel a failover that is already in progress but -## did not produced any configuration change (SLAVEOF NO ONE yet not -## acknowledged by the promoted slave). -## -## - The maximum time a failover in progress waits for all the slaves to be -## reconfigured as slaves of the new master. However even after this time -## the slaves will be reconfigured by the Sentinels anyway, but not with -## the exact parallel-syncs progression as specified. # sentinel['failover_timeout'] = 60000 ``` -**Example configation for Sentinel 3:** +[Reconfigure Omnibus GitLab][reconfigure] for the changes to take effect. + +**Example configuration for Sentinel 3:** In `/etc/gitlab/gitlab.rb`: @@ -643,44 +721,37 @@ redis['master_ip'] = '10.0.0.1' # ip of the initial master redis instance sentinel['bind'] = '10.0.0.3' # sentinel['port'] = 26379 # uncomment to change default port -## Quorum must reflect the amount of voting sentinels it take to start a failover. -## Value must NOT be greater then the ammount of sentinels. -## -## The quorum can be used to tune Sentinel in two ways: -## 1. If a the quorum is set to a value smaller than the majority of Sentinels -## we deploy, we are basically making Sentinel more sensible to master failures, -## triggering a failover as soon as even just a minority of Sentinels is no longer -## able to talk with the master. -## 1. If a quorum is set to a value greater than the majority of Sentinels, we are -## making Sentinel able to failover only when there are a very large number (larger -## than majority) of well connected Sentinels which agree about the master being down.s sentinel['quorum'] = 2 ## Consider unresponsive server down after x amount of ms. # sentinel['down_after_milliseconds'] = 10000 -## Specifies the failover timeout in milliseconds. It is used in many ways: -## -## - The time needed to re-start a failover after a previous failover was -## already tried against the same master by a given Sentinel, is two -## times the failover timeout. -## -## - The time needed for a slave replicating to a wrong master according -## to a Sentinel current configuration, to be forced to replicate -## with the right master, is exactly the failover timeout (counting since -## the moment a Sentinel detected the misconfiguration). -## -## - The time needed to cancel a failover that is already in progress but -## did not produced any configuration change (SLAVEOF NO ONE yet not -## acknowledged by the promoted slave). -## -## - The maximum time a failover in progress waits for all the slaves to be -## reconfigured as slaves of the new master. However even after this time -## the slaves will be reconfigured by the Sentinels anyway, but not with -## the exact parallel-syncs progression as specified. # sentinel['failover_timeout'] = 60000 ``` +[Reconfigure Omnibus GitLab][reconfigure] for the changes to take effect. + +### Example configuration for the GitLab application + +In `/etc/gitlab/gitlab.rb`: + +``` +# Must be the same in every sentinel node +redis['master_name'] = 'gitlab-redis' + +# The same password for Redis authentication you set up for the master node. +redis['password'] = 'redis-password-goes-here' + +# A list of sentinels with `host` and `port` +gitlab_rails['redis_sentinels'] = [ + {'host' => '10.0.0.1', 'port' => 26379}, + {'host' => '10.0.0.2', 'port' => 26379}, + {'host' => '10.0.0.3', 'port' => 26379} +] +``` + +[Reconfigure Omnibus GitLab][reconfigure] for the changes to take effect. + ## Advanced configuration Omnibus GitLab configures some things behind the curtains to make the sysadmins' @@ -688,8 +759,8 @@ lives easier. If you want to know what happens underneath keep reading. ### Control running services -In the previous example above we've used `redis_sentinel_role` and -`redis_master_role` which simplify the amount of configuration changes. +In the previous example, we've used `redis_sentinel_role` and +`redis_master_role` which simplifies the amount of configuration changes. If you want more control, here is what each one sets for you automatically when enabled: @@ -698,10 +769,10 @@ when enabled: ## Redis Sentinel Role redis_sentinel_role['enable'] = true -# When Sentinel Role is enabled, the following services are enabled/disabled: +# When Sentinel Role is enabled, the following services are also enabled sentinel['enable'] = true -# This others are disabled: +# The following services are disabled redis['enable'] = false bootstrap['enable'] = false nginx['enable'] = false @@ -709,17 +780,17 @@ postgresql['enable'] = false gitlab_rails['enable'] = false mailroom['enable'] = false -## Redis master/slave Role: +------- + +## Redis master/slave Role redis_master_role['enable'] = true # enable only one of them redis_slave_role['enable'] = true # enable only one of them -# When Redis Master or Slave role are enabled, the following services are enabled/disabled: -# (Note that if redis and sentinel roles are combined both services will be enabled) +# When Redis Master or Slave role are enabled, the following services are +# enabled/disabled. Note that if Redis and Sentinel roles are combined, both +# services will be enabled. -# When Sentinel Role is enabled, the following services are enabled/disabled: -redis['enable'] = true - -# This others are disabled: +# The following services are disabled sentinel['enable'] = false bootstrap['enable'] = false nginx['enable'] = false @@ -727,7 +798,7 @@ postgresql['enable'] = false gitlab_rails['enable'] = false mailroom['enable'] = false -# Redis Slave role also change this setting from default 'true' to 'false': +# For Redis Slave role, also change this setting from default 'true' to 'false': redis['master'] = false ``` @@ -856,6 +927,16 @@ To make sure your configuration is correct: Changes to Redis HA over time. +**8.14** + +- Redis Sentinel support is production-ready and bundled in the Omnibus GitLab + Enterprise Edition package +- Documentation restructure for better readability + +**8.11** + +- Experimental Redis Sentinel support was added + ### Experimental Redis Sentinel support > @@ -880,7 +961,7 @@ Read more on high-availability configuration: [gh-534]: https://github.com/redis/redis-rb/issues/534 [redis]: http://redis.io/ [sentinel]: http://redis.io/topics/sentinel -[omnifile]: https://gitlab.com/gitlab-org/omnibus-gitlab/blob/82b7345b150f072c8673c79738ce893f92d0d652/files/gitlab-cookbooks/gitlab/libraries/gitlab_rails.rb#L134-159 +[omnifile]: https://gitlab.com/gitlab-org/omnibus-gitlab/blob/master/files/gitlab-cookbooks/gitlab/libraries/gitlab_rails.rb [source]: ../../install/installation.md [ce]: https://about.gitlab.com/downloads [ee]: https://about.gitlab.com/downloads-ee -- cgit v1.2.1 From d54b88260c3cdedd3497c6fa9fa15e3ceedaebcb Mon Sep 17 00:00:00 2001 From: Akram FARES Date: Tue, 11 Oct 2016 10:32:40 +0000 Subject: Limit autocomplete to currently selected items --- app/assets/javascripts/gfm_auto_complete.js.es6 | 6 ++++++ app/controllers/projects_controller.rb | 4 +++- app/services/projects/autocomplete_service.rb | 9 ++++++++- .../22680-unlabel-limit-autocomplete-to-selected-items.yml | 4 ++++ 4 files changed, 21 insertions(+), 2 deletions(-) create mode 100644 changelogs/unreleased/22680-unlabel-limit-autocomplete-to-selected-items.yml diff --git a/app/assets/javascripts/gfm_auto_complete.js.es6 b/app/assets/javascripts/gfm_auto_complete.js.es6 index e72e2194be8..3101b306080 100644 --- a/app/assets/javascripts/gfm_auto_complete.js.es6 +++ b/app/assets/javascripts/gfm_auto_complete.js.es6 @@ -51,6 +51,11 @@ if (!GitLab.GfmAutoComplete.dataLoaded) { return this.at; } else { + if (value.indexOf("unlabel") !== -1) { + GitLab.GfmAutoComplete.input.atwho('load', '~', GitLab.GfmAutoComplete.cachedData.unlabels); + } else { + GitLab.GfmAutoComplete.input.atwho('load', '~', GitLab.GfmAutoComplete.cachedData.labels); + } return value; } } @@ -352,3 +357,4 @@ }; }).call(this); + diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb index a8a18b4fa16..7376c2bfeb7 100644 --- a/app/controllers/projects_controller.rb +++ b/app/controllers/projects_controller.rb @@ -144,13 +144,15 @@ class ProjectsController < Projects::ApplicationController autocomplete = ::Projects::AutocompleteService.new(@project, current_user) participants = ::Projects::ParticipantsService.new(@project, current_user).execute(noteable) + unlabels = autocomplete.unlabels(noteable) @suggestions = { emojis: Gitlab::AwardEmoji.urls, issues: autocomplete.issues, milestones: autocomplete.milestones, mergerequests: autocomplete.merge_requests, - labels: autocomplete.labels, + labels: autocomplete.labels - unlabels, + unlabels: unlabels, members: participants, commands: autocomplete.commands(noteable, params[:type]) } diff --git a/app/services/projects/autocomplete_service.rb b/app/services/projects/autocomplete_service.rb index 015f2828921..223461e88b6 100644 --- a/app/services/projects/autocomplete_service.rb +++ b/app/services/projects/autocomplete_service.rb @@ -13,7 +13,14 @@ module Projects end def labels - LabelsFinder.new(current_user, project_id: project.id).execute.select([:title, :color]) + LabelsFinder.new(current_user, project_id: project.id).execute. + pluck(:title, :color).map { |l| { title: l.first, color: l.second } } + end + + def unlabels(noteable) + return [] unless noteable && noteable.respond_to?(:labels) + + noteable.labels.pluck(:title, :color).map { |l| { title: l.first, color: l.second } } end def commands(noteable, type) diff --git a/changelogs/unreleased/22680-unlabel-limit-autocomplete-to-selected-items.yml b/changelogs/unreleased/22680-unlabel-limit-autocomplete-to-selected-items.yml new file mode 100644 index 00000000000..95fd07c12e1 --- /dev/null +++ b/changelogs/unreleased/22680-unlabel-limit-autocomplete-to-selected-items.yml @@ -0,0 +1,4 @@ +--- +title: Limit autocomplete to currently selected items for unlabel slash command +merge_request: 22680 +author: Akram Fares -- cgit v1.2.1 From 4d3ac38570d468f9db501654d0f19a2b8132803a Mon Sep 17 00:00:00 2001 From: awhildy Date: Tue, 15 Nov 2016 16:34:10 -0800 Subject: [ci skip] Fix UX Guide link on Contributing.md --- CONTRIBUTING.md | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 67c30c2424c..6a009138446 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -9,8 +9,6 @@ - [Helping others](#helping-others) - [I want to contribute!](#i-want-to-contribute) - [Implement design & UI elements](#implement-design-ui-elements) - - [Design reference](#design-reference) - - [UI development kit](#ui-development-kit) - [Issue tracker](#issue-tracker) - [Feature proposals](#feature-proposals) - [Issue tracker guidelines](#issue-tracker-guidelines) @@ -90,7 +88,7 @@ This was inspired by [an article by Kent C. Dodds][medium-up-for-grabs]. ## Implement design & UI elements -Please see the [UI Guide for building GitLab]. +Please see the [UX Guide for GitLab]. ## Issue tracker @@ -469,5 +467,5 @@ available at [http://contributor-covenant.org/version/1/1/0/](http://contributor [doc-styleguide]: doc/development/doc_styleguide.md "Documentation styleguide" [scss-styleguide]: doc/development/scss_styleguide.md "SCSS styleguide" [newlines-styleguide]: doc/development/newlines_styleguide.md "Newlines styleguide" -[UI Guide for building GitLab]: https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/development/ui_guide.md +[UX Guide for GitLab]: http://docs.gitlab.com/ce/development/ux_guide/ [license-finder-doc]: doc/development/licensing.md -- cgit v1.2.1 From dbf5c8abfe44ce8bf4a9517a8acead961edb3e3e Mon Sep 17 00:00:00 2001 From: the-undefined Date: Tue, 15 Nov 2016 07:10:43 +0000 Subject: Move 'Search Snippets' Spinach feature to Rspec This commit moves the `search_snippets.feature` Spinach test to a Rspec feature, as part of deprecating the Spinach test suite. - Remove Spinach discover snippets feature and steps - Remove unused `SharedSearch` module - Add Rspec feature scenarios --- features/snippet_search.feature | 20 -------- features/steps/shared/search.rb | 11 ----- features/steps/snippet_search.rb | 55 --------------------- spec/features/snippets/search_snippets_spec.rb | 66 ++++++++++++++++++++++++++ 4 files changed, 66 insertions(+), 86 deletions(-) delete mode 100644 features/snippet_search.feature delete mode 100644 features/steps/shared/search.rb delete mode 100644 features/steps/snippet_search.rb create mode 100644 spec/features/snippets/search_snippets_spec.rb diff --git a/features/snippet_search.feature b/features/snippet_search.feature deleted file mode 100644 index 834bd3b2376..00000000000 --- a/features/snippet_search.feature +++ /dev/null @@ -1,20 +0,0 @@ -@dashboard -Feature: Snippet Search - Background: - Given I sign in as a user - And I have public "Personal snippet one" snippet - And I have private "Personal snippet private" snippet - And I have a public many lined snippet - - Scenario: I should see my public and private snippets - When I search for "snippet" in snippet titles - Then I should see "Personal snippet one" in results - And I should see "Personal snippet private" in results - - Scenario: I should see three surrounding lines on either side of a matching snippet line - When I search for "line seven" in snippet contents - Then I should see "line four" in results - And I should see "line seven" in results - And I should see "line ten" in results - And I should not see "line three" in results - And I should not see "line eleven" in results diff --git a/features/steps/shared/search.rb b/features/steps/shared/search.rb deleted file mode 100644 index 6c3d601763d..00000000000 --- a/features/steps/shared/search.rb +++ /dev/null @@ -1,11 +0,0 @@ -module SharedSearch - include Spinach::DSL - - def search_snippet_contents(query) - visit "/search?search=#{URI::encode(query)}&snippets=true&scope=snippet_blobs" - end - - def search_snippet_titles(query) - visit "/search?search=#{URI::encode(query)}&snippets=true&scope=snippet_titles" - end -end diff --git a/features/steps/snippet_search.rb b/features/steps/snippet_search.rb deleted file mode 100644 index 32e29ffad1e..00000000000 --- a/features/steps/snippet_search.rb +++ /dev/null @@ -1,55 +0,0 @@ -class Spinach::Features::SnippetSearch < Spinach::FeatureSteps - include SharedAuthentication - include SharedPaths - include SharedSnippet - include SharedUser - include SharedSearch - - step 'I search for "snippet" in snippet titles' do - search_snippet_titles 'snippet' - end - - step 'I search for "snippet private" in snippet titles' do - search_snippet_titles 'snippet private' - end - - step 'I search for "line seven" in snippet contents' do - search_snippet_contents 'line seven' - end - - step 'I should see "line seven" in results' do - expect(page).to have_content 'line seven' - end - - step 'I should see "line four" in results' do - expect(page).to have_content 'line four' - end - - step 'I should see "line ten" in results' do - expect(page).to have_content 'line ten' - end - - step 'I should not see "line eleven" in results' do - expect(page).not_to have_content 'line eleven' - end - - step 'I should not see "line three" in results' do - expect(page).not_to have_content 'line three' - end - - step 'I should see "Personal snippet one" in results' do - expect(page).to have_content 'Personal snippet one' - end - - step 'I should see "Personal snippet private" in results' do - expect(page).to have_content 'Personal snippet private' - end - - step 'I should not see "Personal snippet one" in results' do - expect(page).not_to have_content 'Personal snippet one' - end - - step 'I should not see "Personal snippet private" in results' do - expect(page).not_to have_content 'Personal snippet private' - end -end diff --git a/spec/features/snippets/search_snippets_spec.rb b/spec/features/snippets/search_snippets_spec.rb new file mode 100644 index 00000000000..146cd3af848 --- /dev/null +++ b/spec/features/snippets/search_snippets_spec.rb @@ -0,0 +1,66 @@ +require 'rails_helper' + +feature 'Search Snippets', feature: true do + scenario 'User searches for snippets by title' do + public_snippet = create(:personal_snippet, :public, title: 'Beginning and Middle') + private_snippet = create(:personal_snippet, :private, title: 'Middle and End') + + login_as private_snippet.author + visit dashboard_snippets_path + + page.within '.search' do + fill_in 'search', with: 'Middle' + click_button 'Go' + end + + click_link 'Titles and Filenames' + + expect(page).to have_link(public_snippet.title) + expect(page).to have_link(private_snippet.title) + end + + scenario 'User searches for snippet contents' do + create(:personal_snippet, + :public, + title: 'Many lined snippet', + content: <<-CONTENT.strip_heredoc + |line one + |line two + |line three + |line four + |line five + |line six + |line seven + |line eight + |line nine + |line ten + |line eleven + |line twelve + |line thirteen + |line fourteen + CONTENT + ) + + login_as create(:user) + visit dashboard_snippets_path + + page.within '.search' do + fill_in 'search', with: 'line seven' + click_button 'Go' + end + + expect(page).to have_content('line seven') + + # 3 lines before the matched line should be visible + expect(page).to have_content('line six') + expect(page).to have_content('line five') + expect(page).to have_content('line four') + expect(page).not_to have_content('line three') + + # 3 lines after the matched line should be visible + expect(page).to have_content('line eight') + expect(page).to have_content('line nine') + expect(page).to have_content('line ten') + expect(page).not_to have_content('line eleven') + end +end -- cgit v1.2.1 From 8c3e6987d931847b72752dfcac4215dbdc47fd88 Mon Sep 17 00:00:00 2001 From: Lucas Deschamps Date: Wed, 9 Nov 2016 07:59:51 +0100 Subject: Navigation bar issuables counters reflects dashboard issuables counters --- app/helpers/issuables_helper.rb | 11 ++++++++ app/views/layouts/nav/_dashboard.html.haml | 4 +-- .../fix_navigation_bar_issuables_counters.yml | 4 +++ spec/features/dashboard/issuables_counter_spec.rb | 33 ++++++++++++++++++++++ 4 files changed, 50 insertions(+), 2 deletions(-) create mode 100644 changelogs/unreleased/fix_navigation_bar_issuables_counters.yml create mode 100644 spec/features/dashboard/issuables_counter_spec.rb diff --git a/app/helpers/issuables_helper.rb b/app/helpers/issuables_helper.rb index 8127c3f3ee3..22311267860 100644 --- a/app/helpers/issuables_helper.rb +++ b/app/helpers/issuables_helper.rb @@ -141,8 +141,19 @@ module IssuablesHelper html.html_safe end + def cached_assigned_issuables_count(assignee, issuable_type, state) + cache_key = "#{assignee.id}_#{issuable_type}_#{state}" + Rails.cache.fetch(cache_key, expires_in: 2.minutes) do + assigned_issuables_count(assignee, issuable_type, state) + end + end + private + def assigned_issuables_count(assignee, issuable_type, state) + assignee.send("assigned_#{issuable_type}").send(state).count + end + def sidebar_gutter_collapsed? cookies[:collapsed_gutter] == 'true' end diff --git a/app/views/layouts/nav/_dashboard.html.haml b/app/views/layouts/nav/_dashboard.html.haml index a0356feef95..2a6d9cda379 100644 --- a/app/views/layouts/nav/_dashboard.html.haml +++ b/app/views/layouts/nav/_dashboard.html.haml @@ -26,12 +26,12 @@ = link_to assigned_issues_dashboard_path, title: 'Issues', class: 'dashboard-shortcuts-issues' do %span Issues - %span.count= number_with_delimiter(current_user.assigned_issues.opened.count) + %span.count= number_with_delimiter(cached_assigned_issuables_count(current_user, :issues, :opened)) = nav_link(path: 'dashboard#merge_requests') do = link_to assigned_mrs_dashboard_path, title: 'Merge Requests', class: 'dashboard-shortcuts-merge_requests' do %span Merge Requests - %span.count= number_with_delimiter(current_user.assigned_merge_requests.opened.count) + %span.count= number_with_delimiter(cached_assigned_issuables_count(current_user, :merge_requests, :opened)) = nav_link(controller: 'dashboard/snippets') do = link_to dashboard_snippets_path, title: 'Snippets' do %span diff --git a/changelogs/unreleased/fix_navigation_bar_issuables_counters.yml b/changelogs/unreleased/fix_navigation_bar_issuables_counters.yml new file mode 100644 index 00000000000..c66f191a2d0 --- /dev/null +++ b/changelogs/unreleased/fix_navigation_bar_issuables_counters.yml @@ -0,0 +1,4 @@ +--- +title: Navigation bar issuables counters reflects dashboard issuables counters +merge_request: +author: Lucas Deschamps diff --git a/spec/features/dashboard/issuables_counter_spec.rb b/spec/features/dashboard/issuables_counter_spec.rb new file mode 100644 index 00000000000..699bc102790 --- /dev/null +++ b/spec/features/dashboard/issuables_counter_spec.rb @@ -0,0 +1,33 @@ +require 'spec_helper' + +describe 'Navigation bar counter', feature: true, js: true, caching: true do + let(:user) { create(:user) } + let(:project) { create(:project, namespace: user.namespace) } + + before do + login_as(user) + visit issues_dashboard_path + end + + it 'reflects dashboard issues count' do + create(:issue, project: project, assignee: user) + visit issues_dashboard_path + + dashboard_count = find('li.active span.badge') + nav_count = find('.dashboard-shortcuts-issues span.count') + + expect(dashboard_count).to have_content('0') + expect(nav_count).to have_content('0') + end + + it 'reflects dashboard merge requests count' do + create(:merge_request, assignee: user) + visit merge_requests_dashboard_path + + dashboard_count = find('li.active span.badge') + nav_count = find('.dashboard-shortcuts-merge_requests span.count') + + expect(dashboard_count).to have_content('0') + expect(nav_count).to have_content('0') + end +end -- cgit v1.2.1 From 677af1dd30f9b44a7be9eaa68bddda38331b8bbb Mon Sep 17 00:00:00 2001 From: Lucas Deschamps Date: Wed, 9 Nov 2016 23:15:29 +0100 Subject: Improve changes after MR review. --- app/helpers/issuables_helper.rb | 4 ++-- changelogs/unreleased/fix_navigation_bar_issuables_counters.yml | 2 +- spec/features/dashboard/issuables_counter_spec.rb | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/helpers/issuables_helper.rb b/app/helpers/issuables_helper.rb index 22311267860..58d46daa51e 100644 --- a/app/helpers/issuables_helper.rb +++ b/app/helpers/issuables_helper.rb @@ -142,7 +142,7 @@ module IssuablesHelper end def cached_assigned_issuables_count(assignee, issuable_type, state) - cache_key = "#{assignee.id}_#{issuable_type}_#{state}" + cache_key = hexdigest(['assigned_issuables_count', assignee.id, issuable_type, state].join('-')) Rails.cache.fetch(cache_key, expires_in: 2.minutes) do assigned_issuables_count(assignee, issuable_type, state) end @@ -151,7 +151,7 @@ module IssuablesHelper private def assigned_issuables_count(assignee, issuable_type, state) - assignee.send("assigned_#{issuable_type}").send(state).count + assignee.public_send("assigned_#{issuable_type}").public_send(state).count end def sidebar_gutter_collapsed? diff --git a/changelogs/unreleased/fix_navigation_bar_issuables_counters.yml b/changelogs/unreleased/fix_navigation_bar_issuables_counters.yml index c66f191a2d0..0f7f8155f91 100644 --- a/changelogs/unreleased/fix_navigation_bar_issuables_counters.yml +++ b/changelogs/unreleased/fix_navigation_bar_issuables_counters.yml @@ -1,4 +1,4 @@ --- title: Navigation bar issuables counters reflects dashboard issuables counters -merge_request: +merge_request: 7368 author: Lucas Deschamps diff --git a/spec/features/dashboard/issuables_counter_spec.rb b/spec/features/dashboard/issuables_counter_spec.rb index 699bc102790..f134eee19eb 100644 --- a/spec/features/dashboard/issuables_counter_spec.rb +++ b/spec/features/dashboard/issuables_counter_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' describe 'Navigation bar counter', feature: true, js: true, caching: true do let(:user) { create(:user) } - let(:project) { create(:project, namespace: user.namespace) } + let(:project) { create(:empty_project, namespace: user.namespace) } before do login_as(user) -- cgit v1.2.1 From 7fa366107b30fa0ccac758ff9e6854f86e54b116 Mon Sep 17 00:00:00 2001 From: Lucas Deschamps Date: Thu, 10 Nov 2016 23:41:25 +0100 Subject: Improve issuables_counter_spec relevance. --- spec/features/dashboard/issuables_counter_spec.rb | 33 +++++++++++++++++------ 1 file changed, 25 insertions(+), 8 deletions(-) diff --git a/spec/features/dashboard/issuables_counter_spec.rb b/spec/features/dashboard/issuables_counter_spec.rb index f134eee19eb..1960552b411 100644 --- a/spec/features/dashboard/issuables_counter_spec.rb +++ b/spec/features/dashboard/issuables_counter_spec.rb @@ -1,33 +1,50 @@ require 'spec_helper' -describe 'Navigation bar counter', feature: true, js: true, caching: true do +describe 'Navigation bar counter', feature: true, js: true do let(:user) { create(:user) } let(:project) { create(:empty_project, namespace: user.namespace) } before do login_as(user) - visit issues_dashboard_path end it 'reflects dashboard issues count' do - create(:issue, project: project, assignee: user) + issue = create(:issue, project: project, assignee: user) + visit issues_dashboard_path + + dashboard_count = find('li.active span.badge') + nav_count = find('.dashboard-shortcuts-issues span.count') + + expect(nav_count).to have_content('1') + expect(dashboard_count).to have_content('1') + + issue.assignee = nil visit issues_dashboard_path dashboard_count = find('li.active span.badge') nav_count = find('.dashboard-shortcuts-issues span.count') - expect(dashboard_count).to have_content('0') - expect(nav_count).to have_content('0') + expect(nav_count).to have_content('1') + expect(dashboard_count).to have_content('1') end it 'reflects dashboard merge requests count' do - create(:merge_request, assignee: user) + merge_request = create(:merge_request, source_project: project, assignee: user) + visit merge_requests_dashboard_path + + dashboard_count = find('li.active span.badge') + nav_count = find('.dashboard-shortcuts-merge_requests span.count') + + expect(nav_count).to have_content('1') + expect(dashboard_count).to have_content('1') + + merge_request.assignee = nil visit merge_requests_dashboard_path dashboard_count = find('li.active span.badge') nav_count = find('.dashboard-shortcuts-merge_requests span.count') - expect(dashboard_count).to have_content('0') - expect(nav_count).to have_content('0') + expect(nav_count).to have_content('1') + expect(dashboard_count).to have_content('1') end end -- cgit v1.2.1 From 3d13096f7198ed1ec4b7c568977877452f842394 Mon Sep 17 00:00:00 2001 From: Lucas Deschamps Date: Tue, 15 Nov 2016 09:05:19 +0100 Subject: Refactor issuables_counter_spec. --- spec/features/dashboard/issuables_counter_spec.rb | 40 ++++++++++------------- 1 file changed, 17 insertions(+), 23 deletions(-) diff --git a/spec/features/dashboard/issuables_counter_spec.rb b/spec/features/dashboard/issuables_counter_spec.rb index 1960552b411..41dcfe439c2 100644 --- a/spec/features/dashboard/issuables_counter_spec.rb +++ b/spec/features/dashboard/issuables_counter_spec.rb @@ -1,50 +1,44 @@ require 'spec_helper' -describe 'Navigation bar counter', feature: true, js: true do +describe 'Navigation bar counter', feature: true, js: true, caching: true do let(:user) { create(:user) } let(:project) { create(:empty_project, namespace: user.namespace) } + let(:issue) { create(:issue, project: project) } + let(:merge_request) { create(:merge_request, source_project: project) } before do + issue.update(assignee: user) + merge_request.update(assignee: user) login_as(user) end it 'reflects dashboard issues count' do - issue = create(:issue, project: project, assignee: user) visit issues_dashboard_path - dashboard_count = find('li.active span.badge') - nav_count = find('.dashboard-shortcuts-issues span.count') - - expect(nav_count).to have_content('1') - expect(dashboard_count).to have_content('1') + expect_counters('issues', '1') - issue.assignee = nil + issue.update(assignee: nil) visit issues_dashboard_path - dashboard_count = find('li.active span.badge') - nav_count = find('.dashboard-shortcuts-issues span.count') - - expect(nav_count).to have_content('1') - expect(dashboard_count).to have_content('1') + expect_counters('issues', '1') end it 'reflects dashboard merge requests count' do - merge_request = create(:merge_request, source_project: project, assignee: user) visit merge_requests_dashboard_path - dashboard_count = find('li.active span.badge') - nav_count = find('.dashboard-shortcuts-merge_requests span.count') - - expect(nav_count).to have_content('1') - expect(dashboard_count).to have_content('1') + expect_counters('merge_requests', '1') - merge_request.assignee = nil + merge_request.update(assignee: nil) visit merge_requests_dashboard_path + expect_counters('merge_requests', '1') + end + + def expect_counters(issuable_type, count) dashboard_count = find('li.active span.badge') - nav_count = find('.dashboard-shortcuts-merge_requests span.count') + nav_count = find(".dashboard-shortcuts-#{issuable_type} span.count") - expect(nav_count).to have_content('1') - expect(dashboard_count).to have_content('1') + expect(nav_count).to have_content(count) + expect(dashboard_count).to have_content(count) end end -- cgit v1.2.1 From a75c5f195124c12d3776d8c553bc71721cee121d Mon Sep 17 00:00:00 2001 From: Achilleas Pipinellis Date: Wed, 16 Nov 2016 10:20:59 +0100 Subject: Add clear instructions for the different Redis HA setups --- doc/administration/high_availability/redis.md | 75 ++++++++++++++------------- 1 file changed, 40 insertions(+), 35 deletions(-) diff --git a/doc/administration/high_availability/redis.md b/doc/administration/high_availability/redis.md index 6de92ae3741..7ea49a2c22a 100644 --- a/doc/administration/high_availability/redis.md +++ b/doc/administration/high_availability/redis.md @@ -20,8 +20,9 @@ Omnibus GitLab packages. from source, follow the [Redis HA source installation](redis_source.md) guide. - Redis Sentinel daemon is bundled with Omnibus GitLab Enterprise Edition only. For configuring Sentinel with the Omnibus GitLab Community Edition and - installations from source, follow the - [Redis HA source installation](redis_source.md) guide. + installations from source, read the + [Available configuration setups](#available-configuration-setups) section + below. @@ -40,7 +41,7 @@ Omnibus GitLab packages. - [Step 3. Configuring the Redis Sentinel instances](#step-3-configuring-the-redis-sentinel-instances) - [Step 4. Configuring the GitLab application](#step-4-configuring-the-gitlab-application) - [Switching from an existing single-machine installation to Redis HA](#switching-from-an-existing-single-machine-installation-to-redis-ha) -- [Example of a minimal configuration with 1 master, 2 slaves and 3 sentinels](#example-of-a-minimal-configuration-with-1-master-2-slaves-and-3-sentinels) +- [Example of a minimal configuration with 1 master, 2 slaves and 3 Sentinels](#example-of-a-minimal-configuration-with-1-master-2-slaves-and-3-sentinels) - [Configuration for Redis master](#configuration-for-redis-master) - [Configuration for Redis slaves](#configuration-for-redis-slaves) - [Configuration for Sentinels](#configuration-for-sentinels) @@ -79,17 +80,17 @@ components below. ### High Availability with Sentinel -> +>**Notes:** - Starting with GitLab `8.11`, you can configure a list of Redis Sentinel servers that will monitor a group of Redis servers to provide failover support. - Starting with GitLab `8.14`, the Omnibus GitLab Enterprise Edition package - comes with Redis Sentinel daemon support. + comes with Redis Sentinel daemon built-in. High Availability with Redis requires a few things: - Multiple Redis instances - - Run Redis in a **Master** x **Slave** topology - - Multiple Sentinel instances +- Run Redis in a **Master** x **Slave** topology +- Multiple Sentinel instances - Application support and visibility to all Sentinel and Redis instances Redis Sentinel can handle the most important tasks in an HA environment and that's @@ -132,27 +133,6 @@ the Omnibus GitLab package in `5` **independent** machines, both with - Redis Slave + Sentinel - Redis Slave + Sentinel -### Available configuration setups - -Based on your infrastructure setup and how you have installed GitLab, there are -multiple ways to configure Redis HA. Omnibus GitLab packages have Redis and/or -Redis Sentinel bundled with them so you only need to focus on configuration. -Pick the one that suits your needs. - -- [Installations from source][source]: You need to install Redis and Sentinel - yourself. Use the [Redis HA installation from source](redis_source.md) guide. -- [Omnibus package Community Edition (CE) package][ce]: Redis is bundled, so you - can use the package with only the Redis service enabled (works for both master - and slave setups). To install and configure Sentinel, you can use the - [Redis HA installation from source](redis_source.md) guide. -- [Omnibus package Enterprise Edition (EE) package][ee]: Both Redis and Sentinel - are bundled, so you can use the EE package to setup the whole Redis HA - infrastructure (master, slave and Sentinel). - -Note that even if you have installed GitLab using the Omnibus GitLab packages -(both CE and EE), you can still use an -[external Redis server](#using-a-non-omnibus-external-redis-server). - ### Redis setup overview You must have at least `3` Redis servers: `1` Master, `2` Slaves, and they @@ -168,7 +148,7 @@ simultaneously down. Please note that there are different requirements for Sentinel nodes. If you host them in the same Redis machines, you may need to take that restrictions into consideration when calculating the amount of -nodes to be provisioned. See [Sentinel setup](#sentinel-setup) +nodes to be provisioned. See [Sentinel setup overview](#sentinel-setup-overview) documentation for more information. All Redis nodes should be configured the same way and with similar server specs, as @@ -245,6 +225,31 @@ the official documentation: the slaves will be reconfigured by the Sentinels anyway, but not with the exact parallel-syncs progression as specified. +### Available configuration setups + +Based on your infrastructure setup and how you have installed GitLab, there are +multiple ways to configure Redis HA. Omnibus GitLab packages have Redis and/or +Redis Sentinel bundled with them so you only need to focus on configuration. +Pick the one that suits your needs. + +- [Installations from source][source]: You need to install Redis and Sentinel + yourself. Use the [Redis HA installation from source](redis_source.md) + documentation. +- [Omnibus GitLab **Community Edition** (CE) package][ce]: Redis is bundled, so you + can use the package with only the Redis service enabled as described in steps + 1 and 2 of this document (works for both master and slave setups). To install + and configure Sentinel, jump directly to the Sentinel section in the + [Redis HA installation from source](redis_source.md#step-3-configuring-the-redis-sentinel-instances) documentation. +- [Omnibus GitLab **Enterprise Edition** (EE) package][ee]: Both Redis and Sentinel + are bundled in the package, so you can use the EE package to setup the whole + Redis HA infrastructure (master, slave and Sentinel) which is described in + this document. +- If you have installed GitLab using the Omnibus GitLab packages (CE or EE), + but you want to use your own external Redis server, follow steps 1-3 in the + [Redis HA installation from source](redis_source.md) documentation, then go + straight to step 4 in this guide to + [set up the GitLab application](#step-4-configuring-the-gitlab-application). + ## Configuring Redis HA This is the section where we install and setup the new Redis instances. @@ -284,7 +289,7 @@ The prerequisites for a HA Redis setup are the following: ``` 1. [Download/install](https://about.gitlab.com/installation) the Omnibus GitLab - package using **steps 1 and 2** from the GitLab downloads page. + package you want using **steps 1 and 2** from the GitLab downloads page. - Make sure you select the correct Omnibus package, with the same version and type (Community, Enterprise editions) of your current install. - Do not complete any other steps on the download page. @@ -313,7 +318,7 @@ The prerequisites for a HA Redis setup are the following: 1. To prevent database migrations from running on upgrade, run: ``` - touch /etc/gitlab/skip-auto-migrations + sudo touch /etc/gitlab/skip-auto-migrations ``` Only the primary GitLab application server should handle migrations. @@ -329,7 +334,7 @@ The prerequisites for a HA Redis setup are the following: ``` 1. [Download/install](https://about.gitlab.com/installation) the Omnibus GitLab - package using **steps 1 and 2** from the GitLab downloads page. + package you want using **steps 1 and 2** from the GitLab downloads page. - Make sure you select the correct Omnibus package, with the same version and type (Community, Enterprise editions) of your current install. - Do not complete any other steps on the download page. @@ -367,7 +372,7 @@ The prerequisites for a HA Redis setup are the following: 1. To prevent database migrations from running on upgrade, run: ``` - touch /etc/gitlab/skip-auto-migrations + sudo touch /etc/gitlab/skip-auto-migrations ``` Only the primary GitLab application server should handle migrations. @@ -495,7 +500,7 @@ multiple machines with the Sentinel daemon. 1. To prevent database migrations from running on upgrade, run: ``` - touch /etc/gitlab/skip-auto-migrations + sudo touch /etc/gitlab/skip-auto-migrations ``` Only the primary GitLab application server should handle migrations. @@ -560,7 +565,7 @@ redis['enable'] = false If you fail to replicate first, you may loose data (unprocessed background jobs). -## Example of a minimal configuration with 1 master, 2 slaves and 3 sentinels +## Example of a minimal configuration with 1 master, 2 slaves and 3 Sentinels In this example we consider that all servers have an internal network interface with IPs in the `10.0.0.x` range, and that they can connect -- cgit v1.2.1 From 178d5ae9719b0edf6a4873502422e3302d264528 Mon Sep 17 00:00:00 2001 From: Achilleas Pipinellis Date: Wed, 16 Nov 2016 10:21:44 +0100 Subject: Rearrange Redis HA source installations sections --- .../high_availability/redis_source.md | 57 +++++++++++++--------- 1 file changed, 33 insertions(+), 24 deletions(-) diff --git a/doc/administration/high_availability/redis_source.md b/doc/administration/high_availability/redis_source.md index 20630b070c0..a420d44f451 100644 --- a/doc/administration/high_availability/redis_source.md +++ b/doc/administration/high_availability/redis_source.md @@ -1,26 +1,34 @@ -# Configuring Redis for GitLab HA (Source Install) +# Configuring non-Omnibus Redis for GitLab HA -We highly recommend that you use Omnibus GitLab packages, as we can optimize -required packages specifically for GitLab, and we will take care of upgrading +This is the documentation for configuring a Highly Available Redis setup when +you have installed Redis all by yourself and not using the bundled one that +comes with the Omnibus packages. + +We cannot stress enough the importance of reading the +[Overview section](redis.md#overview) of the Omnibus Redis HA as it provides +some invaluable information to the configuration of Redis. Please proceed to +read it before going forward with this guide. + +We also highly recommend that you use the Omnibus GitLab packages, as we +optimize them specifically for GitLab, and we will take care of upgrading Redis to the latest supported version. -If you are building packages for a specific distro, or trying to build some -internal automation, you can check this documentation to learn about the -minimal setup, required changes, etc. +If you're not sure whether this guide is for you, please refer to +[Available configuration setups](redis.md#available-configuration-setups) in +the Omnibus Redis HA documentation. -If you want to see the documentation for Omnibus GitLab Install, please -[read it here](redis.md). +--- **Table of Contents** -- [Configure your own Redis server](#configure-your-own-redis-server) - - [Configuring Master Redis instance](#configuring-master-redis-instance) - - [Configuring Slave Redis instances](#configuring-slave-redis-instances) - - [Configuring Redis Sentinel instances](#configuring-redis-sentinel-instances) -- [GitLab setup](#gitlab-setup) -- [Example configurations](#example-configurations) +- [Configuring your own Redis server](#configuring-your-own-redis-server) + - [Step 1. Configuring the master Redis instance](#step-1-configuring-the-master-redis-instance) + - [Step 2. Configuring the slave Redis instances](#step-2-configuring-the-slave-redis-instances) + - [Step 3. Configuring the Redis Sentinel instances](#step-3-configuring-the-redis-sentinel-instances) + - [Step 4. Configuring the GitLab application](#step-4-configuring-the-gitlab-application) +- [Example of minimal configuration with 1 master, 2 slaves and 3 Sentinels](#example-of-minimal-configuration-with-1-master-2-slaves-and-3-sentinels) - [Configuring Redis Master](#configuring-redis-master) - [Configuring Redis Slaves](#configuring-redis-slaves) - [Configuring Redis Sentinel](#configuring-redis-sentinel) @@ -28,7 +36,7 @@ If you want to see the documentation for Omnibus GitLab Install, please -## Configure your own Redis server +## Configuring your own Redis server Redis server must be configured to use TCP connection instead of socket, and since Redis `3.2`, you must define a password to receive external @@ -41,8 +49,7 @@ To configure Redis to use TCP connection you need to define both `bind` and `port`. You can bind to all interfaces (`0.0.0.0`) or specify the IP of the desired interface (for ex. one from an internal network). - -### Configuring Master Redis instance +### Step 1. Configuring the master Redis instance You need to make the following changes in `redis.conf`: @@ -61,7 +68,7 @@ You need to make the following changes in `redis.conf`: See [example configuration](#configuring-redis-master) below. -### Configuring Slave Redis instances +### Step 2. Configuring the slave Redis instances 1. Follow same instructions for Redis Master @@ -71,7 +78,7 @@ See [example configuration](#configuring-redis-master) below. See [example configuration](#configuring-redis-slaves) below. -### Configuring Redis Sentinel instances +### Step 3. Configuring the Redis Sentinel instances Sentinel is a special type of Redis server. It inherits most of the basic configuration options you can define in `redis.conf`, with specific ones @@ -122,7 +129,7 @@ And the sentinel specific ones: See [example configuration](#configuring-redis-sentinel) below. -## GitLab setup +### Step 4. Configuring the GitLab application You can enable or disable Sentinel support at any time in new or existing installations. From the GitLab application perspective, all it requires is @@ -141,7 +148,7 @@ which ideally should not have Redis or Sentinels in the same machine for a HA se 1. Restart GitLab for the changes to take effect. -## Example configurations +## Example of minimal configuration with 1 master, 2 slaves and 3 Sentinels In this example we consider that all servers have an internal network interface with IPs in the `10.0.0.x` range, and that they can connect @@ -247,12 +254,13 @@ sentinel failover_timeout 30000 ## Troubleshooting -We have a more detailed [Troubleshooting](redis.md#troubleshooting) explained in the documentation for Omnibus -Install. Here we will list only the things that are specific to a **Source** install. +We have a more detailed [Troubleshooting](redis.md#troubleshooting) explained +in the documentation for Omnibus GitLab installations. Here we will list only +the things that are specific to a source installation. If you get an error in GitLab like: `Redis::CannotConnectError: No sentinels available.`, there may be something wrong with your configuration files or it can be related -to [this issue][gh-531]. +to [this upstream issue][gh-531]. It's a bit non-intuitive the way you have to config `resque.yml` and `sentinel.conf`, otherwise `redis-rb` will not work properly. @@ -287,3 +295,4 @@ production: When in doubt, please read [Redis Sentinel documentation](http://redis.io/topics/sentinel) [gh-531]: https://github.com/redis/redis-rb/issues/531 +[downloads]: https://about.gitlab.com/downloads -- cgit v1.2.1 From c95b88e649789e6dc27e4b97856a8bdf069312cc Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Wed, 16 Nov 2016 10:36:33 +0100 Subject: Improve readability in project model for environments --- app/models/project.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/models/project.rb b/app/models/project.rb index 1aad749c77e..d885419e0a1 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -1289,12 +1289,12 @@ class Project < ActiveRecord::Base end def environments_for(ref, commit: nil, with_tags: false) - environments_query = with_tags ? 'ref = ? OR tag IS TRUE' : 'ref = ?' + deployments_query = with_tags ? 'ref = ? OR tag IS TRUE' : 'ref = ?' environment_ids = deployments + .where(deployments_query, ref.to_s) .group(:environment_id) .select(:environment_id) - .where(environments_query, ref.to_s) envs = environments.available.where(id: environment_ids) -- cgit v1.2.1 From 00d9d7678b9df3a25c4f4e8f210c9d17a798c9cd Mon Sep 17 00:00:00 2001 From: Ben Bodenmiller Date: Wed, 16 Nov 2016 01:49:45 -0800 Subject: fix "Without projects" filter --- CHANGELOG.md | 1 + app/models/user.rb | 2 +- spec/models/user_spec.rb | 22 ++++++++++++++++++++++ 3 files changed, 24 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1893d5626df..c35175b4bbc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -73,6 +73,7 @@ entry. - Fix applying GitHub-imported labels when importing job is interrupted - Allow to search for user by secondary email address in the admin interface(/admin/users) !7115 (YarNayar) - Updated commit SHA styling on the branches page. +- Fix "Without projects" filter. !6611 (Ben Bodenmiller) - Fix 404 when visit /projects page ## 8.13.5 (2016-11-08) diff --git a/app/models/user.rb b/app/models/user.rb index 3813df6684e..5a2b232c4ed 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -173,7 +173,7 @@ class User < ActiveRecord::Base scope :external, -> { where(external: true) } scope :active, -> { with_state(:active) } scope :not_in_project, ->(project) { project.users.present? ? where("id not in (:ids)", ids: project.users.map(&:id) ) : all } - scope :without_projects, -> { where('id NOT IN (SELECT DISTINCT(user_id) FROM members)') } + scope :without_projects, -> { where('id NOT IN (SELECT DISTINCT(user_id) FROM members WHERE user_id IS NOT NULL AND requested_at IS NULL)') } scope :todo_authors, ->(user_id, state) { where(id: Todo.where(user_id: user_id, state: state).select(:author_id)) } def self.with_two_factor diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index 3b152e15b61..0dd63017780 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -490,6 +490,28 @@ describe User, models: true do end end + describe '.without_projects' do + let!(:project) { create(:empty_project, :public) } + let!(:user) { create(:user) } + let!(:user_without_project) { create(:user) } + let!(:user_without_project2) { create(:user) } + + before do + # add user to project + project.team << [user, :master] + + # create invite to projet + create(:project_member, :developer, project: project, invite_token: '1234', invite_email: 'inviteduser1@example.com') + + # create request to join project + project.request_access(user_without_project2) + end + + it { expect(User.without_projects).not_to include user } + it { expect(User.without_projects).to include user_without_project } + it { expect(User.without_projects).to include user_without_project2 } + end + describe '.not_in_project' do before do User.delete_all -- cgit v1.2.1 From e49229aa04144e0ce7672ad0a34a24da9b09027e Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Wed, 16 Nov 2016 10:54:20 +0100 Subject: Refactor methods for environments in project model --- app/models/environment.rb | 2 +- app/models/project.rb | 17 ++++++++++++----- app/services/ci/stop_environments_service.rb | 3 ++- 3 files changed, 15 insertions(+), 7 deletions(-) diff --git a/app/models/environment.rb b/app/models/environment.rb index fe3df473d73..8e4de640953 100644 --- a/app/models/environment.rb +++ b/app/models/environment.rb @@ -37,7 +37,7 @@ class Environment < ActiveRecord::Base state :stopped end - def recently_updated_on?(ref) + def recently_updated_on_branch?(ref) ref.to_s == last_deployment.ref end diff --git a/app/models/project.rb b/app/models/project.rb index d885419e0a1..297d7721abe 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -1296,12 +1296,19 @@ class Project < ActiveRecord::Base .group(:environment_id) .select(:environment_id) - envs = environments.available.where(id: environment_ids) + environments_found = environments.available + .where(id: environment_ids).to_a - if commit - envs.select { |environment| env.includes_commit?(commit) } - else - envs.select { |environment| env.recently_updated_on?(ref) } + return environments_found unless commit + + environments_found.select do |environment| + environment.includes_commit?(commit) + end + end + + def environments_recently_updated_on_branch(branch) + environments_for(branch).select do |environment| + environment.recently_updated_on_branch?(branch) end end diff --git a/app/services/ci/stop_environments_service.rb b/app/services/ci/stop_environments_service.rb index b7a49a424ce..0ffe73611c4 100644 --- a/app/services/ci/stop_environments_service.rb +++ b/app/services/ci/stop_environments_service.rb @@ -27,7 +27,8 @@ module Ci end def environments - @environments ||= project.environments_for(@ref) + @environments ||= project + .environments_recently_updated_on_branch(@ref) end end end -- cgit v1.2.1 From d9d69d7ba7dd660c9b3e0ab30b58891a8b760328 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Wed, 16 Nov 2016 11:26:36 +0100 Subject: Add specs for environments recently updated on branch --- app/models/environment.rb | 2 +- spec/models/environment_spec.rb | 21 +++++++++++++++++++++ spec/models/project_spec.rb | 42 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 64 insertions(+), 1 deletion(-) diff --git a/app/models/environment.rb b/app/models/environment.rb index 8e4de640953..5278efd71d2 100644 --- a/app/models/environment.rb +++ b/app/models/environment.rb @@ -38,7 +38,7 @@ class Environment < ActiveRecord::Base end def recently_updated_on_branch?(ref) - ref.to_s == last_deployment.ref + ref.to_s == last_deployment.try(:ref) end def last_deployment diff --git a/spec/models/environment_spec.rb b/spec/models/environment_spec.rb index a94e6d0165f..60bbe3fcd72 100644 --- a/spec/models/environment_spec.rb +++ b/spec/models/environment_spec.rb @@ -166,4 +166,25 @@ describe Environment, models: true do end end end + + describe 'recently_updated_on_branch?' do + subject { environment.recently_updated_on_branch?('feature') } + + context 'when last deployment to environment is the most recent one' do + before do + create(:deployment, environment: environment, ref: 'feature') + end + + it { is_expected.to be true } + end + + context 'when last deployment to environment is not the most recent' do + before do + create(:deployment, environment: environment, ref: 'feature') + create(:deployment, environment: environment, ref: 'master') + end + + it { is_expected.to be false } + end + end end diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index d835eac34c2..dd30ec123a7 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -1688,6 +1688,48 @@ describe Project, models: true do end end + describe '#environments_recently_updated_on_branch' do + let(:project) { create(:project) } + let(:environment) { create(:environment, project: project) } + + context 'when last deployment to environment is the most recent one' do + before do + create(:deployment, environment: environment, ref: 'feature') + end + + it 'finds recently updated environment' do + expect(project.environments_recently_updated_on_branch('feature')) + .to contain_exactly(environment) + end + end + + context 'when last deployment to environment is not the most recent' do + before do + create(:deployment, environment: environment, ref: 'feature') + create(:deployment, environment: environment, ref: 'master') + end + + it 'does not find environment' do + expect(project.environments_recently_updated_on_branch('feature')) + .to be_empty + end + end + + context 'when there are two environments that deploy to the same branch' do + let(:second_environment) { create(:environment, project: project) } + + before do + create(:deployment, environment: environment, ref: 'feature') + create(:deployment, environment: second_environment, ref: 'feature') + end + + it 'finds both environments' do + expect(project.environments_recently_updated_on_branch('feature')) + .to contain_exactly(environment, second_environment) + end + end + end + def enable_lfs allow(Gitlab.config.lfs).to receive(:enabled).and_return(true) end -- cgit v1.2.1 From f913170e2f76ef44800f0272cb7fb40b9d6709ee Mon Sep 17 00:00:00 2001 From: Adam Niedzielski Date: Wed, 16 Nov 2016 11:33:20 +0100 Subject: Do not create a MergeRequestDiff record when source branch is deleted In order to have a valid MergeRequestDiff record when need head_commit_sha. When a source branch is deleted head_commit_sha is nil. This causes an exception in merge request "Changes" tab. --- app/services/merge_requests/refresh_service.rb | 21 ++++++++++++--------- ...x-merge-request-screen-deleted-source-branch.yml | 4 ++++ .../services/merge_requests/refresh_service_spec.rb | 10 ++++++++++ 3 files changed, 26 insertions(+), 9 deletions(-) create mode 100644 changelogs/unreleased/fix-merge-request-screen-deleted-source-branch.yml diff --git a/app/services/merge_requests/refresh_service.rb b/app/services/merge_requests/refresh_service.rb index 22596b4014a..4a7e6930842 100644 --- a/app/services/merge_requests/refresh_service.rb +++ b/app/services/merge_requests/refresh_service.rb @@ -60,15 +60,7 @@ module MergeRequests merge_requests = filter_merge_requests(merge_requests) merge_requests.each do |merge_request| - if merge_request.source_branch == @branch_name || force_push? - merge_request.reload_diff - else - mr_commit_ids = merge_request.commits.map(&:id) - push_commit_ids = @commits.map(&:id) - matches = mr_commit_ids & push_commit_ids - merge_request.reload_diff if matches.any? - end - + reload_diff(merge_request) unless branch_removed? merge_request.mark_as_unchecked end end @@ -173,5 +165,16 @@ module MergeRequests def branch_removed? Gitlab::Git.blank_ref?(@newrev) end + + def reload_diff(merge_request) + if merge_request.source_branch == @branch_name || force_push? + merge_request.reload_diff + else + mr_commit_ids = merge_request.commits.map(&:id) + push_commit_ids = @commits.map(&:id) + matches = mr_commit_ids & push_commit_ids + merge_request.reload_diff if matches.any? + end + end end end diff --git a/changelogs/unreleased/fix-merge-request-screen-deleted-source-branch.yml b/changelogs/unreleased/fix-merge-request-screen-deleted-source-branch.yml new file mode 100644 index 00000000000..a6bee989f6d --- /dev/null +++ b/changelogs/unreleased/fix-merge-request-screen-deleted-source-branch.yml @@ -0,0 +1,4 @@ +--- +title: Do not create a MergeRequestDiff record when source branch is deleted +merge_request: 7481 +author: diff --git a/spec/services/merge_requests/refresh_service_spec.rb b/spec/services/merge_requests/refresh_service_spec.rb index e515bc9f89c..0220f7e1db2 100644 --- a/spec/services/merge_requests/refresh_service_spec.rb +++ b/spec/services/merge_requests/refresh_service_spec.rb @@ -227,6 +227,16 @@ describe MergeRequests::RefreshService, services: true do end end + context 'when the source branch is deleted' do + it 'does not create a MergeRequestDiff record' do + refresh_service = service.new(@project, @user) + + expect do + refresh_service.execute(@oldrev, Gitlab::Git::BLANK_SHA, 'refs/heads/master') + end.not_to change { MergeRequestDiff.count } + end + end + def reload_mrs @merge_request.reload @fork_merge_request.reload -- cgit v1.2.1 From dc54239683fbe77b13216deb6a0728563a0a89d0 Mon Sep 17 00:00:00 2001 From: Achilleas Pipinellis Date: Wed, 16 Nov 2016 11:42:12 +0100 Subject: Merge examples of Redis master/slave + Sentinels --- doc/administration/high_availability/redis.md | 144 +++++++------------------- 1 file changed, 37 insertions(+), 107 deletions(-) diff --git a/doc/administration/high_availability/redis.md b/doc/administration/high_availability/redis.md index 7ea49a2c22a..02ca4999cd4 100644 --- a/doc/administration/high_availability/redis.md +++ b/doc/administration/high_availability/redis.md @@ -29,22 +29,23 @@ Omnibus GitLab packages. **Table of Contents** - [Overview](#overview) - - [Prerequisites](#prerequisites) - [High Availability with Sentinel](#high-availability-with-sentinel) - [Recommended setup](#recommended-setup) - - [Available configuration setups](#available-configuration-setups) - [Redis setup overview](#redis-setup-overview) - [Sentinel setup overview](#sentinel-setup-overview) + - [Available configuration setups](#available-configuration-setups) - [Configuring Redis HA](#configuring-redis-ha) - - [Step 1. Configuring the Master Redis instance](#step-1-configuring-the-master-redis-instance) - - [Step 2. Configuring the Slave Redis instances](#step-2-configuring-the-slave-redis-instances) + - [Prerequisites](#prerequisites) + - [Step 1. Configuring the master Redis instance](#step-1-configuring-the-master-redis-instance) + - [Step 2. Configuring the slave Redis instances](#step-2-configuring-the-slave-redis-instances) - [Step 3. Configuring the Redis Sentinel instances](#step-3-configuring-the-redis-sentinel-instances) - [Step 4. Configuring the GitLab application](#step-4-configuring-the-gitlab-application) - [Switching from an existing single-machine installation to Redis HA](#switching-from-an-existing-single-machine-installation-to-redis-ha) - [Example of a minimal configuration with 1 master, 2 slaves and 3 Sentinels](#example-of-a-minimal-configuration-with-1-master-2-slaves-and-3-sentinels) - - [Configuration for Redis master](#configuration-for-redis-master) - - [Configuration for Redis slaves](#configuration-for-redis-slaves) - - [Configuration for Sentinels](#configuration-for-sentinels) + - [Example configuration for Redis master and Sentinel 1](#example-configuration-for-redis-master-and-sentinel-1) + - [Example configuration for Redis slave 1 and Sentinel 2](#example-configuration-for-redis-slave-1-and-sentinel-2) + - [Example configuration for Redis slave 2 and Sentinel 3](#example-configuration-for-redis-slave-2-and-sentinel-3) + - [Example configuration for the GitLab application](#example-configuration-for-the-gitlab-application) - [Advanced configuration](#advanced-configuration) - [Control running services](#control-running-services) - [Troubleshooting](#troubleshooting) @@ -277,7 +278,7 @@ The prerequisites for a HA Redis setup are the following: change the default ones). 1. The server that hosts the GitLab application must be able to access the Redis nodes. -1. Protect the nodes from access from external networks (Internet), using +1. Protect the nodes from access from external networks ([Internet][it]), using firewall. ### Step 1. Configuring the master Redis instance @@ -527,13 +528,13 @@ which ideally should not have Redis or Sentinels on it for a HA setup. 1. Edit `/etc/gitlab/gitlab.rb` and add/change the following lines: ``` - # Must be the same in every sentinel node + ## Must be the same in every sentinel node redis['master_name'] = 'gitlab-redis' - # The same password for Redis authentication you set up for the master node. + ## The same password for Redis authentication you set up for the master node. redis['password'] = 'redis-password-goes-here' - # A list of sentinels with `host` and `port` + ## A list of sentinels with `host` and `port` gitlab_rails['redis_sentinels'] = [ {'host' => '10.0.0.1', 'port' => 26379}, {'host' => '10.0.0.2', 'port' => 26379}, @@ -567,6 +568,11 @@ If you fail to replicate first, you may loose data (unprocessed background jobs) ## Example of a minimal configuration with 1 master, 2 slaves and 3 Sentinels +>**Note:** +Redis Sentinel is bundled with Omnibus GitLab Enterprise Edition only. For +different setups, read the +[available configuration setups](#available-configuration-setups) section. + In this example we consider that all servers have an internal network interface with IPs in the `10.0.0.x` range, and that they can connect to each other using these IPs. @@ -595,142 +601,70 @@ The same thing will happen with `sentinel.conf` that will be overridden after th initial execution, after any new sentinel node starts watching the **Master**, or a failover promotes a different **Master** node. -### Example configuration for Redis master - -**Example configation for Redis Master:** +### Example configuration for Redis master and Sentinel 1 In `/etc/gitlab/gitlab.rb`: ```ruby redis_master_role['enable'] = true - +redis_sentinel_role['enable'] = true redis['bind'] = '10.0.0.1' redis['port'] = 6379 redis['password'] = 'redis-password-goes-here' -``` - -[Reconfigure Omnibus GitLab][reconfigure] for the changes to take effect. - -### Example configuration for Redis slaves - -**Example configuration for Slave 1:** - -In `/etc/gitlab/gitlab.rb`: - -```ruby -redis_slave_role['enable'] = true - -redis['bind'] = '10.0.0.2' -redis['port'] = 6379 -redis['password'] = 'redis-password-goes-here' -redis['master_password'] = 'redis-password-goes-here' - -redis['master_ip'] = '10.0.0.1' # IP of master Redis server -#redis['master_port'] = 6379 # Port of master Redis server, uncomment to change to non default -``` - -[Reconfigure Omnibus GitLab][reconfigure] for the changes to take effect. - -**Example configuration for Slave 2:** - -In `/etc/gitlab/gitlab.rb`: - -```ruby -redis_slave_role['enable'] = true - -redis['bind'] = '10.0.0.3' -redis['port'] = 6379 -redis['password'] = 'redis-password-goes-here' -redis['master_password'] = 'redis-password-goes-here' - -redis['master_ip'] = '10.0.0.1' # IP of master Redis server -#redis['master_port'] = 6379 # Port of master Redis server, uncomment to change to non default -``` - -[Reconfigure Omnibus GitLab][reconfigure] for the changes to take effect. - -### Example configuration for Sentinels - ->**Note:** -Redis Sentinel is bundled with Omnibus GitLab Enterprise Edition only. For the -Omnibus Community Edition and installations from source, follow the -[Redis HA source install](redis_source.md) guide. - -Please note that some of the variables are already configured previously -as they are required for Redis replication. - -**Example configuration for Sentinel 1:** - -In `/etc/gitlab/gitlab.rb`: - -```ruby -redis_sentinel_role['enable'] = true - redis['master_name'] = 'gitlab-redis' # must be the same in every sentinel node redis['master_password'] = 'redis-password-goes-here' # the same value defined in redis['password'] in the master instance redis['master_ip'] = '10.0.0.1' # ip of the initial master redis instance #redis['master_port'] = 6379 # port of the initial master redis instance, uncomment to change to non default - -## Configure Sentinel sentinel['bind'] = '10.0.0.1' # sentinel['port'] = 26379 # uncomment to change default port sentinel['quorum'] = 2 - -## Consider unresponsive server down after x amount of ms. # sentinel['down_after_milliseconds'] = 10000 - # sentinel['failover_timeout'] = 60000 ``` [Reconfigure Omnibus GitLab][reconfigure] for the changes to take effect. -**Example configuration for Sentinel 2:** +### Example configuration for Redis slave 1 and Sentinel 2 In `/etc/gitlab/gitlab.rb`: ```ruby +redis_slave_role['enable'] = true redis_sentinel_role['enable'] = true - +redis['bind'] = '10.0.0.2' +redis['port'] = 6379 +redis['password'] = 'redis-password-goes-here' +redis['master_password'] = 'redis-password-goes-here' +redis['master_ip'] = '10.0.0.1' # IP of master Redis server +#redis['master_port'] = 6379 # Port of master Redis server, uncomment to change to non default redis['master_name'] = 'gitlab-redis' # must be the same in every sentinel node -redis['master_password'] = 'redis-password-goes-here' # the same value defined in redis['password'] in the master instance -redis['master_ip'] = '10.0.0.1' # ip of the initial master redis instance -#redis['master_port'] = 6379 # port of the initial master redis instance, uncomment to change to non default - -## Configure Sentinel sentinel['bind'] = '10.0.0.2' # sentinel['port'] = 26379 # uncomment to change default port - sentinel['quorum'] = 2 - -## Consider unresponsive server down after x amount of ms. # sentinel['down_after_milliseconds'] = 10000 - # sentinel['failover_timeout'] = 60000 ``` [Reconfigure Omnibus GitLab][reconfigure] for the changes to take effect. -**Example configuration for Sentinel 3:** +### Example configuration for Redis slave 2 and Sentinel 3 In `/etc/gitlab/gitlab.rb`: ```ruby +redis_slave_role['enable'] = true redis_sentinel_role['enable'] = true - +redis['bind'] = '10.0.0.3' +redis['port'] = 6379 +redis['password'] = 'redis-password-goes-here' +redis['master_password'] = 'redis-password-goes-here' +redis['master_ip'] = '10.0.0.1' # IP of master Redis server +#redis['master_port'] = 6379 # Port of master Redis server, uncomment to change to non default redis['master_name'] = 'gitlab-redis' # must be the same in every sentinel node -redis['master_password'] = 'redis-password-goes-here' # the same value defined in redis['password'] in the master instance -redis['master_ip'] = '10.0.0.1' # ip of the initial master redis instance -#redis['master_port'] = 6379 # port of the initial master redis instance, uncomment to change to non default - -## Configure Sentinel sentinel['bind'] = '10.0.0.3' # sentinel['port'] = 26379 # uncomment to change default port - sentinel['quorum'] = 2 - -## Consider unresponsive server down after x amount of ms. # sentinel['down_after_milliseconds'] = 10000 - # sentinel['failover_timeout'] = 60000 ``` @@ -740,14 +674,9 @@ sentinel['quorum'] = 2 In `/etc/gitlab/gitlab.rb`: -``` -# Must be the same in every sentinel node +```ruby redis['master_name'] = 'gitlab-redis' - -# The same password for Redis authentication you set up for the master node. redis['password'] = 'redis-password-goes-here' - -# A list of sentinels with `host` and `port` gitlab_rails['redis_sentinels'] = [ {'host' => '10.0.0.1', 'port' => 26379}, {'host' => '10.0.0.2', 'port' => 26379}, @@ -970,3 +899,4 @@ Read more on high-availability configuration: [source]: ../../install/installation.md [ce]: https://about.gitlab.com/downloads [ee]: https://about.gitlab.com/downloads-ee +[it]: https://gitlab.com/gitlab-org/gitlab-ce/uploads/c4cc8cd353604bd80315f9384035ff9e/The_Internet_IT_Crowd.png -- cgit v1.2.1 From b6447f3027b7439e33bb8864992f5b4e60ca5cc5 Mon Sep 17 00:00:00 2001 From: Achilleas Pipinellis Date: Wed, 16 Nov 2016 11:51:28 +0100 Subject: Add steps to Redis HA source installation --- .../high_availability/redis_source.md | 196 +++++++++++++-------- 1 file changed, 123 insertions(+), 73 deletions(-) diff --git a/doc/administration/high_availability/redis_source.md b/doc/administration/high_availability/redis_source.md index a420d44f451..1660e26c784 100644 --- a/doc/administration/high_availability/redis_source.md +++ b/doc/administration/high_availability/redis_source.md @@ -38,45 +38,79 @@ the Omnibus Redis HA documentation. ## Configuring your own Redis server -Redis server must be configured to use TCP connection instead of socket, -and since Redis `3.2`, you must define a password to receive external -connections (`requirepass`). +This is the section where we install and setup the new Redis instances. -You will also need to define equal password for slave password definition -(`masterauth`), in the same instance, if you are using Redis with Sentinel. +### Prerequisites -To configure Redis to use TCP connection you need to define both -`bind` and `port`. You can bind to all interfaces (`0.0.0.0`) or specify the -IP of the desired interface (for ex. one from an internal network). +- All Redis servers in this guide must be configured to use a TCP connection + instead of a socket. To configure Redis to use TCP connections you need to + define both `bind` and `port` in the Redis config file. You can bind to all + interfaces (`0.0.0.0`) or specify the IP of the desired interface + (e.g., one from an internal network). +- Since Redis 3.2, you must define a password to receive external connections + (`requirepass`). +- If you are using Redis with Sentinel, you will also need to define the same + password for the slave password definition (`masterauth`) in the same instance. + +In addition, read the prerequisites as described in the +[Omnibus Redis HA document](redis.md#prerequisites) since they provide some +valuable information for the general setup. ### Step 1. Configuring the master Redis instance -You need to make the following changes in `redis.conf`: +Assuming that the Redis master instance IP is `10.0.0.1`: -1. Define a `bind` address pointing to a local IP that your other machines - can reach you. If you really need to bind to an external accessible IP, make - sure you add extra firewall rules to prevent unauthorized access: +1. [Install Redis](../../install/installation.md#6-redis) +1. Edit `/etc/redis/redis.conf`: -1. Define a `port` to force redis to listen on TCP so other machines can - connect to it (default port is `6379`). + ```conf + ## Define a `bind` address pointing to a local IP that your other machines + ## can reach you. If you really need to bind to an external accessible IP, make + ## sure you add extra firewall rules to prevent unauthorized access: + bind 10.0.0.1 -1. Set up password authentication (use the same password in all nodes). - The password should be defined equal for both `requirepass` and `masterauth` - when setting up Redis to use with Sentinel. + ## Define a `port` to force redis to listen on TCP so other machines can + ## connect to it (default port is `6379`). + port 6379 -1. Restart the Redis services for the changes to take effect. + ## Set up password authentication (use the same password in all nodes). + ## The password should be defined equal for both `requirepass` and `masterauth` + ## when setting up Redis to use with Sentinel. + requirepass redis-password-goes-here + masterauth redis-password-goes-here + ``` -See [example configuration](#configuring-redis-master) below. +1. Restart the Redis service for the changes to take effect. ### Step 2. Configuring the slave Redis instances -1. Follow same instructions for Redis Master +Assuming that the Redis slave instance IP is `10.0.0.2`: + +1. [Install Redis](../../install/installation.md#6-redis) +1. Edit `/etc/redis/redis.conf`: + + ```conf + ## Define a `bind` address pointing to a local IP that your other machines + ## can reach you. If you really need to bind to an external accessible IP, make + ## sure you add extra firewall rules to prevent unauthorized access: + bind 10.0.0.2 + + ## Define a `port` to force redis to listen on TCP so other machines can + ## connect to it (default port is `6379`). + port 6379 -1. Define `slaveof` pointing to the Redis master instance with **IP** and **port**. + ## Set up password authentication (use the same password in all nodes). + ## The password should be defined equal for both `requirepass` and `masterauth` + ## when setting up Redis to use with Sentinel. + requirepass redis-password-goes-here + masterauth redis-password-goes-here -1. Restart the Redis services for the changes to take effect. + ## Define `slaveof` pointing to the Redis master instance with IP and port. + slaveof 10.0.0.1 6379 + ``` -See [example configuration](#configuring-redis-slaves) below. +1. Restart the Redis service for the changes to take effect. +1. Go through the steps again for all the other slave nodes. ### Step 3. Configuring the Redis Sentinel instances @@ -84,50 +118,64 @@ Sentinel is a special type of Redis server. It inherits most of the basic configuration options you can define in `redis.conf`, with specific ones starting with `sentinel` prefix. -You will need to define the initial configs to enable connectivity: - -1. Define a `bind` address pointing to a local IP that your other machines - can reach you. If you really need to bind to an external accessible IP, make - sure you add extra firewall rules to prevent unauthorized access: - -1. Define a `port` to force sentinel to listen on TCP so other machines can - connect to it (default port is `26379`). - -And the sentinel specific ones: - -1. Define with `sentinel auth-pass` the same shared password you have - defined for both Redis **Master** and **Slaves** instances. - -1. Define with `sentinel monitor` the **IP** and **port** of the Redis - **Master** node, and the **quorum** required to start a failover. - If you need more information to understand about quorum, please - read the detailed explanation in the [HA documentation for Omnibus Installs](redis.md). - -1. Define with `sentinel down-after-milliseconds` the amount in `ms` of time - that an unresponsive server will be considered down. - -1. Define a value for `sentinel failover_timeout` in `ms`. This has multiple - meanings: - - * The time needed to re-start a failover after a previous failover was - already tried against the same master by a given Sentinel, is two - times the failover timeout. - - * The time needed for a slave replicating to a wrong master according - to a Sentinel current configuration, to be forced to replicate - with the right master, is exactly the failover timeout (counting since - the moment a Sentinel detected the misconfiguration). - - * The time needed to cancel a failover that is already in progress but - did not produced any configuration change (SLAVEOF NO ONE yet not - acknowledged by the promoted slave). - - * The maximum time a failover in progress waits for all the slaves to be - reconfigured as slaves of the new master. However even after this time - the slaves will be reconfigured by the Sentinels anyway, but not with - the exact parallel-syncs progression as specified. - -See [example configuration](#configuring-redis-sentinel) below. +Assuming that the Redis Sentinel is installed on the same instance as Redis +master with IP `10.0.0.1` (some settings might overlap with the master): + +1. [Install Redis](../../install/installation.md#6-redis) +1. Edit `/etc/redis/redis.conf`: + + ```conf + ## Define a `bind` address pointing to a local IP that your other machines + ## can reach you. If you really need to bind to an external accessible IP, make + ## sure you add extra firewall rules to prevent unauthorized access: + bind 10.0.0.1 + + ## Define a `port` to force Sentinel to listen on TCP so other machines can + ## connect to it (default port is `6379`). + port 26379 + + ## Set up password authentication (use the same password in all nodes). + ## The password should be defined equal for both `requirepass` and `masterauth` + ## when setting up Redis to use with Sentinel. + requirepass redis-password-goes-here + masterauth redis-password-goes-here + + ## Define with `sentinel auth-pass` the same shared password you have + ## defined for both Redis master and slaves instances. + sentinel auth-pass gitlab-redis redis-password-goes-here + + ## Define with `sentinel monitor` the IP and port of the Redis + ## master node, and the quorum required to start a failover. + sentinel monitor gitlab-redis 10.0.0.1 6379 2 + + ## Define with `sentinel down-after-milliseconds` the time in `ms` + ## that an unresponsive server will be considered down. + sentinel down-after-milliseconds gitlab-redis 10000 + + ## Define a value for `sentinel failover_timeout` in `ms`. This has multiple + ## meanings: + ## + ## * The time needed to re-start a failover after a previous failover was + ## already tried against the same master by a given Sentinel, is two + ## times the failover timeout. + ## + ## * The time needed for a slave replicating to a wrong master according + ## to a Sentinel current configuration, to be forced to replicate + ## with the right master, is exactly the failover timeout (counting since + ## the moment a Sentinel detected the misconfiguration). + ## + ## * The time needed to cancel a failover that is already in progress but + ## did not produced any configuration change (SLAVEOF NO ONE yet not + ## acknowledged by the promoted slave). + ## + ## * The maximum time a failover in progress waits for all the slaves to be + ## reconfigured as slaves of the new master. However even after this time + ## the slaves will be reconfigured by the Sentinels anyway, but not with + ## the exact parallel-syncs progression as specified. + sentinel failover_timeout 30000 + ``` +1. Restart the Redis service for the changes to take effect. +1. Go through the steps again for all the other Sentinel nodes. ### Step 4. Configuring the GitLab application @@ -136,17 +184,17 @@ installations. From the GitLab application perspective, all it requires is the correct credentials for the Sentinel nodes. While it doesn't require a list of all Sentinel nodes, in case of a failure, -it needs to access at one of listed ones. +it needs to access at least one of listed ones. ->**Note:** The following steps should be performed in the [GitLab application server](gitlab.md) -which ideally should not have Redis or Sentinels in the same machine for a HA setup. +which ideally should not have Redis or Sentinels in the same machine for a HA +setup: 1. Edit `/home/git/gitlab/config/resque.yml` following the example in `/home/git/gitlab/config/resque.yml.example`, and uncomment the sentinels lines, pointing to the correct server credentials. -1. Restart GitLab for the changes to take effect. +1. [Restart GitLab][restart] for the changes to take effect. ## Example of minimal configuration with 1 master, 2 slaves and 3 Sentinels @@ -156,7 +204,7 @@ to each other using these IPs. In a real world usage, you would also setup firewall rules to prevent unauthorized access from other machines, and block traffic from the -outside (Internet). +outside ([Internet][it]). We will use the same `3` nodes with **Redis** + **Sentinel** topology discussed in the [Configuring Redis for GitLab HA](redis.md) documentation. @@ -165,7 +213,7 @@ Here is a list and description of each **machine** and the assigned **IP**: * `10.0.0.1`: Redis Master + Sentinel 1 * `10.0.0.2`: Redis Slave 1 + Sentinel 2 -* `10.0.0.2`: Redis Slave 2 + Sentinel 3 +* `10.0.0.3`: Redis Slave 2 + Sentinel 3 Please note that after the initial configuration, if a failover is initiated by the Sentinel nodes, the Redis nodes will be reconfigured and the **Master** @@ -296,3 +344,5 @@ When in doubt, please read [Redis Sentinel documentation](http://redis.io/topics [gh-531]: https://github.com/redis/redis-rb/issues/531 [downloads]: https://about.gitlab.com/downloads +[restart]: ../restart_gitlab.md#installations-from-source +[it]: https://gitlab.com/gitlab-org/gitlab-ce/uploads/c4cc8cd353604bd80315f9384035ff9e/The_Internet_IT_Crowd.png -- cgit v1.2.1 From 850405b42ef408785c6133ab7ba9c7f7303f18b6 Mon Sep 17 00:00:00 2001 From: Achilleas Pipinellis Date: Wed, 16 Nov 2016 12:04:33 +0100 Subject: Merge examples of Redis master/slave + Sentinels for source docs [ci skip] --- .../high_availability/redis_source.md | 173 +++++++++++++-------- 1 file changed, 106 insertions(+), 67 deletions(-) diff --git a/doc/administration/high_availability/redis_source.md b/doc/administration/high_availability/redis_source.md index 1660e26c784..e7a8f47355d 100644 --- a/doc/administration/high_availability/redis_source.md +++ b/doc/administration/high_availability/redis_source.md @@ -24,14 +24,16 @@ the Omnibus Redis HA documentation. **Table of Contents** - [Configuring your own Redis server](#configuring-your-own-redis-server) + - [Prerequisites](#prerequisites) - [Step 1. Configuring the master Redis instance](#step-1-configuring-the-master-redis-instance) - [Step 2. Configuring the slave Redis instances](#step-2-configuring-the-slave-redis-instances) - [Step 3. Configuring the Redis Sentinel instances](#step-3-configuring-the-redis-sentinel-instances) - [Step 4. Configuring the GitLab application](#step-4-configuring-the-gitlab-application) - [Example of minimal configuration with 1 master, 2 slaves and 3 Sentinels](#example-of-minimal-configuration-with-1-master-2-slaves-and-3-sentinels) - - [Configuring Redis Master](#configuring-redis-master) - - [Configuring Redis Slaves](#configuring-redis-slaves) - - [Configuring Redis Sentinel](#configuring-redis-sentinel) + - [Example configuration for Redis master and Sentinel 1](#example-configuration-for-redis-master-and-sentinel-1) + - [Example configuration for Redis slave 1 and Sentinel 2](#example-configuration-for-redis-slave-1-and-sentinel-2) + - [Example configuration for Redis slave 2 and Sentinel 3](#example-configuration-for-redis-slave-2-and-sentinel-3) + - [Example configuration of the GitLab application](#example-configuration-of-the-gitlab-application) - [Troubleshooting](#troubleshooting) @@ -121,8 +123,8 @@ starting with `sentinel` prefix. Assuming that the Redis Sentinel is installed on the same instance as Redis master with IP `10.0.0.1` (some settings might overlap with the master): -1. [Install Redis](../../install/installation.md#6-redis) -1. Edit `/etc/redis/redis.conf`: +1. [Install Redis Sentinel](http://redis.io/topics/sentinel) +1. Edit `/etc/redis/sentinel.conf`: ```conf ## Define a `bind` address pointing to a local IP that your other machines @@ -191,8 +193,24 @@ which ideally should not have Redis or Sentinels in the same machine for a HA setup: 1. Edit `/home/git/gitlab/config/resque.yml` following the example in - `/home/git/gitlab/config/resque.yml.example`, and uncomment the sentinels - lines, pointing to the correct server credentials. + [resque.yml.example][resque], and uncomment the Sentinel lines, pointing to + the correct server credentials: + + ```yaml + # resque.yaml + production: + url: redis://:redi-password-goes-here@gitlab-redis/ + sentinels: + - + host: 10.0.0.1 + port: 26379 # point to sentinel, not to redis port + - + host: 10.0.0.2 + port: 26379 # point to sentinel, not to redis port + - + host: 10.0.0.3 + port: 26379 # point to sentinel, not to redis port + ``` 1. [Restart GitLab][restart] for the changes to take effect. @@ -206,14 +224,16 @@ In a real world usage, you would also setup firewall rules to prevent unauthorized access from other machines, and block traffic from the outside ([Internet][it]). -We will use the same `3` nodes with **Redis** + **Sentinel** topology -discussed in the [Configuring Redis for GitLab HA](redis.md) documentation. +For this example, **Sentinel 1** will be configured in the same machine as the +**Redis Master**, **Sentinel 2** and **Sentinel 3** in the same machines as the +**Slave 1** and **Slave 2** respectively. Here is a list and description of each **machine** and the assigned **IP**: * `10.0.0.1`: Redis Master + Sentinel 1 * `10.0.0.2`: Redis Slave 1 + Sentinel 2 * `10.0.0.3`: Redis Slave 2 + Sentinel 3 +* `10.0.0.4`: GitLab application Please note that after the initial configuration, if a failover is initiated by the Sentinel nodes, the Redis nodes will be reconfigured and the **Master** @@ -224,81 +244,100 @@ The same thing will happen with `sentinel.conf` that will be overridden after th initial execution, after any new sentinel node starts watching the **Master**, or a failover promotes a different **Master** node. -### Configuring Redis Master +### Example configuration for Redis master and Sentinel 1 -**Example configation for Redis Master - `redis.conf`:** +1. In `/etc/redis/redis.conf`: -```conf -bind 10.0.0.1 -port 6379 -requirepass redis-password-goes-here -masterauth redis-password-goes-here -``` + ```conf + bind 10.0.0.1 + port 6379 + requirepass redis-password-goes-here + masterauth redis-password-goes-here + ``` + +1. In `/etc/redis/sentinel.conf`: -### Configuring Redis Slaves + ```conf + bind 10.0.0.1 + port 26379 + sentinel auth-pass gitlab-redis redis-password-goes-here + sentinel monitor gitlab-redis 10.0.0.1 6379 2 + sentinel down-after-milliseconds gitlab-redis 10000 + sentinel failover_timeout 30000 + ``` -**Example configation for Slave 1 - `redis.conf`:** +1. Restart the Redis service for the changes to take effect. -```conf -bind 10.0.0.2 -port 6379 -requirepass redis-password-goes-here -masterauth redis-password-goes-here +### Example configuration for Redis slave 1 and Sentinel 2 -# IP and port of the master Redis server -slaveof 10.0.0.1 6379 -``` +1. In `/etc/redis/redis.conf`: -**Example configation for Slave 2 - `redis.conf`:** + ```conf + bind 10.0.0.2 + port 6379 + requirepass redis-password-goes-here + masterauth redis-password-goes-here + slaveof 10.0.0.1 6379 + ``` -```conf -bind 10.0.0.3 -port 6379 -requirepass redis-password-goes-here -masterauth redis-password-goes-here +1. In `/etc/redis/sentinel.conf`: -# IP and port of the master Redis server -slaveof 10.0.0.1 6379 -``` + ```conf + bind 10.0.0.2 + port 26379 + sentinel auth-pass gitlab-redis redis-password-goes-here + sentinel monitor gitlab-redis 10.0.0.1 6379 2 + sentinel down-after-milliseconds gitlab-redis 10000 + sentinel failover_timeout 30000 + ``` -### Configuring Redis Sentinel +1. Restart the Redis service for the changes to take effect. -For this example, **Sentinel 1** will be configured in the same machine as the -**Redis Master**, **Sentinel 2** and **Sentinel 3** in the same machines as the -**Slave 1** and **Slave 2** respectively. +### Example configuration for Redis slave 2 and Sentinel 3 -**Example configation for Sentinel 1 - `sentinel.conf`:** +1. In `/etc/redis/redis.conf`: -```conf -bind 10.0.0.1 -port 26379 -sentinel auth-pass gitlab-redis redis-password-goes-here -sentinel monitor gitlab-redis 10.0.0.1 6379 2 -sentinel down-after-milliseconds gitlab-redis 10000 -sentinel failover_timeout 30000 -``` + ```conf + bind 10.0.0.3 + port 6379 + requirepass redis-password-goes-here + masterauth redis-password-goes-here + slaveof 10.0.0.1 6379 + ``` -**Example configation for Sentinel 2 - `sentinel.conf`:** +1. In `/etc/redis/sentinel.conf`: -```conf -bind 10.0.0.2 -port 26379 -sentinel auth-pass gitlab-redis redis-password-goes-here -sentinel monitor gitlab-redis 10.0.0.1 6379 2 -sentinel down-after-milliseconds gitlab-redis 10000 -sentinel failover_timeout 30000 -``` + ```conf + bind 10.0.0.3 + port 26379 + sentinel auth-pass gitlab-redis redis-password-goes-here + sentinel monitor gitlab-redis 10.0.0.1 6379 2 + sentinel down-after-milliseconds gitlab-redis 10000 + sentinel failover_timeout 30000 + ``` -**Example configation for Sentinel 3 - `sentinel.conf`:** +1. Restart the Redis service for the changes to take effect. -```conf -bind 10.0.0.3 -port 26379 -sentinel auth-pass gitlab-redis redis-password-goes-here -sentinel monitor gitlab-redis 10.0.0.1 6379 2 -sentinel down-after-milliseconds gitlab-redis 10000 -sentinel failover_timeout 30000 -``` +### Example configuration of the GitLab application + +1. Edit `/home/git/gitlab/config/resque.yml`: + + ```yaml + production: + url: redis://:redi-password-goes-here@gitlab-redis/ + sentinels: + - + host: 10.0.0.1 + port: 26379 # point to sentinel, not to redis port + - + host: 10.0.0.2 + port: 26379 # point to sentinel, not to redis port + - + host: 10.0.0.3 + port: 26379 # point to sentinel, not to redis port + ``` + +1. [Restart GitLab][restart] for the changes to take effect. ## Troubleshooting -- cgit v1.2.1 From 72f538731a233ab0f75e4ac139452806e2ee8cf0 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Wed, 16 Nov 2016 12:23:39 +0100 Subject: Remove redundant call to after branch delete service --- app/services/delete_branch_service.rb | 9 --------- spec/features/environments_spec.rb | 17 ++++++++++++++++- spec/services/delete_branch_service_spec.rb | 12 ------------ 3 files changed, 16 insertions(+), 22 deletions(-) diff --git a/app/services/delete_branch_service.rb b/app/services/delete_branch_service.rb index a9fe8198172..3e5dd4ebb86 100644 --- a/app/services/delete_branch_service.rb +++ b/app/services/delete_branch_service.rb @@ -22,7 +22,6 @@ class DeleteBranchService < BaseService end if repository.rm_branch(current_user, branch_name) - execute_after_branch_delete_hooks(branch_name) success('Branch was removed') else error('Failed to remove branch') @@ -48,12 +47,4 @@ class DeleteBranchService < BaseService "#{Gitlab::Git::BRANCH_REF_PREFIX}#{branch.name}", []) end - - private - - def execute_after_branch_delete_hooks(branch_name) - AfterBranchDeleteService - .new(project, current_user) - .execute(branch_name) - end end diff --git a/spec/features/environments_spec.rb b/spec/features/environments_spec.rb index 8b34cb13b9d..e57c355916d 100644 --- a/spec/features/environments_spec.rb +++ b/spec/features/environments_spec.rb @@ -291,11 +291,26 @@ feature 'Environments', feature: true do scenario 'user deletes the branch with running environment' do visit namespace_project_branches_path(project.namespace, project) - page.within('.js-branch-feature') { find('a.btn-remove').click } + remove_branch_with_hooks(project, user, 'feature') do + page.within('.js-branch-feature') { find('a.btn-remove').click } + end + visit_environment(environment) expect(page).to have_no_link('Stop') end + + def remove_branch_with_hooks(project, user, branch) + params = { + oldrev: project.commit(branch).id, + newrev: Gitlab::Git::BLANK_SHA, + ref: "refs/heads/#{branch}" + } + + yield + + GitPushService.new(project, user, params).execute + end end def visit_environments(project) diff --git a/spec/services/delete_branch_service_spec.rb b/spec/services/delete_branch_service_spec.rb index 3ca4eb14518..336f5dafb5b 100644 --- a/spec/services/delete_branch_service_spec.rb +++ b/spec/services/delete_branch_service_spec.rb @@ -20,12 +20,6 @@ describe DeleteBranchService, services: true do expect(result[:status]).to eq :success expect(branch_exists?('feature')).to be false end - - it 'calls after branch delete hooks' do - expect(service).to receive(:execute_after_branch_delete_hooks) - - service.execute('feature') - end end context 'when user does not have access to push to repository' do @@ -38,12 +32,6 @@ describe DeleteBranchService, services: true do expect(result[:message]).to eq 'You dont have push access to repo' expect(branch_exists?('feature')).to be true end - - it 'does not call after branch delete hooks' do - expect(service).not_to receive(:execute_after_branch_delete_hooks) - - service.execute('feature') - end end end -- cgit v1.2.1 From 1708d3a966b73852797ee87310773700a92ea9f2 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Wed, 16 Nov 2016 12:30:44 +0100 Subject: Add Changelog entry for auto-close environments --- .../unreleased/feature-environment-teardown-when-branch-deleted.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 changelogs/unreleased/feature-environment-teardown-when-branch-deleted.yml diff --git a/changelogs/unreleased/feature-environment-teardown-when-branch-deleted.yml b/changelogs/unreleased/feature-environment-teardown-when-branch-deleted.yml new file mode 100644 index 00000000000..0441b68e45f --- /dev/null +++ b/changelogs/unreleased/feature-environment-teardown-when-branch-deleted.yml @@ -0,0 +1,4 @@ +--- +title: Auto-close environment when branch is deleted +merge_request: 7355 +author: -- cgit v1.2.1 From 3cf516cd201e0802d284d664e50dfde409047ca4 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Wed, 16 Nov 2016 12:33:04 +0100 Subject: Add comment related to workaround used in specs --- spec/features/environments_spec.rb | 3 +++ 1 file changed, 3 insertions(+) diff --git a/spec/features/environments_spec.rb b/spec/features/environments_spec.rb index e57c355916d..1fe509c2cac 100644 --- a/spec/features/environments_spec.rb +++ b/spec/features/environments_spec.rb @@ -300,6 +300,9 @@ feature 'Environments', feature: true do expect(page).to have_no_link('Stop') end + ## + # This is a workaround for problem described in #24543 + # def remove_branch_with_hooks(project, user, branch) params = { oldrev: project.commit(branch).id, -- cgit v1.2.1 From 5d5b80a608d8d89525e5e903ea47c6b66e13ed23 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Wed, 16 Nov 2016 12:39:06 +0100 Subject: Remove unnecessary check from environments service --- app/services/ci/stop_environments_service.rb | 5 ----- 1 file changed, 5 deletions(-) diff --git a/app/services/ci/stop_environments_service.rb b/app/services/ci/stop_environments_service.rb index 0ffe73611c4..cf590459cb2 100644 --- a/app/services/ci/stop_environments_service.rb +++ b/app/services/ci/stop_environments_service.rb @@ -6,7 +6,6 @@ module Ci @ref = branch_name return unless has_ref? - return unless has_environments? environments.each do |environment| next unless environment.stoppable? @@ -22,10 +21,6 @@ module Ci @ref.present? end - def has_environments? - environments.any? - end - def environments @environments ||= project .environments_recently_updated_on_branch(@ref) -- cgit v1.2.1 From ca89b6e3d2af2e78b2b726c468caa2c9f678544c Mon Sep 17 00:00:00 2001 From: Herbert Kagumba Date: Wed, 16 Nov 2016 14:09:40 +0300 Subject: Change the slack notification comment link. --- .../project_services/slack_service/note_message.rb | 12 ++++++------ changelogs/unreleased/24107-slack-comment-link.yml | 4 ++++ .../project_services/slack_service/note_message_spec.rb | 16 ++++++++-------- 3 files changed, 18 insertions(+), 14 deletions(-) create mode 100644 changelogs/unreleased/24107-slack-comment-link.yml diff --git a/app/models/project_services/slack_service/note_message.rb b/app/models/project_services/slack_service/note_message.rb index 9e84e90f38c..797c5937f09 100644 --- a/app/models/project_services/slack_service/note_message.rb +++ b/app/models/project_services/slack_service/note_message.rb @@ -46,25 +46,25 @@ class SlackService commit_sha = commit[:id] commit_sha = Commit.truncate_sha(commit_sha) commented_on_message( - "[commit #{commit_sha}](#{@note_url})", + "commit #{commit_sha}", format_title(commit[:message])) end def create_issue_note(issue) commented_on_message( - "[issue ##{issue[:iid]}](#{@note_url})", + "issue ##{issue[:iid]}", format_title(issue[:title])) end def create_merge_note(merge_request) commented_on_message( - "[merge request !#{merge_request[:iid]}](#{@note_url})", + "merge request !#{merge_request[:iid]}", format_title(merge_request[:title])) end def create_snippet_note(snippet) commented_on_message( - "[snippet ##{snippet[:id]}](#{@note_url})", + "snippet ##{snippet[:id]}", format_title(snippet[:title])) end @@ -76,8 +76,8 @@ class SlackService "[#{@project_name}](#{@project_url})" end - def commented_on_message(target_link, title) - @message = "#{@user_name} commented on #{target_link} in #{project_link}: *#{title}*" + def commented_on_message(target, title) + @message = "#{@user_name} [commented on #{target}](#{@note_url}) in #{project_link}: *#{title}*" end end end diff --git a/changelogs/unreleased/24107-slack-comment-link.yml b/changelogs/unreleased/24107-slack-comment-link.yml new file mode 100644 index 00000000000..9c17d6fd825 --- /dev/null +++ b/changelogs/unreleased/24107-slack-comment-link.yml @@ -0,0 +1,4 @@ +--- +title: Change slack notification comment link +merge_request: 7498 +author: Herbert Kagumba diff --git a/spec/models/project_services/slack_service/note_message_spec.rb b/spec/models/project_services/slack_service/note_message_spec.rb index 38cfe4ad3e3..97f818125d3 100644 --- a/spec/models/project_services/slack_service/note_message_spec.rb +++ b/spec/models/project_services/slack_service/note_message_spec.rb @@ -37,8 +37,8 @@ describe SlackService::NoteMessage, models: true do it 'returns a message regarding notes on commits' do message = SlackService::NoteMessage.new(@args) - expect(message.pretext).to eq("test.user commented on " \ - " in : " \ + expect(message.pretext).to eq("test.user in : " \ "*Added a commit message*") expected_attachments = [ { @@ -63,8 +63,8 @@ describe SlackService::NoteMessage, models: true do it 'returns a message regarding notes on a merge request' do message = SlackService::NoteMessage.new(@args) - expect(message.pretext).to eq("test.user commented on " \ - " in : " \ + expect(message.pretext).to eq("test.user in : " \ "*merge request title*") expected_attachments = [ { @@ -90,8 +90,8 @@ describe SlackService::NoteMessage, models: true do it 'returns a message regarding notes on an issue' do message = SlackService::NoteMessage.new(@args) expect(message.pretext).to eq( - "test.user commented on " \ - " in : " \ + "test.user in : " \ "*issue title*") expected_attachments = [ { @@ -115,8 +115,8 @@ describe SlackService::NoteMessage, models: true do it 'returns a message regarding notes on a project snippet' do message = SlackService::NoteMessage.new(@args) - expect(message.pretext).to eq("test.user commented on " \ - " in : " \ + expect(message.pretext).to eq("test.user in : " \ "*snippet title*") expected_attachments = [ { -- cgit v1.2.1 From ef3be00a0297dfa31002616df6ee49a0e2132cb7 Mon Sep 17 00:00:00 2001 From: Adam Niedzielski Date: Wed, 16 Nov 2016 12:46:07 +0100 Subject: Defer saving project services to the database if there are no user changes --- app/controllers/projects/services_controller.rb | 5 ++--- app/models/project.rb | 20 ++++++++++++-------- app/models/service.rb | 4 ++-- app/services/projects/create_service.rb | 9 ++++++++- app/views/projects/services/index.html.haml | 5 +++-- .../adam-build-missing-services-when-necessary.yml | 4 ++++ lib/api/helpers.rb | 17 +---------------- spec/models/project_spec.rb | 6 ------ spec/models/service_spec.rb | 10 +++------- spec/requests/api/services_spec.rb | 3 +-- spec/services/projects/create_service_spec.rb | 20 +++++++++++++------- 11 files changed, 49 insertions(+), 54 deletions(-) create mode 100644 changelogs/unreleased/adam-build-missing-services-when-necessary.yml diff --git a/app/controllers/projects/services_controller.rb b/app/controllers/projects/services_controller.rb index 97e6e9471e0..40a23a6f806 100644 --- a/app/controllers/projects/services_controller.rb +++ b/app/controllers/projects/services_controller.rb @@ -10,8 +10,7 @@ class Projects::ServicesController < Projects::ApplicationController layout "project_settings" def index - @project.build_missing_services - @services = @project.services.visible.reload + @services = @project.find_or_initialize_services end def edit @@ -46,6 +45,6 @@ class Projects::ServicesController < Projects::ApplicationController private def service - @service ||= @project.services.find { |service| service.to_param == params[:id] } + @service ||= @project.find_or_initialize_service(params[:id]) end end diff --git a/app/models/project.rb b/app/models/project.rb index 94aabafce20..478e4a2cb75 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -76,7 +76,6 @@ class Project < ActiveRecord::Base has_many :boards, before_add: :validate_board_limit, dependent: :destroy # Project services - has_many :services has_one :campfire_service, dependent: :destroy has_one :drone_ci_service, dependent: :destroy has_one :emails_on_push_service, dependent: :destroy @@ -748,27 +747,32 @@ class Project < ActiveRecord::Base update_column(:has_external_wiki, services.external_wikis.any?) end - def build_missing_services + def find_or_initialize_services services_templates = Service.where(template: true) - Service.available_services_names.each do |service_name| + Service.available_services_names.map do |service_name| service = find_service(services, service_name) - # If service is available but missing in db - if service.nil? + if service + service + else # We should check if template for the service exists template = find_service(services_templates, service_name) if template.nil? - # If no template, we should create an instance. Ex `create_gitlab_ci_service` - public_send("create_#{service_name}_service") + # If no template, we should create an instance. Ex `build_gitlab_ci_service` + public_send("build_#{service_name}_service") else - Service.create_from_template(self.id, template) + Service.build_from_template(id, template) end end end end + def find_or_initialize_service(name) + find_or_initialize_services.find { |service| service.to_param == name } + end + def create_labels Label.templates.each do |label| params = label.attributes.except('id', 'template', 'created_at', 'updated_at') diff --git a/app/models/service.rb b/app/models/service.rb index 625fbc48302..9d6ff190cdf 100644 --- a/app/models/service.rb +++ b/app/models/service.rb @@ -222,11 +222,11 @@ class Service < ActiveRecord::Base ] end - def self.create_from_template(project_id, template) + def self.build_from_template(project_id, template) service = template.dup service.template = false service.project_id = project_id - service if service.save + service end private diff --git a/app/services/projects/create_service.rb b/app/services/projects/create_service.rb index 15d7918e7fd..28db145a1f4 100644 --- a/app/services/projects/create_service.rb +++ b/app/services/projects/create_service.rb @@ -95,7 +95,7 @@ module Projects unless @project.gitlab_project_import? @project.create_wiki unless skip_wiki? - @project.build_missing_services + create_services_from_active_templates(@project) @project.create_labels end @@ -135,5 +135,12 @@ module Projects @project end + + def create_services_from_active_templates(project) + Service.where(template: true, active: true).each do |template| + service = Service.build_from_template(project.id, template) + service.save! + end + end end end diff --git a/app/views/projects/services/index.html.haml b/app/views/projects/services/index.html.haml index 4a33a5bc6f6..66fd3029dc9 100644 --- a/app/views/projects/services/index.html.haml +++ b/app/views/projects/services/index.html.haml @@ -28,5 +28,6 @@ %td.hidden-xs = service.description %td.light - = time_ago_in_words service.updated_at - ago + - if service.updated_at.present? + = time_ago_in_words service.updated_at + ago diff --git a/changelogs/unreleased/adam-build-missing-services-when-necessary.yml b/changelogs/unreleased/adam-build-missing-services-when-necessary.yml new file mode 100644 index 00000000000..8b157e31e99 --- /dev/null +++ b/changelogs/unreleased/adam-build-missing-services-when-necessary.yml @@ -0,0 +1,4 @@ +--- +title: Defer saving project services to the database if there are no user changes +merge_request: 6958 +author: diff --git a/lib/api/helpers.rb b/lib/api/helpers.rb index 3c9d7b1aaef..b4e0457f773 100644 --- a/lib/api/helpers.rb +++ b/lib/api/helpers.rb @@ -81,25 +81,10 @@ module API end def project_service - @project_service ||= begin - underscored_service = params[:service_slug].underscore - - if Service.available_services_names.include?(underscored_service) - user_project.build_missing_services - - service_method = "#{underscored_service}_service" - - send_service(service_method) - end - end - + @project_service ||= user_project.find_or_initialize_service(params[:service_slug].underscore) @project_service || not_found!("Service") end - def send_service(service_method) - user_project.send(service_method) - end - def service_attributes @service_attributes ||= project_service.fields.inject([]) do |arr, hash| arr << hash[:name].to_sym diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index 0810d06b50f..642f1edfe3f 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -496,9 +496,6 @@ describe Project, models: true do end it 'returns nil and does not query services when there is no external issue tracker' do - project.build_missing_services - project.reload - expect(project).not_to receive(:services) expect(project.external_issue_tracker).to eq(nil) @@ -506,9 +503,6 @@ describe Project, models: true do it 'retrieves external_issue_tracker querying services and cache it when there is external issue tracker' do ext_project.reload # Factory returns a project with changed attributes - ext_project.build_missing_services - ext_project.reload - expect(ext_project).to receive(:services).once.and_call_original 2.times { expect(ext_project.external_issue_tracker).to be_a_kind_of(RedmineService) } diff --git a/spec/models/service_spec.rb b/spec/models/service_spec.rb index 43937a54b2c..b1615a95004 100644 --- a/spec/models/service_spec.rb +++ b/spec/models/service_spec.rb @@ -53,7 +53,7 @@ describe Service, models: true do describe "Template" do describe "for pushover service" do - let(:service_template) do + let!(:service_template) do PushoverService.create( template: true, properties: { @@ -66,13 +66,9 @@ describe Service, models: true do let(:project) { create(:project) } describe 'is prefilled for projects pushover service' do - before do - service_template - project.build_missing_services - end - it "has all fields prefilled" do - service = project.pushover_service + service = project.find_or_initialize_service('pushover') + expect(service.template).to eq(false) expect(service.device).to eq('MyDevice') expect(service.sound).to eq('mic') diff --git a/spec/requests/api/services_spec.rb b/spec/requests/api/services_spec.rb index 375671bca4c..2aadab3cbe1 100644 --- a/spec/requests/api/services_spec.rb +++ b/spec/requests/api/services_spec.rb @@ -56,8 +56,7 @@ describe API::API, api: true do # inject some properties into the service before do - project.build_missing_services - service_object = project.send(service_method) + service_object = project.find_or_initialize_service(service) service_object.properties = service_attrs service_object.save end diff --git a/spec/services/projects/create_service_spec.rb b/spec/services/projects/create_service_spec.rb index 876bfaf085c..2cf9883113c 100644 --- a/spec/services/projects/create_service_spec.rb +++ b/spec/services/projects/create_service_spec.rb @@ -10,13 +10,6 @@ describe Projects::CreateService, services: true do } end - it 'creates services on Project creation' do - project = create_project(@user, @opts) - project.reload - - expect(project.services).not_to be_empty - end - it 'creates labels on Project creation if there are templates' do Label.create(title: "bug", template: true) project = create_project(@user, @opts) @@ -137,6 +130,19 @@ describe Projects::CreateService, services: true do expect(project.namespace).to eq(@user.namespace) end end + + context 'when there is an active service template' do + before do + create(:service, project: nil, template: true, active: true) + end + + it 'creates a service from this template' do + project = create_project(@user, @opts) + project.reload + + expect(project.services.count).to eq 1 + end + end end def create_project(user, opts) -- cgit v1.2.1 From aa9a289ce55d5099e6a65f25e7c3b38f66148aca Mon Sep 17 00:00:00 2001 From: Marin Jankovski Date: Fri, 11 Nov 2016 15:40:19 +0100 Subject: Make mail_room idle_timeout option configurable. --- config/gitlab.yml.example | 2 ++ config/mail_room.yml | 2 +- lib/gitlab/mail_room.rb | 1 + spec/config/mail_room_spec.rb | 1 + 4 files changed, 5 insertions(+), 1 deletion(-) diff --git a/config/gitlab.yml.example b/config/gitlab.yml.example index 699ab6075b6..2da225904e1 100644 --- a/config/gitlab.yml.example +++ b/config/gitlab.yml.example @@ -138,6 +138,8 @@ production: &base # The mailbox where incoming mail will end up. Usually "inbox". mailbox: "inbox" + # The mailbox where incoming mail will end up. Usually "inbox". + idle_timeout: 60 ## Build Artifacts artifacts: diff --git a/config/mail_room.yml b/config/mail_room.yml index b026d510f1b..774c5350a45 100644 --- a/config/mail_room.yml +++ b/config/mail_room.yml @@ -15,7 +15,7 @@ :start_tls: <%= config[:start_tls].to_json %> :email: <%= config[:user].to_json %> :password: <%= config[:password].to_json %> - :idle_timeout: 60 + :idle_timeout: <%= config[:idle_timeout].to_json %> :name: <%= config[:mailbox].to_json %> diff --git a/lib/gitlab/mail_room.rb b/lib/gitlab/mail_room.rb index a5220d92312..3503fac40e8 100644 --- a/lib/gitlab/mail_room.rb +++ b/lib/gitlab/mail_room.rb @@ -31,6 +31,7 @@ module Gitlab config[:ssl] = false if config[:ssl].nil? config[:start_tls] = false if config[:start_tls].nil? config[:mailbox] = 'inbox' if config[:mailbox].nil? + config[:idle_timeout] = 60 if config[:idle_timeout].nil? if config[:enabled] && config[:address] gitlab_redis = Gitlab::Redis.new(rails_env) diff --git a/spec/config/mail_room_spec.rb b/spec/config/mail_room_spec.rb index 22bf3055538..294fae95752 100644 --- a/spec/config/mail_room_spec.rb +++ b/spec/config/mail_room_spec.rb @@ -47,6 +47,7 @@ describe 'mail_room.yml' do expect(mailbox[:email]).to eq('gitlab-incoming@gmail.com') expect(mailbox[:password]).to eq('[REDACTED]') expect(mailbox[:name]).to eq('inbox') + expect(mailbox[:idle_timeout]).to eq(60) redis_url = gitlab_redis.url sentinels = gitlab_redis.sentinels -- cgit v1.2.1 From 04f2dd65fb355eb3bf20e0d3deef9123132aacad Mon Sep 17 00:00:00 2001 From: Marin Jankovski Date: Fri, 11 Nov 2016 15:49:51 +0100 Subject: Add idle_timeout to reply by email doc. --- config/gitlab.yml.example | 2 +- doc/administration/reply_by_email.md | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/config/gitlab.yml.example b/config/gitlab.yml.example index 2da225904e1..327e4a7937c 100644 --- a/config/gitlab.yml.example +++ b/config/gitlab.yml.example @@ -138,7 +138,7 @@ production: &base # The mailbox where incoming mail will end up. Usually "inbox". mailbox: "inbox" - # The mailbox where incoming mail will end up. Usually "inbox". + # The IDLE command timeout. idle_timeout: 60 ## Build Artifacts diff --git a/doc/administration/reply_by_email.md b/doc/administration/reply_by_email.md index 5a9a1582877..b42892eef68 100644 --- a/doc/administration/reply_by_email.md +++ b/doc/administration/reply_by_email.md @@ -105,6 +105,8 @@ To set up a basic Postfix mail server with IMAP access on Ubuntu, follow # The mailbox where incoming mail will end up. Usually "inbox". gitlab_rails['incoming_email_mailbox_name'] = "inbox" + # The IDLE command timeout. + gitlab_rails['incoming_email_idle_timeout'] = 60 ``` ```ruby @@ -133,6 +135,8 @@ To set up a basic Postfix mail server with IMAP access on Ubuntu, follow # The mailbox where incoming mail will end up. Usually "inbox". gitlab_rails['incoming_email_mailbox_name'] = "inbox" + # The IDLE command timeout. + gitlab_rails['incoming_email_idle_timeout'] = 60 ``` 1. Reconfigure GitLab and restart mailroom for the changes to take effect: @@ -192,6 +196,8 @@ To set up a basic Postfix mail server with IMAP access on Ubuntu, follow # The mailbox where incoming mail will end up. Usually "inbox". mailbox: "inbox" + # The IDLE command timeout. + email_idle_timeout: 60 ``` ```yaml @@ -221,6 +227,8 @@ To set up a basic Postfix mail server with IMAP access on Ubuntu, follow # The mailbox where incoming mail will end up. Usually "inbox". mailbox: "inbox" + # The IDLE command timeout. + email_idle_timeout: 60 ``` 1. Enable `mail_room` in the init script at `/etc/default/gitlab`: @@ -277,6 +285,8 @@ To set up a basic Postfix mail server with IMAP access on Ubuntu, follow # The mailbox where incoming mail will end up. Usually "inbox". mailbox: "inbox" + # The IDLE command timeout. + email_idle_timeout: 60 ``` As mentioned, the part after `+` is ignored, and this will end up in the mailbox for `gitlab-incoming@gmail.com`. -- cgit v1.2.1 From c44a77836a4f6f64888094ea3d393e87fc894403 Mon Sep 17 00:00:00 2001 From: Marin Jankovski Date: Wed, 16 Nov 2016 12:49:41 +0100 Subject: Add mail_room idle_timeout to the Changelog. --- changelogs/unreleased/mailroom_idle_timeout.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 changelogs/unreleased/mailroom_idle_timeout.yml diff --git a/changelogs/unreleased/mailroom_idle_timeout.yml b/changelogs/unreleased/mailroom_idle_timeout.yml new file mode 100644 index 00000000000..276b28a56dd --- /dev/null +++ b/changelogs/unreleased/mailroom_idle_timeout.yml @@ -0,0 +1,4 @@ +--- +title: Allow mail_room idle_timeout option to be configurable +merge_request: 7423 +author: -- cgit v1.2.1 From 7004851569ec0ae0c0162c772243ea702c36aa15 Mon Sep 17 00:00:00 2001 From: Tomasz Maczukin Date: Thu, 27 Oct 2016 17:19:38 +0200 Subject: Add CHANGELOG entry --- changelogs/unreleased/fix-trace-patch-updated-at.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 changelogs/unreleased/fix-trace-patch-updated-at.yml diff --git a/changelogs/unreleased/fix-trace-patch-updated-at.yml b/changelogs/unreleased/fix-trace-patch-updated-at.yml new file mode 100644 index 00000000000..88f400f0a0e --- /dev/null +++ b/changelogs/unreleased/fix-trace-patch-updated-at.yml @@ -0,0 +1,4 @@ +--- +title: Fix trace patching feature - update the updated_at value +merge_request: 7146 +author: -- cgit v1.2.1 From a5632e802b72db01c0fb0b8bec77c0fc28b41427 Mon Sep 17 00:00:00 2001 From: Valery Sizov Date: Thu, 10 Nov 2016 19:27:09 +0200 Subject: Search for a filename in a project --- app/assets/stylesheets/pages/search.scss | 4 ++ app/helpers/search_helper.rb | 29 +----------- app/models/project.rb | 2 +- app/models/project_wiki.rb | 2 +- app/models/repository.rb | 17 ++++++-- app/views/search/results/_blob.html.haml | 12 ++--- .../23117-search-for-a-filename-in-a-project.yml | 4 ++ lib/gitlab/project_search_results.rb | 51 +++++++++++++++++++++- spec/helpers/search_helper_spec.rb | 32 -------------- spec/lib/gitlab/project_search_results_spec.rb | 51 ++++++++++++++++++++-- spec/models/repository_spec.rb | 34 ++++++++++++--- 11 files changed, 155 insertions(+), 83 deletions(-) create mode 100644 changelogs/unreleased/23117-search-for-a-filename-in-a-project.yml diff --git a/app/assets/stylesheets/pages/search.scss b/app/assets/stylesheets/pages/search.scss index 1a116f6a919..49f65fe4901 100644 --- a/app/assets/stylesheets/pages/search.scss +++ b/app/assets/stylesheets/pages/search.scss @@ -8,6 +8,10 @@ border-bottom: none; } } + + .blob-result { + margin: 5px 0; + } } .search { diff --git a/app/helpers/search_helper.rb b/app/helpers/search_helper.rb index aba3a3f9c5d..cdb9663877c 100644 --- a/app/helpers/search_helper.rb +++ b/app/helpers/search_helper.rb @@ -31,34 +31,7 @@ module SearchHelper end def parse_search_result(result) - ref = nil - filename = nil - basename = nil - startline = 0 - - result.each_line.each_with_index do |line, index| - if line =~ /^.*:.*:\d+:/ - ref, filename, startline = line.split(':') - startline = startline.to_i - index - extname = Regexp.escape(File.extname(filename)) - basename = filename.sub(/#{extname}$/, '') - break - end - end - - data = "" - - result.each_line do |line| - data << line.sub(ref, '').sub(filename, '').sub(/^:-\d+-/, '').sub(/^::\d+:/, '') - end - - OpenStruct.new( - filename: filename, - basename: basename, - ref: ref, - startline: startline, - data: data - ) + Gitlab::ProjectSearchResults.parse_search_result(result) end private diff --git a/app/models/project.rb b/app/models/project.rb index bab2f0c53ca..327f81412f8 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -879,7 +879,7 @@ class Project < ActiveRecord::Base end def empty_repo? - !repository.exists? || !repository.has_visible_content? + repository.empty_repo? end def repo diff --git a/app/models/project_wiki.rb b/app/models/project_wiki.rb index 46f70da2452..9db96347322 100644 --- a/app/models/project_wiki.rb +++ b/app/models/project_wiki.rb @@ -127,7 +127,7 @@ class ProjectWiki end def search_files(query) - repository.search_files(query, default_branch) + repository.search_files_by_content(query, default_branch) end def repository diff --git a/app/models/repository.rb b/app/models/repository.rb index 4282197faa5..a035768a6c1 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -1063,16 +1063,25 @@ class Repository merge_base(ancestor_id, descendant_id) == ancestor_id end - def search_files(query, ref) - unless exists? && has_visible_content? && query.present? - return [] - end + def empty_repo? + !exists? || !has_visible_content? + end + + def search_files_by_content(query, ref) + return [] if empty_repo? || query.blank? offset = 2 args = %W(#{Gitlab.config.git.bin_path} grep -i -I -n --before-context #{offset} --after-context #{offset} -E -e #{Regexp.escape(query)} #{ref || root_ref}) Gitlab::Popen.popen(args, path_to_repo).first.scrub.split(/^--$/) end + def search_files_by_name(query, ref) + return [] if empty_repo? || query.blank? + + args = %W(#{Gitlab.config.git.bin_path} ls-tree --full-tree -r #{ref || root_ref} --name-status | #{Regexp.escape(query)}) + Gitlab::Popen.popen(args, path_to_repo).first.lines.map(&:strip) + end + def fetch_ref(source_path, source_ref, target_ref) args = %W(#{Gitlab.config.git.bin_path} fetch --no-tags -f #{source_path} #{source_ref}:#{target_ref}) Gitlab::Popen.popen(args, path_to_repo) diff --git a/app/views/search/results/_blob.html.haml b/app/views/search/results/_blob.html.haml index 6f0a0ea36ec..9e8adc82583 100644 --- a/app/views/search/results/_blob.html.haml +++ b/app/views/search/results/_blob.html.haml @@ -1,11 +1,13 @@ -- blob = parse_search_result(blob) +- file_name, blob = blob .blob-result .file-holder .file-title - - blob_link = namespace_project_blob_path(@project.namespace, @project, tree_join(blob.ref, blob.filename)) + - ref = @search_results.repository_ref + - blob_link = namespace_project_blob_path(@project.namespace, @project, tree_join(ref, file_name)) = link_to blob_link do %i.fa.fa-file %strong - = blob.filename - .file-content.code.term - = render 'shared/file_highlight', blob: blob, first_line_number: blob.startline, blob_link: blob_link + = file_name + - if blob + .file-content.code.term + = render 'shared/file_highlight', blob: blob, first_line_number: blob.startline, blob_link: blob_link diff --git a/changelogs/unreleased/23117-search-for-a-filename-in-a-project.yml b/changelogs/unreleased/23117-search-for-a-filename-in-a-project.yml new file mode 100644 index 00000000000..156f8d779ca --- /dev/null +++ b/changelogs/unreleased/23117-search-for-a-filename-in-a-project.yml @@ -0,0 +1,4 @@ +--- +title: Search for a filename in a project +merge_request: +author: diff --git a/lib/gitlab/project_search_results.rb b/lib/gitlab/project_search_results.rb index b8326a64b22..66e6b29e798 100644 --- a/lib/gitlab/project_search_results.rb +++ b/lib/gitlab/project_search_results.rb @@ -5,7 +5,7 @@ module Gitlab def initialize(current_user, project, query, repository_ref = nil) @current_user = current_user @project = project - @repository_ref = repository_ref.presence + @repository_ref = repository_ref.presence || project.default_branch @query = query end @@ -40,10 +40,57 @@ module Gitlab @commits_count ||= commits.count end + def self.parse_search_result(result) + ref = nil + filename = nil + basename = nil + startline = 0 + + result.each_line.each_with_index do |line, index| + if line =~ /^.*:.*:\d+:/ + ref, filename, startline = line.split(':') + startline = startline.to_i - index + extname = Regexp.escape(File.extname(filename)) + basename = filename.sub(/#{extname}$/, '') + break + end + end + + data = "" + + result.each_line do |line| + data << line.sub(ref, '').sub(filename, '').sub(/^:-\d+-/, '').sub(/^::\d+:/, '') + end + + OpenStruct.new( + filename: filename, + basename: basename, + ref: ref, + startline: startline, + data: data + ) + end + private def blobs - @blobs ||= project.repository.search_files(query, repository_ref) + @blobs ||= begin + blobs = project.repository.search_files_by_content(query, repository_ref).first(100) + found_file_names = Set.new + + results = blobs.map do |blob| + blob = self.class.parse_search_result(blob) + found_file_names << blob.filename + + [blob.filename, blob] + end + + project.repository.search_files_by_name(query, repository_ref).first(100).each do |filename| + results << [filename, nil] unless found_file_names.include?(filename) + end + + results.sort_by(&:first) + end end def wiki_blobs diff --git a/spec/helpers/search_helper_spec.rb b/spec/helpers/search_helper_spec.rb index 64aa41020c9..4b2ca3514f8 100644 --- a/spec/helpers/search_helper_spec.rb +++ b/spec/helpers/search_helper_spec.rb @@ -6,38 +6,6 @@ describe SearchHelper do str end - describe 'parsing result' do - let(:project) { create(:project) } - let(:repository) { project.repository } - let(:results) { repository.search_files('feature', 'master') } - let(:search_result) { results.first } - - subject { helper.parse_search_result(search_result) } - - it "returns a valid OpenStruct object" do - is_expected.to be_an OpenStruct - expect(subject.filename).to eq('CHANGELOG') - expect(subject.basename).to eq('CHANGELOG') - expect(subject.ref).to eq('master') - expect(subject.startline).to eq(188) - expect(subject.data.lines[2]).to eq(" - Feature: Replace teams with group membership\n") - end - - context "when filename has extension" do - let(:search_result) { "master:CONTRIBUTE.md:5:- [Contribute to GitLab](#contribute-to-gitlab)\n" } - - it { expect(subject.filename).to eq('CONTRIBUTE.md') } - it { expect(subject.basename).to eq('CONTRIBUTE') } - end - - context "when file under directory" do - let(:search_result) { "master:a/b/c.md:5:a b c\n" } - - it { expect(subject.filename).to eq('a/b/c.md') } - it { expect(subject.basename).to eq('a/b/c') } - end - end - describe 'search_autocomplete_source' do context "with no current user" do before do diff --git a/spec/lib/gitlab/project_search_results_spec.rb b/spec/lib/gitlab/project_search_results_spec.rb index 29abb4d4d07..a0fdad87eee 100644 --- a/spec/lib/gitlab/project_search_results_spec.rb +++ b/spec/lib/gitlab/project_search_results_spec.rb @@ -6,22 +6,65 @@ describe Gitlab::ProjectSearchResults, lib: true do let(:query) { 'hello world' } describe 'initialize with empty ref' do - let(:results) { Gitlab::ProjectSearchResults.new(user, project, query, '') } + let(:results) { described_class.new(user, project, query, '') } it { expect(results.project).to eq(project) } - it { expect(results.repository_ref).to be_nil } it { expect(results.query).to eq('hello world') } end describe 'initialize with ref' do let(:ref) { 'refs/heads/test' } - let(:results) { Gitlab::ProjectSearchResults.new(user, project, query, ref) } + let(:results) { described_class.new(user, project, query, ref) } it { expect(results.project).to eq(project) } it { expect(results.repository_ref).to eq(ref) } it { expect(results.query).to eq('hello world') } end + describe 'blob search' do + let(:results) { described_class.new(user, project, 'files').objects('blobs') } + + it 'finds by name' do + expect(results).to include(["files/images/wm.svg", nil]) + end + + it 'finds by content' do + blob = results.select { |result| result.first == "CHANGELOG" }.flatten.last + + expect(blob.filename).to eq("CHANGELOG") + end + + describe 'parsing results' do + let(:results) { project.repository.search_files_by_content('feature', 'master') } + let(:search_result) { results.first } + + subject { described_class.parse_search_result(search_result) } + + it "returns a valid OpenStruct object" do + is_expected.to be_an OpenStruct + expect(subject.filename).to eq('CHANGELOG') + expect(subject.basename).to eq('CHANGELOG') + expect(subject.ref).to eq('master') + expect(subject.startline).to eq(188) + expect(subject.data.lines[2]).to eq(" - Feature: Replace teams with group membership\n") + end + + context "when filename has extension" do + let(:search_result) { "master:CONTRIBUTE.md:5:- [Contribute to GitLab](#contribute-to-gitlab)\n" } + + it { expect(subject.filename).to eq('CONTRIBUTE.md') } + it { expect(subject.basename).to eq('CONTRIBUTE') } + end + + context "when file under directory" do + let(:search_result) { "master:a/b/c.md:5:a b c\n" } + + it { expect(subject.filename).to eq('a/b/c.md') } + it { expect(subject.basename).to eq('a/b/c') } + end + end + end + describe 'confidential issues' do let(:query) { 'issue' } let(:author) { create(:user) } @@ -66,7 +109,7 @@ describe Gitlab::ProjectSearchResults, lib: true do end it 'lists project confidential issues for assignee' do - results = described_class.new(assignee, project.id, query) + results = described_class.new(assignee, project, query) issues = results.objects('issues') expect(issues).to include issue diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb index fe26b4ac18c..3bd5741f2b7 100644 --- a/spec/models/repository_spec.rb +++ b/spec/models/repository_spec.rb @@ -393,33 +393,33 @@ describe Repository, models: true do end end - describe "search_files" do - let(:results) { repository.search_files('feature', 'master') } + describe "search_files_by_content" do + let(:results) { repository.search_files_by_content('feature', 'master') } subject { results } it { is_expected.to be_an Array } it 'regex-escapes the query string' do - results = repository.search_files("test\\", 'master') + results = repository.search_files_by_content("test\\", 'master') expect(results.first).not_to start_with('fatal:') end it 'properly handles an unmatched parenthesis' do - results = repository.search_files("test(", 'master') + results = repository.search_files_by_content("test(", 'master') expect(results.first).not_to start_with('fatal:') end it 'properly handles when query is not present' do - results = repository.search_files('', 'master') + results = repository.search_files_by_content('', 'master') expect(results).to match_array([]) end it 'properly handles query when repo is empty' do repository = create(:empty_project).repository - results = repository.search_files('test', 'master') + results = repository.search_files_by_content('test', 'master') expect(results).to match_array([]) end @@ -432,6 +432,28 @@ describe Repository, models: true do end end + describe "search_files_by_name" do + let(:results) { repository.search_files_by_name('files', 'master') } + + it 'returns result' do + expect(results.first).to eq('files/html/500.html') + end + + it 'properly handles when query is not present' do + results = repository.search_files_by_name('', 'master') + + expect(results).to match_array([]) + end + + it 'properly handles query when repo is empty' do + repository = create(:empty_project).repository + + results = repository.search_files_by_name('test', 'master') + + expect(results).to match_array([]) + end + end + describe '#create_ref' do it 'redirects the call to fetch_ref' do ref, ref_path = '1', '2' -- cgit v1.2.1 From baf09d2a7a78f9260f3dff601ed7a10d18d3bec4 Mon Sep 17 00:00:00 2001 From: Gabriel Mazetto Date: Thu, 22 Sep 2016 06:05:28 -0300 Subject: Improved redis sentinel documentation for CE --- doc/administration/high_availability/redis.md | 62 +++++++++++---------------- 1 file changed, 26 insertions(+), 36 deletions(-) diff --git a/doc/administration/high_availability/redis.md b/doc/administration/high_availability/redis.md index 8656b42f274..a1426575abd 100644 --- a/doc/administration/high_availability/redis.md +++ b/doc/administration/high_availability/redis.md @@ -143,6 +143,7 @@ the master, and `masterauth` in slaves. redis['port'] = 6379 ## Slave redis instance + redis['master'] = false redis['master_ip'] = '10.10.10.10' # IP of master Redis server redis['master_port'] = 6379 # Port of master Redis server redis['master_password'] = "" @@ -157,31 +158,19 @@ servers. ### Sentinel setup -We don't provide yet an automated way to setup and run the Sentinel daemon -from Omnibus installation method. You must follow the instructions below and -run it by yourself. +We provide an automated way to setup and run the Sentinel daemon +with GitLab EE. -The support for Sentinel in Ruby has some [caveats](https://github.com/redis/redis-rb/issues/531). -While you can give any name for the `master-group-name` part of the -configuration, as in this example: - -```conf -sentinel monitor -``` - -,for it to work in Ruby, you have to use the "hostname" of the master Redis -server, otherwise you will get an error message like: -`Redis::CannotConnectError: No sentinels available.`. Read -[Sentinel troubleshooting](#sentinel-troubleshooting) for more information. +See the instructions below how to setup it by yourself. Here is an example configuration file (`sentinel.conf`) for a Sentinel node: ```conf port 26379 -sentinel monitor master-redis.example.com 10.10.10.10 6379 1 -sentinel down-after-milliseconds master-redis.example.com 10000 -sentinel config-epoch master-redis.example.com 0 -sentinel leader-epoch master-redis.example.com 0 +sentinel monitor gitlab-redis 10.0.0.1 6379 1 +sentinel down-after-milliseconds gitlab-redis 10000 +sentinel config-epoch gitlab-redis 0 +sentinel leader-epoch gitlab-redis 0 ``` --- @@ -213,10 +202,11 @@ The following steps should be performed in the [GitLab application server](gitla 1. Edit `/etc/gitlab/gitlab.rb` and add/change the following lines: ```ruby - gitlab-rails['redis_host'] = "master-redis.example.com" - gitlab-rails['redis_port'] = 6379 - gitlab-rails['redis_password'] = '' - gitlab-rails['redis_sentinels'] = [ + redis['master_name'] = "gitlab-redis" + redis['master_ip'] = "10.0.0.1" + redis['master_port'] = 6379 + redis['master_password'] = '' + gitlab_rails['redis_sentinels'] = [ {'host' => '10.10.10.1', 'port' => 26379}, {'host' => '10.10.10.2', 'port' => 26379}, {'host' => '10.10.10.3', 'port' => 26379} @@ -229,33 +219,33 @@ The following steps should be performed in the [GitLab application server](gitla If you get an error like: `Redis::CannotConnectError: No sentinels available.`, there may be something wrong with your configuration files or it can be related -to [this issue][gh-531] ([pull request][gh-534] that should make things better). +to [this issue][gh-531]. -It's a bit rigid the way you have to config `resque.yml` and `sentinel.conf`, -otherwise `redis-rb` will not work properly. +It's a bit non-intuitive the way you have to config `resque.yml` and +`sentinel.conf`, otherwise `redis-rb` will not work properly. -The hostname ('my-primary-redis') of the primary Redis server (`sentinel.conf`) -**must** match the one configured in GitLab (`resque.yml` for source installations -or `gitlab-rails['redis_*']` in Omnibus) and it must be valid ex: +The `master-group-name` ('gitlab-redis') defined in (`sentinel.conf`) +**must** be used as the hostname in GitLab (`resque.yml` for source installations +or `gitlab-rails['redis_*']` in Omnibus): ```conf # sentinel.conf: -sentinel monitor my-primary-redis 10.10.10.10 6379 1 -sentinel down-after-milliseconds my-primary-redis 10000 -sentinel config-epoch my-primary-redis 0 -sentinel leader-epoch my-primary-redis 0 +sentinel monitor gitlab-redis 10.10.10.10 6379 1 +sentinel down-after-milliseconds gitlab-redis 10000 +sentinel config-epoch gitlab-redis 0 +sentinel leader-epoch gitlab-redis 0 ``` ```yaml # resque.yaml production: - url: redis://my-primary-redis:6378 + url: redis://:myredispassword@gitlab-redis/ sentinels: - - host: slave1 + host: slave1.example.com # or use ip port: 26380 # point to sentinel, not to redis port - - host: slave2 + host: slave2.exampl.com # or use ip port: 26381 # point to sentinel, not to redis port ``` -- cgit v1.2.1 From eb73cd08086c445210532c746ce305938bc1b53c Mon Sep 17 00:00:00 2001 From: Gabriel Mazetto Date: Fri, 7 Oct 2016 01:16:32 +0200 Subject: Small fixes on Sentinel documentation for CE --- doc/administration/high_availability/redis.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/doc/administration/high_availability/redis.md b/doc/administration/high_availability/redis.md index a1426575abd..97842f50b81 100644 --- a/doc/administration/high_availability/redis.md +++ b/doc/administration/high_availability/redis.md @@ -203,8 +203,6 @@ The following steps should be performed in the [GitLab application server](gitla ```ruby redis['master_name'] = "gitlab-redis" - redis['master_ip'] = "10.0.0.1" - redis['master_port'] = 6379 redis['master_password'] = '' gitlab_rails['redis_sentinels'] = [ {'host' => '10.10.10.1', 'port' => 26379}, -- cgit v1.2.1 From e26d8e0272d822a5086ce073a67039cf0107c9c8 Mon Sep 17 00:00:00 2001 From: Gabriel Mazetto Date: Fri, 7 Oct 2016 01:47:22 +0200 Subject: Updated password examples and improved omnibus troubleshooting --- doc/administration/high_availability/redis.md | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/doc/administration/high_availability/redis.md b/doc/administration/high_availability/redis.md index 97842f50b81..acdfc2773a9 100644 --- a/doc/administration/high_availability/redis.md +++ b/doc/administration/high_availability/redis.md @@ -45,7 +45,7 @@ Redis. redis['bind'] = '0.0.0.0' # If you wish to use Redis authentication (recommended) - redis['password'] = 'Redis Password' + redis['password'] = 'redis-password-goes-here' ``` 1. Run `sudo touch /etc/gitlab/skip-auto-migrations` to prevent database migrations @@ -132,7 +132,7 @@ the master, and `masterauth` in slaves. redis['port'] = 6379 ## Master redis instance - redis['password'] = '' + redis['password'] = 'redis-password-goes-here' ``` 1. Edit `/etc/gitlab/gitlab.rb` of a slave Redis machine (should be one or more machines): @@ -146,7 +146,7 @@ the master, and `masterauth` in slaves. redis['master'] = false redis['master_ip'] = '10.10.10.10' # IP of master Redis server redis['master_port'] = 6379 # Port of master Redis server - redis['master_password'] = "" + redis['master_password'] = "redis-password-goes-here" ``` 1. Reconfigure the GitLab for the changes to take effect: `sudo gitlab-ctl reconfigure` @@ -203,7 +203,7 @@ The following steps should be performed in the [GitLab application server](gitla ```ruby redis['master_name'] = "gitlab-redis" - redis['master_password'] = '' + redis['master_password'] = 'redis-password-goes-here' gitlab_rails['redis_sentinels'] = [ {'host' => '10.10.10.1', 'port' => 26379}, {'host' => '10.10.10.2', 'port' => 26379}, @@ -215,6 +215,21 @@ The following steps should be performed in the [GitLab application server](gitla ### Sentinel troubleshooting +#### Omnibus install + +If you get an error like: `Redis::CannotConnectError: No sentinels available.`, +there may be something wrong with your configuration files or it can be related +to [this issue][gh-531]. + +You must make sure you are defining the same value in `redis['master_name']` +and `redis['master_pasword']` as you defined for your sentinel node. + +The way the redis connector `redis-rb` works with sentinel is a bit +non-intuitive. We try to hide the complexity in omnibus, but it still requires +a few extra configs. + +#### Source install + If you get an error like: `Redis::CannotConnectError: No sentinels available.`, there may be something wrong with your configuration files or it can be related to [this issue][gh-531]. -- cgit v1.2.1 From 95f6cf339a8c0f5d63178a92eaef2d02bcfb297f Mon Sep 17 00:00:00 2001 From: Gabriel Mazetto Date: Sat, 15 Oct 2016 05:40:15 +0200 Subject: Improved documentation on HA sentinel part and Redis replication troubleshooting. --- doc/administration/high_availability/redis.md | 316 +++++++++++++++++++++----- 1 file changed, 256 insertions(+), 60 deletions(-) diff --git a/doc/administration/high_availability/redis.md b/doc/administration/high_availability/redis.md index acdfc2773a9..e416514bedf 100644 --- a/doc/administration/high_availability/redis.md +++ b/doc/administration/high_availability/redis.md @@ -8,6 +8,27 @@ that comes bundled with GitLab Omnibus packages. information. We recommend using a combination of a Redis password and tight firewall rules to secure your Redis service. + + +**Table of Contents** + +- [Configure your own Redis server](#configure-your-own-redis-server) +- [Configure Redis using Omnibus](#configure-redis-using-omnibus) +- [Experimental Redis Sentinel support](#experimental-redis-sentinel-support) + - [Redis setup](#redis-setup) + - [Source install](#source-install) + - [Omnibus Install](#omnibus-install) + - [Troubleshooting Replication](#troubleshooting-replication) + - [Sentinel](#sentinel) + - [Sentinel setup (Community Edition)](#sentinel-setup-community-edition) + - [Sentinel setup (EE Only)](#sentinel-setup-ee-only) + - [GitLab setup](#gitlab-setup) + - [Sentinel troubleshooting](#sentinel-troubleshooting) + - [Omnibus install](#omnibus-install) + - [Source install](#source-install-1) + + + ## Configure your own Redis server If you're hosting GitLab on a cloud provider, you can optionally use a @@ -37,6 +58,7 @@ Redis. unicorn['enable'] = false sidekiq['enable'] = false postgresql['enable'] = false + gitlab_rails['enable'] = false gitlab_workhorse['enable'] = false mailroom['enable'] = false @@ -59,120 +81,294 @@ Redis. ## Experimental Redis Sentinel support -> [Introduced][ce-1877] in GitLab 8.11. +> [Introduced][ce-1877] in GitLab 8.11, improved in 8.13. Since GitLab 8.11, you can configure a list of Redis Sentinel servers that will monitor a group of Redis servers to provide you with a standard failover support. -There is currently one exception to the Sentinel support: `mail_room`, the -component that processes incoming emails. It doesn't support Sentinel yet, but -we hope to integrate a future release that does support it. - To get a better understanding on how to correctly setup Sentinel, please read the [Redis Sentinel documentation](http://redis.io/topics/sentinel) first, as failing to configure it correctly can lead to data loss. +Redis Sentinel can handle the most important tasks in a HA environment to help +keep servers online with minimal to no downtime: + +- Monitors master and slave instances to see if they are available +- Promote a slave to master when the master fails. +- Demote a master to slave when failed master comes back online (to prevent + data-partitioning). +- Can be queried by clients to always connect to the correct master server. + +There is currently one exception to the Sentinel support: `mail_room`, the +component that processes incoming emails. It doesn't support Sentinel yet, but +we hope to integrate a future release that does support it soon. + The configuration consists of three parts: -- Redis setup -- Sentinel setup -- GitLab setup +- Setup Redis Master and Slave nodes +- Setup Sentinel nodes +- Setup GitLab + +> **IMPORTANT**: You need at least 3 independent machines: physical, or VMs +running into distinct physical machines. If you fail to provision the +machines in that specific way, any issue with the shared environment can +bring your entire setup down. Read carefully how to configure those components below. ### Redis setup -You must have at least 2 Redis servers: 1 Master, 1 or more Slaves. +You must have at least `3` Redis servers: `1` Master, `2` Slaves, and they need to +be each in a independent machine (see explanation above). + They should be configured the same way and with similar server specs, as -in a failover situation, any Slave can be elected as the new Master by +in a failover situation, any `Slave` can be elected as the new `Master` by the Sentinel servers. -In a minimal setup, the only required change for the slaves in `redis.conf` -is the addition of a `slaveof` line pointing to the initial master. -You can increase the security by defining a `requirepass` configuration in -the master, and `masterauth` in slaves. +With Sentinel, you must define a password to protect the access as both +Sentinel instances and other redis instances should be able to talk to +each other over the network. ---- +You'll need to define both `requirepass` and `masterauth` in all +nodes because they can be re-configured at any time by the Sentinels +during a failover, and change it's status as `Master` or `Slave`. -**Configuring your own Redis server** +Initial `Slave` nodes will have in `redis.conf` an additional `slaveof` line +pointing to the initial `Master`. -1. Add to the slaves' `redis.conf`: +#### Source install - ```conf - # IP and port of the master Redis server - slaveof 10.10.10.10 6379 - ``` +**Master Redis instance** -1. Optionally, set up password authentication for increased security. - Add the following to master's `redis.conf`: +You need to make the following changes in `redis.conf`: - ```conf - # Optional password authentication for increased security - requirepass "" - ``` +1. Define a `bind` address pointing to a local IP that your other machines + can reach you. If you really need to bind to an external acessible IP, make + sure you add extra firewall rules to prevent unauthorized access: -1. Then add this line to all the slave servers' `redis.conf`: + ```conf + # By default, if no "bind" configuration directive is specified, Redis listens + # for connections from all the network interfaces available on the server. + # It is possible to listen to just one or multiple selected interfaces using + # the "bind" configuration directive, followed by one or more IP addresses. + # + # Examples: + # + # bind 192.168.1.100 10.0.0.1 + # bind 127.0.0.1 ::1 + bind 0.0.0.0 # This will bind to all interfaces + ``` + +1. Define a `port` to force redis to listin on TCP so other machines can + connect to it: + + ```conf + # Accept connections on the specified port, default is 6379 (IANA #815344). + # If port 0 is specified Redis will not listen on a TCP socket. + port 6379 + ``` + +1. Set up password authentication (use the same password in all nodes) ```conf - masterauth "" + requirepass "redis-password-goes-here" + masterauth "redis-password-goes-here" ``` 1. Restart the Redis services for the changes to take effect. ---- +**Slave Redis instance** -**Using Redis via Omnibus** +1. Follow same instructions from master with the extra change in `redis.conf`: -1. Edit `/etc/gitlab/gitlab.rb` of a master Redis machine (usualy a single machine): + ```conf + # IP and port of the master Redis server + slaveof 10.10.10.10 6379 + ``` - ```ruby - ## Redis TCP support (will disable UNIX socket transport) - redis['bind'] = '0.0.0.0' # or specify an IP to bind to a single one - redis['port'] = 6379 +1. Restart the Redis services for the changes to take effect. - ## Master redis instance - redis['password'] = 'redis-password-goes-here' - ``` +#### Omnibus Install -1. Edit `/etc/gitlab/gitlab.rb` of a slave Redis machine (should be one or more machines): +You need to install the omnibus package in 3 different and independent machines. +We will elect one as the initial `Master` and the other 2 as `Slaves`. - ```ruby - ## Redis TCP support (will disable UNIX socket transport) - redis['bind'] = '0.0.0.0' # or specify an IP to bind to a single one - redis['port'] = 6379 +If you are migrating from a single machine install, you may want to setup the +machines as Slaves, pointing to the original machine as `Master`, to migrate +the data first, and than switch to this setup. - ## Slave redis instance - redis['master'] = false - redis['master_ip'] = '10.10.10.10' # IP of master Redis server - redis['master_port'] = 6379 # Port of master Redis server - redis['master_password'] = "redis-password-goes-here" - ``` +To disable redis in the single install, edit `/etc/gitlab/gitlab.rb`: + +```ruby +redis['enable'] = false +``` + +**Master Redis instances** + +You need to make the following changes in `/etc/gitlab/gitlab.rb`: + +1. Define a `redis['bind']` address pointing to a local IP that your other machines + can reach you. If you really need to bind to an external acessible IP, make + sure you add extra firewall rules to prevent unauthorized access. +1. Define a `redis['port']` to force redis to listin on TCP so other machines can + connect to it. +1. Set up password authentication with `redis['master_password']` (use the same + password in all nodes). -1. Reconfigure the GitLab for the changes to take effect: `sudo gitlab-ctl reconfigure` +```ruby +## Redis TCP support (will disable UNIX socket transport) +redis['bind'] = '0.0.0.0' # or specify an IP to bind to a single one +redis['port'] = 6379 +redis['requirepass'] = 'redis-password-goes-here' +redis['master_password'] = 'redis-password-goes-here' +``` + +Reconfigure GitLab Omnibus for the changes to take effect: `sudo gitlab-ctl reconfigure` + +**Slave Redis instances** + +You need to make the same changes listed for the `Master` instance, +with an additional `Slave` section as in the example below: + +```ruby +## Redis TCP support (will disable UNIX socket transport) +redis['bind'] = '0.0.0.0' # or specify an IP to bind to a single one +redis['port'] = 6379 +redis['requirepass'] = 'redis-password-goes-here' +redis['master_password'] = 'redis-password-goes-here' + +## Slave redis instance +redis['master'] = false +redis['master_ip'] = '10.10.10.10' # IP of master Redis server +redis['master_port'] = 6379 # Port of master Redis server +``` + +Reconfigure GitLab Omnibus for the changes to take effect: `sudo gitlab-ctl reconfigure` + +#### Troubleshooting Replication + +You can check if everything is correct by connecting to each server using +`redis-cli` application, and sending the `INFO` command. + +If authentication was correctly defined, it should fail with: +`NOAUTH Authentication required` error. Try to authenticate with the +previous defined password with `AUTH redis-password-goes-here` and +try the `INFO` command again. + +Look for the `# Replication` section where you should see some important +information like the `role` of the server. + +When connected to a `master` redis, you will see the number of connected +`slaves`, and a list of each with connection details. + +When it's a `slave`, you will see details of the master connection and if +its `up` or `down`. --- Now that the Redis servers are all set up, let's configure the Sentinel servers. -### Sentinel setup +If you are not sure if your Redis servers are working and replicating +correctly, please read the [Troubleshooting Replication](#troubleshooting-replication) +and fix it before proceeding with Sentinel setup. + +### Sentinel + +You must have at least `3` Redis Sentinel servers, and they need to +be each in a independent machine. You can install them in the same +machines you installed the other `3` Redis servers. + +This number is required for the consensus algorithm to be effective +in the case of a failure. You should always have and `odd` number +of Sentinel nodes provisioned. -We provide an automated way to setup and run the Sentinel daemon -with GitLab EE. +Here is a simple explanation on how Sentinel handles a failover: -See the instructions below how to setup it by yourself. +When a number of Sentinels (`quorum` value) agree the fact the `master` is +not reachable, the **majority** of the sentinels must elect a temporary +Sentinel `leader`, that will be responsible to start the failover proceedings. -Here is an example configuration file (`sentinel.conf`) for a Sentinel node: +As an example, for a cluster of `3` Sentinels, at least `2` must agree on a +`leader`. If you have total of `5` at least `3` must agree on the leader. + +The `quorum` is only used to detect failure, not to elect the `leader`. + +Official [Sentinel documentation](http://redis.io/topics/sentinel#example-sentinel-deployments) +also lists different network topologies and warns againts situations like +network partition and how it can affect the state of the HA solution. Make +sure you read it carefully and understand the implications in your current +setup. + +To make Sentinel setup easier, ee provide an [automated way to setup and run](#sentinel-setup-ee-only) +the Sentinel daemon with GitLab EE. + +#### Sentinel setup (Community Edition) + +For GitLab CE, you need to install, configure, execute and monitor Sentinel +by yourself. + +Here is an example configuration file (`sentinel.conf`) for a minimal Sentinel +node: ```conf -port 26379 -sentinel monitor gitlab-redis 10.0.0.1 6379 1 +bind 0.0.0.0 # bind to all interfaces or change to a specific IP +port 26379 # default sentinel port +sentinel auth-pass gitlab-redis redis-password-goes-here +sentinel monitor gitlab-redis 10.0.0.1 6379 2 sentinel down-after-milliseconds gitlab-redis 10000 sentinel config-epoch gitlab-redis 0 sentinel leader-epoch gitlab-redis 0 ``` +#### Sentinel setup (EE Only) + +To setup sentinel, you must edit `/etc/gitlab/gitlab.rb` file. +This is a minimal configuration required to run the daemon: + +```ruby +redis['master_name'] = 'gitlab-redis' # must be the same in every sentinel node +redis['master_ip'] = '10.0.0.1' # ip of the initial master redis instance +redis['master_port'] = 6379 # port of the initial master redis instance +redis['master_password'] = 'your-secure-password-here' # the same value defined in redis['password'] in the master instance + +sentinel['enable'] = true +# sentinel['port'] = 26379 + +## Quorum must reflect the amount of voting sentinels it take to start a failover. +sentinel['quorum'] = 2 + +## Consider unresponsive server down after x amount of ms. +# sentinel['down_after_milliseconds'] = 10000 + +# sentinel['failover_timeout'] = 60000 +``` + +When you install Sentinel in a separate machine, you need to control which +other services will be running in it. Take a look at the following variables +and enable or disable whenever it fits your strategy: + +```ruby +# Enabled Redis and Sentinel services +redis['enable'] = true +sentinel['enable'] = true + +# Disabled all other services +redis['enable'] = false +bootstrap['enable'] = false +nginx['enable'] = false +unicorn['enable'] = false +sidekiq['enable'] = false +postgresql['enable'] = false +gitlab_workhorse['enable'] = false +gitlab_rails['enable'] = false +mailroom['enable'] = false +``` + +Remember that enabling a new service may also require additional configuration +params (like `redis` for example). + --- The final part is to inform the main GitLab application server of the Redis @@ -243,7 +439,7 @@ or `gitlab-rails['redis_*']` in Omnibus): ```conf # sentinel.conf: -sentinel monitor gitlab-redis 10.10.10.10 6379 1 +sentinel monitor gitlab-redis 10.10.10.10 6379 2 sentinel down-after-milliseconds gitlab-redis 10000 sentinel config-epoch gitlab-redis 0 sentinel leader-epoch gitlab-redis 0 @@ -276,7 +472,7 @@ To make sure your configuration is correct: sudo gitlab-rails console # For source installations - sudo -u git rails console RAILS_ENV=production + sudo -u git rails console production ``` 1. Run in the console: -- cgit v1.2.1 From c60437786bfe43344b4a5eb040437f73f37c6396 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Sun, 13 Nov 2016 20:35:47 +0100 Subject: Create relation between chat user and GitLab user and allow to authorize them [ci skip] --- app/controllers/profiles/chat_names_controller.rb | 64 ++++++++++++++++++++++ app/models/chat_name.rb | 12 ++++ app/models/user.rb | 1 + app/services/chat_names/find_user_service.rb | 15 +++++ app/services/chat_names/request_service.rb | 32 +++++++++++ app/views/layouts/nav/_profile.html.haml | 4 ++ app/views/profiles/chat_names/index.html.haml | 49 +++++++++++++++++ app/views/profiles/chat_names/new.html.haml | 15 +++++ config/routes/profile.rb | 6 ++ .../20161113184239_create_user_chat_names_table.rb | 20 +++++++ db/schema.rb | 16 +++++- lib/gitlab/chat_name_token.rb | 45 +++++++++++++++ 12 files changed, 278 insertions(+), 1 deletion(-) create mode 100644 app/controllers/profiles/chat_names_controller.rb create mode 100644 app/models/chat_name.rb create mode 100644 app/services/chat_names/find_user_service.rb create mode 100644 app/services/chat_names/request_service.rb create mode 100644 app/views/profiles/chat_names/index.html.haml create mode 100644 app/views/profiles/chat_names/new.html.haml create mode 100644 db/migrate/20161113184239_create_user_chat_names_table.rb create mode 100644 lib/gitlab/chat_name_token.rb diff --git a/app/controllers/profiles/chat_names_controller.rb b/app/controllers/profiles/chat_names_controller.rb new file mode 100644 index 00000000000..8c5b83adaf4 --- /dev/null +++ b/app/controllers/profiles/chat_names_controller.rb @@ -0,0 +1,64 @@ +class Profiles::ChatNamesController < Profiles::ApplicationController + before_action :chat_names + before_action :chat_name_token, only: [:new] + before_action :chat_name_params, only: [:new, :create, :deny] + + def index + end + + def new + end + + def create + new_chat_name = current_user.chat_names.new(chat_name_params) + + if new_chat_name.save + flash[:notice] = "Authorized chat nickname #{new_chat_name.chat_name}" + else + flash[:alert] = "Could not authorize chat nickname. Try again!" + end + + delete_chat_name_token + redirect_to profile_chat_names_path + end + + def deny + delete_chat_name_token + + flash[:alert] = "Denied authorization of chat nickname #{chat_name_params[:user_name]}" + + redirect_to profile_chat_names_path + end + + def destroy + @chat_name = chat_names.find(params[:id]) + + if @chat_name.destroy + flash[:notice] = "Delete chat nickname: #{@chat_name.chat_name}!" + else + flash[:alert] = "Could not delete chat nickname #{@chat_name.chat_name}." + end + + redirect_to profile_chat_names_path + end + + private + + def delete_chat_name_token + chat_name_token.delete + end + + def chat_name_params + @chat_name_params ||= chat_name_token.get || render_404 + end + + def chat_name_token + return render_404 unless params[:token] || render_404 + + @chat_name_token ||= Gitlab::ChatNameToken.new(params[:token]) + end + + def chat_names + @chat_names ||= current_user.chat_names + end +end diff --git a/app/models/chat_name.rb b/app/models/chat_name.rb new file mode 100644 index 00000000000..f321db75eeb --- /dev/null +++ b/app/models/chat_name.rb @@ -0,0 +1,12 @@ +class ChatName < ActiveRecord::Base + belongs_to :service + belongs_to :user + + validates :user, presence: true + validates :service, presence: true + validates :team_id, presence: true + validates :chat_id, presence: true + + validates :user_id, uniqueness: { scope: [:service_id] } + validates :chat_id, uniqueness: { scope: [:service_id, :team_id] } +end diff --git a/app/models/user.rb b/app/models/user.rb index 3813df6684e..10fe69818b6 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -56,6 +56,7 @@ class User < ActiveRecord::Base has_many :personal_access_tokens, dependent: :destroy has_many :identities, dependent: :destroy, autosave: true has_many :u2f_registrations, dependent: :destroy + has_many :chat_names, dependent: :destroy # Groups has_many :members, dependent: :destroy diff --git a/app/services/chat_names/find_user_service.rb b/app/services/chat_names/find_user_service.rb new file mode 100644 index 00000000000..6b7f75430a8 --- /dev/null +++ b/app/services/chat_names/find_user_service.rb @@ -0,0 +1,15 @@ +module ChatNames + class FindUserService + def initialize(chat_names, params) + @chat_names = chat_names + @params = params + end + + def execute + @chat_names.find_by( + team_id: @params[:team_id], + chat_id: @params[:user_id] + ) + end + end +end diff --git a/app/services/chat_names/request_service.rb b/app/services/chat_names/request_service.rb new file mode 100644 index 00000000000..c67b93f932f --- /dev/null +++ b/app/services/chat_names/request_service.rb @@ -0,0 +1,32 @@ +module ChatNames + class RequestService + include Gitlab::Routing.url_helpers + + def initialize(service, params) + @service = service + @params = params + end + + def execute + token = chat_name_token.store!(chat_name_params) + + new_profile_chat_name_url(token: token) if token + end + + private + + def chat_name_token + Gitlab::ChatNameToken.new + end + + def chat_name_params + { + service_id: @service.id, + team_id: @params[:team_id], + team_domain: @params[:team_domain], + chat_id: @params[:user_id], + chat_name: @params[:user_name] + } + end + end +end diff --git a/app/views/layouts/nav/_profile.html.haml b/app/views/layouts/nav/_profile.html.haml index 6d514f669db..e06301bda14 100644 --- a/app/views/layouts/nav/_profile.html.haml +++ b/app/views/layouts/nav/_profile.html.haml @@ -17,6 +17,10 @@ = link_to applications_profile_path, title: 'Applications' do %span Applications + = nav_link(controller: :chat_names) do + = link_to profile_chat_names_path, title: 'Chat' do + %span + Chat = nav_link(controller: :personal_access_tokens) do = link_to profile_personal_access_tokens_path, title: 'Access Tokens' do %span diff --git a/app/views/profiles/chat_names/index.html.haml b/app/views/profiles/chat_names/index.html.haml new file mode 100644 index 00000000000..f90ac4c6a03 --- /dev/null +++ b/app/views/profiles/chat_names/index.html.haml @@ -0,0 +1,49 @@ +- page_title "Chat" += render 'profiles/head' + +.row.prepend-top-default + .col-lg-3.profile-settings-sidebar + %h4.prepend-top-0 + = page_title + %p + You can see your Chat integrations. + + .col-lg-9 + %h5 Active chat names (#{@chat_names.length}) + + - if @chat_names.present? + .table-responsive + %table.table.chat-names + %thead + %tr + %th Project + %th Service + %th Team domain + %th Nickname + %th Created + %th + %tbody + - @chat_names.each do |chat_name| + - service = chat_name.service + - project = service.project + %tr + %td + %strong + - if can?(current_user, :read_project, project) + = link_to project.name_with_namespace, project_path(project) + - else + .light N/A + %td + %strong + - if can?(current_user, :admin_project, project) + = link_to service.title, edit_namespace_project_service_path(project.namespace, project, service) + - else + = chat_name.service.title + %td= chat_name.team_domain + %td= chat_name.chat_name + %td= chat_name.created_at + %td= link_to "Remove", profile_chat_name_path(chat_name), method: :delete, class: "btn btn-danger pull-right", data: { confirm: "Are you sure you want to revoke this nickname?" } + + - else + .settings-message.text-center + You don't have any active chat names. diff --git a/app/views/profiles/chat_names/new.html.haml b/app/views/profiles/chat_names/new.html.haml new file mode 100644 index 00000000000..0b9ee8c71ad --- /dev/null +++ b/app/views/profiles/chat_names/new.html.haml @@ -0,0 +1,15 @@ +%h3.page-title Authorization required +%main{:role => "main"} + %p.h4 + Authorize the chat user + %strong.text-info= @chat_name_params[:chat_name] + to use your account? + + %hr/ + .actions + = form_tag profile_chat_names_path, method: :post do + = hidden_field_tag :token, @chat_name_token.token + = submit_tag "Authorize", class: "btn btn-success wide pull-left" + = form_tag deny_profile_chat_names_path, method: :delete do + = hidden_field_tag :token, @chat_name_token.token + = submit_tag "Deny", class: "btn btn-danger prepend-left-10" diff --git a/config/routes/profile.rb b/config/routes/profile.rb index 52b9a565db8..6b91485da9e 100644 --- a/config/routes/profile.rb +++ b/config/routes/profile.rb @@ -23,6 +23,12 @@ resource :profile, only: [:show, :update] do resource :preferences, only: [:show, :update] resources :keys, only: [:index, :show, :new, :create, :destroy] resources :emails, only: [:index, :create, :destroy] + resources :chat_names, only: [:index, :new, :create, :destroy] do + collection do + delete :deny + end + end + resource :avatar, only: [:destroy] resources :personal_access_tokens, only: [:index, :create] do diff --git a/db/migrate/20161113184239_create_user_chat_names_table.rb b/db/migrate/20161113184239_create_user_chat_names_table.rb new file mode 100644 index 00000000000..f9ab2adf2a9 --- /dev/null +++ b/db/migrate/20161113184239_create_user_chat_names_table.rb @@ -0,0 +1,20 @@ +class CreateUserChatNamesTable < ActiveRecord::Migration + include Gitlab::Database::MigrationHelpers + + DOWNTIME = false + + def change + create_table :chat_names do |t| + t.integer "user_id", null: false + t.integer "service_id", null: false + t.string "team_id" + t.string "team_domain" + t.string "chat_id" + t.string "chat_name" + t.timestamps + end + + add_index :chat_names, [:user_id, :service_id], unique: true + add_index :chat_names, [:service_id, :team_id, :chat_id], unique: true + end +end diff --git a/db/schema.rb b/db/schema.rb index 62c325a52d7..5f25f0a305f 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20161106185620) do +ActiveRecord::Schema.define(version: 20161113184239) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -149,6 +149,20 @@ ActiveRecord::Schema.define(version: 20161106185620) do t.text "message_html" end + create_table "chat_names", force: :cascade do |t| + t.integer "user_id", null: false + t.integer "service_id", null: false + t.string "team_id" + t.string "team_domain" + t.string "chat_id" + t.string "chat_name" + t.datetime "created_at" + t.datetime "updated_at" + end + + add_index "chat_names", ["service_id", "team_id", "user_id"], name: "index_chat_names_on_service_id_and_team_id_and_user_id", unique: true, using: :btree + add_index "chat_names", ["user_id", "service_id"], name: "index_chat_names_on_user_id_and_service_id", unique: true, using: :btree + create_table "ci_application_settings", force: :cascade do |t| t.boolean "all_broken_builds" t.boolean "add_pusher" diff --git a/lib/gitlab/chat_name_token.rb b/lib/gitlab/chat_name_token.rb new file mode 100644 index 00000000000..c8349839219 --- /dev/null +++ b/lib/gitlab/chat_name_token.rb @@ -0,0 +1,45 @@ +require 'json' + +module Gitlab + class ChatNameToken + attr_reader :token + + TOKEN_LENGTH = 50 + EXPIRY_TIME = 1800 + + def initialize(token = new_token) + @token = token + end + + def get + Gitlab::Redis.with do |redis| + data = redis.get(redis_key) + JSON.parse(data, symbolize_names: true) if data + end + end + + def store!(params) + Gitlab::Redis.with do |redis| + params = params.to_json + redis.set(redis_key, params, ex: EXPIRY_TIME) + token + end + end + + def delete + Gitlab::Redis.with do |redis| + redis.del(redis_key) + end + end + + private + + def new_token + Devise.friendly_token(TOKEN_LENGTH) + end + + def redis_key + "gitlab:chat_names:#{token}" + end + end +end -- cgit v1.2.1 From 0f951737af830163260a9535990a18fba130b443 Mon Sep 17 00:00:00 2001 From: Achilleas Pipinellis Date: Wed, 16 Nov 2016 13:56:31 +0100 Subject: Remove experimental heading and move note at the top [ci skip] --- doc/administration/high_availability/redis.md | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/doc/administration/high_availability/redis.md b/doc/administration/high_availability/redis.md index a87c1dde55d..f9bc4f59345 100644 --- a/doc/administration/high_availability/redis.md +++ b/doc/administration/high_availability/redis.md @@ -1,5 +1,11 @@ # Configuring Redis for GitLab HA +> +Experimental Redis Sentinel support was [Introduced][ce-1877] in GitLab 8.11. +Starting with 8.14, Redis Sentinel is no longer experimental. +If you've used it with versions `< 8.14` before, please check the updated +documentation here. + High Availability with [Redis] is possible using a **Master** x **Slave** topology with a [Redis Sentinel][sentinel] service to watch and automatically start the failover procedure. @@ -52,7 +58,6 @@ Omnibus GitLab packages. - [Troubleshooting Redis replication](#troubleshooting-redis-replication) - [Troubleshooting Sentinel](#troubleshooting-sentinel) - [Changelog](#changelog) - - [Experimental Redis Sentinel support](#experimental-redis-sentinel-support) - [Further reading](#further-reading) @@ -871,14 +876,6 @@ Changes to Redis HA over time. - Experimental Redis Sentinel support was added -### Experimental Redis Sentinel support - -> -Experimental Redis Sentinel support was [Introduced][ce-1877] in GitLab 8.11. -Starting with 8.14, Redis Sentinel is no longer experimental. -If you used with versions `< 8.14` before, please check the updated -documentation here. - ## Further reading Read more on High Availability: -- cgit v1.2.1 From d5dc44f0588690fb79ddc2aa3ee150012d5e4873 Mon Sep 17 00:00:00 2001 From: Achilleas Pipinellis Date: Wed, 16 Nov 2016 13:59:52 +0100 Subject: Minor edits in Redis HA source install [ci skip] --- doc/administration/high_availability/redis_source.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/doc/administration/high_availability/redis_source.md b/doc/administration/high_availability/redis_source.md index e7a8f47355d..8558ba82d63 100644 --- a/doc/administration/high_availability/redis_source.md +++ b/doc/administration/high_availability/redis_source.md @@ -345,12 +345,12 @@ We have a more detailed [Troubleshooting](redis.md#troubleshooting) explained in the documentation for Omnibus GitLab installations. Here we will list only the things that are specific to a source installation. -If you get an error in GitLab like: `Redis::CannotConnectError: No sentinels available.`, +If you get an error in GitLab like `Redis::CannotConnectError: No sentinels available.`, there may be something wrong with your configuration files or it can be related to [this upstream issue][gh-531]. -It's a bit non-intuitive the way you have to config `resque.yml` and -`sentinel.conf`, otherwise `redis-rb` will not work properly. +You must make sure that `resque.yml` and `sentinel.conf` are configured correctly, +otherwise `redis-rb` will not work properly. The `master-group-name` ('gitlab-redis') defined in (`sentinel.conf`) **must** be used as the hostname in GitLab (`resque.yml`): @@ -379,7 +379,7 @@ production: port: 26379 # point to sentinel, not to redis port ``` -When in doubt, please read [Redis Sentinel documentation](http://redis.io/topics/sentinel) +When in doubt, please read [Redis Sentinel documentation](http://redis.io/topics/sentinel). [gh-531]: https://github.com/redis/redis-rb/issues/531 [downloads]: https://about.gitlab.com/downloads -- cgit v1.2.1 From 02a2f3138d51f6f2dc1555f8aa3e802e1a5404de Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Wed, 16 Nov 2016 13:04:29 +0000 Subject: Fixed issue boards counter border when unauthorized When unauthorized the border on the counter in issue boards didn't span the whole way around Closes #23664 --- app/views/projects/boards/components/_board.html.haml | 2 +- changelogs/unreleased/issue-boards-counter-border-fix.yml | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) create mode 100644 changelogs/unreleased/issue-boards-counter-border-fix.yml diff --git a/app/views/projects/boards/components/_board.html.haml b/app/views/projects/boards/components/_board.html.haml index 47165c70097..a2e5118a9f3 100644 --- a/app/views/projects/boards/components/_board.html.haml +++ b/app/views/projects/boards/components/_board.html.haml @@ -7,7 +7,7 @@ data: { container: "body", placement: "bottom" } } {{ list.title }} .board-issue-count-holder.pull-right.clearfix{ "v-if" => 'list.type !== "blank"' } - %span.board-issue-count.pull-left{ ":class" => '{ "has-btn": list.type !== "done" }' } + %span.board-issue-count.pull-left{ ":class" => '{ "has-btn": list.type !== "done" && !disabled }' } {{ list.issuesSize }} - if can?(current_user, :admin_issue, @project) %button.btn.btn-small.btn-default.pull-right.has-tooltip{ type: "button", diff --git a/changelogs/unreleased/issue-boards-counter-border-fix.yml b/changelogs/unreleased/issue-boards-counter-border-fix.yml new file mode 100644 index 00000000000..c98adb6af7c --- /dev/null +++ b/changelogs/unreleased/issue-boards-counter-border-fix.yml @@ -0,0 +1,4 @@ +--- +title: Fixed issue boards counter border when unauthorized +merge_request: +author: -- cgit v1.2.1 From c44474150c8a82e62ed1e0ed5758b1f38bbf7c41 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Wed, 16 Nov 2016 11:51:47 +0200 Subject: Limit labels returned for a specific project as an administrator Prior, an administrator viewing a project's Labels page would see _all_ labels from every project they had access to, rather than only the labels of that specific project (if any). This was not an information disclosure, as admins have access to everything, but it was a performance issue. --- app/finders/labels_finder.rb | 47 +++++++++++++++----------------- changelogs/unreleased/rs-issue-24527.yml | 4 +++ spec/finders/labels_finder_spec.rb | 15 ++++++++++ 3 files changed, 41 insertions(+), 25 deletions(-) create mode 100644 changelogs/unreleased/rs-issue-24527.yml diff --git a/app/finders/labels_finder.rb b/app/finders/labels_finder.rb index 865f093f04a..fa0e2a5e3d8 100644 --- a/app/finders/labels_finder.rb +++ b/app/finders/labels_finder.rb @@ -6,7 +6,7 @@ class LabelsFinder < UnionFinder def execute(skip_authorization: false) @skip_authorization = skip_authorization - items = find_union(label_ids, Label) + items = find_union(label_ids, Label) || Label.none items = with_title(items) sort(items) end @@ -18,9 +18,11 @@ class LabelsFinder < UnionFinder def label_ids label_ids = [] - if project - label_ids << project.group.labels if project.group.present? - label_ids << project.labels + if project? + if project + label_ids << project.group.labels if project.group.present? + label_ids << project.labels + end else label_ids << Label.where(group_id: projects.group_ids) label_ids << Label.where(project_id: projects.select(:id)) @@ -40,16 +42,16 @@ class LabelsFinder < UnionFinder items.where(title: title) end - def group_id - params[:group_id].presence + def group? + params[:group_id].present? end - def project_id - params[:project_id].presence + def project? + params[:project_id].present? end - def projects_ids - params[:project_ids] + def projects? + params[:project_ids].present? end def title @@ -59,8 +61,9 @@ class LabelsFinder < UnionFinder def project return @project if defined?(@project) - if project_id - @project = find_project + if project? + @project = Project.find(params[:project_id]) + @project = nil unless authorized_to_read_labels?(@project) else @project = nil end @@ -68,26 +71,20 @@ class LabelsFinder < UnionFinder @project end - def find_project - if skip_authorization - Project.find_by(id: project_id) - else - available_projects.find_by(id: project_id) - end - end - def projects return @projects if defined?(@projects) - @projects = skip_authorization ? Project.all : available_projects - @projects = @projects.in_namespace(group_id) if group_id - @projects = @projects.where(id: projects_ids) if projects_ids + @projects = skip_authorization ? Project.all : ProjectsFinder.new.execute(current_user) + @projects = @projects.in_namespace(params[:group_id]) if group? + @projects = @projects.where(id: params[:project_ids]) if projects? @projects = @projects.reorder(nil) @projects end - def available_projects - @available_projects ||= ProjectsFinder.new.execute(current_user) + def authorized_to_read_labels?(project) + return true if skip_authorization + + Ability.allowed?(current_user, :read_label, project) end end diff --git a/changelogs/unreleased/rs-issue-24527.yml b/changelogs/unreleased/rs-issue-24527.yml new file mode 100644 index 00000000000..a7b6358e60e --- /dev/null +++ b/changelogs/unreleased/rs-issue-24527.yml @@ -0,0 +1,4 @@ +--- +title: Limit labels returned for a specific project as an administrator +merge_request: 7496 +author: diff --git a/spec/finders/labels_finder_spec.rb b/spec/finders/labels_finder_spec.rb index 10cfb66ec1c..9085cc8debf 100644 --- a/spec/finders/labels_finder_spec.rb +++ b/spec/finders/labels_finder_spec.rb @@ -64,6 +64,21 @@ describe LabelsFinder do expect(finder.execute).to eq [group_label_2, project_label_1, group_label_1] end + + context 'as an administrator' do + it 'does not return labels from another project' do + # Purposefully creating a project with _nothing_ associated to it + isolated_project = create(:empty_project) + admin = create(:admin) + + # project_3 has a label associated to it, which we don't want coming + # back when we ask for the isolated project's labels + project_3.team << [admin, :reporter] + finder = described_class.new(admin, project_id: isolated_project.id) + + expect(finder.execute).to be_empty + end + end end context 'filtering by title' do -- cgit v1.2.1 From 465c61ee6889e03016712cbda7fd48e0e973cb76 Mon Sep 17 00:00:00 2001 From: Achilleas Pipinellis Date: Wed, 16 Nov 2016 14:17:58 +0100 Subject: Remove login as root step from Redis HA docs [ci skip] --- doc/administration/high_availability/redis.md | 31 ++++++++------------------- 1 file changed, 9 insertions(+), 22 deletions(-) diff --git a/doc/administration/high_availability/redis.md b/doc/administration/high_availability/redis.md index f9bc4f59345..1fa3560e932 100644 --- a/doc/administration/high_availability/redis.md +++ b/doc/administration/high_availability/redis.md @@ -107,10 +107,12 @@ to help keep servers online with minimal to no downtime. Redis Sentinel: - Promotes a **Slave** to **Master** when the **Master** fails - Demotes a **Master** to **Slave** when the failed **Master** comes back online (to prevent data-partitioning) -- Can be queried by clients to always connect to the current **Master** server +- Can be queried by the application to always connect to the current **Master** + server -When a **Master** fails to respond, it's the client's responsibility to handle -timeout and reconnect (querying a **Sentinel** for a new **Master**). +When a **Master** fails to respond, it's the application's responsibility +(in our case GitLab) to handle timeout and reconnect (querying a **Sentinel** +for a new **Master**). To get a better understanding on how to correctly setup Sentinel, please read the [Redis Sentinel documentation](http://redis.io/topics/sentinel) first, as @@ -289,12 +291,7 @@ The prerequisites for a HA Redis setup are the following: ### Step 1. Configuring the master Redis instance -1. SSH into the **master** Redis server and login as root: - - ``` - sudo -i - ``` - +1. SSH into the **master** Redis server. 1. [Download/install](https://about.gitlab.com/installation) the Omnibus GitLab package you want using **steps 1 and 2** from the GitLab downloads page. - Make sure you select the correct Omnibus package, with the same version @@ -334,12 +331,7 @@ The prerequisites for a HA Redis setup are the following: ### Step 2. Configuring the slave Redis instances -1. SSH into the **slave** Redis server and login as root: - - ``` - sudo -i - ``` - +1. SSH into the **slave** Redis server. 1. [Download/install](https://about.gitlab.com/installation) the Omnibus GitLab package you want using **steps 1 and 2** from the GitLab downloads page. - Make sure you select the correct Omnibus package, with the same version @@ -417,12 +409,7 @@ multiple machines with the Sentinel daemon. --- -1. SSH into the server that will host Redis Sentinel and login as root: - - ``` - sudo -i - ``` - +1. SSH into the server that will host Redis Sentinel. 1. **You can omit this step if the Sentinels will be hosted in the same node as the other Redis instances.** @@ -437,7 +424,6 @@ multiple machines with the Sentinel daemon. Sentinels in the same node as the other Redis instances, some values might be duplicate below): - ```ruby redis_sentinel_role['enable'] = true @@ -530,6 +516,7 @@ it needs to access at least one of the listed. The following steps should be performed in the [GitLab application server](gitlab.md) which ideally should not have Redis or Sentinels on it for a HA setup. +1. SSH into the server where the GitLab application is installed. 1. Edit `/etc/gitlab/gitlab.rb` and add/change the following lines: ``` -- cgit v1.2.1 From d64183e1fa26ab77107e3a2a20be1fe4df3a1875 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Wed, 16 Nov 2016 14:56:30 +0100 Subject: Add most of specs for chat names --- app/services/chat_names/authorize_user_service.rb | 38 +++++++++++++++++++++ app/services/chat_names/find_user_service.rb | 17 ++++++++-- app/services/chat_names/request_service.rb | 32 ------------------ app/views/profiles/chat_names/index.html.haml | 14 +++++--- .../20161113184239_create_user_chat_names_table.rb | 15 +++++---- db/schema.rb | 11 +++--- lib/gitlab/chat_name_token.rb | 2 +- spec/factories/chat_names.rb | 16 +++++++++ spec/lib/gitlab/chat_name_token_spec.rb | 39 ++++++++++++++++++++++ spec/models/chat_name_spec.rb | 16 +++++++++ spec/models/user_spec.rb | 1 + .../chat_names/authorize_user_service_spec.rb | 27 +++++++++++++++ spec/services/chat_names/find_user_service_spec.rb | 36 ++++++++++++++++++++ 13 files changed, 212 insertions(+), 52 deletions(-) create mode 100644 app/services/chat_names/authorize_user_service.rb delete mode 100644 app/services/chat_names/request_service.rb create mode 100644 spec/factories/chat_names.rb create mode 100644 spec/lib/gitlab/chat_name_token_spec.rb create mode 100644 spec/models/chat_name_spec.rb create mode 100644 spec/services/chat_names/authorize_user_service_spec.rb create mode 100644 spec/services/chat_names/find_user_service_spec.rb diff --git a/app/services/chat_names/authorize_user_service.rb b/app/services/chat_names/authorize_user_service.rb new file mode 100644 index 00000000000..321bf3a9205 --- /dev/null +++ b/app/services/chat_names/authorize_user_service.rb @@ -0,0 +1,38 @@ +module ChatNames + class AuthorizeUserService + include Gitlab::Routing.url_helpers + + def initialize(service, params) + @service = service + @params = params + end + + def execute + return unless chat_name_params.values.all?(&:present?) + + token = request_token + + new_profile_chat_name_url(token: token) if token + end + + private + + def request_token + chat_name_token.store!(chat_name_params) + end + + def chat_name_token + Gitlab::ChatNameToken.new + end + + def chat_name_params + { + service_id: @service.id, + team_id: @params[:team_id], + team_domain: @params[:team_domain], + chat_id: @params[:user_id], + chat_name: @params[:user_name] + } + end + end +end diff --git a/app/services/chat_names/find_user_service.rb b/app/services/chat_names/find_user_service.rb index 6b7f75430a8..28e3e155be1 100644 --- a/app/services/chat_names/find_user_service.rb +++ b/app/services/chat_names/find_user_service.rb @@ -1,12 +1,23 @@ module ChatNames class FindUserService - def initialize(chat_names, params) - @chat_names = chat_names + def initialize(service, params) + @service = service @params = params end def execute - @chat_names.find_by( + chat_name = find_chat_name + return unless chat_name + + chat_name.update(used_at: Time.now) + chat_name.user + end + + private + + def find_chat_name + ChatName.find_by( + service: @service, team_id: @params[:team_id], chat_id: @params[:user_id] ) diff --git a/app/services/chat_names/request_service.rb b/app/services/chat_names/request_service.rb deleted file mode 100644 index c67b93f932f..00000000000 --- a/app/services/chat_names/request_service.rb +++ /dev/null @@ -1,32 +0,0 @@ -module ChatNames - class RequestService - include Gitlab::Routing.url_helpers - - def initialize(service, params) - @service = service - @params = params - end - - def execute - token = chat_name_token.store!(chat_name_params) - - new_profile_chat_name_url(token: token) if token - end - - private - - def chat_name_token - Gitlab::ChatNameToken.new - end - - def chat_name_params - { - service_id: @service.id, - team_id: @params[:team_id], - team_domain: @params[:team_domain], - chat_id: @params[:user_id], - chat_name: @params[:user_name] - } - end - end -end diff --git a/app/views/profiles/chat_names/index.html.haml b/app/views/profiles/chat_names/index.html.haml index f90ac4c6a03..ae0b6336944 100644 --- a/app/views/profiles/chat_names/index.html.haml +++ b/app/views/profiles/chat_names/index.html.haml @@ -1,4 +1,4 @@ -- page_title "Chat" +- page_title 'Chat' = render 'profiles/head' .row.prepend-top-default @@ -20,7 +20,7 @@ %th Service %th Team domain %th Nickname - %th Created + %th Last used %th %tbody - @chat_names.each do |chat_name| @@ -41,8 +41,14 @@ = chat_name.service.title %td= chat_name.team_domain %td= chat_name.chat_name - %td= chat_name.created_at - %td= link_to "Remove", profile_chat_name_path(chat_name), method: :delete, class: "btn btn-danger pull-right", data: { confirm: "Are you sure you want to revoke this nickname?" } + %td= + - if chat_name.used_at + time_ago_with_tooltip(chat_name.used_at) + - else + Never + + %td + = link_to 'Remove', profile_chat_name_path(chat_name), method: :delete, class: 'btn btn-danger pull-right', data: { confirm: 'Are you sure you want to revoke this nickname?' } - else .settings-message.text-center diff --git a/db/migrate/20161113184239_create_user_chat_names_table.rb b/db/migrate/20161113184239_create_user_chat_names_table.rb index f9ab2adf2a9..1e138fba202 100644 --- a/db/migrate/20161113184239_create_user_chat_names_table.rb +++ b/db/migrate/20161113184239_create_user_chat_names_table.rb @@ -5,13 +5,14 @@ class CreateUserChatNamesTable < ActiveRecord::Migration def change create_table :chat_names do |t| - t.integer "user_id", null: false - t.integer "service_id", null: false - t.string "team_id" - t.string "team_domain" - t.string "chat_id" - t.string "chat_name" - t.timestamps + t.integer :user_id, null: false + t.integer :service_id, null: false + t.string :team_id, null: false + t.string :team_domain + t.string :chat_id, null: false + t.string :chat_name + t.datetime :used_at + t.timestamps null: false end add_index :chat_names, [:user_id, :service_id], unique: true diff --git a/db/schema.rb b/db/schema.rb index 5f25f0a305f..1c1a7a37096 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -152,15 +152,16 @@ ActiveRecord::Schema.define(version: 20161113184239) do create_table "chat_names", force: :cascade do |t| t.integer "user_id", null: false t.integer "service_id", null: false - t.string "team_id" + t.string "team_id", null: false t.string "team_domain" - t.string "chat_id" + t.string "chat_id", null: false t.string "chat_name" - t.datetime "created_at" - t.datetime "updated_at" + t.datetime "used_at" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false end - add_index "chat_names", ["service_id", "team_id", "user_id"], name: "index_chat_names_on_service_id_and_team_id_and_user_id", unique: true, using: :btree + add_index "chat_names", ["service_id", "team_id", "chat_id"], name: "index_chat_names_on_service_id_and_team_id_and_chat_id", unique: true, using: :btree add_index "chat_names", ["user_id", "service_id"], name: "index_chat_names_on_user_id_and_service_id", unique: true, using: :btree create_table "ci_application_settings", force: :cascade do |t| diff --git a/lib/gitlab/chat_name_token.rb b/lib/gitlab/chat_name_token.rb index c8349839219..543f038a4d4 100644 --- a/lib/gitlab/chat_name_token.rb +++ b/lib/gitlab/chat_name_token.rb @@ -5,7 +5,7 @@ module Gitlab attr_reader :token TOKEN_LENGTH = 50 - EXPIRY_TIME = 1800 + EXPIRY_TIME = 10.minutes # 10 minutes def initialize(token = new_token) @token = token diff --git a/spec/factories/chat_names.rb b/spec/factories/chat_names.rb new file mode 100644 index 00000000000..24225468d55 --- /dev/null +++ b/spec/factories/chat_names.rb @@ -0,0 +1,16 @@ +FactoryGirl.define do + factory :chat_name, class: ChatName do + user factory: :user + service factory: :service + + team_id 'T0001' + team_domain 'Awesome Team' + + sequence :chat_id do |n| + "U#{n}" + end + sequence :chat_name do |n| + "user#{n}" + end + end +end diff --git a/spec/lib/gitlab/chat_name_token_spec.rb b/spec/lib/gitlab/chat_name_token_spec.rb new file mode 100644 index 00000000000..8d7e7a99059 --- /dev/null +++ b/spec/lib/gitlab/chat_name_token_spec.rb @@ -0,0 +1,39 @@ +require 'spec_helper' + +describe Gitlab::ChatNameToken, lib: true do + context 'when using unknown token' do + let(:token) { } + + subject { described_class.new(token).get } + + it 'returns empty data' do + is_expected.to be_nil + end + end + + context 'when storing data' do + let(:data) { + { key: 'value' } + } + + subject { described_class.new(@token) } + + before do + @token = described_class.new.store!(data) + end + + it 'returns stored data' do + expect(subject.get).to eq(data) + end + + context 'and after deleting them' do + before do + subject.delete + end + + it 'data are removed' do + expect(subject.get).to be_nil + end + end + end +end diff --git a/spec/models/chat_name_spec.rb b/spec/models/chat_name_spec.rb new file mode 100644 index 00000000000..b02971cab82 --- /dev/null +++ b/spec/models/chat_name_spec.rb @@ -0,0 +1,16 @@ +require 'spec_helper' + +describe ChatName, models: true do + subject { create(:chat_name) } + + it { is_expected.to belong_to(:service) } + it { is_expected.to belong_to(:user) } + + it { is_expected.to validate_presence_of(:user) } + it { is_expected.to validate_presence_of(:service) } + it { is_expected.to validate_presence_of(:team_id) } + it { is_expected.to validate_presence_of(:chat_id) } + + it { is_expected.to validate_uniqueness_of(:user_id).scoped_to(:service_id) } + it { is_expected.to validate_uniqueness_of(:chat_id).scoped_to(:service_id, :team_id) } +end diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index 3b152e15b61..be6767855c7 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -33,6 +33,7 @@ describe User, models: true do it { is_expected.to have_many(:award_emoji).dependent(:destroy) } it { is_expected.to have_many(:builds).dependent(:nullify) } it { is_expected.to have_many(:pipelines).dependent(:nullify) } + it { is_expected.to have_many(:chat_names).dependent(:destroy) } describe '#group_members' do it 'does not include group memberships for which user is a requester' do diff --git a/spec/services/chat_names/authorize_user_service_spec.rb b/spec/services/chat_names/authorize_user_service_spec.rb new file mode 100644 index 00000000000..f8c26e51bfc --- /dev/null +++ b/spec/services/chat_names/authorize_user_service_spec.rb @@ -0,0 +1,27 @@ +require 'spec_helper' + +describe ChatNames::AuthorizeUserService, services: true do + describe '#execute' do + let(:service) { create(:service) } + + subject { described_class.new(service, params).execute } + + context 'when all parameters are valid' do + let(:params) { { team_id: 'T0001', team_domain: 'myteam', user_id: 'U0001', user_name: 'user' } } + + it 'requests a new token' do + is_expected.to include('http') + is_expected.to include('://') + is_expected.to include('token=') + end + end + + context 'when there are missing parameters' do + let(:params) { { } } + + it 'does not request a new token' do + is_expected.to be_nil + end + end + end +end diff --git a/spec/services/chat_names/find_user_service_spec.rb b/spec/services/chat_names/find_user_service_spec.rb new file mode 100644 index 00000000000..cf5844069f9 --- /dev/null +++ b/spec/services/chat_names/find_user_service_spec.rb @@ -0,0 +1,36 @@ +require 'spec_helper' + +describe ChatNames::FindUserService, services: true do + describe '#execute' do + let(:service) { create(:service) } + + subject { described_class.new(service, params).execute } + + context 'find user mapping' do + let(:user) { create(:user) } + let!(:chat_name) { create(:chat_name, user: user, service: service) } + + context 'when existing user is requested' do + let(:params) { { team_id: chat_name.team_id, user_id: chat_name.chat_id } } + + it 'returns existing user' do + is_expected.to eq(user) + end + + it 'updates when last time chat name was used' do + subject + + expect(chat_name.reload.used_at).to be_like_time(Time.now) + end + end + + context 'when different user is requested' do + let(:params) { { team_id: chat_name.team_id, user_id: 'non-existing-user' } } + + it 'returns existing user' do + is_expected.to be_nil + end + end + end + end +end -- cgit v1.2.1 From 6715091bbc035142237f5121623bb0b84c3def51 Mon Sep 17 00:00:00 2001 From: Achilleas Pipinellis Date: Wed, 19 Oct 2016 11:54:38 +0200 Subject: WIP refactor environments --- doc/ci/environments.md | 159 +++++++++++++++++++++++++++++++-------- doc/ci/img/deployments_view.png | Bin 0 -> 179909 bytes doc/ci/img/environments_view.png | Bin 0 -> 57534 bytes doc/ci/yaml/README.md | 62 ++++++++++++++- doc/user/permissions.md | 4 +- 5 files changed, 190 insertions(+), 35 deletions(-) create mode 100644 doc/ci/img/deployments_view.png create mode 100644 doc/ci/img/environments_view.png diff --git a/doc/ci/environments.md b/doc/ci/environments.md index e070302fb82..9d65621fbbb 100644 --- a/doc/ci/environments.md +++ b/doc/ci/environments.md @@ -3,69 +3,166 @@ >**Note:** Introduced in GitLab 8.9. -## Environments +During the development of a software there can be many stages until it's ready +for public consumption. You sure want to first see your code in a testing or +staging environment before you release it to the public. That way you can +prevent bugs not only in your software, but in the deployment process as well. + +With environments you can control the Continuous Deployment of your software all +within GitLab. All you need to do is define them in your project's +[`.gitlab-ci.yml`][yaml]. + +In the following sections, we'll see how that works. + +## An environments example + +Let's assume that you have + +1. Define the environments in `.gitlab-ci.yml` +1. Push the repository to GitLab +1. Runner picks up the job +1. The job finishes successfully +1. The environments get created if they don't already exist +1. A deployment is recorded remembering the environment name and the Git SHA of + the last commit of the pipeline + +Further runs of the CI will + +Actions + +View environments +View deployments + Rollback deployments + Run deployments +View link to environment URL +View last commit message of deployment +View person who performed the deployment +View commit SHA that triggered the deployment +View branch the deployment was based on +View time ago the deployment was performed + +Environments are like tags for your CI jobs, describing where code gets deployed. + +You can think of names such as testing, staging or production. -Environments are places where code gets deployed, such as staging or production. CI/CD [Pipelines] usually have one or more [jobs] that deploy to an environment. + Defining environments in a project's `.gitlab-ci.yml` lets developers track [deployments] to these environments. -## Deployments +The environments page can only be viewed by Reporters and above. For more +information on the permissions, see the [permissions documentation][permissions]. -Deployments are created when [jobs] deploy versions of code to [environments]. +### Defining environments -### Checkout deployments locally +While you can create and delete environments manually in the web interface, we +recommend that you define your environments in `.gitlab-ci.yml` first. They will +be automatically created for you after the first deploy. -Since 8.13, a reference in the git repository is saved for each deployment. So -knowing what the state is of your current environments is only a `git fetch` -away. +The `environment` keyword is just a hint for GitLab that this job actually +deploys to this environment. Each time the job succeeds, a deployment is +recorded, remembering the Git SHA and environment name. -In your git config, append the `[remote ""]` block with an extra -fetch line: +Add something like this to your `.gitlab-ci.yml`: ``` -fetch = +refs/environments/*:refs/remotes/origin/environments/* +production: + stage: deploy + script: make deploy-to-prod + environment: + name: production ``` -## Defining environments +See the [yaml definition](yaml/README.md#environment) of environments. -You can create and delete environments manually in the web interface, but we -recommend that you define your environments in `.gitlab-ci.yml` first, which -will automatically create environments for you after the first deploy. +### View the environment status -The `environment` is just a hint for GitLab that this job actually deploys to -this environment. Each time the job succeeds, a deployment is recorded, -remembering the git SHA and environment. +GitLab keeps track of your deployments, so you always know what is currently +being deployed on your servers. You can find the environment list under +**Pipelines > Environments** for your project. You'll see the git SHA and date +of the last deployment to each environment defined. + +![Environments](img/environments_view.png) + +>**Note:** +Only deploys that happen after your `.gitlab-ci.yml` is properly configured will +show up in the "Environment" and "Last deployment" lists. + +## Manually deploying to environments + + +## Dynamic environments + +As the name suggests, it is possible to create environments on the fly by just +declaring their names dynamically in `.gitlab-ci.yml`. + +GitLab Runner exposes various [environment variables][variables] when a job runs, +and as such you can use them -Add something like this to your `.gitlab-ci.yml`: ``` -production: +review: stage: deploy - script: dpl... - environment: production + script: + - rsync -av --delete public /srv/nginx/pages/$CI_BUILD_REF_NAME + environment: + name: review/$CI_BUILD_REF_NAME + url: https://$CI_BUILD_REF_NAME.example.com ``` -See full [documentation](yaml/README.md#environment). +### Closing an environment -## Seeing environment status +``` +review: + stage: deploy + script: + - rsync -av --delete public /srv/nginx/pages/$CI_BUILD_REF_NAME + environment: + name: review/$CI_BUILD_REF_NAME + url: http://$CI_BUILD_REF_NAME.$APPS_DOMAIN + on_stop: stop_review + +stop_review: + script: rm -rf /srv/nginx/pages/$CI_BUILD_REF_NAME + when: manual + environment: + name: review/$CI_BUILD_REF_NAME + action: stop +``` -You can find the environment list under **Pipelines > Environments** for your -project. You'll see the git SHA and date of the last deployment to each -environment defined. +## The relationship between deployments and environments ->**Note:** -Only deploys that happen after your `.gitlab-ci.yml` is properly configured will -show up in the environments and deployments lists. +Deployments are created when [jobs] deploy versions of code to [environments], +so every environment can have one or more deployments. GitLab keeps track of +your deployments, so you always know what is currently being deployed on your +servers. -## Seeing deployment history +### View the deployment history Clicking on an environment will show the history of deployments. +![Deployments](img/deployments_view.png) + >**Note:** Only deploys that happen after your `.gitlab-ci.yml` is properly configured will show up in the environments and deployments lists. +### Checkout deployments locally + +Since 8.13, a reference in the git repository is saved for each deployment. So +knowing what the state is of your current environments is only a `git fetch` +away. + +In your git config, append the `[remote ""]` block with an extra +fetch line: + +``` +fetch = +refs/environments/*:refs/remotes/origin/environments/* +``` + [Pipelines]: pipelines.md [jobs]: yaml/README.md#jobs +[yaml]: yaml/README.md [environments]: #environments [deployments]: #deployments +[permissions]: ../user/permissions.md +[variables]: variables/README.md diff --git a/doc/ci/img/deployments_view.png b/doc/ci/img/deployments_view.png new file mode 100644 index 00000000000..250e051bf42 Binary files /dev/null and b/doc/ci/img/deployments_view.png differ diff --git a/doc/ci/img/environments_view.png b/doc/ci/img/environments_view.png new file mode 100644 index 00000000000..131a9718cc4 Binary files /dev/null and b/doc/ci/img/environments_view.png differ diff --git a/doc/ci/yaml/README.md b/doc/ci/yaml/README.md index 5c0e1c44e3f..5c7d300e3d9 100644 --- a/doc/ci/yaml/README.md +++ b/doc/ci/yaml/README.md @@ -573,7 +573,8 @@ In its simplest form, the `environment` keyword can be defined like: deploy to production: stage: deploy script: git push production HEAD:master - environment: production + environment: + name: production ``` In the above example, the `deploy to production` job will be marked as doing a @@ -673,6 +674,61 @@ The `stop_review_app` job is **required** to have the following keywords defined - `environment:name` - `environment:action` +#### environment:name + +#### environment:url + +Optional. + +#### environment:on_stop + +> [Introduced][ce-6669] in GitLab 8.13. + +Closing environments can be achieved with the `on_stop` keyword defined under +`environment`. It declares a different job that has to be run in order to close +the environment. + +This job is required to have the following keywords defined: + +- `when` - [reference](#when) +- `environment:name` +- `environment:action` - reference below + +See below for an example. + +#### environment:action + +> [Introduced][ce-6669] in GitLab 8.13. + +The `action` keyword is to be used in conjunction with `on_stop` and is defined +in the job that depends on the one that was called from. + +Take for instance: + +```yaml +review: + stage: deploy + script: make deploy-app + environment: + name: review + on_stop: stop_review + +stop_review: + stage: deploy + script: make delete-app + when: manual + environment: + name: review + action: stop +``` + +In the above example we set up the `review` job to deploy to the `review` +environment, and we also defined a new `stop_review` job under `on_stop`. +Once the `review` job is successfully finished, it will trigger the `stop_review` +job based on what is defined under `when`. In this case we set it up to `manual` +so it will need a [manual action](#manual-actions) via GitLab's web interface +in order to run. + #### dynamic environments > [Introduced][ce-6323] in GitLab 8.12 and GitLab Runner 1.6. @@ -681,7 +737,9 @@ The `stop_review_app` job is **required** to have the following keywords defined These parameters can use any of the defined [CI variables](#variables) (including predefined, secure variables and `.gitlab-ci.yml` variables). -For example: +--- + +**Example configurations** ``` deploy as review app: diff --git a/doc/user/permissions.md b/doc/user/permissions.md index d6216a8dd50..a33183fa01c 100644 --- a/doc/user/permissions.md +++ b/doc/user/permissions.md @@ -32,6 +32,8 @@ The following table depicts the various user permission levels in a project. | See a commit status | | ✓ | ✓ | ✓ | ✓ | | See a container registry | | ✓ | ✓ | ✓ | ✓ | | See environments | | ✓ | ✓ | ✓ | ✓ | +| Create new environments | | | ✓ | ✓ | ✓ | +| Delete environments | | | | ✓ | ✓ | | See a list of merge requests | | ✓ | ✓ | ✓ | ✓ | | Manage/Accept merge requests | | | ✓ | ✓ | ✓ | | Create new merge request | | | ✓ | ✓ | ✓ | @@ -45,7 +47,6 @@ The following table depicts the various user permission levels in a project. | Create or update commit status | | | ✓ | ✓ | ✓ | | Update a container registry | | | ✓ | ✓ | ✓ | | Remove a container registry image | | | ✓ | ✓ | ✓ | -| Create new environments | | | ✓ | ✓ | ✓ | | Create new milestones | | | | ✓ | ✓ | | Add new team members | | | | ✓ | ✓ | | Push to protected branches | | | | ✓ | ✓ | @@ -58,7 +59,6 @@ The following table depicts the various user permission levels in a project. | Manage runners | | | | ✓ | ✓ | | Manage build triggers | | | | ✓ | ✓ | | Manage variables | | | | ✓ | ✓ | -| Delete environments | | | | ✓ | ✓ | | Switch visibility level | | | | | ✓ | | Transfer project to another namespace | | | | | ✓ | | Remove project | | | | | ✓ | -- cgit v1.2.1 From 8666fd7613e0e4c3eaaf3e730f0cb0ac75aff245 Mon Sep 17 00:00:00 2001 From: Achilleas Pipinellis Date: Fri, 4 Nov 2016 11:20:44 +0100 Subject: Begin writing Overview section --- doc/ci/environments.md | 57 +++++++++++++++++++++++----------------------- doc/ci/yaml/README.md | 62 ++------------------------------------------------ 2 files changed, 30 insertions(+), 89 deletions(-) diff --git a/doc/ci/environments.md b/doc/ci/environments.md index 9d65621fbbb..60541c44016 100644 --- a/doc/ci/environments.md +++ b/doc/ci/environments.md @@ -3,20 +3,38 @@ >**Note:** Introduced in GitLab 8.9. -During the development of a software there can be many stages until it's ready -for public consumption. You sure want to first see your code in a testing or -staging environment before you release it to the public. That way you can -prevent bugs not only in your software, but in the deployment process as well. - -With environments you can control the Continuous Deployment of your software all +During the development of a software, there can be many stages until it's ready +for public consumption. You sure want to first test your code and then deploy it +in a testing or staging environment before you release it to the public. That +way you can prevent bugs not only in your software, but in the deployment +process as well. + +In case you use GitLab CI to not only test or build your project, but also +deploy it in your infrastructure, GitLab provides a way to track your deployments +so you always know what is currently being deployed on your servers. With +environments you can control the Continuous Deployment of your software all within GitLab. All you need to do is define them in your project's -[`.gitlab-ci.yml`][yaml]. +[`.gitlab-ci.yml`][yaml]. GitLab provides a full history of your deployments per +every environment. + +## Overview + +Deployments are created when [jobs] deploy versions of code to environments, +so every environment can have one or more deployments. GitLab keeps track of +your deployments, so you always know what is currently being deployed on your +servers. + +Environments are like tags for your CI jobs, describing where code gets deployed. +CI/CD [Pipelines] usually have one or more [jobs] that deploy to an environment. +You can think of names such as testing, staging or production. -In the following sections, we'll see how that works. +Defining environments in a project's `.gitlab-ci.yml` lets developers track +[deployments] to these environments. -## An environments example +The environments page can only be viewed by Reporters and above. For more +information on the permissions, see the [permissions documentation][permissions]. -Let's assume that you have +Let's assume that you have: 1. Define the environments in `.gitlab-ci.yml` 1. Push the repository to GitLab @@ -41,18 +59,6 @@ View commit SHA that triggered the deployment View branch the deployment was based on View time ago the deployment was performed -Environments are like tags for your CI jobs, describing where code gets deployed. - -You can think of names such as testing, staging or production. - -CI/CD [Pipelines] usually have one or more [jobs] that deploy to an environment. - -Defining environments in a project's `.gitlab-ci.yml` lets developers track -[deployments] to these environments. - -The environments page can only be viewed by Reporters and above. For more -information on the permissions, see the [permissions documentation][permissions]. - ### Defining environments While you can create and delete environments manually in the web interface, we @@ -129,13 +135,6 @@ stop_review: action: stop ``` -## The relationship between deployments and environments - -Deployments are created when [jobs] deploy versions of code to [environments], -so every environment can have one or more deployments. GitLab keeps track of -your deployments, so you always know what is currently being deployed on your -servers. - ### View the deployment history Clicking on an environment will show the history of deployments. diff --git a/doc/ci/yaml/README.md b/doc/ci/yaml/README.md index 5c7d300e3d9..5c0e1c44e3f 100644 --- a/doc/ci/yaml/README.md +++ b/doc/ci/yaml/README.md @@ -573,8 +573,7 @@ In its simplest form, the `environment` keyword can be defined like: deploy to production: stage: deploy script: git push production HEAD:master - environment: - name: production + environment: production ``` In the above example, the `deploy to production` job will be marked as doing a @@ -674,61 +673,6 @@ The `stop_review_app` job is **required** to have the following keywords defined - `environment:name` - `environment:action` -#### environment:name - -#### environment:url - -Optional. - -#### environment:on_stop - -> [Introduced][ce-6669] in GitLab 8.13. - -Closing environments can be achieved with the `on_stop` keyword defined under -`environment`. It declares a different job that has to be run in order to close -the environment. - -This job is required to have the following keywords defined: - -- `when` - [reference](#when) -- `environment:name` -- `environment:action` - reference below - -See below for an example. - -#### environment:action - -> [Introduced][ce-6669] in GitLab 8.13. - -The `action` keyword is to be used in conjunction with `on_stop` and is defined -in the job that depends on the one that was called from. - -Take for instance: - -```yaml -review: - stage: deploy - script: make deploy-app - environment: - name: review - on_stop: stop_review - -stop_review: - stage: deploy - script: make delete-app - when: manual - environment: - name: review - action: stop -``` - -In the above example we set up the `review` job to deploy to the `review` -environment, and we also defined a new `stop_review` job under `on_stop`. -Once the `review` job is successfully finished, it will trigger the `stop_review` -job based on what is defined under `when`. In this case we set it up to `manual` -so it will need a [manual action](#manual-actions) via GitLab's web interface -in order to run. - #### dynamic environments > [Introduced][ce-6323] in GitLab 8.12 and GitLab Runner 1.6. @@ -737,9 +681,7 @@ in order to run. These parameters can use any of the defined [CI variables](#variables) (including predefined, secure variables and `.gitlab-ci.yml` variables). ---- - -**Example configurations** +For example: ``` deploy as review app: -- cgit v1.2.1 From dd3c2714689421ad782bbbb3f9c09e81b8b20e71 Mon Sep 17 00:00:00 2001 From: Achilleas Pipinellis Date: Mon, 7 Nov 2016 20:57:18 +0100 Subject: Move paragraph to overview --- doc/ci/environments.md | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/doc/ci/environments.md b/doc/ci/environments.md index 60541c44016..0c1557eeaef 100644 --- a/doc/ci/environments.md +++ b/doc/ci/environments.md @@ -11,20 +11,21 @@ process as well. In case you use GitLab CI to not only test or build your project, but also deploy it in your infrastructure, GitLab provides a way to track your deployments -so you always know what is currently being deployed on your servers. With -environments you can control the Continuous Deployment of your software all -within GitLab. All you need to do is define them in your project's -[`.gitlab-ci.yml`][yaml]. GitLab provides a full history of your deployments per -every environment. +so you always know what is currently being deployed on your servers. ## Overview +With environments, you can control the Continuous Deployment of your software all +within GitLab. All you need to do is define them in your project's +[`.gitlab-ci.yml`][yaml] as we will explore below. GitLab provides a full +history of your deployments per every environment. + +Environments are like tags for your CI jobs, describing where code gets deployed. Deployments are created when [jobs] deploy versions of code to environments, so every environment can have one or more deployments. GitLab keeps track of your deployments, so you always know what is currently being deployed on your servers. -Environments are like tags for your CI jobs, describing where code gets deployed. CI/CD [Pipelines] usually have one or more [jobs] that deploy to an environment. You can think of names such as testing, staging or production. -- cgit v1.2.1 From 1c994dbc05c147714479288126742f3fee158fd8 Mon Sep 17 00:00:00 2001 From: Nick Thomas Date: Tue, 15 Nov 2016 15:02:44 +0000 Subject: Fix POST /internal/allowed to cope with gitlab-shell v4.0.0 project paths gitlab-shell v3.6.6 would give project paths like so: * namespace/project gitlab-shell v4.0.0 can give project paths like so: * /namespace1/namespace2/project * /namespace/project * /path/to/repository/storage/namespace1/namespace2/project * /path/to/repository/storage/namespace/project --- app/models/repository.rb | 10 ---- .../24496-fix-internal-api-project-lookup.yml | 4 ++ lib/api/helpers/internal_helpers.rb | 57 ++++++++++++++++++++++ lib/api/internal.rb | 38 +-------------- spec/models/repository_spec.rb | 10 ---- spec/requests/api/api_internal_helpers_spec.rb | 32 ++++++++++++ spec/requests/api/internal_spec.rb | 36 ++++++++++++-- 7 files changed, 126 insertions(+), 61 deletions(-) create mode 100644 changelogs/unreleased/24496-fix-internal-api-project-lookup.yml create mode 100644 lib/api/helpers/internal_helpers.rb create mode 100644 spec/requests/api/api_internal_helpers_spec.rb diff --git a/app/models/repository.rb b/app/models/repository.rb index 4282197faa5..57acd279a02 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -15,16 +15,6 @@ class Repository Gitlab.config.repositories.storages end - def self.remove_storage_from_path(repo_path) - storages.find do |_, storage_path| - if repo_path.start_with?(storage_path) - return repo_path.sub(storage_path, '') - end - end - - repo_path - end - def initialize(path_with_namespace, project) @path_with_namespace = path_with_namespace @project = project diff --git a/changelogs/unreleased/24496-fix-internal-api-project-lookup.yml b/changelogs/unreleased/24496-fix-internal-api-project-lookup.yml new file mode 100644 index 00000000000..a95295c00f3 --- /dev/null +++ b/changelogs/unreleased/24496-fix-internal-api-project-lookup.yml @@ -0,0 +1,4 @@ +--- +title: Fix POST /internal/allowed to cope with gitlab-shell v4.0.0 project paths +merge_request: 7480 +author: diff --git a/lib/api/helpers/internal_helpers.rb b/lib/api/helpers/internal_helpers.rb new file mode 100644 index 00000000000..eb223c1101d --- /dev/null +++ b/lib/api/helpers/internal_helpers.rb @@ -0,0 +1,57 @@ +module API + module Helpers + module InternalHelpers + # Project paths may be any of the following: + # * /repository/storage/path/namespace/project + # * /namespace/project + # * namespace/project + # + # In addition, they may have a '.git' extension and multiple namespaces + # + # Transform all these cases to 'namespace/project' + def clean_project_path(project_path, storage_paths = Repository.storages.values) + project_path = project_path.sub(/\.git\z/, '') + + storage_paths.each do |storage_path| + storage_path = File.expand_path(storage_path) + + if project_path.start_with?(storage_path) + project_path = project_path.sub(storage_path, '') + break + end + end + + project_path.sub(/\A\//, '') + end + + def project_path + @project_path ||= clean_project_path(params[:project]) + end + + def wiki? + @wiki ||= project_path.end_with?('.wiki') && + !Project.find_with_namespace(project_path) + end + + def project + @project ||= begin + # Check for *.wiki repositories. + # Strip out the .wiki from the pathname before finding the + # project. This applies the correct project permissions to + # the wiki repository as well. + project_path.chomp!('.wiki') if wiki? + + Project.find_with_namespace(project_path) + end + end + + def ssh_authentication_abilities + [ + :read_project, + :download_code, + :push_code + ] + end + end + end +end diff --git a/lib/api/internal.rb b/lib/api/internal.rb index ccf181402f9..7087ce11401 100644 --- a/lib/api/internal.rb +++ b/lib/api/internal.rb @@ -3,6 +3,8 @@ module API class Internal < Grape::API before { authenticate_by_gitlab_shell_token! } + helpers ::API::Helpers::InternalHelpers + namespace 'internal' do # Check if git command is allowed to project # @@ -14,42 +16,6 @@ module API # ref - branch name # forced_push - forced_push # protocol - Git access protocol being used, e.g. HTTP or SSH - # - - helpers do - def project_path - @project_path ||= begin - project_path = params[:project].sub(/\.git\z/, '') - Repository.remove_storage_from_path(project_path) - end - end - - def wiki? - @wiki ||= project_path.end_with?('.wiki') && - !Project.find_with_namespace(project_path) - end - - def project - @project ||= begin - # Check for *.wiki repositories. - # Strip out the .wiki from the pathname before finding the - # project. This applies the correct project permissions to - # the wiki repository as well. - project_path.chomp!('.wiki') if wiki? - - Project.find_with_namespace(project_path) - end - end - - def ssh_authentication_abilities - [ - :read_project, - :download_code, - :push_code - ] - end - end - post "/allowed" do status 200 diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb index fe26b4ac18c..c93ec08b822 100644 --- a/spec/models/repository_spec.rb +++ b/spec/models/repository_spec.rb @@ -1534,14 +1534,4 @@ describe Repository, models: true do end.to raise_error(Repository::CommitError) end end - - describe '#remove_storage_from_path' do - let(:storage_path) { project.repository_storage_path } - let(:project_path) { project.path_with_namespace } - let(:full_path) { File.join(storage_path, project_path) } - - it { expect(Repository.remove_storage_from_path(full_path)).to eq(project_path) } - it { expect(Repository.remove_storage_from_path(project_path)).to eq(project_path) } - it { expect(Repository.remove_storage_from_path(storage_path)).to eq('') } - end end diff --git a/spec/requests/api/api_internal_helpers_spec.rb b/spec/requests/api/api_internal_helpers_spec.rb new file mode 100644 index 00000000000..be4bc39ada2 --- /dev/null +++ b/spec/requests/api/api_internal_helpers_spec.rb @@ -0,0 +1,32 @@ +require 'spec_helper' + +describe ::API::Helpers::InternalHelpers do + include ::API::Helpers::InternalHelpers + + describe '.clean_project_path' do + project = 'namespace/project' + namespaced = File.join('namespace2', project) + + { + File.join(Dir.pwd, project) => project, + File.join(Dir.pwd, namespaced) => namespaced, + project => project, + namespaced => namespaced, + project + '.git' => project, + namespaced + '.git' => namespaced, + "/" + project => project, + "/" + namespaced => namespaced, + }.each do |project_path, expected| + context project_path do + # Relative and absolute storage paths, with and without trailing / + ['.', './', Dir.pwd, Dir.pwd + '/'].each do |storage_path| + context "storage path is #{storage_path}" do + subject { clean_project_path(project_path, [storage_path]) } + + it { is_expected.to eq(expected) } + end + end + end + end + end +end diff --git a/spec/requests/api/internal_spec.rb b/spec/requests/api/internal_spec.rb index f0f590b0331..8f1a1f9e827 100644 --- a/spec/requests/api/internal_spec.rb +++ b/spec/requests/api/internal_spec.rb @@ -191,6 +191,26 @@ describe API::API, api: true do expect(json_response["status"]).to be_truthy expect(json_response["repository_path"]).to eq(project.repository.path_to_repo) end + + context 'project as /namespace/project' do + it do + pull(key, project_with_repo_path('/' + project.path_with_namespace)) + + expect(response).to have_http_status(200) + expect(json_response["status"]).to be_truthy + expect(json_response["repository_path"]).to eq(project.repository.path_to_repo) + end + end + + context 'project as namespace/project' do + it do + pull(key, project_with_repo_path(project.path_with_namespace)) + + expect(response).to have_http_status(200) + expect(json_response["status"]).to be_truthy + expect(json_response["repository_path"]).to eq(project.repository.path_to_repo) + end + end end end @@ -299,7 +319,7 @@ describe API::API, api: true do context 'project does not exist' do it do - pull(key, OpenStruct.new(path_with_namespace: 'gitlab/notexists')) + pull(key, project_with_repo_path('gitlab/notexist')) expect(response).to have_http_status(200) expect(json_response["status"]).to be_falsey @@ -392,11 +412,17 @@ describe API::API, api: true do end end + def project_with_repo_path(path) + double().tap do |fake_project| + allow(fake_project).to receive_message_chain('repository.path_to_repo' => path) + end + end + def pull(key, project, protocol = 'ssh') post( api("/internal/allowed"), key_id: key.id, - project: project.path_with_namespace, + project: project.repository.path_to_repo, action: 'git-upload-pack', secret_token: secret_token, protocol: protocol @@ -408,7 +434,7 @@ describe API::API, api: true do api("/internal/allowed"), changes: 'd14d6c0abdd253381df51a723d58691b2ee1ab08 570e7b2abdd848b95f2f578043fc23bd6f6fd24d refs/heads/master', key_id: key.id, - project: project.path_with_namespace, + project: project.repository.path_to_repo, action: 'git-receive-pack', secret_token: secret_token, protocol: protocol @@ -420,7 +446,7 @@ describe API::API, api: true do api("/internal/allowed"), ref: 'master', key_id: key.id, - project: project.path_with_namespace, + project: project.repository.path_to_repo, action: 'git-upload-archive', secret_token: secret_token, protocol: 'ssh' @@ -432,7 +458,7 @@ describe API::API, api: true do api("/internal/lfs_authenticate"), key_id: key_id, secret_token: secret_token, - project: project.path_with_namespace + project: project.repository.path_to_repo ) end end -- cgit v1.2.1 From d47fca53db5840e85d38b35058d4bbd94ea917db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Coutable?= Date: Wed, 16 Nov 2016 15:17:09 +0100 Subject: Allow commit note to be visible if repo is visible MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rémy Coutable --- app/helpers/events_helper.rb | 4 ++-- app/models/event.rb | 6 ++--- ...ctivity-page-does-not-show-commits-comments.yml | 4 ++++ spec/models/event_spec.rb | 27 ++++++++++++++++++++++ 4 files changed, 36 insertions(+), 5 deletions(-) create mode 100644 changelogs/unreleased/23824-activity-page-does-not-show-commits-comments.yml diff --git a/app/helpers/events_helper.rb b/app/helpers/events_helper.rb index 00e64076408..f1a0b929d82 100644 --- a/app/helpers/events_helper.rb +++ b/app/helpers/events_helper.rb @@ -86,7 +86,7 @@ module EventsHelper elsif event.merge_request? namespace_project_merge_request_url(event.project.namespace, event.project, event.merge_request) - elsif event.note? && event.commit_note? + elsif event.commit_note? namespace_project_commit_url(event.project.namespace, event.project, event.note_target) elsif event.note? @@ -127,7 +127,7 @@ module EventsHelper end def event_note_target_path(event) - if event.note? && event.commit_note? + if event.commit_note? namespace_project_commit_path(event.project.namespace, event.project, event.note_target, diff --git a/app/models/event.rb b/app/models/event.rb index c76d88b1c7b..21eaca917b8 100644 --- a/app/models/event.rb +++ b/app/models/event.rb @@ -62,7 +62,7 @@ class Event < ActiveRecord::Base end def visible_to_user?(user = nil) - if push? + if push? || commit_note? Ability.allowed?(user, :download_code, project) elsif membership_changed? true @@ -283,7 +283,7 @@ class Event < ActiveRecord::Base end def commit_note? - target.for_commit? + note? && target && target.for_commit? end def issue_note? @@ -295,7 +295,7 @@ class Event < ActiveRecord::Base end def project_snippet_note? - target.for_snippet? + note? && target && target.for_snippet? end def note_target diff --git a/changelogs/unreleased/23824-activity-page-does-not-show-commits-comments.yml b/changelogs/unreleased/23824-activity-page-does-not-show-commits-comments.yml new file mode 100644 index 00000000000..48f733f9c5e --- /dev/null +++ b/changelogs/unreleased/23824-activity-page-does-not-show-commits-comments.yml @@ -0,0 +1,4 @@ +--- +title: Allow commit note to be visible if repo is visible +merge_request: +author: diff --git a/spec/models/event_spec.rb b/spec/models/event_spec.rb index 29a3af68a9b..b684053cd02 100644 --- a/spec/models/event_spec.rb +++ b/spec/models/event_spec.rb @@ -94,6 +94,7 @@ describe Event, models: true do let(:admin) { create(:admin) } let(:issue) { create(:issue, project: project, author: author, assignee: assignee) } let(:confidential_issue) { create(:issue, :confidential, project: project, author: author, assignee: assignee) } + let(:note_on_commit) { create(:note_on_commit, project: project) } let(:note_on_issue) { create(:note_on_issue, noteable: issue, project: project) } let(:note_on_confidential_issue) { create(:note_on_issue, noteable: confidential_issue, project: project) } let(:event) { Event.new(project: project, target: target, author_id: author.id) } @@ -103,6 +104,32 @@ describe Event, models: true do project.team << [guest, :guest] end + context 'commit note event' do + let(:target) { note_on_commit } + + it do + aggregate_failures do + expect(event.visible_to_user?(non_member)).to eq true + expect(event.visible_to_user?(member)).to eq true + expect(event.visible_to_user?(guest)).to eq true + expect(event.visible_to_user?(admin)).to eq true + end + end + + context 'private project' do + let(:project) { create(:empty_project, :private) } + + it do + aggregate_failures do + expect(event.visible_to_user?(non_member)).to eq false + expect(event.visible_to_user?(member)).to eq true + expect(event.visible_to_user?(guest)).to eq false + expect(event.visible_to_user?(admin)).to eq true + end + end + end + end + context 'issue event' do context 'for non confidential issues' do let(:target) { issue } -- cgit v1.2.1 From 3e336475141524dfaf583a05288fc82545955ac7 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Wed, 16 Nov 2016 14:54:07 +0000 Subject: Stopped multiple requests with dropdowns Opening the user dropdown currently sends 2 requests. This has been changed so only one is sent Closes #24131 --- app/assets/javascripts/gl_dropdown.js | 2 +- changelogs/unreleased/user-dropdown-multiple-requests-fix.yml | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) create mode 100644 changelogs/unreleased/user-dropdown-multiple-requests-fix.yml diff --git a/app/assets/javascripts/gl_dropdown.js b/app/assets/javascripts/gl_dropdown.js index 98e43c4d088..c016f25a7d3 100644 --- a/app/assets/javascripts/gl_dropdown.js +++ b/app/assets/javascripts/gl_dropdown.js @@ -249,7 +249,7 @@ _this.fullData = data; _this.parseData(_this.fullData); _this.focusTextInput(); - if (_this.options.filterable && _this.filter && _this.filter.input) { + if (_this.options.filterable && _this.filter && _this.filter.input && _this.filter.input.val().trim() !== '') { return _this.filter.input.trigger('input'); } }; diff --git a/changelogs/unreleased/user-dropdown-multiple-requests-fix.yml b/changelogs/unreleased/user-dropdown-multiple-requests-fix.yml new file mode 100644 index 00000000000..a83441b852a --- /dev/null +++ b/changelogs/unreleased/user-dropdown-multiple-requests-fix.yml @@ -0,0 +1,4 @@ +--- +title: Fixed multiple requests sent when opening dropdowns +merge_request: +author: -- cgit v1.2.1 From c61a9b0f1a12972dd3c2ae4e968c11c72ced6756 Mon Sep 17 00:00:00 2001 From: Marin Jankovski Date: Wed, 16 Nov 2016 16:24:46 +0100 Subject: Correct the idle_timeout in the docs for installation from source. --- doc/administration/reply_by_email.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/administration/reply_by_email.md b/doc/administration/reply_by_email.md index b42892eef68..14cd7a03826 100644 --- a/doc/administration/reply_by_email.md +++ b/doc/administration/reply_by_email.md @@ -197,7 +197,7 @@ To set up a basic Postfix mail server with IMAP access on Ubuntu, follow # The mailbox where incoming mail will end up. Usually "inbox". mailbox: "inbox" # The IDLE command timeout. - email_idle_timeout: 60 + idle_timeout: 60 ``` ```yaml @@ -228,7 +228,7 @@ To set up a basic Postfix mail server with IMAP access on Ubuntu, follow # The mailbox where incoming mail will end up. Usually "inbox". mailbox: "inbox" # The IDLE command timeout. - email_idle_timeout: 60 + idle_timeout: 60 ``` 1. Enable `mail_room` in the init script at `/etc/default/gitlab`: @@ -286,7 +286,7 @@ To set up a basic Postfix mail server with IMAP access on Ubuntu, follow # The mailbox where incoming mail will end up. Usually "inbox". mailbox: "inbox" # The IDLE command timeout. - email_idle_timeout: 60 + idle_timeout: 60 ``` As mentioned, the part after `+` is ignored, and this will end up in the mailbox for `gitlab-incoming@gmail.com`. -- cgit v1.2.1 From 21f1bee2c6ebb4ab307fab19e6560245a9fdb0d7 Mon Sep 17 00:00:00 2001 From: Achilleas Pipinellis Date: Wed, 16 Nov 2016 17:13:08 +0100 Subject: Add section on defining environments [ci skip] --- doc/ci/environments.md | 166 ++++++++++++++++++-------- doc/ci/img/environments_available_staging.png | Bin 0 -> 27398 bytes 2 files changed, 114 insertions(+), 52 deletions(-) create mode 100644 doc/ci/img/environments_available_staging.png diff --git a/doc/ci/environments.md b/doc/ci/environments.md index 0c1557eeaef..b3d4ec40830 100644 --- a/doc/ci/environments.md +++ b/doc/ci/environments.md @@ -3,20 +3,21 @@ >**Note:** Introduced in GitLab 8.9. -During the development of a software, there can be many stages until it's ready +During the development of software, there can be many stages until it's ready for public consumption. You sure want to first test your code and then deploy it in a testing or staging environment before you release it to the public. That way you can prevent bugs not only in your software, but in the deployment process as well. -In case you use GitLab CI to not only test or build your project, but also -deploy it in your infrastructure, GitLab provides a way to track your deployments -so you always know what is currently being deployed on your servers. +GitLab CI is capable of not only testing or building your projects, but also +deploying them in your infrastructure, with the added benefit of giving you a +way to track your deployments. In other words, you can always know what is +currently being deployed or has been deployed on your servers. ## Overview -With environments, you can control the Continuous Deployment of your software all -within GitLab. All you need to do is define them in your project's +With environments, you can control the Continuous Deployment of your software +all within GitLab. All you need to do is define them in your project's [`.gitlab-ci.yml`][yaml] as we will explore below. GitLab provides a full history of your deployments per every environment. @@ -26,63 +27,97 @@ so every environment can have one or more deployments. GitLab keeps track of your deployments, so you always know what is currently being deployed on your servers. -CI/CD [Pipelines] usually have one or more [jobs] that deploy to an environment. -You can think of names such as testing, staging or production. +To better understand how environments and deployments work, let's consider an +example. We assume that you have already created a project in GitLab and set up +a Runner. The example will cover the following: -Defining environments in a project's `.gitlab-ci.yml` lets developers track -[deployments] to these environments. +- We are developing an application +- We want to run tests and build our app on all branches +- Our default branch is `master` +- We deploy the app only when a pipeline on `master` branch is run -The environments page can only be viewed by Reporters and above. For more -information on the permissions, see the [permissions documentation][permissions]. +Let's see how it all ties together. -Let's assume that you have: +## Defining environments -1. Define the environments in `.gitlab-ci.yml` -1. Push the repository to GitLab -1. Runner picks up the job -1. The job finishes successfully -1. The environments get created if they don't already exist -1. A deployment is recorded remembering the environment name and the Git SHA of - the last commit of the pipeline +Let's consider the following `.gitlab-ci.yml` example: -Further runs of the CI will +```yaml +stages: + - test + - build + - deploy -Actions +test: + stage: test + script: echo "Running tests" -View environments -View deployments - Rollback deployments - Run deployments -View link to environment URL -View last commit message of deployment -View person who performed the deployment -View commit SHA that triggered the deployment -View branch the deployment was based on -View time ago the deployment was performed +build: + stage: build + script: echo "Building the app" -### Defining environments +deploy_staging: + stage: deploy + script: + - echo "Deploy to staging server" + environment: + name: staging + url: https://staging.example.com + only: + - master +``` -While you can create and delete environments manually in the web interface, we -recommend that you define your environments in `.gitlab-ci.yml` first. They will -be automatically created for you after the first deploy. +We have defined 3 [stages](yaml/README.md#stages): -The `environment` keyword is just a hint for GitLab that this job actually -deploys to this environment. Each time the job succeeds, a deployment is -recorded, remembering the Git SHA and environment name. +- test +- build +- deploy -Add something like this to your `.gitlab-ci.yml`: +The jobs assigned to these stages will run in this order. If a job fails, then +the builds that are assigned to the next stage won't run, rendering the pipeline +as failed. In our case, the `test` job will run first, then the `build` and +lastly the `deploy_staging`. With this, we ensure that first the tests pass, +then our app is able to be built successfully, and lastly we deploy to the +staging server. -``` -production: - stage: deploy - script: make deploy-to-prod - environment: - name: production -``` +With the above `.gitlab-ci.yml` we have achieved that: -See the [yaml definition](yaml/README.md#environment) of environments. +- All branches will run the `test` and `build` jobs. +- The `deploy_staging` job will [only](yaml/README.md#only) run on the `master` + branch which means all merge requests +- When a merge request is merged, all jobs will run and the `deploy_staging` + in particular will deploy our code to a staging server while the deployment + will be recorded in an environment named `staging`. -### View the environment status +The `environment` keyword is just a hint for GitLab that this job actually +deploys to this environment. Each time the job succeeds, a deployment is +recorded, remembering the Git SHA and environment name. Here's how the +Environments page looks so far. + +![Staging environment view](img/environments_available_staging.png) + +TODO: describe what the above page means + +>**Notes:** +- While you can create environments manually in the web interface, we recommend + that you define your environments in `.gitlab-ci.yml` first. They will + be automatically created for you after the first deploy. +- The environments page can only be viewed by Reporters and above. For more + information on the permissions, see the [permissions documentation][permissions]. + +As we've pointed in the Overview section, environments are like tags for your +CI jobs, describing where code gets deployed. Here's what happened behind the +scenes: + +1. The jobs and environments were defined in `.gitlab-ci.yml` +1. Changes were pushed to the repository in GitLab +1. The Runner(s) picked up the jobs +1. The jobs finished successfully +1. The environments got created if they didn't already exist +1. A deployment was recorded remembering the environment name and the Git SHA of + the last commit of the pipeline + +## View the environment status GitLab keeps track of your deployments, so you always know what is currently being deployed on your servers. You can find the environment list under @@ -97,6 +132,9 @@ show up in the "Environment" and "Last deployment" lists. ## Manually deploying to environments +CI/CD [Pipelines] usually have one or more [jobs] that deploy to an environment. +You can think of names such as testing, staging or production. + ## Dynamic environments @@ -116,7 +154,7 @@ review: url: https://$CI_BUILD_REF_NAME.example.com ``` -### Closing an environment +## Closing an environment ``` review: @@ -136,7 +174,7 @@ stop_review: action: stop ``` -### View the deployment history +## View the deployment history Clicking on an environment will show the history of deployments. @@ -146,7 +184,7 @@ Clicking on an environment will show the history of deployments. Only deploys that happen after your `.gitlab-ci.yml` is properly configured will show up in the environments and deployments lists. -### Checkout deployments locally +## Checkout deployments locally Since 8.13, a reference in the git repository is saved for each deployment. So knowing what the state is of your current environments is only a `git fetch` @@ -159,6 +197,30 @@ fetch line: fetch = +refs/environments/*:refs/remotes/origin/environments/* ``` +## Further reading + +Below are some links you may find interesting: + +- [The `.gitlab-ci.yml` definition of environments](yaml/README.md#environment) +- [A blog post on Deployments & Environments](https://about.gitlab.com/2016/08/26/ci-deployment-and-environments/) +- [Review Apps](review_apps.md) Expand dynamic environments to deploy your code for every branch + + +## WIP + +Actions + +View environments +View deployments + Rollback deployments + Run deployments +View link to environment URL +View last commit message of deployment +View person who performed the deployment +View commit SHA that triggered the deployment +View branch the deployment was based on +View time ago the deployment was performed + [Pipelines]: pipelines.md [jobs]: yaml/README.md#jobs [yaml]: yaml/README.md diff --git a/doc/ci/img/environments_available_staging.png b/doc/ci/img/environments_available_staging.png new file mode 100644 index 00000000000..784c4fd944c Binary files /dev/null and b/doc/ci/img/environments_available_staging.png differ -- cgit v1.2.1 From 5cac52d8e05492d78869974c4ee9a928da7c6983 Mon Sep 17 00:00:00 2001 From: Achilleas Pipinellis Date: Wed, 16 Nov 2016 17:20:07 +0100 Subject: Change deployments view image [ci skip] --- doc/ci/img/deployments_view.png | Bin 179909 -> 57598 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/doc/ci/img/deployments_view.png b/doc/ci/img/deployments_view.png index 250e051bf42..ca6097cbea4 100644 Binary files a/doc/ci/img/deployments_view.png and b/doc/ci/img/deployments_view.png differ -- cgit v1.2.1 From 600282799606ed260a1149d8312494a2f6a83290 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Coutable?= Date: Thu, 10 Nov 2016 15:24:23 +0100 Subject: Add a gotcha about FactoryGirl sequences MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Related to https://gitlab.com/gitlab-org/gitlab-ce/issues/24341 Signed-off-by: Rémy Coutable --- doc/development/gotchas.md | 89 ++++++++++++++++++++++++++++++++++++++++++++++ doc/development/testing.md | 1 + 2 files changed, 90 insertions(+) diff --git a/doc/development/gotchas.md b/doc/development/gotchas.md index b25ce79e89f..7bfc9cb361f 100644 --- a/doc/development/gotchas.md +++ b/doc/development/gotchas.md @@ -32,6 +32,95 @@ spec/models/user_spec.rb|6 error| Failure/Error: u = described_class.new NoMeth Except for the top-level `describe` block, always provide a String argument to `describe`. +## Don't assert against the absolute value of a sequence-generated attribute + +Consider the following factory: + +```ruby +FactoryGirl.define do + factory :label do + sequence(:title) { |n| "label#{n}" } + end +end +``` + +Consider the following API spec: + +```ruby +require 'rails_helper' + +describe API::Labels do + it 'creates a first label' do + create(:label) + + get api("/projects/#{project.id}/labels", user) + + expect(response).to have_http_status(200) + expect(json_response.first['name']).to eq('label1') + end + + it 'creates a second label' do + create(:label) + + get api("/projects/#{project.id}/labels", user) + + expect(response).to have_http_status(200) + expect(json_response.first['name']).to eq('label1') + end +end +``` + +When run, this spec doesn't do what we might expect: + +```sh +1) API::API reproduce sequence issue creates a second label + Failure/Error: expect(json_response.first['name']).to eq('label1') + + expected: "label1" + got: "label2" + + (compared using ==) +``` + +That's because FactoryGirl sequences are not reseted for each example. + +Please remember that sequence-generated values exist only to avoid having to +explicitly set attributes that have a uniqueness constraint when using a factory. + +### Solution + +If you assert against a sequence-generated attribute's value, you should set it +explicitly. Also, the value you set shouldn't match the sequence pattern. + +For instance, using our `:label` factory, writing `create(:label, title: 'foo')` +is ok, but `create(:label, title: 'label1')` is not. + +Following is the fixed API spec: + +```ruby +require 'rails_helper' + +describe API::Labels do + it 'creates a first label' do + create(:label, title: 'foo') + + get api("/projects/#{project.id}/labels", user) + + expect(response).to have_http_status(200) + expect(json_response.first['name']).to eq('foo') + end + + it 'creates a second label' do + create(:label, title: 'bar') + + get api("/projects/#{project.id}/labels", user) + + expect(response).to have_http_status(200) + expect(json_response.first['name']).to eq('bar') + end +end +``` + ## Don't `rescue Exception` See ["Why is it bad style to `rescue Exception => e` in Ruby?"][Exception]. diff --git a/doc/development/testing.md b/doc/development/testing.md index b0b26ccf57a..4dc535fb359 100644 --- a/doc/development/testing.md +++ b/doc/development/testing.md @@ -64,6 +64,7 @@ the command line via `bundle exec teaspoon`, or via a web browser at methods. - Use `context` to test branching logic. - Don't `describe` symbols (see [Gotchas](gotchas.md#dont-describe-symbols)). +- Don't assert against the absolute value of a sequence-generated attribute (see [Gotchas](gotchas.md#dont-assert-against-the-absolute-value-of-a-sequence-generated-attribute)). - Don't supply the `:each` argument to hooks since it's the default. - Prefer `not_to` to `to_not` (_this is enforced by Rubocop_). - Try to match the ordering of tests to the ordering within the class. -- cgit v1.2.1 From 2749e7a58285e59381a3e788983678d9cf633b45 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Wed, 16 Nov 2016 18:02:51 +0100 Subject: Fix `chat_names` before_action --- app/controllers/profiles/chat_names_controller.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/profiles/chat_names_controller.rb b/app/controllers/profiles/chat_names_controller.rb index 8c5b83adaf4..0d3bdeff416 100644 --- a/app/controllers/profiles/chat_names_controller.rb +++ b/app/controllers/profiles/chat_names_controller.rb @@ -1,5 +1,5 @@ class Profiles::ChatNamesController < Profiles::ApplicationController - before_action :chat_names + before_action :chat_names, only: [:index] before_action :chat_name_token, only: [:new] before_action :chat_name_params, only: [:new, :create, :deny] -- cgit v1.2.1 From 39754c6c05671f3b7071d731171e1e1d78c5c480 Mon Sep 17 00:00:00 2001 From: Didem Acet Date: Tue, 15 Nov 2016 21:00:14 +0200 Subject: Added colored labels to related MR list. --- app/assets/stylesheets/pages/issues.scss | 24 +++++++++++++++++++--- .../projects/issues/_merge_requests.html.haml | 16 ++++++++++----- changelogs/unreleased/related-mr-labels.yml | 4 ++++ 3 files changed, 36 insertions(+), 8 deletions(-) create mode 100644 changelogs/unreleased/related-mr-labels.yml diff --git a/app/assets/stylesheets/pages/issues.scss b/app/assets/stylesheets/pages/issues.scss index 3e7fc3fa52c..e9d43f33c2d 100644 --- a/app/assets/stylesheets/pages/issues.scss +++ b/app/assets/stylesheets/pages/issues.scss @@ -55,9 +55,27 @@ ul.related-merge-requests > li { } .merge-request-status { - color: $gl-gray; - font-size: 15px; - font-weight: bold; + font-size: 13px; + padding: 0px 5px; + color: #FFF; + height: 20px; + border-radius: 3px; + line-height: 18px; + + &.merged { + border: 1px solid #2A96CC; + background: #2D9FD8; + } + + &.closed { + border: 1px solid #C5254D; + background: #D62954; + } + + &.open { + border: 1px solid #2EA65E; + background: #31AF64; + } } .merge-request, diff --git a/app/views/projects/issues/_merge_requests.html.haml b/app/views/projects/issues/_merge_requests.html.haml index 31d3ec23276..747bfa554cb 100644 --- a/app/views/projects/issues/_merge_requests.html.haml +++ b/app/views/projects/issues/_merge_requests.html.haml @@ -19,11 +19,17 @@ in - project = merge_request.target_project = link_to project.name_with_namespace, namespace_project_path(project.namespace, project) - %span.merge-request-status.prepend-left-10 - - if merge_request.merged? - MERGED - - elsif merge_request.closed? - CLOSED + + - if merge_request.merged? + %span.merge-request-status.prepend-left-10.merged + Merged + - elsif merge_request.closed? + %span.merge-request-status.prepend-left-10.closed + Closed + - else + %span.merge-request-status.prepend-left-10.open + Open + - if @closed_by_merge_requests.present? %li = render partial: 'projects/issues/closed_by_box', locals: {merge_request_count: @merge_requests.count} diff --git a/changelogs/unreleased/related-mr-labels.yml b/changelogs/unreleased/related-mr-labels.yml new file mode 100644 index 00000000000..268e0eab870 --- /dev/null +++ b/changelogs/unreleased/related-mr-labels.yml @@ -0,0 +1,4 @@ +--- +title: Added colored labels to related MR list. +merge_request: 7486 +author: Didem Acet -- cgit v1.2.1 From e633688f39466d154796531638fb93adb0360577 Mon Sep 17 00:00:00 2001 From: Didem Acet Date: Wed, 16 Nov 2016 19:20:59 +0200 Subject: Fix SCSS linter errors. --- app/assets/stylesheets/pages/issues.scss | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/app/assets/stylesheets/pages/issues.scss b/app/assets/stylesheets/pages/issues.scss index e9d43f33c2d..eb171195309 100644 --- a/app/assets/stylesheets/pages/issues.scss +++ b/app/assets/stylesheets/pages/issues.scss @@ -56,25 +56,26 @@ ul.related-merge-requests > li { .merge-request-status { font-size: 13px; - padding: 0px 5px; - color: #FFF; + padding: 0 5px; + color: $white-light; height: 20px; border-radius: 3px; line-height: 18px; + border: 1px solid; &.merged { - border: 1px solid #2A96CC; - background: #2D9FD8; + border-color: darken($blue-normal, 10%); + background: $blue-normal; } &.closed { - border: 1px solid #C5254D; - background: #D62954; + border-color: darken($red-normal, 10%); + background: $red-normal; } &.open { - border: 1px solid #2EA65E; - background: #31AF64; + border: 1px solid darken($green-normal, 10%); + background: $green-normal; } } -- cgit v1.2.1 From 606afe2e81188b3556350e29ade7270536a6f58a Mon Sep 17 00:00:00 2001 From: Nick Thomas Date: Wed, 16 Nov 2016 19:19:22 +0000 Subject: Fix some failing specs due to missing access_requestable trait --- spec/models/user_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index 3159243553b..580ce4a9e0a 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -491,7 +491,7 @@ describe User, models: true do end describe '.without_projects' do - let!(:project) { create(:empty_project, :public) } + let!(:project) { create(:empty_project, :public, :access_requestable) } let!(:user) { create(:user) } let!(:user_without_project) { create(:user) } let!(:user_without_project2) { create(:user) } -- cgit v1.2.1 From 6e7019901e4c0c6b137d1cc6d8875259ded525ce Mon Sep 17 00:00:00 2001 From: Nick Thomas Date: Wed, 16 Nov 2016 20:34:09 +0000 Subject: tests: Move rake assets:precompile and cloning gitlab-{shell,test} to the prepare stage --- .gitlab-ci.yml | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 84f1f115b3c..436e9ec6c60 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -71,11 +71,23 @@ update-knapsack: - mysql:latest - redis:alpine +setup-test-env: + <<: *use-db + stage: prepare + script: + - bundle exec rake assets:precompile 2>/dev/null + - bundle exec ruby -Ispec -e 'require "spec_helper" ; TestEnv.init' + artifacts: + expire_in: 7d + paths: + - public/assets + - tmp/tests + + .rspec-knapsack: &rspec-knapsack stage: test <<: *use-db script: - - bundle exec rake assets:precompile 2>/dev/null - JOB_NAME=( $CI_BUILD_NAME ) - export CI_NODE_INDEX=${JOB_NAME[1]} - export CI_NODE_TOTAL=${JOB_NAME[2]} @@ -93,7 +105,6 @@ update-knapsack: stage: test <<: *use-db script: - - bundle exec rake assets:precompile 2>/dev/null - JOB_NAME=( $CI_BUILD_NAME ) - export CI_NODE_INDEX=${JOB_NAME[1]} - export CI_NODE_TOTAL=${JOB_NAME[2]} -- cgit v1.2.1 From 6f714dfb4a9b823ab75508f252d06e19e286d5f2 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Wed, 16 Nov 2016 23:10:27 +0100 Subject: Improve code design after code review --- app/controllers/profiles/chat_names_controller.rb | 4 ++-- app/services/chat_names/find_user_service.rb | 2 +- app/views/profiles/chat_names/index.html.haml | 14 ++++++++------ app/views/profiles/chat_names/new.html.haml | 4 ++-- db/migrate/20161113184239_create_user_chat_names_table.rb | 2 +- db/schema.rb | 2 +- lib/gitlab/chat_name_token.rb | 2 +- spec/lib/gitlab/chat_name_token_spec.rb | 4 +--- spec/services/chat_names/authorize_user_service_spec.rb | 4 +--- spec/services/chat_names/find_user_service_spec.rb | 2 +- spec/support/matchers/be_url.rb | 5 +++++ 11 files changed, 24 insertions(+), 21 deletions(-) create mode 100644 spec/support/matchers/be_url.rb diff --git a/app/controllers/profiles/chat_names_controller.rb b/app/controllers/profiles/chat_names_controller.rb index 0d3bdeff416..7f9c8e64cca 100644 --- a/app/controllers/profiles/chat_names_controller.rb +++ b/app/controllers/profiles/chat_names_controller.rb @@ -13,7 +13,7 @@ class Profiles::ChatNamesController < Profiles::ApplicationController new_chat_name = current_user.chat_names.new(chat_name_params) if new_chat_name.save - flash[:notice] = "Authorized chat nickname #{new_chat_name.chat_name}" + flash[:notice] = "Authorized #{new_chat_name.chat_name}" else flash[:alert] = "Could not authorize chat nickname. Try again!" end @@ -34,7 +34,7 @@ class Profiles::ChatNamesController < Profiles::ApplicationController @chat_name = chat_names.find(params[:id]) if @chat_name.destroy - flash[:notice] = "Delete chat nickname: #{@chat_name.chat_name}!" + flash[:notice] = "Deleted chat nickname: #{@chat_name.chat_name}!" else flash[:alert] = "Could not delete chat nickname #{@chat_name.chat_name}." end diff --git a/app/services/chat_names/find_user_service.rb b/app/services/chat_names/find_user_service.rb index 28e3e155be1..08079fdaf70 100644 --- a/app/services/chat_names/find_user_service.rb +++ b/app/services/chat_names/find_user_service.rb @@ -9,7 +9,7 @@ module ChatNames chat_name = find_chat_name return unless chat_name - chat_name.update(used_at: Time.now) + chat_name.update(last_used_at: Time.now) chat_name.user end diff --git a/app/views/profiles/chat_names/index.html.haml b/app/views/profiles/chat_names/index.html.haml index ae0b6336944..6d8d606583d 100644 --- a/app/views/profiles/chat_names/index.html.haml +++ b/app/views/profiles/chat_names/index.html.haml @@ -6,7 +6,7 @@ %h4.prepend-top-0 = page_title %p - You can see your Chat integrations. + You can see your Chat accounts. .col-lg-9 %h5 Active chat names (#{@chat_names.length}) @@ -39,11 +39,13 @@ = link_to service.title, edit_namespace_project_service_path(project.namespace, project, service) - else = chat_name.service.title - %td= chat_name.team_domain - %td= chat_name.chat_name - %td= - - if chat_name.used_at - time_ago_with_tooltip(chat_name.used_at) + %td + = chat_name.team_domain + %td + = chat_name.chat_name + %td + - if chat_name.last_used_at + time_ago_with_tooltip(chat_name.last_used_at) - else Never diff --git a/app/views/profiles/chat_names/new.html.haml b/app/views/profiles/chat_names/new.html.haml index 0b9ee8c71ad..f635acf96e2 100644 --- a/app/views/profiles/chat_names/new.html.haml +++ b/app/views/profiles/chat_names/new.html.haml @@ -1,11 +1,11 @@ %h3.page-title Authorization required %main{:role => "main"} %p.h4 - Authorize the chat user + Authorize %strong.text-info= @chat_name_params[:chat_name] to use your account? - %hr/ + %hr .actions = form_tag profile_chat_names_path, method: :post do = hidden_field_tag :token, @chat_name_token.token diff --git a/db/migrate/20161113184239_create_user_chat_names_table.rb b/db/migrate/20161113184239_create_user_chat_names_table.rb index 1e138fba202..97b597654f7 100644 --- a/db/migrate/20161113184239_create_user_chat_names_table.rb +++ b/db/migrate/20161113184239_create_user_chat_names_table.rb @@ -11,7 +11,7 @@ class CreateUserChatNamesTable < ActiveRecord::Migration t.string :team_domain t.string :chat_id, null: false t.string :chat_name - t.datetime :used_at + t.datetime :last_used_at t.timestamps null: false end diff --git a/db/schema.rb b/db/schema.rb index 3688df3a238..5732b1b73c1 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -159,7 +159,7 @@ ActiveRecord::Schema.define(version: 20161113184239) do t.string "team_domain" t.string "chat_id", null: false t.string "chat_name" - t.datetime "used_at" + t.datetime "last_used_at" t.datetime "created_at", null: false t.datetime "updated_at", null: false end diff --git a/lib/gitlab/chat_name_token.rb b/lib/gitlab/chat_name_token.rb index 543f038a4d4..1b081aa9b1d 100644 --- a/lib/gitlab/chat_name_token.rb +++ b/lib/gitlab/chat_name_token.rb @@ -5,7 +5,7 @@ module Gitlab attr_reader :token TOKEN_LENGTH = 50 - EXPIRY_TIME = 10.minutes # 10 minutes + EXPIRY_TIME = 10.minutes def initialize(token = new_token) @token = token diff --git a/spec/lib/gitlab/chat_name_token_spec.rb b/spec/lib/gitlab/chat_name_token_spec.rb index 8d7e7a99059..8c1e6efa9db 100644 --- a/spec/lib/gitlab/chat_name_token_spec.rb +++ b/spec/lib/gitlab/chat_name_token_spec.rb @@ -12,9 +12,7 @@ describe Gitlab::ChatNameToken, lib: true do end context 'when storing data' do - let(:data) { - { key: 'value' } - } + let(:data) { { key: 'value' } } subject { described_class.new(@token) } diff --git a/spec/services/chat_names/authorize_user_service_spec.rb b/spec/services/chat_names/authorize_user_service_spec.rb index f8c26e51bfc..2ecee58e92d 100644 --- a/spec/services/chat_names/authorize_user_service_spec.rb +++ b/spec/services/chat_names/authorize_user_service_spec.rb @@ -10,9 +10,7 @@ describe ChatNames::AuthorizeUserService, services: true do let(:params) { { team_id: 'T0001', team_domain: 'myteam', user_id: 'U0001', user_name: 'user' } } it 'requests a new token' do - is_expected.to include('http') - is_expected.to include('://') - is_expected.to include('token=') + is_expected.to be_url end end diff --git a/spec/services/chat_names/find_user_service_spec.rb b/spec/services/chat_names/find_user_service_spec.rb index cf5844069f9..5b885b2c657 100644 --- a/spec/services/chat_names/find_user_service_spec.rb +++ b/spec/services/chat_names/find_user_service_spec.rb @@ -20,7 +20,7 @@ describe ChatNames::FindUserService, services: true do it 'updates when last time chat name was used' do subject - expect(chat_name.reload.used_at).to be_like_time(Time.now) + expect(chat_name.reload.last_used_at).to be_like_time(Time.now) end end diff --git a/spec/support/matchers/be_url.rb b/spec/support/matchers/be_url.rb new file mode 100644 index 00000000000..f8096af1b22 --- /dev/null +++ b/spec/support/matchers/be_url.rb @@ -0,0 +1,5 @@ +RSpec::Matchers.define :be_url do |_| + match do |actual| + URI.parse(actual) rescue false + end +end -- cgit v1.2.1 From 4d7b44b5b7566988d405c4dd4e68f605586d0874 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Wed, 16 Nov 2016 23:10:53 +0100 Subject: Add specs for testing authorization and deny of chat user --- changelogs/unreleased/add-chat-names.yml | 4 ++ spec/features/profiles/chat_names_spec.rb | 77 +++++++++++++++++++++++++++++++ 2 files changed, 81 insertions(+) create mode 100644 changelogs/unreleased/add-chat-names.yml create mode 100644 spec/features/profiles/chat_names_spec.rb diff --git a/changelogs/unreleased/add-chat-names.yml b/changelogs/unreleased/add-chat-names.yml new file mode 100644 index 00000000000..6a1e05783a3 --- /dev/null +++ b/changelogs/unreleased/add-chat-names.yml @@ -0,0 +1,4 @@ +--- +title: Allow to connect Chat account with GitLab +merge_request: 7450 +author: diff --git a/spec/features/profiles/chat_names_spec.rb b/spec/features/profiles/chat_names_spec.rb new file mode 100644 index 00000000000..6f6f7029c0b --- /dev/null +++ b/spec/features/profiles/chat_names_spec.rb @@ -0,0 +1,77 @@ +require 'rails_helper' + +feature 'Profile > Chat', feature: true do + given(:user) { create(:user) } + given(:service) { create(:service) } + + before do + login_as(user) + end + + describe 'uses authorization link' do + given(:params) do + { team_id: 'T00', team_domain: 'my_chat_team', user_id: 'U01', user_name: 'my_chat_user' } + end + given!(:authorize_url) { ChatNames::AuthorizeUserService.new(service, params).execute } + given(:authorize_path) { URI.parse(authorize_url).request_uri } + + before do + visit authorize_path + end + + context 'clicks authorize' do + before do + click_button 'Authorize' + end + + scenario 'goes to list of chat names and see chat account' do + expect(page.current_path).to eq(profile_chat_names_path) + expect(page).to have_content('my_chat_team') + expect(page).to have_content('my_chat_user') + end + + scenario 'second use of link is denied' do + visit authorize_path + + expect(page).to have_http_status(:not_found) + end + end + + context 'clicks deny' do + before do + click_button 'Deny' + end + + scenario 'goes to list of chat names and do not see chat account' do + expect(page.current_path).to eq(profile_chat_names_path) + expect(page).not_to have_content('my_chat_team') + expect(page).not_to have_content('my_chat_user') + end + + scenario 'second use of link is denied' do + visit authorize_path + + expect(page).to have_http_status(:not_found) + end + end + end + + describe 'visits chat accounts' do + given!(:chat_name) { create(:chat_name, user: user, service: service) } + + before do + visit profile_chat_names_path + end + + scenario 'sees chat user' do + expect(page).to have_content(chat_name.team_domain) + expect(page).to have_content(chat_name.chat_name) + end + + scenario 'removes chat account' do + click_link 'Remove' + + expect(page).to have_content("You don't have any active chat names.") + end + end +end -- cgit v1.2.1 From 3a6e38db5316a73809c3303de2aa8a0ece866fcc Mon Sep 17 00:00:00 2001 From: Achilleas Pipinellis Date: Thu, 17 Nov 2016 00:04:28 +0100 Subject: Finish most of environments [ci skip] --- doc/ci/environments.md | 184 +++++++++++++++------ doc/ci/img/environments_manual_action_builds.png | Bin 0 -> 27170 bytes .../img/environments_manual_action_deployments.png | Bin 0 -> 34504 bytes .../environments_manual_action_environments.png | Bin 0 -> 40297 bytes .../img/environments_manual_action_pipelines.png | Bin 0 -> 42212 bytes .../environments_manual_action_single_pipeline.png | Bin 0 -> 42233 bytes 6 files changed, 132 insertions(+), 52 deletions(-) create mode 100644 doc/ci/img/environments_manual_action_builds.png create mode 100644 doc/ci/img/environments_manual_action_deployments.png create mode 100644 doc/ci/img/environments_manual_action_environments.png create mode 100644 doc/ci/img/environments_manual_action_pipelines.png create mode 100644 doc/ci/img/environments_manual_action_single_pipeline.png diff --git a/doc/ci/environments.md b/doc/ci/environments.md index b3d4ec40830..a96e975bb92 100644 --- a/doc/ci/environments.md +++ b/doc/ci/environments.md @@ -59,10 +59,10 @@ build: deploy_staging: stage: deploy script: - - echo "Deploy to staging server" + - echo "Deploy to staging server" environment: - name: staging - url: https://staging.example.com + name: staging + url: https://staging.example.com only: - master ``` @@ -80,23 +80,45 @@ lastly the `deploy_staging`. With this, we ensure that first the tests pass, then our app is able to be built successfully, and lastly we deploy to the staging server. -With the above `.gitlab-ci.yml` we have achieved that: +The `environment` keyword is just a hint for GitLab that this job actually +deploys to this environment's `name`. It can also have a `url` which, as we +will later see, is exposed in various places within GitLab. Each time a job that +has an environment specified and succeeds, a deployment is recorded, remembering +the Git SHA and environment name. + +To sum up, with the above `.gitlab-ci.yml` we have achieved that: - All branches will run the `test` and `build` jobs. -- The `deploy_staging` job will [only](yaml/README.md#only) run on the `master` - branch which means all merge requests +- The `deploy_staging` job will run [only](yaml/README.md#only) on the `master` + branch which means all merge requests that are created from branches don't + get to deploy to the staging server - When a merge request is merged, all jobs will run and the `deploy_staging` in particular will deploy our code to a staging server while the deployment will be recorded in an environment named `staging`. -The `environment` keyword is just a hint for GitLab that this job actually -deploys to this environment. Each time the job succeeds, a deployment is -recorded, remembering the Git SHA and environment name. Here's how the -Environments page looks so far. +Let's now see how that information is exposed within GitLab. + +## Viewing the current status of an environment + +The environment list under your project's **Pipelines ➔ Environments**, is +where you can find information of the last deployment status of an environment. + +Here's how the Environments page looks so far. ![Staging environment view](img/environments_available_staging.png) -TODO: describe what the above page means +There's a bunch of information there, specifically you can see: + +- The environment's name with a link to its deployments +- The last deployment ID number and who performed it +- The build ID of the last deployment with its respective job name +- The commit information of the last deployment such as who committed, to what + branch and the Git SHA of the commit +- The exact time the last deployment was performed +- A button that takes you to the URL that you have defined under the + `environment` keyword in `.gitlab-ci.yml` +- A button that re-deploys the latest deployment, meaning it runs the job + defined by the environment name for that specific commit >**Notes:** - While you can create environments manually in the web interface, we recommend @@ -104,37 +126,105 @@ TODO: describe what the above page means be automatically created for you after the first deploy. - The environments page can only be viewed by Reporters and above. For more information on the permissions, see the [permissions documentation][permissions]. +- Only deploys that happen after your `.gitlab-ci.yml` is properly configured + will show up in the "Environment" and "Last deployment" lists. -As we've pointed in the Overview section, environments are like tags for your -CI jobs, describing where code gets deployed. Here's what happened behind the -scenes: +The information shown in the Environments page is limited to the latest +deployments, but as you may have guessed an environment can have multiple +deployments. -1. The jobs and environments were defined in `.gitlab-ci.yml` -1. Changes were pushed to the repository in GitLab -1. The Runner(s) picked up the jobs -1. The jobs finished successfully -1. The environments got created if they didn't already exist -1. A deployment was recorded remembering the environment name and the Git SHA of - the last commit of the pipeline - -## View the environment status +## Viewing the deployment history of an environment GitLab keeps track of your deployments, so you always know what is currently -being deployed on your servers. You can find the environment list under -**Pipelines > Environments** for your project. You'll see the git SHA and date -of the last deployment to each environment defined. +being deployed on your servers. That way you can have the full history of your +deployments per every environment right in your browser. Clicking on an +environment will show the history of its deployments. Assuming you have deployed +multiple times already, here's how a specific environment's page looks like. + +![Deployments](img/deployments_view.png) -![Environments](img/environments_view.png) +We can see the same information as when in the Environments page, but this time +all deployments are shown. As you may have noticed, apart from the **Re-deploy** +button there are now **Rollback** buttons for each deployment. Let's see how +that works. + +## Rolling back changes + +You can't control everything, so sometimes things go wrong. When that unfortunate +time comes GitLab has you covered. Simply by clicking the **Rollback** button +that can be found in the deployments page +(**Pipelines ➔ Environments ➔ `environment name`**) you can relaunch the +job with the commit associated with it. >**Note:** -Only deploys that happen after your `.gitlab-ci.yml` is properly configured will -show up in the "Environment" and "Last deployment" lists. +Bare in mind that your mileage will vary and it's entirely up to how you define +the deployment process in the job's `script` whether the rollback succeeds or not. +GitLab CI is just following orders. + +Thankfully that was the staging server that we had to rollback, and since we +learn from our mistakes, we decided to not make the same again when we deploy +to the production server. Enter manual actions for deployments. ## Manually deploying to environments -CI/CD [Pipelines] usually have one or more [jobs] that deploy to an environment. -You can think of names such as testing, staging or production. +Turning a job from running automatically to a manual action is as simple as +adding `when: manual` to it. To expand on our previous example, let's add +another job that this time deploys our app to a production server and is +tracked by a `production` environment. The `.gitlab-ci.yml` looks like this +so far: +```yaml +stages: + - test + - build + - deploy + +test: + stage: test + script: echo "Running tests" + +build: + stage: build + script: echo "Building the app" + +deploy_staging: + stage: deploy + script: + - echo "Deploy to staging server" + environment: + name: staging + url: https://staging.example.com + only: + - master + +deploy_prod: + stage: deploy + script: + - echo "Deploy to production server" + environment: + name: production + url: https://example.com + when: manual + only: + - master +``` + +The `when: manual` action exposes a play button in GitLab's UI and the +`deploy_prod` job will only be triggered if and when we click that play button. +You can find it in the pipeline, build, environment, and deployment views. + +| Pipelines | Single pipeline | Environments | Deployments | Builds | +| --------- | ----------------| ------------ | ----------- | -------| +| ![Pipelines manual action](img/environments_manual_action_pipelines.png) | ![Pipelines manual action](img/environments_manual_action_single_pipeline.png) | ![Environments manual action](img/environments_manual_action_environments.png) | ![Deployments manual action](img/environments_manual_action_deployments.png) | ![Builds manual action](img/environments_manual_action_builds.png) | + +Clicking on the play button in either of these places will trigger the +`deploy_prod` job, and the deployment will be recorded under a new +environment named `production`. + +While this is fine for deploying to some stable environments like staging or +production, what happens for branches? So far we haven't defined anything +regarding deployments for branches other than `master`. Dynamic environments +will help us achieve that. ## Dynamic environments @@ -174,16 +264,6 @@ stop_review: action: stop ``` -## View the deployment history - -Clicking on an environment will show the history of deployments. - -![Deployments](img/deployments_view.png) - ->**Note:** -Only deploys that happen after your `.gitlab-ci.yml` is properly configured will -show up in the environments and deployments lists. - ## Checkout deployments locally Since 8.13, a reference in the git repository is saved for each deployment. So @@ -206,20 +286,20 @@ Below are some links you may find interesting: - [Review Apps](review_apps.md) Expand dynamic environments to deploy your code for every branch -## WIP +## TODO Actions -View environments -View deployments - Rollback deployments - Run deployments -View link to environment URL -View last commit message of deployment -View person who performed the deployment -View commit SHA that triggered the deployment -View branch the deployment was based on -View time ago the deployment was performed +- View environments + +- View deployments + + - Rollback deployments + + - Run deployments + +- View link to environment URL +- View last commit message of deployment + +- View person who performed the deployment + +- View commit SHA that triggered the deployment + +- View branch the deployment was based on + +- View time ago the deployment was performed + [Pipelines]: pipelines.md [jobs]: yaml/README.md#jobs diff --git a/doc/ci/img/environments_manual_action_builds.png b/doc/ci/img/environments_manual_action_builds.png new file mode 100644 index 00000000000..d4bb7ccdbae Binary files /dev/null and b/doc/ci/img/environments_manual_action_builds.png differ diff --git a/doc/ci/img/environments_manual_action_deployments.png b/doc/ci/img/environments_manual_action_deployments.png new file mode 100644 index 00000000000..c2477381c80 Binary files /dev/null and b/doc/ci/img/environments_manual_action_deployments.png differ diff --git a/doc/ci/img/environments_manual_action_environments.png b/doc/ci/img/environments_manual_action_environments.png new file mode 100644 index 00000000000..56601c0db2d Binary files /dev/null and b/doc/ci/img/environments_manual_action_environments.png differ diff --git a/doc/ci/img/environments_manual_action_pipelines.png b/doc/ci/img/environments_manual_action_pipelines.png new file mode 100644 index 00000000000..eb6e87cd956 Binary files /dev/null and b/doc/ci/img/environments_manual_action_pipelines.png differ diff --git a/doc/ci/img/environments_manual_action_single_pipeline.png b/doc/ci/img/environments_manual_action_single_pipeline.png new file mode 100644 index 00000000000..9713ad212e2 Binary files /dev/null and b/doc/ci/img/environments_manual_action_single_pipeline.png differ -- cgit v1.2.1 From 604aa680a2437ba410520b826c33ea57310300fe Mon Sep 17 00:00:00 2001 From: Taurie Davis Date: Wed, 16 Nov 2016 23:26:01 +0000 Subject: Update CONTRIBUTING.md - Adding info about issues within a milestone --- CONTRIBUTING.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 6a009138446..659871a06a4 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -216,7 +216,10 @@ associated with in the description of the issue. We welcome merge requests with fixes and improvements to GitLab code, tests, and/or documentation. The features we would really like a merge request for are listed with the label [`Accepting Merge Requests` on our issue tracker for CE][accepting-mrs-ce] -and [EE][accepting-mrs-ee] but other improvements are also welcome. +and [EE][accepting-mrs-ee] but other improvements are also welcome. Please note +that if an issue is marked for the current milestone either before or while you +are working on it, a team member may take over the merge request in order to +ensure the work is finished before the release date. If you want to add a new feature that is not labeled it is best to first create a feedback issue (if there isn't one already) and leave a comment asking for it -- cgit v1.2.1 From 0fe3aa2d0bcff645d6cb4e53fb6643dced0a84a5 Mon Sep 17 00:00:00 2001 From: Semyon Pupkov Date: Wed, 16 Nov 2016 00:49:29 +0500 Subject: Remove empty db fixtures file --- db/fixtures/test/001_repo.rb | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 db/fixtures/test/001_repo.rb diff --git a/db/fixtures/test/001_repo.rb b/db/fixtures/test/001_repo.rb deleted file mode 100644 index e69de29bb2d..00000000000 -- cgit v1.2.1 From ff97de61df3ebbeea31e5fef833e8a477c13ab3c Mon Sep 17 00:00:00 2001 From: Achilleas Pipinellis Date: Thu, 17 Nov 2016 08:54:51 +0100 Subject: Move name rules under environment:name in yaml readme --- doc/ci/yaml/README.md | 33 +++++++++++++++++---------------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/doc/ci/yaml/README.md b/doc/ci/yaml/README.md index 5c0e1c44e3f..bc9c0897f5a 100644 --- a/doc/ci/yaml/README.md +++ b/doc/ci/yaml/README.md @@ -552,28 +552,14 @@ An example usage of manual actions is deployment to production. If `environment` is specified and no environment under that name exists, a new one will be created automatically. -The `environment` name can contain: - -- letters -- digits -- spaces -- `-` -- `_` -- `/` -- `$` -- `{` -- `}` - -Common names are `qa`, `staging`, and `production`, but you can use whatever -name works with your workflow. - In its simplest form, the `environment` keyword can be defined like: ``` deploy to production: stage: deploy script: git push production HEAD:master - environment: production + environment: + name: production ``` In the above example, the `deploy to production` job will be marked as doing a @@ -588,6 +574,21 @@ Before GitLab 8.11, the name of an environment could be defined as a string like `environment: production`. The recommended way now is to define it under the `name` keyword. +The `environment` name can contain: + +- letters +- digits +- spaces +- `-` +- `_` +- `/` +- `$` +- `{` +- `}` + +Common names are `qa`, `staging`, and `production`, but you can use whatever +name works with your workflow. + Instead of defining the name of the environment right after the `environment` keyword, it is also possible to define it as a separate value. For that, use the `name` keyword under `environment`: -- cgit v1.2.1 From cdb726ce202165d1ab747830d805a6144d93c5cc Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Thu, 17 Nov 2016 10:10:11 +0100 Subject: Fix code review style issues --- app/views/profiles/chat_names/index.html.haml | 2 +- spec/services/chat_names/authorize_user_service_spec.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/views/profiles/chat_names/index.html.haml b/app/views/profiles/chat_names/index.html.haml index 6d8d606583d..a8dd258732f 100644 --- a/app/views/profiles/chat_names/index.html.haml +++ b/app/views/profiles/chat_names/index.html.haml @@ -38,7 +38,7 @@ - if can?(current_user, :admin_project, project) = link_to service.title, edit_namespace_project_service_path(project.namespace, project, service) - else - = chat_name.service.title + = service.title %td = chat_name.team_domain %td diff --git a/spec/services/chat_names/authorize_user_service_spec.rb b/spec/services/chat_names/authorize_user_service_spec.rb index 2ecee58e92d..d50bfb0492c 100644 --- a/spec/services/chat_names/authorize_user_service_spec.rb +++ b/spec/services/chat_names/authorize_user_service_spec.rb @@ -15,7 +15,7 @@ describe ChatNames::AuthorizeUserService, services: true do end context 'when there are missing parameters' do - let(:params) { { } } + let(:params) { {} } it 'does not request a new token' do is_expected.to be_nil -- cgit v1.2.1 From 95d552b8dddf70bccd62e3e378264125504e80ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Coutable?= Date: Wed, 2 Nov 2016 17:16:06 +0100 Subject: Start to document how to code for CE with EE in mind MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rémy Coutable --- doc/development/README.md | 1 + doc/development/limit_ee_conflicts.md | 247 ++++++++++++++++++++++++++++++++++ 2 files changed, 248 insertions(+) create mode 100644 doc/development/limit_ee_conflicts.md diff --git a/doc/development/README.md b/doc/development/README.md index f88456a7a7a..371bb55c127 100644 --- a/doc/development/README.md +++ b/doc/development/README.md @@ -22,6 +22,7 @@ ## Process - [Generate a changelog entry with `bin/changelog`](changelog.md) +- [Limit conflicts with EE when developing on CE](limit_ee_conflicts.md) - [Code review guidelines](code_review.md) for reviewing code and having code reviewed. - [Merge request performance guidelines](merge_request_performance_guidelines.md) for ensuring merge requests do not negatively impact GitLab performance diff --git a/doc/development/limit_ee_conflicts.md b/doc/development/limit_ee_conflicts.md new file mode 100644 index 00000000000..e8af1c6af7b --- /dev/null +++ b/doc/development/limit_ee_conflicts.md @@ -0,0 +1,247 @@ +# Limit conflicts with EE when developing on CE + +This guide contains best-practices for avoiding conflicts between CE and EE. + +## Context + +Usually, GitLab Community Edition is merged into the Enterprise Edition once a +week. During these merges, it's very common to get conflicts when some changes +in CE do not apply cleanly to EE. + +In this document, we will list the best practices to avoid such conflicts or to +make them easily solvable by the person who does the CE->EE merge. + +## Different type of conflicts + +### Models + +#### Common issues + +TODO + +#### Mitigations + +TODO + +### Services + +#### Common issues + +TODO + +#### Mitigations + +TODO + +### Controllers + +#### Common issues + +In controllers, the most common type of conflicts is either in a `before_action` +that has a list of actions in CE but EE adds some actions to that list. + +Same problems often occurs for `params.require` / `params.permit` calls. + +Other conflicts usually involve specific code for EE-specific features such as: + +- LDAP: + ```diff + def destroy + @key = current_user.keys.find(params[:id]) + - @key.destroy + + @key.destroy unless @key.is_a? LDAPKey + + respond_to do |format| + ``` +- Geo: + ```diff + def after_sign_out_path_for(resource) + - current_application_settings.after_sign_out_path.presence || new_user_session_path + + if Gitlab::Geo.secondary? + + Gitlab::Geo.primary_node.oauth_logout_url(@geo_logout_state) + + else + + current_application_settings.after_sign_out_path.presence || new_user_session_path + + end + end + ``` +- Audit log: + ```diff + def approve_access_request + - Members::ApproveAccessRequestService.new(membershipable, current_user, params).execute + + member = Members::ApproveAccessRequestService.new(membershipable, current_user, params).execute + + + + log_audit_event(member, action: :create) + + redirect_to polymorphic_url([membershipable, :members]) + end + ``` + +#### Mitigations + +Separate CE and EE actions/keywords. For instance for `params.require` in +`ProjectsController`: + +```ruby +def project_params + params.require(:project).permit(project_params_ce) + # On EE, this is always: + # params.require(:project).permit(project_params_ce + project_params_ee) +end + +# Always returns an array of symbols, created however best fits the use case. +# It _should_ be sorted alphabetically. +def project_params_ce + %i[ + description + name + path + ] +end + +# (On EE) +def project_params_ee + %i[ + approvals_before_merge + approver_group_ids + approver_ids + ... + ] +end +``` + +### Views + +#### Common issues + +A few issues often happen here: + +1. Indentation issue +1. A block of code added in CE conflicts because there is already another block + at the same place in EE + +#### Mitigations + +Blocks of code that are EE-specific should be moved to partials as much as +possible to avoid conflicts with big chunks of HAML code that that are not funny +to resolve when you add the indentation in the equation. + +For instance this kind of things: + +```haml +- if can?(current_user, :"admin_#{issuable.to_ability_name}", issuable.project) + - has_due_date = issuable.has_attribute?(:due_date) + %hr + .row + %div{ class: (has_due_date ? "col-lg-6" : "col-sm-12") } + .form-group.issue-assignee + = f.label :assignee_id, "Assignee", class: "control-label #{"col-lg-4" if has_due_date}" + .col-sm-10{ class: ("col-lg-8" if has_due_date) } + .issuable-form-select-holder + - if issuable.assignee_id + = f.hidden_field :assignee_id + = dropdown_tag(user_dropdown_label(issuable.assignee_id, "Assignee"), options: { toggle_class: "js-dropdown-keep-input js-user-search js-issuable-form-dropdown js-assignee-search", title: "Select assignee", filter: true, dropdown_class: "dropdown-menu-user dropdown-menu-selectable dropdown-menu-assignee js-filter-submit", + placeholder: "Search assignee", data: { first_user: current_user.try(:username), null_user: true, current_user: true, project_id: project.try(:id), selected: issuable.assignee_id, field_name: "#{issuable.class.model_name.param_key}[assignee_id]", default_label: "Assignee"} }) + .form-group.issue-milestone + = f.label :milestone_id, "Milestone", class: "control-label #{"col-lg-4" if has_due_date}" + .col-sm-10{ class: ("col-lg-8" if has_due_date) } + .issuable-form-select-holder + = render "shared/issuable/milestone_dropdown", selected: issuable.milestone, name: "#{issuable.class.model_name.param_key}[milestone_id]", show_any: false, show_upcoming: false, extra_class: "js-issuable-form-dropdown js-dropdown-keep-input", dropdown_title: "Select milestone" + .form-group + - has_labels = @labels && @labels.any? + = f.label :label_ids, "Labels", class: "control-label #{"col-lg-4" if has_due_date}" + = f.hidden_field :label_ids, multiple: true, value: '' + .col-sm-10{ class: "#{"col-lg-8" if has_due_date} #{'issuable-form-padding-top' if !has_labels}" } + .issuable-form-select-holder + = render "shared/issuable/label_dropdown", classes: ["js-issuable-form-dropdown"], selected: issuable.labels, data_options: { field_name: "#{issuable.class.model_name.param_key}[label_ids][]", show_any: false, show_menu_above: 'true' }, dropdown_title: "Select label" + + - if issuable.respond_to?(:weight) + .form-group + = f.label :label_ids, class: "control-label #{"col-lg-4" if has_due_date}" do + Weight + .col-sm-10{ class: ("col-lg-8" if has_due_date) } + = f.select :weight, issues_weight_options(issuable.weight, edit: true), { include_blank: true }, + { class: 'select2 js-select2', data: { placeholder: "Select weight" }} + + - if has_due_date + .col-lg-6 + .form-group + = f.label :due_date, "Due date", class: "control-label" + .col-sm-10 + .issuable-form-select-holder + = f.text_field :due_date, id: "issuable-due-date", class: "datepicker form-control", placeholder: "Select due date" +``` + +could be simplified by using partials: + +```haml += render 'metadata_form', issuable: issuable +``` + +and then the `_metadata_form.html.haml` could be as follows: + +```haml +- return unless can?(current_user, :"admin_#{issuable.to_ability_name}", issuable.project) + +- has_due_date = issuable.has_attribute?(:due_date) +%hr +.row + %div{ class: (has_due_date ? "col-lg-6" : "col-sm-12") } + .form-group.issue-assignee + = f.label :assignee_id, "Assignee", class: "control-label #{"col-lg-4" if has_due_date}" + .col-sm-10{ class: ("col-lg-8" if has_due_date) } + .issuable-form-select-holder + - if issuable.assignee_id + = f.hidden_field :assignee_id + = dropdown_tag(user_dropdown_label(issuable.assignee_id, "Assignee"), options: { toggle_class: "js-dropdown-keep-input js-user-search js-issuable-form-dropdown js-assignee-search", title: "Select assignee", filter: true, dropdown_class: "dropdown-menu-user dropdown-menu-selectable dropdown-menu-assignee js-filter-submit", + placeholder: "Search assignee", data: { first_user: current_user.try(:username), null_user: true, current_user: true, project_id: project.try(:id), selected: issuable.assignee_id, field_name: "#{issuable.class.model_name.param_key}[assignee_id]", default_label: "Assignee"} }) + .form-group.issue-milestone + = f.label :milestone_id, "Milestone", class: "control-label #{"col-lg-4" if has_due_date}" + .col-sm-10{ class: ("col-lg-8" if has_due_date) } + .issuable-form-select-holder + = render "shared/issuable/milestone_dropdown", selected: issuable.milestone, name: "#{issuable.class.model_name.param_key}[milestone_id]", show_any: false, show_upcoming: false, extra_class: "js-issuable-form-dropdown js-dropdown-keep-input", dropdown_title: "Select milestone" + .form-group + - has_labels = @labels && @labels.any? + = f.label :label_ids, "Labels", class: "control-label #{"col-lg-4" if has_due_date}" + = f.hidden_field :label_ids, multiple: true, value: '' + .col-sm-10{ class: "#{"col-lg-8" if has_due_date} #{'issuable-form-padding-top' if !has_labels}" } + .issuable-form-select-holder + = render "shared/issuable/label_dropdown", classes: ["js-issuable-form-dropdown"], selected: issuable.labels, data_options: { field_name: "#{issuable.class.model_name.param_key}[label_ids][]", show_any: false, show_menu_above: 'true' }, dropdown_title: "Select label" + + = render 'weight_form', issuable: issuable, has_due_date: has_due_date + + - if has_due_date + .col-lg-6 + .form-group + = f.label :due_date, "Due date", class: "control-label" + .col-sm-10 + .issuable-form-select-holder + = f.text_field :due_date, id: "issuable-due-date", class: "datepicker form-control", placeholder: "Select due date" +``` + +and then the `_weight_form.html.haml` could be as follows: + +```haml +- return unless issuable.respond_to?(:weight) + +- has_due_date = issuable.has_attribute?(:due_date) + +.form-group + = f.label :label_ids, class: "control-label #{"col-lg-4" if has_due_date}" do + Weight + .col-sm-10{ class: ("col-lg-8" if has_due_date) } + = f.select :weight, issues_weight_options(issuable.weight, edit: true), { include_blank: true }, + { class: 'select2 js-select2', data: { placeholder: "Select weight" }} +``` + +Note: + +- The safeguards at the top allows to get rid of an unneccessary indentation +level +- Here we only moved the 'Weight' code to a partial since this is the only + EE-specific code in that view, so it's the most likely to conflict, but you + are encouraged to use partials even for code that's in CE to logically split + big views into several smaller files. + +--- + +[Return to Development documentation](README.md) -- cgit v1.2.1 From 54fc574ebad57fddeaede8edeae04d53031d7712 Mon Sep 17 00:00:00 2001 From: Achilleas Pipinellis Date: Thu, 17 Nov 2016 10:36:58 +0100 Subject: Finish dynamic environments and URLs sections [ci skip] --- doc/ci/environments.md | 160 ++++++++++++++++++++++- doc/ci/img/environments_link_url.png | Bin 0 -> 33561 bytes doc/ci/img/environments_link_url_deployments.png | Bin 0 -> 19652 bytes doc/ci/img/environments_link_url_mr.png | Bin 0 -> 47347 bytes doc/ci/img/environments_mr_review_app.png | Bin 0 -> 39780 bytes 5 files changed, 156 insertions(+), 4 deletions(-) create mode 100644 doc/ci/img/environments_link_url.png create mode 100644 doc/ci/img/environments_link_url_deployments.png create mode 100644 doc/ci/img/environments_link_url_mr.png create mode 100644 doc/ci/img/environments_mr_review_app.png diff --git a/doc/ci/environments.md b/doc/ci/environments.md index a96e975bb92..428e8a0cf25 100644 --- a/doc/ci/environments.md +++ b/doc/ci/environments.md @@ -221,6 +221,11 @@ Clicking on the play button in either of these places will trigger the `deploy_prod` job, and the deployment will be recorded under a new environment named `production`. +>**Note:** +Remember that if your environment's name is `production` (all lowercase), then +it will get recorded in [Cycle Analytics](../user/project/cycle_analytics.md). +Double the benefit! + While this is fine for deploying to some stable environments like staging or production, what happens for branches? So far we haven't defined anything regarding deployments for branches other than `master`. Dynamic environments @@ -229,21 +234,162 @@ will help us achieve that. ## Dynamic environments As the name suggests, it is possible to create environments on the fly by just -declaring their names dynamically in `.gitlab-ci.yml`. +declaring their names dynamically in `.gitlab-ci.yml`. Dynamic environments is +the base of [Review apps](review_apps.md). GitLab Runner exposes various [environment variables][variables] when a job runs, -and as such you can use them +and as such, you can use them as environment names. Let's add another job in +our example which will deploy to all branches except `master`: +```yaml +deploy_review: + stage: deploy + script: + - echo "Deploy a review app" + environment: + name: review/$CI_BUILD_REF_NAME + url: https://$CI_BUILD_REF_NAME.example.com + only: + - branches + except: + - master ``` -review: + +Let's break it down in pieces. The job's name is `deploy_review` and it runs +on the `deploy` stage. The `script` at this point is fictional, you'd have to +use your own based on your deployment. Then, we set the `environment` with the +`environment:name` being `review/$CI_BUILD_REF_NAME`. Now that's an interesting +one. Since the [environment name][env-name] can contain also slashes (`/`), we +can use this pattern to distinguish between dynamic environments and the regular +ones. + +So, the first part is `review`, followed by a `/` and then `$CI_BUILD_REF_NAME` +which takes the value of the branch name. We also use the same +`$CI_BUILD_REF_NAME` value in the `environment:url` so that the environment +can get a specific and distinct URL for each branch. Again, the way you set up +the webserver to serve these requests is based on your setup. + +Last but not least, we tell the job to run [`only`][only] on branches +[`except`][only] master. + +>**Note:** +You are not bound to use only slashes in the dynamic environments' names (`/`), +but as we will see later, this will enable the "grouping similar environments" +feature. + +The whole `.gitlab-ci.yml` looks like this so far: + +```yaml +stages: + - test + - build + - deploy + +test: + stage: test + script: echo "Running tests" + +build: + stage: build + script: echo "Building the app" + +deploy_review: stage: deploy script: - - rsync -av --delete public /srv/nginx/pages/$CI_BUILD_REF_NAME + - echo "Deploy a review app" environment: name: review/$CI_BUILD_REF_NAME url: https://$CI_BUILD_REF_NAME.example.com + only: + - branches + except: + - master + +deploy_staging: + stage: deploy + script: + - echo "Deploy to staging server" + environment: + name: staging + url: https://staging.example.com + only: + - master + +deploy_prod: + stage: deploy + script: + - echo "Deploy to production server" + environment: + name: production + url: https://example.com + when: manual + only: + - master ``` +A more realistic example would include copying files to a location where a +webserver (NGINX) could then read and serve. The example below will copy the +`public` directory to `/srv/nginx/$CI_BUILD_REF_NAME/public`: + +```yaml +review_app: + stage: deploy + script: + - rsync -av --delete public /srv/nginx/$CI_BUILD_REF_NAME + environment: + name: review/$CI_BUILD_REF_NAME + url: https://$CI_BUILD_REF_NAME.example.com +``` + +It is assumed that the user has already setup NGINX and GitLab Runner in the +server this job will run on. + +--- + +The development workflow would now be: + +- Developer creates a branch locally +- Developer makes changes, commits and pushes the branch to GitLab +- Developer creates a merge request + +Behind the scenes: + +- GitLab Runner picks up the changes and starts running the jobs +- The jobs run sequentially as defined in `stages` + - First, the tests pass + - Then, the build begins and successfully also passes + - Lastly, the app is deployed to an environment with a name specific to the + branch + +So now, every branch gets its own environment and is deployed to its own place +with the added benefit of having a [history of deployments](#viewing-the-deployment-history-of-an-environment) +and also being able to [rollback changes](#rolling-back-changes) if needed. +Let's briefly see where URL that's defined in the environments is exposed. + +## Making use of the environment URL + +The environment URL is exposed in a few places within GitLab. + +| In a merge request widget as a link | In the Environments view as a button | In the Deployments view as a button | +| -------------------- | ------------ | ----------- | +| ![Environment URL in merge request](img/environments_mr_review_app.png) | ![Environment URL in environments](img/environments_link_url.png) | ![Environment URL in deployments](img/environments_link_url_deployments.png) | + +If a merge request is eventually merged to the default branch (in our case +`master`) and that branch also deploys to an environment (in our case `staging` +and/or `production`) you can see this information in the merge request itself. + +![Environment URLs in merge request](img/environments_link_url_mr.png) + +--- + +We now have a full development cycle, where our app is tested, built, deployed +as a Review app, deployed to a staging server once the merge request is merged, +and finally manually deployed to the production server. What we just described +is a single workflow, but imagine tens of developers working on a project +at the same time. They each push to their branches, and dynamic environments are +created all the time. In that case, we probably need to do some clean up. Read +next how environments can be closed. + ## Closing an environment ``` @@ -264,6 +410,10 @@ stop_review: action: stop ``` +## Grouping similar environments + +folders in environments page + ## Checkout deployments locally Since 8.13, a reference in the git repository is saved for each deployment. So @@ -308,3 +458,5 @@ Actions [deployments]: #deployments [permissions]: ../user/permissions.md [variables]: variables/README.md +[env-name]: yaml/README.md#environment-name +[only]: yaml/README.md#only-and-except diff --git a/doc/ci/img/environments_link_url.png b/doc/ci/img/environments_link_url.png new file mode 100644 index 00000000000..224c21adfb5 Binary files /dev/null and b/doc/ci/img/environments_link_url.png differ diff --git a/doc/ci/img/environments_link_url_deployments.png b/doc/ci/img/environments_link_url_deployments.png new file mode 100644 index 00000000000..9419668a9bd Binary files /dev/null and b/doc/ci/img/environments_link_url_deployments.png differ diff --git a/doc/ci/img/environments_link_url_mr.png b/doc/ci/img/environments_link_url_mr.png new file mode 100644 index 00000000000..3276dfb6096 Binary files /dev/null and b/doc/ci/img/environments_link_url_mr.png differ diff --git a/doc/ci/img/environments_mr_review_app.png b/doc/ci/img/environments_mr_review_app.png new file mode 100644 index 00000000000..a2ae25d62fa Binary files /dev/null and b/doc/ci/img/environments_mr_review_app.png differ -- cgit v1.2.1 From 6b35c4ad65205391a054725e2bf0b86985895b86 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Thu, 17 Nov 2016 09:39:23 +0000 Subject: Revert "Merge branch '22680-unlabel-limit-autocomplete-to-selected-items' into 'master'" This reverts merge request !6796 --- app/assets/javascripts/gfm_auto_complete.js.es6 | 6 ------ app/controllers/projects_controller.rb | 4 +--- app/services/projects/autocomplete_service.rb | 9 +-------- .../22680-unlabel-limit-autocomplete-to-selected-items.yml | 4 ---- 4 files changed, 2 insertions(+), 21 deletions(-) delete mode 100644 changelogs/unreleased/22680-unlabel-limit-autocomplete-to-selected-items.yml diff --git a/app/assets/javascripts/gfm_auto_complete.js.es6 b/app/assets/javascripts/gfm_auto_complete.js.es6 index 19bfdf1de8c..58c1179d250 100644 --- a/app/assets/javascripts/gfm_auto_complete.js.es6 +++ b/app/assets/javascripts/gfm_auto_complete.js.es6 @@ -51,11 +51,6 @@ if (!GitLab.GfmAutoComplete.dataLoaded) { return this.at; } else { - if (value.indexOf("unlabel") !== -1) { - GitLab.GfmAutoComplete.input.atwho('load', '~', GitLab.GfmAutoComplete.cachedData.unlabels); - } else { - GitLab.GfmAutoComplete.input.atwho('load', '~', GitLab.GfmAutoComplete.cachedData.labels); - } return value; } } @@ -363,4 +358,3 @@ }; }).call(this); - diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb index 7376c2bfeb7..a8a18b4fa16 100644 --- a/app/controllers/projects_controller.rb +++ b/app/controllers/projects_controller.rb @@ -144,15 +144,13 @@ class ProjectsController < Projects::ApplicationController autocomplete = ::Projects::AutocompleteService.new(@project, current_user) participants = ::Projects::ParticipantsService.new(@project, current_user).execute(noteable) - unlabels = autocomplete.unlabels(noteable) @suggestions = { emojis: Gitlab::AwardEmoji.urls, issues: autocomplete.issues, milestones: autocomplete.milestones, mergerequests: autocomplete.merge_requests, - labels: autocomplete.labels - unlabels, - unlabels: unlabels, + labels: autocomplete.labels, members: participants, commands: autocomplete.commands(noteable, params[:type]) } diff --git a/app/services/projects/autocomplete_service.rb b/app/services/projects/autocomplete_service.rb index 223461e88b6..015f2828921 100644 --- a/app/services/projects/autocomplete_service.rb +++ b/app/services/projects/autocomplete_service.rb @@ -13,14 +13,7 @@ module Projects end def labels - LabelsFinder.new(current_user, project_id: project.id).execute. - pluck(:title, :color).map { |l| { title: l.first, color: l.second } } - end - - def unlabels(noteable) - return [] unless noteable && noteable.respond_to?(:labels) - - noteable.labels.pluck(:title, :color).map { |l| { title: l.first, color: l.second } } + LabelsFinder.new(current_user, project_id: project.id).execute.select([:title, :color]) end def commands(noteable, type) diff --git a/changelogs/unreleased/22680-unlabel-limit-autocomplete-to-selected-items.yml b/changelogs/unreleased/22680-unlabel-limit-autocomplete-to-selected-items.yml deleted file mode 100644 index 95fd07c12e1..00000000000 --- a/changelogs/unreleased/22680-unlabel-limit-autocomplete-to-selected-items.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Limit autocomplete to currently selected items for unlabel slash command -merge_request: 22680 -author: Akram Fares -- cgit v1.2.1 From 3c8db237d9a436e8d305dea6fbc53c62d1aa641b Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Thu, 17 Nov 2016 11:45:14 +0200 Subject: Remove activerecord-session_store gem We use the Redis-backed session store from `redis-store` so this is unnecessary. --- Gemfile | 1 - Gemfile.lock | 7 ------- 2 files changed, 8 deletions(-) diff --git a/Gemfile b/Gemfile index f2291568d25..92c05b27d8c 100644 --- a/Gemfile +++ b/Gemfile @@ -334,7 +334,6 @@ gem 'email_reply_parser', '~> 0.5.8' gem 'ruby-prof', '~> 0.16.2' ## CI -gem 'activerecord-session_store', '~> 1.0.0' gem 'nested_form', '~> 0.3.2' # OAuth diff --git a/Gemfile.lock b/Gemfile.lock index 81b43f2238a..0f66f264d46 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -32,12 +32,6 @@ GEM activemodel (= 4.2.7.1) activesupport (= 4.2.7.1) arel (~> 6.0) - activerecord-session_store (1.0.0) - actionpack (>= 4.0, < 5.1) - activerecord (>= 4.0, < 5.1) - multi_json (~> 1.11, >= 1.11.2) - rack (>= 1.5.2, < 3) - railties (>= 4.0, < 5.1) activerecord_sane_schema_dumper (0.2) rails (>= 4, < 5) activesupport (4.2.7.1) @@ -809,7 +803,6 @@ PLATFORMS DEPENDENCIES RedCloth (~> 4.3.2) ace-rails-ap (~> 4.1.0) - activerecord-session_store (~> 1.0.0) activerecord_sane_schema_dumper (= 0.2) acts-as-taggable-on (~> 4.0) addressable (~> 2.3.8) -- cgit v1.2.1 From aee501cf43811f819e95541d5a0c0c6d702bf8da Mon Sep 17 00:00:00 2001 From: Achilleas Pipinellis Date: Thu, 17 Nov 2016 10:53:42 +0100 Subject: Remove ToC and fix headings in Markdown docs [ci skip] --- doc/user/markdown.md | 156 +++++++++++++++++---------------------------------- 1 file changed, 53 insertions(+), 103 deletions(-) diff --git a/doc/user/markdown.md b/doc/user/markdown.md index dbc7e0f14e3..162d1bd7ed4 100644 --- a/doc/user/markdown.md +++ b/doc/user/markdown.md @@ -1,43 +1,5 @@ # Markdown -## Table of Contents - -**[GitLab Flavored Markdown](#gitlab-flavored-markdown-gfm)** - -* [Newlines](#newlines) -* [Multiple underscores in words](#multiple-underscores-in-words) -* [URL auto-linking](#url-auto-linking) -* [Multiline Blockquote](#multiline-blockquote) -* [Code and Syntax Highlighting](#code-and-syntax-highlighting) -* [Inline Diff](#inline-diff) -* [Emoji](#emoji) -* [Special GitLab references](#special-gitlab-references) -* [Task Lists](#task-lists) -* [Videos](#videos) - -**[Standard Markdown](#standard-markdown)** - -* [Headers](#headers) -* [Emphasis](#emphasis) -* [Lists](#lists) -* [Links](#links) -* [Images](#images) -* [Blockquotes](#blockquotes) -* [Inline HTML](#inline-html) -* [Horizontal Rule](#horizontal-rule) -* [Line Breaks](#line-breaks) -* [Tables](#tables) -* [Footnotes](#footnotes) - -**[Wiki-Specific Markdown](#wiki-specific-markdown)** - -* [Wiki - Direct page link](#wiki-direct-page-link) -* [Wiki - Direct file link](#wiki-direct-file-link) -* [Wiki - Hierarchical link](#wiki-hierarchical-link) -* [Wiki - Root link](#wiki-root-link) - -**[References](#references)** - ## GitLab Flavored Markdown (GFM) > **Note:** @@ -64,7 +26,7 @@ You can use GFM in the following areas: You can also use other rich text files in GitLab. You might have to install a dependency to do so. Please see the [github-markup gem readme](https://github.com/gitlabhq/markup#markups) for more information. -## Newlines +### Newlines > If this is not rendered correctly, see https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/user/markdown.md#newlines @@ -84,7 +46,7 @@ Violets are blue Sugar is sweet -## Multiple underscores in words +### Multiple underscores in words > If this is not rendered correctly, see https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/user/markdown.md#multiple-underscores-in-words @@ -99,7 +61,7 @@ perform_complicated_task do_this_and_do_that_and_another_thing -## URL auto-linking +### URL auto-linking > If this is not rendered correctly, see https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/user/markdown.md#url-auto-linking @@ -120,7 +82,7 @@ GFM will autolink almost any URL you copy and paste into your text: * irc://irc.freenode.net/gitlab * http://localhost:3000 -## Multiline Blockquote +### Multiline Blockquote > If this is not rendered correctly, see https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/user/markdown.md#multiline-blockquote @@ -154,7 +116,7 @@ multiple lines, you can quote that without having to manually prepend `>` to every line! >>> -## Code and Syntax Highlighting +### Code and Syntax Highlighting > If this is not rendered correctly, see https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/user/markdown.md#code-and-syntax-highlighting @@ -224,7 +186,7 @@ s = "There is no highlighting for this." But let's throw in a tag. ``` -## Inline Diff +### Inline Diff > If this is not rendered correctly, see https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/user/markdown.md#inline-diff @@ -240,7 +202,7 @@ However the wrapping tags cannot be mixed as such: - {- deletions -] - [- deletions -} -## Emoji +### Emoji > If this is not rendered correctly, see https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/user/markdown.md#emoji @@ -265,7 +227,7 @@ If you are new to this, don't be :fearful:. You can easily join the emoji :famil Consult the [Emoji Cheat Sheet](http://emoji.codes) for a list of all supported emoji codes. :thumbsup: -## Special GitLab References +### Special GitLab References GFM recognizes special references. @@ -305,7 +267,7 @@ GFM also recognizes certain cross-project references: | `namespace/project@9ba12248...b19a04f5` | commit range comparison | | `namespace/project~"Some label"` | issues with given label | -## Task Lists +### Task Lists > If this is not rendered correctly, see https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/user/markdown.md#task-lists @@ -328,7 +290,7 @@ You can add task lists to issues, merge requests and comments. To create a task Task lists can only be created in descriptions, not in titles. Task item state can be managed by editing the description's Markdown or by toggling the rendered check boxes. -## Videos +### Videos > If this is not rendered correctly, see https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/user/markdown.md#videos @@ -345,9 +307,9 @@ Here's a sample video: ![Sample Video](img/markdown_video.mp4) -# Standard Markdown +## Standard Markdown -## Headers +### Headers ```no-highlight # H1 @@ -366,21 +328,6 @@ Alt-H2 ------ ``` -# H1 -## H2 -### H3 -#### H4 -##### H5 -###### H6 - -Alternatively, for H1 and H2, an underline-ish style: - -Alt-H1 -====== - -Alt-H2 ------- - ### Header IDs and links All Markdown-rendered headers automatically get IDs, except in comments. @@ -416,7 +363,7 @@ Would generate the following link IDs: Note that the Emoji processing happens before the header IDs are generated, so the Emoji is converted to an image which then gets removed from the ID. -## Emphasis +### Emphasis ```no-highlight Emphasis, aka italics, with *asterisks* or _underscores_. @@ -436,7 +383,7 @@ Combined emphasis with **asterisks and _underscores_**. Strikethrough uses two tildes. ~~Scratch this.~~ -## Lists +### Lists ```no-highlight 1. First ordered list item @@ -492,7 +439,7 @@ the second list item will be incorrectly labeled as `1`. Second paragraph of first item. 2. Another item -## Links +### Links There are two ways to create links, inline-style and reference-style. @@ -501,9 +448,9 @@ There are two ways to create links, inline-style and reference-style. [I'm a reference-style link][Arbitrary case-insensitive reference text] [I'm a relative reference to a repository file](LICENSE) - + [I am an absolute reference within the repository](/doc/user/markdown.md) - + [I link to the Milestones page](/../milestones) [You can use numbers for reference-style link definitions][1] @@ -523,9 +470,9 @@ There are two ways to create links, inline-style and reference-style. [I'm a relative reference to a repository file](LICENSE)[^1] [I am an absolute reference within the repository](/doc/user/markdown.md) - + [I link to the Milestones page](/../milestones) - + [You can use numbers for reference-style link definitions][1] Or leave it empty and use the [link text itself][] @@ -544,7 +491,8 @@ Relative links do not allow referencing project files in a wiki page or wiki pag will point the link to `wikis/style` when the link is inside of a wiki markdown file. -## Images + +### Images Here's our logo (hover to see the title text): @@ -568,7 +516,7 @@ Reference-style: [logo]: img/markdown_logo.png -## Blockquotes +### Blockquotes ```no-highlight > Blockquotes are very handy in email to emulate reply text. @@ -586,7 +534,7 @@ Quote break. > This is a very long line that will still be quoted properly when it wraps. Oh boy let's keep writing to make sure this is long enough to actually wrap for everyone. Oh, you can *put* **Markdown** into a blockquote. -## Inline HTML +### Inline HTML You can also use raw HTML in your Markdown, and it'll mostly work pretty well. @@ -610,7 +558,7 @@ See the documentation for HTML::Pipeline's [SanitizationFilter](http://www.rubyd
Does *not* work **very** well. Use HTML tags.
-## Horizontal Rule +### Horizontal Rule ``` Three or more... @@ -642,7 +590,7 @@ ___ Underscores -## Line Breaks +### Line Breaks My basic recommendation for learning how line breaks work is to experiment and discover -- hit <Enter> once (i.e., insert one newline), then hit it twice (i.e., insert two newlines), see what happens. You'll soon learn to get what you want. "Markdown Toggle" is your friend. @@ -672,7 +620,7 @@ This line is also a separate paragraph, and... This line is on its own line, because the previous line ends with two spaces. -## Tables +### Tables Tables aren't part of the core Markdown spec, but they are part of GFM and Markdown Here supports them. @@ -708,16 +656,15 @@ By including colons in the header row, you can align the text within that column | Cell 1 | Cell 2 | Cell 3 | Cell 4 | Cell 5 | Cell 6 | | Cell 7 | Cell 8 | Cell 9 | Cell 10 | Cell 11 | Cell 12 | -## Footnotes - -You can add footnotes to your text as follows.[^1] -[^1]: This is my awesome footnote. +### Footnotes ``` -You can add footnotes to your text as follows.[^1] -[^1]: This is my awesome footnote. +You can add footnotes to your text as follows.[^2] +[^2]: This is my awesome footnote. ``` +You can add footnotes to your text as follows.[^2] + ## Wiki-specific Markdown The following examples show how links inside wikis behave. @@ -752,30 +699,30 @@ A link can be constructed relative to the current wiki page using `./`, - If this snippet was placed on a page at `/documentation/main`, it would link to `/documentation/related`: - ```markdown - [Link to Related Page](./related) - ``` + ```markdown + [Link to Related Page](./related) + ``` - If this snippet was placed on a page at `/documentation/related/content`, it would link to `/documentation/main`: - ```markdown - [Link to Related Page](../main) - ``` + ```markdown + [Link to Related Page](../main) + ``` - If this snippet was placed on a page at `/documentation/main`, it would link to `/documentation/related.md`: - ```markdown - [Link to Related Page](./related.md) - ``` + ```markdown + [Link to Related Page](./related.md) + ``` - If this snippet was placed on a page at `/documentation/related/content`, it would link to `/documentation/main.md`: - ```markdown - [Link to Related Page](../main.md) - ``` + ```markdown + [Link to Related Page](../main.md) + ``` ### Wiki - Root link @@ -783,22 +730,25 @@ A link starting with a `/` is relative to the wiki root. - This snippet links to `/documentation`: - ```markdown - [Link to Related Page](/documentation) - ``` + ```markdown + [Link to Related Page](/documentation) + ``` - This snippet links to `/miscellaneous.md`: - ```markdown - [Link to Related Page](/miscellaneous.md) - ``` + ```markdown + [Link to Related Page](/miscellaneous.md) + ``` + ## References - This document leveraged heavily from the [Markdown-Cheatsheet](https://github.com/adam-p/markdown-here/wiki/Markdown-Cheatsheet). - The [Markdown Syntax Guide](https://daringfireball.net/projects/markdown/syntax) at Daring Fireball is an excellent resource for a detailed explanation of standard markdown. - [Dillinger.io](http://dillinger.io) is a handy tool for testing standard markdown. +[^1]: This link will be broken if you see this document from the Help page or docs.gitlab.com +[^2]: This is my awesome footnote. + [markdown.md]: https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/user/markdown.md [rouge]: http://rouge.jneen.net/ "Rouge website" [redcarpet]: https://github.com/vmg/redcarpet "Redcarpet website" -[^1]: This link will be broken if you see this document from the Help page or docs.gitlab.com -- cgit v1.2.1 From 27db4bc062f6087d988b167f3643e9b8752c3cf3 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Thu, 17 Nov 2016 11:55:04 +0200 Subject: Remove unused nested_form gem --- Gemfile | 3 --- Gemfile.lock | 2 -- 2 files changed, 5 deletions(-) diff --git a/Gemfile b/Gemfile index 92c05b27d8c..327d35c2350 100644 --- a/Gemfile +++ b/Gemfile @@ -333,9 +333,6 @@ gem 'email_reply_parser', '~> 0.5.8' gem 'ruby-prof', '~> 0.16.2' -## CI -gem 'nested_form', '~> 0.3.2' - # OAuth gem 'oauth2', '~> 1.2.0' diff --git a/Gemfile.lock b/Gemfile.lock index 0f66f264d46..e6c8adea280 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -410,7 +410,6 @@ GEM multi_xml (0.5.5) multipart-post (2.0.0) mysql2 (0.3.20) - nested_form (0.3.2) net-ldap (0.12.1) net-ssh (3.0.1) newrelic_rpm (3.16.0.318) @@ -894,7 +893,6 @@ DEPENDENCIES minitest (~> 5.7.0) mousetrap-rails (~> 1.4.6) mysql2 (~> 0.3.16) - nested_form (~> 0.3.2) net-ssh (~> 3.0.1) newrelic_rpm (~> 3.16) nokogiri (~> 1.6.7, >= 1.6.7.2) -- cgit v1.2.1 From f9646d3e04a41aaeb6a2e604622ecad88a961641 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Thu, 17 Nov 2016 11:28:25 +0200 Subject: Highlight first user autocomplete option --- app/assets/javascripts/gfm_auto_complete.js.es6 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/assets/javascripts/gfm_auto_complete.js.es6 b/app/assets/javascripts/gfm_auto_complete.js.es6 index 19bfdf1de8c..0253f36c21b 100644 --- a/app/assets/javascripts/gfm_auto_complete.js.es6 +++ b/app/assets/javascripts/gfm_auto_complete.js.es6 @@ -35,7 +35,7 @@ DefaultOptions: { sorter: function(query, items, searchKey) { // Highlight first item only if at least one char was typed - this.setting.highlightFirst = query.length > 0; + this.setting.highlightFirst = this.setting.alwaysHighlightFirst || query.length > 0; if ((items[0].name != null) && items[0].name === 'loading') { return items; } @@ -117,6 +117,7 @@ insertTpl: '${atwho-at}${username}', searchKey: 'search', data: ['loading'], + alwaysHighlightFirst: true, callbacks: { sorter: this.DefaultOptions.sorter, filter: this.DefaultOptions.filter, @@ -363,4 +364,3 @@ }; }).call(this); - -- cgit v1.2.1 From 25c9c8ce732a59eb05f93b9004e12917b8a00407 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Coutable?= Date: Wed, 16 Nov 2016 18:39:36 +0100 Subject: Document the `rake ee_compat_check` task MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rémy Coutable --- doc/development/limit_ee_conflicts.md | 144 ++++++++++++++++++++-------------- 1 file changed, 85 insertions(+), 59 deletions(-) diff --git a/doc/development/limit_ee_conflicts.md b/doc/development/limit_ee_conflicts.md index e8af1c6af7b..29299b0690b 100644 --- a/doc/development/limit_ee_conflicts.md +++ b/doc/development/limit_ee_conflicts.md @@ -8,75 +8,54 @@ Usually, GitLab Community Edition is merged into the Enterprise Edition once a week. During these merges, it's very common to get conflicts when some changes in CE do not apply cleanly to EE. -In this document, we will list the best practices to avoid such conflicts or to -make them easily solvable by the person who does the CE->EE merge. +There are a few things that can help you as a developer to: -## Different type of conflicts +- know when your merge request to CE will conflict when merged to EE +- avoid such conflicts in the first place +- ease future conflict resolutions if conflict is inevitable -### Models +## Check the `rake ee_compat_check` in your merge requests -#### Common issues +For each commit (except on `master`), the `rake ee_compat_check` CI job tries to +detect if the current branch's changes will conflict during the CE->EE merge. -TODO +The job reports what files are conflicting and how to setup a merge request +against EE. Here is roughly how it works: -#### Mitigations +1. Generates the diff between your branch and current CE `master` +1. Tries to apply it to current EE `master` +1. If it applies cleanly, the job succeeds, otherwise... +1. Detects a branch with the `-ee` suffix in EE +1. If it exists, generate the diff between this branch and current EE `master` +1. Tries to apply it to current EE `master` +1. If it applies cleanly, the job succeeds -TODO +In the case where the job fails, it means you should create a `-ee` +branch, push it to EE and open a merge request against EE `master`. At this +point if you retry the failing job in your CE merge request, it should now pass. -### Services +Notes: -#### Common issues +- This task is not a silver-bullet, its current goal is to bring awareness to + developers that their work needs to be ported to EE. +- Community contributors shouldn't submit merge requests against EE, but + reviewers should take actions by either creating such EE merge request or + asking a GitLab developer to do it once the merge request is merged. +- If you branch is more than 500 commits behind `master`, the job will fail and + you should rebase your branch upon latest `master`. -TODO - -#### Mitigations - -TODO +## Possible type of conflicts ### Controllers -#### Common issues +#### List or arrays are augmented in EE In controllers, the most common type of conflicts is either in a `before_action` that has a list of actions in CE but EE adds some actions to that list. Same problems often occurs for `params.require` / `params.permit` calls. -Other conflicts usually involve specific code for EE-specific features such as: - -- LDAP: - ```diff - def destroy - @key = current_user.keys.find(params[:id]) - - @key.destroy - + @key.destroy unless @key.is_a? LDAPKey - - respond_to do |format| - ``` -- Geo: - ```diff - def after_sign_out_path_for(resource) - - current_application_settings.after_sign_out_path.presence || new_user_session_path - + if Gitlab::Geo.secondary? - + Gitlab::Geo.primary_node.oauth_logout_url(@geo_logout_state) - + else - + current_application_settings.after_sign_out_path.presence || new_user_session_path - + end - end - ``` -- Audit log: - ```diff - def approve_access_request - - Members::ApproveAccessRequestService.new(membershipable, current_user, params).execute - + member = Members::ApproveAccessRequestService.new(membershipable, current_user, params).execute - + - + log_audit_event(member, action: :create) - - redirect_to polymorphic_url([membershipable, :members]) - end - ``` - -#### Mitigations +##### Mitigations Separate CE and EE actions/keywords. For instance for `params.require` in `ProjectsController`: @@ -109,20 +88,56 @@ def project_params_ee end ``` -### Views +#### Additional condition(s) in EE + +For instance for LDAP: + +```diff + def destroy + @key = current_user.keys.find(params[:id]) + - @key.destroy + + @key.destroy unless @key.is_a? LDAPKey + + respond_to do |format| +``` + +Or for Geo: -#### Common issues +```diff +def after_sign_out_path_for(resource) +- current_application_settings.after_sign_out_path.presence || new_user_session_path ++ if Gitlab::Geo.secondary? ++ Gitlab::Geo.primary_node.oauth_logout_url(@geo_logout_state) ++ else ++ current_application_settings.after_sign_out_path.presence || new_user_session_path ++ end +end +``` -A few issues often happen here: +Or even for audit log: -1. Indentation issue -1. A block of code added in CE conflicts because there is already another block - at the same place in EE +```diff +def approve_access_request +- Members::ApproveAccessRequestService.new(membershipable, current_user, params).execute ++ member = Members::ApproveAccessRequestService.new(membershipable, current_user, params).execute ++ ++ log_audit_event(member, action: :create) -#### Mitigations + redirect_to polymorphic_url([membershipable, :members]) +end +``` + +### Views + +#### Additional view code in EE + +A block of code added in CE conflicts because there is already another block +at the same place in EE + +##### Mitigations Blocks of code that are EE-specific should be moved to partials as much as -possible to avoid conflicts with big chunks of HAML code that that are not funny +possible to avoid conflicts with big chunks of HAML code that that are not fun to resolve when you add the indentation in the equation. For instance this kind of things: @@ -242,6 +257,17 @@ level are encouraged to use partials even for code that's in CE to logically split big views into several smaller files. +#### Indentation issue + +Sometimes a code block is indented more or less in EE because there's an +additional condition. + +##### Mitigations + +Blocks of code that are EE-specific should be moved to partials as much as +possible to avoid conflicts with big chunks of HAML code that that are not fun +to resolve when you add the indentation in the equation. + --- [Return to Development documentation](README.md) -- cgit v1.2.1 From 556aaebc22b58d1e496512323173ea1ca28370d1 Mon Sep 17 00:00:00 2001 From: Achilleas Pipinellis Date: Thu, 17 Nov 2016 11:58:57 +0100 Subject: Finish "Stopping envs" and "Grouping similar envs" sections [ci skip] --- doc/ci/environments.md | 83 +++++++++++++++++++---------- doc/ci/img/environments_dynamic_groups.png | Bin 0 -> 134164 bytes 2 files changed, 56 insertions(+), 27 deletions(-) create mode 100644 doc/ci/img/environments_dynamic_groups.png diff --git a/doc/ci/environments.md b/doc/ci/environments.md index 428e8a0cf25..861a639b3fc 100644 --- a/doc/ci/environments.md +++ b/doc/ci/environments.md @@ -273,9 +273,9 @@ Last but not least, we tell the job to run [`only`][only] on branches [`except`][only] master. >**Note:** -You are not bound to use only slashes in the dynamic environments' names (`/`), -but as we will see later, this will enable the "grouping similar environments" -feature. +You are not bound to use the same prefix or only slashes in the dynamic +environments' names (`/`), but as we will see later, this will enable the +[grouping similar environments](#grouping-similar-environments) feature. The whole `.gitlab-ci.yml` looks like this so far: @@ -388,31 +388,74 @@ and finally manually deployed to the production server. What we just described is a single workflow, but imagine tens of developers working on a project at the same time. They each push to their branches, and dynamic environments are created all the time. In that case, we probably need to do some clean up. Read -next how environments can be closed. +next how environments can be stopped. -## Closing an environment +## Stopping an environment -``` -review: +By stopping an environment, you are effectively terminating its recording of the +deployments that happen in it. + +>**Note:** +Starting with GitLab 8.14, dynamic environments will be stopped automatically +when their associated branch is removed. + +There is a special case where environments can be manually closed. That can +happen if you provide another job for that matter. The syntax is a little +tricky since a job calls another job to do the job. + +Consider the following example where the `deploy_review` calls the `stop_review` +to clean up and stop the environment: + +```yaml +deploy_review: stage: deploy script: - - rsync -av --delete public /srv/nginx/pages/$CI_BUILD_REF_NAME + - echo "Deploy a review app" environment: name: review/$CI_BUILD_REF_NAME - url: http://$CI_BUILD_REF_NAME.$APPS_DOMAIN + url: https://$CI_BUILD_REF_NAME.example.com on_stop: stop_review + only: + - branches + except: + - master stop_review: - script: rm -rf /srv/nginx/pages/$CI_BUILD_REF_NAME + script: + - echo "Remove review app" when: manual environment: name: review/$CI_BUILD_REF_NAME action: stop ``` +You can read more in the [`.gitlab-ci.yml` reference][onstop]. + ## Grouping similar environments -folders in environments page +> [Introduced][ce-7015] in GitLab 8.14. + +As we've seen in the [dynamic environments](#dynamic-environments), you can + +In short, environments that are named like `type/foo` are presented under a +group named `type`. + +In our minimal example, we name the environments `review/$CI_BUILD_REF_NAME` +where `$CI_BUILD_REF_NAME` is the branch name: + +```yaml +deploy_review: + stage: deploy + script: + - echo "Deploy a review app" + environment: + name: review/$CI_BUILD_REF_NAME +``` + +In that case, if you visit the Environments page, and provided the branches +exist, you should see something like: + +![Environment groups](img/environments_dynamic_groups.png) ## Checkout deployments locally @@ -435,22 +478,6 @@ Below are some links you may find interesting: - [A blog post on Deployments & Environments](https://about.gitlab.com/2016/08/26/ci-deployment-and-environments/) - [Review Apps](review_apps.md) Expand dynamic environments to deploy your code for every branch - -## TODO - -Actions - -- View environments + -- View deployments + - - Rollback deployments + - - Run deployments + -- View link to environment URL -- View last commit message of deployment + -- View person who performed the deployment + -- View commit SHA that triggered the deployment + -- View branch the deployment was based on + -- View time ago the deployment was performed + - [Pipelines]: pipelines.md [jobs]: yaml/README.md#jobs [yaml]: yaml/README.md @@ -460,3 +487,5 @@ Actions [variables]: variables/README.md [env-name]: yaml/README.md#environment-name [only]: yaml/README.md#only-and-except +[onstop]: yaml/README.md#environment-on_stop +[ce-7015]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/7015 diff --git a/doc/ci/img/environments_dynamic_groups.png b/doc/ci/img/environments_dynamic_groups.png new file mode 100644 index 00000000000..e89b66c502c Binary files /dev/null and b/doc/ci/img/environments_dynamic_groups.png differ -- cgit v1.2.1 From 964872eb20ca6eb2da649032291ae089a8837adc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Coutable?= Date: Thu, 17 Nov 2016 12:08:03 +0100 Subject: Update CHANGELOG.md for 8.13.6 [ci skip] --- CHANGELOG.md | 11 +++++++++++ .../23824-activity-page-does-not-show-commits-comments.yml | 4 ---- changelogs/unreleased/24038-fix-no-register-pane-if-ldap.yml | 4 ---- changelogs/unreleased/24397-load-labels-on-mr-tabs.yml | 4 ---- changelogs/unreleased/fix-cache-for-commit-status.yml | 4 ---- .../fix-uncheckable-label-for-force_remove_source_branch.yml | 4 ---- changelogs/unreleased/fix_saml_ldap_link.yml | 5 ----- changelogs/unreleased/issue_20245.yml | 4 ---- changelogs/unreleased/rs-issue-24527.yml | 4 ---- 9 files changed, 11 insertions(+), 33 deletions(-) delete mode 100644 changelogs/unreleased/23824-activity-page-does-not-show-commits-comments.yml delete mode 100644 changelogs/unreleased/24038-fix-no-register-pane-if-ldap.yml delete mode 100644 changelogs/unreleased/24397-load-labels-on-mr-tabs.yml delete mode 100644 changelogs/unreleased/fix-cache-for-commit-status.yml delete mode 100644 changelogs/unreleased/fix-uncheckable-label-for-force_remove_source_branch.yml delete mode 100644 changelogs/unreleased/fix_saml_ldap_link.yml delete mode 100644 changelogs/unreleased/issue_20245.yml delete mode 100644 changelogs/unreleased/rs-issue-24527.yml diff --git a/CHANGELOG.md b/CHANGELOG.md index c35175b4bbc..73f2703a0a4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -76,6 +76,17 @@ entry. - Fix "Without projects" filter. !6611 (Ben Bodenmiller) - Fix 404 when visit /projects page +## 8.13.6 (2016-11-17) + +- Omniauth auto link LDAP user falls back to find by DN when user cannot be found by UID. !7002 +- Fix no "Register" tab if ldap auth is enabled (#24038). !7274 (Luc Didry) +- Fix cache for commit status in commits list to respect branches. !7372 +- Fix issue causing Labels not to appear in sidebar on MR page. !7416 (Alex Sanford) +- Limit labels returned for a specific project as an administrator. !7496 +- Clicking "force remove source branch" label now toggles the checkbox again. +- Allow commit note to be visible if repo is visible. +- Fix project Visibility Level selector not using default values. + ## 8.13.5 (2016-11-08) - Restore unauthenticated access to public container registries diff --git a/changelogs/unreleased/23824-activity-page-does-not-show-commits-comments.yml b/changelogs/unreleased/23824-activity-page-does-not-show-commits-comments.yml deleted file mode 100644 index 48f733f9c5e..00000000000 --- a/changelogs/unreleased/23824-activity-page-does-not-show-commits-comments.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Allow commit note to be visible if repo is visible -merge_request: -author: diff --git a/changelogs/unreleased/24038-fix-no-register-pane-if-ldap.yml b/changelogs/unreleased/24038-fix-no-register-pane-if-ldap.yml deleted file mode 100644 index 53f418b6b18..00000000000 --- a/changelogs/unreleased/24038-fix-no-register-pane-if-ldap.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Fix no "Register" tab if ldap auth is enabled (#24038) -merge_request: 7274 -author: Luc Didry diff --git a/changelogs/unreleased/24397-load-labels-on-mr-tabs.yml b/changelogs/unreleased/24397-load-labels-on-mr-tabs.yml deleted file mode 100644 index 6bfa7fa1a49..00000000000 --- a/changelogs/unreleased/24397-load-labels-on-mr-tabs.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Fix issue causing Labels not to appear in sidebar on MR page -merge_request: 7416 -author: Alex Sanford diff --git a/changelogs/unreleased/fix-cache-for-commit-status.yml b/changelogs/unreleased/fix-cache-for-commit-status.yml deleted file mode 100644 index eb4e96e75ae..00000000000 --- a/changelogs/unreleased/fix-cache-for-commit-status.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Fix cache for commit status in commits list to respect branches -merge_request: 7372 -author: diff --git a/changelogs/unreleased/fix-uncheckable-label-for-force_remove_source_branch.yml b/changelogs/unreleased/fix-uncheckable-label-for-force_remove_source_branch.yml deleted file mode 100644 index 8b41063151b..00000000000 --- a/changelogs/unreleased/fix-uncheckable-label-for-force_remove_source_branch.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Clicking "force remove source branch" label now toggles the checkbox again -merge_request: -author: diff --git a/changelogs/unreleased/fix_saml_ldap_link.yml b/changelogs/unreleased/fix_saml_ldap_link.yml deleted file mode 100644 index 3b6f26d610e..00000000000 --- a/changelogs/unreleased/fix_saml_ldap_link.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Omniauth auto link LDAP user falls back to find by DN when user cannot be found - by UID -merge_request: 7002 -author: diff --git a/changelogs/unreleased/issue_20245.yml b/changelogs/unreleased/issue_20245.yml deleted file mode 100644 index e5d09d85683..00000000000 --- a/changelogs/unreleased/issue_20245.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Fix project Visibility Level selector not using default values -merge_request: -author: diff --git a/changelogs/unreleased/rs-issue-24527.yml b/changelogs/unreleased/rs-issue-24527.yml deleted file mode 100644 index a7b6358e60e..00000000000 --- a/changelogs/unreleased/rs-issue-24527.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Limit labels returned for a specific project as an administrator -merge_request: 7496 -author: -- cgit v1.2.1 From 80073da95771fc51a4ac05814b0a7ceae1cbdac9 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Thu, 17 Nov 2016 11:13:39 +0000 Subject: Include author in assignee dropdown search When searching for the author in the assignee dropdown it now correctly returns the user Closes #22905 --- app/controllers/autocomplete_controller.rb | 8 ++++- .../unreleased/assignee-dropdown-autocomplete.yml | 4 +++ spec/features/issues/issue_sidebar_spec.rb | 35 +++++++++++++++++++++- 3 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 changelogs/unreleased/assignee-dropdown-autocomplete.yml diff --git a/app/controllers/autocomplete_controller.rb b/app/controllers/autocomplete_controller.rb index daa82336208..fcfa508128e 100644 --- a/app/controllers/autocomplete_controller.rb +++ b/app/controllers/autocomplete_controller.rb @@ -55,7 +55,13 @@ class AutocompleteController < ApplicationController def find_users @users = if @project - @project.team.users + user_ids = @project.team.users.map(&:id) + + if params[:author_id].present? + user_ids << params[:author_id] + end + + User.where(id: user_ids) elsif params[:group_id].present? group = Group.find(params[:group_id]) return render_404 unless can?(current_user, :read_group, group) diff --git a/changelogs/unreleased/assignee-dropdown-autocomplete.yml b/changelogs/unreleased/assignee-dropdown-autocomplete.yml new file mode 100644 index 00000000000..9d046b726b7 --- /dev/null +++ b/changelogs/unreleased/assignee-dropdown-autocomplete.yml @@ -0,0 +1,4 @@ +--- +title: Assignee dropdown now searches author of issue or merge request +merge_request: +author: diff --git a/spec/features/issues/issue_sidebar_spec.rb b/spec/features/issues/issue_sidebar_spec.rb index 4b1aec8bf71..bc068b5e7e0 100644 --- a/spec/features/issues/issue_sidebar_spec.rb +++ b/spec/features/issues/issue_sidebar_spec.rb @@ -1,7 +1,9 @@ require 'rails_helper' feature 'Issue Sidebar', feature: true do - let(:project) { create(:project) } + include WaitForAjax + + let(:project) { create(:project, :public) } let(:issue) { create(:issue, project: project) } let!(:user) { create(:user)} @@ -10,6 +12,37 @@ feature 'Issue Sidebar', feature: true do login_as(user) end + context 'assignee', js: true do + let(:user2) { create(:user) } + let(:issue2) { create(:issue, project: project, author: user2) } + + before do + project.team << [user, :developer] + visit_issue(project, issue2) + + find('.block.assignee .edit-link').click + + wait_for_ajax + end + + it 'shows author in assignee dropdown' do + page.within '.dropdown-menu-user' do + expect(page).to have_content(user2.name) + end + end + + it 'shows author when filtering assignee dropdown' do + page.within '.dropdown-menu-user' do + find('.dropdown-input-field').native.send_keys user2.name + sleep 1 # Required to wait for end of input delay + + wait_for_ajax + + expect(page).to have_content(user2.name) + end + end + end + context 'as a allowed user' do before do project.team << [user, :developer] -- cgit v1.2.1 From 9ea4a5bca589aa095dd3b633245b551b4c758d82 Mon Sep 17 00:00:00 2001 From: Achilleas Pipinellis Date: Thu, 17 Nov 2016 12:15:06 +0100 Subject: Add note about auto-stopping of environments --- doc/ci/environments.md | 17 +++++++++++++---- doc/ci/yaml/README.md | 7 ++++++- 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/doc/ci/environments.md b/doc/ci/environments.md index 861a639b3fc..cc65f0ad8ad 100644 --- a/doc/ci/environments.md +++ b/doc/ci/environments.md @@ -395,11 +395,12 @@ next how environments can be stopped. By stopping an environment, you are effectively terminating its recording of the deployments that happen in it. ->**Note:** -Starting with GitLab 8.14, dynamic environments will be stopped automatically -when their associated branch is removed. +A branch is associated with an environment when the CI pipeline that is created +for this branch, was recently deployed to this environment. You can think of +the CI pipeline as the glue between the branch and the environment: +`branch ➔ CI pipeline ➔ environment`. -There is a special case where environments can be manually closed. That can +There is a special case where environments can be manually stopped. That can happen if you provide another job for that matter. The syntax is a little tricky since a job calls another job to do the job. @@ -429,6 +430,14 @@ stop_review: action: stop ``` +>**Note:** +Starting with GitLab 8.14, dynamic environments will be stopped automatically +when their associated branch is deleted. + +When you have an environment that has a stop action defined (typically when +the environment describes a review app), GitLab will automatically trigger a +stop action when the associated branch is deleted. + You can read more in the [`.gitlab-ci.yml` reference][onstop]. ## Grouping similar environments diff --git a/doc/ci/yaml/README.md b/doc/ci/yaml/README.md index bc9c0897f5a..1e096444903 100644 --- a/doc/ci/yaml/README.md +++ b/doc/ci/yaml/README.md @@ -627,7 +627,12 @@ deploy to production: #### environment:on_stop -> [Introduced][ce-6669] in GitLab 8.13. +> +**Notes:** +- [Introduced][ce-6669] in GitLab 8.13. +- Starting with GitLab 8.14, when you have an environment that has a stop action + defined, GitLab will automatically trigger a stop action when the associated + branch is deleted. Closing (stoping) environments can be achieved with the `on_stop` keyword defined under `environment`. It declares a different job that runs in order to close -- cgit v1.2.1 From e5aebccb640636cf0ea74b3569772b8a9156bb48 Mon Sep 17 00:00:00 2001 From: Achilleas Pipinellis Date: Thu, 17 Nov 2016 12:16:13 +0100 Subject: Add stop environment permissions and remove delete [ci skip] --- doc/user/permissions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/user/permissions.md b/doc/user/permissions.md index a33183fa01c..cea78864df2 100644 --- a/doc/user/permissions.md +++ b/doc/user/permissions.md @@ -33,7 +33,7 @@ The following table depicts the various user permission levels in a project. | See a container registry | | ✓ | ✓ | ✓ | ✓ | | See environments | | ✓ | ✓ | ✓ | ✓ | | Create new environments | | | ✓ | ✓ | ✓ | -| Delete environments | | | | ✓ | ✓ | +| Stop environments | | | ✓ | ✓ | ✓ | | See a list of merge requests | | ✓ | ✓ | ✓ | ✓ | | Manage/Accept merge requests | | | ✓ | ✓ | ✓ | | Create new merge request | | | ✓ | ✓ | ✓ | -- cgit v1.2.1 From 1ccc69d0dc25e1b59e68e1737c217f3003b3b372 Mon Sep 17 00:00:00 2001 From: Achilleas Pipinellis Date: Wed, 19 Oct 2016 11:55:27 +0200 Subject: Add Review apps link to CI README --- doc/ci/README.md | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/doc/ci/README.md b/doc/ci/README.md index 6b90940c047..545cc72682d 100644 --- a/doc/ci/README.md +++ b/doc/ci/README.md @@ -1,6 +1,6 @@ -## GitLab CI Documentation +# GitLab CI Documentation -### CI User documentation +## CI User documentation - [Get started with GitLab CI](quick_start/README.md) - [CI examples for various languages](examples/README.md) @@ -20,4 +20,8 @@ - [API](../api/ci/README.md) - [CI services (linked docker containers)](services/README.md) - [CI/CD pipelines settings](../user/project/pipelines/settings.md) -- [**New CI build permissions model**](../user/project/new_ci_build_permissions_model.md) Read about what changed in GitLab 8.12 and how that affects your builds. There's a new way to access your Git submodules and LFS objects in builds. +- [Review Apps](review_apps/index.md) + +## Breaking changes + +- [New CI build permissions model](../user/project/new_ci_build_permissions_model.md) Read about what changed in GitLab 8.12 and how that affects your builds. There's a new way to access your Git submodules and LFS objects in builds. -- cgit v1.2.1 From eee65421feedaf5aa510e05794f4da0618401e96 Mon Sep 17 00:00:00 2001 From: Achilleas Pipinellis Date: Wed, 19 Oct 2016 11:55:46 +0200 Subject: WIP review apps Closes https://gitlab.com/gitlab-org/gitlab-ce/issues/23484 [ci skip] --- doc/ci/review_apps/index.md | 82 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 82 insertions(+) create mode 100644 doc/ci/review_apps/index.md diff --git a/doc/ci/review_apps/index.md b/doc/ci/review_apps/index.md new file mode 100644 index 00000000000..ee279025a9d --- /dev/null +++ b/doc/ci/review_apps/index.md @@ -0,0 +1,82 @@ +# Review apps + +> [Introduced][ce-21971] in GitLab 8.12. Further additions were made in GitLab 8.13. + +Review apps are automatically-created [environments] that run your code for each +branch. That means [merge requests] can be reviewed in a live-running environment. + +They mostly make sense to be used with web applications. + +Review Apps can make a huge impact on your development flow. Reviewing anything +from performance to interface changes becomes much easier with a live environment. + +> +Inspired by [Heroku's Review Apps][heroku-apps] which itself was inspired by +[Fourchette]. + +## Dynamic environments + +You can now use predefined CI variables as a name for environments. In addition, +you can specify a URL for the environment configuration in your .gitlab-ci.yml +file. + +- Mapping branch with environment + +This issue describes dynamic environments implementation, mostly to be used with dynamically create applications. +These application will be mostly used for Review Apps. + +## Assumptions + +1. We will allow to create dynamic environments from `.gitlab-ci.yml`, by allowing to specify environment variables: `review_apps_${CI_BUILD_REF_NAME}`, +1. We will use multiple deployments of the same application per environment, +1. The URL will be assigned to environment on the creation, and updated later if necessary, +1. The URL will be specified in `.gitlab-ci.yml`, possibly introducing regexp for getting an URL from build log if required, +1. We need some form to distinguish between production/staging and review app environment, +1. We don't try to manage life cycle of deployments in the first iteration, possibly we will extend a Pipeline to add jobs that will be responsible either for cleaning up or removing old deployments and closing environments. + +## Configuration + +``` +review_apps: + environment: + name: review/$CI_BUILD_REF_NAME + url: http://$CI_BUILD_REF_NAME.review.gitlab.com/ +``` + +### Remarks + +1. We are limited to use only CI predefined variables, since this is not easy task to pass the URL from build, +2. I do prefer nested `url:` since this allows us in the future to extend that with more `environment:` configuration or constraints, like: `required_variables:` or `access_level` of user allowed to use that. +3. Following the problem from (1) we can extend `url:` with a `regexp` that will be used to fish a URL from build log. + +## Distinguish between production and review apps + +### Convention over configuration + +We would expect the environments to be of `type/name`: + +1. This would allow us to have a clear distinction between different environment types: `production/gitlab.com`, `staging/dev`, `review-apps/feature/branch`, +2. Since we use a folder structure we could group all environments by `type` and strip that from environment name, +3. We would be aware of some of these types and for example for `review-apps` show them differently in context of Merge Requests, ex. calculating `deployed ago` a little differently. +3. We could easily group all `types` across from group from all projects. + +The `type/name` also plays nice with `Variables` and `Runners`, because we can limit their usage: + +1. We could extend the resources with a field that would allow us to filter for what types it can be used, ex.: `production/*` or `review-apps/*` +2. We could limit runners to be used only by `review-apps/*`, + +## Destroying Review Apps + + +## Examples + +- Use with NGINX +- Use with Amazon S3 +- Use on Heroku with dpl +- Use with OpenShift/kubernetes + +[ce-21971]: https://gitlab.com/gitlab-org/gitlab-ce/issues/21971 +[heroku-apps]: https://devcenter.heroku.com/articles/github-integration-review-apps +[environments]: ../environments.md +[merge requests]: ../../user/project/merge_requests.md +[fourchette]: https://github.com/rainforestapp/fourchette -- cgit v1.2.1 From 0881c3a82a6c4d198856b6a1e976b932b2b0f837 Mon Sep 17 00:00:00 2001 From: Achilleas Pipinellis Date: Thu, 27 Oct 2016 14:18:41 +0200 Subject: Add an intro and an Overview section for Review Apps [ci skip] --- doc/ci/review_apps/index.md | 126 +++++++++++++++++++++++++++++--------------- 1 file changed, 83 insertions(+), 43 deletions(-) diff --git a/doc/ci/review_apps/index.md b/doc/ci/review_apps/index.md index ee279025a9d..ce1fbaaab04 100644 --- a/doc/ci/review_apps/index.md +++ b/doc/ci/review_apps/index.md @@ -1,29 +1,87 @@ -# Review apps +# Getting started with Review Apps > [Introduced][ce-21971] in GitLab 8.12. Further additions were made in GitLab 8.13. +> +Inspired by [Heroku's Review Apps][heroku-apps] which itself was inspired by +[Fourchette]. -Review apps are automatically-created [environments] that run your code for each -branch. That means [merge requests] can be reviewed in a live-running environment. +The base of Review Apps is the [dynamic environments] which allow you to create +a new environment (dynamically) for each one of your branches. -They mostly make sense to be used with web applications. +A Review App can then be visible as a link when you visit the [merge request] +relevant to the branch. That way, you are able to see live all changes introduced +by the merge request changes. Reviewing anything, from performance to interface +changes, becomes much easier with a live environment and as such, Review Apps +can make a huge impact on your development flow. -Review Apps can make a huge impact on your development flow. Reviewing anything -from performance to interface changes becomes much easier with a live environment. +They mostly make sense to be used with web applications, but you can use them +any way you'd like. -> -Inspired by [Heroku's Review Apps][heroku-apps] which itself was inspired by -[Fourchette]. +## Overview + +Simply put, a Review App is a mapping of +a branch with an environment as there is a 1:1 relation between them. + +To get a better understanding of Review Apps, you must first learn how +environments and deployments work. The following docs will help you grasp that +knowledge: + +1. First, learn about [environments][] and their role in the development workflow. +1. Then make a small stop to learn about [CI variables][variables] and how they + can be used in your CI jobs. +1. Next, explore the [`environment` syntax][yaml-env] as defined in `.gitlab-ci.yml`. + This will be your primary reference when you are finally comfortable with + how environments work. +1. Additionally, find out about [manual actions][] and how you can use them to + deploy to critical environments like production with the push of a button. +1. And as a last step, follow the [NGINX example tutorial][app-nginx] which will + guide you step by step to set up the infrastructure and make use of + Review Apps with a simple HTML site and NGINX. + +## Configuration + +For configuration examples see the [dynamic environments] documentation. + +## Creating and destroying Review Apps + +The creation and destruction of a Review App is defined in `.gitlab-ci.yml`. +Check the [environments] documentation how to do so. + +## A simple workflow + +The process of adding Review Apps in your workflow would look like: + +1. Set up the infrastructure to host and deploy the Review Apps. +1. Install and configure a Runner that does the deployment. +1. Set up a job in `.gitlab-ci.yml` that uses the predefined + [predefined CI environment variable][variables] `${CI_BUILD_REF_NAME}` to + create dynamic environments and restrict it to run only on branches. +1. Optionally set a job that stops the Review Apps. + +From there on, you would follow the branched Git flow: + +1. Push a branch and let the Runner deploy the Review App based on the `script` + definition of the dynamic environment job. +1. Wait for the Runner to build and/or deploy your web app. +1. Click on the link that's present in the MR related to the branch and see the + changes live. + +### Limitations + +We are limited to use only [CI predefined variables][variables]. -## Dynamic environments +## Examples + +A list of examples used with Review Apps can be found below: -You can now use predefined CI variables as a name for environments. In addition, -you can specify a URL for the environment configuration in your .gitlab-ci.yml -file. +- [Use with NGINX][app-nginx] - Use NGINX and the shell executor of GitLab Runner + to deploy a simple HTML website. -- Mapping branch with environment +And below is a soon to be added examples list: -This issue describes dynamic environments implementation, mostly to be used with dynamically create applications. -These application will be mostly used for Review Apps. +- Use with Amazon S3 +- Use on Heroku with dpl +- Use with OpenShift/kubernetes ## Assumptions @@ -34,23 +92,10 @@ These application will be mostly used for Review Apps. 1. We need some form to distinguish between production/staging and review app environment, 1. We don't try to manage life cycle of deployments in the first iteration, possibly we will extend a Pipeline to add jobs that will be responsible either for cleaning up or removing old deployments and closing environments. -## Configuration - -``` -review_apps: - environment: - name: review/$CI_BUILD_REF_NAME - url: http://$CI_BUILD_REF_NAME.review.gitlab.com/ -``` - -### Remarks - -1. We are limited to use only CI predefined variables, since this is not easy task to pass the URL from build, -2. I do prefer nested `url:` since this allows us in the future to extend that with more `environment:` configuration or constraints, like: `required_variables:` or `access_level` of user allowed to use that. -3. Following the problem from (1) we can extend `url:` with a `regexp` that will be used to fish a URL from build log. - ## Distinguish between production and review apps +- Are dynamic environments distinguishable by the slash in `environment:url`? + ### Convention over configuration We would expect the environments to be of `type/name`: @@ -65,18 +110,13 @@ The `type/name` also plays nice with `Variables` and `Runners`, because we can l 1. We could extend the resources with a field that would allow us to filter for what types it can be used, ex.: `production/*` or `review-apps/*` 2. We could limit runners to be used only by `review-apps/*`, -## Destroying Review Apps - - -## Examples - -- Use with NGINX -- Use with Amazon S3 -- Use on Heroku with dpl -- Use with OpenShift/kubernetes - +[app-nginx]: nginx_guide.md [ce-21971]: https://gitlab.com/gitlab-org/gitlab-ce/issues/21971 -[heroku-apps]: https://devcenter.heroku.com/articles/github-integration-review-apps +[dynamic environments]: ../environments.md#dynamic-environments [environments]: ../environments.md -[merge requests]: ../../user/project/merge_requests.md [fourchette]: https://github.com/rainforestapp/fourchette +[heroku-apps]: https://devcenter.heroku.com/articles/github-integration-review-apps +[manual actions]: ../environments.md#manual-actions +[merge request]: ../../user/project/merge_requests.md +[variables]: ../variables/README.md +[yaml-env]: ../yaml/README.md#environment -- cgit v1.2.1 From 17cf2adf0438f306f0d47e0178f0718a46471fb2 Mon Sep 17 00:00:00 2001 From: Achilleas Pipinellis Date: Thu, 27 Oct 2016 14:19:43 +0200 Subject: Add note about current limitation in $CI_BUILD_REF_NAME [ci skip] --- doc/ci/yaml/README.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/doc/ci/yaml/README.md b/doc/ci/yaml/README.md index 1e096444903..b73cc41f95d 100644 --- a/doc/ci/yaml/README.md +++ b/doc/ci/yaml/README.md @@ -687,6 +687,13 @@ The `stop_review_app` job is **required** to have the following keywords defined These parameters can use any of the defined [CI variables](#variables) (including predefined, secure variables and `.gitlab-ci.yml` variables). +>**Note:** +Be aware than if the branch name contains special characters and you use the +`$CI_BUILD_REF_NAME` variable to dynamically create environments, there might +be complications during deployment. Follow the +[issue 22849](https://gitlab.com/gitlab-org/gitlab-ce/issues/22849) for more +information. + For example: ``` -- cgit v1.2.1 From 46a7e681e0dad51b7465f056d18e187310049694 Mon Sep 17 00:00:00 2001 From: Achilleas Pipinellis Date: Thu, 27 Oct 2016 15:31:06 +0200 Subject: Get rid most of the irrelevant sections [ci skip] --- .../review_apps/img/review_apps_preview_in_mr.png | Bin 0 -> 28689 bytes doc/ci/review_apps/index.md | 59 ++++++++++----------- 2 files changed, 27 insertions(+), 32 deletions(-) create mode 100644 doc/ci/review_apps/img/review_apps_preview_in_mr.png diff --git a/doc/ci/review_apps/img/review_apps_preview_in_mr.png b/doc/ci/review_apps/img/review_apps_preview_in_mr.png new file mode 100644 index 00000000000..15bcb90518c Binary files /dev/null and b/doc/ci/review_apps/img/review_apps_preview_in_mr.png differ diff --git a/doc/ci/review_apps/index.md b/doc/ci/review_apps/index.md index ce1fbaaab04..b4abb8c0e5c 100644 --- a/doc/ci/review_apps/index.md +++ b/doc/ci/review_apps/index.md @@ -19,8 +19,19 @@ any way you'd like. ## Overview -Simply put, a Review App is a mapping of -a branch with an environment as there is a 1:1 relation between them. +Simply put, a Review App is a mapping of a branch with an environment as there +is a 1:1 relation between them. + +Here's an example of what it looks like when viewing a merge request with a +dynamically set environment. + +![Review App in merge request](img/review_apps_preview_in_mr.png) + +In the image above you can see that the `add-new-line` branch was successfully +built and deployed under a dynamic environment and can be previewed with an +also dynamically URL. + +--- To get a better understanding of Review Apps, you must first learn how environments and deployments work. The following docs will help you grasp that @@ -34,9 +45,9 @@ knowledge: how environments work. 1. Additionally, find out about [manual actions][] and how you can use them to deploy to critical environments like production with the push of a button. -1. And as a last step, follow the [NGINX example tutorial][app-nginx] which will +1. And as a last step, follow the [example tutorials](#examples) which will guide you step by step to set up the infrastructure and make use of - Review Apps with a simple HTML site and NGINX. + Review Apps. ## Configuration @@ -66,7 +77,18 @@ From there on, you would follow the branched Git flow: 1. Click on the link that's present in the MR related to the branch and see the changes live. -### Limitations +## Distinguish between production and review apps + +1. We need some form to distinguish between production/staging and review app environment, + +- Are dynamic environments distinguishable by the slash in `environment:url`? + +We would expect the environments to be of `type/name`: + +1. This would allow us to have a clear distinction between different environment types: `production/gitlab.com`, `staging/dev`, `review-apps/feature/branch`, +3. We would be aware of some of these types and for example for `review-apps` show them differently in context of Merge Requests, ex. calculating `deployed ago` a little differently. + +## Limitations We are limited to use only [CI predefined variables][variables]. @@ -83,33 +105,6 @@ And below is a soon to be added examples list: - Use on Heroku with dpl - Use with OpenShift/kubernetes -## Assumptions - -1. We will allow to create dynamic environments from `.gitlab-ci.yml`, by allowing to specify environment variables: `review_apps_${CI_BUILD_REF_NAME}`, -1. We will use multiple deployments of the same application per environment, -1. The URL will be assigned to environment on the creation, and updated later if necessary, -1. The URL will be specified in `.gitlab-ci.yml`, possibly introducing regexp for getting an URL from build log if required, -1. We need some form to distinguish between production/staging and review app environment, -1. We don't try to manage life cycle of deployments in the first iteration, possibly we will extend a Pipeline to add jobs that will be responsible either for cleaning up or removing old deployments and closing environments. - -## Distinguish between production and review apps - -- Are dynamic environments distinguishable by the slash in `environment:url`? - -### Convention over configuration - -We would expect the environments to be of `type/name`: - -1. This would allow us to have a clear distinction between different environment types: `production/gitlab.com`, `staging/dev`, `review-apps/feature/branch`, -2. Since we use a folder structure we could group all environments by `type` and strip that from environment name, -3. We would be aware of some of these types and for example for `review-apps` show them differently in context of Merge Requests, ex. calculating `deployed ago` a little differently. -3. We could easily group all `types` across from group from all projects. - -The `type/name` also plays nice with `Variables` and `Runners`, because we can limit their usage: - -1. We could extend the resources with a field that would allow us to filter for what types it can be used, ex.: `production/*` or `review-apps/*` -2. We could limit runners to be used only by `review-apps/*`, - [app-nginx]: nginx_guide.md [ce-21971]: https://gitlab.com/gitlab-org/gitlab-ce/issues/21971 [dynamic environments]: ../environments.md#dynamic-environments -- cgit v1.2.1 From a81546de3c512bb4b480582522dcc6121c0136b3 Mon Sep 17 00:00:00 2001 From: Achilleas Pipinellis Date: Thu, 27 Oct 2016 16:03:56 +0200 Subject: Link to NGINX example project for the time being [ci skip] --- doc/ci/review_apps/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/ci/review_apps/index.md b/doc/ci/review_apps/index.md index b4abb8c0e5c..2fe41197206 100644 --- a/doc/ci/review_apps/index.md +++ b/doc/ci/review_apps/index.md @@ -105,7 +105,7 @@ And below is a soon to be added examples list: - Use on Heroku with dpl - Use with OpenShift/kubernetes -[app-nginx]: nginx_guide.md +[app-nginx]: https://gitlab.com/gitlab-examples/review-apps-nginx [ce-21971]: https://gitlab.com/gitlab-org/gitlab-ce/issues/21971 [dynamic environments]: ../environments.md#dynamic-environments [environments]: ../environments.md -- cgit v1.2.1 From 35c45f7eac2392db136275a8352d3c6c68176a92 Mon Sep 17 00:00:00 2001 From: Achilleas Pipinellis Date: Thu, 17 Nov 2016 12:50:05 +0100 Subject: Add a prerequisites section, add some links --- doc/ci/review_apps/index.md | 27 ++++++++++++++++++++++----- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/doc/ci/review_apps/index.md b/doc/ci/review_apps/index.md index 2fe41197206..aea6a8c2ea0 100644 --- a/doc/ci/review_apps/index.md +++ b/doc/ci/review_apps/index.md @@ -31,7 +31,16 @@ In the image above you can see that the `add-new-line` branch was successfully built and deployed under a dynamic environment and can be previewed with an also dynamically URL. ---- +The details of the Review Apps implementation depend widely on your real +technology stack and on your deployment process. The simplest case it to +deploy a simple static HTML website, but it will not be that straightforward +when your app is using a database for example. To make a branch be deployed +on a temporary instance and booting up this instance with all required software +and services automatically on the fly is not a trivial task. However, it is +doable, especially if you use Docker, or at least a configuration management +tool like Chef, Puppet, Ansible or Salt. + +## Prerequisites To get a better understanding of Review Apps, you must first learn how environments and deployments work. The following docs will help you grasp that @@ -51,11 +60,15 @@ knowledge: ## Configuration -For configuration examples see the [dynamic environments] documentation. +The configuration of Review apps depends on your technology stack and your +infrastructure. Read the [dynamic environments] documentation to understand +how to define and create them. ## Creating and destroying Review Apps -The creation and destruction of a Review App is defined in `.gitlab-ci.yml`. +The creation and destruction of a Review App is defined in `.gitlab-ci.yml` +at a job level under the `environment` keyword. + Check the [environments] documentation how to do so. ## A simple workflow @@ -63,11 +76,12 @@ Check the [environments] documentation how to do so. The process of adding Review Apps in your workflow would look like: 1. Set up the infrastructure to host and deploy the Review Apps. -1. Install and configure a Runner that does the deployment. +1. [Install][install-runner] and [configure][conf-runner] a Runner that does + the deployment. 1. Set up a job in `.gitlab-ci.yml` that uses the predefined [predefined CI environment variable][variables] `${CI_BUILD_REF_NAME}` to create dynamic environments and restrict it to run only on branches. -1. Optionally set a job that stops the Review Apps. +1. Optionally set a job that [manually stops][manual-env] the Review Apps. From there on, you would follow the branched Git flow: @@ -115,3 +129,6 @@ And below is a soon to be added examples list: [merge request]: ../../user/project/merge_requests.md [variables]: ../variables/README.md [yaml-env]: ../yaml/README.md#environment +[install-runner]: https://docs.gitlab.com/runner/install/ +[conf-runner]: https://docs.gitlab.com/runner/commands/ +[manual-env]: ../environments.md#stopping-an-environment -- cgit v1.2.1 From 11c5fcb43ce55c9b96428f440f19d459ae09166b Mon Sep 17 00:00:00 2001 From: Achilleas Pipinellis Date: Thu, 17 Nov 2016 12:50:40 +0100 Subject: Fix URL to review apps docs --- doc/ci/environments.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/ci/environments.md b/doc/ci/environments.md index cc65f0ad8ad..1d663060241 100644 --- a/doc/ci/environments.md +++ b/doc/ci/environments.md @@ -485,7 +485,7 @@ Below are some links you may find interesting: - [The `.gitlab-ci.yml` definition of environments](yaml/README.md#environment) - [A blog post on Deployments & Environments](https://about.gitlab.com/2016/08/26/ci-deployment-and-environments/) -- [Review Apps](review_apps.md) Expand dynamic environments to deploy your code for every branch +- [Review Apps - Use dynamic environments to deploy your code for every branch](review_apps/index.md) [Pipelines]: pipelines.md [jobs]: yaml/README.md#jobs -- cgit v1.2.1 From ffadc93a124eb6ba03b8f21e259cd6292191ea48 Mon Sep 17 00:00:00 2001 From: Achilleas Pipinellis Date: Thu, 17 Nov 2016 12:50:57 +0100 Subject: Add link to environments docs --- doc/ci/yaml/README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/doc/ci/yaml/README.md b/doc/ci/yaml/README.md index b73cc41f95d..6fee750c709 100644 --- a/doc/ci/yaml/README.md +++ b/doc/ci/yaml/README.md @@ -541,6 +541,8 @@ same manual action multiple times. An example usage of manual actions is deployment to production. +Read more at the [environments documentation][env-manual]. + ### environment > Introduced in GitLab 8.9. @@ -1223,6 +1225,7 @@ capitalization, the commit will be created but the builds will be skipped. Visit the [examples README][examples] to see a list of examples using GitLab CI with various languages. +[env-manual]: ../environments.md#manually-deploying-to-environments [examples]: ../examples/README.md [ce-6323]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/6323 [environment]: ../environments.md -- cgit v1.2.1 From 184c1489d88ac7d9a5d6c34c6bfb5b973f120870 Mon Sep 17 00:00:00 2001 From: Achilleas Pipinellis Date: Thu, 17 Nov 2016 12:57:08 +0100 Subject: Add Limitations sections to environments and review apps docs [ci skip] --- doc/ci/environments.md | 9 +++++++++ doc/ci/review_apps/index.md | 13 +------------ 2 files changed, 10 insertions(+), 12 deletions(-) diff --git a/doc/ci/environments.md b/doc/ci/environments.md index 1d663060241..aed17325b3a 100644 --- a/doc/ci/environments.md +++ b/doc/ci/environments.md @@ -479,6 +479,15 @@ fetch line: fetch = +refs/environments/*:refs/remotes/origin/environments/* ``` +## Limitations + +- You are limited to use only the [CI predefined variables][variables] in the + `environment: name`. Any variables defined inside `script` will not work. +- If the branch name contains special characters and you use the + `$CI_BUILD_REF_NAME` variable to dynamically create environments, there might + be complications during deployment. Follow the [issue 22849][ce-22849] for + more information. + ## Further reading Below are some links you may find interesting: diff --git a/doc/ci/review_apps/index.md b/doc/ci/review_apps/index.md index aea6a8c2ea0..42b5ec740d9 100644 --- a/doc/ci/review_apps/index.md +++ b/doc/ci/review_apps/index.md @@ -91,20 +91,9 @@ From there on, you would follow the branched Git flow: 1. Click on the link that's present in the MR related to the branch and see the changes live. -## Distinguish between production and review apps - -1. We need some form to distinguish between production/staging and review app environment, - -- Are dynamic environments distinguishable by the slash in `environment:url`? - -We would expect the environments to be of `type/name`: - -1. This would allow us to have a clear distinction between different environment types: `production/gitlab.com`, `staging/dev`, `review-apps/feature/branch`, -3. We would be aware of some of these types and for example for `review-apps` show them differently in context of Merge Requests, ex. calculating `deployed ago` a little differently. - ## Limitations -We are limited to use only [CI predefined variables][variables]. +Check the [environments limitations](../environments.md#limitations). ## Examples -- cgit v1.2.1 From 642e668c295e6758bf3498daab06414f498607a0 Mon Sep 17 00:00:00 2001 From: Achilleas Pipinellis Date: Thu, 17 Nov 2016 13:00:37 +0100 Subject: Add 8.14 to versions with further additions to review apps [ci skip] --- doc/ci/review_apps/index.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/doc/ci/review_apps/index.md b/doc/ci/review_apps/index.md index 42b5ec740d9..b41ae130bc2 100644 --- a/doc/ci/review_apps/index.md +++ b/doc/ci/review_apps/index.md @@ -1,9 +1,10 @@ # Getting started with Review Apps -> [Introduced][ce-21971] in GitLab 8.12. Further additions were made in GitLab 8.13. > -Inspired by [Heroku's Review Apps][heroku-apps] which itself was inspired by -[Fourchette]. +- [Introduced][ce-21971] in GitLab 8.12. Further additions were made in GitLab + 8.13 and 8.14. +- Inspired by [Heroku's Review Apps][heroku-apps] which itself was inspired by + [Fourchette]. The base of Review Apps is the [dynamic environments] which allow you to create a new environment (dynamically) for each one of your branches. -- cgit v1.2.1 From 5fa7169618e2be9956482e2feedaff651f080761 Mon Sep 17 00:00:00 2001 From: Achilleas Pipinellis Date: Thu, 17 Nov 2016 13:03:29 +0100 Subject: Remove ToC since it's now supported in the docs portal itself [ci skip] --- doc/administration/high_availability/redis.md | 32 ---------------------- .../high_availability/redis_source.md | 21 -------------- 2 files changed, 53 deletions(-) diff --git a/doc/administration/high_availability/redis.md b/doc/administration/high_availability/redis.md index 1fa3560e932..f532a106bc6 100644 --- a/doc/administration/high_availability/redis.md +++ b/doc/administration/high_availability/redis.md @@ -30,38 +30,6 @@ Omnibus GitLab packages. [Available configuration setups](#available-configuration-setups) section below. - - -**Table of Contents** - -- [Overview](#overview) - - [High Availability with Sentinel](#high-availability-with-sentinel) - - [Recommended setup](#recommended-setup) - - [Redis setup overview](#redis-setup-overview) - - [Sentinel setup overview](#sentinel-setup-overview) - - [Available configuration setups](#available-configuration-setups) -- [Configuring Redis HA](#configuring-redis-ha) - - [Prerequisites](#prerequisites) - - [Step 1. Configuring the master Redis instance](#step-1-configuring-the-master-redis-instance) - - [Step 2. Configuring the slave Redis instances](#step-2-configuring-the-slave-redis-instances) - - [Step 3. Configuring the Redis Sentinel instances](#step-3-configuring-the-redis-sentinel-instances) - - [Step 4. Configuring the GitLab application](#step-4-configuring-the-gitlab-application) -- [Switching from an existing single-machine installation to Redis HA](#switching-from-an-existing-single-machine-installation-to-redis-ha) -- [Example of a minimal configuration with 1 master, 2 slaves and 3 Sentinels](#example-of-a-minimal-configuration-with-1-master-2-slaves-and-3-sentinels) - - [Example configuration for Redis master and Sentinel 1](#example-configuration-for-redis-master-and-sentinel-1) - - [Example configuration for Redis slave 1 and Sentinel 2](#example-configuration-for-redis-slave-1-and-sentinel-2) - - [Example configuration for Redis slave 2 and Sentinel 3](#example-configuration-for-redis-slave-2-and-sentinel-3) - - [Example configuration for the GitLab application](#example-configuration-for-the-gitlab-application) -- [Advanced configuration](#advanced-configuration) - - [Control running services](#control-running-services) -- [Troubleshooting](#troubleshooting) - - [Troubleshooting Redis replication](#troubleshooting-redis-replication) - - [Troubleshooting Sentinel](#troubleshooting-sentinel) -- [Changelog](#changelog) -- [Further reading](#further-reading) - - - ## Overview Before diving into the details of setting up Redis and Redis Sentinel for HA, diff --git a/doc/administration/high_availability/redis_source.md b/doc/administration/high_availability/redis_source.md index 8558ba82d63..3629772b8af 100644 --- a/doc/administration/high_availability/redis_source.md +++ b/doc/administration/high_availability/redis_source.md @@ -17,27 +17,6 @@ If you're not sure whether this guide is for you, please refer to [Available configuration setups](redis.md#available-configuration-setups) in the Omnibus Redis HA documentation. ---- - - - -**Table of Contents** - -- [Configuring your own Redis server](#configuring-your-own-redis-server) - - [Prerequisites](#prerequisites) - - [Step 1. Configuring the master Redis instance](#step-1-configuring-the-master-redis-instance) - - [Step 2. Configuring the slave Redis instances](#step-2-configuring-the-slave-redis-instances) - - [Step 3. Configuring the Redis Sentinel instances](#step-3-configuring-the-redis-sentinel-instances) - - [Step 4. Configuring the GitLab application](#step-4-configuring-the-gitlab-application) -- [Example of minimal configuration with 1 master, 2 slaves and 3 Sentinels](#example-of-minimal-configuration-with-1-master-2-slaves-and-3-sentinels) - - [Example configuration for Redis master and Sentinel 1](#example-configuration-for-redis-master-and-sentinel-1) - - [Example configuration for Redis slave 1 and Sentinel 2](#example-configuration-for-redis-slave-1-and-sentinel-2) - - [Example configuration for Redis slave 2 and Sentinel 3](#example-configuration-for-redis-slave-2-and-sentinel-3) - - [Example configuration of the GitLab application](#example-configuration-of-the-gitlab-application) -- [Troubleshooting](#troubleshooting) - - - ## Configuring your own Redis server This is the section where we install and setup the new Redis instances. -- cgit v1.2.1 From f0ed5fea81b537ae6c0262ed8f6249b47acafcdf Mon Sep 17 00:00:00 2001 From: tiagonbotelho Date: Wed, 16 Nov 2016 18:20:05 +0000 Subject: adds fix for security issue when annonymous user does not have access to repository we now display the activity feed instead of the readme --- app/helpers/preferences_helper.rb | 6 +-- app/views/projects/_empty.html.haml | 58 --------------------- app/views/projects/empty.html.haml | 60 +++++++++++++++++++++- .../23990-project-show-error-when-empty-repo.yml | 2 +- spec/helpers/preferences_helper_spec.rb | 36 ++++++++++--- 5 files changed, 92 insertions(+), 70 deletions(-) delete mode 100644 app/views/projects/_empty.html.haml diff --git a/app/helpers/preferences_helper.rb b/app/helpers/preferences_helper.rb index f7189e0c5a1..6e68aad4cb7 100644 --- a/app/helpers/preferences_helper.rb +++ b/app/helpers/preferences_helper.rb @@ -50,7 +50,7 @@ module PreferencesHelper end def default_project_view - return annonymous_project_view unless current_user + return anonymous_project_view unless current_user user_view = current_user.project_view @@ -67,7 +67,7 @@ module PreferencesHelper end end - def annonymous_project_view - @project.empty_repo? ? 'empty' : 'readme' + def anonymous_project_view + @project.empty_repo? || !can?(current_user, :download_code, @project) ? 'activity' : 'readme' end end diff --git a/app/views/projects/_empty.html.haml b/app/views/projects/_empty.html.haml deleted file mode 100644 index 56276e164de..00000000000 --- a/app/views/projects/_empty.html.haml +++ /dev/null @@ -1,58 +0,0 @@ -.row-content-block.second-block.center - %h3.page-title - The repository for this project is empty - - if can?(current_user, :push_code, @project) - %p - If you already have files you can push them using command line instructions below. - %p - Otherwise you can start with adding a - = succeed ',' do - = link_to "README", new_readme_path, class: 'underlined-link' - a - = succeed ',' do - = link_to "LICENSE", add_special_file_path(@project, file_name: 'LICENSE'), class: 'underlined-link' - or a - = link_to '.gitignore', add_special_file_path(@project, file_name: '.gitignore'), class: 'underlined-link' - to this project. - %p - You will need to be owner or have the master permission level for the initial push, as the master branch is automatically protected. - -- if can?(current_user, :push_code, @project) - %div{ class: container_class } - .prepend-top-20 - .empty_wrapper - %h3.page-title-empty - Command line instructions - %div.git-empty - %fieldset - %h5 Git global setup - %pre.light-well - :preserve - git config --global user.name "#{h git_user_name}" - git config --global user.email "#{h git_user_email}" - - %fieldset - %h5 Create a new repository - %pre.light-well - :preserve - git clone #{ content_tag(:span, default_url_to_repo, class: 'clone')} - cd #{h @project.path} - touch README.md - git add README.md - git commit -m "add README" - git push -u origin master - - %fieldset - %h5 Existing folder or Git repository - %pre.light-well - :preserve - cd existing_folder - git init - git remote add origin #{ content_tag(:span, default_url_to_repo, class: 'clone')} - git add . - git commit - git push -u origin master - - - if can? current_user, :remove_project, @project - .prepend-top-20 - = link_to 'Remove project', [@project.namespace.becomes(Namespace), @project], data: { confirm: remove_project_message(@project)}, method: :delete, class: "btn btn-remove pull-right" diff --git a/app/views/projects/empty.html.haml b/app/views/projects/empty.html.haml index 94895699453..7a39064adc5 100644 --- a/app/views/projects/empty.html.haml +++ b/app/views/projects/empty.html.haml @@ -6,4 +6,62 @@ = render 'shared/no_password' = render "home_panel" -= render "empty" + +.row-content-block.second-block.center + %h3.page-title + The repository for this project is empty + - if can?(current_user, :push_code, @project) + %p + If you already have files you can push them using command line instructions below. + %p + Otherwise you can start with adding a + = succeed ',' do + = link_to "README", new_readme_path, class: 'underlined-link' + a + = succeed ',' do + = link_to "LICENSE", add_special_file_path(@project, file_name: 'LICENSE'), class: 'underlined-link' + or a + = link_to '.gitignore', add_special_file_path(@project, file_name: '.gitignore'), class: 'underlined-link' + to this project. + %p + You will need to be owner or have the master permission level for the initial push, as the master branch is automatically protected. + +- if can?(current_user, :push_code, @project) + %div{ class: container_class } + .prepend-top-20 + .empty_wrapper + %h3.page-title-empty + Command line instructions + %div.git-empty + %fieldset + %h5 Git global setup + %pre.light-well + :preserve + git config --global user.name "#{h git_user_name}" + git config --global user.email "#{h git_user_email}" + + %fieldset + %h5 Create a new repository + %pre.light-well + :preserve + git clone #{ content_tag(:span, default_url_to_repo, class: 'clone')} + cd #{h @project.path} + touch README.md + git add README.md + git commit -m "add README" + git push -u origin master + + %fieldset + %h5 Existing folder or Git repository + %pre.light-well + :preserve + cd existing_folder + git init + git remote add origin #{ content_tag(:span, default_url_to_repo, class: 'clone')} + git add . + git commit + git push -u origin master + + - if can? current_user, :remove_project, @project + .prepend-top-20 + = link_to 'Remove project', [@project.namespace.becomes(Namespace), @project], data: { confirm: remove_project_message(@project)}, method: :delete, class: "btn btn-remove pull-right" diff --git a/changelogs/unreleased/23990-project-show-error-when-empty-repo.yml b/changelogs/unreleased/23990-project-show-error-when-empty-repo.yml index 040737f917c..8d4593d4df7 100644 --- a/changelogs/unreleased/23990-project-show-error-when-empty-repo.yml +++ b/changelogs/unreleased/23990-project-show-error-when-empty-repo.yml @@ -1,4 +1,4 @@ --- -title: 500 error on project show when user is not logged in and project is still empty +title: fixes 500 error on project show when user is not logged in and project is still empty merge_request: 7376 author: diff --git a/spec/helpers/preferences_helper_spec.rb b/spec/helpers/preferences_helper_spec.rb index 02b464f7e07..77841e85223 100644 --- a/spec/helpers/preferences_helper_spec.rb +++ b/spec/helpers/preferences_helper_spec.rb @@ -86,21 +86,43 @@ describe PreferencesHelper do end end - describe 'default_project_view' do + describe '#default_project_view' do context 'user not signed in' do before do - @project = create(:project) + helper.instance_variable_set(:@project, project) stub_user end - it 'returns readme view if repository is not empty' do - expect(helper.default_project_view).to eq('readme') + context 'when repository is empty' do + let(:project) { create(:project_empty_repo, :public) } + + it 'returns activity if user has repository access' do + allow(helper).to receive(:can?).with(nil, :download_code, project).and_return(true) + + expect(helper.default_project_view).to eq('activity') + end + + it 'returns activity if user does not have repository access' do + allow(helper).to receive(:can?).with(nil, :download_code, project).and_return(false) + + expect(helper.default_project_view).to eq('activity') + end end - it 'returns activity if repository is empty' do - expect(@project).to receive(:empty_repo?).and_return(true) + context 'when repository is not empty' do + let(:project) { create(:project, :public) } + + it 'returns readme if user has repository access' do + allow(helper).to receive(:can?).with(nil, :download_code, project).and_return(true) + + expect(helper.default_project_view).to eq('readme') + end + + it 'returns activity if user does not have repository access' do + allow(helper).to receive(:can?).with(nil, :download_code, project).and_return(false) - expect(helper.default_project_view).to eq('empty') + expect(helper.default_project_view).to eq('activity') + end end end end -- cgit v1.2.1 From 9b86a5e19d902771ce13f3ddffe89b804479fcbc Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Thu, 17 Nov 2016 14:02:14 +0100 Subject: Fix code review --- app/controllers/profiles/chat_names_controller.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/profiles/chat_names_controller.rb b/app/controllers/profiles/chat_names_controller.rb index 7f9c8e64cca..3756ef364ea 100644 --- a/app/controllers/profiles/chat_names_controller.rb +++ b/app/controllers/profiles/chat_names_controller.rb @@ -25,7 +25,7 @@ class Profiles::ChatNamesController < Profiles::ApplicationController def deny delete_chat_name_token - flash[:alert] = "Denied authorization of chat nickname #{chat_name_params[:user_name]}" + flash[:notice] = "Denied authorization of chat nickname #{chat_name_params[:user_name]}." redirect_to profile_chat_names_path end -- cgit v1.2.1 From 7a37e5c6ab6f96daae2612ef278899899359e271 Mon Sep 17 00:00:00 2001 From: Achilleas Pipinellis Date: Thu, 17 Nov 2016 14:10:55 +0100 Subject: Mention Git strategy none [ci skip] --- doc/ci/environments.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/doc/ci/environments.md b/doc/ci/environments.md index cc65f0ad8ad..91e6c042e0d 100644 --- a/doc/ci/environments.md +++ b/doc/ci/environments.md @@ -422,6 +422,8 @@ deploy_review: - master stop_review: + variables: + GIT_STRATEGY: none script: - echo "Remove review app" when: manual @@ -430,6 +432,10 @@ stop_review: action: stop ``` +Setting the [`GIT_STRATEGY`][git-strategy] to `none` is necessary on the +`stop_review` job so that the [GitLab Runner] won't try to checkout the code +after the branch is deleted. + >**Note:** Starting with GitLab 8.14, dynamic environments will be stopped automatically when their associated branch is deleted. @@ -445,6 +451,8 @@ You can read more in the [`.gitlab-ci.yml` reference][onstop]. > [Introduced][ce-7015] in GitLab 8.14. As we've seen in the [dynamic environments](#dynamic-environments), you can +prepend their name with a word, then followed by a `/` and finally the branch +name which is automatically defined by the `CI_BUILD_REF_NAME` variable. In short, environments that are named like `type/foo` are presented under a group named `type`. @@ -498,3 +506,5 @@ Below are some links you may find interesting: [only]: yaml/README.md#only-and-except [onstop]: yaml/README.md#environment-on_stop [ce-7015]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/7015 +[gitlab runner]: https://docs.gitlab.com/runner/ +[git-strategy]: yaml/README.md#git-strategy -- cgit v1.2.1 From 1ddfc574ce1753ba783c5c1734efb6105f20f3d7 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Thu, 17 Nov 2016 14:58:03 +0100 Subject: Add chat_name partial --- app/controllers/profiles/chat_names_controller.rb | 2 +- app/views/profiles/chat_names/_chat_name.html.haml | 27 +++++++++++++++++++ app/views/profiles/chat_names/index.html.haml | 31 ++-------------------- 3 files changed, 30 insertions(+), 30 deletions(-) create mode 100644 app/views/profiles/chat_names/_chat_name.html.haml diff --git a/app/controllers/profiles/chat_names_controller.rb b/app/controllers/profiles/chat_names_controller.rb index 3756ef364ea..6a1f468ba5a 100644 --- a/app/controllers/profiles/chat_names_controller.rb +++ b/app/controllers/profiles/chat_names_controller.rb @@ -1,9 +1,9 @@ class Profiles::ChatNamesController < Profiles::ApplicationController - before_action :chat_names, only: [:index] before_action :chat_name_token, only: [:new] before_action :chat_name_params, only: [:new, :create, :deny] def index + @chat_names = current_user.chat_names end def new diff --git a/app/views/profiles/chat_names/_chat_name.html.haml b/app/views/profiles/chat_names/_chat_name.html.haml new file mode 100644 index 00000000000..6b32d377e1a --- /dev/null +++ b/app/views/profiles/chat_names/_chat_name.html.haml @@ -0,0 +1,27 @@ +- service = chat_name.service +- project = service.project +%tr + %td + %strong + - if can?(current_user, :read_project, project) + = link_to project.name_with_namespace, project_path(project) + - else + .light N/A + %td + %strong + - if can?(current_user, :admin_project, project) + = link_to service.title, edit_namespace_project_service_path(project.namespace, project, service) + - else + = service.title + %td + = chat_name.team_domain + %td + = chat_name.chat_name + %td + - if chat_name.last_used_at + time_ago_with_tooltip(chat_name.last_used_at) + - else + Never + + %td + = link_to 'Remove', profile_chat_name_path(chat_name), method: :delete, class: 'btn btn-danger pull-right', data: { confirm: 'Are you sure you want to revoke this nickname?' } diff --git a/app/views/profiles/chat_names/index.html.haml b/app/views/profiles/chat_names/index.html.haml index a8dd258732f..20cc636b2da 100644 --- a/app/views/profiles/chat_names/index.html.haml +++ b/app/views/profiles/chat_names/index.html.haml @@ -9,7 +9,7 @@ You can see your Chat accounts. .col-lg-9 - %h5 Active chat names (#{@chat_names.length}) + %h5 Active chat names (#{@chat_names.size}) - if @chat_names.present? .table-responsive @@ -23,34 +23,7 @@ %th Last used %th %tbody - - @chat_names.each do |chat_name| - - service = chat_name.service - - project = service.project - %tr - %td - %strong - - if can?(current_user, :read_project, project) - = link_to project.name_with_namespace, project_path(project) - - else - .light N/A - %td - %strong - - if can?(current_user, :admin_project, project) - = link_to service.title, edit_namespace_project_service_path(project.namespace, project, service) - - else - = service.title - %td - = chat_name.team_domain - %td - = chat_name.chat_name - %td - - if chat_name.last_used_at - time_ago_with_tooltip(chat_name.last_used_at) - - else - Never - - %td - = link_to 'Remove', profile_chat_name_path(chat_name), method: :delete, class: 'btn btn-danger pull-right', data: { confirm: 'Are you sure you want to revoke this nickname?' } + = render @chat_names - else .settings-message.text-center -- cgit v1.2.1 From d444fd3460e896065c21abda9a1cafa93f9315a5 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Thu, 17 Nov 2016 16:29:16 +0100 Subject: Change last_used_at to use touch --- app/services/chat_names/find_user_service.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/services/chat_names/find_user_service.rb b/app/services/chat_names/find_user_service.rb index 08079fdaf70..4f5c5567b42 100644 --- a/app/services/chat_names/find_user_service.rb +++ b/app/services/chat_names/find_user_service.rb @@ -9,7 +9,7 @@ module ChatNames chat_name = find_chat_name return unless chat_name - chat_name.update(last_used_at: Time.now) + chat_name.touch(:last_used_at) chat_name.user end -- cgit v1.2.1 From b80ed61831f2133fe1a2246fb372c26f7805b93c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Coutable?= Date: Thu, 17 Nov 2016 18:12:10 +0100 Subject: Add missing item for 8.13.6 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ci skip] Signed-off-by: Rémy Coutable --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 73f2703a0a4..5dad96e398a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -79,6 +79,7 @@ entry. ## 8.13.6 (2016-11-17) - Omniauth auto link LDAP user falls back to find by DN when user cannot be found by UID. !7002 +- Fix Milestone dropdown not stay selected for `Upcoming` and `No Milestone` option. !7117 - Fix no "Register" tab if ldap auth is enabled (#24038). !7274 (Luc Didry) - Fix cache for commit status in commits list to respect branches. !7372 - Fix issue causing Labels not to appear in sidebar on MR page. !7416 (Alex Sanford) -- cgit v1.2.1 From 96c1cf2f79ff50925fd8b40eb2e941cfdeeefaac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Coutable?= Date: Thu, 17 Nov 2016 18:27:38 +0100 Subject: Fix wrong changelog item MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ci skip] Signed-off-by: Rémy Coutable --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5dad96e398a..9f41cbc9228 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -80,6 +80,7 @@ entry. - Omniauth auto link LDAP user falls back to find by DN when user cannot be found by UID. !7002 - Fix Milestone dropdown not stay selected for `Upcoming` and `No Milestone` option. !7117 +- Fix relative links in Markdown wiki when displayed in "Project" tab. !7218 - Fix no "Register" tab if ldap auth is enabled (#24038). !7274 (Luc Didry) - Fix cache for commit status in commits list to respect branches. !7372 - Fix issue causing Labels not to appear in sidebar on MR page. !7416 (Alex Sanford) @@ -115,7 +116,6 @@ entry. - Removes any symlinks before importing a project export file. CVE-2016-9086 - Fixed Import/Export foreign key issue to do with project members. -- Fix relative links in Markdown wiki when displayed in "Project" tab !7218 - Changed build dropdown list length to be 6,5 builds long in the pipeline graph ## 8.13.2 (2016-10-31) -- cgit v1.2.1 From 3fa5e73400548f38cbfec1d9e1f6bf22e38214cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Coutable?= Date: Thu, 17 Nov 2016 18:34:45 +0100 Subject: Fix typos MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rémy Coutable --- doc/development/limit_ee_conflicts.md | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/doc/development/limit_ee_conflicts.md b/doc/development/limit_ee_conflicts.md index 29299b0690b..b7e6387838e 100644 --- a/doc/development/limit_ee_conflicts.md +++ b/doc/development/limit_ee_conflicts.md @@ -50,10 +50,10 @@ Notes: #### List or arrays are augmented in EE -In controllers, the most common type of conflicts is either in a `before_action` -that has a list of actions in CE but EE adds some actions to that list. +In controllers, the most common type of conflict is with `before_action` that +has a list of actions in CE but EE adds some actions to that list. -Same problems often occurs for `params.require` / `params.permit` calls. +The same problem often occurs for `params.require` / `params.permit` calls. ##### Mitigations @@ -64,7 +64,7 @@ Separate CE and EE actions/keywords. For instance for `params.require` in def project_params params.require(:project).permit(project_params_ce) # On EE, this is always: - # params.require(:project).permit(project_params_ce + project_params_ee) + # params.require(:project).permit(project_params_ce << project_params_ee) end # Always returns an array of symbols, created however best fits the use case. @@ -138,9 +138,9 @@ at the same place in EE Blocks of code that are EE-specific should be moved to partials as much as possible to avoid conflicts with big chunks of HAML code that that are not fun -to resolve when you add the indentation in the equation. +to resolve when you add the indentation to the equation. -For instance this kind of things: +For instance this kind of thing: ```haml - if can?(current_user, :"admin_#{issuable.to_ability_name}", issuable.project) @@ -250,8 +250,7 @@ and then the `_weight_form.html.haml` could be as follows: Note: -- The safeguards at the top allows to get rid of an unneccessary indentation -level +- The safeguards at the top allow to get rid of an unneccessary indentation level - Here we only moved the 'Weight' code to a partial since this is the only EE-specific code in that view, so it's the most likely to conflict, but you are encouraged to use partials even for code that's in CE to logically split -- cgit v1.2.1