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 @@
}
}
}
.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 {
color: $gl-warning;
}
.shared_runners_limit_under_quota {
color: $gl-success;
}
.shared_runners_limit_over_quota {
color: $gl-danger;
}
.breadcrumb.repo-breadcrumb {
padding: 0;
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
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)
IssuesFinder.new(current_user, group_id: group.id).execute
end
......
......@@ -192,6 +192,11 @@ class Namespace < ActiveRecord::Base
shared_runners_minutes.to_i >= shared_runners_minutes_limit
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
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 @@
= link_to group_audit_events_path(@group), title: "Audit Events" do
%span
Audit Events
= nav_link(controller: :pipeline_quota) do
= link_to group_pipeline_quota_path(@group), title: "Pipeline Quota" do
%span
Pipeline Quota
%li
= link_to 'Edit Group', edit_group_path(@group)
- @no_container = true
- page_title "Pipelines"
= content_for :flash_message do
= render 'shared/shared_runner_minutes_limit', project: @project
= render "projects/pipelines/head"
%div{ class: container_class }
......
- project = local_assigns.fetch(:project, nil)
- 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)
- 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
= namespace.name
has exceeded their build minutes quota. Pipelines will not run anymore on shared runners.
......
......@@ -26,6 +26,7 @@ scope(path: 'groups/*group_id',
## EE-specific
resource :notification_setting, only: [:update]
resources :audit_events, only: [:index]
resources :pipeline_quota, only: [:index]
## EE-specific
## EE-specific
......
FactoryGirl.define do
factory :group do
factory :group, class: Group, parent: :namespace do
sequence(:name) { |n| "group#{n}" }
path { name.downcase.gsub(/\s/, '_') }
type 'Group'
......
......@@ -2,8 +2,8 @@ require 'spec_helper'
feature 'CI shared runner limits', feature: true do
let(:user) { create(:user) }
let(:project) { create(:project, :public, namespace: namespace, shared_runners_enabled: true) }
let(:namespace) { create(:namespace) }
let!(:project) { create(:project, namespace: group, shared_runners_enabled: true) }
let(:group) { create(:group) }
before do
login_as(user)
......@@ -11,40 +11,60 @@ feature 'CI shared runner limits', feature: true do
context 'when project member' do
before do
project.team << [user, :developer]
group.add_developer(user)
end
context 'without limit' 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
end
end
context 'when limit is defined' 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
visit namespace_project_path(project.namespace, project)
expect_quota_exceeded_alert("#{namespace.name} has exceeded their build minutes quota.")
visit_project_home
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
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
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
end
end
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
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
end
end
......@@ -52,16 +72,29 @@ feature 'CI shared runner limits', feature: true do
end
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
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
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)
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?
......
......@@ -8,7 +8,7 @@ describe UpdateBuildMinutesService, services: true do
let(:build) do
create(:ci_build, :success,
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
subject { described_class.new.execute(build) }
......@@ -19,11 +19,13 @@ describe UpdateBuildMinutesService, services: true do
it "creates a metrics and sets duration" do
subject
#expect(project.reload.project_metrics.shared_runners_minutes).to
# expect(project.reload.project_metrics.shared_runners_minutes).to(
# eq(build.duration)
# )
expect(namespace.reload.namespace_metrics.shared_runners_minutes).to
expect(namespace.reload.namespace_metrics.shared_runners_minutes).to(
eq(build.duration)
)
end
context 'when metrics are created' do
......@@ -35,11 +37,13 @@ describe UpdateBuildMinutesService, services: true do
it "updates metrics and adds duration" do
subject
expect(project.project_metrics.shared_runners_minutes).to
expect(project.project_metrics.shared_runners_minutes).to(
eq(100 + build.duration)
)
expect(namespace.namespace_metrics.shared_runners_minutes).to
expect(namespace.namespace_metrics.shared_runners_minutes).to(
eq(100 + build.duration)
)
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