Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
914ea32e0e
commit
d2ffc30fd5
|
@ -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(
|
||||
|
|
|
@ -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__(
|
||||
|
|
|
@ -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,
|
||||
},
|
||||
});
|
||||
},
|
||||
|
|
|
@ -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">
|
||||
|
|
|
@ -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
|
|||
*/
|
||||
$mr-tabs-height: 48px;
|
||||
$mr-version-controls-height: 56px;
|
||||
$mr-widget-margin-left: 40px;
|
||||
|
||||
/*
|
||||
Compare Branches
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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')
|
||||
|
|
|
@ -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 } }
|
||||
|
|
|
@ -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)')
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Enable Request Access functionality by default for new projects and groups
|
||||
merge_request: 17662
|
||||
author:
|
||||
type: changed
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Fix Web IDE tree not updating modified status
|
||||
merge_request: 18647
|
||||
author:
|
||||
type: fixed
|
|
@ -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
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Expose subscribed attribute for epic on API
|
||||
merge_request: 18475
|
||||
author:
|
||||
type: added
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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"
|
||||
|
|
|
@ -147,7 +147,8 @@ Example response:
|
|||
"closed_at": "2018-08-18T12:22:05.239Z",
|
||||
"labels": [],
|
||||
"upvotes": 4,
|
||||
"downvotes": 0
|
||||
"downvotes": 0,
|
||||
"subscribed": true
|
||||
}
|
||||
```
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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),
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 ""
|
||||
|
||||
|
|
|
@ -153,7 +153,8 @@ redis:
|
|||
redis-ha:
|
||||
enabled: false
|
||||
registry:
|
||||
minReplicas: 1
|
||||
hpa:
|
||||
minReplicas: 1
|
||||
resources:
|
||||
requests:
|
||||
cpu: 50m
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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) }
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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) }
|
||||
|
|
|
@ -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) }
|
||||
|
|
|
@ -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,
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
|
@ -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', () => {
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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) }
|
||||
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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) }
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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) }
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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) }
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue