Commit 8c47ec83 authored by Kamil Trzcinski's avatar Kamil Trzcinski

Merge branch 'introduce-build-minutes' of gitlab.com:gitlab-org/gitlab-ee into...

Merge branch 'introduce-build-minutes' of gitlab.com:gitlab-org/gitlab-ee into introduce-build-minutes
parents 35b94edd e98cdd4d
...@@ -90,3 +90,39 @@ ...@@ -90,3 +90,39 @@
} }
} }
} }
.panel {
.shared_runners_limit_under_quota {
color: $gl-success;
}
.shared_runners_limit_over_quota {
color: $gl-danger;
}
}
.pipeline-quota {
border-top: 1px solid $table-border-color;
border-bottom: 1px solid $table-border-color;
margin: 0 0 $gl-padding;
.row {
padding-top: 10px;
padding-bottom: 10px;
}
.right {
text-align: right;
}
.progress {
height: 6px;
width: 100%;
margin-bottom: 0;
margin-top: 4px;
}
}
table.pipeline-project-metrics tr td {
padding: $gl-padding;
}
...@@ -368,14 +368,6 @@ a.deploy-project-label { ...@@ -368,14 +368,6 @@ a.deploy-project-label {
color: $gl-warning; color: $gl-warning;
} }
.shared_runners_limit_under_quota {
color: $gl-success;
}
.shared_runners_limit_over_quota {
color: $gl-danger;
}
.breadcrumb.repo-breadcrumb { .breadcrumb.repo-breadcrumb {
padding: 0; padding: 0;
background: transparent; background: transparent;
......
class Groups::PipelineQuotaController < Groups::ApplicationController
before_action :authorize_admin_group!
layout 'group_settings'
def index
@projects = @group.projects.page(params[:page])
end
end
...@@ -67,6 +67,28 @@ module GroupsHelper ...@@ -67,6 +67,28 @@ module GroupsHelper
end end
end end
def group_shared_runner_limits_progress_bar(group)
percent = [group.shared_runners_minutes_percent_used, 100].min
status =
if percent == 100
'danger'
elsif percent >= 80
'warning'
else
'success'
end
options = {
class: "progress-bar progress-bar-#{status}",
style: "width: #{percent}%;"
}
content_tag :div, class: 'progress' do
content_tag :div, nil, options
end
end
def group_issues(group) def group_issues(group)
IssuesFinder.new(current_user, group_id: group.id).execute IssuesFinder.new(current_user, group_id: group.id).execute
end end
......
...@@ -192,6 +192,11 @@ class Namespace < ActiveRecord::Base ...@@ -192,6 +192,11 @@ class Namespace < ActiveRecord::Base
shared_runners_minutes.to_i >= shared_runners_minutes_limit shared_runners_minutes.to_i >= shared_runners_minutes_limit
end end
def shared_runners_minutes_percent_used
return 0 unless shared_runners_enabled? && shared_runners_minutes_limit_enabled?
100 * shared_runners_minutes.to_i / shared_runners_minutes_limit
end
private private
def repository_storage_paths def repository_storage_paths
......
- page_title "Pipeline Quota"
%h3.page-title Group Pipeline Quota
%p.light Monthly build minutes usage across shared runners for #{@group.name}
.pipeline-quota.container-fluid
.row
.col-sm-6
%strong
- last_reset = @group.shared_runners_minutes_last_reset
- if last_reset
Usage since
= last_reset.strftime('%b %d, %Y')
- else
Current Period Usage
%div
= group_shared_runner_limits_quota(@group)
minutes
.col-sm-6.right
- if @group.shared_runners_minutes_limit_enabled?
= "#{@group.shared_runners_minutes_percent_used}% used"
- else
Unlimited
= group_shared_runner_limits_progress_bar(@group)
%table.table.pipeline-project-metrics
%thead
%tr
%th Project
%th Minutes
%tbody
- @projects.each do |project|
%tr
%td
.avatar-container.s20.hidden-xs
= project_icon(project, alt: '', class: 'avatar project-avatar s20')
%strong= link_to project.name, project
%td
= project.shared_runners_minutes.to_i
- if @projects.blank?
%tr
%td{colspan: 2}
.nothing-here-block This group has no projects
= paginate @projects, theme: "gitlab"
...@@ -28,5 +28,9 @@ ...@@ -28,5 +28,9 @@
= link_to group_audit_events_path(@group), title: "Audit Events" do = link_to group_audit_events_path(@group), title: "Audit Events" do
%span %span
Audit Events Audit Events
= nav_link(controller: :pipeline_quota) do
= link_to group_pipeline_quota_path(@group), title: "Pipeline Quota" do
%span
Pipeline Quota
%li %li
= link_to 'Edit Group', edit_group_path(@group) = link_to 'Edit Group', edit_group_path(@group)
- @no_container = true - @no_container = true
- page_title "Pipelines" - page_title "Pipelines"
= content_for :flash_message do
= render 'shared/shared_runner_minutes_limit', project: @project
= render "projects/pipelines/head" = render "projects/pipelines/head"
%div{ class: container_class } %div{ class: container_class }
......
- project = local_assigns.fetch(:project, nil) - project = local_assigns.fetch(:project, nil)
- namespace = local_assigns.fetch(:namespace, project && project.namespace) - namespace = local_assigns.fetch(:namespace, project && project.namespace)
- has_limit = (project || namespace).shared_runners_minutes_limit_enabled?
- can_see_status = project.nil? || can?(current_user, :create_pipeline, project) - can_see_status = project.nil? || can?(current_user, :create_pipeline, project)
- if cookies[:hide_shared_runner_quota_message].blank? && namespace.shared_runners_minutes_used? && can_see_status - if cookies[:hide_shared_runner_quota_message].blank? && has_limit && namespace.shared_runners_minutes_used? && can_see_status
.shared-runner-quota-message.alert.alert-warning.hidden-xs .shared-runner-quota-message.alert.alert-warning.hidden-xs
= namespace.name = namespace.name
has exceeded their build minutes quota. Pipelines will not run anymore on shared runners. has exceeded their build minutes quota. Pipelines will not run anymore on shared runners.
......
...@@ -26,6 +26,7 @@ scope(path: 'groups/*group_id', ...@@ -26,6 +26,7 @@ scope(path: 'groups/*group_id',
## EE-specific ## EE-specific
resource :notification_setting, only: [:update] resource :notification_setting, only: [:update]
resources :audit_events, only: [:index] resources :audit_events, only: [:index]
resources :pipeline_quota, only: [:index]
## EE-specific ## EE-specific
## EE-specific ## EE-specific
......
FactoryGirl.define do FactoryGirl.define do
factory :group do factory :group, class: Group, parent: :namespace do
sequence(:name) { |n| "group#{n}" } sequence(:name) { |n| "group#{n}" }
path { name.downcase.gsub(/\s/, '_') } path { name.downcase.gsub(/\s/, '_') }
type 'Group' type 'Group'
......
...@@ -2,8 +2,8 @@ require 'spec_helper' ...@@ -2,8 +2,8 @@ require 'spec_helper'
feature 'CI shared runner limits', feature: true do feature 'CI shared runner limits', feature: true do
let(:user) { create(:user) } let(:user) { create(:user) }
let(:project) { create(:project, :public, namespace: namespace, shared_runners_enabled: true) } let!(:project) { create(:project, namespace: group, shared_runners_enabled: true) }
let(:namespace) { create(:namespace) } let(:group) { create(:group) }
before do before do
login_as(user) login_as(user)
...@@ -11,40 +11,60 @@ feature 'CI shared runner limits', feature: true do ...@@ -11,40 +11,60 @@ feature 'CI shared runner limits', feature: true do
context 'when project member' do context 'when project member' do
before do before do
project.team << [user, :developer] group.add_developer(user)
end end
context 'without limit' do context 'without limit' do
scenario 'it does not display a warning message on project homepage' do scenario 'it does not display a warning message on project homepage' do
visit namespace_project_path(project.namespace, project) visit_project_home
expect_no_quota_exceeded_alert
end
scenario 'it does not display a warning message on pipelines page' do
visit_project_pipelines
expect_no_quota_exceeded_alert expect_no_quota_exceeded_alert
end end
end end
context 'when limit is defined' do context 'when limit is defined' do
context 'when limit is exceeded' do context 'when limit is exceeded' do
let(:namespace) { create(:namespace, :with_used_build_minutes_limit) } let(:group) { create(:group, :with_used_build_minutes_limit) }
scenario 'it displays a warning message on project homepage' do scenario 'it displays a warning message on project homepage' do
visit namespace_project_path(project.namespace, project) visit_project_home
expect_quota_exceeded_alert("#{namespace.name} has exceeded their build minutes quota.") expect_quota_exceeded_alert("#{group.name} has exceeded their build minutes quota.")
end
scenario 'it displays a warning message on pipelines page' do
visit_project_pipelines
expect_quota_exceeded_alert("#{group.name} has exceeded their build minutes quota.")
end end
end end
context 'when limit not yet exceeded' do context 'when limit not yet exceeded' do
let(:namespace) { create(:namespace, :with_not_used_build_minutes_limit) } let(:group) { create(:group, :with_not_used_build_minutes_limit) }
scenario 'it does not display a warning message on project homepage' do scenario 'it does not display a warning message on project homepage' do
visit namespace_project_path(project.namespace, project) visit_project_home
expect_no_quota_exceeded_alert
end
scenario 'it does not display a warning message on pipelines page' do
visit_project_pipelines
expect_no_quota_exceeded_alert expect_no_quota_exceeded_alert
end end
end end
context 'when minutes are not yet set' do context 'when minutes are not yet set' do
let(:namespace) { create(:namespace, :with_build_minutes_limit) } let(:group) { create(:group, :with_build_minutes_limit) }
scenario 'it does not display a warning message on project homepage' do scenario 'it does not display a warning message on project homepage' do
visit namespace_project_path(project.namespace, project) visit_project_home
expect_no_quota_exceeded_alert
end
scenario 'it does not display a warning message on pipelines page' do
visit_project_pipelines
expect_no_quota_exceeded_alert expect_no_quota_exceeded_alert
end end
end end
...@@ -52,16 +72,29 @@ feature 'CI shared runner limits', feature: true do ...@@ -52,16 +72,29 @@ feature 'CI shared runner limits', feature: true do
end end
context 'when not a project member' do context 'when not a project member' do
let(:namespace) { create(:namespace, :with_used_build_minutes_limit) } let(:group) { create(:group, :with_used_build_minutes_limit) }
context 'when limit is defined and limit is exceeded' do context 'when limit is defined and limit is exceeded' do
scenario 'it does not display a warning message on project homepage' do scenario 'it does not display a warning message on project homepage' do
visit namespace_project_path(project.namespace, project) visit_project_home
expect_no_quota_exceeded_alert
end
scenario 'it does not display a warning message on pipelines page' do
visit_project_pipelines
expect_no_quota_exceeded_alert expect_no_quota_exceeded_alert
end end
end end
end end
def visit_project_home
visit namespace_project_path(project.namespace, project)
end
def visit_project_pipelines
visit namespace_project_pipelines_path(project.namespace, project)
end
def expect_quota_exceeded_alert(message = nil) def expect_quota_exceeded_alert(message = nil)
expect(page).to have_selector('.shared-runner-quota-message', count: 1) expect(page).to have_selector('.shared-runner-quota-message', count: 1)
expect(page.find('.shared-runner-quota-message')).to have_content(message) unless message.nil? expect(page.find('.shared-runner-quota-message')).to have_content(message) unless message.nil?
......
...@@ -8,7 +8,7 @@ describe UpdateBuildMinutesService, services: true do ...@@ -8,7 +8,7 @@ describe UpdateBuildMinutesService, services: true do
let(:build) do let(:build) do
create(:ci_build, :success, create(:ci_build, :success,
runner: runner, pipeline: pipeline, runner: runner, pipeline: pipeline,
started_at: 2.hour.ago, finished_at: 1.hour.ago) started_at: 2.hours.ago, finished_at: 1.hour.ago)
end end
subject { described_class.new.execute(build) } subject { described_class.new.execute(build) }
...@@ -19,11 +19,13 @@ describe UpdateBuildMinutesService, services: true do ...@@ -19,11 +19,13 @@ describe UpdateBuildMinutesService, services: true do
it "creates a metrics and sets duration" do it "creates a metrics and sets duration" do
subject subject
#expect(project.reload.project_metrics.shared_runners_minutes).to # expect(project.reload.project_metrics.shared_runners_minutes).to(
# eq(build.duration) # eq(build.duration)
# )
expect(namespace.reload.namespace_metrics.shared_runners_minutes).to expect(namespace.reload.namespace_metrics.shared_runners_minutes).to(
eq(build.duration) eq(build.duration)
)
end end
context 'when metrics are created' do context 'when metrics are created' do
...@@ -35,11 +37,13 @@ describe UpdateBuildMinutesService, services: true do ...@@ -35,11 +37,13 @@ describe UpdateBuildMinutesService, services: true do
it "updates metrics and adds duration" do it "updates metrics and adds duration" do
subject subject
expect(project.project_metrics.shared_runners_minutes).to expect(project.project_metrics.shared_runners_minutes).to(
eq(100 + build.duration) eq(100 + build.duration)
)
expect(namespace.namespace_metrics.shared_runners_minutes).to expect(namespace.namespace_metrics.shared_runners_minutes).to(
eq(100 + build.duration) eq(100 + build.duration)
)
end end
end end
end end
......
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