Commit d2ffc30f authored by GitLab Bot's avatar GitLab Bot

Add latest changes from gitlab-org/gitlab@master

parent 914ea32e
...@@ -73,16 +73,15 @@ export default { ...@@ -73,16 +73,15 @@ export default {
const entry = data.entries[key]; const entry = data.entries[key];
const foundEntry = state.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) { if (!foundEntry) {
Object.assign(state.entries, { Object.assign(state.entries, {
[key]: entry, [key]: entry,
}); });
} else if (foundEntry.deleted) { } else if (foundEntry.deleted) {
Object.assign(state.entries, { Object.assign(state.entries, {
[key]: { [key]: Object.assign(entry, { replaces: true }),
...entry,
replaces: true,
},
}); });
} else { } else {
const tree = entry.tree.filter( const tree = entry.tree.filter(
......
...@@ -15,15 +15,19 @@ export default { ...@@ -15,15 +15,19 @@ export default {
GlLoadingIcon, GlLoadingIcon,
}, },
props: { props: {
endpoint: {
type: String,
required: true,
},
characterError: { characterError: {
type: Boolean, type: Boolean,
required: false, required: false,
default: false, default: false,
}, },
containersErrorImage: {
type: String,
required: true,
},
endpoint: {
type: String,
required: true,
},
helpPagePath: { helpPagePath: {
type: String, type: String,
required: true, required: true,
...@@ -32,7 +36,11 @@ export default { ...@@ -32,7 +36,11 @@ export default {
type: String, type: String,
required: true, required: true,
}, },
containersErrorImage: { personalAccessTokensHelpLink: {
type: String,
required: true,
},
registryHostUrlWithPort: {
type: String, type: String,
required: true, required: true,
}, },
...@@ -40,6 +48,10 @@ export default { ...@@ -40,6 +48,10 @@ export default {
type: String, type: String,
required: true, required: true,
}, },
twoFactorAuthHelpLink: {
type: String,
required: true,
},
}, },
store, store,
computed: { computed: {
...@@ -79,6 +91,26 @@ export default { ...@@ -79,6 +91,26 @@ export default {
false, 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() { dockerBuildCommand() {
// eslint-disable-next-line @gitlab/i18n/no-non-i18n-strings // eslint-disable-next-line @gitlab/i18n/no-non-i18n-strings
return `docker build -t ${this.repositoryUrl} .`; return `docker build -t ${this.repositoryUrl} .`;
...@@ -130,6 +162,17 @@ export default { ...@@ -130,6 +162,17 @@ export default {
<template #description> <template #description>
<p class="js-no-container-images-text" v-html="noContainerImagesText"></p> <p class="js-no-container-images-text" v-html="noContainerImagesText"></p>
<h5>{{ s__('ContainerRegistry|Quick Start') }}</h5> <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> <p>
{{ {{
s__( s__(
......
...@@ -13,23 +13,29 @@ export default () => ...@@ -13,23 +13,29 @@ export default () =>
data() { data() {
const { dataset } = document.querySelector(this.$options.el); const { dataset } = document.querySelector(this.$options.el);
return { return {
endpoint: dataset.endpoint,
characterError: Boolean(dataset.characterError), characterError: Boolean(dataset.characterError),
containersErrorImage: dataset.containersErrorImage,
endpoint: dataset.endpoint,
helpPagePath: dataset.helpPagePath, helpPagePath: dataset.helpPagePath,
noContainersImage: dataset.noContainersImage, noContainersImage: dataset.noContainersImage,
containersErrorImage: dataset.containersErrorImage, personalAccessTokensHelpLink: dataset.personalAccessTokensHelpLink,
registryHostUrlWithPort: dataset.registryHostUrlWithPort,
repositoryUrl: dataset.repositoryUrl, repositoryUrl: dataset.repositoryUrl,
twoFactorAuthHelpLink: dataset.twoFactorAuthHelpLink,
}; };
}, },
render(createElement) { render(createElement) {
return createElement('registry-app', { return createElement('registry-app', {
props: { props: {
endpoint: this.endpoint,
characterError: this.characterError, characterError: this.characterError,
containersErrorImage: this.containersErrorImage,
endpoint: this.endpoint,
helpPagePath: this.helpPagePath, helpPagePath: this.helpPagePath,
noContainersImage: this.noContainersImage, noContainersImage: this.noContainersImage,
containersErrorImage: this.containersErrorImage, personalAccessTokensHelpLink: this.personalAccessTokensHelpLink,
registryHostUrlWithPort: this.registryHostUrlWithPort,
repositoryUrl: this.repositoryUrl, repositoryUrl: this.repositoryUrl,
twoFactorAuthHelpLink: this.twoFactorAuthHelpLink,
}, },
}); });
}, },
......
...@@ -39,9 +39,6 @@ export default { ...@@ -39,9 +39,6 @@ export default {
ariaLabel() { ariaLabel() {
return this.isCollapsed ? __('Expand') : __('Collapse'); return this.isCollapsed ? __('Expand') : __('Collapse');
}, },
isButtonDisabled() {
return this.isLoading || this.hasError;
},
}, },
methods: { methods: {
toggleCollapsed() { toggleCollapsed() {
...@@ -53,25 +50,35 @@ export default { ...@@ -53,25 +50,35 @@ export default {
<template> <template>
<div> <div>
<div class="mr-widget-extension d-flex align-items-center pl-3"> <div class="mr-widget-extension d-flex align-items-center pl-3">
<gl-button <div v-if="hasError" class="ci-widget media">
class="btn-blank btn s32 square append-right-default" <div class="media-body">
:aria-label="ariaLabel" <span class="gl-font-size-small mr-widget-margin-left gl-line-height-24 js-error-state">{{
:disabled="isButtonDisabled" title
@click="toggleCollapsed" }}</span>
> </div>
<gl-loading-icon v-if="isLoading" /> </div>
<icon v-else :name="arrowIconName" class="js-icon" />
</gl-button> <template v-else>
<gl-button <gl-button
variant="link" class="btn-blank btn s32 square append-right-default"
class="js-title" :aria-label="ariaLabel"
:disabled="isButtonDisabled" :disabled="isLoading"
:class="{ 'border-0': isButtonDisabled }" @click="toggleCollapsed"
@click="toggleCollapsed" >
> <gl-loading-icon v-if="isLoading" />
<template v-if="isCollapsed">{{ title }}</template> <icon v-else :name="arrowIconName" class="js-icon" />
<template v-else>{{ __('Collapse') }}</template> </gl-button>
</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>
<div v-if="!isCollapsed" class="border-top js-slot-container"> <div v-if="!isCollapsed" class="border-top js-slot-container">
......
...@@ -560,3 +560,6 @@ img.emoji { ...@@ -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; }
...@@ -833,6 +833,7 @@ Merge Requests ...@@ -833,6 +833,7 @@ Merge Requests
*/ */
$mr-tabs-height: 48px; $mr-tabs-height: 48px;
$mr-version-controls-height: 56px; $mr-version-controls-height: 56px;
$mr-widget-margin-left: 40px;
/* /*
Compare Branches Compare Branches
......
...@@ -19,6 +19,8 @@ ...@@ -19,6 +19,8 @@
border-top: 1px solid $border-color; border-top: 1px solid $border-color;
} }
.mr-widget-margin-left { margin-left: $mr-widget-margin-left; }
.media-section { .media-section {
@include media-breakpoint-down(md) { @include media-breakpoint-down(md) {
align-items: flex-start; align-items: flex-start;
......
...@@ -108,6 +108,11 @@ module ApplicationHelper ...@@ -108,6 +108,11 @@ module ApplicationHelper
Gitlab.config.extra Gitlab.config.extra
end end
# shortcut for gitlab registry config
def registry_config
Gitlab.config.registry
end
# Render a `time` element with Javascript-based relative date and tooltip # Render a `time` element with Javascript-based relative date and tooltip
# #
# time - Time object # time - Time object
......
...@@ -67,7 +67,7 @@ class ContainerRepository < ApplicationRecord ...@@ -67,7 +67,7 @@ class ContainerRepository < ApplicationRecord
def delete_tags! def delete_tags!
return unless has_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| digests.all? do |digest|
delete_tag_by_digest(digest) delete_tag_by_digest(digest)
......
...@@ -48,10 +48,10 @@ module Projects ...@@ -48,10 +48,10 @@ module Projects
# rubocop: disable CodeReuse/ActiveRecord # rubocop: disable CodeReuse/ActiveRecord
Gitlab::Sentry.track_exception(ArgumentError.new('multiple tag digests')) if tag_digests.many? Gitlab::Sentry.track_exception(ArgumentError.new('multiple tag digests')) if tag_digests.many?
# deletes the dummy image # Deletes the dummy image
# all created tag digests are the same since they all have the same 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 # 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) success(deleted: tag_names)
else else
error('could not delete tags') error('could not delete tags')
......
...@@ -5,7 +5,10 @@ ...@@ -5,7 +5,10 @@
.col-12 .col-12
#js-vue-registry-images{ data: { endpoint: project_container_registry_index_path(@project, format: :json), #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'), "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'), "no_containers_image" => image_path('illustrations/docker-empty-state.svg'),
"containers_error_image" => image_path('illustrations/docker-error-state.svg'), "containers_error_image" => image_path('illustrations/docker-error-state.svg'),
"repository_url" => escape_once(@project.container_registry_url), "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 } } character_error: @character_error.to_s } }
...@@ -3,6 +3,4 @@ ...@@ -3,6 +3,4 @@
.form-check .form-check
= form.check_box :request_access_enabled, class: 'form-check-input', data: { qa_selector: 'request_access_checkbox' } = 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 = form.label :request_access_enabled, class: 'form-check-label' do
%span{ class: label_class }= _('Allow users to request access') %span{ class: label_class }= _('Allow users to request access (if visibility is public or internal)')
%br
%span.text-muted= _('Allow users to request access if visibility is public or internal.')
---
title: Enable Request Access functionality by default for new projects and groups
merge_request: 17662
author:
type: changed
---
title: Fix Web IDE tree not updating modified status
merge_request: 18647
author:
type: fixed
---
title: Adds login input with copy box and supporting copy to empty container registry view
merge_request: 18244
author: nate geslin
type: added
---
title: Expose subscribed attribute for epic on API
merge_request: 18475
author:
type: added
...@@ -34,6 +34,12 @@ Sidekiq.configure_server do |config| ...@@ -34,6 +34,12 @@ Sidekiq.configure_server do |config|
config.on(:startup) do config.on(:startup) do
# webserver metrics are cleaned up in config.ru: `warmup` block # webserver metrics are cleaned up in config.ru: `warmup` block
Prometheus::CleanupMultiprocDirService.new.execute 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 Gitlab::Metrics::Exporter::SidekiqExporter.instance.start
end end
......
# 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
# 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
...@@ -2333,7 +2333,7 @@ ActiveRecord::Schema.define(version: 2019_10_04_133612) do ...@@ -2333,7 +2333,7 @@ ActiveRecord::Schema.define(version: 2019_10_04_133612) do
t.boolean "membership_lock", default: false t.boolean "membership_lock", default: false
t.boolean "share_with_group_lock", default: false t.boolean "share_with_group_lock", default: false
t.integer "visibility_level", default: 20, null: 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_status", default: "ready", null: false
t.string "ldap_sync_error" t.string "ldap_sync_error"
t.datetime "ldap_sync_last_update_at" t.datetime "ldap_sync_last_update_at"
...@@ -2922,7 +2922,7 @@ ActiveRecord::Schema.define(version: 2019_10_04_133612) do ...@@ -2922,7 +2922,7 @@ ActiveRecord::Schema.define(version: 2019_10_04_133612) do
t.boolean "has_external_issue_tracker" t.boolean "has_external_issue_tracker"
t.string "repository_storage", default: "default", null: false t.string "repository_storage", default: "default", null: false
t.boolean "repository_read_only" 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.boolean "has_external_wiki"
t.string "ci_config_path" t.string "ci_config_path"
t.boolean "lfs_enabled" t.boolean "lfs_enabled"
......
...@@ -147,7 +147,8 @@ Example response: ...@@ -147,7 +147,8 @@ Example response:
"closed_at": "2018-08-18T12:22:05.239Z", "closed_at": "2018-08-18T12:22:05.239Z",
"labels": [], "labels": [],
"upvotes": 4, "upvotes": 4,
"downvotes": 0 "downvotes": 0,
"subscribed": true
} }
``` ```
......
...@@ -36,7 +36,9 @@ module ContainerRegistry ...@@ -36,7 +36,9 @@ module ContainerRegistry
end end
def delete_repository_tag(name, reference) 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 end
def upload_raw_blob(path, blob) def upload_raw_blob(path, blob)
...@@ -84,7 +86,9 @@ module ContainerRegistry ...@@ -84,7 +86,9 @@ module ContainerRegistry
end end
def delete_blob(name, digest) 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 end
def put_tag(name, reference, manifest) def put_tag(name, reference, manifest)
......
...@@ -5,7 +5,7 @@ module Gitlab ...@@ -5,7 +5,7 @@ module Gitlab
class GitalyCheck class GitalyCheck
extend BaseAbstractCheck extend BaseAbstractCheck
METRIC_PREFIX = 'gitaly_health_check' METRIC_PREFIX = 'gitaly_health_check'.freeze
class << self class << self
def readiness def readiness
......
...@@ -6,10 +6,10 @@ module Gitlab ...@@ -6,10 +6,10 @@ module Gitlab
class Readiness class Readiness
attr_reader :checks attr_reader :checks
# This accepts an array of Proc # This accepts an array of objects implementing `:readiness`
# that returns `::Gitlab::HealthChecks::Result` # that returns `::Gitlab::HealthChecks::Result`
def initialize(*additional_checks) def initialize(*additional_checks)
@checks = ::Gitlab::HealthChecks::CHECKS.map { |check| check.method(:readiness) } @checks = ::Gitlab::HealthChecks::CHECKS
@checks += additional_checks @checks += additional_checks
end end
...@@ -43,7 +43,7 @@ module Gitlab ...@@ -43,7 +43,7 @@ module Gitlab
def probe_readiness def probe_readiness
checks checks
.flat_map(&:call) .flat_map(&:readiness)
.compact .compact
.group_by(&:name) .group_by(&:name)
end end
......
# 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
...@@ -7,6 +7,8 @@ module Gitlab ...@@ -7,6 +7,8 @@ module Gitlab
def readiness def readiness
check_result = check check_result = check
return if check_result.nil?
if successful?(check_result) if successful?(check_result)
HealthChecks::Result.new(name, true) HealthChecks::Result.new(name, true)
elsif check_result.is_a?(Timeout::Error) elsif check_result.is_a?(Timeout::Error)
...@@ -20,6 +22,8 @@ module Gitlab ...@@ -20,6 +22,8 @@ module Gitlab
def metrics def metrics
result, elapsed = with_timing(&method(:check)) 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 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), metric("#{metric_prefix}_timeout", result.is_a?(Timeout::Error) ? 1 : 0),
......
# 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
...@@ -6,6 +6,8 @@ module Gitlab ...@@ -6,6 +6,8 @@ module Gitlab
class BaseExporter < Daemon class BaseExporter < Daemon
attr_reader :server attr_reader :server
attr_accessor :additional_checks
def enabled? def enabled?
settings.enabled settings.enabled
end end
...@@ -32,12 +34,10 @@ module Gitlab ...@@ -32,12 +34,10 @@ module Gitlab
Port: settings.port, BindAddress: settings.address, Port: settings.port, BindAddress: settings.address,
Logger: logger, AccessLog: access_log) Logger: logger, AccessLog: access_log)
server.mount_proc '/readiness' do |req, res| server.mount_proc '/readiness' do |req, res|
render_probe( render_probe(readiness_probe, req, res)
::Gitlab::HealthChecks::Probes::Readiness.new, req, res)
end end
server.mount_proc '/liveness' do |req, res| server.mount_proc '/liveness' do |req, res|
render_probe( render_probe(liveness_probe, req, res)
::Gitlab::HealthChecks::Probes::Liveness.new, req, res)
end end
server.mount '/', Rack::Handler::WEBrick, rack_app server.mount '/', Rack::Handler::WEBrick, rack_app
...@@ -52,8 +52,10 @@ module Gitlab ...@@ -52,8 +52,10 @@ module Gitlab
def stop_working def stop_working
if server 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.shutdown
server.listeners.each(&:close)
end end
@server = nil @server = nil
...@@ -67,6 +69,14 @@ module Gitlab ...@@ -67,6 +69,14 @@ module Gitlab
end end
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) def render_probe(probe, req, res)
result = probe.execute result = probe.execute
......
...@@ -7,6 +7,16 @@ module Gitlab ...@@ -7,6 +7,16 @@ module Gitlab
module Metrics module Metrics
module Exporter module Exporter
class WebExporter < BaseExporter 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 def settings
Settings.monitoring.web_exporter Settings.monitoring.web_exporter
end end
......
...@@ -1413,10 +1413,7 @@ msgstr "" ...@@ -1413,10 +1413,7 @@ msgstr ""
msgid "Allow users to register any application to use GitLab as an OAuth provider" msgid "Allow users to register any application to use GitLab as an OAuth provider"
msgstr "" msgstr ""
msgid "Allow users to request access" msgid "Allow users to request access (if visibility is public or internal)"
msgstr ""
msgid "Allow users to request access if visibility is public or internal."
msgstr "" msgstr ""
msgid "Allowed email domain restriction only permitted for top-level groups" msgid "Allowed email domain restriction only permitted for top-level groups"
...@@ -4352,12 +4349,18 @@ msgstr "" ...@@ -4352,12 +4349,18 @@ msgstr ""
msgid "ContainerRegistry|Copy build command" msgid "ContainerRegistry|Copy build command"
msgstr "" msgstr ""
msgid "ContainerRegistry|Copy login command"
msgstr ""
msgid "ContainerRegistry|Copy push command" msgid "ContainerRegistry|Copy push command"
msgstr "" msgstr ""
msgid "ContainerRegistry|Docker connection error" msgid "ContainerRegistry|Docker connection error"
msgstr "" 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" msgid "ContainerRegistry|Last Updated"
msgstr "" msgstr ""
......
...@@ -153,7 +153,8 @@ redis: ...@@ -153,7 +153,8 @@ redis:
redis-ha: redis-ha:
enabled: false enabled: false
registry: registry:
minReplicas: 1 hpa:
minReplicas: 1
resources: resources:
requests: requests:
cpu: 50m cpu: 50m
......
...@@ -6,7 +6,7 @@ describe Groups::GroupMembersController do ...@@ -6,7 +6,7 @@ describe Groups::GroupMembersController do
include ExternalAuthorizationServiceHelpers include ExternalAuthorizationServiceHelpers
let(:user) { create(:user) } let(:user) { create(:user) }
let(:group) { create(:group, :public, :access_requestable) } let(:group) { create(:group, :public) }
let(:membership) { create(:group_member, group: group) } let(:membership) { create(:group_member, group: group) }
describe 'GET index' do describe 'GET index' do
......
...@@ -4,7 +4,7 @@ require('spec_helper') ...@@ -4,7 +4,7 @@ require('spec_helper')
describe Projects::ProjectMembersController do describe Projects::ProjectMembersController do
let(:user) { create(:user) } let(:user) { create(:user) }
let(:project) { create(:project, :public, :access_requestable) } let(:project) { create(:project, :public) }
describe 'GET index' do describe 'GET index' do
it 'has the project_members address with a 200 status code' do it 'has the project_members address with a 200 status code' do
......
...@@ -32,8 +32,8 @@ FactoryBot.define do ...@@ -32,8 +32,8 @@ FactoryBot.define do
avatar { fixture_file_upload('spec/fixtures/dk.png') } avatar { fixture_file_upload('spec/fixtures/dk.png') }
end end
trait :access_requestable do trait :request_access_disabled do
request_access_enabled { true } request_access_enabled { false }
end end
trait :nested do trait :nested do
......
...@@ -117,8 +117,8 @@ FactoryBot.define do ...@@ -117,8 +117,8 @@ FactoryBot.define do
storage_version { nil } storage_version { nil }
end end
trait :access_requestable do trait :request_access_disabled do
request_access_enabled { true } request_access_enabled { false }
end end
trait :with_avatar do trait :with_avatar do
......
...@@ -4,7 +4,7 @@ require 'spec_helper' ...@@ -4,7 +4,7 @@ require 'spec_helper'
describe 'Groups > Members > Maintainer manages access requests' do describe 'Groups > Members > Maintainer manages access requests' do
it_behaves_like '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) } let(:members_page_path) { group_group_members_path(entity) }
end end
end end
...@@ -5,7 +5,7 @@ require 'spec_helper' ...@@ -5,7 +5,7 @@ require 'spec_helper'
describe 'Groups > Members > Request access' do describe 'Groups > Members > Request access' do
let(:user) { create(:user) } let(:user) { create(:user) }
let(:owner) { 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) } let!(:project) { create(:project, :private, namespace: group) }
before do before do
......
...@@ -5,8 +5,8 @@ require 'spec_helper' ...@@ -5,8 +5,8 @@ require 'spec_helper'
describe 'Projects members' do describe 'Projects members' do
let(:user) { create(:user) } let(:user) { create(:user) }
let(:developer) { create(:user) } let(:developer) { create(:user) }
let(:group) { create(:group, :public, :access_requestable) } let(:group) { create(:group, :public) }
let(:project) { create(:project, :public, :access_requestable, creator: user, group: group) } 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(: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(:group_invitee) { create(:group_member, group: group, invite_token: '123', invite_email: 'test2@abc.com', user: nil) }
let(:project_requester) { create(:user) } let(:project_requester) { create(:user) }
......
...@@ -5,8 +5,8 @@ require 'spec_helper' ...@@ -5,8 +5,8 @@ require 'spec_helper'
describe 'Projects > Members > Group requester cannot request access to project', :js do describe 'Projects > Members > Group requester cannot request access to project', :js do
let(:user) { create(:user) } let(:user) { create(:user) }
let(:owner) { create(:user) } let(:owner) { create(:user) }
let(:group) { create(:group, :public, :access_requestable) } let(:group) { create(:group, :public) }
let(:project) { create(:project, :public, :access_requestable, namespace: group) } let(:project) { create(:project, :public, namespace: group) }
before do before do
group.add_owner(owner) group.add_owner(owner)
......
...@@ -4,7 +4,7 @@ require 'spec_helper' ...@@ -4,7 +4,7 @@ require 'spec_helper'
describe 'Projects > Members > Maintainer manages access requests' do describe 'Projects > Members > Maintainer manages access requests' do
it_behaves_like '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) } let(:members_page_path) { project_project_members_path(entity) }
end end
end end
...@@ -4,7 +4,7 @@ require 'spec_helper' ...@@ -4,7 +4,7 @@ require 'spec_helper'
describe 'Projects > Members > User requests access', :js do describe 'Projects > Members > User requests access', :js do
let(:user) { create(:user) } let(:user) { create(:user) }
let(:project) { create(:project, :public, :access_requestable, :repository) } let(:project) { create(:project, :public, :repository) }
let(:maintainer) { project.owner } let(:maintainer) { project.owner }
before do before do
......
...@@ -7,13 +7,13 @@ describe AccessRequestsFinder do ...@@ -7,13 +7,13 @@ describe AccessRequestsFinder do
let(:access_requester) { create(:user) } let(:access_requester) { create(:user) }
let(:project) do let(:project) do
create(:project, :public, :access_requestable) do |project| create(:project, :public) do |project|
project.request_access(access_requester) project.request_access(access_requester)
end end
end end
let(:group) do let(:group) do
create(:group, :public, :access_requestable) do |group| create(:group, :public) do |group|
group.request_access(access_requester) group.request_access(access_requester)
end end
end end
......
...@@ -4,7 +4,7 @@ require 'spec_helper' ...@@ -4,7 +4,7 @@ require 'spec_helper'
describe GroupMembersFinder, '#execute' do describe GroupMembersFinder, '#execute' do
let(:group) { create(:group) } 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(:user1) { create(:user) }
let(:user2) { create(:user) } let(:user2) { create(:user) }
let(:user3) { create(:user) } let(:user3) { create(:user) }
......
...@@ -4,7 +4,7 @@ require 'spec_helper' ...@@ -4,7 +4,7 @@ require 'spec_helper'
describe MembersFinder, '#execute' do describe MembersFinder, '#execute' do
set(:group) { create(:group) } 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(:project) { create(:project, namespace: nested_group) }
set(:user1) { create(:user) } set(:user1) { create(:user) }
set(:user2) { create(:user) } set(:user2) { create(:user) }
...@@ -57,7 +57,7 @@ describe MembersFinder, '#execute' do ...@@ -57,7 +57,7 @@ describe MembersFinder, '#execute' do
context 'when include_invited_groups_members == true' do context 'when include_invited_groups_members == true' do
subject { described_class.new(project, user2).execute(include_invited_groups_members: true) } 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(:nested_linked_group) { create(:group, parent: linked_group) }
set(:linked_group_member) { linked_group.add_guest(user1) } set(:linked_group_member) { linked_group.add_guest(user1) }
set(:nested_linked_group_member) { nested_linked_group.add_guest(user2) } set(:nested_linked_group_member) { nested_linked_group.add_guest(user2) }
......
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,
);
});
});
});
});
import registry from '~/registry/components/app.vue';
import { mount } from '@vue/test-utils'; import { mount } from '@vue/test-utils';
import registry from '~/registry/components/app.vue';
import { TEST_HOST } from '../../helpers/test_constants'; import { TEST_HOST } from '../../helpers/test_constants';
import { reposServerResponse, parsedReposServerResponse } from '../mock_data'; import { reposServerResponse, parsedReposServerResponse } from '../mock_data';
...@@ -8,6 +8,7 @@ describe('Registry List', () => { ...@@ -8,6 +8,7 @@ describe('Registry List', () => {
const findCollapsibleContainer = w => w.findAll({ name: 'CollapsibeContainerRegisty' }); const findCollapsibleContainer = w => w.findAll({ name: 'CollapsibeContainerRegisty' });
const findNoContainerImagesText = w => w.find('.js-no-container-images-text'); 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 findSpinner = w => w.find('.gl-spinner');
const findCharacterErrorText = w => w.find('.js-character-error-text'); const findCharacterErrorText = w => w.find('.js-character-error-text');
...@@ -17,6 +18,9 @@ describe('Registry List', () => { ...@@ -17,6 +18,9 @@ describe('Registry List', () => {
noContainersImage: 'foo', noContainersImage: 'foo',
containersErrorImage: 'foo', containersErrorImage: 'foo',
repositoryUrl: 'foo', repositoryUrl: 'foo',
registryHostUrlWithPort: 'foo',
personalAccessTokensHelpLink: 'foo',
twoFactorAuthHelpLink: 'foo',
}; };
const setMainEndpoint = jest.fn(); const setMainEndpoint = jest.fn();
...@@ -67,6 +71,13 @@ describe('Registry List', () => { ...@@ -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', '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', () => { describe('while loading data', () => {
......
...@@ -44,6 +44,7 @@ describe('Merge Requests Artifacts list app', () => { ...@@ -44,6 +44,7 @@ describe('Merge Requests Artifacts list app', () => {
const findButtons = () => wrapper.findAll('button'); const findButtons = () => wrapper.findAll('button');
const findTitle = () => wrapper.find('.js-title'); const findTitle = () => wrapper.find('.js-title');
const findErrorMessage = () => wrapper.find('.js-error-state');
const findTableRows = () => wrapper.findAll('tbody tr'); const findTableRows = () => wrapper.findAll('tbody tr');
describe('while loading', () => { describe('while loading', () => {
...@@ -109,13 +110,12 @@ describe('Merge Requests Artifacts list app', () => { ...@@ -109,13 +110,12 @@ describe('Merge Requests Artifacts list app', () => {
}); });
it('renders the error state', () => { 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(); const buttons = findButtons();
expect(buttons.at(0).attributes('disabled')).toBe('disabled'); expect(buttons.exists()).toBe(false);
expect(buttons.at(1).attributes('disabled')).toBe('disabled');
}); });
}); });
}); });
...@@ -20,6 +20,7 @@ describe('Merge Request Collapsible Extension', () => { ...@@ -20,6 +20,7 @@ describe('Merge Request Collapsible Extension', () => {
}; };
const findTitle = () => wrapper.find('.js-title'); const findTitle = () => wrapper.find('.js-title');
const findErrorMessage = () => wrapper.find('.js-error-state');
afterEach(() => { afterEach(() => {
wrapper.destroy(); wrapper.destroy();
...@@ -87,19 +88,12 @@ describe('Merge Request Collapsible Extension', () => { ...@@ -87,19 +88,12 @@ describe('Merge Request Collapsible Extension', () => {
mountComponent(Object.assign({}, data, { hasError: true })); mountComponent(Object.assign({}, data, { hasError: true }));
}); });
it('renders the buttons disabled', () => { it('does not render the buttons', () => {
expect( expect(wrapper.findAll('button').exists()).toBe(false);
wrapper });
.findAll('button')
.at(0) it('renders title message provided', () => {
.attributes('disabled'), expect(findErrorMessage().text()).toBe(data.title);
).toEqual('disabled');
expect(
wrapper
.findAll('button')
.at(1)
.attributes('disabled'),
).toEqual('disabled');
}); });
}); });
}); });
...@@ -5,11 +5,11 @@ require 'spec_helper' ...@@ -5,11 +5,11 @@ require 'spec_helper'
describe MembersHelper do describe MembersHelper do
describe '#remove_member_message' do describe '#remove_member_message' do
let(:requester) { create(:user) } 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) { build(:project_member, project: project) }
let(:project_member_invite) { build(:project_member, project: project).tap { |m| m.generate_invite_token! } } let(:project_member_invite) { build(:project_member, project: project).tap { |m| m.generate_invite_token! } }
let(:project_member_request) { project.request_access(requester) } 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) { build(:group_member, group: group) }
let(:group_member_invite) { build(:group_member, group: group).tap { |m| m.generate_invite_token! } } let(:group_member_invite) { build(:group_member, group: group).tap { |m| m.generate_invite_token! } }
let(:group_member_request) { group.request_access(requester) } let(:group_member_request) { group.request_access(requester) }
...@@ -26,10 +26,10 @@ describe MembersHelper do ...@@ -26,10 +26,10 @@ describe MembersHelper do
describe '#remove_member_title' do describe '#remove_member_title' do
let(:requester) { create(:user) } 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) { build(:project_member, project: project) }
let(:project_member_request) { project.request_access(requester) } 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) { build(:group_member, group: group) }
let(:group_member_request) { group.request_access(requester) } let(:group_member_request) { group.request_access(requester) }
......
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
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
...@@ -64,6 +64,18 @@ describe Gitlab::Metrics::Exporter::BaseExporter do ...@@ -64,6 +64,18 @@ describe Gitlab::Metrics::Exporter::BaseExporter do
exporter.start.join exporter.start.join
end end
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 end
describe '#stop' do describe '#stop' do
......
...@@ -714,7 +714,7 @@ describe Notify do ...@@ -714,7 +714,7 @@ describe Notify do
describe 'project access requested' do describe 'project access requested' do
let(:project) do let(:project) do
create(:project, :public, :access_requestable) do |project| create(:project, :public) do |project|
project.add_maintainer(project.owner) project.add_maintainer(project.owner)
end end
end end
...@@ -743,7 +743,7 @@ describe Notify do ...@@ -743,7 +743,7 @@ describe Notify do
end end
describe 'project access denied' do describe 'project access denied' do
let(:project) { create(:project, :public, :access_requestable) } let(:project) { create(:project, :public) }
let(:project_member) do let(:project_member) do
project.request_access(user) project.request_access(user)
project.requesters.find_by(user_id: user.id) project.requesters.find_by(user_id: user.id)
...@@ -765,7 +765,7 @@ describe Notify do ...@@ -765,7 +765,7 @@ describe Notify do
describe 'project access changed' do describe 'project access changed' do
let(:owner) { create(:user, name: "Chang O'Keefe") } 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) } let(:project_member) { create(:project_member, project: project, user: user) }
subject { described_class.member_access_granted_email('project', project_member.id) } subject { described_class.member_access_granted_email('project', project_member.id) }
...@@ -1167,7 +1167,7 @@ describe Notify do ...@@ -1167,7 +1167,7 @@ describe Notify do
context 'for a group' do context 'for a group' do
describe 'group access requested' do describe 'group access requested' do
let(:group) { create(:group, :public, :access_requestable) } let(:group) { create(:group, :public) }
let(:group_member) do let(:group_member) do
group.request_access(user) group.request_access(user)
group.requesters.find_by(user_id: user.id) group.requesters.find_by(user_id: user.id)
......
...@@ -4,7 +4,7 @@ require 'spec_helper' ...@@ -4,7 +4,7 @@ require 'spec_helper'
describe Ci::BuildMetadata do describe Ci::BuildMetadata do
set(:user) { create(:user) } 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(:project) { create(:project, :repository, group: group, build_timeout: 2000) }
set(:pipeline) do set(:pipeline) do
......
...@@ -4,7 +4,7 @@ require 'spec_helper' ...@@ -4,7 +4,7 @@ require 'spec_helper'
describe Ci::Build do describe Ci::Build do
set(:user) { create(:user) } set(:user) { create(:user) }
set(:group) { create(:group, :access_requestable) } set(:group) { create(:group) }
set(:project) { create(:project, :repository, group: group) } set(:project) { create(:project, :repository, group: group) }
set(:pipeline) do set(:pipeline) do
......
...@@ -5,7 +5,7 @@ require 'spec_helper' ...@@ -5,7 +5,7 @@ require 'spec_helper'
describe AccessRequestable do describe AccessRequestable do
describe 'Group' do describe 'Group' do
describe '#request_access' do describe '#request_access' do
let(:group) { create(:group, :public, :access_requestable) } let(:group) { create(:group, :public) }
let(:user) { create(:user) } let(:user) { create(:user) }
it { expect(group.request_access(user)).to be_a(GroupMember) } it { expect(group.request_access(user)).to be_a(GroupMember) }
...@@ -13,7 +13,7 @@ describe AccessRequestable do ...@@ -13,7 +13,7 @@ describe AccessRequestable do
end end
describe '#access_requested?' do describe '#access_requested?' do
let(:group) { create(:group, :public, :access_requestable) } let(:group) { create(:group, :public) }
let(:user) { create(:user) } let(:user) { create(:user) }
before do before do
...@@ -26,14 +26,14 @@ describe AccessRequestable do ...@@ -26,14 +26,14 @@ describe AccessRequestable do
describe 'Project' do describe 'Project' do
describe '#request_access' do describe '#request_access' do
let(:project) { create(:project, :public, :access_requestable) } let(:project) { create(:project, :public) }
let(:user) { create(:user) } let(:user) { create(:user) }
it { expect(project.request_access(user)).to be_a(ProjectMember) } it { expect(project.request_access(user)).to be_a(ProjectMember) }
end end
describe '#access_requested?' do describe '#access_requested?' do
let(:project) { create(:project, :public, :access_requestable) } let(:project) { create(:project, :public) }
let(:user) { create(:user) } let(:user) { create(:user) }
before do before do
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
require 'spec_helper' require 'spec_helper'
describe Group do describe Group do
let!(:group) { create(:group, :access_requestable) } let!(:group) { create(:group) }
describe 'associations' do describe 'associations' do
it { is_expected.to have_many :projects } it { is_expected.to have_many :projects }
...@@ -331,7 +331,7 @@ describe Group do ...@@ -331,7 +331,7 @@ describe Group do
end end
describe '#avatar_url' do describe '#avatar_url' do
let!(:group) { create(:group, :access_requestable, :with_avatar) } let!(:group) { create(:group, :with_avatar) }
let(:user) { create(:user) } let(:user) { create(:user) }
context 'when avatar file is uploaded' do context 'when avatar file is uploaded' do
......
...@@ -92,7 +92,7 @@ describe Member do ...@@ -92,7 +92,7 @@ describe Member do
describe 'Scopes & finders' do describe 'Scopes & finders' do
before do before do
project = create(:project, :public, :access_requestable) project = create(:project, :public)
group = create(:group) group = create(:group)
@owner_user = create(:user).tap { |u| group.add_owner(u) } @owner_user = create(:user).tap { |u| group.add_owner(u) }
@owner = group.members.find_by(user_id: @owner_user.id) @owner = group.members.find_by(user_id: @owner_user.id)
...@@ -230,7 +230,7 @@ describe Member do ...@@ -230,7 +230,7 @@ describe Member do
describe '.add_user' do describe '.add_user' do
%w[project group].each do |source_type| %w[project group].each do |source_type|
context "when source is a #{source_type}" do 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!(:user) { create(:user) }
let!(:admin) { create(:admin) } let!(:admin) { create(:admin) }
...@@ -437,7 +437,7 @@ describe Member do ...@@ -437,7 +437,7 @@ describe Member do
describe '.add_users' do describe '.add_users' do
%w[project group].each do |source_type| %w[project group].each do |source_type|
context "when source is a #{source_type}" do 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!(:admin) { create(:admin) }
let(:user1) { create(:user) } let(:user1) { create(:user) }
let(:user2) { create(:user) } let(:user2) { create(:user) }
......
...@@ -153,7 +153,7 @@ describe Project do ...@@ -153,7 +153,7 @@ describe Project do
end end
describe '#members & #requesters' do describe '#members & #requesters' do
let(:project) { create(:project, :public, :access_requestable) } let(:project) { create(:project, :public) }
let(:requester) { create(:user) } let(:requester) { create(:user) }
let(:developer) { create(:user) } let(:developer) { create(:user) }
before do before do
......
...@@ -141,7 +141,7 @@ describe ProjectTeam do ...@@ -141,7 +141,7 @@ describe ProjectTeam do
describe '#find_member' do describe '#find_member' do
context 'personal project' do context 'personal project' do
let(:project) do let(:project) do
create(:project, :public, :access_requestable) create(:project, :public)
end end
let(:requester) { create(:user) } let(:requester) { create(:user) }
...@@ -161,7 +161,7 @@ describe ProjectTeam do ...@@ -161,7 +161,7 @@ describe ProjectTeam do
end end
context 'group project' do context 'group project' do
let(:group) { create(:group, :access_requestable) } let(:group) { create(:group) }
let(:project) { create(:project, group: group) } let(:project) { create(:project, group: group) }
let(:requester) { create(:user) } let(:requester) { create(:user) }
...@@ -246,7 +246,7 @@ describe ProjectTeam do ...@@ -246,7 +246,7 @@ describe ProjectTeam do
context 'personal project' do context 'personal project' do
let(:project) do let(:project) do
create(:project, :public, :access_requestable) create(:project, :public)
end end
context 'when project is not shared with group' do context 'when project is not shared with group' do
...@@ -292,7 +292,7 @@ describe ProjectTeam do ...@@ -292,7 +292,7 @@ describe ProjectTeam do
end end
context 'group project' do context 'group project' do
let(:group) { create(:group, :access_requestable) } let(:group) { create(:group) }
let!(:project) do let!(:project) do
create(:project, group: group) create(:project, group: group)
end end
......
...@@ -79,7 +79,7 @@ describe User do ...@@ -79,7 +79,7 @@ describe User do
describe '#group_members' do describe '#group_members' do
it 'does not include group memberships for which user is a requester' do it 'does not include group memberships for which user is a requester' do
user = create(:user) user = create(:user)
group = create(:group, :public, :access_requestable) group = create(:group, :public)
group.request_access(user) group.request_access(user)
expect(user.group_members).to be_empty expect(user.group_members).to be_empty
...@@ -89,7 +89,7 @@ describe User do ...@@ -89,7 +89,7 @@ describe User do
describe '#project_members' do describe '#project_members' do
it 'does not include project memberships for which user is a requester' do it 'does not include project memberships for which user is a requester' do
user = create(:user) user = create(:user)
project = create(:project, :public, :access_requestable) project = create(:project, :public)
project.request_access(user) project.request_access(user)
expect(user.project_members).to be_empty expect(user.project_members).to be_empty
...@@ -1191,7 +1191,7 @@ describe User do ...@@ -1191,7 +1191,7 @@ describe User do
end end
describe '.without_projects' do describe '.without_projects' do
let!(:project) { create(:project, :public, :access_requestable) } let!(:project) { create(:project, :public) }
let!(:user) { create(:user) } let!(:user) { create(:user) }
let!(:user_without_project) { create(:user) } let!(:user_without_project) { create(:user) }
let!(:user_without_project2) { create(:user) } let!(:user_without_project2) { create(:user) }
......
...@@ -7,7 +7,7 @@ describe API::AccessRequests do ...@@ -7,7 +7,7 @@ describe API::AccessRequests do
set(:stranger) { create(:user) } set(:stranger) { create(:user) }
set(:project) do 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_developer(developer)
project.add_maintainer(maintainer) project.add_maintainer(maintainer)
project.request_access(access_requester) project.request_access(access_requester)
...@@ -15,7 +15,7 @@ describe API::AccessRequests do ...@@ -15,7 +15,7 @@ describe API::AccessRequests do
end end
set(:group) do set(:group) do
create(:group, :public, :access_requestable) do |group| create(:group, :public) do |group|
group.add_developer(developer) group.add_developer(developer)
group.add_owner(maintainer) group.add_owner(maintainer)
group.request_access(access_requester) group.request_access(access_requester)
......
...@@ -345,7 +345,7 @@ describe API::Badges do ...@@ -345,7 +345,7 @@ describe API::Badges do
end end
def setup_project 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_developer(developer)
project.add_maintainer(maintainer) project.add_maintainer(maintainer)
project.request_access(access_requester) project.request_access(access_requester)
...@@ -356,7 +356,7 @@ describe API::Badges do ...@@ -356,7 +356,7 @@ describe API::Badges do
end end
def setup_group def setup_group
create(:group, :public, :access_requestable) do |group| create(:group, :public) do |group|
group.add_developer(developer) group.add_developer(developer)
group.add_owner(maintainer) group.add_owner(maintainer)
group.request_access(access_requester) group.request_access(access_requester)
......
...@@ -7,7 +7,7 @@ describe API::Members do ...@@ -7,7 +7,7 @@ describe API::Members do
let(:stranger) { create(:user) } let(:stranger) { create(:user) }
let(:project) do 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_developer(developer)
project.add_maintainer(maintainer) project.add_maintainer(maintainer)
project.request_access(access_requester) project.request_access(access_requester)
...@@ -15,7 +15,7 @@ describe API::Members do ...@@ -15,7 +15,7 @@ describe API::Members do
end end
let!(:group) do let!(:group) do
create(:group, :public, :access_requestable) do |group| create(:group, :public) do |group|
group.add_developer(developer) group.add_developer(developer)
group.add_owner(maintainer) group.add_owner(maintainer)
group.request_access(access_requester) group.request_access(access_requester)
......
...@@ -3,8 +3,8 @@ ...@@ -3,8 +3,8 @@
require 'spec_helper' require 'spec_helper'
describe Members::ApproveAccessRequestService do describe Members::ApproveAccessRequestService do
let(:project) { create(:project, :public, :access_requestable) } let(:project) { create(:project, :public) }
let(:group) { create(:group, :public, :access_requestable) } let(:group) { create(:group, :public) }
let(:current_user) { create(:user) } let(:current_user) { create(:user) }
let(:access_requester_user) { create(:user) } let(:access_requester_user) { create(:user) }
let(:access_requester) { source.requesters.find_by!(user_id: access_requester_user.id) } let(:access_requester) { source.requesters.find_by!(user_id: access_requester_user.id) }
......
...@@ -41,7 +41,7 @@ describe Members::RequestAccessService do ...@@ -41,7 +41,7 @@ describe Members::RequestAccessService do
context 'when access requests are disabled' do context 'when access requests are disabled' do
%i[project group].each do |source_type| %i[project group].each do |source_type|
it_behaves_like 'a service raising Gitlab::Access::AccessDeniedError' do 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 end
end end
...@@ -49,7 +49,7 @@ describe Members::RequestAccessService do ...@@ -49,7 +49,7 @@ describe Members::RequestAccessService do
context 'when current user can request access to the project' do context 'when current user can request access to the project' do
%i[project group].each do |source_type| %i[project group].each do |source_type|
it_behaves_like 'a service creating a access request' do 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 end
end end
......
...@@ -1942,7 +1942,7 @@ describe NotificationService, :mailer do ...@@ -1942,7 +1942,7 @@ describe NotificationService, :mailer do
let(:developer) { create(:user) } let(:developer) { create(:user) }
let!(:group) do let!(:group) do
create(:group, :public, :access_requestable) do |group| create(:group, :public) do |group|
group.add_owner(owner) group.add_owner(owner)
group.add_maintainer(maintainer) group.add_maintainer(maintainer)
group.add_developer(developer) group.add_developer(developer)
...@@ -1968,7 +1968,7 @@ describe NotificationService, :mailer do ...@@ -1968,7 +1968,7 @@ describe NotificationService, :mailer do
end end
it_behaves_like 'sends notification only to a maximum of ten, most recently active group owners' do 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) } let(:notification_trigger) { group.request_access(added_user) }
end end
end end
...@@ -2029,7 +2029,7 @@ describe NotificationService, :mailer do ...@@ -2029,7 +2029,7 @@ describe NotificationService, :mailer do
let(:maintainer) { create(:user) } let(:maintainer) { create(:user) }
let!(:project) do let!(:project) do
create(:project, :public, :access_requestable) do |project| create(:project, :public) do |project|
project.add_developer(developer) project.add_developer(developer)
project.add_maintainer(maintainer) project.add_maintainer(maintainer)
end end
...@@ -2053,7 +2053,7 @@ describe NotificationService, :mailer do ...@@ -2053,7 +2053,7 @@ describe NotificationService, :mailer do
end end
it_behaves_like 'sends notification only to a maximum of ten, most recently active project maintainers' do 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) } let(:notification_trigger) { project.request_access(added_user) }
end end
end end
...@@ -2064,7 +2064,7 @@ describe NotificationService, :mailer do ...@@ -2064,7 +2064,7 @@ describe NotificationService, :mailer do
context 'when the project has no maintainers' do context 'when the project has no maintainers' do
context 'when the group has at least one owner' 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 before do
reset_delivered_emails! reset_delivered_emails!
...@@ -2079,14 +2079,14 @@ describe NotificationService, :mailer do ...@@ -2079,14 +2079,14 @@ describe NotificationService, :mailer do
end end
it_behaves_like 'sends notification only to a maximum of ten, most recently active group owners' do 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) } let(:notification_trigger) { project.request_access(added_user) }
end end
end end
context 'when the group does not have any owners' do context 'when the group does not have any owners' do
let(:group) { create(:group) } let(:group) { create(:group) }
let!(:project) { create(:project, :public, :access_requestable, namespace: group) } let!(:project) { create(:project, :public, namespace: group) }
context 'recipients' do context 'recipients' do
before do before do
...@@ -2107,7 +2107,7 @@ describe NotificationService, :mailer do ...@@ -2107,7 +2107,7 @@ describe NotificationService, :mailer do
let(:developer) { create(:user) } let(:developer) { create(:user) }
let!(:project) do 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_maintainer(maintainer)
project.add_developer(developer) project.add_developer(developer)
end end
...@@ -2128,7 +2128,7 @@ describe NotificationService, :mailer do ...@@ -2128,7 +2128,7 @@ describe NotificationService, :mailer do
end end
it_behaves_like 'sends notification only to a maximum of ten, most recently active project maintainers' do 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) } let(:notification_trigger) { project.request_access(added_user) }
end end
end end
......
...@@ -157,6 +157,6 @@ describe Projects::ContainerRepository::CleanupTagsService do ...@@ -157,6 +157,6 @@ describe Projects::ContainerRepository::CleanupTagsService do
def expect_delete(digest) def expect_delete(digest)
expect_any_instance_of(ContainerRegistry::Client) expect_any_instance_of(ContainerRegistry::Client)
.to receive(:delete_repository_tag) .to receive(:delete_repository_tag)
.with(repository.path, digest) .with(repository.path, digest) { true }
end end
end end
...@@ -87,6 +87,21 @@ describe Projects::ContainerRepository::DeleteTagsService do ...@@ -87,6 +87,21 @@ describe Projects::ContainerRepository::DeleteTagsService do
is_expected.to include(status: :success) is_expected.to include(status: :success)
end 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 end
end end
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment