Commit 91d0d91f authored by Rémy Coutable's avatar Rémy Coutable

Merge remote-tracking branch 'origin/master' into ce-to-ee-2017-11-22

Signed-off-by: default avatarRémy Coutable <remy@rymai.me>
parents 423dde58 80e00cb2
......@@ -48,13 +48,32 @@
created() {
eventHub.$on('toggleFolder', this.toggleFolder);
<<<<<<< HEAD
=======
eventHub.$on('toggleDeployBoard', this.toggleDeployBoard);
>>>>>>> origin/master
},
beforeDestroy() {
eventHub.$off('toggleFolder');
<<<<<<< HEAD
},
methods: {
=======
eventHub.$off('toggleDeployBoard');
},
methods: {
/**
* Toggles the visibility of the deploy boards of the clicked environment.
* @param {Object} model
*/
toggleDeployBoard(model) {
this.store.toggleDeployBoard(model.id);
},
>>>>>>> origin/master
toggleFolder(folder) {
this.store.toggleFolder(folder);
......
......@@ -21,7 +21,10 @@ import tabs from '../../vue_shared/components/navigation_tabs.vue';
import container from '../components/container.vue';
export default {
<<<<<<< HEAD
=======
>>>>>>> origin/master
components: {
environmentTable,
container,
......@@ -58,7 +61,10 @@ export default {
}
});
},
<<<<<<< HEAD
=======
>>>>>>> origin/master
/**
* Handles URL and query parameter changes.
* When the user uses the pagination or the tabs,
......
......@@ -44,12 +44,21 @@ export default class EnvironmentsStore {
storeEnvironments(environments = []) {
const filteredEnvironments = environments.map((env) => {
const oldEnvironmentState = this.state.environments
<<<<<<< HEAD
.find((element) => {
if (env.latest) {
return element.id === env.latest.id;
}
return element.id === env.id;
}) || {};
=======
.find((element) => {
if (env.latest) {
return element.id === env.latest.id;
}
return element.id === env.id;
}) || {};
>>>>>>> origin/master
let filtered = {};
......
......@@ -171,7 +171,10 @@
*/
updateContent(parameters) {
this.updateInternalState(parameters);
<<<<<<< HEAD
=======
>>>>>>> origin/master
// fetch new data
return this.service.getPipelines(this.requestData)
.then((response) => {
......
import renderMath from './render_math';
<<<<<<< HEAD
import renderMermaid from './render_mermaid';
=======
>>>>>>> origin/master
// Render Gitlab flavoured Markdown
//
......@@ -8,7 +11,10 @@ import renderMermaid from './render_mermaid';
$.fn.renderGFM = function renderGFM() {
this.find('.js-syntax-highlight').syntaxHighlight();
renderMath(this.find('.js-render-math'));
<<<<<<< HEAD
renderMermaid(this.find('.js-render-mermaid'));
=======
>>>>>>> origin/master
return this;
};
......
......@@ -204,7 +204,12 @@ class ApplicationController < ActionController::Base
end
def check_password_expiration
<<<<<<< HEAD
return if session[:impersonator_id] || !current_user&.allow_password_authentication?
=======
return if session[:impersonator_id]
return unless current_user&.allow_password_authentication?
>>>>>>> origin/master
password_expires_at = current_user&.password_expires_at
......
......@@ -213,6 +213,10 @@ class Namespace < ActiveRecord::Base
parent.present?
end
def root_ancestor
ancestors.reorder(nil).find_by(parent_id: nil)
end
def subgroup?
has_parent?
end
......
......@@ -500,6 +500,14 @@ class Project < ActiveRecord::Base
.base_and_ancestors(upto: top)
end
def root_namespace
if namespace.has_parent?
namespace.root_ancestor
else
namespace
end
end
def lfs_enabled?
return namespace.lfs_enabled? if self[:lfs_enabled].nil?
......
......@@ -20,7 +20,8 @@
= render 'groups/group_admin_settings', f: f
= render 'namespaces/shared_runners_minutes_setting', f: f
- if @group.shared_runner_minutes_supported?
= render 'namespaces/shared_runners_minutes_setting', f: f
- if @group.new_record?
.form-group
......
......@@ -10,3 +10,4 @@
"can-create-deployment" => can?(current_user, :create_deployment, @project).to_s,
"can-read-environment" => can?(current_user, :read_environment, @project).to_s,
"css-class" => container_class } }
---
title: Account shared runner minutes to top-level namespace
merge_request:
author:
type: fixed
---
title: EE Protected Branches API access levels include user_id/group_id where relevant
merge_request: 3535
author:
type: changed
This diff is collapsed.
......@@ -571,8 +571,7 @@ or a failover promotes a different **Master** node.
In `/etc/gitlab/gitlab.rb`:
```ruby
redis_master_role['enable'] = true
redis_sentinel_role['enable'] = true
roles ['redis_sentinel_role', 'redis_master_role']
redis['bind'] = '10.0.0.1'
redis['port'] = 6379
redis['password'] = 'redis-password-goes-here'
......@@ -594,8 +593,7 @@ sentinel['quorum'] = 2
In `/etc/gitlab/gitlab.rb`:
```ruby
redis_slave_role['enable'] = true
redis_sentinel_role['enable'] = true
roles ['redis_sentinel_role', 'redis_slave_role']
redis['bind'] = '10.0.0.2'
redis['port'] = 6379
redis['password'] = 'redis-password-goes-here'
......@@ -617,8 +615,7 @@ sentinel['quorum'] = 2
In `/etc/gitlab/gitlab.rb`:
```ruby
redis_slave_role['enable'] = true
redis_sentinel_role['enable'] = true
roles ['redis_sentinel_role', 'redis_slave_role']
redis['bind'] = '10.0.0.3'
redis['port'] = 6379
redis['password'] = 'redis-password-goes-here'
......
......@@ -36,13 +36,17 @@ Example response:
"push_access_levels": [
{
"access_level": 40,
"user_id": null,
"group_id": null,
"access_level_description": "Masters"
}
],
"merge_access_levels": [
{
"access_level": 40,
"access_level_description": "Masters"
"access_level": null,
"user_id": null,
"group_id": 1234,
"access_level_description": "Example Merge Group"
}
]
},
......@@ -75,13 +79,17 @@ Example response:
"push_access_levels": [
{
"access_level": 40,
"user_id": null,
"group_id": null,
"access_level_description": "Masters"
}
],
"merge_access_levels": [
{
"access_level": 40,
"access_level_description": "Masters"
"access_level": null,
"user_id": null,
"group_id": 1234,
"access_level_description": "Example Merge Group"
}
]
}
......@@ -115,12 +123,16 @@ Example response:
"push_access_levels": [
{
"access_level": 30,
"user_id": null,
"group_id": null,
"access_level_description": "Developers + Masters"
}
],
"merge_access_levels": [
{
"access_level": 30,
"user_id": null,
"group_id": null,
"access_level_description": "Developers + Masters"
}
]
......
......@@ -223,9 +223,6 @@ will not be able to perform all necessary configuration steps. Refer to
match your database replication requirements. Consult the [PostgreSQL - Replication documentation](https://www.postgresql.org/docs/9.6/static/runtime-config-replication.html)
for more information.
1. Check to make sure your firewall rules are set so that the secondary nodes
can access port `5432` on the primary node.
1. Save the file and [reconfigure GitLab][] for the database listen changes to
take effect.
......@@ -319,18 +316,23 @@ primary before the database is replicated.
1. Test that the remote connection to the primary server works.
```
# Certificate and key currently used by GitLab
sudo -u gitlab-psql /opt/gitlab/embedded/bin/psql -h primary.geo.example.com -U gitlab_replicator -d "dbname=gitlabhq_production sslmode=verify-ca" -W
# Certificate and key currently used by GitLab, and connecting by FQDN
sudo -u gitlab-psql /opt/gitlab/embedded/bin/psql -h primary.geo.example.com -U gitlab_replicator -d "dbname=gitlabhq_production sslmode=verify-full" -W
# Self-signed certificate and key
sudo -u gitlab-psql /opt/gitlab/embedded/bin/psql -h 1.2.3.4 -U gitlab_replicator -d "dbname=gitlabhq_production sslmode=verify-full" -W
# Self-signed certificate and key, or connecting by IP address
sudo -u gitlab-psql /opt/gitlab/embedded/bin/psql -h 1.2.3.4 -U gitlab_replicator -d "dbname=gitlabhq_production sslmode=verify-ca" -W
```
When prompted enter the password you set in the first step for the
`gitlab_replicator` user. If all worked correctly, you should see the
database prompt.
A failure to connect here indicates that the TLS or networking configuration
is incorrect. Ensure that you've used the correct certificates and IP
addresses / FQDNs throughout. If you have a firewall, ensure that the
secondary is permitted to access the primary on port 5432.
1. Exit the PostgreSQL console:
```
......@@ -391,10 +393,10 @@ data before running `pg_basebackup`.
1. Execute the command below to start a backup/restore and begin the replication:
```
# Certificate and key currently used by GitLab
# Certificate and key currently used by GitLab, and connecting by FQDN
gitlab-ctl replicate-geo-database --host=primary.geo.example.com --slot-name=secondary_example
# Self-signed certificate and key
# Self-signed certificate and key, or connecting by IP
gitlab-ctl replicate-geo-database --host=1.2.3.4 --slot-name=secondary_example --sslmode=verify-ca
```
......
......@@ -270,22 +270,24 @@ primary before the database is replicated.
1. Test that the remote connection to the primary server works:
If you're using a CA-issued certificate and connecting by FQDN:
```
# Certificate and key currently used by GitLab, and connecting by FQDN
sudo -u postgres psql -h primary.geo.example.com -U gitlab_replicator -d "dbname=gitlabhq_production sslmode=verify-ca" -W
```
If you're using a self-signed certificate or connecting by IP address:
```
sudo -u postgres psql -h 1.2.3.4 -U gitlab_replicator -d "dbname=gitlabhq_production sslmode=verify-full" -W
# Self-signed certificate and key, or connecting by IP address
sudo -u postgres psql -h 1.2.3.4 -U gitlab_replicator -d "dbname=gitlabhq_production sslmode=verify-ca" -W
```
When prompted enter the password you set in the first step for the
`gitlab_replicator` user. If all worked correctly, you should see the
database prompt.
A failure to connect here indicates that the TLS or networking configuration
is incorrect. Ensure that you've used the correct certificates and IP
addresses / FQDNs throughout. If you have a firewall, ensure that the
secondary is permitted to access the primary on port 5432.
1. Exit the PostgreSQL console:
```
......
class Groups::PipelineQuotaController < Groups::ApplicationController
before_action :authorize_admin_group!
before_action :validate_shared_runner_minutes_support!
layout 'group_settings'
def index
@projects = @group.projects.with_shared_runners_limit_enabled.page(params[:page])
@projects = all_projects.with_shared_runners_limit_enabled.page(params[:page])
end
private
def all_projects
if Feature.enabled?(:shared_runner_minutes_on_root_namespace)
@group.all_projects
else
@group.projects
end
end
def validate_shared_runner_minutes_support!
render_404 unless @group.shared_runner_minutes_supported?
end
end
......@@ -34,6 +34,7 @@ module EE
to: :namespace_statistics, allow_nil: true
validate :validate_plan_name
validate :validate_shared_runner_minutes_support
end
module ClassMethods
......@@ -42,10 +43,6 @@ module EE
end
end
def root_ancestor
ancestors.reorder(nil).find_by(parent_id: nil)
end
def move_dir
raise NotImplementedError unless defined?(super)
......@@ -96,13 +93,22 @@ module EE
actual_plan&.name || FREE_PLAN
end
def shared_runner_minutes_supported?
if has_parent?
!Feature.enabled?(:shared_runner_minutes_on_root_namespace)
else
true
end
end
def actual_shared_runners_minutes_limit
shared_runners_minutes_limit ||
current_application_settings.shared_runners_minutes
end
def shared_runners_minutes_limit_enabled?
shared_runners_enabled? &&
shared_runner_minutes_supported? &&
shared_runners_enabled? &&
actual_shared_runners_minutes_limit.nonzero?
end
......@@ -111,6 +117,14 @@ module EE
shared_runners_minutes.to_i >= actual_shared_runners_minutes_limit
end
def shared_runners_enabled?
if Feature.enabled?(:shared_runner_minutes_on_root_namespace)
all_projects.with_shared_runners.any?
else
projects.with_shared_runners.any?
end
end
# These helper methods are required to not break the Namespace API.
def plan=(plan_name)
if plan_name.is_a?(String)
......@@ -140,6 +154,14 @@ module EE
end
end
def validate_shared_runner_minutes_support
return if shared_runner_minutes_supported?
if shared_runners_minutes_limit_changed?
errors.add(:shared_runners_minutes_limit, 'is not supported for this namespace')
end
end
def load_feature_available(feature)
globally_available = License.feature_available?(feature)
......
......@@ -54,7 +54,7 @@ module EE
to: :statistics, allow_nil: true
delegate :actual_shared_runners_minutes_limit,
:shared_runners_minutes_used?, to: :namespace
:shared_runners_minutes_used?, to: :shared_runners_limit_namespace
validates :repository_size_limit,
numericality: { only_integer: true, greater_than_or_equal_to: 0, allow_nil: true }
......@@ -82,6 +82,14 @@ module EE
end
end
def shared_runners_limit_namespace
if Feature.enabled?(:shared_runner_minutes_on_root_namespace)
root_namespace
else
namespace
end
end
def mirror
super && feature_available?(:repository_mirrors)
end
......@@ -190,11 +198,12 @@ module EE
end
def shared_runners_available?
super && !namespace.shared_runners_minutes_used?
super && !shared_runners_limit_namespace.shared_runners_minutes_used?
end
def shared_runners_minutes_limit_enabled?
!public? && shared_runners_enabled? && namespace.shared_runners_minutes_limit_enabled?
!public? && shared_runners_enabled? &&
shared_runners_limit_namespace.shared_runners_minutes_limit_enabled?
end
def feature_available?(feature, user = nil)
......
......@@ -17,8 +17,7 @@ module EE
end
def builds_check_limit
::Namespace.reorder(nil)
.where('namespaces.id = projects.namespace_id')
all_namespaces
.joins('LEFT JOIN namespace_statistics ON namespace_statistics.namespace_id = namespaces.id')
.where('COALESCE(namespaces.shared_runners_minutes_limit, ?, 0) = 0 OR ' \
'COALESCE(namespace_statistics.shared_runners_seconds, 0) < COALESCE(namespaces.shared_runners_minutes_limit, ?, 0) * 60',
......@@ -26,6 +25,16 @@ module EE
.select('1')
end
def all_namespaces
namespaces = ::Namespace.reorder(nil).where('namespaces.id = projects.namespace_id')
if Feature.enabled?(:shared_runner_minutes_on_root_namespace)
namespaces = ::Gitlab::GroupHierarchy.new(namespaces).roots
end
namespaces
end
def application_shared_runners_minutes
current_application_settings.shared_runners_minutes
end
......
......@@ -18,10 +18,10 @@ class UpdateBuildMinutesService < BaseService
end
def project_statistics
project.statistics || project.create_statistics(namespace: namespace)
project.statistics || project.create_statistics(namespace: project.namespace)
end
def namespace
project.namespace
project.shared_runners_limit_namespace
end
end
......@@ -33,7 +33,7 @@
%td
.avatar-container.s20.hidden-xs
= project_icon(project, alt: '', class: 'avatar project-avatar s20')
%strong= link_to project.name, project
%strong= link_to project.full_name, project
%td
= project.shared_runners_minutes
- if projects.blank?
......
- if project.namespace.shared_runners_minutes_used?
- quota_used = project.namespace.shared_runners_minutes
- quota_limit = project.namespace.actual_shared_runners_minutes_limit
- if project.shared_runners_limit_namespace.shared_runners_minutes_used?
- quota_used = project.shared_runners_limit_namespace.shared_runners_minutes
- quota_limit = project.shared_runners_limit_namespace.actual_shared_runners_minutes_limit
.bs-callout.bs-callout-warning
%p
You have used all your shared Runners pipeline minutes.
......
- project = local_assigns.fetch(:project, nil)
- namespace = local_assigns.fetch(:namespace, project && project.namespace)
- namespace = local_assigns.fetch(:namespace, project && project.shared_runners_limit_namespace)
- scope = (project || namespace).full_path
- has_limit = (project || namespace).shared_runners_minutes_limit_enabled?
- can_see_status = project.nil? || can?(current_user, :create_pipeline, project)
......
......@@ -341,6 +341,12 @@ module API
class ProtectedRefAccess < Grape::Entity
expose :access_level
## EE-only
expose :user_id
expose :group_id
## EE-only
expose :access_level_description do |protected_ref_access|
protected_ref_access.humanize
end
......
......@@ -1246,11 +1246,19 @@ module Gitlab
# Gitaly migration: https://gitlab.com/gitlab-org/gitaly/issues/695
def git_merged_branch_names(branch_names = [])
root_sha = find_branch(root_ref).target
<<<<<<< HEAD
git_arguments =
%W[branch --merged #{root_sha}
--format=%(refname:short)\ %(objectname)] + branch_names
=======
git_arguments =
%W[branch --merged #{root_sha}
--format=%(refname:short)\ %(objectname)] + branch_names
>>>>>>> origin/master
lines = run_git(git_arguments).first.lines
lines.each_with_object([]) do |line, branches|
......
......@@ -33,6 +33,10 @@ module Gitlab
base_and_ancestors(upto: upto).where.not(id: ancestors_base.select(:id))
end
def roots
base_and_ancestors.where(namespaces: { parent_id: nil })
end
# Returns a relation that includes the ancestors_base set of groups
# and all their ancestors (recursively).
#
......
......@@ -95,8 +95,56 @@ feature 'Groups > Pipeline Quota' do
end
page.within('.pipeline-project-metrics') do
expect(page).to have_content(project.name)
expect(page).not_to have_content(other_project.name)
expect(page).to have_content(project.full_name)
expect(page).not_to have_content(other_project.full_name)
end
end
end
context 'with shared_runner_minutes_on_root_namespace disabled' do
before do
stub_feature_flags(shared_runner_minutes_on_root_namespace: false)
end
context 'when accessing group with subgroups' do
let(:group) { create(:group, :with_used_build_minutes_limit) }
let!(:subgroup) { create(:group, parent: group) }
let!(:subproject) { create(:project, namespace: subgroup, shared_runners_enabled: true) }
it 'does not show project of subgroup' do
visit_pipeline_quota_page
expect(page).to have_content(project.full_name)
expect(page).not_to have_content(subproject.full_name)
end
end
end
context 'with shared_runner_minutes_on_root_namespace enabled', :nested_groups do
before do
stub_feature_flags(shared_runner_minutes_on_root_namespace: true)
end
context 'when accessing subgroup' do
let(:root_ancestor) { create(:group) }
let(:group) { create(:group, parent: root_ancestor) }
it 'does not show subproject' do
visit_pipeline_quota_page
expect(page).to have_http_status(:not_found)
end
end
context 'when accesing root group' do
let!(:subgroup) { create(:group, parent: group) }
let!(:subproject) { create(:project, namespace: subgroup, shared_runners_enabled: true) }
it 'does show projects of subgroup' do
visit_pipeline_quota_page
expect(page).to have_content(project.full_name)
expect(page).to have_content(subproject.full_name)
end
end
end
......
......@@ -63,6 +63,34 @@ describe Namespace do
end
end
end
describe '#validate_shared_runner_minutes_support' do
before do
stub_feature_flags(shared_runner_minutes_on_root_namespace: true)
end
context 'when changing :shared_runners_minutes_limit' do
before do
namespace.shared_runners_minutes_limit = 100
end
context 'when group is subgroup' do
set(:root_ancestor) { create(:group) }
let(:namespace) { create(:namespace, parent: root_ancestor) }
it 'is invalid' do
expect(namespace).not_to be_valid
expect(namespace.errors[:shared_runners_minutes_limit]).to include('is not supported for this namespace')
end
end
context 'when group is root' do
it 'is valid' do
expect(namespace).to be_valid
end
end
end
end
end
describe '#move_dir' do
......@@ -295,6 +323,42 @@ describe Namespace do
end
end
describe '#shared_runner_minutes_supported?' do
subject { namespace.shared_runner_minutes_supported? }
context 'when is subgroup' do
before do
namespace.parent = build(:group)
end
context 'when shared_runner_minutes_on_root_namespace is disabled' do
before do
stub_feature_flags(shared_runner_minutes_on_root_namespace: false)
end
it 'returns true' do
is_expected.to eq(true)
end
end
context 'when shared_runner_minutes_on_root_namespace is enabled', :nested_groups do
before do
stub_feature_flags(shared_runner_minutes_on_root_namespace: true)
end
it 'returns false' do
is_expected.to eq(false)
end
end
end
context 'when is root' do
it 'returns true' do
is_expected.to eq(true)
end
end
end
describe '#shared_runners_minutes_limit_enabled?' do
subject { namespace.shared_runners_minutes_limit_enabled? }
......@@ -315,6 +379,15 @@ describe Namespace do
end
it { is_expected.to be_truthy }
context 'when is subgroup', :nested_groups do
before do
stub_feature_flags(shared_runner_minutes_on_root_namespace: true)
namespace.parent = build(:group)
end
it { is_expected.to be_falsey }
end
end
end
......@@ -323,6 +396,49 @@ describe Namespace do
end
end
describe '#shared_runners_enabled?' do
subject { namespace.shared_runners_enabled? }
context 'subgroup with shared runners enabled project' do
let(:subgroup) { create(:group, parent: namespace) }
let!(:subproject) { create(:project, namespace: subgroup, shared_runners_enabled: true) }
context 'when shared_runner_minutes_on_root_namespace is disabled' do
before do
stub_feature_flags(shared_runner_minutes_on_root_namespace: false)
end
it "returns false" do
is_expected.to eq(false)
end
end
context 'when shared_runner_minutes_on_root_namespace is enabled', :nested_groups do
before do
stub_feature_flags(shared_runner_minutes_on_root_namespace: true)
end
it "returns true" do
is_expected.to eq(true)
end
end
end
context 'group with shared runners enabled project' do
let!(:project) { create(:project, namespace: namespace, shared_runners_enabled: true) }
it "returns true" do
is_expected.to eq(true)
end
end
context 'group without projects' do
it "returns false" do
is_expected.to eq(false)
end
end
end
describe '#shared_runners_minutes_used?' do
subject { namespace.shared_runners_minutes_used? }
......@@ -363,19 +479,6 @@ describe Namespace do
end
end
describe '#root_ancestor' do
it 'returns the top most ancestor', :nested_groups do
root_group = create(:group)
nested_group = create(:group, parent: root_group)
deep_nested_group = create(:group, parent: nested_group)
very_deep_nested_group = create(:group, parent: deep_nested_group)
expect(nested_group.root_ancestor).to eq(root_group)
expect(deep_nested_group.root_ancestor).to eq(root_group)
expect(very_deep_nested_group.root_ancestor).to eq(root_group)
end
end
describe '#actual_plan' do
context 'when namespace has a plan associated' do
before do
......
......@@ -8,9 +8,9 @@ describe Project do
it { is_expected.to delegate_method(:shared_runners_seconds).to(:statistics) }
it { is_expected.to delegate_method(:shared_runners_seconds_last_reset).to(:statistics) }
it { is_expected.to delegate_method(:actual_shared_runners_minutes_limit).to(:namespace) }
it { is_expected.to delegate_method(:shared_runners_minutes_limit_enabled?).to(:namespace) }
it { is_expected.to delegate_method(:shared_runners_minutes_used?).to(:namespace) }
it { is_expected.to delegate_method(:actual_shared_runners_minutes_limit).to(:shared_runners_limit_namespace) }
it { is_expected.to delegate_method(:shared_runners_minutes_limit_enabled?).to(:shared_runners_limit_namespace) }
it { is_expected.to delegate_method(:shared_runners_minutes_used?).to(:shared_runners_limit_namespace) }
it { is_expected.to have_one(:mirror_data).class_name('ProjectMirrorData') }
it { is_expected.to have_many(:path_locks) }
......@@ -532,6 +532,34 @@ describe Project do
end
end
describe '#shared_runners_limit_namespace' do
set(:root_ancestor) { create(:group) }
set(:group) { create(:group, parent: root_ancestor) }
let(:project) { create(:project, namespace: group) }
subject { project.shared_runners_limit_namespace }
context 'when shared_runner_minutes_on_root_namespace is disabled' do
before do
stub_feature_flags(shared_runner_minutes_on_root_namespace: false)
end
it 'returns parent namespace' do
is_expected.to eq(group)
end
end
context 'when shared_runner_minutes_on_root_namespace is enabled', :nested_groups do
before do
stub_feature_flags(shared_runner_minutes_on_root_namespace: true)
end
it 'returns root namespace' do
is_expected.to eq(root_ancestor)
end
end
end
describe '#shared_runners_minutes_limit_enabled?' do
let(:project) { create(:project) }
......
......@@ -62,6 +62,66 @@ module Ci
end
end
end
context 'when group is subgroup' do
let!(:root_ancestor) { create(:group) }
let!(:group) { create(:group, parent: root_ancestor) }
let!(:project) { create :project, shared_runners_enabled: true, group: group }
let(:build) { execute(shared_runner) }
context 'when shared_runner_minutes_on_root_namespace is disabled' do
before do
stub_feature_flags(shared_runner_minutes_on_root_namespace: false)
end
it "does return a build" do
expect(build).not_to be_nil
end
context 'when we are over limit on subnamespace' do
before do
group.create_namespace_statistics(
shared_runners_seconds: 6001)
end
it "does not return a build" do
expect(build).to be_nil
end
end
end
context 'when shared_runner_minutes_on_root_namespace is enabled', :nested_groups do
before do
stub_feature_flags(shared_runner_minutes_on_root_namespace: true)
end
it "does return a build" do
expect(build).not_to be_nil
end
context 'when we are over limit on subnamespace' do
before do
group.create_namespace_statistics(
shared_runners_seconds: 6001)
end
it "limit is ignored and build is returned" do
expect(build).not_to be_nil
end
end
context 'when we are over limit on root namespace' do
before do
root_ancestor.create_namespace_statistics(
shared_runners_seconds: 6001)
end
it "does not return a build" do
expect(build).to be_nil
end
end
end
end
end
def execute(runner)
......
require 'spec_helper'
describe 'admin/groups/_form' do
set(:admin) { create(:admin) }
before do
assign(:group, group)
allow(view).to receive(:can?) { true }
allow(view).to receive(:current_user) { admin }
allow(view).to receive(:visibility_level) { group.visibility_level }
end
describe 'when :shared_runner_minutes_on_root_namespace is disabled' do
before do
stub_feature_flags(shared_runner_minutes_on_root_namespace: false)
end
context 'when sub group is used' do
let(:root_ancestor) { create(:group) }
let(:group) { build(:group, parent: root_ancestor) }
it 'renders shared_runners_minutes_setting' do
render
expect(rendered).to render_template('namespaces/_shared_runners_minutes_setting')
end
end
end
describe 'when :shared_runner_minutes_on_root_namespace is enabled', :nested_groups do
before do
stub_feature_flags(shared_runner_minutes_on_root_namespace: true)
end
context 'when sub group is used' do
let(:root_ancestor) { create(:group) }
let(:group) { build(:group, parent: root_ancestor) }
it 'does not render shared_runners_minutes_setting' do
render
expect(rendered).not_to render_template('namespaces/_shared_runners_minutes_setting')
end
end
context 'when root group is used' do
let(:group) { build(:group) }
it 'does not render shared_runners_minutes_setting' do
render
expect(rendered).to render_template('namespaces/_shared_runners_minutes_setting')
end
end
end
end
<<<<<<< HEAD
=======
>>>>>>> origin/master
import Vue from 'vue';
import emptyState from '~/environments/components/empty_state.vue';
import mountComponent from '../helpers/vue_mount_component_helper';
......
......@@ -128,7 +128,10 @@ describe('Environment', () => {
setTimeout(() => {
spyOn(component, 'updateContent');
component.$el.querySelector('.js-environments-tab-stopped').click();
<<<<<<< HEAD:spec/javascripts/environments/environments_app_spec.js
=======
>>>>>>> origin/master:spec/javascripts/environments/environments_app_spec.js
expect(component.updateContent).toHaveBeenCalledWith({ scope: 'stopped', page: '1' });
done();
});
......
......@@ -95,7 +95,11 @@ describe('Environments Folder View', () => {
it('should render parent folder name', (done) => {
setTimeout(() => {
expect(
<<<<<<< HEAD
component.$el.querySelector('.js-folder-name').textContent.trim(),
=======
component.$el.querySelector('.js-folder-name').textContent,
>>>>>>> origin/master
).toContain('Environments / review');
done();
}, 0);
......@@ -107,6 +111,7 @@ describe('Environments Folder View', () => {
expect(
component.$el.querySelectorAll('.gl-pagination'),
).not.toBeNull();
<<<<<<< HEAD
done();
}, 0);
});
......@@ -117,15 +122,33 @@ describe('Environments Folder View', () => {
component.$el.querySelector('.gl-pagination .js-last-button a').click();
expect(component.updateContent).toHaveBeenCalledWith({ scope: component.scope, page: '10' });
=======
>>>>>>> origin/master
done();
}, 0);
});
<<<<<<< HEAD
it('should make an API request when using tabs', (done) => {
setTimeout(() => {
spyOn(component, 'updateContent');
component.$el.querySelector('.js-environments-tab-stopped').click();
=======
it('should make an API request when changing page', (done) => {
spyOn(component, 'updateContent');
setTimeout(() => {
component.$el.querySelector('.gl-pagination .js-last-button a').click();
expect(component.updateContent).toHaveBeenCalledWith({ scope: component.scope, page: '10' });
done();
}, 0);
});
it('should make an API request when using tabs', (done) => {
setTimeout(() => {
spyOn(component, 'updateContent');
component.$el.querySelector('.js-environments-tab-stopped').click();
>>>>>>> origin/master
expect(component.updateContent).toHaveBeenCalledWith({ scope: 'stopped', page: '1' });
done();
});
......
......@@ -187,6 +187,7 @@ describe('Pipelines', () => {
store: new Store(),
});
component.updateContent({ scope: 'finished', page: '4' });
<<<<<<< HEAD
expect(component.page).toEqual('4');
expect(component.scope).toEqual('finished');
......@@ -213,6 +214,36 @@ describe('Pipelines', () => {
component = mountComponent(PipelinesComponent, {
store: new Store(),
});
=======
expect(component.page).toEqual('4');
expect(component.scope).toEqual('finished');
expect(component.requestData.scope).toEqual('finished');
expect(component.requestData.page).toEqual('4');
});
});
describe('onChangeTab', () => {
it('should set page to 1', () => {
component = mountComponent(PipelinesComponent, {
store: new Store(),
});
spyOn(component, 'updateContent');
component.onChangeTab('running');
expect(component.updateContent).toHaveBeenCalledWith({ scope: 'running', page: '1' });
});
});
describe('onChangePage', () => {
it('should update page and keep scope', () => {
component = mountComponent(PipelinesComponent, {
store: new Store(),
});
>>>>>>> origin/master
spyOn(component, 'updateContent');
component.onChangePage(4);
......
......@@ -83,6 +83,20 @@ describe Gitlab::GroupHierarchy, :postgresql do
end
end
describe '#root' do
it 'includes only the roots' do
relation = described_class.new(Group.where(id: child2)).roots
expect(relation).to contain_exactly(parent)
end
it 'when quering parent it includes parent' do
relation = described_class.new(Group.where(id: parent)).roots
expect(relation).to contain_exactly(parent)
end
end
describe '#all_groups' do
let(:relation) do
described_class.new(Group.where(id: child1.id)).all_groups
......
......@@ -675,4 +675,17 @@ describe Namespace do
expect(other_namespace.find_fork_of(project)).to eq(other_fork)
end
end
describe '#root_ancestor' do
it 'returns the top most ancestor', :nested_groups do
root_group = create(:group)
nested_group = create(:group, parent: root_group)
deep_nested_group = create(:group, parent: nested_group)
very_deep_nested_group = create(:group, parent: deep_nested_group)
expect(nested_group.root_ancestor).to eq(root_group)
expect(deep_nested_group.root_ancestor).to eq(root_group)
expect(very_deep_nested_group.root_ancestor).to eq(root_group)
end
end
end
......@@ -3474,4 +3474,27 @@ describe Project do
expect(project.wiki_repository_exists?).to eq(false)
end
end
describe '#root_namespace' do
let(:project) { build(:project, namespace: parent) }
subject { project.root_namespace }
context 'when namespace has parent group' do
let(:root_ancestor) { create(:group) }
let(:parent) { build(:group, parent: root_ancestor) }
it 'returns root ancestor' do
is_expected.to eq(root_ancestor)
end
end
context 'when namespace is root ancestor' do
let(:parent) { build(:group) }
it 'returns current namespace' do
is_expected.to eq(parent)
end
end
end
end
......@@ -66,6 +66,27 @@ describe API::ProtectedBranches do
let(:message) { '404 Not found' }
end
end
context 'with per user/group access levels' do
let(:push_user) { create(:user) }
let(:merge_group) { create(:group) }
before do
protected_branch.push_access_levels.create!(user: push_user)
protected_branch.merge_access_levels.create!(group: merge_group)
end
it 'returns access level details' do
get api(route, user)
push_user_ids = json_response['push_access_levels'].map {|level| level['user_id']}
merge_group_ids = json_response['merge_access_levels'].map {|level| level['group_id']}
expect(response).to have_gitlab_http_status(200)
expect(push_user_ids).to include(push_user.id)
expect(merge_group_ids).to include(merge_group.id)
end
end
end
context 'when authenticated as a master' do
......
......@@ -42,6 +42,40 @@ describe UpdateBuildMinutesService do
.to eq(100 + build.duration.to_i)
end
end
context 'when namespace is subgroup' do
let(:root_ancestor) { create(:group, shared_runners_minutes_limit: 100) }
context 'when shared_runner_minutes_on_root_namespace is disabled' do
let(:namespace) { create(:namespace, parent: root_ancestor, shared_runners_minutes_limit: 100) }
before do
stub_feature_flags(shared_runner_minutes_on_root_namespace: false)
end
it 'creates a statistics in current namespace' do
subject
expect(namespace.namespace_statistics.reload.shared_runners_seconds)
.to eq(build.duration.to_i)
end
end
context 'when shared_runner_minutes_on_root_namespace is enabled', :nested_groups do
let(:namespace) { create(:namespace, parent: root_ancestor) }
before do
stub_feature_flags(shared_runner_minutes_on_root_namespace: true)
end
it 'creates a statistics in root namespace' do
subject
expect(root_ancestor.namespace_statistics.reload.shared_runners_seconds)
.to eq(build.duration.to_i)
end
end
end
end
context 'for specific runner' do
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment