diff options
author | Clement Ho <clemmakesapps@gmail.com> | 2018-05-11 13:51:58 +0000 |
---|---|---|
committer | Clement Ho <clemmakesapps@gmail.com> | 2018-05-11 13:51:58 +0000 |
commit | 15cdc8bfc833fabd187d2ac9e49ed0c7ce9b9610 (patch) | |
tree | 8e487cd40543af62aa42dc0dbe50b894a2bbec8c | |
parent | 43f83ba76e8a582845b2aabcc607b350caea112e (diff) | |
parent | 8c2b73dc8c5adf41b94033fe1d0a265524e304c4 (diff) | |
download | gitlab-ce-15cdc8bfc833fabd187d2ac9e49ed0c7ce9b9610.tar.gz |
Merge branch 'master' into 'bootstrap4'
# Conflicts:
# app/views/groups/group_members/index.html.haml
44 files changed, 500 insertions, 157 deletions
@@ -6,7 +6,7 @@ end gem_versions = {} gem_versions['activerecord_sane_schema_dumper'] = rails5? ? '1.0' : '0.2' gem_versions['default_value_for'] = rails5? ? '~> 3.0.5' : '~> 3.0.0' -gem_versions['rails'] = rails5? ? '5.0.6' : '4.2.10' +gem_versions['rails'] = rails5? ? '5.0.7' : '4.2.10' gem_versions['rails-i18n'] = rails5? ? '~> 5.1' : '~> 4.0.9' # --- The end of special code for migrating to Rails 5.0 --- diff --git a/Gemfile.rails5.lock b/Gemfile.rails5.lock index 3056b97ccd5..fc6dfd040c2 100644 --- a/Gemfile.rails5.lock +++ b/Gemfile.rails5.lock @@ -4,43 +4,43 @@ GEM RedCloth (4.3.2) abstract_type (0.0.7) ace-rails-ap (4.1.4) - actioncable (5.0.6) - actionpack (= 5.0.6) + actioncable (5.0.7) + actionpack (= 5.0.7) nio4r (>= 1.2, < 3.0) websocket-driver (~> 0.6.1) - actionmailer (5.0.6) - actionpack (= 5.0.6) - actionview (= 5.0.6) - activejob (= 5.0.6) + actionmailer (5.0.7) + actionpack (= 5.0.7) + actionview (= 5.0.7) + activejob (= 5.0.7) mail (~> 2.5, >= 2.5.4) rails-dom-testing (~> 2.0) - actionpack (5.0.6) - actionview (= 5.0.6) - activesupport (= 5.0.6) + actionpack (5.0.7) + actionview (= 5.0.7) + activesupport (= 5.0.7) rack (~> 2.0) rack-test (~> 0.6.3) rails-dom-testing (~> 2.0) rails-html-sanitizer (~> 1.0, >= 1.0.2) - actionview (5.0.6) - activesupport (= 5.0.6) + actionview (5.0.7) + activesupport (= 5.0.7) builder (~> 3.1) erubis (~> 2.7.0) rails-dom-testing (~> 2.0) rails-html-sanitizer (~> 1.0, >= 1.0.3) - activejob (5.0.6) - activesupport (= 5.0.6) + activejob (5.0.7) + activesupport (= 5.0.7) globalid (>= 0.3.6) - activemodel (5.0.6) - activesupport (= 5.0.6) - activerecord (5.0.6) - activemodel (= 5.0.6) - activesupport (= 5.0.6) + activemodel (5.0.7) + activesupport (= 5.0.7) + activerecord (5.0.7) + activemodel (= 5.0.7) + activesupport (= 5.0.7) arel (~> 7.0) activerecord_sane_schema_dumper (1.0) rails (>= 5, < 6) - activesupport (5.0.6) + activesupport (5.0.7) concurrent-ruby (~> 1.0, >= 1.0.2) - i18n (~> 0.7) + i18n (>= 0.7, < 2) minitest (~> 5.1) tzinfo (~> 1.1) acts-as-taggable-on (5.0.0) @@ -62,13 +62,13 @@ GEM asciidoctor (1.5.6.1) asciidoctor-plantuml (0.0.8) asciidoctor (~> 1.5) - asset_sync (2.2.0) + asset_sync (2.4.0) activemodel (>= 4.1.0) fog-core mime-types (>= 2.99) unf ast (2.4.0) - atomic (1.1.100) + atomic (1.1.99) attr_encrypted (3.1.0) encryptor (~> 3.0.0) attr_required (1.0.1) @@ -144,12 +144,10 @@ GEM connection_pool (2.2.1) crack (0.4.3) safe_yaml (~> 1.0.0) - crass (1.0.3) + crass (1.0.4) creole (0.5.0) css_parser (1.6.0) addressable - d3_rails (3.5.17) - railties (>= 3.1.0) daemons (1.2.6) database_cleaner (1.5.3) debug_inspector (0.0.3) @@ -292,7 +290,7 @@ GEM po_to_json (>= 1.0.0) rails (>= 3.2.0) gherkin-ruby (0.3.2) - gitaly-proto (0.97.0) + gitaly-proto (0.99.0) google-protobuf (~> 3.1) grpc (~> 1.10) github-linguist (5.3.3) @@ -335,9 +333,8 @@ GEM activesupport (>= 4.2.0) gollum-grit_adapter (1.0.1) gitlab-grit (~> 2.7, >= 2.7.1) - gon (6.1.0) + gon (6.2.0) actionpack (>= 3.0) - json multi_json request_store (>= 1.0) google-api-client (0.19.8) @@ -367,8 +364,8 @@ GEM rack (>= 1.3.0) rack-accept virtus (>= 1.0.0) - grape-entity (0.6.1) - activesupport (>= 5.0.0) + grape-entity (0.7.1) + activesupport (>= 4.0) multi_json (>= 1.3.2) grape-route-helpers (2.1.0) activesupport @@ -420,7 +417,7 @@ GEM json (~> 1.8) multi_xml (>= 0.5.2) httpclient (2.8.3) - i18n (0.9.5) + i18n (1.0.1) concurrent-ruby (~> 1.0) ice_nine (0.11.2) influxdb (0.5.3) @@ -515,7 +512,7 @@ GEM net-ldap (0.16.1) net-ssh (4.2.0) netrc (0.11.0) - nio4r (2.2.0) + nio4r (2.3.1) nokogiri (1.8.2) mini_portile2 (~> 2.3.0) numerizer (0.1.1) @@ -545,9 +542,9 @@ GEM omniauth (~> 1.2) omniauth-facebook (4.0.0) omniauth-oauth2 (~> 1.2) - omniauth-github (1.1.2) - omniauth (~> 1.0) - omniauth-oauth2 (~> 1.1) + omniauth-github (1.3.0) + omniauth (~> 1.5) + omniauth-oauth2 (>= 1.4.0, < 2.0) omniauth-gitlab (1.0.3) omniauth (~> 1.0) omniauth-oauth2 (~> 1.0) @@ -633,7 +630,7 @@ GEM parser unparser procto (0.0.3) - prometheus-client-mmap (0.9.1) + prometheus-client-mmap (0.9.2) pry (0.11.3) coderay (~> 1.1.0) method_source (~> 0.9.0) @@ -644,7 +641,7 @@ GEM pry (>= 0.10.4) public_suffix (3.0.2) pyu-ruby-sasl (0.0.3.3) - rack (2.0.4) + rack (2.0.5) rack-accept (0.4.5) rack (>= 0.4) rack-attack (4.4.1) @@ -662,17 +659,17 @@ GEM rack rack-test (0.6.3) rack (>= 1.0) - rails (5.0.6) - actioncable (= 5.0.6) - actionmailer (= 5.0.6) - actionpack (= 5.0.6) - actionview (= 5.0.6) - activejob (= 5.0.6) - activemodel (= 5.0.6) - activerecord (= 5.0.6) - activesupport (= 5.0.6) + rails (5.0.7) + actioncable (= 5.0.7) + actionmailer (= 5.0.7) + actionpack (= 5.0.7) + actionview (= 5.0.7) + activejob (= 5.0.7) + activemodel (= 5.0.7) + activerecord (= 5.0.7) + activesupport (= 5.0.7) bundler (>= 1.3.0) - railties (= 5.0.6) + railties (= 5.0.7) sprockets-rails (>= 2.0.0) rails-controller-testing (1.0.2) actionpack (~> 5.x, >= 5.0.1) @@ -683,21 +680,21 @@ GEM rails-dom-testing (2.0.3) activesupport (>= 4.2.0) nokogiri (>= 1.6) - rails-html-sanitizer (1.0.3) - loofah (~> 2.0) + rails-html-sanitizer (1.0.4) + loofah (~> 2.2, >= 2.2.2) rails-i18n (5.1.1) i18n (>= 0.7, < 2) railties (>= 5.0, < 6) - railties (5.0.6) - actionpack (= 5.0.6) - activesupport (= 5.0.6) + railties (5.0.7) + actionpack (= 5.0.7) + activesupport (= 5.0.7) method_source rake (>= 0.8.7) thor (>= 0.18.1, < 2.0) rainbow (2.2.2) rake raindrops (0.19.0) - rake (12.3.0) + rake (12.3.1) rb-fsevent (0.10.3) rb-inotify (0.9.10) ffi (>= 0.5.0, < 2) @@ -1001,7 +998,7 @@ DEPENDENCIES asana (~> 0.6.0) asciidoctor (~> 1.5.6) asciidoctor-plantuml (= 0.0.8) - asset_sync (~> 2.2.0) + asset_sync (~> 2.4) attr_encrypted (~> 3.1.0) awesome_print (~> 1.2.0) babosa (~> 1.0.2) @@ -1027,12 +1024,11 @@ DEPENDENCIES concurrent-ruby (~> 1.0.5) connection_pool (~> 2.0) creole (~> 0.5.0) - d3_rails (~> 3.5.0) database_cleaner (~> 1.5.0) deckar01-task_list (= 2.0.0) default_value_for (~> 3.0.5) device_detector - devise (~> 4.2) + devise (~> 4.4) devise-two-factor (~> 3.0.0) diffy (~> 3.1.0) doorkeeper (~> 4.3) @@ -1063,7 +1059,7 @@ DEPENDENCIES gettext (~> 3.2.2) gettext_i18n_rails (~> 1.8.0) gettext_i18n_rails_js (~> 1.3) - gitaly-proto (~> 0.97.0) + gitaly-proto (~> 0.99.0) github-linguist (~> 5.3.3) gitlab-flowdock-git-hook (~> 1.0.1) gitlab-gollum-lib (~> 4.2) @@ -1071,12 +1067,12 @@ DEPENDENCIES gitlab-markup (~> 1.6.2) gitlab-styles (~> 2.3) gitlab_omniauth-ldap (~> 2.0.4) - gon (~> 6.1.0) + gon (~> 6.2) google-api-client (~> 0.19.8) google-protobuf (= 3.5.1) gpgme grape (~> 1.0) - grape-entity (~> 0.6.0) + grape-entity (~> 0.7.1) grape-route-helpers (~> 2.1.0) grape_logging (~> 1.7) grpc (~> 1.11.0) @@ -1117,7 +1113,7 @@ DEPENDENCIES omniauth-azure-oauth2 (~> 0.0.9) omniauth-cas3 (~> 1.1.4) omniauth-facebook (~> 4.0.0) - omniauth-github (~> 1.1.1) + omniauth-github (~> 1.3) omniauth-gitlab (~> 1.0.2) omniauth-google-oauth2 (~> 0.5.3) omniauth-kerberos (~> 0.3.0) @@ -1136,14 +1132,14 @@ DEPENDENCIES peek-sidekiq (~> 1.0.3) pg (~> 0.18.2) premailer-rails (~> 1.9.7) - prometheus-client-mmap (~> 0.9.1) + prometheus-client-mmap (~> 0.9.2) pry-byebug (~> 3.4.1) pry-rails (~> 0.3.4) rack-attack (~> 4.4.1) rack-cors (~> 1.0.0) rack-oauth2 (~> 1.2.1) rack-proxy (~> 0.6.0) - rails (= 5.0.6) + rails (= 5.0.7) rails-controller-testing rails-deprecated_sanitizer (~> 1.0.3) rails-i18n (~> 5.1) diff --git a/app/assets/javascripts/ide/components/repo_editor.vue b/app/assets/javascripts/ide/components/repo_editor.vue index 8e5ddedbb78..566aad801d1 100644 --- a/app/assets/javascripts/ide/components/repo_editor.vue +++ b/app/assets/javascripts/ide/components/repo_editor.vue @@ -140,7 +140,7 @@ export default { this.file.staged && this.file.key.indexOf('unstaged-') === 0 ? head : null, ); - if (this.viewer === viewerTypes.mr) { + if (this.viewer === viewerTypes.mr && this.file.mrChange) { this.editor.attachMergeRequestModel(this.model); } else { this.editor.attachModel(this.model); diff --git a/app/assets/javascripts/ide/stores/utils.js b/app/assets/javascripts/ide/stores/utils.js index bc79ff4a542..e0b9766fbee 100644 --- a/app/assets/javascripts/ide/stores/utils.js +++ b/app/assets/javascripts/ide/stores/utils.js @@ -44,6 +44,7 @@ export const dataStructure = () => ({ size: 0, parentPath: null, lastOpenedAt: 0, + mrChange: null, }); export const decorateData = entity => { diff --git a/app/assets/javascripts/registry/components/table_registry.vue b/app/assets/javascripts/registry/components/table_registry.vue index 90f2493724d..36215a6f845 100644 --- a/app/assets/javascripts/registry/components/table_registry.vue +++ b/app/assets/javascripts/registry/components/table_registry.vue @@ -111,7 +111,13 @@ </td> <td> - {{ timeFormated(item.createdAt) }} + <span + v-tooltip + :title="tooltipTitle(item.createdAt)" + data-placement="bottom" + > + {{ timeFormated(item.createdAt) }} + </span> </td> <td class="content"> diff --git a/app/assets/javascripts/user_callout.js b/app/assets/javascripts/user_callout.js index 97d5cf96bcb..96dfff77859 100644 --- a/app/assets/javascripts/user_callout.js +++ b/app/assets/javascripts/user_callout.js @@ -15,7 +15,7 @@ export default class UserCallout { init() { if (!this.isCalloutDismissed || this.isCalloutDismissed === 'false') { - $('.js-close-callout').on('click', e => this.dismissCallout(e)); + this.userCalloutBody.find('.js-close-callout').on('click', e => this.dismissCallout(e)); } } @@ -23,12 +23,15 @@ export default class UserCallout { const $currentTarget = $(e.currentTarget); if (this.options.setCalloutPerProject) { - Cookies.set(this.cookieName, 'true', { expires: 365, path: this.userCalloutBody.data('projectPath') }); + Cookies.set(this.cookieName, 'true', { + expires: 365, + path: this.userCalloutBody.data('projectPath'), + }); } else { Cookies.set(this.cookieName, 'true', { expires: 365 }); } - if ($currentTarget.hasClass('close')) { + if ($currentTarget.hasClass('close') || $currentTarget.hasClass('js-close')) { this.userCalloutBody.remove(); } } diff --git a/app/assets/javascripts/vue_merge_request_widget/components/deployment.vue b/app/assets/javascripts/vue_merge_request_widget/components/deployment.vue index 7bc13d428a8..1608858da22 100644 --- a/app/assets/javascripts/vue_merge_request_widget/components/deployment.vue +++ b/app/assets/javascripts/vue_merge_request_widget/components/deployment.vue @@ -109,12 +109,12 @@ export default { rel="noopener noreferrer nofollow" class="deploy-link js-deploy-url" > + {{ deployment.external_url_formatted }} <i class="fa fa-external-link" aria-hidden="true" > </i> - {{ deployment.external_url_formatted }} </a> </template> <span diff --git a/app/assets/stylesheets/framework/mixins.scss b/app/assets/stylesheets/framework/mixins.scss index e12b5aab381..9ff24ebc127 100644 --- a/app/assets/stylesheets/framework/mixins.scss +++ b/app/assets/stylesheets/framework/mixins.scss @@ -17,6 +17,16 @@ */ @mixin markdown-table { width: auto; + display: inline-block; + overflow-x: auto; + border-left: 0; + border-right: 0; + border-bottom: 0; + + @supports(width: fit-content) { + display: block; + width: fit-content; + } } /* diff --git a/app/assets/stylesheets/framework/terms.scss b/app/assets/stylesheets/framework/terms.scss index 16293d32dfa..744fd0ff796 100644 --- a/app/assets/stylesheets/framework/terms.scss +++ b/app/assets/stylesheets/framework/terms.scss @@ -17,6 +17,7 @@ display: flex; align-items: center; justify-content: space-between; + line-height: $line-height-base; .title { display: flex; @@ -33,10 +34,14 @@ .navbar-collapse { padding-right: 0; + + .navbar-nav { + margin: 0; + } } - .nav li a { - color: $theme-gray-700; + .nav li { + float: none; } } diff --git a/app/assets/stylesheets/pages/wiki.scss b/app/assets/stylesheets/pages/wiki.scss index 89788933085..800f5c68e39 100644 --- a/app/assets/stylesheets/pages/wiki.scss +++ b/app/assets/stylesheets/pages/wiki.scss @@ -180,11 +180,6 @@ ul.wiki-pages-list.content-list { } } -.wiki-holder { - overflow-x: auto; - overflow-y: hidden; -} - .wiki { table { @include markdown-table; diff --git a/app/controllers/groups/group_members_controller.rb b/app/controllers/groups/group_members_controller.rb index 134b0dfc0db..ef3eba80154 100644 --- a/app/controllers/groups/group_members_controller.rb +++ b/app/controllers/groups/group_members_controller.rb @@ -11,13 +11,20 @@ class Groups::GroupMembersController < Groups::ApplicationController :override def index + can_manage_members = can?(current_user, :admin_group_member, @group) + @sort = params[:sort].presence || sort_value_name @project = @group.projects.find(params[:project_id]) if params[:project_id] @members = GroupMembersFinder.new(@group).execute - @members = @members.non_invite unless can?(current_user, :admin_group, @group) + @members = @members.non_invite unless can_manage_members @members = @members.search(params[:search]) if params[:search].present? @members = @members.sort_by_attribute(@sort) + + if can_manage_members && params[:two_factor].present? + @members = @members.filter_by_2fa(params[:two_factor]) + end + @members = @members.page(params[:page]).per(50) @members = present_members(@members.includes(:user)) diff --git a/app/models/ci/runner.rb b/app/models/ci/runner.rb index ed8b30dae49..bda69f85a78 100644 --- a/app/models/ci/runner.rb +++ b/app/models/ci/runner.rb @@ -108,7 +108,13 @@ module Ci end def assign_to(project, current_user = nil) - self.is_shared = false if shared? + if shared? + self.is_shared = false if shared? + self.runner_type = :project_type + elsif group_type? + raise ArgumentError, 'Transitioning a group runner to a project runner is not supported' + end + self.save project.runner_projects.create(runner_id: self.id) end diff --git a/app/models/concerns/routable.rb b/app/models/concerns/routable.rb index 915ad6959be..0176a12a131 100644 --- a/app/models/concerns/routable.rb +++ b/app/models/concerns/routable.rb @@ -4,7 +4,9 @@ module Routable extend ActiveSupport::Concern included do - has_one :route, as: :source, autosave: true, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent + # Remove `inverse_of: source` when upgraded to rails 5.2 + # See https://github.com/rails/rails/pull/28808 + has_one :route, as: :source, autosave: true, dependent: :destroy, inverse_of: :source # rubocop:disable Cop/ActiveRecordDependent has_many :redirect_routes, as: :source, autosave: true, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent validates :route, presence: true diff --git a/app/models/concerns/sha_attribute.rb b/app/models/concerns/sha_attribute.rb index 3340dc96e9f..3796737427a 100644 --- a/app/models/concerns/sha_attribute.rb +++ b/app/models/concerns/sha_attribute.rb @@ -22,7 +22,8 @@ module ShaAttribute column = columns.find { |c| c.name == name.to_s } unless column - raise ArgumentError.new("sha_attribute #{name.inspect} is invalid since the column doesn't exist") + warn "WARNING: sha_attribute #{name.inspect} is invalid since the column doesn't exist - you may need to run database migrations" + return end unless column.type == :binary diff --git a/app/models/member.rb b/app/models/member.rb index eac4a22a03f..68572f2e33a 100644 --- a/app/models/member.rb +++ b/app/models/member.rb @@ -96,6 +96,17 @@ class Member < ActiveRecord::Base joins(:user).merge(User.search(query)) end + def filter_by_2fa(value) + case value + when 'enabled' + left_join_users.merge(User.with_two_factor_indistinct) + when 'disabled' + left_join_users.merge(User.without_two_factor) + else + all + end + end + def sort_by_attribute(method) case method.to_s when 'access_level_asc' then reorder(access_level: :asc) diff --git a/app/models/user.rb b/app/models/user.rb index a9cfd39f604..d74d5aade5a 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -237,14 +237,18 @@ class User < ActiveRecord::Base scope :order_recent_sign_in, -> { reorder(Gitlab::Database.nulls_last_order('current_sign_in_at', 'DESC')) } scope :order_oldest_sign_in, -> { reorder(Gitlab::Database.nulls_last_order('current_sign_in_at', 'ASC')) } - def self.with_two_factor + def self.with_two_factor_indistinct joins("LEFT OUTER JOIN u2f_registrations AS u2f ON u2f.user_id = users.id") - .where("u2f.id IS NOT NULL OR otp_required_for_login = ?", true).distinct(arel_table[:id]) + .where("u2f.id IS NOT NULL OR users.otp_required_for_login = ?", true) + end + + def self.with_two_factor + with_two_factor_indistinct.distinct(arel_table[:id]) end def self.without_two_factor joins("LEFT OUTER JOIN u2f_registrations AS u2f ON u2f.user_id = users.id") - .where("u2f.id IS NULL AND otp_required_for_login = ?", false) + .where("u2f.id IS NULL AND users.otp_required_for_login = ?", false) end # diff --git a/app/views/groups/group_members/index.html.haml b/app/views/groups/group_members/index.html.haml index 685178ca9d2..6a0321bcd2b 100644 --- a/app/views/groups/group_members/index.html.haml +++ b/app/views/groups/group_members/index.html.haml @@ -1,10 +1,11 @@ - page_title "Members" +- can_manage_members = can?(current_user, :admin_group_member, @group) .project-members-page.prepend-top-default %h4 Members %hr - - if can?(current_user, :admin_group_member, @group) + - if can_manage_members .project-members-new.append-bottom-default %p.clearfix Add new member to @@ -13,20 +14,23 @@ = render 'shared/members/requests', membership_source: @group, requesters: @requesters - .append-bottom-default.clearfix + .clearfix %h5.member.existing-title Existing members - = form_tag group_group_members_path(@group), method: :get, class: 'form-inline member-search-form' do - .form-group - = search_field_tag :search, params[:search], { placeholder: 'Find existing members by name', class: 'form-control', spellcheck: false } - %button.member-search-btn{ type: "submit", "aria-label" => "Submit search" } - = icon("search") - = render 'shared/members/sort_dropdown' .card - .card-header - Members with access to - %strong= @group.name - %span.badge.badge-pill= @members.total_count + .card-header.flex-project-members-panel + %span.flex-project-title + Members with access to + %strong= @group.name + %span.badge= @members.total_count + = form_tag group_group_members_path(@group), method: :get, class: 'form-inline member-search-form flex-project-members-form' do + .form-group + = search_field_tag :search, params[:search], { placeholder: 'Find existing members by name', class: 'form-control', spellcheck: false } + %button.member-search-btn{ type: "submit", "aria-label" => "Submit search" } + = icon("search") + - if can_manage_members + = render 'shared/members/filter_2fa_dropdown' + = render 'shared/members/sort_dropdown' %ul.content-list.members-list = render partial: 'shared/members/member', collection: @members, as: :member = paginate @members, theme: 'gitlab' diff --git a/app/views/groups/issues.html.haml b/app/views/groups/issues.html.haml index bbfbea4ac7a..662db18cf86 100644 --- a/app/views/groups/issues.html.haml +++ b/app/views/groups/issues.html.haml @@ -8,7 +8,7 @@ .top-area = render 'shared/issuable/nav', type: :issues .nav-controls - = link_to params.merge(rss_url_options), class: 'btn' do + = link_to safe_params.merge(rss_url_options), class: 'btn' do = icon('rss') %span.icon-label Subscribe diff --git a/app/views/layouts/terms.html.haml b/app/views/layouts/terms.html.haml index d670147226d..2a6927b9962 100644 --- a/app/views/layouts/terms.html.haml +++ b/app/views/layouts/terms.html.haml @@ -20,10 +20,10 @@ = brand_header_logo - logo_text = brand_header_logo_type - if logo_text.present? - %span.logo-text.hidden-xs.prepend-left-8 + %span.logo-text.prepend-left-8 = logo_text - if header_link?(:user_dropdown) - .navbar-collapse.collapse + .navbar-collapse %ul.nav.navbar-nav %li.header-user.dropdown = link_to current_user, class: user_dropdown_class, data: { toggle: "dropdown" } do diff --git a/app/views/projects/_wiki.html.haml b/app/views/projects/_wiki.html.haml index a56c3503c77..5646dc464f8 100644 --- a/app/views/projects/_wiki.html.haml +++ b/app/views/projects/_wiki.html.haml @@ -1,6 +1,6 @@ - if @wiki_home.present? %div{ class: container_class } - .wiki-holder.prepend-top-default.append-bottom-default + .prepend-top-default.append-bottom-default .wiki = render_wiki_content(@wiki_home) - else diff --git a/app/views/projects/wikis/show.html.haml b/app/views/projects/wikis/show.html.haml index b3b83cee81a..ff72c8bb75d 100644 --- a/app/views/projects/wikis/show.html.haml +++ b/app/views/projects/wikis/show.html.haml @@ -24,7 +24,7 @@ - history_link = link_to s_("WikiHistoricalPage|history"), project_wiki_history_path(@project, @page) = (s_("WikiHistoricalPage|You can view the %{most_recent_link} or browse the %{history_link}.") % { most_recent_link: most_recent_link, history_link: history_link }).html_safe -.wiki-holder.prepend-top-default.append-bottom-default +.prepend-top-default.append-bottom-default .wiki = render_wiki_content(@page) diff --git a/app/views/shared/members/_filter_2fa_dropdown.html.haml b/app/views/shared/members/_filter_2fa_dropdown.html.haml new file mode 100644 index 00000000000..95c35c56b3c --- /dev/null +++ b/app/views/shared/members/_filter_2fa_dropdown.html.haml @@ -0,0 +1,11 @@ +- filter = params[:two_factor] || 'everyone' +- filter_options = { 'everyone' => 'Everyone', 'enabled' => 'Enabled', 'disabled' => 'Disabled' } +.dropdown.inline.member-filter-2fa-dropdown + = dropdown_toggle('2FA: ' + filter_options[filter], { toggle: 'dropdown' }) + %ul.dropdown-menu.dropdown-menu-align-right.dropdown-menu-selectable + %li.dropdown-header + Filter by two-factor authentication + - filter_options.each do |value, title| + %li + = link_to filter_group_project_member_path(two_factor: value), class: ("is-active" if filter == value) do + = title diff --git a/app/views/shared/members/_member.html.haml b/app/views/shared/members/_member.html.haml index 8e5cfc89df7..21d02cca209 100644 --- a/app/views/shared/members/_member.html.haml +++ b/app/views/shared/members/_member.html.haml @@ -20,6 +20,10 @@ %label.badge.badge-danger %strong Blocked + - if user.two_factor_enabled? + %label.label.label-info + 2FA + - if source.instance_of?(Group) && source != @group · = link_to source.full_name, source, class: "member-group-link" diff --git a/changelogs/unreleased/40725-move-mr-external-link-to-right.yml b/changelogs/unreleased/40725-move-mr-external-link-to-right.yml new file mode 100644 index 00000000000..e3ebeb5eb61 --- /dev/null +++ b/changelogs/unreleased/40725-move-mr-external-link-to-right.yml @@ -0,0 +1,5 @@ +--- +title: Moves MR widget external link icon to the right +merge_request: 18828 +author: Jacopo Beschi @jacopo-beschi +type: changed diff --git a/changelogs/unreleased/46210-terms-acceptance-dropdown-menu.yml b/changelogs/unreleased/46210-terms-acceptance-dropdown-menu.yml new file mode 100644 index 00000000000..8a7c549e356 --- /dev/null +++ b/changelogs/unreleased/46210-terms-acceptance-dropdown-menu.yml @@ -0,0 +1,5 @@ +--- +title: 46210 Display logo and user dropdown on mobile for terms page and fix styling +merge_request: +author: +type: fixed diff --git a/changelogs/unreleased/docs-42067-document-runner-registration-api.yml b/changelogs/unreleased/docs-42067-document-runner-registration-api.yml new file mode 100644 index 00000000000..6b507174044 --- /dev/null +++ b/changelogs/unreleased/docs-42067-document-runner-registration-api.yml @@ -0,0 +1,5 @@ +--- +title: Expand documentation for Runners API +merge_request: 16484 +author: +type: other diff --git a/changelogs/unreleased/dz-add-2fa-filter.yml b/changelogs/unreleased/dz-add-2fa-filter.yml new file mode 100644 index 00000000000..82d501d6604 --- /dev/null +++ b/changelogs/unreleased/dz-add-2fa-filter.yml @@ -0,0 +1,5 @@ +--- +title: Add 2FA filter to the group members page +merge_request: 18483 +author: +type: changed diff --git a/changelogs/unreleased/fix-registry-created-at-tooltip.yml b/changelogs/unreleased/fix-registry-created-at-tooltip.yml new file mode 100644 index 00000000000..911b3b10fd4 --- /dev/null +++ b/changelogs/unreleased/fix-registry-created-at-tooltip.yml @@ -0,0 +1,5 @@ +--- +title: 'Add missing tooltip to creation date on container registry overview' +merge_request: 18767 +author: Lars Greiss +type: fixed diff --git a/changelogs/unreleased/zj-wiki-find-file-opt-out.yml b/changelogs/unreleased/zj-wiki-find-file-opt-out.yml new file mode 100644 index 00000000000..5af53c56017 --- /dev/null +++ b/changelogs/unreleased/zj-wiki-find-file-opt-out.yml @@ -0,0 +1,5 @@ +--- +title: Finding a wiki page is done by Gitaly by default +merge_request: +author: +type: other diff --git a/config/initializers/6_validations.rb b/config/initializers/6_validations.rb index d92cdb97766..89aabe530fe 100644 --- a/config/initializers/6_validations.rb +++ b/config/initializers/6_validations.rb @@ -26,17 +26,6 @@ def validate_storages_config Gitlab.config.repositories.storages.each do |name, repository_storage| storage_validation_error("\"#{name}\" is not a valid storage name") unless storage_name_valid?(name) - if repository_storage.is_a?(String) - raise "#{name} is not a valid storage, because it has no `path` key. " \ - "It may be configured as:\n\n#{name}:\n path: #{repository_storage}\n\n" \ - "For source installations, update your config/gitlab.yml Refer to gitlab.yml.example for an updated example.\n\n" \ - "If you're using the Gitlab Development Kit, you can update your configuration running `gdk reconfigure`.\n" - end - - if !repository_storage.is_a?(Gitlab::GitalyClient::StorageSettings) || repository_storage.legacy_disk_path.nil? - storage_validation_error("#{name} is not a valid storage, because it has no `path` key. Refer to gitlab.yml.example for an updated example") - end - %w(failure_count_threshold failure_reset_time storage_timeout).each do |setting| # Falling back to the defaults is fine! next if repository_storage[setting].nil? diff --git a/doc/api/runners.md b/doc/api/runners.md index f384ac57bfe..3ca07ce9795 100644 --- a/doc/api/runners.md +++ b/doc/api/runners.md @@ -411,3 +411,86 @@ DELETE /projects/:id/runners/:runner_id ``` curl --request DELETE --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v4/projects/9/runners/9" ``` + +## Register a new Runner + +Register a new Runner for the instance. + +``` +POST /runners +``` + +| Attribute | Type | Required | Description | +|-------------|---------|----------|---------------------| +| `token` | string | yes | Registration token ([Read how to obtain a token](../ci/runners/README.md)) | +| `description`| string | no | Runner's description| +| `info` | hash | no | Runner's metadata | +| `active` | boolean| no | Whether the Runner is active | +| `locked` | boolean| no | Whether the Runner should be locked for current project | +| `run_untagged` | boolean | no | Whether the Runner should handle untagged jobs | +| `tag_list` | Array[String] | no | List of Runner's tags | +| `maximum_timeout` | integer | no | Maximum timeout set when this Runner will handle the job | + +``` +curl --request POST "https://gitlab.example.com/api/v4/runners" --form "token=ipzXrMhuyyJPifUt6ANz" --form "description=test-1-20150125-test" --form "tag_list=ruby,mysql,tag1,tag2" +``` + +Response: + +| Status | Description | +|-----------|---------------------------------| +| 201 | Runner was created | + +Example response: + +```json +{ + "id": "12345", + "token": "6337ff461c94fd3fa32ba3b1ff4125" +} +``` + +## Delete a registered Runner + +Deletes a registed Runner. + +``` +DELETE /runners +``` + +| Attribute | Type | Required | Description | +|-------------|---------|----------|---------------------| +| `token` | string | yes | Runner's authentication token | + +``` +curl --request DELETE "https://gitlab.example.com/api/v4/runners" --form "token=ebb6fc00521627750c8bb750f2490e" +``` + +Response: + +| Status | Description | +|-----------|---------------------------------| +| 204 | Runner was deleted | + +## Verify authentication for a registered Runner + +Validates authentication credentials for a registered Runner. + +``` +POST /runners/verify +``` + +| Attribute | Type | Required | Description | +|-------------|---------|----------|---------------------| +| `token` | string | yes | Runner's authentication token | + +``` +curl --request POST "https://gitlab.example.com/api/v4/runners/verify" --form "token=ebb6fc00521627750c8bb750f2490e" +``` + +Response: + +| Status | Description | +|-----------|---------------------------------| +| 200 | Credentials are valid | +| 403 | Credentials are invalid | diff --git a/doc/install/kubernetes/gitlab_omnibus.md b/doc/install/kubernetes/gitlab_omnibus.md index 9c5258c2cdf..98af87455ec 100644 --- a/doc/install/kubernetes/gitlab_omnibus.md +++ b/doc/install/kubernetes/gitlab_omnibus.md @@ -129,8 +129,8 @@ You may see a temporary error message `SchedulerPredicates failed due to Persist Add the GitLab Helm repository and initialize Helm: ```bash -helm repo add gitlab https://charts.gitlab.io helm init +helm repo add gitlab https://charts.gitlab.io ``` Once you have reviewed the [configuration settings](#configuring-and-installing-gitlab) you can install the chart. We recommending saving your configuration options in a `values.yaml` file for easier upgrades in the future. diff --git a/lib/gitlab/git/wiki.rb b/lib/gitlab/git/wiki.rb index 84a26fe4a6f..d75a5f15c29 100644 --- a/lib/gitlab/git/wiki.rb +++ b/lib/gitlab/git/wiki.rb @@ -67,7 +67,8 @@ module Gitlab end def page(title:, version: nil, dir: nil) - @repository.gitaly_migrate(:wiki_find_page) do |is_enabled| + @repository.gitaly_migrate(:wiki_find_page, + status: Gitlab::GitalyClient::MigrationStatus::OPT_OUT) do |is_enabled| if is_enabled gitaly_find_page(title: title, version: version, dir: dir) else diff --git a/lib/gitlab/gitaly_client/storage_settings.rb b/lib/gitlab/gitaly_client/storage_settings.rb index 8668caf0c55..9a576e463e3 100644 --- a/lib/gitlab/gitaly_client/storage_settings.rb +++ b/lib/gitlab/gitaly_client/storage_settings.rb @@ -5,6 +5,14 @@ module Gitlab # directly. class StorageSettings DirectPathAccessError = Class.new(StandardError) + InvalidConfigurationError = Class.new(StandardError) + + INVALID_STORAGE_MESSAGE = <<~MSG.freeze + Storage is invalid because it has no `path` key. + + For source installations, update your config/gitlab.yml Refer to gitlab.yml.example for an updated example. + If you're using the Gitlab Development Kit, you can update your configuration running `gdk reconfigure`. + MSG # This class will give easily recognizable NoMethodErrors Deprecated = Class.new @@ -12,7 +20,8 @@ module Gitlab attr_reader :legacy_disk_path def initialize(storage) - raise "expected a Hash, got a #{storage.class.name}" unless storage.is_a?(Hash) + raise InvalidConfigurationError, "expected a Hash, got a #{storage.class.name}" unless storage.is_a?(Hash) + raise InvalidConfigurationError, INVALID_STORAGE_MESSAGE unless storage.has_key?('path') # Support a nil 'path' field because some of the circuit breaker tests use it. @legacy_disk_path = File.expand_path(storage['path'], Rails.root) if storage['path'] diff --git a/lib/gitlab/multi_collection_paginator.rb b/lib/gitlab/multi_collection_paginator.rb index 43921a8c1c0..fd5de73c526 100644 --- a/lib/gitlab/multi_collection_paginator.rb +++ b/lib/gitlab/multi_collection_paginator.rb @@ -5,7 +5,7 @@ module Gitlab def initialize(*collections, per_page: nil) raise ArgumentError.new('Only 2 collections are supported') if collections.size != 2 - @per_page = per_page || Kaminari.config.default_per_page + @per_page = (per_page || Kaminari.config.default_per_page).to_i @first_collection, @second_collection = collections end diff --git a/spec/features/groups/members/filter_members_spec.rb b/spec/features/groups/members/filter_members_spec.rb new file mode 100644 index 00000000000..5ddb5894624 --- /dev/null +++ b/spec/features/groups/members/filter_members_spec.rb @@ -0,0 +1,54 @@ +require 'spec_helper' + +feature 'Groups > Members > Filter members' do + let(:user) { create(:user) } + let(:user_with_2fa) { create(:user, :two_factor_via_otp) } + let(:group) { create(:group) } + + background do + group.add_owner(user) + group.add_master(user_with_2fa) + + sign_in(user) + end + + scenario 'shows all members' do + visit_members_list + + expect(first_member).to include(user.name) + expect(second_member).to include(user_with_2fa.name) + expect(page).to have_css('.member-filter-2fa-dropdown .dropdown-toggle-text', text: '2FA: Everyone') + end + + scenario 'shows only 2FA members' do + visit_members_list(two_factor: 'enabled') + + expect(first_member).to include(user_with_2fa.name) + expect(members_list.size).to eq(1) + expect(page).to have_css('.member-filter-2fa-dropdown .dropdown-toggle-text', text: '2FA: Enabled') + end + + scenario 'shows only non 2FA members' do + visit_members_list(two_factor: 'disabled') + + expect(first_member).to include(user.name) + expect(members_list.size).to eq(1) + expect(page).to have_css('.member-filter-2fa-dropdown .dropdown-toggle-text', text: '2FA: Disabled') + end + + def visit_members_list(options = {}) + visit group_group_members_path(group.to_param, options) + end + + def members_list + page.all('ul.content-list > li') + end + + def first_member + members_list.first.text + end + + def second_member + members_list.last.text + end +end diff --git a/spec/initializers/6_validations_spec.rb b/spec/initializers/6_validations_spec.rb index 1dc307ea922..8d9dc092547 100644 --- a/spec/initializers/6_validations_spec.rb +++ b/spec/initializers/6_validations_spec.rb @@ -42,26 +42,6 @@ describe '6_validations' do expect { validate_storages_config }.to raise_error('"name with spaces" is not a valid storage name. Please fix this in your gitlab.yml before starting GitLab.') end end - - context 'with incomplete settings' do - before do - mock_storages('foo' => {}) - end - - it 'throws an error suggesting the user to update its settings' do - expect { validate_storages_config }.to raise_error('foo is not a valid storage, because it has no `path` key. Refer to gitlab.yml.example for an updated example. Please fix this in your gitlab.yml before starting GitLab.') - end - end - - context 'with deprecated settings structure' do - before do - mock_storages('foo' => 'tmp/tests/paths/a/b/c') - end - - it 'throws an error suggesting the user to update its settings' do - expect { validate_storages_config }.to raise_error("foo is not a valid storage, because it has no `path` key. It may be configured as:\n\nfoo:\n path: tmp/tests/paths/a/b/c\n\nFor source installations, update your config/gitlab.yml Refer to gitlab.yml.example for an updated example.\n\nIf you're using the Gitlab Development Kit, you can update your configuration running `gdk reconfigure`.\n") - end - end end describe 'validate_storages_paths' do diff --git a/spec/javascripts/ide/components/repo_editor_spec.js b/spec/javascripts/ide/components/repo_editor_spec.js index 360b6d4dc15..ff500acd849 100644 --- a/spec/javascripts/ide/components/repo_editor_spec.js +++ b/spec/javascripts/ide/components/repo_editor_spec.js @@ -24,7 +24,7 @@ describe('RepoEditor', () => { f.active = true; f.tempFile = true; vm.$store.state.openFiles.push(f); - vm.$store.state.entries[f.path] = f; + Vue.set(vm.$store.state.entries, f.path, f); vm.monaco = true; vm.$mount(); @@ -215,6 +215,30 @@ describe('RepoEditor', () => { expect(vm.editor.attachModel).toHaveBeenCalledWith(vm.model); }); + it('attaches model to merge request editor', () => { + vm.$store.state.viewer = 'mrdiff'; + vm.file.mrChange = true; + spyOn(vm.editor, 'attachMergeRequestModel'); + + Editor.editorInstance.modelManager.dispose(); + + vm.setupEditor(); + + expect(vm.editor.attachMergeRequestModel).toHaveBeenCalledWith(vm.model); + }); + + it('does not attach model to merge request editor when not a MR change', () => { + vm.$store.state.viewer = 'mrdiff'; + vm.file.mrChange = false; + spyOn(vm.editor, 'attachMergeRequestModel'); + + Editor.editorInstance.modelManager.dispose(); + + vm.setupEditor(); + + expect(vm.editor.attachMergeRequestModel).not.toHaveBeenCalledWith(vm.model); + }); + it('adds callback methods', () => { spyOn(vm.editor, 'onPositionChange').and.callThrough(); diff --git a/spec/lib/gitlab/gitaly_client/storage_settings_spec.rb b/spec/lib/gitlab/gitaly_client/storage_settings_spec.rb new file mode 100644 index 00000000000..c89913ec8e9 --- /dev/null +++ b/spec/lib/gitlab/gitaly_client/storage_settings_spec.rb @@ -0,0 +1,29 @@ +require 'spec_helper' + +describe Gitlab::GitalyClient::StorageSettings do + describe "#initialize" do + context 'when the storage contains no path' do + it 'raises an error' do + expect do + described_class.new("foo" => {}) + end.to raise_error(described_class::InvalidConfigurationError) + end + end + + context "when the argument isn't a hash" do + it 'raises an error' do + expect do + described_class.new("test") + end.to raise_error("expected a Hash, got a String") + end + end + + context 'when the storage is valid' do + it 'raises no error' do + expect do + described_class.new("path" => Rails.root) + end.not_to raise_error + end + end + end +end diff --git a/spec/models/ci/runner_spec.rb b/spec/models/ci/runner_spec.rb index eb59ba7cbe9..e2b212f4f4c 100644 --- a/spec/models/ci/runner_spec.rb +++ b/spec/models/ci/runner_spec.rb @@ -200,15 +200,29 @@ describe Ci::Runner do describe '#assign_to' do let!(:project) { FactoryBot.create(:project) } - let!(:shared_runner) { FactoryBot.create(:ci_runner, :shared) } - before do - shared_runner.assign_to(project) + subject { runner.assign_to(project) } + + context 'with shared_runner' do + let!(:runner) { FactoryBot.create(:ci_runner, :shared) } + + it 'transitions shared runner to project runner and assigns project' do + subject + expect(runner).to be_specific + expect(runner).to be_project_type + expect(runner.projects).to eq([project]) + expect(runner.only_for?(project)).to be_truthy + end end - it { expect(shared_runner).to be_specific } - it { expect(shared_runner.projects).to eq([project]) } - it { expect(shared_runner.only_for?(project)).to be_truthy } + context 'with group runner' do + let!(:runner) { FactoryBot.create(:ci_runner, runner_type: :group_type) } + + it 'raises an error' do + expect { subject } + .to raise_error(ArgumentError, 'Transitioning a group runner to a project runner is not supported') + end + end end describe '.online' do diff --git a/spec/models/clusters/applications/runner_spec.rb b/spec/models/clusters/applications/runner_spec.rb index 5f2c723d483..3ef59457c5f 100644 --- a/spec/models/clusters/applications/runner_spec.rb +++ b/spec/models/clusters/applications/runner_spec.rb @@ -55,13 +55,9 @@ describe Clusters::Applications::Runner do context 'without a runner' do let(:project) { create(:project) } - let(:cluster) { create(:cluster) } + let(:cluster) { create(:cluster, projects: [project]) } let(:gitlab_runner) { create(:clusters_applications_runner, cluster: cluster) } - before do - cluster.projects << project - end - it 'creates a runner' do expect do subject diff --git a/spec/models/clusters/platforms/kubernetes_spec.rb b/spec/models/clusters/platforms/kubernetes_spec.rb index add481b8096..ab7f89f9bf4 100644 --- a/spec/models/clusters/platforms/kubernetes_spec.rb +++ b/spec/models/clusters/platforms/kubernetes_spec.rb @@ -239,17 +239,19 @@ describe Clusters::Platforms::Kubernetes, :use_clean_rails_memory_store_caching it { is_expected.to be_nil } end - context 'when kubernetes responds with valid pods' do + context 'when kubernetes responds with valid pods and deployments' do before do stub_kubeclient_pods + stub_kubeclient_deployments end - it { is_expected.to eq(pods: [kube_pod]) } + it { is_expected.to include(pods: [kube_pod]) } end context 'when kubernetes responds with 500s' do before do stub_kubeclient_pods(status: 500) + stub_kubeclient_deployments(status: 500) end it { expect { subject }.to raise_error(Kubeclient::HttpError) } @@ -258,9 +260,10 @@ describe Clusters::Platforms::Kubernetes, :use_clean_rails_memory_store_caching context 'when kubernetes responds with 404s' do before do stub_kubeclient_pods(status: 404) + stub_kubeclient_deployments(status: 404) end - it { is_expected.to eq(pods: []) } + it { is_expected.to include(pods: []) } end end end diff --git a/spec/models/concerns/sha_attribute_spec.rb b/spec/models/concerns/sha_attribute_spec.rb index 592feddf1dc..0d3beb6a6e3 100644 --- a/spec/models/concerns/sha_attribute_spec.rb +++ b/spec/models/concerns/sha_attribute_spec.rb @@ -36,24 +36,26 @@ describe ShaAttribute do end context 'when the table does not exist' do - it 'allows the attribute to be added' do + it 'allows the attribute to be added and issues a warning' do allow(model).to receive(:table_exists?).and_return(false) expect(model).not_to receive(:columns) expect(model).to receive(:attribute) + expect(model).to receive(:warn) model.sha_attribute(:name) end end context 'when the column does not exist' do - it 'raises ArgumentError' do + it 'allows the attribute to be added and issues a warning' do allow(model).to receive(:table_exists?).and_return(true) expect(model).to receive(:columns) - expect(model).not_to receive(:attribute) + expect(model).to receive(:attribute) + expect(model).to receive(:warn) - expect { model.sha_attribute(:no_name) }.to raise_error(ArgumentError) + model.sha_attribute(:no_name) end end diff --git a/spec/support/helpers/kubernetes_helpers.rb b/spec/support/helpers/kubernetes_helpers.rb index e46b61b6461..683a64504a1 100644 --- a/spec/support/helpers/kubernetes_helpers.rb +++ b/spec/support/helpers/kubernetes_helpers.rb @@ -9,8 +9,13 @@ module KubernetesHelpers kube_response(kube_pods_body) end + def kube_deployments_response + kube_response(kube_deployments_body) + end + def stub_kubeclient_discover(api_url) WebMock.stub_request(:get, api_url + '/api/v1').to_return(kube_response(kube_v1_discovery_body)) + WebMock.stub_request(:get, api_url + '/apis/extensions/v1beta1').to_return(kube_response(kube_v1beta1_discovery_body)) end def stub_kubeclient_pods(response = nil) @@ -20,6 +25,13 @@ module KubernetesHelpers WebMock.stub_request(:get, pods_url).to_return(response || kube_pods_response) end + def stub_kubeclient_deployments(response = nil) + stub_kubeclient_discover(service.api_url) + deployments_url = service.api_url + "/apis/extensions/v1beta1/namespaces/#{service.actual_namespace}/deployments" + + WebMock.stub_request(:get, deployments_url).to_return(response || kube_deployments_response) + end + def stub_kubeclient_get_secrets(api_url, **options) WebMock.stub_request(:get, api_url + '/api/v1/secrets') .to_return(kube_response(kube_v1_secrets_body(options))) @@ -53,6 +65,18 @@ module KubernetesHelpers "kind" => "APIResourceList", "resources" => [ { "name" => "pods", "namespaced" => true, "kind" => "Pod" }, + { "name" => "deployments", "namespaced" => true, "kind" => "Deployment" }, + { "name" => "secrets", "namespaced" => true, "kind" => "Secret" } + ] + } + end + + def kube_v1beta1_discovery_body + { + "kind" => "APIResourceList", + "resources" => [ + { "name" => "pods", "namespaced" => true, "kind" => "Pod" }, + { "name" => "deployments", "namespaced" => true, "kind" => "Deployment" }, { "name" => "secrets", "namespaced" => true, "kind" => "Secret" } ] } @@ -65,14 +89,25 @@ module KubernetesHelpers } end + def kube_deployments_body + { + "kind" => "DeploymentList", + "items" => [kube_deployment] + } + end + # This is a partial response, it will have many more elements in reality but # these are the ones we care about at the moment - def kube_pod(name: "kube-pod", app: "valid-pod-label") + def kube_pod(name: "kube-pod", app: "valid-pod-label", status: "Running", track: nil) { "metadata" => { "name" => name, + "generate_name" => "generated-name-with-suffix", "creationTimestamp" => "2016-11-25T19:55:19Z", - "labels" => { "app" => app } + "labels" => { + "app" => app, + "track" => track + } }, "spec" => { "containers" => [ @@ -80,7 +115,27 @@ module KubernetesHelpers { "name" => "container-1" } ] }, - "status" => { "phase" => "Running" } + "status" => { "phase" => status } + } + end + + def kube_deployment(name: "kube-deployment", app: "valid-deployment-label", track: nil) + { + "metadata" => { + "name" => name, + "generation" => 4, + "labels" => { + "app" => app, + "track" => track + }.compact + }, + "spec" => { "replicas" => 3 }, + "status" => { + "observedGeneration" => 4, + "replicas" => 3, + "updatedReplicas" => 3, + "availableReplicas" => 3 + } } end @@ -101,4 +156,12 @@ module KubernetesHelpers terminal end end + + def kube_deployment_rollout_status + ::Gitlab::Kubernetes::RolloutStatus.from_deployments(kube_deployment) + end + + def empty_deployment_rollout_status + ::Gitlab::Kubernetes::RolloutStatus.from_deployments() + end end |