Commit e600d6b4 authored by Jan Provaznik's avatar Jan Provaznik

Merge remote-tracking branch 'gitlab-ee/master' into dev-master-ee

parents 525ba37f cd8b5987
...@@ -58,10 +58,15 @@ export const alternativeTokenKeys = [ ...@@ -58,10 +58,15 @@ export const alternativeTokenKeys = [
export const conditions = [ export const conditions = [
{ {
url: 'assignee_id=0', url: 'assignee_id=None',
tokenKey: 'assignee', tokenKey: 'assignee',
value: 'none', value: 'none',
}, },
{
url: 'assignee_id=Any',
tokenKey: 'assignee',
value: 'any',
},
{ {
url: 'milestone_title=No+Milestone', url: 'milestone_title=No+Milestone',
tokenKey: 'milestone', tokenKey: 'milestone',
......
...@@ -4,12 +4,13 @@ class Dashboard::MilestonesController < Dashboard::ApplicationController ...@@ -4,12 +4,13 @@ class Dashboard::MilestonesController < Dashboard::ApplicationController
include MilestoneActions include MilestoneActions
before_action :projects before_action :projects
before_action :groups, only: :index
before_action :milestone, only: [:show, :merge_requests, :participants, :labels] before_action :milestone, only: [:show, :merge_requests, :participants, :labels]
def index def index
respond_to do |format| respond_to do |format|
format.html do format.html do
@milestone_states = GlobalMilestone.states_count(@projects) @milestone_states = Milestone.states_count(@projects.select(:id), @groups.select(:id))
@milestones = Kaminari.paginate_array(milestones).page(params[:page]) @milestones = Kaminari.paginate_array(milestones).page(params[:page])
end end
format.json do format.json do
...@@ -42,4 +43,8 @@ class Dashboard::MilestonesController < Dashboard::ApplicationController ...@@ -42,4 +43,8 @@ class Dashboard::MilestonesController < Dashboard::ApplicationController
@milestone = DashboardMilestone.build(@projects, params[:title]) @milestone = DashboardMilestone.build(@projects, params[:title])
render_404 unless @milestone render_404 unless @milestone
end end
def groups
@groups ||= GroupsFinder.new(current_user, state_all: true).execute
end
end end
...@@ -12,7 +12,7 @@ class Groups::MilestonesController < Groups::ApplicationController ...@@ -12,7 +12,7 @@ class Groups::MilestonesController < Groups::ApplicationController
def index def index
respond_to do |format| respond_to do |format|
format.html do format.html do
@milestone_states = GlobalMilestone.states_count(group_projects, group) @milestone_states = Milestone.states_count(group_projects, [group])
@milestones = Kaminari.paginate_array(milestones).page(params[:page]) @milestones = Kaminari.paginate_array(milestones).page(params[:page])
end end
format.json do format.json do
......
...@@ -22,6 +22,12 @@ module Clusters ...@@ -22,6 +22,12 @@ module Clusters
has_many :cluster_projects, class_name: 'Clusters::Project' has_many :cluster_projects, class_name: 'Clusters::Project'
has_many :projects, through: :cluster_projects, class_name: '::Project' has_many :projects, through: :cluster_projects, class_name: '::Project'
has_many :cluster_groups, class_name: 'Clusters::Group'
has_many :groups, through: :cluster_groups, class_name: '::Group'
has_one :cluster_group, -> { order(id: :desc) }, class_name: 'Clusters::Group'
has_one :group, through: :cluster_group, class_name: '::Group'
# we force autosave to happen when we save `Cluster` model # we force autosave to happen when we save `Cluster` model
has_one :provider_gcp, class_name: 'Clusters::Providers::Gcp', autosave: true has_one :provider_gcp, class_name: 'Clusters::Providers::Gcp', autosave: true
...@@ -40,8 +46,12 @@ module Clusters ...@@ -40,8 +46,12 @@ module Clusters
accepts_nested_attributes_for :platform_kubernetes, update_only: true accepts_nested_attributes_for :platform_kubernetes, update_only: true
validates :name, cluster_name: true validates :name, cluster_name: true
validates :cluster_type, presence: true
validate :restrict_modification, on: :update validate :restrict_modification, on: :update
validate :no_groups, unless: :group_type?
validate :no_projects, unless: :project_type?
delegate :status, to: :provider, allow_nil: true delegate :status, to: :provider, allow_nil: true
delegate :status_reason, to: :provider, allow_nil: true delegate :status_reason, to: :provider, allow_nil: true
delegate :on_creation?, to: :provider, allow_nil: true delegate :on_creation?, to: :provider, allow_nil: true
...@@ -52,6 +62,12 @@ module Clusters ...@@ -52,6 +62,12 @@ module Clusters
delegate :available?, to: :application_ingress, prefix: true, allow_nil: true delegate :available?, to: :application_ingress, prefix: true, allow_nil: true
delegate :available?, to: :application_prometheus, prefix: true, allow_nil: true delegate :available?, to: :application_prometheus, prefix: true, allow_nil: true
enum cluster_type: {
instance_type: 1,
group_type: 2,
project_type: 3
}
enum platform_type: { enum platform_type: {
kubernetes: 1 kubernetes: 1
} }
...@@ -124,5 +140,17 @@ module Clusters ...@@ -124,5 +140,17 @@ module Clusters
true true
end end
def no_groups
if groups.any?
errors.add(:cluster, 'cannot have groups assigned')
end
end
def no_projects
if projects.any?
errors.add(:cluster, 'cannot have projects assigned')
end
end
end end
end end
# frozen_string_literal: true
module Clusters
class Group < ActiveRecord::Base
self.table_name = 'cluster_groups'
belongs_to :cluster, class_name: 'Clusters::Cluster'
belongs_to :group, class_name: '::Group'
end
end
...@@ -42,6 +42,7 @@ module DeploymentPlatform ...@@ -42,6 +42,7 @@ module DeploymentPlatform
{ {
name: 'kubernetes-template', name: 'kubernetes-template',
projects: [self], projects: [self],
cluster_type: :project_type,
provider_type: :user, provider_type: :user,
platform_type: :kubernetes, platform_type: :kubernetes,
platform_kubernetes_attributes: platform_kubernetes_attributes_from_service_template platform_kubernetes_attributes: platform_kubernetes_attributes_from_service_template
......
...@@ -36,50 +36,6 @@ class GlobalMilestone ...@@ -36,50 +36,6 @@ class GlobalMilestone
new(title, child_milestones) new(title, child_milestones)
end end
def self.states_count(projects, group = nil)
legacy_group_milestones_count = legacy_group_milestone_states_count(projects)
group_milestones_count = group_milestones_states_count(group)
legacy_group_milestones_count.merge(group_milestones_count) do |k, legacy_group_milestones_count, group_milestones_count|
legacy_group_milestones_count + group_milestones_count
end
end
def self.group_milestones_states_count(group)
return STATE_COUNT_HASH unless group
params = { group_ids: [group.id], state: 'all' }
relation = MilestonesFinder.new(params).execute # rubocop: disable CodeReuse/Finder
grouped_by_state = relation.reorder(nil).group(:state).count
{
opened: grouped_by_state['active'] || 0,
closed: grouped_by_state['closed'] || 0,
all: grouped_by_state.values.sum
}
end
# Counts the legacy group milestones which must be grouped by title
def self.legacy_group_milestone_states_count(projects)
return STATE_COUNT_HASH unless projects
params = { project_ids: projects.map(&:id), state: 'all' }
relation = MilestonesFinder.new(params).execute # rubocop: disable CodeReuse/Finder
project_milestones_by_state_and_title = relation.reorder(nil).group(:state, :title).count
opened = count_by_state(project_milestones_by_state_and_title, 'active')
closed = count_by_state(project_milestones_by_state_and_title, 'closed')
all = project_milestones_by_state_and_title.map { |(_, title), _| title }.uniq.count
{
opened: opened,
closed: closed,
all: all
}
end
def self.count_by_state(milestones_by_state_and_title, state) def self.count_by_state(milestones_by_state_and_title, state)
milestones_by_state_and_title.count do |(milestone_state, _), _| milestones_by_state_and_title.count do |(milestone_state, _), _|
milestone_state == state milestone_state == state
......
...@@ -43,6 +43,9 @@ class Group < Namespace ...@@ -43,6 +43,9 @@ class Group < Namespace
has_many :boards has_many :boards
has_many :badges, class_name: 'GroupBadge' has_many :badges, class_name: 'GroupBadge'
has_many :cluster_groups, class_name: 'Clusters::Group'
has_many :clusters, through: :cluster_groups, class_name: 'Clusters::Cluster'
has_many :todos has_many :todos
accepts_nested_attributes_for :variables, allow_destroy: true accepts_nested_attributes_for :variables, allow_destroy: true
......
...@@ -174,6 +174,22 @@ class Milestone < ActiveRecord::Base ...@@ -174,6 +174,22 @@ class Milestone < ActiveRecord::Base
sorted.with_order_id_desc sorted.with_order_id_desc
end end
def self.states_count(projects, groups = nil)
return STATE_COUNT_HASH unless projects || groups
counts = Milestone
.for_projects_and_groups(projects&.map(&:id), groups&.map(&:id))
.reorder(nil)
.group(:state)
.count
{
opened: counts['active'] || 0,
closed: counts['closed'] || 0,
all: counts.values.sum
}
end
## ##
# Returns the String necessary to reference this Milestone in Markdown. Group # Returns the String necessary to reference this Milestone in Markdown. Group
# milestones only support name references, and do not support cross-project # milestones only support name references, and do not support cross-project
......
...@@ -11,9 +11,9 @@ module Clusters ...@@ -11,9 +11,9 @@ module Clusters
end end
def execute(project:, access_token: nil) def execute(project:, access_token: nil)
raise ArgumentError.new(_('Instance does not support multiple Kubernetes clusters')) unless can_create_cluster?(project) raise ArgumentError, _('Instance does not support multiple Kubernetes clusters') unless can_create_cluster?(project)
cluster_params = params.merge(user: current_user, projects: [project]) cluster_params = params.merge(user: current_user, cluster_type: :project_type, projects: [project])
cluster_params[:provider_gcp_attributes].try do |provider| cluster_params[:provider_gcp_attributes].try do |provider|
provider[:access_token] = access_token provider[:access_token] = access_token
end end
......
...@@ -61,7 +61,10 @@ ...@@ -61,7 +61,10 @@
%ul{ data: { dropdown: true } } %ul{ data: { dropdown: true } }
%li.filter-dropdown-item{ data: { value: 'none' } } %li.filter-dropdown-item{ data: { value: 'none' } }
%button.btn.btn-link{ type: 'button' } %button.btn.btn-link{ type: 'button' }
= _('No Assignee') = _('None')
%li.filter-dropdown-item{ data: { value: 'any' } }
%button.btn.btn-link{ type: 'button' }
= _('Any')
%li.divider.droplab-item-ignore %li.divider.droplab-item-ignore
- if current_user - if current_user
= render 'shared/issuable/user_dropdown_item', = render 'shared/issuable/user_dropdown_item',
...@@ -81,7 +84,7 @@ ...@@ -81,7 +84,7 @@
%li.filter-dropdown-item{ data: { value: 'upcoming' } } %li.filter-dropdown-item{ data: { value: 'upcoming' } }
%button.btn.btn-link{ type: 'button' } %button.btn.btn-link{ type: 'button' }
= _('Upcoming') = _('Upcoming')
%li.filter-dropdown-item{ 'data-value' => 'started' } %li.filter-dropdown-item{ data: { value: 'started' } }
%button.btn.btn-link{ type: 'button' } %button.btn.btn-link{ type: 'button' }
= _('Started') = _('Started')
%li.divider.droplab-item-ignore %li.divider.droplab-item-ignore
......
---
title: Adds model and migrations to enable group level clusters
merge_request: 22307
author:
type: other
---
title: Add None/Any option for assignee_id in search bar
merge_request: 22599
author: Heinrich Lee Yu
type: added
---
title: Fixing count on Milestones
merge_request: 21446
author:
type: fixed
...@@ -87,6 +87,7 @@ Rails.application.routes.draw do ...@@ -87,6 +87,7 @@ Rails.application.routes.draw do
get 'ide' => 'ide#index' get 'ide' => 'ide#index'
get 'ide/*vueroute' => 'ide#index', format: false get 'ide/*vueroute' => 'ide#index', format: false
draw :operations
draw :instance_statistics draw :instance_statistics
end end
......
# frozen_string_literal: true
class CreateClusterGroups < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
def change
create_table :cluster_groups do |t|
t.references :cluster, null: false, foreign_key: { on_delete: :cascade }
t.references :group, null: false, index: true
t.index [:cluster_id, :group_id], unique: true
t.foreign_key :namespaces, column: :group_id, on_delete: :cascade
end
end
end
# frozen_string_literal: true
class AddClusterTypeToClusters < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
PROJECT_CLUSTER_TYPE = 3
disable_ddl_transaction!
def up
add_column_with_default(:clusters, :cluster_type, :smallint, default: PROJECT_CLUSTER_TYPE)
end
def down
remove_column(:clusters, :cluster_type)
end
end
...@@ -717,6 +717,14 @@ ActiveRecord::Schema.define(version: 20181017131623) do ...@@ -717,6 +717,14 @@ ActiveRecord::Schema.define(version: 20181017131623) do
add_index "ci_variables", ["project_id", "key", "environment_scope"], name: "index_ci_variables_on_project_id_and_key_and_environment_scope", unique: true, using: :btree add_index "ci_variables", ["project_id", "key", "environment_scope"], name: "index_ci_variables_on_project_id_and_key_and_environment_scope", unique: true, using: :btree
create_table "cluster_groups", force: :cascade do |t|
t.integer "cluster_id", null: false
t.integer "group_id", null: false
end
add_index "cluster_groups", ["cluster_id", "group_id"], name: "index_cluster_groups_on_cluster_id_and_group_id", unique: true, using: :btree
add_index "cluster_groups", ["group_id"], name: "index_cluster_groups_on_group_id", using: :btree
create_table "cluster_platforms_kubernetes", force: :cascade do |t| create_table "cluster_platforms_kubernetes", force: :cascade do |t|
t.integer "cluster_id", null: false t.integer "cluster_id", null: false
t.datetime "created_at", null: false t.datetime "created_at", null: false
...@@ -772,6 +780,7 @@ ActiveRecord::Schema.define(version: 20181017131623) do ...@@ -772,6 +780,7 @@ ActiveRecord::Schema.define(version: 20181017131623) do
t.boolean "enabled", default: true t.boolean "enabled", default: true
t.string "name", null: false t.string "name", null: false
t.string "environment_scope", default: "*", null: false t.string "environment_scope", default: "*", null: false
t.integer "cluster_type", limit: 2, default: 3, null: false
end end
add_index "clusters", ["enabled"], name: "index_clusters_on_enabled", using: :btree add_index "clusters", ["enabled"], name: "index_clusters_on_enabled", using: :btree
...@@ -3225,6 +3234,8 @@ ActiveRecord::Schema.define(version: 20181017131623) do ...@@ -3225,6 +3234,8 @@ ActiveRecord::Schema.define(version: 20181017131623) do
add_foreign_key "ci_triggers", "projects", name: "fk_e3e63f966e", on_delete: :cascade add_foreign_key "ci_triggers", "projects", name: "fk_e3e63f966e", on_delete: :cascade
add_foreign_key "ci_triggers", "users", column: "owner_id", name: "fk_e8e10d1964", on_delete: :cascade add_foreign_key "ci_triggers", "users", column: "owner_id", name: "fk_e8e10d1964", on_delete: :cascade
add_foreign_key "ci_variables", "projects", name: "fk_ada5eb64b3", on_delete: :cascade add_foreign_key "ci_variables", "projects", name: "fk_ada5eb64b3", on_delete: :cascade
add_foreign_key "cluster_groups", "clusters", on_delete: :cascade
add_foreign_key "cluster_groups", "namespaces", column: "group_id", on_delete: :cascade
add_foreign_key "cluster_platforms_kubernetes", "clusters", on_delete: :cascade add_foreign_key "cluster_platforms_kubernetes", "clusters", on_delete: :cascade
add_foreign_key "cluster_projects", "clusters", on_delete: :cascade add_foreign_key "cluster_projects", "clusters", on_delete: :cascade
add_foreign_key "cluster_projects", "projects", on_delete: :cascade add_foreign_key "cluster_projects", "projects", on_delete: :cascade
......
...@@ -12,25 +12,21 @@ Read the documentation for [Disaster Recovery](../disaster_recovery/index.md). ...@@ -12,25 +12,21 @@ Read the documentation for [Disaster Recovery](../disaster_recovery/index.md).
We currently replicate project repositories, LFS objects, generated We currently replicate project repositories, LFS objects, generated
attachments / avatars and the whole database. This means user accounts, attachments / avatars and the whole database. This means user accounts,
issues, merge requests, groups, project data, etc., will be available for issues, merge requests, groups, project data, etc., will be available for
query. We currently don't replicate artifact data (`shared/folder`). query.
## Can I git push to a secondary node? ## Can I git push to a secondary node?
No. All writing operations (this includes `git push`) must be done in your Yes, you can push changes to a **secondary** node. The push will be proxied to the **primary** node.
primary node.
## How long does it take to have a commit replicated to a secondary node? ## How long does it take to have a commit replicated to a secondary node?
All replication operations are asynchronous and are queued to be dispatched in All replication operations are asynchronous and are queued to be dispatched. Therefore, it depends on a lot of
a batched request every 10 minutes. Besides that, it depends on a lot of other
factors including the amount of traffic, how big your commit is, the factors including the amount of traffic, how big your commit is, the
connectivity between your nodes, your hardware, etc. connectivity between your nodes, your hardware, etc.
## What if the SSH server runs at a different port? ## What if the SSH server runs at a different port?
We send the clone url from the primary server to any secondaries, so it That's totally fine. We use HTTP(s) to fetch repository changes from the **primary** node to **secondary** nodes.
doesn't matter. If primary is running on port `2200`, clone url will reflect
that.
## Is this possible to set up a Docker Registry for a secondary node that mirrors the one on a primary node? ## Is this possible to set up a Docker Registry for a secondary node that mirrors the one on a primary node?
......
...@@ -134,7 +134,7 @@ Parameters: ...@@ -134,7 +134,7 @@ Parameters:
"description": "Amazing release. Wow" "description": "Amazing release. Wow"
}, },
"name": "v1.0.0", "name": "v1.0.0",
"target: "2695effb5807a22ff3d138d593fd856244e155e7", "target": "2695effb5807a22ff3d138d593fd856244e155e7",
"message": null "message": null
} }
``` ```
......
...@@ -63,4 +63,10 @@ are still maintained, they have been deprecated with GitLab 11.0 and may be remo ...@@ -63,4 +63,10 @@ are still maintained, they have been deprecated with GitLab 11.0 and may be remo
in next major release, GitLab 12.0. You are advised to update your current `.gitlab-ci.yml` in next major release, GitLab 12.0. You are advised to update your current `.gitlab-ci.yml`
configuration to reflect that change. configuration to reflect that change.
CAUTION: **Caution:**
Starting with GitLab 11.5, Container Scanning feature is licensed under the name `container_scanning`.
While the old name `sast_container` is still maintained, it has been deprecated with GitLab 11.5 and
may be removed in next major release, GitLab 12.0. You are advised to update your current `.gitlab-ci.yml`
configuration to reflect that change if you are using the `$GITLAB_FEATURES` environment variable.
[ee]: https://about.gitlab.com/pricing/ [ee]: https://about.gitlab.com/pricing/
...@@ -176,8 +176,8 @@ Clicking on the current board name in the upper left corner will reveal a ...@@ -176,8 +176,8 @@ Clicking on the current board name in the upper left corner will reveal a
menu from where you can create another Issue Board and rename or delete the menu from where you can create another Issue Board and rename or delete the
existing one. existing one.
Clicking on the main issue board link will take you to the last board When you're revisiting an issue board in a project or group with multiple boards,
you visited. GitLab will automatically load the last board you visited.
NOTE: **Note:** NOTE: **Note:**
The Multiple Issue Boards feature is available for The Multiple Issue Boards feature is available for
......
...@@ -40,6 +40,12 @@ are still maintained they have been deprecated with GitLab 11.0 and may be remov ...@@ -40,6 +40,12 @@ are still maintained they have been deprecated with GitLab 11.0 and may be remov
in next major release, GitLab 12.0. You are advised to update your current `.gitlab-ci.yml` in next major release, GitLab 12.0. You are advised to update your current `.gitlab-ci.yml`
configuration to reflect that change. configuration to reflect that change.
CAUTION: **Caution:**
Starting with GitLab 11.5, Container Scanning feature is licensed under the name `container_scanning`.
While the old name `sast_container` is still maintained, it has been deprecated with GitLab 11.5 and
may be removed in next major release, GitLab 12.0. You are advised to update your current `.gitlab-ci.yml`
configuration to reflect that change if you are using the `$GITLAB_FEATURES` environment variable.
[ee-3672]: https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/3672 [ee-3672]: https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/3672
[ee]: https://about.gitlab.com/pricing/ [ee]: https://about.gitlab.com/pricing/
[ci]: ../../../ci/README.md [ci]: ../../../ci/README.md
......
...@@ -40,6 +40,16 @@ The same process is valid for merge requests. Navigate to your project's **Merge ...@@ -40,6 +40,16 @@ The same process is valid for merge requests. Navigate to your project's **Merge
and click **Search or filter results...**. Merge requests can be filtered by author, assignee, and click **Search or filter results...**. Merge requests can be filtered by author, assignee,
milestone, and label. milestone, and label.
### Filtering by **None** / **Any**
Some filter fields like milestone and assignee, allow you to filter by **None** or **Any**.
![filter by none any](img/issues_filter_none_any.png)
Selecting **None** returns results that have an empty value for that field. E.g.: no milestone, no assignee.
Selecting **Any** does the opposite. It returns results that have a non-empty value for that field.
### Searching for specific terms ### Searching for specific terms
You can filter issues and merge requests by specific terms included in titles or descriptions. You can filter issues and merge requests by specific terms included in titles or descriptions.
......
...@@ -17,7 +17,6 @@ module Projects ...@@ -17,7 +17,6 @@ module Projects
end end
update_branches update_branches
update_root_ref
success success
rescue Gitlab::Shell::Error, UpdateError => e rescue Gitlab::Shell::Error, UpdateError => e
...@@ -61,22 +60,6 @@ module Projects ...@@ -61,22 +60,6 @@ module Projects
end end
end end
# Update the default branch querying the remote to determine its HEAD
def update_root_ref
# Fetch remote default branch doesn't work with SSH mirrors:
# https://gitlab.com/gitlab-org/gitaly/issues/1363
return if ssh_mirror?
project.update_root_ref(::Repository::MIRROR_REMOTE)
end
def ssh_mirror?
uri = Addressable::URI.parse(project.import_url)
uri.scheme.present? && uri.scheme == 'ssh'
rescue Addressable::URI::InvalidURIError
false
end
def update_tags(&block) def update_tags(&block)
old_tags = repository_tags_with_target.each_with_object({}) { |tag, tags| tags[tag.name] = tag } old_tags = repository_tags_with_target.each_with_object({}) { |tag, tags| tags[tag.name] = tag }
......
---
title: Does not synchronize default branch for pull mirrors
merge_request: 8138
author:
type: changed
# frozen_string_literal: true
# Placeholder for https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/7341
# Added to resolve https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/8131
import Vue from 'vue'; import Vue from 'vue';
import component from 'ee/jobs/components/shared_runner_limit_block.vue'; import component from 'ee/jobs/components/shared_runner_limit_block.vue';
import mountComponent from '../helpers/vue_mount_component_helper'; import mountComponent from 'spec/helpers/vue_mount_component_helper';
import { trimText } from '../helpers/vue_component_helper'; import { trimText } from 'spec/helpers/vue_component_helper';
describe('Shared Runner Limit Block', () => { describe('Shared Runner Limit Block', () => {
const Component = Vue.extend(component); const Component = Vue.extend(component);
......
...@@ -8,9 +8,9 @@ import mountComponent from 'spec/helpers/vue_mount_component_helper'; ...@@ -8,9 +8,9 @@ import mountComponent from 'spec/helpers/vue_mount_component_helper';
import { TEST_HOST } from 'spec/test_constants'; import { TEST_HOST } from 'spec/test_constants';
import state from 'ee/vue_shared/security_reports/store/state'; import state from 'ee/vue_shared/security_reports/store/state';
import mockData, { baseIssues, headIssues, basePerformance, headPerformance } from './mock_data'; import mockData, { baseIssues, headIssues, basePerformance, headPerformance } from 'spec/vue_mr_widget/mock_data';
import { // eslint-disable-line import/order import {
sastIssues, sastIssues,
sastIssuesBase, sastIssuesBase,
dockerReport, dockerReport,
......
...@@ -23,8 +23,6 @@ describe Projects::UpdateMirrorService do ...@@ -23,8 +23,6 @@ describe Projects::UpdateMirrorService do
end end
it "fetches the upstream repository" do it "fetches the upstream repository" do
stub_find_remote_root_ref(project)
expect(project).to receive(:fetch_mirror) expect(project).to receive(:fetch_mirror)
service.execute service.execute
...@@ -32,7 +30,6 @@ describe Projects::UpdateMirrorService do ...@@ -32,7 +30,6 @@ describe Projects::UpdateMirrorService do
it 'rescues exceptions from Repository#ff_merge' do it 'rescues exceptions from Repository#ff_merge' do
stub_fetch_mirror(project) stub_fetch_mirror(project)
stub_find_remote_root_ref(project)
expect(project.repository).to receive(:ff_merge).and_raise(Gitlab::Git::PreReceiveError) expect(project.repository).to receive(:ff_merge).and_raise(Gitlab::Git::PreReceiveError)
...@@ -41,7 +38,6 @@ describe Projects::UpdateMirrorService do ...@@ -41,7 +38,6 @@ describe Projects::UpdateMirrorService do
it "returns success when updated succeeds" do it "returns success when updated succeeds" do
stub_fetch_mirror(project) stub_fetch_mirror(project)
stub_find_remote_root_ref(project)
result = service.execute result = service.execute
...@@ -51,7 +47,6 @@ describe Projects::UpdateMirrorService do ...@@ -51,7 +47,6 @@ describe Projects::UpdateMirrorService do
context "updating tags" do context "updating tags" do
it "creates new tags" do it "creates new tags" do
stub_fetch_mirror(project) stub_fetch_mirror(project)
stub_find_remote_root_ref(project)
service.execute service.execute
...@@ -60,7 +55,6 @@ describe Projects::UpdateMirrorService do ...@@ -60,7 +55,6 @@ describe Projects::UpdateMirrorService do
it "only invokes GitTagPushService for tags pointing to commits" do it "only invokes GitTagPushService for tags pointing to commits" do
stub_fetch_mirror(project) stub_fetch_mirror(project)
stub_find_remote_root_ref(project)
expect(GitTagPushService).to receive(:new) expect(GitTagPushService).to receive(:new)
.with(project, project.owner, hash_including(ref: 'refs/tags/new-tag')) .with(project, project.owner, hash_including(ref: 'refs/tags/new-tag'))
...@@ -84,7 +78,6 @@ describe Projects::UpdateMirrorService do ...@@ -84,7 +78,6 @@ describe Projects::UpdateMirrorService do
project.reload project.reload
stub_fetch_mirror(project) stub_fetch_mirror(project)
stub_find_remote_root_ref(project)
service.execute service.execute
...@@ -93,7 +86,6 @@ describe Projects::UpdateMirrorService do ...@@ -93,7 +86,6 @@ describe Projects::UpdateMirrorService do
it 'does not create an unprotected branch' do it 'does not create an unprotected branch' do
stub_fetch_mirror(project) stub_fetch_mirror(project)
stub_find_remote_root_ref(project)
service.execute service.execute
...@@ -105,7 +97,6 @@ describe Projects::UpdateMirrorService do ...@@ -105,7 +97,6 @@ describe Projects::UpdateMirrorService do
project.reload project.reload
stub_fetch_mirror(project) stub_fetch_mirror(project)
stub_find_remote_root_ref(project)
service.execute service.execute
...@@ -115,7 +106,6 @@ describe Projects::UpdateMirrorService do ...@@ -115,7 +106,6 @@ describe Projects::UpdateMirrorService do
it "does not update unprotected branches" do it "does not update unprotected branches" do
stub_fetch_mirror(project) stub_fetch_mirror(project)
stub_find_remote_root_ref(project)
service.execute service.execute
...@@ -126,7 +116,6 @@ describe Projects::UpdateMirrorService do ...@@ -126,7 +116,6 @@ describe Projects::UpdateMirrorService do
it "creates new branches" do it "creates new branches" do
stub_fetch_mirror(project) stub_fetch_mirror(project)
stub_find_remote_root_ref(project)
service.execute service.execute
...@@ -135,7 +124,6 @@ describe Projects::UpdateMirrorService do ...@@ -135,7 +124,6 @@ describe Projects::UpdateMirrorService do
it "updates existing branches" do it "updates existing branches" do
stub_fetch_mirror(project) stub_fetch_mirror(project)
stub_find_remote_root_ref(project)
service.execute service.execute
...@@ -146,7 +134,6 @@ describe Projects::UpdateMirrorService do ...@@ -146,7 +134,6 @@ describe Projects::UpdateMirrorService do
context 'with diverged branches' do context 'with diverged branches' do
before do before do
stub_fetch_mirror(project) stub_fetch_mirror(project)
stub_find_remote_root_ref(project)
end end
context 'when mirror_overwrites_diverged_branches is true' do context 'when mirror_overwrites_diverged_branches is true' do
...@@ -187,7 +174,6 @@ describe Projects::UpdateMirrorService do ...@@ -187,7 +174,6 @@ describe Projects::UpdateMirrorService do
it 'does not add a default master branch' do it 'does not add a default master branch' do
project = create(:project_empty_repo, :mirror, import_url: Project::UNKNOWN_IMPORT_URL) project = create(:project_empty_repo, :mirror, import_url: Project::UNKNOWN_IMPORT_URL)
repository = project.repository repository = project.repository
stub_find_remote_root_ref(project)
allow(project).to receive(:fetch_mirror) { create_file(repository) } allow(project).to receive(:fetch_mirror) { create_file(repository) }
expect(CreateBranchService).not_to receive(:create_master_branch) expect(CreateBranchService).not_to receive(:create_master_branch)
...@@ -209,35 +195,8 @@ describe Projects::UpdateMirrorService do ...@@ -209,35 +195,8 @@ describe Projects::UpdateMirrorService do
end end
end end
context 'synchronize the default branch' do
it 'updates the default branch when HEAD has changed' do
stub_fetch_mirror(project)
stub_find_remote_root_ref(project, ref: 'new-branch')
expect { service.execute }.to change { project.default_branch }.from('master').to('new-branch')
end
it 'does not update the default branch when HEAD does not change' do
stub_fetch_mirror(project)
stub_find_remote_root_ref(project, ref: 'master')
expect { service.execute }.not_to change { project.default_branch }
end
it 'does not update the default branch with SSH mirrors' do
project.update(import_url: 'ssh://git@example.com/foo/bar.git')
expect(project.repository)
.not_to receive(:find_remote_root_ref)
.with('upstream')
service.execute
end
end
it "fails when the mirror user doesn't have access" do it "fails when the mirror user doesn't have access" do
stub_fetch_mirror(project) stub_fetch_mirror(project)
stub_find_remote_root_ref(project)
result = described_class.new(project, create(:user)).execute result = described_class.new(project, create(:user)).execute
...@@ -260,13 +219,6 @@ describe Projects::UpdateMirrorService do ...@@ -260,13 +219,6 @@ describe Projects::UpdateMirrorService do
end end
end end
def stub_find_remote_root_ref(project, remote: 'upstream', ref: 'master')
allow(project.repository)
.to receive(:find_remote_root_ref)
.with(remote)
.and_return(ref)
end
def stub_fetch_mirror(project, repository: project.repository) def stub_fetch_mirror(project, repository: project.repository)
allow(project).to receive(:fetch_mirror) { fetch_mirror(repository) } allow(project).to receive(:fetch_mirror) { fetch_mirror(repository) }
end end
......
...@@ -5269,9 +5269,6 @@ msgstr "" ...@@ -5269,9 +5269,6 @@ msgstr ""
msgid "No" msgid "No"
msgstr "" msgstr ""
msgid "No Assignee"
msgstr ""
msgid "No Label" msgid "No Label"
msgstr "" msgstr ""
......
...@@ -7,12 +7,6 @@ module EESpecificCheck ...@@ -7,12 +7,6 @@ module EESpecificCheck
'doc/**/*', # https://gitlab.com/gitlab-org/gitlab-ee/issues/4948#note_59945483 'doc/**/*', # https://gitlab.com/gitlab-org/gitlab-ee/issues/4948#note_59945483
'qa/**/*', # https://gitlab.com/gitlab-org/gitlab-ee/issues/4997#note_59764702 'qa/**/*', # https://gitlab.com/gitlab-org/gitlab-ee/issues/4997#note_59764702
'scripts/**/*', 'scripts/**/*',
# https://gitlab.com/gitlab-org/gitlab-ee/issues/3871
'spec/javascripts/ee/boards/models/list_spec.js',
'spec/javascripts/jobs/shared_runner_limit_block_spec.js',
'spec/javascripts/monitoring/alert_widget_form_spec.js',
'spec/javascripts/monitoring/alert_widget_spec.js',
'spec/javascripts/vue_mr_widget/ee_mr_widget_options_spec.js',
'vendor/assets/javascripts/jasmine-jquery.js', 'vendor/assets/javascripts/jasmine-jquery.js',
'.gitlab-ci.yml', '.gitlab-ci.yml',
'db/schema.rb', 'db/schema.rb',
......
...@@ -45,6 +45,8 @@ describe Dashboard::MilestonesController do ...@@ -45,6 +45,8 @@ describe Dashboard::MilestonesController do
end end
describe "#index" do describe "#index" do
render_views
it 'returns group and project milestones to which the user belongs' do it 'returns group and project milestones to which the user belongs' do
get :index, format: :json get :index, format: :json
...@@ -53,5 +55,12 @@ describe Dashboard::MilestonesController do ...@@ -53,5 +55,12 @@ describe Dashboard::MilestonesController do
expect(json_response.map { |i| i["first_milestone"]["id"] }).to match_array([group_milestone.id, project_milestone.id]) expect(json_response.map { |i| i["first_milestone"]["id"] }).to match_array([group_milestone.id, project_milestone.id])
expect(json_response.map { |i| i["group_name"] }.compact).to match_array(group.name) expect(json_response.map { |i| i["group_name"] }.compact).to match_array(group.name)
end end
it 'should contain group and project milestones to which the user belongs to' do
get :index
expect(response.body).to include("Open\n<span class=\"badge badge-pill\">3</span>")
expect(response.body).to include("Closed\n<span class=\"badge badge-pill\">0</span>")
end
end end
end end
...@@ -37,7 +37,7 @@ describe Groups::BoardsController do ...@@ -37,7 +37,7 @@ describe Groups::BoardsController do
allow(visited).to receive(:board_id).and_return(12) allow(visited).to receive(:board_id).and_return(12)
allow_any_instance_of(Boards::Visits::LatestService).to receive(:execute).and_return(visited) allow_any_instance_of(Boards::Visits::LatestService).to receive(:execute).and_return(visited)
list_boards list_boards format: :html
expect(response).to render_template :index expect(response).to render_template :index
expect(response.content_type).to eq 'text/html' expect(response.content_type).to eq 'text/html'
......
...@@ -2,13 +2,28 @@ FactoryBot.define do ...@@ -2,13 +2,28 @@ FactoryBot.define do
factory :cluster, class: Clusters::Cluster do factory :cluster, class: Clusters::Cluster do
user user
name 'test-cluster' name 'test-cluster'
cluster_type :project_type
trait :instance do
cluster_type { Clusters::Cluster.cluster_types[:instance_type] }
end
trait :project do trait :project do
cluster_type { Clusters::Cluster.cluster_types[:project_type] }
before(:create) do |cluster, evaluator| before(:create) do |cluster, evaluator|
cluster.projects << create(:project, :repository) cluster.projects << create(:project, :repository)
end end
end end
trait :group do
cluster_type { Clusters::Cluster.cluster_types[:group_type] }
before(:create) do |cluster, evalutor|
cluster.groups << create(:group)
end
end
trait :provided_by_user do trait :provided_by_user do
provider_type :user provider_type :user
platform_type :kubernetes platform_type :kubernetes
......
...@@ -106,7 +106,7 @@ describe 'Issue Boards add issue modal filtering', :js do ...@@ -106,7 +106,7 @@ describe 'Issue Boards add issue modal filtering', :js do
it 'filters by unassigned' do it 'filters by unassigned' do
set_filter('assignee') set_filter('assignee')
click_filter_link('No Assignee') click_filter_link('None')
submit_filter submit_filter
page.within('.add-issues-modal') do page.within('.add-issues-modal') do
......
...@@ -95,9 +95,9 @@ describe 'Group milestones' do ...@@ -95,9 +95,9 @@ describe 'Group milestones' do
end end
it 'counts milestones correctly' do it 'counts milestones correctly' do
expect(find('.top-area .active .badge').text).to eq("2") expect(find('.top-area .active .badge').text).to eq("3")
expect(find('.top-area .closed .badge').text).to eq("2") expect(find('.top-area .closed .badge').text).to eq("3")
expect(find('.top-area .all .badge').text).to eq("4") expect(find('.top-area .all .badge').text).to eq("6")
end end
it 'lists legacy group milestones and group milestones' do it 'lists legacy group milestones and group milestones' do
......
...@@ -156,13 +156,21 @@ describe 'Dropdown assignee', :js do ...@@ -156,13 +156,21 @@ describe 'Dropdown assignee', :js do
expect_filtered_search_input_empty expect_filtered_search_input_empty
end end
it 'selects `no assignee`' do it 'selects `None`' do
find('#js-dropdown-assignee .filter-dropdown-item', text: 'No Assignee').click find('#js-dropdown-assignee .filter-dropdown-item', text: 'None').click
expect(page).to have_css(js_dropdown_assignee, visible: false) expect(page).to have_css(js_dropdown_assignee, visible: false)
expect_tokens([assignee_token('none')]) expect_tokens([assignee_token('none')])
expect_filtered_search_input_empty expect_filtered_search_input_empty
end end
it 'selects `Any`' do
find('#js-dropdown-assignee .filter-dropdown-item', text: 'Any').click
expect(page).to have_css(js_dropdown_assignee, visible: false)
expect_tokens([assignee_token('any')])
expect_filtered_search_input_empty
end
end end
describe 'selecting from dropdown without Ajax call' do describe 'selecting from dropdown without Ajax call' do
......
...@@ -118,7 +118,7 @@ describe 'Visual tokens', :js do ...@@ -118,7 +118,7 @@ describe 'Visual tokens', :js do
describe 'selecting static option from dropdown' do describe 'selecting static option from dropdown' do
before do before do
find("#js-dropdown-assignee").find('.filter-dropdown-item', text: 'No Assignee').click find("#js-dropdown-assignee").find('.filter-dropdown-item', text: 'None').click
end end
it 'changes value in visual token' do it 'changes value in visual token' do
......
...@@ -4,7 +4,10 @@ require 'spec_helper' ...@@ -4,7 +4,10 @@ require 'spec_helper'
describe Clusters::Cluster do describe Clusters::Cluster do
it { is_expected.to belong_to(:user) } it { is_expected.to belong_to(:user) }
it { is_expected.to have_many(:cluster_projects) }
it { is_expected.to have_many(:projects) } it { is_expected.to have_many(:projects) }
it { is_expected.to have_many(:cluster_groups) }
it { is_expected.to have_many(:groups) }
it { is_expected.to have_one(:provider_gcp) } it { is_expected.to have_one(:provider_gcp) }
it { is_expected.to have_one(:platform_kubernetes) } it { is_expected.to have_one(:platform_kubernetes) }
it { is_expected.to have_one(:application_helm) } it { is_expected.to have_one(:application_helm) }
...@@ -178,6 +181,53 @@ describe Clusters::Cluster do ...@@ -178,6 +181,53 @@ describe Clusters::Cluster do
it { expect(cluster.update(enabled: false)).to be_truthy } it { expect(cluster.update(enabled: false)).to be_truthy }
end end
end end
describe 'cluster_type validations' do
let(:instance_cluster) { create(:cluster, :instance) }
let(:group_cluster) { create(:cluster, :group) }
let(:project_cluster) { create(:cluster, :project) }
it 'validates presence' do
cluster = build(:cluster, :project, cluster_type: nil)
expect(cluster).not_to be_valid
expect(cluster.errors.full_messages).to include("Cluster type can't be blank")
end
context 'project_type cluster' do
it 'does not allow setting group' do
project_cluster.groups << build(:group)
expect(project_cluster).not_to be_valid
expect(project_cluster.errors.full_messages).to include('Cluster cannot have groups assigned')
end
end
context 'group_type cluster' do
it 'does not allow setting project' do
group_cluster.projects << build(:project)
expect(group_cluster).not_to be_valid
expect(group_cluster.errors.full_messages).to include('Cluster cannot have projects assigned')
end
end
context 'instance_type cluster' do
it 'does not allow setting group' do
instance_cluster.groups << build(:group)
expect(instance_cluster).not_to be_valid
expect(instance_cluster.errors.full_messages).to include('Cluster cannot have groups assigned')
end
it 'does not allow setting project' do
instance_cluster.projects << build(:project)
expect(instance_cluster).not_to be_valid
expect(instance_cluster.errors.full_messages).to include('Cluster cannot have projects assigned')
end
end
end
end end
describe '#provider' do describe '#provider' do
...@@ -229,6 +279,23 @@ describe Clusters::Cluster do ...@@ -229,6 +279,23 @@ describe Clusters::Cluster do
end end
end end
describe '#group' do
subject { cluster.group }
context 'when cluster belongs to a group' do
let(:cluster) { create(:cluster, :group) }
let(:group) { cluster.groups.first }
it { is_expected.to eq(group) }
end
context 'when cluster does not belong to any group' do
let(:cluster) { create(:cluster) }
it { is_expected.to be_nil }
end
end
describe '#applications' do describe '#applications' do
set(:cluster) { create(:cluster) } set(:cluster) { create(:cluster) }
......
# frozen_string_literal: true
require 'spec_helper'
describe Clusters::Group do
it { is_expected.to belong_to(:cluster) }
it { is_expected.to belong_to(:group) }
end
...@@ -92,41 +92,6 @@ describe GlobalMilestone do ...@@ -92,41 +92,6 @@ describe GlobalMilestone do
end end
end end
describe '.states_count' do
context 'when the projects have milestones' do
before do
create(:closed_milestone, title: 'Active Group Milestone', project: project3)
create(:active_milestone, title: 'Active Group Milestone', project: project1)
create(:active_milestone, title: 'Active Group Milestone', project: project2)
create(:closed_milestone, title: 'Closed Group Milestone', project: project1)
create(:closed_milestone, title: 'Closed Group Milestone', project: project2)
create(:closed_milestone, title: 'Closed Group Milestone', project: project3)
end
it 'returns the quantity of global milestones in each possible state' do
expected_count = { opened: 1, closed: 2, all: 2 }
count = described_class.states_count(Project.all)
expect(count).to eq(expected_count)
end
end
context 'when the projects do not have milestones' do
before do
project1
end
it 'returns 0 as the quantity of global milestones in each state' do
expected_count = { opened: 0, closed: 0, all: 0 }
count = described_class.states_count(Project.all)
expect(count).to eq(expected_count)
end
end
end
describe '#initialize' do describe '#initialize' do
let(:milestone1_project1) { create(:milestone, title: "Milestone v1.2", project: project1) } let(:milestone1_project1) { create(:milestone, title: "Milestone v1.2", project: project1) }
let(:milestone1_project2) { create(:milestone, title: "Milestone v1.2", project: project2) } let(:milestone1_project2) { create(:milestone, title: "Milestone v1.2", project: project2) }
......
...@@ -19,6 +19,8 @@ describe Group do ...@@ -19,6 +19,8 @@ describe Group do
it { is_expected.to have_one(:chat_team) } it { is_expected.to have_one(:chat_team) }
it { is_expected.to have_many(:custom_attributes).class_name('GroupCustomAttribute') } it { is_expected.to have_many(:custom_attributes).class_name('GroupCustomAttribute') }
it { is_expected.to have_many(:badges).class_name('GroupBadge') } it { is_expected.to have_many(:badges).class_name('GroupBadge') }
it { is_expected.to have_many(:cluster_groups).class_name('Clusters::Group') }
it { is_expected.to have_many(:clusters).class_name('Clusters::Cluster') }
describe '#members & #requesters' do describe '#members & #requesters' do
let(:requester) { create(:user) } let(:requester) { create(:user) }
......
...@@ -350,4 +350,41 @@ describe Milestone do ...@@ -350,4 +350,41 @@ describe Milestone do
end end
end end
end end
describe '.states_count' do
context 'when the projects have milestones' do
before do
project_1 = create(:project)
project_2 = create(:project)
group_1 = create(:group)
group_2 = create(:group)
create(:active_milestone, title: 'Active Group Milestone', project: project_1)
create(:closed_milestone, title: 'Closed Group Milestone', project: project_1)
create(:active_milestone, title: 'Active Group Milestone', project: project_2)
create(:closed_milestone, title: 'Closed Group Milestone', project: project_2)
create(:closed_milestone, title: 'Active Group Milestone', group: group_1)
create(:closed_milestone, title: 'Closed Group Milestone', group: group_1)
create(:closed_milestone, title: 'Active Group Milestone', group: group_2)
create(:closed_milestone, title: 'Closed Group Milestone', group: group_2)
end
it 'returns the quantity of milestones in each possible state' do
expected_count = { opened: 5, closed: 6, all: 11 }
count = described_class.states_count(Project.all, Group.all)
expect(count).to eq(expected_count)
end
end
context 'when the projects do not have milestones' do
it 'returns 0 as the quantity of global milestones in each state' do
expected_count = { opened: 0, closed: 0, all: 0 }
count = described_class.states_count([project])
expect(count).to eq(expected_count)
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