diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2019-10-16 12:06:32 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2019-10-16 12:06:32 +0000 |
commit | d2ffc30fd583e86d4122bb5061098f4f3ca7b3f1 (patch) | |
tree | cb29c77a3ea49eb8ec732b0e644ed6cfad4770d9 | |
parent | 914ea32e0efca21436220df2c10e1bfbe4ed3da9 (diff) | |
download | gitlab-ce-d2ffc30fd583e86d4122bb5061098f4f3ca7b3f1.tar.gz |
Add latest changes from gitlab-org/gitlab@master
69 files changed, 638 insertions, 145 deletions
diff --git a/app/assets/javascripts/ide/stores/mutations.js b/app/assets/javascripts/ide/stores/mutations.js index 2587b57a817..e84e2782e46 100644 --- a/app/assets/javascripts/ide/stores/mutations.js +++ b/app/assets/javascripts/ide/stores/mutations.js @@ -73,16 +73,15 @@ export default { const entry = data.entries[key]; const foundEntry = state.entries[key]; + // NOTE: We can't clone `entry` in any of the below assignments because + // we need `state.entries` and the `entry.tree` to reference the same object. if (!foundEntry) { Object.assign(state.entries, { [key]: entry, }); } else if (foundEntry.deleted) { Object.assign(state.entries, { - [key]: { - ...entry, - replaces: true, - }, + [key]: Object.assign(entry, { replaces: true }), }); } else { const tree = entry.tree.filter( diff --git a/app/assets/javascripts/registry/components/app.vue b/app/assets/javascripts/registry/components/app.vue index 7ae06af02cf..a20bae9e37e 100644 --- a/app/assets/javascripts/registry/components/app.vue +++ b/app/assets/javascripts/registry/components/app.vue @@ -15,15 +15,19 @@ export default { GlLoadingIcon, }, props: { - endpoint: { - type: String, - required: true, - }, characterError: { type: Boolean, required: false, default: false, }, + containersErrorImage: { + type: String, + required: true, + }, + endpoint: { + type: String, + required: true, + }, helpPagePath: { type: String, required: true, @@ -32,7 +36,11 @@ export default { type: String, required: true, }, - containersErrorImage: { + personalAccessTokensHelpLink: { + type: String, + required: true, + }, + registryHostUrlWithPort: { type: String, required: true, }, @@ -40,6 +48,10 @@ export default { type: String, required: true, }, + twoFactorAuthHelpLink: { + type: String, + required: true, + }, }, store, computed: { @@ -79,6 +91,26 @@ export default { false, ); }, + notLoggedInToRegistryText() { + return sprintf( + s__(`ContainerRegistry|If you are not already logged in, you need to authenticate to + the Container Registry by using your GitLab username and password. If you have + %{twofaDocLinkStart}Two-Factor Authentication%{twofaDocLinkEnd} enabled, use a + %{personalAccessTokensDocLinkStart}Personal Access Token + %{personalAccessTokensDocLinkEnd}instead of a password.`), + { + twofaDocLinkStart: `<a href="${this.twoFactorAuthHelpLink}" target="_blank">`, + twofaDocLinkEnd: '</a>', + personalAccessTokensDocLinkStart: `<a href="${this.personalAccessTokensHelpLink}" target="_blank">`, + personalAccessTokensDocLinkEnd: '</a>', + }, + false, + ); + }, + dockerLoginCommand() { + // eslint-disable-next-line @gitlab/i18n/no-non-i18n-strings + return `docker login ${this.registryHostUrlWithPort}`; + }, dockerBuildCommand() { // eslint-disable-next-line @gitlab/i18n/no-non-i18n-strings return `docker build -t ${this.repositoryUrl} .`; @@ -130,6 +162,17 @@ export default { <template #description> <p class="js-no-container-images-text" v-html="noContainerImagesText"></p> <h5>{{ s__('ContainerRegistry|Quick Start') }}</h5> + <p class="js-not-logged-in-to-registry-text" v-html="notLoggedInToRegistryText"></p> + <div class="input-group append-bottom-10"> + <input :value="dockerLoginCommand" type="text" class="form-control monospace" readonly /> + <span class="input-group-append"> + <clipboard-button + :text="dockerLoginCommand" + :title="s__('ContainerRegistry|Copy login command')" + class="input-group-text" + /> + </span> + </div> <p> {{ s__( diff --git a/app/assets/javascripts/registry/index.js b/app/assets/javascripts/registry/index.js index d8daec29fda..38c3d67042c 100644 --- a/app/assets/javascripts/registry/index.js +++ b/app/assets/javascripts/registry/index.js @@ -13,23 +13,29 @@ export default () => data() { const { dataset } = document.querySelector(this.$options.el); return { - endpoint: dataset.endpoint, characterError: Boolean(dataset.characterError), + containersErrorImage: dataset.containersErrorImage, + endpoint: dataset.endpoint, helpPagePath: dataset.helpPagePath, noContainersImage: dataset.noContainersImage, - containersErrorImage: dataset.containersErrorImage, + personalAccessTokensHelpLink: dataset.personalAccessTokensHelpLink, + registryHostUrlWithPort: dataset.registryHostUrlWithPort, repositoryUrl: dataset.repositoryUrl, + twoFactorAuthHelpLink: dataset.twoFactorAuthHelpLink, }; }, render(createElement) { return createElement('registry-app', { props: { - endpoint: this.endpoint, characterError: this.characterError, + containersErrorImage: this.containersErrorImage, + endpoint: this.endpoint, helpPagePath: this.helpPagePath, noContainersImage: this.noContainersImage, - containersErrorImage: this.containersErrorImage, + personalAccessTokensHelpLink: this.personalAccessTokensHelpLink, + registryHostUrlWithPort: this.registryHostUrlWithPort, repositoryUrl: this.repositoryUrl, + twoFactorAuthHelpLink: this.twoFactorAuthHelpLink, }, }); }, diff --git a/app/assets/javascripts/vue_merge_request_widget/components/mr_collapsible_extension.vue b/app/assets/javascripts/vue_merge_request_widget/components/mr_collapsible_extension.vue index ae1d9368008..36f291e995c 100644 --- a/app/assets/javascripts/vue_merge_request_widget/components/mr_collapsible_extension.vue +++ b/app/assets/javascripts/vue_merge_request_widget/components/mr_collapsible_extension.vue @@ -39,9 +39,6 @@ export default { ariaLabel() { return this.isCollapsed ? __('Expand') : __('Collapse'); }, - isButtonDisabled() { - return this.isLoading || this.hasError; - }, }, methods: { toggleCollapsed() { @@ -53,25 +50,35 @@ export default { <template> <div> <div class="mr-widget-extension d-flex align-items-center pl-3"> - <gl-button - class="btn-blank btn s32 square append-right-default" - :aria-label="ariaLabel" - :disabled="isButtonDisabled" - @click="toggleCollapsed" - > - <gl-loading-icon v-if="isLoading" /> - <icon v-else :name="arrowIconName" class="js-icon" /> - </gl-button> - <gl-button - variant="link" - class="js-title" - :disabled="isButtonDisabled" - :class="{ 'border-0': isButtonDisabled }" - @click="toggleCollapsed" - > - <template v-if="isCollapsed">{{ title }}</template> - <template v-else>{{ __('Collapse') }}</template> - </gl-button> + <div v-if="hasError" class="ci-widget media"> + <div class="media-body"> + <span class="gl-font-size-small mr-widget-margin-left gl-line-height-24 js-error-state">{{ + title + }}</span> + </div> + </div> + + <template v-else> + <gl-button + class="btn-blank btn s32 square append-right-default" + :aria-label="ariaLabel" + :disabled="isLoading" + @click="toggleCollapsed" + > + <gl-loading-icon v-if="isLoading" /> + <icon v-else :name="arrowIconName" class="js-icon" /> + </gl-button> + <gl-button + variant="link" + class="js-title" + :disabled="isLoading" + :class="{ 'border-0': isLoading }" + @click="toggleCollapsed" + > + <template v-if="isCollapsed">{{ title }}</template> + <template v-else>{{ __('Collapse') }}</template> + </gl-button> + </template> </div> <div v-if="!isCollapsed" class="border-top js-slot-container"> diff --git a/app/assets/stylesheets/framework/common.scss b/app/assets/stylesheets/framework/common.scss index 370fc84e492..4b89a2f2b04 100644 --- a/app/assets/stylesheets/framework/common.scss +++ b/app/assets/stylesheets/framework/common.scss @@ -560,3 +560,6 @@ img.emoji { } } } + +.gl-font-size-small { font-size: $gl-font-size-small; } +.gl-line-height-24 { line-height: $gl-line-height-24; } diff --git a/app/assets/stylesheets/framework/variables.scss b/app/assets/stylesheets/framework/variables.scss index f352ee33535..dfc39d8e03b 100644 --- a/app/assets/stylesheets/framework/variables.scss +++ b/app/assets/stylesheets/framework/variables.scss @@ -833,6 +833,7 @@ Merge Requests */ $mr-tabs-height: 48px; $mr-version-controls-height: 56px; +$mr-widget-margin-left: 40px; /* Compare Branches diff --git a/app/assets/stylesheets/pages/merge_requests.scss b/app/assets/stylesheets/pages/merge_requests.scss index e6feded1d4f..971f3b2c308 100644 --- a/app/assets/stylesheets/pages/merge_requests.scss +++ b/app/assets/stylesheets/pages/merge_requests.scss @@ -19,6 +19,8 @@ border-top: 1px solid $border-color; } +.mr-widget-margin-left { margin-left: $mr-widget-margin-left; } + .media-section { @include media-breakpoint-down(md) { align-items: flex-start; diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 5c2420e80f2..ecaeb7060c8 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -108,6 +108,11 @@ module ApplicationHelper Gitlab.config.extra end + # shortcut for gitlab registry config + def registry_config + Gitlab.config.registry + end + # Render a `time` element with Javascript-based relative date and tooltip # # time - Time object diff --git a/app/models/container_repository.rb b/app/models/container_repository.rb index 583e23d1274..253f74e9811 100644 --- a/app/models/container_repository.rb +++ b/app/models/container_repository.rb @@ -67,7 +67,7 @@ class ContainerRepository < ApplicationRecord def delete_tags! return unless has_tags? - digests = tags.map { |tag| tag.digest }.to_set + digests = tags.map { |tag| tag.digest }.compact.to_set digests.all? do |digest| delete_tag_by_digest(digest) diff --git a/app/services/projects/container_repository/delete_tags_service.rb b/app/services/projects/container_repository/delete_tags_service.rb index 22656b29c58..5129e2269a8 100644 --- a/app/services/projects/container_repository/delete_tags_service.rb +++ b/app/services/projects/container_repository/delete_tags_service.rb @@ -48,10 +48,10 @@ module Projects # rubocop: disable CodeReuse/ActiveRecord Gitlab::Sentry.track_exception(ArgumentError.new('multiple tag digests')) if tag_digests.many? - # deletes the dummy image - # all created tag digests are the same since they all have the same dummy image. + # Deletes the dummy image + # All created tag digests are the same since they all have the same dummy image. # a single delete is sufficient to remove all tags with it - if container_repository.client.delete_repository_tag(container_repository.path, tag_digests.first) + if container_repository.delete_tag_by_digest(tag_digests.first) success(deleted: tag_names) else error('could not delete tags') diff --git a/app/views/projects/registry/repositories/index.html.haml b/app/views/projects/registry/repositories/index.html.haml index ddb19cb6b65..b2e160e37bc 100644 --- a/app/views/projects/registry/repositories/index.html.haml +++ b/app/views/projects/registry/repositories/index.html.haml @@ -5,7 +5,10 @@ .col-12 #js-vue-registry-images{ data: { endpoint: project_container_registry_index_path(@project, format: :json), "help_page_path" => help_page_path('user/packages/container_registry/index'), + "two_factor_auth_help_link" => help_page_path('user/profile/account/two_factor_authentication'), + "personal_access_tokens_help_link" => help_page_path('user/profile/personal_access_tokens'), "no_containers_image" => image_path('illustrations/docker-empty-state.svg'), "containers_error_image" => image_path('illustrations/docker-error-state.svg'), "repository_url" => escape_once(@project.container_registry_url), + "registry_host_url_with_port" => escape_once(registry_config.host_port), character_error: @character_error.to_s } } diff --git a/app/views/shared/_allow_request_access.html.haml b/app/views/shared/_allow_request_access.html.haml index 2b24bde9e59..ca82f2f3377 100644 --- a/app/views/shared/_allow_request_access.html.haml +++ b/app/views/shared/_allow_request_access.html.haml @@ -3,6 +3,4 @@ .form-check = form.check_box :request_access_enabled, class: 'form-check-input', data: { qa_selector: 'request_access_checkbox' } = form.label :request_access_enabled, class: 'form-check-label' do - %span{ class: label_class }= _('Allow users to request access') - %br - %span.text-muted= _('Allow users to request access if visibility is public or internal.') + %span{ class: label_class }= _('Allow users to request access (if visibility is public or internal)') diff --git a/changelogs/unreleased/18217-request-access-to-project-should-be-on-by-default.yml b/changelogs/unreleased/18217-request-access-to-project-should-be-on-by-default.yml new file mode 100644 index 00000000000..3fb1b874e77 --- /dev/null +++ b/changelogs/unreleased/18217-request-access-to-project-should-be-on-by-default.yml @@ -0,0 +1,5 @@ +--- +title: Enable Request Access functionality by default for new projects and groups +merge_request: 17662 +author: +type: changed diff --git a/changelogs/unreleased/29881-fix-ide-delete-and-readd.yml b/changelogs/unreleased/29881-fix-ide-delete-and-readd.yml new file mode 100644 index 00000000000..91445ca791b --- /dev/null +++ b/changelogs/unreleased/29881-fix-ide-delete-and-readd.yml @@ -0,0 +1,5 @@ +--- +title: Fix Web IDE tree not updating modified status +merge_request: 18647 +author: +type: fixed diff --git a/changelogs/unreleased/feature-add-copyable-login-with-copy-to-empty-container-registry-view.yml b/changelogs/unreleased/feature-add-copyable-login-with-copy-to-empty-container-registry-view.yml new file mode 100644 index 00000000000..6d7a773120b --- /dev/null +++ b/changelogs/unreleased/feature-add-copyable-login-with-copy-to-empty-container-registry-view.yml @@ -0,0 +1,5 @@ +--- +title: Adds login input with copy box and supporting copy to empty container registry view +merge_request: 18244 +author: nate geslin +type: added diff --git a/changelogs/unreleased/issue_11240.yml b/changelogs/unreleased/issue_11240.yml new file mode 100644 index 00000000000..751440d1e8c --- /dev/null +++ b/changelogs/unreleased/issue_11240.yml @@ -0,0 +1,5 @@ +--- +title: Expose subscribed attribute for epic on API +merge_request: 18475 +author: +type: added diff --git a/config/initializers/7_prometheus_metrics.rb b/config/initializers/7_prometheus_metrics.rb index c5cd055f4ff..d58fb54f71c 100644 --- a/config/initializers/7_prometheus_metrics.rb +++ b/config/initializers/7_prometheus_metrics.rb @@ -34,6 +34,12 @@ Sidekiq.configure_server do |config| config.on(:startup) do # webserver metrics are cleaned up in config.ru: `warmup` block Prometheus::CleanupMultiprocDirService.new.execute + # In production, sidekiq is run in a multi-process setup where processes might interfere + # with each other cleaning up and reinitializing prometheus database files, which is why + # we're re-doing the work every time here. + # A cleaner solution would be to run the cleanup pre-fork, and the initialization once + # after all workers have forked, but I don't know how at this point. + ::Prometheus::Client.reinitialize_on_pid_change(force: true) Gitlab::Metrics::Exporter::SidekiqExporter.instance.start end diff --git a/db/migrate/20190925055714_default_request_access_groups.rb b/db/migrate/20190925055714_default_request_access_groups.rb new file mode 100644 index 00000000000..ba3efbe56f4 --- /dev/null +++ b/db/migrate/20190925055714_default_request_access_groups.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +class DefaultRequestAccessGroups < ActiveRecord::Migration[5.2] + include Gitlab::Database::MigrationHelpers + + DOWNTIME = false + + def up + change_column_default :namespaces, :request_access_enabled, true + end + + def down + change_column_default :namespaces, :request_access_enabled, false + end +end diff --git a/db/migrate/20190925055902_default_request_access_projects.rb b/db/migrate/20190925055902_default_request_access_projects.rb new file mode 100644 index 00000000000..3ad88d0963d --- /dev/null +++ b/db/migrate/20190925055902_default_request_access_projects.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +class DefaultRequestAccessProjects < ActiveRecord::Migration[5.2] + include Gitlab::Database::MigrationHelpers + + DOWNTIME = false + + def up + change_column_default :projects, :request_access_enabled, true + end + + def down + change_column_default :projects, :request_access_enabled, false + end +end diff --git a/db/schema.rb b/db/schema.rb index b9952cfc12d..c860fed7bbc 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -2333,7 +2333,7 @@ ActiveRecord::Schema.define(version: 2019_10_04_133612) do t.boolean "membership_lock", default: false t.boolean "share_with_group_lock", default: false t.integer "visibility_level", default: 20, null: false - t.boolean "request_access_enabled", default: false, null: false + t.boolean "request_access_enabled", default: true, null: false t.string "ldap_sync_status", default: "ready", null: false t.string "ldap_sync_error" t.datetime "ldap_sync_last_update_at" @@ -2922,7 +2922,7 @@ ActiveRecord::Schema.define(version: 2019_10_04_133612) do t.boolean "has_external_issue_tracker" t.string "repository_storage", default: "default", null: false t.boolean "repository_read_only" - t.boolean "request_access_enabled", default: false, null: false + t.boolean "request_access_enabled", default: true, null: false t.boolean "has_external_wiki" t.string "ci_config_path" t.boolean "lfs_enabled" diff --git a/doc/api/epics.md b/doc/api/epics.md index c24df6a236f..a5ec42a2a41 100644 --- a/doc/api/epics.md +++ b/doc/api/epics.md @@ -147,7 +147,8 @@ Example response: "closed_at": "2018-08-18T12:22:05.239Z", "labels": [], "upvotes": 4, - "downvotes": 0 + "downvotes": 0, + "subscribed": true } ``` diff --git a/lib/container_registry/client.rb b/lib/container_registry/client.rb index 2bd8eb65306..92861c567a8 100644 --- a/lib/container_registry/client.rb +++ b/lib/container_registry/client.rb @@ -36,7 +36,9 @@ module ContainerRegistry end def delete_repository_tag(name, reference) - faraday.delete("/v2/#{name}/manifests/#{reference}").success? + result = faraday.delete("/v2/#{name}/manifests/#{reference}") + + result.success? || result.status == 404 end def upload_raw_blob(path, blob) @@ -84,7 +86,9 @@ module ContainerRegistry end def delete_blob(name, digest) - faraday.delete("/v2/#{name}/blobs/#{digest}").success? + result = faraday.delete("/v2/#{name}/blobs/#{digest}") + + result.success? || result.status == 404 end def put_tag(name, reference, manifest) diff --git a/lib/gitlab/health_checks/gitaly_check.rb b/lib/gitlab/health_checks/gitaly_check.rb index f5f142c251f..e780bf8a986 100644 --- a/lib/gitlab/health_checks/gitaly_check.rb +++ b/lib/gitlab/health_checks/gitaly_check.rb @@ -5,7 +5,7 @@ module Gitlab class GitalyCheck extend BaseAbstractCheck - METRIC_PREFIX = 'gitaly_health_check' + METRIC_PREFIX = 'gitaly_health_check'.freeze class << self def readiness diff --git a/lib/gitlab/health_checks/probes/readiness.rb b/lib/gitlab/health_checks/probes/readiness.rb index b789cbe1ae6..28abf490ffc 100644 --- a/lib/gitlab/health_checks/probes/readiness.rb +++ b/lib/gitlab/health_checks/probes/readiness.rb @@ -6,10 +6,10 @@ module Gitlab class Readiness attr_reader :checks - # This accepts an array of Proc + # This accepts an array of objects implementing `:readiness` # that returns `::Gitlab::HealthChecks::Result` def initialize(*additional_checks) - @checks = ::Gitlab::HealthChecks::CHECKS.map { |check| check.method(:readiness) } + @checks = ::Gitlab::HealthChecks::CHECKS @checks += additional_checks end @@ -43,7 +43,7 @@ module Gitlab def probe_readiness checks - .flat_map(&:call) + .flat_map(&:readiness) .compact .group_by(&:name) end diff --git a/lib/gitlab/health_checks/puma_check.rb b/lib/gitlab/health_checks/puma_check.rb new file mode 100644 index 00000000000..7aafe29fbae --- /dev/null +++ b/lib/gitlab/health_checks/puma_check.rb @@ -0,0 +1,36 @@ +# frozen_string_literal: true + +module Gitlab + module HealthChecks + # This check can only be run on Puma `master` process + class PumaCheck + extend SimpleAbstractCheck + + class << self + private + + def metric_prefix + 'puma_check' + end + + def successful?(result) + result > 0 + end + + def check + return unless defined?(::Puma) + + stats = Puma.stats + stats = JSON.parse(stats) + + # If `workers` is missing this means that + # Puma server is running in single mode + stats.fetch('workers', 1) + rescue NoMethodError + # server is not ready + 0 + end + end + end + end +end diff --git a/lib/gitlab/health_checks/simple_abstract_check.rb b/lib/gitlab/health_checks/simple_abstract_check.rb index 959f28791c3..4e0b9296819 100644 --- a/lib/gitlab/health_checks/simple_abstract_check.rb +++ b/lib/gitlab/health_checks/simple_abstract_check.rb @@ -7,6 +7,8 @@ module Gitlab def readiness check_result = check + return if check_result.nil? + if successful?(check_result) HealthChecks::Result.new(name, true) elsif check_result.is_a?(Timeout::Error) @@ -20,6 +22,8 @@ module Gitlab def metrics result, elapsed = with_timing(&method(:check)) + return if result.nil? + Rails.logger.error("#{human_name} check returned unexpected result #{result}") unless successful?(result) # rubocop:disable Gitlab/RailsLogger [ metric("#{metric_prefix}_timeout", result.is_a?(Timeout::Error) ? 1 : 0), diff --git a/lib/gitlab/health_checks/unicorn_check.rb b/lib/gitlab/health_checks/unicorn_check.rb new file mode 100644 index 00000000000..a30ae015257 --- /dev/null +++ b/lib/gitlab/health_checks/unicorn_check.rb @@ -0,0 +1,41 @@ +# frozen_string_literal: true + +module Gitlab + module HealthChecks + # This check can only be run on Unicorn `master` process + class UnicornCheck + extend SimpleAbstractCheck + + class << self + include Gitlab::Utils::StrongMemoize + + private + + def metric_prefix + 'unicorn_check' + end + + def successful?(result) + result > 0 + end + + def check + return unless http_servers + + http_servers.sum(&:worker_processes) # rubocop: disable CodeReuse/ActiveRecord + end + + # Traversal of ObjectSpace is expensive, on fully loaded application + # it takes around 80ms. The instances of HttpServers are not a subject + # to change so we can cache the list of servers. + def http_servers + strong_memoize(:http_servers) do + next unless defined?(::Unicorn::HttpServer) + + ObjectSpace.each_object(::Unicorn::HttpServer).to_a + end + end + end + end + end +end diff --git a/lib/gitlab/metrics/exporter/base_exporter.rb b/lib/gitlab/metrics/exporter/base_exporter.rb index 3aac18f59c6..13483e437d8 100644 --- a/lib/gitlab/metrics/exporter/base_exporter.rb +++ b/lib/gitlab/metrics/exporter/base_exporter.rb @@ -6,6 +6,8 @@ module Gitlab class BaseExporter < Daemon attr_reader :server + attr_accessor :additional_checks + def enabled? settings.enabled end @@ -32,12 +34,10 @@ module Gitlab Port: settings.port, BindAddress: settings.address, Logger: logger, AccessLog: access_log) server.mount_proc '/readiness' do |req, res| - render_probe( - ::Gitlab::HealthChecks::Probes::Readiness.new, req, res) + render_probe(readiness_probe, req, res) end server.mount_proc '/liveness' do |req, res| - render_probe( - ::Gitlab::HealthChecks::Probes::Liveness.new, req, res) + render_probe(liveness_probe, req, res) end server.mount '/', Rack::Handler::WEBrick, rack_app @@ -52,8 +52,10 @@ module Gitlab def stop_working if server + # we close sockets if thread is not longer running + # this happens, when the process forks + server.listeners.each(&:close) unless thread.alive? server.shutdown - server.listeners.each(&:close) end @server = nil @@ -67,6 +69,14 @@ module Gitlab end end + def readiness_probe + ::Gitlab::HealthChecks::Probes::Readiness.new(*additional_checks) + end + + def liveness_probe + ::Gitlab::HealthChecks::Probes::Liveness.new + end + def render_probe(probe, req, res) result = probe.execute diff --git a/lib/gitlab/metrics/exporter/web_exporter.rb b/lib/gitlab/metrics/exporter/web_exporter.rb index fac7043352a..f4b42759fb9 100644 --- a/lib/gitlab/metrics/exporter/web_exporter.rb +++ b/lib/gitlab/metrics/exporter/web_exporter.rb @@ -7,6 +7,16 @@ module Gitlab module Metrics module Exporter class WebExporter < BaseExporter + # This exporter is always run on master process + def initialize + super + + self.additional_checks = [ + Gitlab::HealthChecks::PumaCheck, + Gitlab::HealthChecks::UnicornCheck + ] + end + def settings Settings.monitoring.web_exporter end diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 0741e0445a6..d8c5a934542 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -1413,10 +1413,7 @@ msgstr "" msgid "Allow users to register any application to use GitLab as an OAuth provider" msgstr "" -msgid "Allow users to request access" -msgstr "" - -msgid "Allow users to request access if visibility is public or internal." +msgid "Allow users to request access (if visibility is public or internal)" msgstr "" msgid "Allowed email domain restriction only permitted for top-level groups" @@ -4352,12 +4349,18 @@ msgstr "" msgid "ContainerRegistry|Copy build command" msgstr "" +msgid "ContainerRegistry|Copy login command" +msgstr "" + msgid "ContainerRegistry|Copy push command" msgstr "" msgid "ContainerRegistry|Docker connection error" msgstr "" +msgid "ContainerRegistry|If you are not already logged in, you need to authenticate to the Container Registry by using your GitLab username and password. If you have %{twofaDocLinkStart}Two-Factor Authentication%{twofaDocLinkEnd} enabled, use a %{personalAccessTokensDocLinkStart}Personal Access Token %{personalAccessTokensDocLinkEnd}instead of a password." +msgstr "" + msgid "ContainerRegistry|Last Updated" msgstr "" diff --git a/scripts/review_apps/base-config.yaml b/scripts/review_apps/base-config.yaml index de5e14af4d4..1dc79e6d45e 100644 --- a/scripts/review_apps/base-config.yaml +++ b/scripts/review_apps/base-config.yaml @@ -153,7 +153,8 @@ redis: redis-ha: enabled: false registry: - minReplicas: 1 + hpa: + minReplicas: 1 resources: requests: cpu: 50m diff --git a/spec/controllers/groups/group_members_controller_spec.rb b/spec/controllers/groups/group_members_controller_spec.rb index 0c3dd971582..22f970133e3 100644 --- a/spec/controllers/groups/group_members_controller_spec.rb +++ b/spec/controllers/groups/group_members_controller_spec.rb @@ -6,7 +6,7 @@ describe Groups::GroupMembersController do include ExternalAuthorizationServiceHelpers let(:user) { create(:user) } - let(:group) { create(:group, :public, :access_requestable) } + let(:group) { create(:group, :public) } let(:membership) { create(:group_member, group: group) } describe 'GET index' do diff --git a/spec/controllers/projects/project_members_controller_spec.rb b/spec/controllers/projects/project_members_controller_spec.rb index 5130e26c928..2f473d395ad 100644 --- a/spec/controllers/projects/project_members_controller_spec.rb +++ b/spec/controllers/projects/project_members_controller_spec.rb @@ -4,7 +4,7 @@ require('spec_helper') describe Projects::ProjectMembersController do let(:user) { create(:user) } - let(:project) { create(:project, :public, :access_requestable) } + let(:project) { create(:project, :public) } describe 'GET index' do it 'has the project_members address with a 200 status code' do diff --git a/spec/factories/groups.rb b/spec/factories/groups.rb index ba1a4883f85..93c01f8034d 100644 --- a/spec/factories/groups.rb +++ b/spec/factories/groups.rb @@ -32,8 +32,8 @@ FactoryBot.define do avatar { fixture_file_upload('spec/fixtures/dk.png') } end - trait :access_requestable do - request_access_enabled { true } + trait :request_access_disabled do + request_access_enabled { false } end trait :nested do diff --git a/spec/factories/projects.rb b/spec/factories/projects.rb index ae1feb73e4d..9477eeb18d4 100644 --- a/spec/factories/projects.rb +++ b/spec/factories/projects.rb @@ -117,8 +117,8 @@ FactoryBot.define do storage_version { nil } end - trait :access_requestable do - request_access_enabled { true } + trait :request_access_disabled do + request_access_enabled { false } end trait :with_avatar do diff --git a/spec/features/groups/members/master_manages_access_requests_spec.rb b/spec/features/groups/members/master_manages_access_requests_spec.rb index 454da126c81..1c13bd3d59e 100644 --- a/spec/features/groups/members/master_manages_access_requests_spec.rb +++ b/spec/features/groups/members/master_manages_access_requests_spec.rb @@ -4,7 +4,7 @@ require 'spec_helper' describe 'Groups > Members > Maintainer manages access requests' do it_behaves_like 'Maintainer manages access requests' do - let(:entity) { create(:group, :public, :access_requestable) } + let(:entity) { create(:group, :public) } let(:members_page_path) { group_group_members_path(entity) } end end diff --git a/spec/features/groups/members/request_access_spec.rb b/spec/features/groups/members/request_access_spec.rb index 0d5321709ae..5f22af3529c 100644 --- a/spec/features/groups/members/request_access_spec.rb +++ b/spec/features/groups/members/request_access_spec.rb @@ -5,7 +5,7 @@ require 'spec_helper' describe 'Groups > Members > Request access' do let(:user) { create(:user) } let(:owner) { create(:user) } - let(:group) { create(:group, :public, :access_requestable) } + let(:group) { create(:group, :public) } let!(:project) { create(:project, :private, namespace: group) } before do diff --git a/spec/features/projects/members/group_members_spec.rb b/spec/features/projects/members/group_members_spec.rb index dd5fc82e058..4ecc3db78b3 100644 --- a/spec/features/projects/members/group_members_spec.rb +++ b/spec/features/projects/members/group_members_spec.rb @@ -5,8 +5,8 @@ require 'spec_helper' describe 'Projects members' do let(:user) { create(:user) } let(:developer) { create(:user) } - let(:group) { create(:group, :public, :access_requestable) } - let(:project) { create(:project, :public, :access_requestable, creator: user, group: group) } + let(:group) { create(:group, :public) } + let(:project) { create(:project, :public, creator: user, group: group) } let(:project_invitee) { create(:project_member, project: project, invite_token: '123', invite_email: 'test1@abc.com', user: nil) } let(:group_invitee) { create(:group_member, group: group, invite_token: '123', invite_email: 'test2@abc.com', user: nil) } let(:project_requester) { create(:user) } diff --git a/spec/features/projects/members/group_requester_cannot_request_access_to_project_spec.rb b/spec/features/projects/members/group_requester_cannot_request_access_to_project_spec.rb index fb4238f0a1f..ecd55f71c84 100644 --- a/spec/features/projects/members/group_requester_cannot_request_access_to_project_spec.rb +++ b/spec/features/projects/members/group_requester_cannot_request_access_to_project_spec.rb @@ -5,8 +5,8 @@ require 'spec_helper' describe 'Projects > Members > Group requester cannot request access to project', :js do let(:user) { create(:user) } let(:owner) { create(:user) } - let(:group) { create(:group, :public, :access_requestable) } - let(:project) { create(:project, :public, :access_requestable, namespace: group) } + let(:group) { create(:group, :public) } + let(:project) { create(:project, :public, namespace: group) } before do group.add_owner(owner) diff --git a/spec/features/projects/members/master_manages_access_requests_spec.rb b/spec/features/projects/members/master_manages_access_requests_spec.rb index 17d6efbcaa5..f113fb643f8 100644 --- a/spec/features/projects/members/master_manages_access_requests_spec.rb +++ b/spec/features/projects/members/master_manages_access_requests_spec.rb @@ -4,7 +4,7 @@ require 'spec_helper' describe 'Projects > Members > Maintainer manages access requests' do it_behaves_like 'Maintainer manages access requests' do - let(:entity) { create(:project, :public, :access_requestable) } + let(:entity) { create(:project, :public) } let(:members_page_path) { project_project_members_path(entity) } end end diff --git a/spec/features/projects/members/user_requests_access_spec.rb b/spec/features/projects/members/user_requests_access_spec.rb index 9f7327cd6e4..a77f0bdcbe9 100644 --- a/spec/features/projects/members/user_requests_access_spec.rb +++ b/spec/features/projects/members/user_requests_access_spec.rb @@ -4,7 +4,7 @@ require 'spec_helper' describe 'Projects > Members > User requests access', :js do let(:user) { create(:user) } - let(:project) { create(:project, :public, :access_requestable, :repository) } + let(:project) { create(:project, :public, :repository) } let(:maintainer) { project.owner } before do diff --git a/spec/finders/access_requests_finder_spec.rb b/spec/finders/access_requests_finder_spec.rb index 4c67087b50f..fbfc8035bcc 100644 --- a/spec/finders/access_requests_finder_spec.rb +++ b/spec/finders/access_requests_finder_spec.rb @@ -7,13 +7,13 @@ describe AccessRequestsFinder do let(:access_requester) { create(:user) } let(:project) do - create(:project, :public, :access_requestable) do |project| + create(:project, :public) do |project| project.request_access(access_requester) end end let(:group) do - create(:group, :public, :access_requestable) do |group| + create(:group, :public) do |group| group.request_access(access_requester) end end diff --git a/spec/finders/group_members_finder_spec.rb b/spec/finders/group_members_finder_spec.rb index 12f92f6b5b0..08f3b4024b3 100644 --- a/spec/finders/group_members_finder_spec.rb +++ b/spec/finders/group_members_finder_spec.rb @@ -4,7 +4,7 @@ require 'spec_helper' describe GroupMembersFinder, '#execute' do let(:group) { create(:group) } - let(:nested_group) { create(:group, :access_requestable, parent: group) } + let(:nested_group) { create(:group, parent: group) } let(:user1) { create(:user) } let(:user2) { create(:user) } let(:user3) { create(:user) } diff --git a/spec/finders/members_finder_spec.rb b/spec/finders/members_finder_spec.rb index 9849808c255..f9b8fee6f2d 100644 --- a/spec/finders/members_finder_spec.rb +++ b/spec/finders/members_finder_spec.rb @@ -4,7 +4,7 @@ require 'spec_helper' describe MembersFinder, '#execute' do set(:group) { create(:group) } - set(:nested_group) { create(:group, :access_requestable, parent: group) } + set(:nested_group) { create(:group, parent: group) } set(:project) { create(:project, namespace: nested_group) } set(:user1) { create(:user) } set(:user2) { create(:user) } @@ -57,7 +57,7 @@ describe MembersFinder, '#execute' do context 'when include_invited_groups_members == true' do subject { described_class.new(project, user2).execute(include_invited_groups_members: true) } - set(:linked_group) { create(:group, :public, :access_requestable) } + set(:linked_group) { create(:group, :public) } set(:nested_linked_group) { create(:group, parent: linked_group) } set(:linked_group_member) { linked_group.add_guest(user1) } set(:nested_linked_group_member) { nested_linked_group.add_guest(user2) } diff --git a/spec/frontend/ide/stores/integration_spec.js b/spec/frontend/ide/stores/integration_spec.js new file mode 100644 index 00000000000..443de18f288 --- /dev/null +++ b/spec/frontend/ide/stores/integration_spec.js @@ -0,0 +1,100 @@ +import { decorateFiles } from '~/ide/lib/files'; +import { createStore } from '~/ide/stores'; + +const TEST_BRANCH = 'test_branch'; +const TEST_NAMESPACE = 'test_namespace'; +const TEST_PROJECT_ID = `${TEST_NAMESPACE}/test_project`; +const TEST_PATH_DIR = 'src'; +const TEST_PATH = `${TEST_PATH_DIR}/foo.js`; +const TEST_CONTENT = `Lorem ipsum dolar sit +Lorem ipsum dolar +Lorem ipsum +Lorem +`; + +jest.mock('~/ide/ide_router'); + +describe('IDE store integration', () => { + let store; + + beforeEach(() => { + store = createStore(); + store.replaceState({ + ...store.state, + projects: { + [TEST_PROJECT_ID]: { + web_url: 'test_web_url', + branches: [], + }, + }, + currentProjectId: TEST_PROJECT_ID, + currentBranchId: TEST_BRANCH, + }); + }); + + describe('with project and files', () => { + beforeEach(() => { + const { entries, treeList } = decorateFiles({ + data: [`${TEST_PATH_DIR}/`, TEST_PATH, 'README.md'], + projectId: TEST_PROJECT_ID, + branchId: TEST_BRANCH, + }); + + Object.assign(entries[TEST_PATH], { + raw: TEST_CONTENT, + }); + + store.replaceState({ + ...store.state, + trees: { + [`${TEST_PROJECT_ID}/${TEST_BRANCH}`]: { + tree: treeList, + }, + }, + entries, + }); + }); + + describe('when a file is deleted and readded', () => { + beforeEach(() => { + store.dispatch('deleteEntry', TEST_PATH); + store.dispatch('createTempEntry', { name: TEST_PATH, type: 'blob' }); + }); + + it('has changed and staged', () => { + expect(store.state.changedFiles).toEqual([ + expect.objectContaining({ + path: TEST_PATH, + tempFile: true, + deleted: false, + }), + ]); + + expect(store.state.stagedFiles).toEqual([ + expect.objectContaining({ + path: TEST_PATH, + deleted: true, + }), + ]); + }); + + it('cleans up after commit', () => { + const expected = expect.objectContaining({ + path: TEST_PATH, + staged: false, + changed: false, + tempFile: false, + deleted: false, + }); + store.dispatch('stageChange', TEST_PATH); + + store.dispatch('commit/updateFilesAfterCommit', { data: {} }); + + expect(store.state.entries[TEST_PATH]).toEqual(expected); + expect(store.state.entries[TEST_PATH_DIR].tree.find(x => x.path === TEST_PATH)).toEqual( + expected, + ); + }); + }); + }); +}); diff --git a/spec/frontend/registry/components/app_spec.js b/spec/frontend/registry/components/app_spec.js index 190af5c11cd..5dcb61e03b5 100644 --- a/spec/frontend/registry/components/app_spec.js +++ b/spec/frontend/registry/components/app_spec.js @@ -1,5 +1,5 @@ -import registry from '~/registry/components/app.vue'; import { mount } from '@vue/test-utils'; +import registry from '~/registry/components/app.vue'; import { TEST_HOST } from '../../helpers/test_constants'; import { reposServerResponse, parsedReposServerResponse } from '../mock_data'; @@ -8,6 +8,7 @@ describe('Registry List', () => { const findCollapsibleContainer = w => w.findAll({ name: 'CollapsibeContainerRegisty' }); const findNoContainerImagesText = w => w.find('.js-no-container-images-text'); + const findNotLoggedInToRegistryText = w => w.find('.js-not-logged-in-to-registry-text'); const findSpinner = w => w.find('.gl-spinner'); const findCharacterErrorText = w => w.find('.js-character-error-text'); @@ -17,6 +18,9 @@ describe('Registry List', () => { noContainersImage: 'foo', containersErrorImage: 'foo', repositoryUrl: 'foo', + registryHostUrlWithPort: 'foo', + personalAccessTokensHelpLink: 'foo', + twoFactorAuthHelpLink: 'foo', }; const setMainEndpoint = jest.fn(); @@ -67,6 +71,13 @@ describe('Registry List', () => { 'With the Container Registry, every project can have its own space to store its Docker images. More Information', ); }); + + it('should render login help text', () => { + const notLoggedInToRegistryText = findNotLoggedInToRegistryText(localWrapper); + expect(notLoggedInToRegistryText.text()).toEqual( + 'If you are not already logged in, you need to authenticate to the Container Registry by using your GitLab username and password. If you have Two-Factor Authentication enabled, use a Personal Access Token instead of a password.', + ); + }); }); describe('while loading data', () => { diff --git a/spec/frontend/vue_mr_widget/components/artifacts_list_app_spec.js b/spec/frontend/vue_mr_widget/components/artifacts_list_app_spec.js index 49ed796d9a8..7d593a77bf3 100644 --- a/spec/frontend/vue_mr_widget/components/artifacts_list_app_spec.js +++ b/spec/frontend/vue_mr_widget/components/artifacts_list_app_spec.js @@ -44,6 +44,7 @@ describe('Merge Requests Artifacts list app', () => { const findButtons = () => wrapper.findAll('button'); const findTitle = () => wrapper.find('.js-title'); + const findErrorMessage = () => wrapper.find('.js-error-state'); const findTableRows = () => wrapper.findAll('tbody tr'); describe('while loading', () => { @@ -109,13 +110,12 @@ describe('Merge Requests Artifacts list app', () => { }); it('renders the error state', () => { - expect(findTitle().text()).toBe('An error occurred while fetching the artifacts'); + expect(findErrorMessage().text()).toBe('An error occurred while fetching the artifacts'); }); - it('renders disabled buttons', () => { + it('does not render buttons', () => { const buttons = findButtons(); - expect(buttons.at(0).attributes('disabled')).toBe('disabled'); - expect(buttons.at(1).attributes('disabled')).toBe('disabled'); + expect(buttons.exists()).toBe(false); }); }); }); diff --git a/spec/frontend/vue_mr_widget/components/mr_collapsible_extension_spec.js b/spec/frontend/vue_mr_widget/components/mr_collapsible_extension_spec.js index 4c9507223a1..ee107f297ef 100644 --- a/spec/frontend/vue_mr_widget/components/mr_collapsible_extension_spec.js +++ b/spec/frontend/vue_mr_widget/components/mr_collapsible_extension_spec.js @@ -20,6 +20,7 @@ describe('Merge Request Collapsible Extension', () => { }; const findTitle = () => wrapper.find('.js-title'); + const findErrorMessage = () => wrapper.find('.js-error-state'); afterEach(() => { wrapper.destroy(); @@ -87,19 +88,12 @@ describe('Merge Request Collapsible Extension', () => { mountComponent(Object.assign({}, data, { hasError: true })); }); - it('renders the buttons disabled', () => { - expect( - wrapper - .findAll('button') - .at(0) - .attributes('disabled'), - ).toEqual('disabled'); - expect( - wrapper - .findAll('button') - .at(1) - .attributes('disabled'), - ).toEqual('disabled'); + it('does not render the buttons', () => { + expect(wrapper.findAll('button').exists()).toBe(false); + }); + + it('renders title message provided', () => { + expect(findErrorMessage().text()).toBe(data.title); }); }); }); diff --git a/spec/helpers/members_helper_spec.rb b/spec/helpers/members_helper_spec.rb index e52d2166a69..169c8707bf4 100644 --- a/spec/helpers/members_helper_spec.rb +++ b/spec/helpers/members_helper_spec.rb @@ -5,11 +5,11 @@ require 'spec_helper' describe MembersHelper do describe '#remove_member_message' do let(:requester) { create(:user) } - let(:project) { create(:project, :public, :access_requestable) } + let(:project) { create(:project, :public) } let(:project_member) { build(:project_member, project: project) } let(:project_member_invite) { build(:project_member, project: project).tap { |m| m.generate_invite_token! } } let(:project_member_request) { project.request_access(requester) } - let(:group) { create(:group, :access_requestable) } + let(:group) { create(:group) } let(:group_member) { build(:group_member, group: group) } let(:group_member_invite) { build(:group_member, group: group).tap { |m| m.generate_invite_token! } } let(:group_member_request) { group.request_access(requester) } @@ -26,10 +26,10 @@ describe MembersHelper do describe '#remove_member_title' do let(:requester) { create(:user) } - let(:project) { create(:project, :public, :access_requestable) } + let(:project) { create(:project, :public) } let(:project_member) { build(:project_member, project: project) } let(:project_member_request) { project.request_access(requester) } - let(:group) { create(:group, :access_requestable) } + let(:group) { create(:group) } let(:group_member) { build(:group_member, group: group) } let(:group_member_request) { group.request_access(requester) } diff --git a/spec/lib/gitlab/health_checks/puma_check_spec.rb b/spec/lib/gitlab/health_checks/puma_check_spec.rb new file mode 100644 index 00000000000..71b6386b174 --- /dev/null +++ b/spec/lib/gitlab/health_checks/puma_check_spec.rb @@ -0,0 +1,65 @@ +require 'spec_helper' + +describe Gitlab::HealthChecks::PumaCheck do + let(:result_class) { Gitlab::HealthChecks::Result } + let(:readiness) { described_class.readiness } + let(:metrics) { described_class.metrics } + + shared_examples 'with state' do |(state, message)| + it "does provide readiness" do + expect(readiness).to eq(result_class.new('puma_check', state, message)) + end + + it "does provide metrics" do + expect(metrics).to include( + an_object_having_attributes(name: 'puma_check_success', value: state ? 1 : 0)) + expect(metrics).to include( + an_object_having_attributes(name: 'puma_check_latency_seconds', value: be >= 0)) + end + end + + context 'when Puma is not loaded' do + before do + hide_const('Puma') + end + + it "does not provide readiness and metrics" do + expect(readiness).to be_nil + expect(metrics).to be_nil + end + end + + context 'when Puma is loaded' do + before do + stub_const('Puma', Module.new) + end + + context 'when stats are missing' do + before do + expect(Puma).to receive(:stats).and_raise(NoMethodError) + end + + it_behaves_like 'with state', [false, 'unexpected Puma check result: 0'] + end + + context 'for Single mode' do + before do + expect(Puma).to receive(:stats) do + '{}' + end + end + + it_behaves_like 'with state', true + end + + context 'for Cluster mode' do + before do + expect(Puma).to receive(:stats) do + '{"workers":2}' + end + end + + it_behaves_like 'with state', true + end + end +end diff --git a/spec/lib/gitlab/health_checks/unicorn_check_spec.rb b/spec/lib/gitlab/health_checks/unicorn_check_spec.rb new file mode 100644 index 00000000000..c02d0c37738 --- /dev/null +++ b/spec/lib/gitlab/health_checks/unicorn_check_spec.rb @@ -0,0 +1,63 @@ +require 'spec_helper' + +describe Gitlab::HealthChecks::UnicornCheck do + let(:result_class) { Gitlab::HealthChecks::Result } + let(:readiness) { described_class.readiness } + let(:metrics) { described_class.metrics } + + before do + described_class.clear_memoization(:http_servers) + end + + shared_examples 'with state' do |(state, message)| + it "does provide readiness" do + expect(readiness).to eq(result_class.new('unicorn_check', state, message)) + end + + it "does provide metrics" do + expect(metrics).to include( + an_object_having_attributes(name: 'unicorn_check_success', value: state ? 1 : 0)) + expect(metrics).to include( + an_object_having_attributes(name: 'unicorn_check_latency_seconds', value: be >= 0)) + end + end + + context 'when Unicorn is not loaded' do + before do + hide_const('Unicorn') + end + + it "does not provide readiness and metrics" do + expect(readiness).to be_nil + expect(metrics).to be_nil + end + end + + context 'when Unicorn is loaded' do + let(:http_server_class) { Struct.new(:worker_processes) } + + before do + stub_const('Unicorn::HttpServer', http_server_class) + end + + context 'when no servers are running' do + it_behaves_like 'with state', [false, 'unexpected Unicorn check result: 0'] + end + + context 'when servers without workers are running' do + before do + http_server_class.new(0) + end + + it_behaves_like 'with state', [false, 'unexpected Unicorn check result: 0'] + end + + context 'when servers with workers are running' do + before do + http_server_class.new(1) + end + + it_behaves_like 'with state', true + end + end +end diff --git a/spec/lib/gitlab/metrics/exporter/base_exporter_spec.rb b/spec/lib/gitlab/metrics/exporter/base_exporter_spec.rb index bedf4fedcfa..0376da13595 100644 --- a/spec/lib/gitlab/metrics/exporter/base_exporter_spec.rb +++ b/spec/lib/gitlab/metrics/exporter/base_exporter_spec.rb @@ -64,6 +64,18 @@ describe Gitlab::Metrics::Exporter::BaseExporter do exporter.start.join end end + + describe 'when thread is not alive' do + it 'does close listeners' do + expect_any_instance_of(::WEBrick::HTTPServer).to receive(:start) + expect_any_instance_of(::WEBrick::HTTPServer).to receive(:listeners) + .and_call_original + + expect { exporter.start.join }.to change { exporter.thread? }.from(false).to(true) + + exporter.stop + end + end end describe '#stop' do diff --git a/spec/mailers/notify_spec.rb b/spec/mailers/notify_spec.rb index 56fa26d5f23..1991bac0229 100644 --- a/spec/mailers/notify_spec.rb +++ b/spec/mailers/notify_spec.rb @@ -714,7 +714,7 @@ describe Notify do describe 'project access requested' do let(:project) do - create(:project, :public, :access_requestable) do |project| + create(:project, :public) do |project| project.add_maintainer(project.owner) end end @@ -743,7 +743,7 @@ describe Notify do end describe 'project access denied' do - let(:project) { create(:project, :public, :access_requestable) } + let(:project) { create(:project, :public) } let(:project_member) do project.request_access(user) project.requesters.find_by(user_id: user.id) @@ -765,7 +765,7 @@ describe Notify do describe 'project access changed' do let(:owner) { create(:user, name: "Chang O'Keefe") } - let(:project) { create(:project, :public, :access_requestable, namespace: owner.namespace) } + let(:project) { create(:project, :public, namespace: owner.namespace) } let(:project_member) { create(:project_member, project: project, user: user) } subject { described_class.member_access_granted_email('project', project_member.id) } @@ -1167,7 +1167,7 @@ describe Notify do context 'for a group' do describe 'group access requested' do - let(:group) { create(:group, :public, :access_requestable) } + let(:group) { create(:group, :public) } let(:group_member) do group.request_access(user) group.requesters.find_by(user_id: user.id) diff --git a/spec/models/ci/build_metadata_spec.rb b/spec/models/ci/build_metadata_spec.rb index 67cd939b4c6..da95a2d30f5 100644 --- a/spec/models/ci/build_metadata_spec.rb +++ b/spec/models/ci/build_metadata_spec.rb @@ -4,7 +4,7 @@ require 'spec_helper' describe Ci::BuildMetadata do set(:user) { create(:user) } - set(:group) { create(:group, :access_requestable) } + set(:group) { create(:group) } set(:project) { create(:project, :repository, group: group, build_timeout: 2000) } set(:pipeline) do diff --git a/spec/models/ci/build_spec.rb b/spec/models/ci/build_spec.rb index 15281c9e826..26646085921 100644 --- a/spec/models/ci/build_spec.rb +++ b/spec/models/ci/build_spec.rb @@ -4,7 +4,7 @@ require 'spec_helper' describe Ci::Build do set(:user) { create(:user) } - set(:group) { create(:group, :access_requestable) } + set(:group) { create(:group) } set(:project) { create(:project, :repository, group: group) } set(:pipeline) do diff --git a/spec/models/concerns/access_requestable_spec.rb b/spec/models/concerns/access_requestable_spec.rb index de2bc3a387b..5c1694e3737 100644 --- a/spec/models/concerns/access_requestable_spec.rb +++ b/spec/models/concerns/access_requestable_spec.rb @@ -5,7 +5,7 @@ require 'spec_helper' describe AccessRequestable do describe 'Group' do describe '#request_access' do - let(:group) { create(:group, :public, :access_requestable) } + let(:group) { create(:group, :public) } let(:user) { create(:user) } it { expect(group.request_access(user)).to be_a(GroupMember) } @@ -13,7 +13,7 @@ describe AccessRequestable do end describe '#access_requested?' do - let(:group) { create(:group, :public, :access_requestable) } + let(:group) { create(:group, :public) } let(:user) { create(:user) } before do @@ -26,14 +26,14 @@ describe AccessRequestable do describe 'Project' do describe '#request_access' do - let(:project) { create(:project, :public, :access_requestable) } + let(:project) { create(:project, :public) } let(:user) { create(:user) } it { expect(project.request_access(user)).to be_a(ProjectMember) } end describe '#access_requested?' do - let(:project) { create(:project, :public, :access_requestable) } + let(:project) { create(:project, :public) } let(:user) { create(:user) } before do diff --git a/spec/models/group_spec.rb b/spec/models/group_spec.rb index 3f149f9d7ee..892c31a9204 100644 --- a/spec/models/group_spec.rb +++ b/spec/models/group_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' describe Group do - let!(:group) { create(:group, :access_requestable) } + let!(:group) { create(:group) } describe 'associations' do it { is_expected.to have_many :projects } @@ -331,7 +331,7 @@ describe Group do end describe '#avatar_url' do - let!(:group) { create(:group, :access_requestable, :with_avatar) } + let!(:group) { create(:group, :with_avatar) } let(:user) { create(:user) } context 'when avatar file is uploaded' do diff --git a/spec/models/member_spec.rb b/spec/models/member_spec.rb index 2cb4f222ea4..e7f03226826 100644 --- a/spec/models/member_spec.rb +++ b/spec/models/member_spec.rb @@ -92,7 +92,7 @@ describe Member do describe 'Scopes & finders' do before do - project = create(:project, :public, :access_requestable) + project = create(:project, :public) group = create(:group) @owner_user = create(:user).tap { |u| group.add_owner(u) } @owner = group.members.find_by(user_id: @owner_user.id) @@ -230,7 +230,7 @@ describe Member do describe '.add_user' do %w[project group].each do |source_type| context "when source is a #{source_type}" do - let!(:source) { create(source_type, :public, :access_requestable) } + let!(:source) { create(source_type, :public) } let!(:user) { create(:user) } let!(:admin) { create(:admin) } @@ -437,7 +437,7 @@ describe Member do describe '.add_users' do %w[project group].each do |source_type| context "when source is a #{source_type}" do - let!(:source) { create(source_type, :public, :access_requestable) } + let!(:source) { create(source_type, :public) } let!(:admin) { create(:admin) } let(:user1) { create(:user) } let(:user2) { create(:user) } diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index 68833fdaf73..866003c9ffd 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -153,7 +153,7 @@ describe Project do end describe '#members & #requesters' do - let(:project) { create(:project, :public, :access_requestable) } + let(:project) { create(:project, :public) } let(:requester) { create(:user) } let(:developer) { create(:user) } before do diff --git a/spec/models/project_team_spec.rb b/spec/models/project_team_spec.rb index 77c88a04cde..d62fa58739a 100644 --- a/spec/models/project_team_spec.rb +++ b/spec/models/project_team_spec.rb @@ -141,7 +141,7 @@ describe ProjectTeam do describe '#find_member' do context 'personal project' do let(:project) do - create(:project, :public, :access_requestable) + create(:project, :public) end let(:requester) { create(:user) } @@ -161,7 +161,7 @@ describe ProjectTeam do end context 'group project' do - let(:group) { create(:group, :access_requestable) } + let(:group) { create(:group) } let(:project) { create(:project, group: group) } let(:requester) { create(:user) } @@ -246,7 +246,7 @@ describe ProjectTeam do context 'personal project' do let(:project) do - create(:project, :public, :access_requestable) + create(:project, :public) end context 'when project is not shared with group' do @@ -292,7 +292,7 @@ describe ProjectTeam do end context 'group project' do - let(:group) { create(:group, :access_requestable) } + let(:group) { create(:group) } let!(:project) do create(:project, group: group) end diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index 12292dad142..24e66fe14c3 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -79,7 +79,7 @@ describe User do describe '#group_members' do it 'does not include group memberships for which user is a requester' do user = create(:user) - group = create(:group, :public, :access_requestable) + group = create(:group, :public) group.request_access(user) expect(user.group_members).to be_empty @@ -89,7 +89,7 @@ describe User do describe '#project_members' do it 'does not include project memberships for which user is a requester' do user = create(:user) - project = create(:project, :public, :access_requestable) + project = create(:project, :public) project.request_access(user) expect(user.project_members).to be_empty @@ -1191,7 +1191,7 @@ describe User do end describe '.without_projects' do - let!(:project) { create(:project, :public, :access_requestable) } + let!(:project) { create(:project, :public) } let!(:user) { create(:user) } let!(:user_without_project) { create(:user) } let!(:user_without_project2) { create(:user) } diff --git a/spec/requests/api/access_requests_spec.rb b/spec/requests/api/access_requests_spec.rb index 1af6602ea9e..100f3d33c7b 100644 --- a/spec/requests/api/access_requests_spec.rb +++ b/spec/requests/api/access_requests_spec.rb @@ -7,7 +7,7 @@ describe API::AccessRequests do set(:stranger) { create(:user) } set(:project) do - create(:project, :public, :access_requestable, creator_id: maintainer.id, namespace: maintainer.namespace) do |project| + create(:project, :public, creator_id: maintainer.id, namespace: maintainer.namespace) do |project| project.add_developer(developer) project.add_maintainer(maintainer) project.request_access(access_requester) @@ -15,7 +15,7 @@ describe API::AccessRequests do end set(:group) do - create(:group, :public, :access_requestable) do |group| + create(:group, :public) do |group| group.add_developer(developer) group.add_owner(maintainer) group.request_access(access_requester) diff --git a/spec/requests/api/badges_spec.rb b/spec/requests/api/badges_spec.rb index 1dd0cb4817c..771a78a2d91 100644 --- a/spec/requests/api/badges_spec.rb +++ b/spec/requests/api/badges_spec.rb @@ -345,7 +345,7 @@ describe API::Badges do end def setup_project - create(:project, :public, :access_requestable, creator_id: maintainer.id, namespace: project_group) do |project| + create(:project, :public, creator_id: maintainer.id, namespace: project_group) do |project| project.add_developer(developer) project.add_maintainer(maintainer) project.request_access(access_requester) @@ -356,7 +356,7 @@ describe API::Badges do end def setup_group - create(:group, :public, :access_requestable) do |group| + create(:group, :public) do |group| group.add_developer(developer) group.add_owner(maintainer) group.request_access(access_requester) diff --git a/spec/requests/api/members_spec.rb b/spec/requests/api/members_spec.rb index 26f6e705528..7e67ee28bef 100644 --- a/spec/requests/api/members_spec.rb +++ b/spec/requests/api/members_spec.rb @@ -7,7 +7,7 @@ describe API::Members do let(:stranger) { create(:user) } let(:project) do - create(:project, :public, :access_requestable, creator_id: maintainer.id, namespace: maintainer.namespace) do |project| + create(:project, :public, creator_id: maintainer.id, namespace: maintainer.namespace) do |project| project.add_developer(developer) project.add_maintainer(maintainer) project.request_access(access_requester) @@ -15,7 +15,7 @@ describe API::Members do end let!(:group) do - create(:group, :public, :access_requestable) do |group| + create(:group, :public) do |group| group.add_developer(developer) group.add_owner(maintainer) group.request_access(access_requester) diff --git a/spec/services/members/approve_access_request_service_spec.rb b/spec/services/members/approve_access_request_service_spec.rb index f56c31e51f6..5bbceac3dd0 100644 --- a/spec/services/members/approve_access_request_service_spec.rb +++ b/spec/services/members/approve_access_request_service_spec.rb @@ -3,8 +3,8 @@ require 'spec_helper' describe Members::ApproveAccessRequestService do - let(:project) { create(:project, :public, :access_requestable) } - let(:group) { create(:group, :public, :access_requestable) } + let(:project) { create(:project, :public) } + let(:group) { create(:group, :public) } let(:current_user) { create(:user) } let(:access_requester_user) { create(:user) } let(:access_requester) { source.requesters.find_by!(user_id: access_requester_user.id) } diff --git a/spec/services/members/request_access_service_spec.rb b/spec/services/members/request_access_service_spec.rb index 2e5275eb3f2..a0f7ae91bdb 100644 --- a/spec/services/members/request_access_service_spec.rb +++ b/spec/services/members/request_access_service_spec.rb @@ -41,7 +41,7 @@ describe Members::RequestAccessService do context 'when access requests are disabled' do %i[project group].each do |source_type| it_behaves_like 'a service raising Gitlab::Access::AccessDeniedError' do - let(:source) { create(source_type, :public) } + let(:source) { create(source_type, :public, :request_access_disabled) } end end end @@ -49,7 +49,7 @@ describe Members::RequestAccessService do context 'when current user can request access to the project' do %i[project group].each do |source_type| it_behaves_like 'a service creating a access request' do - let(:source) { create(source_type, :public, :access_requestable) } + let(:source) { create(source_type, :public) } end end end diff --git a/spec/services/notification_service_spec.rb b/spec/services/notification_service_spec.rb index bd6734634cb..70ce05927b4 100644 --- a/spec/services/notification_service_spec.rb +++ b/spec/services/notification_service_spec.rb @@ -1942,7 +1942,7 @@ describe NotificationService, :mailer do let(:developer) { create(:user) } let!(:group) do - create(:group, :public, :access_requestable) do |group| + create(:group, :public) do |group| group.add_owner(owner) group.add_maintainer(maintainer) group.add_developer(developer) @@ -1968,7 +1968,7 @@ describe NotificationService, :mailer do end it_behaves_like 'sends notification only to a maximum of ten, most recently active group owners' do - let(:group) { create(:group, :public, :access_requestable) } + let(:group) { create(:group, :public) } let(:notification_trigger) { group.request_access(added_user) } end end @@ -2029,7 +2029,7 @@ describe NotificationService, :mailer do let(:maintainer) { create(:user) } let!(:project) do - create(:project, :public, :access_requestable) do |project| + create(:project, :public) do |project| project.add_developer(developer) project.add_maintainer(maintainer) end @@ -2053,7 +2053,7 @@ describe NotificationService, :mailer do end it_behaves_like 'sends notification only to a maximum of ten, most recently active project maintainers' do - let(:project) { create(:project, :public, :access_requestable) } + let(:project) { create(:project, :public) } let(:notification_trigger) { project.request_access(added_user) } end end @@ -2064,7 +2064,7 @@ describe NotificationService, :mailer do context 'when the project has no maintainers' do context 'when the group has at least one owner' do - let!(:project) { create(:project, :public, :access_requestable, namespace: group) } + let!(:project) { create(:project, :public, namespace: group) } before do reset_delivered_emails! @@ -2079,14 +2079,14 @@ describe NotificationService, :mailer do end it_behaves_like 'sends notification only to a maximum of ten, most recently active group owners' do - let(:group) { create(:group, :public, :access_requestable) } + let(:group) { create(:group, :public) } let(:notification_trigger) { project.request_access(added_user) } end end context 'when the group does not have any owners' do let(:group) { create(:group) } - let!(:project) { create(:project, :public, :access_requestable, namespace: group) } + let!(:project) { create(:project, :public, namespace: group) } context 'recipients' do before do @@ -2107,7 +2107,7 @@ describe NotificationService, :mailer do let(:developer) { create(:user) } let!(:project) do - create(:project, :public, :access_requestable, namespace: group) do |project| + create(:project, :public, namespace: group) do |project| project.add_maintainer(maintainer) project.add_developer(developer) end @@ -2128,7 +2128,7 @@ describe NotificationService, :mailer do end it_behaves_like 'sends notification only to a maximum of ten, most recently active project maintainers' do - let(:project) { create(:project, :public, :access_requestable, namespace: group) } + let(:project) { create(:project, :public, namespace: group) } let(:notification_trigger) { project.request_access(added_user) } end end diff --git a/spec/services/projects/container_repository/cleanup_tags_service_spec.rb b/spec/services/projects/container_repository/cleanup_tags_service_spec.rb index 14247f1c71e..14772d172e8 100644 --- a/spec/services/projects/container_repository/cleanup_tags_service_spec.rb +++ b/spec/services/projects/container_repository/cleanup_tags_service_spec.rb @@ -157,6 +157,6 @@ describe Projects::ContainerRepository::CleanupTagsService do def expect_delete(digest) expect_any_instance_of(ContainerRegistry::Client) .to receive(:delete_repository_tag) - .with(repository.path, digest) + .with(repository.path, digest) { true } end end diff --git a/spec/services/projects/container_repository/delete_tags_service_spec.rb b/spec/services/projects/container_repository/delete_tags_service_spec.rb index 2ec5850c69e..f296ef3a776 100644 --- a/spec/services/projects/container_repository/delete_tags_service_spec.rb +++ b/spec/services/projects/container_repository/delete_tags_service_spec.rb @@ -87,6 +87,21 @@ describe Projects::ContainerRepository::DeleteTagsService do is_expected.to include(status: :success) end + + it 'succedes when tag delete returns 404' do + stub_upload("{\n \"config\": {\n }\n}", 'sha256:4435000728ee66e6a80e55637fc22725c256b61de344a2ecdeaac6bdb36e8bc3') + + stub_request(:put, "http://registry.gitlab/v2/#{repository.path}/manifests/A") + .to_return(status: 200, body: "", headers: { 'docker-content-digest' => 'sha256:dummy' }) + + stub_request(:put, "http://registry.gitlab/v2/#{repository.path}/manifests/Ba") + .to_return(status: 200, body: "", headers: { 'docker-content-digest' => 'sha256:dummy' }) + + stub_request(:delete, "http://registry.gitlab/v2/#{repository.path}/manifests/sha256:dummy") + .to_return(status: 404, body: "", headers: {}) + + is_expected.to include(status: :success) + end end end end |