Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
3612694ce3
commit
6f9218ac84
|
|
@ -607,11 +607,6 @@ Layout/ArgumentAlignment:
|
|||
- 'app/models/wiki_page.rb'
|
||||
- 'app/models/work_item.rb'
|
||||
- 'app/policies/project_snippet_policy.rb'
|
||||
- 'app/presenters/ci/build_runner_presenter.rb'
|
||||
- 'app/presenters/label_presenter.rb'
|
||||
- 'app/presenters/merge_request_presenter.rb'
|
||||
- 'app/presenters/project_presenter.rb'
|
||||
- 'app/presenters/snippet_blob_presenter.rb'
|
||||
- 'app/serializers/build_details_entity.rb'
|
||||
- 'app/serializers/environment_serializer.rb'
|
||||
- 'app/serializers/error_tracking/detailed_error_entity.rb'
|
||||
|
|
@ -1577,7 +1572,6 @@ Layout/ArgumentAlignment:
|
|||
- 'ee/spec/policies/merge_request_policy_spec.rb'
|
||||
- 'ee/spec/policies/vulnerabilities/feedback_policy_spec.rb'
|
||||
- 'ee/spec/policies/vulnerabilities/merge_request_link_policy_spec.rb'
|
||||
- 'ee/spec/presenters/ci/pipeline_presenter_spec.rb'
|
||||
- 'ee/spec/requests/admin/impersonation_tokens_controller_spec.rb'
|
||||
- 'ee/spec/requests/api/analytics/product_analytics_spec.rb'
|
||||
- 'ee/spec/requests/api/analytics/project_deployment_frequency_spec.rb'
|
||||
|
|
@ -2691,10 +2685,6 @@ Layout/ArgumentAlignment:
|
|||
- 'spec/policies/ci/pipeline_schedule_policy_spec.rb'
|
||||
- 'spec/policies/environment_policy_spec.rb'
|
||||
- 'spec/policies/group_policy_spec.rb'
|
||||
- 'spec/presenters/blob_presenter_spec.rb'
|
||||
- 'spec/presenters/ci/build_runner_presenter_spec.rb'
|
||||
- 'spec/presenters/merge_request_presenter_spec.rb'
|
||||
- 'spec/presenters/packages/detail/package_presenter_spec.rb'
|
||||
- 'spec/requests/admin/applications_controller_spec.rb'
|
||||
- 'spec/requests/admin/impersonation_tokens_controller_spec.rb'
|
||||
- 'spec/requests/api/access_requests_spec.rb'
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
906fdaa2fd6f4dc4baec1b36b9341f4157e2042a
|
||||
220959f0ffc3d01fa448cc2c7b45b082d56690ef
|
||||
|
|
|
|||
|
|
@ -1,35 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class ServerlessDomainFinder
|
||||
attr_reader :match, :serverless_domain_cluster, :environment
|
||||
|
||||
def initialize(uri)
|
||||
@match = ::Serverless::Domain::REGEXP.match(uri)
|
||||
end
|
||||
|
||||
def execute
|
||||
return unless serverless?
|
||||
|
||||
@serverless_domain_cluster = ::Serverless::DomainCluster.for_uuid(serverless_domain_cluster_uuid)
|
||||
return unless serverless_domain_cluster&.knative&.external_ip
|
||||
|
||||
@environment = ::Environment.for_id_and_slug(match[:environment_id].to_i(16), match[:environment_slug])
|
||||
return unless environment
|
||||
|
||||
::Serverless::Domain.new(
|
||||
function_name: match[:function_name],
|
||||
serverless_domain_cluster: serverless_domain_cluster,
|
||||
environment: environment
|
||||
)
|
||||
end
|
||||
|
||||
def serverless_domain_cluster_uuid
|
||||
return unless serverless?
|
||||
|
||||
match[:cluster_left] + match[:cluster_middle] + match[:cluster_right]
|
||||
end
|
||||
|
||||
def serverless?
|
||||
!!match
|
||||
end
|
||||
end
|
||||
|
|
@ -13,8 +13,6 @@ module Clusters
|
|||
|
||||
self.table_name = 'clusters_applications_knative'
|
||||
|
||||
has_one :serverless_domain_cluster, class_name: '::Serverless::DomainCluster', foreign_key: 'clusters_applications_knative_id', inverse_of: :knative
|
||||
|
||||
include ::Clusters::Concerns::ApplicationCore
|
||||
include ::Clusters::Concerns::ApplicationStatus
|
||||
include ::Clusters::Concerns::ApplicationVersion
|
||||
|
|
@ -49,8 +47,6 @@ module Clusters
|
|||
|
||||
scope :for_cluster, -> (cluster) { where(cluster: cluster) }
|
||||
|
||||
has_one :pages_domain, through: :serverless_domain_cluster
|
||||
|
||||
def chart
|
||||
'knative/knative'
|
||||
end
|
||||
|
|
|
|||
|
|
@ -363,12 +363,6 @@ module Clusters
|
|||
end
|
||||
end
|
||||
|
||||
def serverless_domain
|
||||
strong_memoize(:serverless_domain) do
|
||||
self.application_knative&.serverless_domain_cluster
|
||||
end
|
||||
end
|
||||
|
||||
def prometheus_adapter
|
||||
integration_prometheus
|
||||
end
|
||||
|
|
|
|||
|
|
@ -15,7 +15,6 @@ class PagesDomain < ApplicationRecord
|
|||
|
||||
belongs_to :project
|
||||
has_many :acme_orders, class_name: "PagesDomainAcmeOrder"
|
||||
has_many :serverless_domain_clusters, class_name: 'Serverless::DomainCluster', inverse_of: :pages_domain
|
||||
|
||||
after_initialize :set_verification_code
|
||||
before_validation :clear_auto_ssl_failure, unless: :auto_ssl_enabled
|
||||
|
|
|
|||
|
|
@ -1,44 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Serverless
|
||||
class Domain
|
||||
include ActiveModel::Model
|
||||
|
||||
REGEXP = %r{^(?<scheme>https?://)?(?<function_name>[^.]+)-(?<cluster_left>\h{2})a1(?<cluster_middle>\h{10})f2(?<cluster_right>\h{2})(?<environment_id>\h+)-(?<environment_slug>[^.]+)\.(?<pages_domain_name>.+)}.freeze
|
||||
UUID_LENGTH = 14
|
||||
|
||||
attr_accessor :function_name, :serverless_domain_cluster, :environment
|
||||
|
||||
validates :function_name, presence: true, allow_blank: false
|
||||
validates :serverless_domain_cluster, presence: true
|
||||
validates :environment, presence: true
|
||||
|
||||
def self.generate_uuid
|
||||
SecureRandom.hex(UUID_LENGTH / 2)
|
||||
end
|
||||
|
||||
def uri
|
||||
URI("https://#{function_name}-#{serverless_domain_cluster_uuid}#{"%x" % environment.id}-#{environment.slug}.#{serverless_domain_cluster.domain}")
|
||||
end
|
||||
|
||||
def knative_uri
|
||||
URI("http://#{function_name}.#{namespace}.#{serverless_domain_cluster.knative.hostname}")
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def namespace
|
||||
serverless_domain_cluster.cluster.kubernetes_namespace_for(environment)
|
||||
end
|
||||
|
||||
def serverless_domain_cluster_uuid
|
||||
[
|
||||
serverless_domain_cluster.uuid[0..1],
|
||||
'a1',
|
||||
serverless_domain_cluster.uuid[2..-3],
|
||||
'f2',
|
||||
serverless_domain_cluster.uuid[-2..]
|
||||
].join
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -1,39 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Serverless
|
||||
class DomainCluster < ApplicationRecord
|
||||
self.table_name = 'serverless_domain_cluster'
|
||||
|
||||
HEX_REGEXP = %r{\A\h+\z}.freeze
|
||||
|
||||
belongs_to :pages_domain
|
||||
belongs_to :knative, class_name: 'Clusters::Applications::Knative', foreign_key: 'clusters_applications_knative_id'
|
||||
belongs_to :creator, class_name: 'User', optional: true
|
||||
|
||||
attr_encrypted :key,
|
||||
mode: :per_attribute_iv,
|
||||
key: Settings.attr_encrypted_db_key_base_32,
|
||||
algorithm: 'aes-256-gcm'
|
||||
|
||||
validates :pages_domain, :knative, presence: true
|
||||
validates :uuid, presence: true, uniqueness: true, length: { is: ::Serverless::Domain::UUID_LENGTH },
|
||||
format: { with: HEX_REGEXP, message: 'only allows hex characters' }
|
||||
|
||||
after_initialize :set_uuid, if: :new_record?
|
||||
|
||||
delegate :domain, to: :pages_domain
|
||||
delegate :cluster, to: :knative
|
||||
|
||||
def self.for_uuid(uuid)
|
||||
joins(:pages_domain, :knative)
|
||||
.includes(:pages_domain, :knative)
|
||||
.find_by(uuid: uuid)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def set_uuid
|
||||
self.uuid = ::Serverless::Domain.generate_uuid
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -1,26 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Serverless
|
||||
class Function
|
||||
attr_accessor :name, :namespace
|
||||
|
||||
def initialize(project, name, namespace)
|
||||
@project = project
|
||||
@name = name
|
||||
@namespace = namespace
|
||||
end
|
||||
|
||||
def id
|
||||
@project.id.to_s + "/" + @name + "/" + @namespace
|
||||
end
|
||||
|
||||
def self.find_by_id(id)
|
||||
array = id.split("/")
|
||||
project = Project.find_by_id(array[0])
|
||||
name = array[1]
|
||||
namespace = array[2]
|
||||
|
||||
self.new(project, name, namespace)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -1,30 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Serverless
|
||||
class LookupPath
|
||||
attr_reader :serverless_domain
|
||||
|
||||
delegate :serverless_domain_cluster, to: :serverless_domain
|
||||
delegate :knative, to: :serverless_domain_cluster
|
||||
delegate :certificate, to: :serverless_domain_cluster
|
||||
delegate :key, to: :serverless_domain_cluster
|
||||
|
||||
def initialize(serverless_domain)
|
||||
@serverless_domain = serverless_domain
|
||||
end
|
||||
|
||||
def source
|
||||
{
|
||||
type: 'serverless',
|
||||
service: serverless_domain.knative_uri.host,
|
||||
cluster: {
|
||||
hostname: knative.hostname,
|
||||
address: knative.external_ip,
|
||||
port: 443,
|
||||
cert: certificate,
|
||||
key: key
|
||||
}
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Serverless
|
||||
class VirtualDomain
|
||||
attr_reader :serverless_domain
|
||||
|
||||
delegate :serverless_domain_cluster, to: :serverless_domain
|
||||
delegate :pages_domain, to: :serverless_domain_cluster
|
||||
delegate :certificate, to: :pages_domain
|
||||
delegate :key, to: :pages_domain
|
||||
|
||||
def initialize(serverless_domain)
|
||||
@serverless_domain = serverless_domain
|
||||
end
|
||||
|
||||
def lookup_paths
|
||||
[
|
||||
::Serverless::LookupPath.new(serverless_domain)
|
||||
]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -34,9 +34,7 @@ module Ci
|
|||
|
||||
def runner_variables
|
||||
variables
|
||||
.sort_and_expand_all(keep_undefined: true,
|
||||
expand_file_refs: false,
|
||||
expand_raw_refs: false)
|
||||
.sort_and_expand_all(keep_undefined: true, expand_file_refs: false, expand_raw_refs: false)
|
||||
.to_runner_variables
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -27,14 +27,18 @@ class LabelPresenter < Gitlab::View::Presenter::Delegated
|
|||
def filter_path(type: :issue)
|
||||
case context_subject
|
||||
when Group
|
||||
send("#{type.to_s.pluralize}_group_path", # rubocop:disable GitlabSecurity/PublicSend
|
||||
context_subject,
|
||||
label_name: [label.name])
|
||||
send( # rubocop:disable GitlabSecurity/PublicSend
|
||||
"#{type.to_s.pluralize}_group_path",
|
||||
context_subject,
|
||||
label_name: [label.name]
|
||||
)
|
||||
when Project
|
||||
send("namespace_project_#{type.to_s.pluralize}_path", # rubocop:disable GitlabSecurity/PublicSend
|
||||
context_subject.namespace,
|
||||
context_subject,
|
||||
label_name: [label.name])
|
||||
send( # rubocop:disable GitlabSecurity/PublicSend
|
||||
"namespace_project_#{type.to_s.pluralize}_path",
|
||||
context_subject.namespace,
|
||||
context_subject,
|
||||
label_name: [label.name]
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -57,9 +57,7 @@ class MergeRequestPresenter < Gitlab::View::Presenter::Delegated
|
|||
notice_now: edit_in_new_fork_notice_now
|
||||
}
|
||||
|
||||
project_forks_path(merge_request.project,
|
||||
namespace_key: current_user.namespace.id,
|
||||
continue: continue_params)
|
||||
project_forks_path(merge_request.project, namespace_key: current_user.namespace.id, continue: continue_params)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -71,9 +69,7 @@ class MergeRequestPresenter < Gitlab::View::Presenter::Delegated
|
|||
notice_now: edit_in_new_fork_notice_now
|
||||
}
|
||||
|
||||
project_forks_path(project,
|
||||
namespace_key: current_user.namespace.id,
|
||||
continue: continue_params)
|
||||
project_forks_path(project, namespace_key: current_user.namespace.id, continue: continue_params)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -155,12 +151,12 @@ class MergeRequestPresenter < Gitlab::View::Presenter::Delegated
|
|||
|
||||
def assign_to_closing_issues_count
|
||||
# rubocop: disable CodeReuse/ServiceClass
|
||||
issues = MergeRequests::AssignIssuesService.new(project: project,
|
||||
current_user: current_user,
|
||||
params: {
|
||||
merge_request: merge_request,
|
||||
closes_issues: closing_issues
|
||||
}).assignable_issues
|
||||
issues = MergeRequests::AssignIssuesService.new(
|
||||
project: project,
|
||||
current_user: current_user,
|
||||
params: { merge_request: merge_request, closes_issues: closing_issues }
|
||||
).assignable_issues
|
||||
|
||||
issues.count
|
||||
# rubocop: enable CodeReuse/ServiceClass
|
||||
end
|
||||
|
|
|
|||
|
|
@ -99,11 +99,7 @@ class ProjectPresenter < Gitlab::View::Presenter::Delegated
|
|||
|
||||
def contribution_guide_path
|
||||
if project && contribution_guide = repository.contribution_guide
|
||||
project_blob_path(
|
||||
project,
|
||||
tree_join(project.default_branch,
|
||||
contribution_guide.name)
|
||||
)
|
||||
project_blob_path(project, tree_join(project.default_branch, contribution_guide.name))
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -166,14 +162,16 @@ class ProjectPresenter < Gitlab::View::Presenter::Delegated
|
|||
|
||||
def storage_anchor_data
|
||||
can_show_quota = can?(current_user, :admin_project, project) && !empty_repo?
|
||||
AnchorData.new(true,
|
||||
statistic_icon('disk') +
|
||||
_('%{strong_start}%{human_size}%{strong_end} Project Storage').html_safe % {
|
||||
human_size: storage_counter(statistics.storage_size),
|
||||
strong_start: '<strong class="project-stat-value">'.html_safe,
|
||||
strong_end: '</strong>'.html_safe
|
||||
},
|
||||
can_show_quota ? project_usage_quotas_path(project) : nil)
|
||||
AnchorData.new(
|
||||
true,
|
||||
statistic_icon('disk') +
|
||||
_('%{strong_start}%{human_size}%{strong_end} Project Storage').html_safe % {
|
||||
human_size: storage_counter(statistics.storage_size),
|
||||
strong_start: '<strong class="project-stat-value">'.html_safe,
|
||||
strong_end: '</strong>'.html_safe
|
||||
},
|
||||
can_show_quota ? project_usage_quotas_path(project) : nil
|
||||
)
|
||||
end
|
||||
|
||||
def releases_anchor_data
|
||||
|
|
@ -182,14 +180,16 @@ class ProjectPresenter < Gitlab::View::Presenter::Delegated
|
|||
releases_count = project.releases.count
|
||||
return if releases_count < 1
|
||||
|
||||
AnchorData.new(true,
|
||||
statistic_icon('deployments') +
|
||||
n_('%{strong_start}%{release_count}%{strong_end} Release', '%{strong_start}%{release_count}%{strong_end} Releases', releases_count).html_safe % {
|
||||
release_count: number_with_delimiter(releases_count),
|
||||
strong_start: '<strong class="project-stat-value">'.html_safe,
|
||||
strong_end: '</strong>'.html_safe
|
||||
},
|
||||
project_releases_path(project))
|
||||
AnchorData.new(
|
||||
true,
|
||||
statistic_icon('deployments') +
|
||||
n_('%{strong_start}%{release_count}%{strong_end} Release', '%{strong_start}%{release_count}%{strong_end} Releases', releases_count).html_safe % {
|
||||
release_count: number_with_delimiter(releases_count),
|
||||
strong_start: '<strong class="project-stat-value">'.html_safe,
|
||||
strong_end: '</strong>'.html_safe
|
||||
},
|
||||
project_releases_path(project)
|
||||
)
|
||||
end
|
||||
|
||||
def environments_anchor_data
|
||||
|
|
@ -198,67 +198,76 @@ class ProjectPresenter < Gitlab::View::Presenter::Delegated
|
|||
environments_count = project.environments.available.count
|
||||
return if environments_count == 0
|
||||
|
||||
AnchorData.new(true,
|
||||
statistic_icon('environment') +
|
||||
n_('%{strong_start}%{count}%{strong_end} Environment', '%{strong_start}%{count}%{strong_end} Environments', environments_count).html_safe % {
|
||||
count: number_with_delimiter(environments_count),
|
||||
strong_start: '<strong class="project-stat-value">'.html_safe,
|
||||
strong_end: '</strong>'.html_safe
|
||||
},
|
||||
project_environments_path(project))
|
||||
AnchorData.new(
|
||||
true,
|
||||
statistic_icon('environment') +
|
||||
n_('%{strong_start}%{count}%{strong_end} Environment', '%{strong_start}%{count}%{strong_end} Environments', environments_count).html_safe % {
|
||||
count: number_with_delimiter(environments_count),
|
||||
strong_start: '<strong class="project-stat-value">'.html_safe,
|
||||
strong_end: '</strong>'.html_safe
|
||||
},
|
||||
project_environments_path(project)
|
||||
)
|
||||
end
|
||||
|
||||
def commits_anchor_data
|
||||
AnchorData.new(true,
|
||||
statistic_icon('commit') +
|
||||
n_('%{strong_start}%{commit_count}%{strong_end} Commit', '%{strong_start}%{commit_count}%{strong_end} Commits', statistics.commit_count).html_safe % {
|
||||
commit_count: number_with_delimiter(statistics.commit_count),
|
||||
strong_start: '<strong class="project-stat-value">'.html_safe,
|
||||
strong_end: '</strong>'.html_safe
|
||||
},
|
||||
empty_repo? ? nil : project_commits_path(project, default_branch_or_main))
|
||||
AnchorData.new(
|
||||
true,
|
||||
statistic_icon('commit') +
|
||||
n_('%{strong_start}%{commit_count}%{strong_end} Commit', '%{strong_start}%{commit_count}%{strong_end} Commits', statistics.commit_count).html_safe % {
|
||||
commit_count: number_with_delimiter(statistics.commit_count),
|
||||
strong_start: '<strong class="project-stat-value">'.html_safe,
|
||||
strong_end: '</strong>'.html_safe
|
||||
},
|
||||
empty_repo? ? nil : project_commits_path(project, default_branch_or_main)
|
||||
)
|
||||
end
|
||||
|
||||
def branches_anchor_data
|
||||
AnchorData.new(true,
|
||||
statistic_icon('branch') +
|
||||
n_('%{strong_start}%{branch_count}%{strong_end} Branch', '%{strong_start}%{branch_count}%{strong_end} Branches', repository.branch_count).html_safe % {
|
||||
branch_count: number_with_delimiter(repository.branch_count),
|
||||
strong_start: '<strong class="project-stat-value">'.html_safe,
|
||||
strong_end: '</strong>'.html_safe
|
||||
},
|
||||
empty_repo? ? nil : project_branches_path(project))
|
||||
AnchorData.new(
|
||||
true,
|
||||
statistic_icon('branch') +
|
||||
n_('%{strong_start}%{branch_count}%{strong_end} Branch', '%{strong_start}%{branch_count}%{strong_end} Branches', repository.branch_count).html_safe % {
|
||||
branch_count: number_with_delimiter(repository.branch_count),
|
||||
strong_start: '<strong class="project-stat-value">'.html_safe,
|
||||
strong_end: '</strong>'.html_safe
|
||||
},
|
||||
empty_repo? ? nil : project_branches_path(project)
|
||||
)
|
||||
end
|
||||
|
||||
def tags_anchor_data
|
||||
AnchorData.new(true,
|
||||
statistic_icon('label') +
|
||||
n_('%{strong_start}%{tag_count}%{strong_end} Tag', '%{strong_start}%{tag_count}%{strong_end} Tags', repository.tag_count).html_safe % {
|
||||
tag_count: number_with_delimiter(repository.tag_count),
|
||||
strong_start: '<strong class="project-stat-value">'.html_safe,
|
||||
strong_end: '</strong>'.html_safe
|
||||
},
|
||||
empty_repo? ? nil : project_tags_path(project))
|
||||
AnchorData.new(
|
||||
true,
|
||||
statistic_icon('label') +
|
||||
n_('%{strong_start}%{tag_count}%{strong_end} Tag', '%{strong_start}%{tag_count}%{strong_end} Tags', repository.tag_count).html_safe % {
|
||||
tag_count: number_with_delimiter(repository.tag_count),
|
||||
strong_start: '<strong class="project-stat-value">'.html_safe,
|
||||
strong_end: '</strong>'.html_safe
|
||||
},
|
||||
empty_repo? ? nil : project_tags_path(project)
|
||||
)
|
||||
end
|
||||
|
||||
def upload_anchor_data
|
||||
strong_memoize(:upload_anchor_data) do
|
||||
next unless can_current_user_push_to_default_branch?
|
||||
|
||||
AnchorData.new(false,
|
||||
statistic_icon('upload') + _('Upload file'),
|
||||
'#modal-upload-blob',
|
||||
'js-upload-file-trigger',
|
||||
nil,
|
||||
nil,
|
||||
{
|
||||
'target_branch' => default_branch_or_main,
|
||||
'original_branch' => default_branch_or_main,
|
||||
'can_push_code' => 'true',
|
||||
'path' => project_create_blob_path(project, default_branch_or_main),
|
||||
'project_path' => project.full_path
|
||||
}
|
||||
)
|
||||
AnchorData.new(
|
||||
false,
|
||||
statistic_icon('upload') + _('Upload file'),
|
||||
'#modal-upload-blob',
|
||||
'js-upload-file-trigger',
|
||||
nil,
|
||||
nil,
|
||||
{
|
||||
'target_branch' => default_branch_or_main,
|
||||
'original_branch' => default_branch_or_main,
|
||||
'can_push_code' => 'true',
|
||||
'path' => project_create_blob_path(project, default_branch_or_main),
|
||||
'project_path' => project.full_path
|
||||
}
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -266,37 +275,38 @@ class ProjectPresenter < Gitlab::View::Presenter::Delegated
|
|||
if can_current_user_push_to_default_branch?
|
||||
new_file_path = empty_repo? ? ide_edit_path(project, default_branch_or_main) : project_new_blob_path(project, default_branch_or_main)
|
||||
|
||||
AnchorData.new(false,
|
||||
statistic_icon + _('New file'),
|
||||
new_file_path,
|
||||
'btn-dashed')
|
||||
AnchorData.new(false, statistic_icon + _('New file'), new_file_path, 'btn-dashed')
|
||||
end
|
||||
end
|
||||
|
||||
def readme_anchor_data
|
||||
if can_current_user_push_to_default_branch? && readme_path.nil?
|
||||
AnchorData.new(false,
|
||||
statistic_icon + _('Add README'),
|
||||
empty_repo? ? add_readme_ide_path : add_readme_path)
|
||||
AnchorData.new(false, statistic_icon + _('Add README'), empty_repo? ? add_readme_ide_path : add_readme_path)
|
||||
elsif readme_path
|
||||
AnchorData.new(false,
|
||||
statistic_icon('doc-text') + _('README'),
|
||||
default_view != 'readme' ? readme_path : '#readme',
|
||||
'btn-default',
|
||||
'doc-text')
|
||||
AnchorData.new(
|
||||
false,
|
||||
statistic_icon('doc-text') + _('README'),
|
||||
default_view != 'readme' ? readme_path : '#readme',
|
||||
'btn-default',
|
||||
'doc-text'
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
def changelog_anchor_data
|
||||
if can_current_user_push_to_default_branch? && repository.changelog.blank?
|
||||
AnchorData.new(false,
|
||||
statistic_icon + _('Add CHANGELOG'),
|
||||
empty_repo? ? add_changelog_ide_path : add_changelog_path)
|
||||
AnchorData.new(
|
||||
false,
|
||||
statistic_icon + _('Add CHANGELOG'),
|
||||
empty_repo? ? add_changelog_ide_path : add_changelog_path
|
||||
)
|
||||
elsif repository.changelog.present?
|
||||
AnchorData.new(false,
|
||||
statistic_icon('doc-text') + _('CHANGELOG'),
|
||||
changelog_path,
|
||||
'btn-default')
|
||||
AnchorData.new(
|
||||
false,
|
||||
statistic_icon('doc-text') + _('CHANGELOG'),
|
||||
changelog_path,
|
||||
'btn-default'
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -304,29 +314,37 @@ class ProjectPresenter < Gitlab::View::Presenter::Delegated
|
|||
icon = statistic_icon('scale')
|
||||
|
||||
if repository.license_blob.present?
|
||||
AnchorData.new(false,
|
||||
icon + content_tag(:span, license_short_name, class: 'project-stat-value'),
|
||||
license_path,
|
||||
'btn-default',
|
||||
nil,
|
||||
'license')
|
||||
AnchorData.new(
|
||||
false,
|
||||
icon + content_tag(:span, license_short_name, class: 'project-stat-value'),
|
||||
license_path,
|
||||
'btn-default',
|
||||
nil,
|
||||
'license'
|
||||
)
|
||||
elsif can_current_user_push_to_default_branch?
|
||||
AnchorData.new(false,
|
||||
content_tag(:span, statistic_icon + _('Add LICENSE'), class: 'add-license-link d-flex'),
|
||||
empty_repo? ? add_license_ide_path : add_license_path)
|
||||
AnchorData.new(
|
||||
false,
|
||||
content_tag(:span, statistic_icon + _('Add LICENSE'), class: 'add-license-link d-flex'),
|
||||
empty_repo? ? add_license_ide_path : add_license_path
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
def contribution_guide_anchor_data
|
||||
if can_current_user_push_to_default_branch? && repository.contribution_guide.blank?
|
||||
AnchorData.new(false,
|
||||
statistic_icon + _('Add CONTRIBUTING'),
|
||||
empty_repo? ? add_contribution_guide_ide_path : add_contribution_guide_path)
|
||||
AnchorData.new(
|
||||
false,
|
||||
statistic_icon + _('Add CONTRIBUTING'),
|
||||
empty_repo? ? add_contribution_guide_ide_path : add_contribution_guide_path
|
||||
)
|
||||
elsif repository.contribution_guide.present?
|
||||
AnchorData.new(false,
|
||||
statistic_icon('doc-text') + _('CONTRIBUTING'),
|
||||
contribution_guide_path,
|
||||
'btn-default')
|
||||
AnchorData.new(
|
||||
false,
|
||||
statistic_icon('doc-text') + _('CONTRIBUTING'),
|
||||
contribution_guide_path,
|
||||
'btn-default'
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -335,35 +353,32 @@ class ProjectPresenter < Gitlab::View::Presenter::Delegated
|
|||
|
||||
if current_user && can?(current_user, :admin_pipeline, project) && repository.gitlab_ci_yml.blank? && !show_auto_devops_callout
|
||||
if auto_devops_enabled?
|
||||
AnchorData.new(false,
|
||||
statistic_icon('settings') + _('Auto DevOps enabled'),
|
||||
project_settings_ci_cd_path(project, anchor: 'autodevops-settings'),
|
||||
'btn-default')
|
||||
AnchorData.new(
|
||||
false,
|
||||
statistic_icon('settings') + _('Auto DevOps enabled'),
|
||||
project_settings_ci_cd_path(project, anchor: 'autodevops-settings'),
|
||||
'btn-default'
|
||||
)
|
||||
else
|
||||
AnchorData.new(false,
|
||||
statistic_icon + _('Enable Auto DevOps'),
|
||||
project_settings_ci_cd_path(project, anchor: 'autodevops-settings'))
|
||||
AnchorData.new(
|
||||
false,
|
||||
statistic_icon + _('Enable Auto DevOps'),
|
||||
project_settings_ci_cd_path(project, anchor: 'autodevops-settings')
|
||||
)
|
||||
end
|
||||
elsif auto_devops_enabled?
|
||||
AnchorData.new(false,
|
||||
_('Auto DevOps enabled'),
|
||||
nil)
|
||||
AnchorData.new(false, _('Auto DevOps enabled'), nil)
|
||||
end
|
||||
end
|
||||
|
||||
def kubernetes_cluster_anchor_data
|
||||
if can_instantiate_cluster?
|
||||
if clusters.empty?
|
||||
AnchorData.new(false,
|
||||
statistic_icon + _('Add Kubernetes cluster'),
|
||||
project_clusters_path(project))
|
||||
AnchorData.new(false, statistic_icon + _('Add Kubernetes cluster'), project_clusters_path(project))
|
||||
else
|
||||
cluster_link = clusters.count == 1 ? project_cluster_path(project, clusters.first) : project_clusters_path(project)
|
||||
|
||||
AnchorData.new(false,
|
||||
_('Kubernetes'),
|
||||
cluster_link,
|
||||
'btn-default')
|
||||
AnchorData.new(false, _('Kubernetes'), cluster_link, 'btn-default')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -372,14 +387,9 @@ class ProjectPresenter < Gitlab::View::Presenter::Delegated
|
|||
return unless can_view_pipeline_editor?(project)
|
||||
|
||||
if cicd_missing?
|
||||
AnchorData.new(false,
|
||||
statistic_icon + _('Set up CI/CD'),
|
||||
project_ci_pipeline_editor_path(project))
|
||||
AnchorData.new(false, statistic_icon + _('Set up CI/CD'), project_ci_pipeline_editor_path(project))
|
||||
elsif repository.gitlab_ci_yml.present?
|
||||
AnchorData.new(false,
|
||||
statistic_icon('doc-text') + _('CI/CD configuration'),
|
||||
project_ci_pipeline_editor_path(project),
|
||||
'btn-default')
|
||||
AnchorData.new(false, statistic_icon('doc-text') + _('CI/CD configuration'), project_ci_pipeline_editor_path(project), 'btn-default')
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -40,9 +40,11 @@ class SnippetBlobPresenter < BlobPresenter
|
|||
end
|
||||
|
||||
def render_rich_partial
|
||||
renderer.render("projects/blob/viewers/_#{blob.rich_viewer.partial_name}",
|
||||
locals: { viewer: blob.rich_viewer, blob: blob, blob_raw_path: raw_path, blob_raw_url: raw_url, parent_dir_raw_path: raw_directory },
|
||||
layout: false)
|
||||
renderer.render(
|
||||
"projects/blob/viewers/_#{blob.rich_viewer.partial_name}",
|
||||
locals: { viewer: blob.rich_viewer, blob: blob, blob_raw_path: raw_path, blob_raw_url: raw_url, parent_dir_raw_path: raw_directory },
|
||||
layout: false
|
||||
)
|
||||
end
|
||||
|
||||
def renderer
|
||||
|
|
|
|||
|
|
@ -1,20 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class ClusterApplicationEntity < Grape::Entity
|
||||
expose :name
|
||||
expose :status_name, as: :status
|
||||
expose :status_reason
|
||||
expose :version, if: -> (e, _) { e.respond_to?(:version) }
|
||||
expose :external_ip, if: -> (e, _) { e.respond_to?(:external_ip) }
|
||||
expose :external_hostname, if: -> (e, _) { e.respond_to?(:external_hostname) }
|
||||
expose :hostname, if: -> (e, _) { e.respond_to?(:hostname) }
|
||||
expose :email, if: -> (e, _) { e.respond_to?(:email) }
|
||||
expose :stack, if: -> (e, _) { e.respond_to?(:stack) }
|
||||
expose :update_available?, as: :update_available, if: -> (e, _) { e.respond_to?(:update_available?) }
|
||||
expose :can_uninstall?, as: :can_uninstall
|
||||
expose :available_domains, using: Serverless::DomainEntity, if: -> (e, _) { e.respond_to?(:available_domains) }
|
||||
expose :pages_domain, using: Serverless::DomainEntity, if: -> (e, _) { e.respond_to?(:pages_domain) }
|
||||
expose :host, if: -> (e, _) { e.respond_to?(:host) }
|
||||
expose :port, if: -> (e, _) { e.respond_to?(:port) }
|
||||
expose :protocol, if: -> (e, _) { e.respond_to?(:protocol) }
|
||||
end
|
||||
|
|
@ -13,7 +13,6 @@ class ClusterEntity < Grape::Entity
|
|||
expose :provider_type
|
||||
expose :status_name, as: :status
|
||||
expose :status_reason
|
||||
expose :applications, using: ClusterApplicationEntity
|
||||
|
||||
expose :path do |cluster|
|
||||
Clusters::ClusterPresenter.new(cluster).show_path # rubocop: disable CodeReuse/Presenter
|
||||
|
|
|
|||
|
|
@ -22,6 +22,6 @@ class ClusterSerializer < BaseSerializer
|
|||
end
|
||||
|
||||
def represent_status(resource)
|
||||
represent(resource, { only: [:status, :status_reason, :applications] })
|
||||
represent(resource, { only: [:status, :status_reason] })
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1,30 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Serverless
|
||||
class AssociateDomainService
|
||||
PLACEHOLDER_HOSTNAME = 'example.com'
|
||||
|
||||
def initialize(knative, pages_domain_id:, creator:)
|
||||
@knative = knative
|
||||
@pages_domain_id = pages_domain_id
|
||||
@creator = creator
|
||||
end
|
||||
|
||||
def execute
|
||||
return if unchanged?
|
||||
|
||||
knative.hostname ||= PLACEHOLDER_HOSTNAME
|
||||
|
||||
knative.pages_domain = knative.find_available_domain(pages_domain_id)
|
||||
knative.serverless_domain_cluster.update(creator: creator) if knative.pages_domain
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
attr_reader :knative, :pages_domain_id, :creator
|
||||
|
||||
def unchanged?
|
||||
knative.pages_domain&.id == pages_domain_id
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -1,4 +1,3 @@
|
|||
- @hide_breadcrumbs = true
|
||||
- page_title _('Enter 2FA for Admin Mode')
|
||||
|
||||
.row.justify-content-center
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
%p.text-center
|
||||
%span.light
|
||||
= _('Already have login and password?')
|
||||
= _('Already have an account?')
|
||||
- path_params = { redirect_to_referer: 'yes' }
|
||||
- path_params[:invite_email] = @invite_email if @invite_email.present?
|
||||
= link_to _('Sign in'), new_session_path(:user, path_params)
|
||||
|
|
|
|||
|
|
@ -1,7 +1,5 @@
|
|||
---
|
||||
table_name: serverless_domain_cluster
|
||||
classes:
|
||||
- Serverless::DomainCluster
|
||||
feature_categories:
|
||||
- kubernetes_management
|
||||
description: "(Deprecated) A custom domain for a GitLab managed Knative installation"
|
||||
|
|
|
|||
|
|
@ -0,0 +1,23 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class QueueBackfillPreparedAtData < Gitlab::Database::Migration[2.1]
|
||||
MIGRATION = 'BackfillPreparedAtMergeRequests'
|
||||
DELAY_INTERVAL = 2.minutes
|
||||
|
||||
restrict_gitlab_migration gitlab_schema: :gitlab_main
|
||||
|
||||
def up
|
||||
queue_batched_background_migration(
|
||||
MIGRATION,
|
||||
:merge_requests,
|
||||
:id,
|
||||
job_interval: DELAY_INTERVAL,
|
||||
batch_size: 5000,
|
||||
sub_batch_size: 250
|
||||
)
|
||||
end
|
||||
|
||||
def down
|
||||
delete_batched_background_migration(MIGRATION, :merge_requests, :id, [])
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1 @@
|
|||
877ea1462505cfc9986353e5fb5f8cfc68a7557140bdc162bcfbd7a68c266f97
|
||||
|
|
@ -133,7 +133,7 @@ Now run a new pipeline for the `main` branch, by pushing a change or manually ru
|
|||
|
||||
## 5. Create a tag
|
||||
|
||||
As the pipeline for `main` is green, we can now [create our first tag](../../../user/project/repository/tags/index.md#tags-sample-workflow): `v1.0`.
|
||||
As the pipeline for `main` is green, we can now [create our first tag](../../../user/project/repository/tags/index.md#create-a-tag): `v1.0`.
|
||||
|
||||
As soon as the `v1.0` tag is created, we see a tag pipeline start.
|
||||
This time the pipeline also has a `create-release` job in the `release` stage:
|
||||
|
|
|
|||
Binary file not shown.
|
Before Width: | Height: | Size: 2.9 KiB |
|
|
@ -9,62 +9,70 @@ type: index, concepts, howto
|
|||
|
||||
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/4466) in GitLab Ultimate 10.6.
|
||||
> - [Moved](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/24780) to GitLab Free in 11.9.
|
||||
> - `CHAT_USER_ID` was [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/341798) in GitLab 14.4.
|
||||
> - `CHAT_USER_ID` [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/341798) in GitLab 14.4.
|
||||
|
||||
GitLab ChatOps provides a method to interact with CI/CD jobs through chat services
|
||||
like Slack. Many organizations' discussion, collaboration, and troubleshooting takes
|
||||
place in chat services. Having a method to run CI/CD jobs with output
|
||||
posted back to the channel can significantly augment your team's workflow.
|
||||
Use GitLab ChatOps to interact with CI/CD jobs through chat services
|
||||
like Slack.
|
||||
|
||||
## How GitLab ChatOps works
|
||||
Many organizations use chat services to collaborate, troubleshoot, and plan work. With ChatOps,
|
||||
you can discuss work with your team, run CI/CD jobs, and view job output, all from the same
|
||||
application.
|
||||
|
||||
GitLab ChatOps is built upon [GitLab CI/CD](../index.md) and
|
||||
[Slack Slash Commands](../../user/project/integrations/slack_slash_commands.md).
|
||||
ChatOps provides a `run` action for [slash commands](../../integration/slash_commands.md)
|
||||
with the following arguments:
|
||||
## ChatOps workflow and CI/CD configuration
|
||||
|
||||
- A `<job name>` to execute.
|
||||
- The `<job arguments>`.
|
||||
ChatOps looks for the specified job in the
|
||||
[`.gitlab-ci.yml`](../yaml/index.md) on the project's default
|
||||
branch. If the job is found, ChatOps creates a pipeline that contains
|
||||
only the specified job. If you set `when: manual`, ChatOps creates the
|
||||
pipeline, but the job doesn't start automatically.
|
||||
|
||||
A job run with ChatOps has the same functionality as a job run from
|
||||
GitLab. The job can use existing [CI/CD variables](../variables/index.md#predefined-cicd-variables) like
|
||||
`GITLAB_USER_ID` to perform additional rights validation, but these
|
||||
variables can be [overridden](../variables/index.md#cicd-variable-precedence).
|
||||
|
||||
You should set [`rules`](../yaml/index.md#rules) so the job does not
|
||||
run as part of the standard CI/CD pipeline.
|
||||
|
||||
ChatOps passes the following [CI/CD variables](../variables/index.md#predefined-cicd-variables)
|
||||
to the job:
|
||||
|
||||
- `CHAT_INPUT` contains any additional arguments.
|
||||
- `CHAT_CHANNEL` is set to the name of channel the action was triggered in.
|
||||
- `CHAT_USER_ID` is set to the chat service's user ID of the user who triggered the slash command.
|
||||
- `CHAT_INPUT` - The arguments passed to `/project-name run`.
|
||||
- `CHAT_CHANNEL` - The name of the chat channel the job is run from.
|
||||
- `CHAT_USER_ID` - The chat service ID of the user who runs the job.
|
||||
|
||||
When executed, ChatOps looks up the specified job name and attempts to match it
|
||||
to a corresponding job in [`.gitlab-ci.yml`](../yaml/index.md). If a matching job
|
||||
is found on the default branch, a pipeline containing only that job is scheduled. After the
|
||||
job completes:
|
||||
When the job runs:
|
||||
|
||||
- If the job completes in *less than 30 minutes*, the ChatOps sends the job's output to Slack.
|
||||
- If the job completes in *more than 30 minutes*, the job must use the
|
||||
- If the job completes in less than 30 minutes, ChatOps sends the job output to the chat channel.
|
||||
- If the job completes in more than 30 minutes, you must use a method like the
|
||||
[Slack API](https://api.slack.com/) to send data to the channel.
|
||||
|
||||
To use the `run` command, you must have at least the
|
||||
Developer role.
|
||||
If a job shouldn't be able to be triggered from chat, you can set the job to `except: [chat]`.
|
||||
## Run a CI/CD job
|
||||
|
||||
## Best practices for ChatOps CI jobs
|
||||
You can run a CI/CD job from chat with the `/project-name run`
|
||||
[slash command](../../integration/slash_commands.md).
|
||||
|
||||
Since ChatOps is built upon GitLab CI/CD, the job has all the same features and
|
||||
functions available. Consider these best practices when creating ChatOps jobs:
|
||||
Prerequisites:
|
||||
|
||||
- GitLab strongly recommends you set [`rules`](../yaml/index.md#rules) so the job does not run as part
|
||||
of the standard CI pipeline.
|
||||
- If the job is set to `when: manual`, ChatOps creates the pipeline, but the job waits to be started.
|
||||
- ChatOps provides limited support for access control. If the user triggering the
|
||||
slash command has at least the Developer role
|
||||
in the project, the job runs. The job itself can use existing
|
||||
[CI/CD variables](../variables/index.md#predefined-cicd-variables) like
|
||||
`GITLAB_USER_ID` to perform additional rights validation, but
|
||||
these variables can be [overridden](../variables/index.md#cicd-variable-precedence).
|
||||
- You must have at least the Developer role for the project.
|
||||
|
||||
### Controlling the ChatOps reply
|
||||
To run a CI/CD job:
|
||||
|
||||
The output for jobs with a single command is sent to the channel as a reply. For
|
||||
example, the chat reply of the following job is `Hello World` in the channel:
|
||||
- In the chat client, enter `/project-name run <job name> <arguments>`.
|
||||
|
||||
ChatOps schedules a pipeline that contains only the specified job.
|
||||
|
||||
### Exclude a job from ChatOps
|
||||
|
||||
To prevent a job from being run from chat:
|
||||
|
||||
- In `.gitlab-ci.yml`, set the job to `except: [chat]`.
|
||||
|
||||
## Customize the ChatOps reply
|
||||
|
||||
ChatOps sends the output for a job with a single command to the
|
||||
channel as a reply. For example, when the following job runs,
|
||||
the chat reply is `Hello world`:
|
||||
|
||||
```yaml
|
||||
stages:
|
||||
|
|
@ -78,13 +86,12 @@ hello-world:
|
|||
- echo "Hello World"
|
||||
```
|
||||
|
||||
Jobs that contain multiple commands (or `before_script`) return additional
|
||||
content in the chat reply. In these cases, both the commands and their output are
|
||||
included, with the commands wrapped in ANSI color codes.
|
||||
If the job contains multiple commands, or if `before_script` is set, ChatOps sends the commands
|
||||
and their output to the channel. The commands are wrapped in ANSI color codes.
|
||||
|
||||
To selectively reply with the output of one command, its output must be bounded by
|
||||
the `chat_reply` section. For example, the following job lists the files in the
|
||||
current directory:
|
||||
To selectively reply with the output of one command, place the output
|
||||
in a `chat_reply` section. For example, the following job lists the
|
||||
files in the current directory:
|
||||
|
||||
```yaml
|
||||
stages:
|
||||
|
|
@ -99,28 +106,11 @@ ls:
|
|||
- echo -e "section_start:$( date +%s ):chat_reply\r\033[0K\n$( ls -la )\nsection_end:$( date +%s ):chat_reply\r\033[0K"
|
||||
```
|
||||
|
||||
## GitLab ChatOps examples
|
||||
## Example scripts
|
||||
|
||||
The GitLab.com team created a repository of [common ChatOps scripts](https://gitlab.com/gitlab-com/chatops)
|
||||
they use to interact with our Production instance of GitLab. Administrators of
|
||||
other GitLab instances may find them useful. They can serve as inspiration for ChatOps
|
||||
scripts you can write to interact with your own applications.
|
||||
The GitLab team maintains a repository of [common ChatOps scripts](https://gitlab.com/gitlab-com/chatops)
|
||||
they use to interact with GitLab.com.
|
||||
|
||||
## GitLab ChatOps icon
|
||||
## Related topics
|
||||
|
||||
The [official GitLab ChatOps icon](img/gitlab-chatops-icon.png) is available for download.
|
||||
You can find and download the official GitLab ChatOps icon here.
|
||||
|
||||

|
||||
|
||||
<!-- ## Troubleshooting
|
||||
|
||||
Include any troubleshooting steps that you can foresee. If you know beforehand what issues
|
||||
one might have when setting this up, or when something is changed, or on upgrading, it's
|
||||
important to describe those, too. Think of things that may go wrong and include them here.
|
||||
This is important to minimize requests for support, and to avoid doc comments with
|
||||
questions that you know someone might ask.
|
||||
|
||||
Each scenario can be a third-level heading, for example `### Getting error message X`.
|
||||
If you have none to add when creating a doc, leave this section in place
|
||||
but commented out to help encourage others to add to it in the future. -->
|
||||
- Download the [official GitLab ChatOps icon](img/gitlab-chatops-icon.png).
|
||||
|
|
|
|||
|
|
@ -453,6 +453,22 @@ Composition API allows you to place the logic in the `<script>` section of the c
|
|||
</script>
|
||||
```
|
||||
|
||||
### `v-bind` limitations
|
||||
|
||||
Avoid using `v-bind="$attrs"` unless absolutely necessary. You might need this when
|
||||
developing a native control wrapper. (This is a good candidate for a `gitlab-ui` component.)
|
||||
In any other cases, always prefer using `props` and explicit data flow.
|
||||
|
||||
Using `v-bind="$attrs"` leads to:
|
||||
|
||||
1. A loss in component's contract. The `props` were designed specifically
|
||||
to address this problem.
|
||||
1. High maintenance cost for each component in the tree. `v-bind="$attrs"` is specifically
|
||||
hard to debug because you must scan the whole hierarchy of components to understand
|
||||
the data flow.
|
||||
1. Problems during migration to Vue 3. `$attrs` in Vue 3 include event listeners which
|
||||
could cause unexpected side-effects after Vue 3 migration is completed.
|
||||
|
||||
### Aim to have one API style per component
|
||||
|
||||
When adding `setup()` property to Vue component, consider refactoring it to Composition API entirely. It's not always feasible, especially for large components, but we should aim to have one API style per component for readability and maintainability.
|
||||
|
|
|
|||
|
|
@ -130,12 +130,6 @@ Use this to check the Git history of the file:
|
|||
git log -- <file>
|
||||
```
|
||||
|
||||
### Find the tags that contain a particular SHA
|
||||
|
||||
```shell
|
||||
git tag --contains <sha>
|
||||
```
|
||||
|
||||
### Check the content of each change to a file
|
||||
|
||||
```shell
|
||||
|
|
|
|||
|
|
@ -97,7 +97,7 @@ docker run -d \
|
|||
-v "${CERTS_DIR}/fullchain.pem:/gitlab-rd-web-ide/certs/fullchain.pem" \
|
||||
-v "${CERTS_DIR}/privkey.pem:/gitlab-rd-web-ide/certs/privkey.pem" \
|
||||
-v "${PROJECTS_DIR}:/projects" \
|
||||
registry.gitlab.com/gitlab-com/create-stage/editor-poc/remote-development/gitlab-rd-web-ide-docker:0.1-alpha \
|
||||
registry.gitlab.com/gitlab-org/remote-development/gitlab-rd-web-ide-docker:0.2-alpha \
|
||||
--log-level warn --domain "${DOMAIN}" --ignore-version-mismatch
|
||||
```
|
||||
|
||||
|
|
|
|||
|
|
@ -6,15 +6,65 @@ info: To determine the technical writer assigned to the Stage/Group associated w
|
|||
|
||||
# Tags **(FREE)**
|
||||
|
||||
Tags help you mark certain deployments and releases for later
|
||||
reference. Git supports two types of tags:
|
||||
In Git, a tag marks an important point in a repository's history.
|
||||
Git supports two types of tags:
|
||||
|
||||
- Annotated tags: An unchangeable part of Git history.
|
||||
- Lightweight (soft) tags: Tags that can be set and removed as needed.
|
||||
- **Lightweight tags** point to specific commits, and contain no other information.
|
||||
Also known as soft tags. Create or remove them as needed.
|
||||
- **Annotated tags** contain metadata, can be signed for verification purposes,
|
||||
and can't be changed.
|
||||
|
||||
The creation or deletion of a tag can be used as a trigger for automation, including:
|
||||
|
||||
- Using a [webhook](../../integrations/webhook_events.md#tag-events) to automate actions
|
||||
like Slack notifications.
|
||||
- Signaling a [repository mirror](../mirror/index.md) to update.
|
||||
- Running a CI/CD pipeline with [`if: $CI_COMMIT_TAG`](../../../../ci/jobs/job_control.md#common-if-clauses-for-rules).
|
||||
|
||||
When you [create a release](../../releases/index.md),
|
||||
GitLab also creates a tag to mark the release point.
|
||||
Many projects combine an annotated release tag with a stable branch. Consider
|
||||
setting deployment or release tags automatically.
|
||||
|
||||
To prevent users from removing a tag with `git push`, create a [push rule](../push_rules.md).
|
||||
|
||||
## Create a tag
|
||||
|
||||
Tags can be created from the command line, or the GitLab UI.
|
||||
|
||||
### From the command line
|
||||
|
||||
To create either a lightweight or annotated tag from the command line, and push it upstream:
|
||||
|
||||
1. To create a lightweight tag, run the command `git tag TAG_NAME`, changing
|
||||
`TAG_NAME` to your desired tag name.
|
||||
1. To create an annotated tag, run one of the versions of `git tag` from the command line:
|
||||
|
||||
```shell
|
||||
# In this short version, the annotated tag's name is "v1.0",
|
||||
# and the message is "Version 1.0".
|
||||
git tag -a v1.0 -m "Version 1.0"
|
||||
|
||||
# Use this version to write a longer tag message
|
||||
# for annotated tag "v1.0" in your text editor.
|
||||
git tag -a v1.0
|
||||
```
|
||||
|
||||
1. Push your tags upstream with `git push origin --tags`.
|
||||
|
||||
### From the UI
|
||||
|
||||
To create a tag from the GitLab UI:
|
||||
|
||||
1. On the top bar, select **Main menu > Projects** and find your project.
|
||||
1. On the left sidebar, select **Repository > Tags**.
|
||||
1. Select **New tag**.
|
||||
1. Provide a **Tag name**.
|
||||
1. For **Create from**, select an existing branch name, tag, or commit SHA.
|
||||
1. Optional. Add a **Message** to create an annotated tag, or leave blank to
|
||||
create a lightweight tag.
|
||||
1. Select **Create tag**.
|
||||
|
||||
## View tags for a project
|
||||
|
||||
1. On the top bar, select **Main menu > Projects** and find your project.
|
||||
|
|
@ -34,25 +84,13 @@ In the GitLab UI, each tag displays:
|
|||
- A [**Create release**](../../releases/index.md#create-a-release) (**{pencil}**) link.
|
||||
- A link to delete the tag.
|
||||
|
||||
## Tags sample workflow
|
||||
## Find tags containing a commit
|
||||
|
||||
1. Create a lightweight tag.
|
||||
1. Create an annotated tag.
|
||||
1. Push the tags to the remote repository.
|
||||
To search all Git tags for a particular SHA (commit identifier), run this
|
||||
command from the command line, replacing `SHA` with the SHA of the commit:
|
||||
|
||||
```shell
|
||||
git checkout main
|
||||
|
||||
# Lightweight tag
|
||||
git tag my_lightweight_tag
|
||||
|
||||
# Annotated tag
|
||||
git tag -a v1.0 -m 'Version 1.0'
|
||||
|
||||
# Show list of the existing tags
|
||||
git tag
|
||||
|
||||
git push origin --tags
|
||||
git tag --contains SHA
|
||||
```
|
||||
|
||||
## Related topics
|
||||
|
|
@ -60,4 +98,3 @@ git push origin --tags
|
|||
- [Tagging](https://git-scm.com/book/en/v2/Git-Basics-Tagging) Git reference page.
|
||||
- [Protected tags](../../protected_tags.md).
|
||||
- [Tags API](../../../../api/tags.md).
|
||||
- [Use `if: $CI_COMMIT_TAG` to run CI/CD pipelines for tags](../../../../ci/jobs/job_control.md#common-if-clauses-for-rules).
|
||||
|
|
|
|||
|
|
@ -0,0 +1,18 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Gitlab
|
||||
module BackgroundMigration
|
||||
# Backfill prepared_at for an array of merge requests
|
||||
class BackfillPreparedAtMergeRequests < ::Gitlab::BackgroundMigration::BatchedMigrationJob
|
||||
scope_to ->(relation) { relation }
|
||||
operation_name :update_all
|
||||
feature_category :code_review_workflow
|
||||
|
||||
def perform
|
||||
each_sub_batch do |sub_batch|
|
||||
sub_batch.where(prepared_at: nil).where.not(merge_status: 'preparing').update_all('prepared_at = created_at')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -30,7 +30,7 @@ module Gitlab
|
|||
end
|
||||
|
||||
def to_result
|
||||
Yaml::Result.new(load!)
|
||||
Yaml::Result.new(load!, error: nil)
|
||||
rescue ::Gitlab::Config::Loader::FormatError => e
|
||||
Yaml::Result.new(error: e)
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1,42 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Gitlab
|
||||
module Prometheus
|
||||
module Queries
|
||||
class KnativeInvocationQuery < BaseQuery
|
||||
include QueryAdditionalMetrics
|
||||
|
||||
def query(serverless_function_id)
|
||||
PrometheusMetricsFinder
|
||||
.new(identifier: :system_metrics_knative_function_invocation_count, common: true)
|
||||
.execute
|
||||
.first
|
||||
.to_query_metric
|
||||
.tap do |q|
|
||||
q.queries[0][:result] = run_query(q.queries[0][:query_range], context(serverless_function_id))
|
||||
end
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def context(function_id)
|
||||
function = ::Serverless::Function.find_by_id(function_id)
|
||||
{
|
||||
function_name: function.name,
|
||||
kube_namespace: function.namespace
|
||||
}
|
||||
end
|
||||
|
||||
def run_query(query, context)
|
||||
query %= context
|
||||
client_query_range(query, start_time: 8.hours.ago.to_f, end_time: Time.now.to_f)
|
||||
end
|
||||
|
||||
def self.transform_reactive_result(result)
|
||||
result[:metrics] = result.delete :data
|
||||
result
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -1,102 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Gitlab::Serverless::Service
|
||||
include Gitlab::Utils::StrongMemoize
|
||||
|
||||
def initialize(attributes)
|
||||
@attributes = attributes
|
||||
end
|
||||
|
||||
def name
|
||||
@attributes.dig('metadata', 'name')
|
||||
end
|
||||
|
||||
def namespace
|
||||
@attributes.dig('metadata', 'namespace')
|
||||
end
|
||||
|
||||
def environment_scope
|
||||
@attributes.dig('environment_scope')
|
||||
end
|
||||
|
||||
def environment
|
||||
@attributes.dig('environment')
|
||||
end
|
||||
|
||||
def podcount
|
||||
@attributes.dig('podcount')
|
||||
end
|
||||
|
||||
def created_at
|
||||
strong_memoize(:created_at) do
|
||||
timestamp = @attributes.dig('metadata', 'creationTimestamp')
|
||||
DateTime.parse(timestamp) if timestamp
|
||||
end
|
||||
end
|
||||
|
||||
def image
|
||||
@attributes.dig(
|
||||
'spec',
|
||||
'runLatest',
|
||||
'configuration',
|
||||
'build',
|
||||
'template',
|
||||
'name')
|
||||
end
|
||||
|
||||
def description
|
||||
knative_07_description || knative_05_06_description
|
||||
end
|
||||
|
||||
def cluster
|
||||
@attributes.dig('cluster')
|
||||
end
|
||||
|
||||
def url
|
||||
proxy_url || knative_06_07_url || knative_05_url
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def proxy_url
|
||||
if cluster&.serverless_domain
|
||||
::Serverless::Domain.new(
|
||||
function_name: name,
|
||||
serverless_domain_cluster: cluster.serverless_domain,
|
||||
environment: environment
|
||||
).uri.to_s
|
||||
end
|
||||
end
|
||||
|
||||
def knative_07_description
|
||||
@attributes.dig(
|
||||
'spec',
|
||||
'template',
|
||||
'metadata',
|
||||
'annotations',
|
||||
'Description'
|
||||
)
|
||||
end
|
||||
|
||||
def knative_05_06_description
|
||||
@attributes.dig(
|
||||
'spec',
|
||||
'runLatest',
|
||||
'configuration',
|
||||
'revisionTemplate',
|
||||
'metadata',
|
||||
'annotations',
|
||||
'Description')
|
||||
end
|
||||
|
||||
def knative_05_url
|
||||
domain = @attributes.dig('status', 'domain')
|
||||
return unless domain
|
||||
|
||||
"http://#{domain}"
|
||||
end
|
||||
|
||||
def knative_06_07_url
|
||||
@attributes.dig('status', 'url')
|
||||
end
|
||||
end
|
||||
|
|
@ -4254,7 +4254,7 @@ msgstr ""
|
|||
msgid "Already blocked"
|
||||
msgstr ""
|
||||
|
||||
msgid "Already have login and password?"
|
||||
msgid "Already have an account?"
|
||||
msgstr ""
|
||||
|
||||
msgid "Also called \"Issuer\" or \"Relying party trust identifier\""
|
||||
|
|
@ -41984,6 +41984,9 @@ msgstr ""
|
|||
msgid "SubscriptionTable|Last invoice"
|
||||
msgstr ""
|
||||
|
||||
msgid "SubscriptionTable|Last updated at %{seatsLastUpdated} UTC"
|
||||
msgstr ""
|
||||
|
||||
msgid "SubscriptionTable|Loading subscriptions"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -42044,6 +42047,9 @@ msgstr ""
|
|||
msgid "SubscriptionTable|Trial start date"
|
||||
msgstr ""
|
||||
|
||||
msgid "SubscriptionTable|Up to date"
|
||||
msgstr ""
|
||||
|
||||
msgid "SubscriptionTable|Usage"
|
||||
msgstr ""
|
||||
|
||||
|
|
|
|||
|
|
@ -1,11 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
FactoryBot.define do
|
||||
factory :serverless_domain, class: '::Serverless::Domain' do
|
||||
function_name { 'test-function' }
|
||||
serverless_domain_cluster { association(:serverless_domain_cluster) }
|
||||
environment { association(:environment) }
|
||||
|
||||
skip_create
|
||||
end
|
||||
end
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
FactoryBot.define do
|
||||
factory :serverless_domain_cluster, class: '::Serverless::DomainCluster' do
|
||||
pages_domain { association(:pages_domain) }
|
||||
knative { association(:clusters_applications_knative) }
|
||||
creator { association(:user) }
|
||||
|
||||
certificate do
|
||||
File.read(Rails.root.join('spec/fixtures/', 'ssl_certificate.pem'))
|
||||
end
|
||||
|
||||
key do
|
||||
File.read(Rails.root.join('spec/fixtures/', 'ssl_key.pem'))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -1,103 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe ServerlessDomainFinder do
|
||||
let(:function_name) { 'test-function' }
|
||||
let(:pages_domain_name) { 'serverless.gitlab.io' }
|
||||
let(:valid_cluster_uuid) { 'aba1cdef123456f278' }
|
||||
let(:invalid_cluster_uuid) { 'aba1cdef123456f178' }
|
||||
let!(:environment) { create(:environment, name: 'test') }
|
||||
|
||||
let(:pages_domain) do
|
||||
create(
|
||||
:pages_domain,
|
||||
:instance_serverless,
|
||||
domain: pages_domain_name
|
||||
)
|
||||
end
|
||||
|
||||
let(:knative_with_ingress) do
|
||||
create(
|
||||
:clusters_applications_knative,
|
||||
external_ip: '10.0.0.1'
|
||||
)
|
||||
end
|
||||
|
||||
let!(:serverless_domain_cluster) do
|
||||
create(
|
||||
:serverless_domain_cluster,
|
||||
uuid: 'abcdef12345678',
|
||||
pages_domain: pages_domain,
|
||||
knative: knative_with_ingress
|
||||
)
|
||||
end
|
||||
|
||||
let(:valid_uri) { "https://#{function_name}-#{valid_cluster_uuid}#{"%x" % environment.id}-#{environment.slug}.#{pages_domain_name}" }
|
||||
let(:valid_fqdn) { "#{function_name}-#{valid_cluster_uuid}#{"%x" % environment.id}-#{environment.slug}.#{pages_domain_name}" }
|
||||
let(:invalid_uri) { "https://#{function_name}-#{invalid_cluster_uuid}#{"%x" % environment.id}-#{environment.slug}.#{pages_domain_name}" }
|
||||
|
||||
let(:valid_finder) { described_class.new(valid_uri) }
|
||||
let(:invalid_finder) { described_class.new(invalid_uri) }
|
||||
|
||||
describe '#serverless?' do
|
||||
context 'with a valid URI' do
|
||||
subject { valid_finder.serverless? }
|
||||
|
||||
it { is_expected.to be_truthy }
|
||||
end
|
||||
|
||||
context 'with an invalid URI' do
|
||||
subject { invalid_finder.serverless? }
|
||||
|
||||
it { is_expected.to be_falsy }
|
||||
end
|
||||
end
|
||||
|
||||
describe '#serverless_domain_cluster_uuid' do
|
||||
context 'with a valid URI' do
|
||||
subject { valid_finder.serverless_domain_cluster_uuid }
|
||||
|
||||
it { is_expected.to eq serverless_domain_cluster.uuid }
|
||||
end
|
||||
|
||||
context 'with an invalid URI' do
|
||||
subject { invalid_finder.serverless_domain_cluster_uuid }
|
||||
|
||||
it { is_expected.to be_nil }
|
||||
end
|
||||
end
|
||||
|
||||
describe '#execute' do
|
||||
context 'with a valid URI' do
|
||||
let(:serverless_domain) do
|
||||
create(
|
||||
:serverless_domain,
|
||||
function_name: function_name,
|
||||
serverless_domain_cluster: serverless_domain_cluster,
|
||||
environment: environment
|
||||
)
|
||||
end
|
||||
|
||||
subject { valid_finder.execute }
|
||||
|
||||
it 'has the correct function_name' do
|
||||
expect(subject.function_name).to eq function_name
|
||||
end
|
||||
|
||||
it 'has the correct serverless_domain_cluster' do
|
||||
expect(subject.serverless_domain_cluster).to eq serverless_domain_cluster
|
||||
end
|
||||
|
||||
it 'has the correct environment' do
|
||||
expect(subject.environment).to eq environment
|
||||
end
|
||||
end
|
||||
|
||||
context 'with an invalid URI' do
|
||||
subject { invalid_finder.execute }
|
||||
|
||||
it { is_expected.to be_nil }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -1,8 +1,7 @@
|
|||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"status",
|
||||
"applications"
|
||||
"status"
|
||||
],
|
||||
"properties": {
|
||||
"status": {
|
||||
|
|
@ -10,12 +9,6 @@
|
|||
},
|
||||
"status_reason": {
|
||||
"$ref": "types/nullable_string.json"
|
||||
},
|
||||
"applications": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/application_status"
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
|
|
@ -115,4 +108,4 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
require 'fast_spec_helper'
|
||||
|
||||
RSpec.describe Atlassian::JiraIssueKeyExtractor do
|
||||
RSpec.describe Atlassian::JiraIssueKeyExtractor, feature_category: :integrations do
|
||||
describe '.has_keys?' do
|
||||
subject { described_class.has_keys?(string) }
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,57 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe Gitlab::BackgroundMigration::BackfillPreparedAtMergeRequests, :migration,
|
||||
feature_category: :code_review_workflow, schema: 20230202135758 do
|
||||
let(:namespaces) { table(:namespaces) }
|
||||
let(:projects) { table(:projects) }
|
||||
let(:mr_table) { table(:merge_requests) }
|
||||
|
||||
let(:namespace) { namespaces.create!(name: 'batchtest1', type: 'Group', path: 'space1') }
|
||||
let(:proj_namespace) { namespaces.create!(name: 'proj1', path: 'proj1', type: 'Project', parent_id: namespace.id) }
|
||||
let(:project) do
|
||||
projects.create!(name: 'proj1', path: 'proj1', namespace_id: namespace.id, project_namespace_id: proj_namespace.id)
|
||||
end
|
||||
|
||||
let(:test_worker) do
|
||||
described_class.new(
|
||||
start_id: 1,
|
||||
end_id: 100,
|
||||
batch_table: :merge_requests,
|
||||
batch_column: :id,
|
||||
sub_batch_size: 10,
|
||||
pause_ms: 0,
|
||||
connection: ApplicationRecord.connection
|
||||
)
|
||||
end
|
||||
|
||||
it 'updates merge requests with prepared_at nil' do
|
||||
time = Time.current
|
||||
|
||||
mr_1 = mr_table.create!(target_project_id: project.id, source_branch: 'master', target_branch: 'feature',
|
||||
prepared_at: nil, merge_status: 'checking')
|
||||
mr_2 = mr_table.create!(target_project_id: project.id, source_branch: 'master', target_branch: 'feature',
|
||||
prepared_at: nil, merge_status: 'preparing')
|
||||
mr_3 = mr_table.create!(target_project_id: project.id, source_branch: 'master', target_branch: 'feature',
|
||||
prepared_at: time)
|
||||
mr_4 = mr_table.create!(target_project_id: project.id, source_branch: 'master', target_branch: 'feature',
|
||||
prepared_at: time, merge_status: 'checking')
|
||||
mr_5 = mr_table.create!(target_project_id: project.id, source_branch: 'master', target_branch: 'feature',
|
||||
prepared_at: time, merge_status: 'preparing')
|
||||
|
||||
expect(mr_1.prepared_at).to be_nil
|
||||
expect(mr_2.prepared_at).to be_nil
|
||||
expect(mr_3.prepared_at.to_i).to eq(time.to_i)
|
||||
expect(mr_4.prepared_at.to_i).to eq(time.to_i)
|
||||
expect(mr_5.prepared_at.to_i).to eq(time.to_i)
|
||||
|
||||
test_worker.perform
|
||||
|
||||
expect(mr_1.reload.prepared_at.to_i).to eq(mr_1.created_at.to_i)
|
||||
expect(mr_2.reload.prepared_at).to be_nil
|
||||
expect(mr_3.reload.prepared_at.to_i).to eq(time.to_i)
|
||||
expect(mr_4.reload.prepared_at.to_i).to eq(time.to_i)
|
||||
expect(mr_5.reload.prepared_at.to_i).to eq(time.to_i)
|
||||
end
|
||||
end
|
||||
|
|
@ -1,31 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe Gitlab::Prometheus::Queries::KnativeInvocationQuery do
|
||||
include PrometheusHelpers
|
||||
|
||||
let(:project) { create(:project) }
|
||||
let(:serverless_func) { ::Serverless::Function.new(project, 'test-name', 'test-ns') }
|
||||
let(:client) { double('prometheus_client') }
|
||||
|
||||
subject { described_class.new(client) }
|
||||
|
||||
context 'verify queries' do
|
||||
before do
|
||||
create(:prometheus_metric,
|
||||
:common,
|
||||
identifier: :system_metrics_knative_function_invocation_count,
|
||||
query: 'sum(ceil(rate(istio_requests_total{destination_service_namespace="%{kube_namespace}", destination_service=~"%{function_name}.*"}[1m])*60))')
|
||||
end
|
||||
|
||||
it 'has the query, but no data' do
|
||||
expect(client).to receive(:query_range).with(
|
||||
'sum(ceil(rate(istio_requests_total{destination_service_namespace="test-ns", destination_service=~"test-name.*"}[1m])*60))',
|
||||
hash_including(:start_time, :end_time)
|
||||
)
|
||||
|
||||
subject.query(serverless_func.id)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -1,136 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe Gitlab::Serverless::Service do
|
||||
let(:cluster) { create(:cluster) }
|
||||
let(:environment) { create(:environment) }
|
||||
let(:attributes) do
|
||||
{
|
||||
'apiVersion' => 'serving.knative.dev/v1alpha1',
|
||||
'kind' => 'Service',
|
||||
'metadata' => {
|
||||
'creationTimestamp' => '2019-10-22T21:19:13Z',
|
||||
'name' => 'kubetest',
|
||||
'namespace' => 'project1-1-environment1'
|
||||
},
|
||||
'spec' => {
|
||||
'runLatest' => {
|
||||
'configuration' => {
|
||||
'build' => {
|
||||
'template' => {
|
||||
'name' => 'some-image'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
'environment_scope' => '*',
|
||||
'cluster' => cluster,
|
||||
'environment' => environment,
|
||||
'podcount' => 0
|
||||
}
|
||||
end
|
||||
|
||||
it 'exposes methods extracting data from the attributes hash' do
|
||||
service = Gitlab::Serverless::Service.new(attributes)
|
||||
|
||||
expect(service.name).to eq('kubetest')
|
||||
expect(service.namespace).to eq('project1-1-environment1')
|
||||
expect(service.environment_scope).to eq('*')
|
||||
expect(service.podcount).to eq(0)
|
||||
expect(service.created_at).to eq(DateTime.parse('2019-10-22T21:19:13Z'))
|
||||
expect(service.image).to eq('some-image')
|
||||
expect(service.cluster).to eq(cluster)
|
||||
expect(service.environment).to eq(environment)
|
||||
end
|
||||
|
||||
it 'returns nil for missing attributes' do
|
||||
service = Gitlab::Serverless::Service.new({})
|
||||
|
||||
[:name, :namespace, :environment_scope, :cluster, :podcount, :created_at, :image, :description, :url, :environment].each do |method|
|
||||
expect(service.send(method)).to be_nil
|
||||
end
|
||||
end
|
||||
|
||||
describe '#description' do
|
||||
it 'extracts the description in knative 7 format if available' do
|
||||
attributes = {
|
||||
'spec' => {
|
||||
'template' => {
|
||||
'metadata' => {
|
||||
'annotations' => {
|
||||
'Description' => 'some description'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
service = Gitlab::Serverless::Service.new(attributes)
|
||||
|
||||
expect(service.description).to eq('some description')
|
||||
end
|
||||
|
||||
it 'extracts the description in knative 5/6 format if 7 is not available' do
|
||||
attributes = {
|
||||
'spec' => {
|
||||
'runLatest' => {
|
||||
'configuration' => {
|
||||
'revisionTemplate' => {
|
||||
'metadata' => {
|
||||
'annotations' => {
|
||||
'Description' => 'some description'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
service = Gitlab::Serverless::Service.new(attributes)
|
||||
|
||||
expect(service.description).to eq('some description')
|
||||
end
|
||||
end
|
||||
|
||||
describe '#url' do
|
||||
let(:serverless_domain) { instance_double(::Serverless::Domain, uri: URI('https://proxy.example.com')) }
|
||||
|
||||
it 'returns proxy URL if cluster has serverless domain' do
|
||||
# cluster = create(:cluster)
|
||||
knative = create(:clusters_applications_knative, :installed, cluster: cluster)
|
||||
create(:serverless_domain_cluster, clusters_applications_knative_id: knative.id)
|
||||
service = Gitlab::Serverless::Service.new(attributes.merge('cluster' => cluster))
|
||||
|
||||
expect(::Serverless::Domain).to receive(:new).with(
|
||||
function_name: service.name,
|
||||
serverless_domain_cluster: service.cluster.serverless_domain,
|
||||
environment: service.environment
|
||||
).and_return(serverless_domain)
|
||||
|
||||
expect(service.url).to eq('https://proxy.example.com')
|
||||
end
|
||||
|
||||
it 'returns the URL from the knative 6/7 format' do
|
||||
attributes = {
|
||||
'status' => {
|
||||
'url' => 'https://example.com'
|
||||
}
|
||||
}
|
||||
service = Gitlab::Serverless::Service.new(attributes)
|
||||
|
||||
expect(service.url).to eq('https://example.com')
|
||||
end
|
||||
|
||||
it 'returns the URL from the knative 5 format' do
|
||||
attributes = {
|
||||
'status' => {
|
||||
'domain' => 'example.com'
|
||||
}
|
||||
}
|
||||
service = Gitlab::Serverless::Service.new(attributes)
|
||||
|
||||
expect(service.url).to eq('http://example.com')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
require_migration!
|
||||
|
||||
RSpec.describe QueueBackfillPreparedAtData, feature_category: :code_review_workflow do
|
||||
let!(:batched_migration) { described_class::MIGRATION }
|
||||
|
||||
it 'schedules a new batched migration' do
|
||||
reversible_migration do |migration|
|
||||
migration.before -> {
|
||||
expect(batched_migration).not_to have_scheduled_batched_migration
|
||||
}
|
||||
|
||||
migration.after -> {
|
||||
expect(batched_migration).to have_scheduled_batched_migration(
|
||||
table_name: :merge_requests,
|
||||
column_name: :id,
|
||||
interval: described_class::DELAY_INTERVAL
|
||||
)
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -17,10 +17,6 @@ RSpec.describe Clusters::Applications::Knative do
|
|||
include_examples 'cluster application version specs', :clusters_applications_knative
|
||||
include_examples 'cluster application initial status specs'
|
||||
|
||||
describe 'associations' do
|
||||
it { is_expected.to have_one(:serverless_domain_cluster).class_name('::Serverless::DomainCluster').with_foreign_key('clusters_applications_knative_id').inverse_of(:knative) }
|
||||
end
|
||||
|
||||
describe 'default values' do
|
||||
it { expect(subject.version).to eq(described_class::VERSION) }
|
||||
end
|
||||
|
|
@ -249,12 +245,4 @@ RSpec.describe Clusters::Applications::Knative do
|
|||
expect(subject.find_available_domain(domain.id)).to eq(domain)
|
||||
end
|
||||
end
|
||||
|
||||
describe '#pages_domain' do
|
||||
let!(:sdc) { create(:serverless_domain_cluster, knative: knative) }
|
||||
|
||||
it 'returns the the associated pages domain' do
|
||||
expect(knative.reload.pages_domain).to eq(sdc.pages_domain)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@ RSpec.describe PagesDomain do
|
|||
|
||||
describe 'associations' do
|
||||
it { is_expected.to belong_to(:project) }
|
||||
it { is_expected.to have_many(:serverless_domain_clusters) }
|
||||
end
|
||||
|
||||
describe '.for_project' do
|
||||
|
|
|
|||
|
|
@ -1,75 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe ::Serverless::DomainCluster do
|
||||
subject { create(:serverless_domain_cluster) }
|
||||
|
||||
describe 'default values' do
|
||||
subject(:domain_cluster) { build(:serverless_domain_cluster) }
|
||||
|
||||
before do
|
||||
allow(::Serverless::Domain).to receive(:generate_uuid).and_return('randomtoken')
|
||||
end
|
||||
|
||||
it { expect(domain_cluster.uuid).to eq('randomtoken') }
|
||||
end
|
||||
|
||||
describe 'validations' do
|
||||
it { is_expected.to validate_presence_of(:pages_domain) }
|
||||
it { is_expected.to validate_presence_of(:knative) }
|
||||
|
||||
it { is_expected.to validate_presence_of(:uuid) }
|
||||
it { is_expected.to validate_length_of(:uuid).is_equal_to(::Serverless::Domain::UUID_LENGTH) }
|
||||
it { is_expected.to validate_uniqueness_of(:uuid) }
|
||||
|
||||
it 'validates that uuid has only hex characters' do
|
||||
subject = build(:serverless_domain_cluster, uuid: 'z1234567890123')
|
||||
subject.valid?
|
||||
|
||||
expect(subject.errors[:uuid]).to include('only allows hex characters')
|
||||
end
|
||||
end
|
||||
|
||||
describe 'associations' do
|
||||
it { is_expected.to belong_to(:pages_domain) }
|
||||
it { is_expected.to belong_to(:knative) }
|
||||
it { is_expected.to belong_to(:creator).optional }
|
||||
end
|
||||
|
||||
describe 'uuid' do
|
||||
context 'when nil' do
|
||||
it 'generates a value by default' do
|
||||
attributes = build(:serverless_domain_cluster).attributes.merge(uuid: nil)
|
||||
expect(::Serverless::Domain).to receive(:generate_uuid).and_call_original
|
||||
|
||||
subject = Serverless::DomainCluster.new(attributes)
|
||||
|
||||
expect(subject.uuid).not_to be_blank
|
||||
end
|
||||
end
|
||||
|
||||
context 'when not nil' do
|
||||
it 'does not override the existing value' do
|
||||
uuid = 'abcd1234567890'
|
||||
expect(build(:serverless_domain_cluster, uuid: uuid).uuid).to eq(uuid)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'cluster' do
|
||||
it { is_expected.to respond_to(:cluster) }
|
||||
end
|
||||
|
||||
describe 'domain' do
|
||||
it { is_expected.to respond_to(:domain) }
|
||||
end
|
||||
|
||||
describe 'certificate' do
|
||||
it { is_expected.to respond_to(:certificate) }
|
||||
end
|
||||
|
||||
describe 'key' do
|
||||
it { is_expected.to respond_to(:key) }
|
||||
end
|
||||
end
|
||||
|
|
@ -1,97 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe ::Serverless::Domain do
|
||||
let(:function_name) { 'test-function' }
|
||||
let(:pages_domain_name) { 'serverless.gitlab.io' }
|
||||
let(:pages_domain) { create(:pages_domain, :instance_serverless, domain: pages_domain_name) }
|
||||
let!(:serverless_domain_cluster) { create(:serverless_domain_cluster, uuid: 'abcdef12345678', pages_domain: pages_domain) }
|
||||
let(:valid_cluster_uuid) { 'aba1cdef123456f278' }
|
||||
let(:invalid_cluster_uuid) { 'aba1cdef123456f178' }
|
||||
let!(:environment) { create(:environment, name: 'test') }
|
||||
|
||||
let(:valid_uri) { "https://#{function_name}-#{valid_cluster_uuid}#{"%x" % environment.id}-#{environment.slug}.#{pages_domain_name}" }
|
||||
let(:valid_fqdn) { "#{function_name}-#{valid_cluster_uuid}#{"%x" % environment.id}-#{environment.slug}.#{pages_domain_name}" }
|
||||
let(:invalid_uri) { "https://#{function_name}-#{invalid_cluster_uuid}#{"%x" % environment.id}-#{environment.slug}.#{pages_domain_name}" }
|
||||
|
||||
shared_examples 'a valid Domain' do
|
||||
describe '#uri' do
|
||||
it 'matches valid URI' do
|
||||
expect(subject.uri.to_s).to eq valid_uri
|
||||
end
|
||||
end
|
||||
|
||||
describe '#function_name' do
|
||||
it 'returns function_name' do
|
||||
expect(subject.function_name).to eq function_name
|
||||
end
|
||||
end
|
||||
|
||||
describe '#serverless_domain_cluster' do
|
||||
it 'returns serverless_domain_cluster' do
|
||||
expect(subject.serverless_domain_cluster).to eq serverless_domain_cluster
|
||||
end
|
||||
end
|
||||
|
||||
describe '#environment' do
|
||||
it 'returns environment' do
|
||||
expect(subject.environment).to eq environment
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '.new' do
|
||||
context 'with valid arguments' do
|
||||
subject do
|
||||
described_class.new(
|
||||
function_name: function_name,
|
||||
serverless_domain_cluster: serverless_domain_cluster,
|
||||
environment: environment
|
||||
)
|
||||
end
|
||||
|
||||
it_behaves_like 'a valid Domain'
|
||||
end
|
||||
|
||||
context 'with invalid arguments' do
|
||||
subject do
|
||||
described_class.new(
|
||||
function_name: function_name,
|
||||
environment: environment
|
||||
)
|
||||
end
|
||||
|
||||
it { is_expected.not_to be_valid }
|
||||
end
|
||||
|
||||
context 'with nil cluster argument' do
|
||||
subject do
|
||||
described_class.new(
|
||||
function_name: function_name,
|
||||
serverless_domain_cluster: nil,
|
||||
environment: environment
|
||||
)
|
||||
end
|
||||
|
||||
it { is_expected.not_to be_valid }
|
||||
end
|
||||
end
|
||||
|
||||
describe '.generate_uuid' do
|
||||
it 'has 14 characters' do
|
||||
expect(described_class.generate_uuid.length).to eq(described_class::UUID_LENGTH)
|
||||
end
|
||||
|
||||
it 'consists of only hexadecimal characters' do
|
||||
expect(described_class.generate_uuid).to match(/\A\h+\z/)
|
||||
end
|
||||
|
||||
it 'uses random characters' do
|
||||
uuid = 'abcd1234567890'
|
||||
|
||||
expect(SecureRandom).to receive(:hex).with(described_class::UUID_LENGTH / 2).and_return(uuid)
|
||||
expect(described_class.generate_uuid).to eq(uuid)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe ::Serverless::Function do
|
||||
let(:project) { create(:project) }
|
||||
let(:func) { described_class.new(project, 'test', 'test-ns') }
|
||||
|
||||
it 'has a proper id' do
|
||||
expect(func.id).to eql("#{project.id}/test/test-ns")
|
||||
expect(func.name).to eql("test")
|
||||
expect(func.namespace).to eql("test-ns")
|
||||
end
|
||||
|
||||
it 'can decode an identifier' do
|
||||
f = described_class.find_by_id("#{project.id}/testfunc/dummy-ns")
|
||||
|
||||
expect(f.name).to eql("testfunc")
|
||||
expect(f.namespace).to eql("dummy-ns")
|
||||
end
|
||||
end
|
||||
|
|
@ -60,9 +60,13 @@ RSpec.describe BlobPresenter do
|
|||
describe '#pipeline_editor_path' do
|
||||
context 'when blob is .gitlab-ci.yml' do
|
||||
before do
|
||||
project.repository.create_file(user, '.gitlab-ci.yml', '',
|
||||
message: 'Add a ci file',
|
||||
branch_name: 'main')
|
||||
project.repository.create_file(
|
||||
user,
|
||||
'.gitlab-ci.yml',
|
||||
'',
|
||||
message: 'Add a ci file',
|
||||
branch_name: 'main'
|
||||
)
|
||||
end
|
||||
|
||||
let(:blob) { repository.blob_at('main', '.gitlab-ci.yml') }
|
||||
|
|
|
|||
|
|
@ -228,16 +228,20 @@ RSpec.describe Ci::BuildRunnerPresenter do
|
|||
let(:pipeline) { build.pipeline }
|
||||
|
||||
it 'returns the correct refspecs' do
|
||||
is_expected.to contain_exactly("+refs/heads/#{build.ref}:refs/remotes/origin/#{build.ref}",
|
||||
"+refs/pipelines/#{pipeline.id}:refs/pipelines/#{pipeline.id}")
|
||||
is_expected.to contain_exactly(
|
||||
"+refs/heads/#{build.ref}:refs/remotes/origin/#{build.ref}",
|
||||
"+refs/pipelines/#{pipeline.id}:refs/pipelines/#{pipeline.id}"
|
||||
)
|
||||
end
|
||||
|
||||
context 'when ref is tag' do
|
||||
let(:build) { create(:ci_build, :tag) }
|
||||
|
||||
it 'returns the correct refspecs' do
|
||||
is_expected.to contain_exactly("+refs/tags/#{build.ref}:refs/tags/#{build.ref}",
|
||||
"+refs/pipelines/#{pipeline.id}:refs/pipelines/#{pipeline.id}")
|
||||
is_expected.to contain_exactly(
|
||||
"+refs/tags/#{build.ref}:refs/tags/#{build.ref}",
|
||||
"+refs/pipelines/#{pipeline.id}:refs/pipelines/#{pipeline.id}"
|
||||
)
|
||||
end
|
||||
|
||||
context 'when GIT_DEPTH is zero' do
|
||||
|
|
@ -246,9 +250,11 @@ RSpec.describe Ci::BuildRunnerPresenter do
|
|||
end
|
||||
|
||||
it 'returns the correct refspecs' do
|
||||
is_expected.to contain_exactly('+refs/tags/*:refs/tags/*',
|
||||
'+refs/heads/*:refs/remotes/origin/*',
|
||||
"+refs/pipelines/#{pipeline.id}:refs/pipelines/#{pipeline.id}")
|
||||
is_expected.to contain_exactly(
|
||||
'+refs/tags/*:refs/tags/*',
|
||||
'+refs/heads/*:refs/remotes/origin/*',
|
||||
"+refs/pipelines/#{pipeline.id}:refs/pipelines/#{pipeline.id}"
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -273,10 +279,11 @@ RSpec.describe Ci::BuildRunnerPresenter do
|
|||
end
|
||||
|
||||
it 'returns the correct refspecs' do
|
||||
is_expected
|
||||
.to contain_exactly("+refs/pipelines/#{pipeline.id}:refs/pipelines/#{pipeline.id}",
|
||||
'+refs/heads/*:refs/remotes/origin/*',
|
||||
'+refs/tags/*:refs/tags/*')
|
||||
is_expected.to contain_exactly(
|
||||
"+refs/pipelines/#{pipeline.id}:refs/pipelines/#{pipeline.id}",
|
||||
'+refs/heads/*:refs/remotes/origin/*',
|
||||
'+refs/tags/*:refs/tags/*'
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -284,8 +291,10 @@ RSpec.describe Ci::BuildRunnerPresenter do
|
|||
let(:merge_request) { create(:merge_request, :with_legacy_detached_merge_request_pipeline) }
|
||||
|
||||
it 'returns the correct refspecs' do
|
||||
is_expected.to contain_exactly("+refs/pipelines/#{pipeline.id}:refs/pipelines/#{pipeline.id}",
|
||||
"+refs/heads/#{build.ref}:refs/remotes/origin/#{build.ref}")
|
||||
is_expected.to contain_exactly(
|
||||
"+refs/pipelines/#{pipeline.id}:refs/pipelines/#{pipeline.id}",
|
||||
"+refs/heads/#{build.ref}:refs/remotes/origin/#{build.ref}"
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -301,9 +310,10 @@ RSpec.describe Ci::BuildRunnerPresenter do
|
|||
end
|
||||
|
||||
it 'exposes the persistent pipeline ref' do
|
||||
is_expected
|
||||
.to contain_exactly("+refs/pipelines/#{pipeline.id}:refs/pipelines/#{pipeline.id}",
|
||||
"+refs/heads/#{build.ref}:refs/remotes/origin/#{build.ref}")
|
||||
is_expected.to contain_exactly(
|
||||
"+refs/pipelines/#{pipeline.id}:refs/pipelines/#{pipeline.id}",
|
||||
"+refs/heads/#{build.ref}:refs/remotes/origin/#{build.ref}"
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -327,16 +337,14 @@ RSpec.describe Ci::BuildRunnerPresenter do
|
|||
|
||||
context 'when there is a file variable to expand' do
|
||||
before_all do
|
||||
create(:ci_variable, project: project,
|
||||
key: 'regular_var',
|
||||
value: 'value 1')
|
||||
create(:ci_variable, project: project,
|
||||
key: 'file_var',
|
||||
value: 'value 2',
|
||||
variable_type: :file)
|
||||
create(:ci_variable, project: project,
|
||||
key: 'var_with_variables',
|
||||
value: 'value 3 and $regular_var and $file_var and $undefined_var')
|
||||
create(:ci_variable, project: project, key: 'regular_var', value: 'value 1')
|
||||
create(:ci_variable, project: project, key: 'file_var', value: 'value 2', variable_type: :file)
|
||||
create(
|
||||
:ci_variable,
|
||||
project: project,
|
||||
key: 'var_with_variables',
|
||||
value: 'value 3 and $regular_var and $file_var and $undefined_var'
|
||||
)
|
||||
end
|
||||
|
||||
it 'returns variables with expanded' do
|
||||
|
|
@ -353,16 +361,14 @@ RSpec.describe Ci::BuildRunnerPresenter do
|
|||
|
||||
context 'when there is a raw variable to expand' do
|
||||
before_all do
|
||||
create(:ci_variable, project: project,
|
||||
key: 'regular_var',
|
||||
value: 'value 1')
|
||||
create(:ci_variable, project: project,
|
||||
key: 'raw_var',
|
||||
value: 'value 2',
|
||||
raw: true)
|
||||
create(:ci_variable, project: project,
|
||||
key: 'var_with_variables',
|
||||
value: 'value 3 and $regular_var and $raw_var and $undefined_var')
|
||||
create(:ci_variable, project: project, key: 'regular_var', value: 'value 1')
|
||||
create(:ci_variable, project: project, key: 'raw_var', value: 'value 2', raw: true)
|
||||
create(
|
||||
:ci_variable,
|
||||
project: project,
|
||||
key: 'var_with_variables',
|
||||
value: 'value 3 and $regular_var and $raw_var and $undefined_var'
|
||||
)
|
||||
end
|
||||
|
||||
it 'returns expanded variables without expanding raws' do
|
||||
|
|
|
|||
|
|
@ -125,9 +125,12 @@ RSpec.describe MergeRequestPresenter do
|
|||
let_it_be(:issue_b) { create(:issue, project: project) }
|
||||
|
||||
let_it_be(:resource) do
|
||||
create(:merge_request,
|
||||
source_project: project, target_project: project,
|
||||
description: "Fixes #{issue_a.to_reference} Related #{issue_b.to_reference}")
|
||||
create(
|
||||
:merge_request,
|
||||
source_project: project,
|
||||
target_project: project,
|
||||
description: "Fixes #{issue_a.to_reference} Related #{issue_b.to_reference}"
|
||||
)
|
||||
end
|
||||
|
||||
before_all do
|
||||
|
|
|
|||
|
|
@ -89,8 +89,7 @@ RSpec.describe ::Packages::Detail::PackagePresenter do
|
|||
let_it_be(:package) { create(:npm_package, :with_build, project: project) }
|
||||
|
||||
let_it_be(:package_file_build_info) do
|
||||
create(:package_file_build_info, package_file: package.package_files.first,
|
||||
pipeline: package.pipelines.first)
|
||||
create(:package_file_build_info, package_file: package.package_files.first, pipeline: package.pipelines.first)
|
||||
end
|
||||
|
||||
it 'returns details with package_file pipeline' do
|
||||
|
|
|
|||
|
|
@ -76,94 +76,6 @@ RSpec.describe API::Internal::Pages, feature_category: :pages do
|
|||
end
|
||||
end
|
||||
|
||||
context 'serverless domain' do
|
||||
let(:namespace) { create(:namespace, name: 'gitlab-org') }
|
||||
let(:project) { create(:project, namespace: namespace, name: 'gitlab-ce') }
|
||||
let(:environment) { create(:environment, project: project) }
|
||||
let(:pages_domain) { create(:pages_domain, domain: 'serverless.gitlab.io') }
|
||||
let(:knative_without_ingress) { create(:clusters_applications_knative) }
|
||||
let(:knative_with_ingress) { create(:clusters_applications_knative, external_ip: '10.0.0.1') }
|
||||
|
||||
context 'without a knative ingress gateway IP' do
|
||||
let!(:serverless_domain_cluster) do
|
||||
create(
|
||||
:serverless_domain_cluster,
|
||||
uuid: 'abcdef12345678',
|
||||
pages_domain: pages_domain,
|
||||
knative: knative_without_ingress
|
||||
)
|
||||
end
|
||||
|
||||
let(:serverless_domain) do
|
||||
create(
|
||||
:serverless_domain,
|
||||
serverless_domain_cluster: serverless_domain_cluster,
|
||||
environment: environment
|
||||
)
|
||||
end
|
||||
|
||||
it 'responds with 204 no content' do
|
||||
query_host(serverless_domain.uri.host)
|
||||
|
||||
expect(response).to have_gitlab_http_status(:no_content)
|
||||
expect(response.body).to be_empty
|
||||
end
|
||||
end
|
||||
|
||||
context 'with a knative ingress gateway IP' do
|
||||
let!(:serverless_domain_cluster) do
|
||||
create(
|
||||
:serverless_domain_cluster,
|
||||
uuid: 'abcdef12345678',
|
||||
pages_domain: pages_domain,
|
||||
knative: knative_with_ingress
|
||||
)
|
||||
end
|
||||
|
||||
let(:serverless_domain) do
|
||||
create(
|
||||
:serverless_domain,
|
||||
serverless_domain_cluster: serverless_domain_cluster,
|
||||
environment: environment
|
||||
)
|
||||
end
|
||||
|
||||
it 'responds with 204 because of feature deprecation' do
|
||||
query_host(serverless_domain.uri.host)
|
||||
|
||||
expect(response).to have_gitlab_http_status(:no_content)
|
||||
expect(response.body).to be_empty
|
||||
|
||||
##
|
||||
# Serverless serving and reverse proxy to Kubernetes / Knative has
|
||||
# been deprecated and disabled, as per
|
||||
# https://gitlab.com/gitlab-org/gitlab-pages/-/issues/467
|
||||
#
|
||||
# expect(response).to match_response_schema('internal/serverless/virtual_domain')
|
||||
# expect(json_response['certificate']).to eq(pages_domain.certificate)
|
||||
# expect(json_response['key']).to eq(pages_domain.key)
|
||||
#
|
||||
# expect(json_response['lookup_paths']).to eq(
|
||||
# [
|
||||
# {
|
||||
# 'source' => {
|
||||
# 'type' => 'serverless',
|
||||
# 'service' => "test-function.#{project.name}-#{project.id}-#{environment.slug}.#{serverless_domain_cluster.knative.hostname}",
|
||||
# 'cluster' => {
|
||||
# 'hostname' => serverless_domain_cluster.knative.hostname,
|
||||
# 'address' => serverless_domain_cluster.knative.external_ip,
|
||||
# 'port' => 443,
|
||||
# 'cert' => serverless_domain_cluster.certificate,
|
||||
# 'key' => serverless_domain_cluster.key
|
||||
# }
|
||||
# }
|
||||
# }
|
||||
# ]
|
||||
# )
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'custom domain' do
|
||||
let(:namespace) { create(:namespace, name: 'gitlab-org') }
|
||||
let(:project) { create(:project, namespace: namespace, name: 'gitlab-ce') }
|
||||
|
|
|
|||
|
|
@ -1,81 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe ClusterApplicationEntity do
|
||||
describe '#as_json' do
|
||||
let(:application) { build(:clusters_applications_helm, version: '0.1.1') }
|
||||
|
||||
subject { described_class.new(application).as_json }
|
||||
|
||||
it 'has name' do
|
||||
expect(subject[:name]).to eq(application.name)
|
||||
end
|
||||
|
||||
it 'has status' do
|
||||
expect(subject[:status]).to eq(:not_installable)
|
||||
end
|
||||
|
||||
it 'has version' do
|
||||
expect(subject[:version]).to eq('0.1.1')
|
||||
end
|
||||
|
||||
it 'has no status_reason' do
|
||||
expect(subject[:status_reason]).to be_nil
|
||||
end
|
||||
|
||||
it 'has can_uninstall' do
|
||||
expect(subject[:can_uninstall]).to be_truthy
|
||||
end
|
||||
|
||||
context 'non-helm application' do
|
||||
let(:application) { build(:clusters_applications_runner, version: '0.0.0') }
|
||||
|
||||
it 'has update_available' do
|
||||
expect(subject[:update_available]).to be_truthy
|
||||
end
|
||||
end
|
||||
|
||||
context 'when application is errored' do
|
||||
let(:application) { build(:clusters_applications_helm, :errored) }
|
||||
|
||||
it 'has corresponded data' do
|
||||
expect(subject[:status]).to eq(:errored)
|
||||
expect(subject[:status_reason]).not_to be_nil
|
||||
expect(subject[:status_reason]).to eq(application.status_reason)
|
||||
end
|
||||
end
|
||||
|
||||
context 'for ingress application' do
|
||||
let(:application) do
|
||||
build(
|
||||
:clusters_applications_ingress,
|
||||
:installed,
|
||||
external_ip: '111.222.111.222'
|
||||
)
|
||||
end
|
||||
|
||||
it 'includes external_ip' do
|
||||
expect(subject[:external_ip]).to eq('111.222.111.222')
|
||||
end
|
||||
end
|
||||
|
||||
context 'for knative application' do
|
||||
let(:pages_domain) { create(:pages_domain, :instance_serverless) }
|
||||
let(:application) { build(:clusters_applications_knative, :installed) }
|
||||
|
||||
before do
|
||||
create(:serverless_domain_cluster, knative: application, pages_domain: pages_domain)
|
||||
end
|
||||
|
||||
it 'includes available domains' do
|
||||
expect(subject[:available_domains].length).to eq(1)
|
||||
expect(subject[:available_domains].first).to eq(id: pages_domain.id, domain: pages_domain.domain)
|
||||
end
|
||||
|
||||
it 'includes pages_domain' do
|
||||
expect(subject[:pages_domain]).to eq(id: pages_domain.id, domain: pages_domain.domain)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -41,19 +41,5 @@ RSpec.describe ClusterEntity do
|
|||
expect(subject[:status_reason]).to be_nil
|
||||
end
|
||||
end
|
||||
|
||||
context 'when no application has been installed' do
|
||||
let(:cluster) { create(:cluster, :instance) }
|
||||
|
||||
subject { described_class.new(cluster, request: request).as_json[:applications] }
|
||||
|
||||
it 'contains helm as not_installable' do
|
||||
expect(subject).not_to be_empty
|
||||
|
||||
helm = subject[0]
|
||||
expect(helm[:name]).to eq('helm')
|
||||
expect(helm[:status]).to eq(:not_installable)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -34,13 +34,13 @@ RSpec.describe ClusterSerializer do
|
|||
end
|
||||
|
||||
it 'serializes attrs correctly' do
|
||||
is_expected.to contain_exactly(:status, :status_reason, :applications)
|
||||
is_expected.to contain_exactly(:status, :status_reason)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when provider type is user' do
|
||||
it 'serializes attrs correctly' do
|
||||
is_expected.to contain_exactly(:status, :status_reason, :applications)
|
||||
is_expected.to contain_exactly(:status, :status_reason)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1,91 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe Serverless::AssociateDomainService do
|
||||
let_it_be(:sdc_pages_domain) { create(:pages_domain, :instance_serverless) }
|
||||
let_it_be(:sdc_cluster) { create(:cluster, :with_installed_helm, :provided_by_gcp) }
|
||||
let_it_be(:sdc_knative) { create(:clusters_applications_knative, cluster: sdc_cluster) }
|
||||
let_it_be(:sdc_creator) { create(:user) }
|
||||
|
||||
let(:sdc) do
|
||||
create(:serverless_domain_cluster,
|
||||
knative: sdc_knative,
|
||||
creator: sdc_creator,
|
||||
pages_domain: sdc_pages_domain)
|
||||
end
|
||||
|
||||
let(:knative) { sdc.knative }
|
||||
let(:creator) { sdc.creator }
|
||||
let(:pages_domain_id) { sdc.pages_domain_id }
|
||||
|
||||
subject { described_class.new(knative, pages_domain_id: pages_domain_id, creator: creator) }
|
||||
|
||||
context 'when the domain is unchanged' do
|
||||
let(:creator) { create(:user) }
|
||||
|
||||
it 'does not update creator' do
|
||||
expect { subject.execute }.not_to change { sdc.reload.creator }
|
||||
end
|
||||
end
|
||||
|
||||
context 'when domain is changed to nil' do
|
||||
let_it_be(:creator) { create(:user) }
|
||||
let_it_be(:pages_domain_id) { nil }
|
||||
|
||||
it 'removes the association between knative and the domain' do
|
||||
expect { subject.execute }.to change { knative.reload.pages_domain }.from(sdc.pages_domain).to(nil)
|
||||
end
|
||||
|
||||
it 'does not attempt to update creator' do
|
||||
expect { subject.execute }.not_to raise_error
|
||||
end
|
||||
end
|
||||
|
||||
context 'when a new domain is associated' do
|
||||
let_it_be(:creator) { create(:user) }
|
||||
let_it_be(:pages_domain_id) { create(:pages_domain, :instance_serverless).id }
|
||||
|
||||
it 'creates an association with the domain' do
|
||||
expect { subject.execute }.to change { knative.reload.pages_domain.id }
|
||||
.from(sdc.pages_domain.id)
|
||||
.to(pages_domain_id)
|
||||
end
|
||||
|
||||
it 'updates creator' do
|
||||
expect { subject.execute }.to change { sdc.reload.creator }.from(sdc.creator).to(creator)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when knative is not authorized to use the pages domain' do
|
||||
let_it_be(:pages_domain_id) { create(:pages_domain).id }
|
||||
|
||||
before do
|
||||
expect(knative).to receive(:available_domains).and_return(PagesDomain.none)
|
||||
end
|
||||
|
||||
it 'sets pages_domain_id to nil' do
|
||||
expect { subject.execute }.to change { knative.reload.pages_domain }.from(sdc.pages_domain).to(nil)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'for new knative application' do
|
||||
let_it_be(:cluster) { create(:cluster, :with_installed_helm, :provided_by_gcp) }
|
||||
|
||||
context 'when knative hostname is nil' do
|
||||
let(:knative) { build(:clusters_applications_knative, cluster: cluster, hostname: nil) }
|
||||
|
||||
it 'sets hostname to a placeholder value' do
|
||||
expect { subject.execute }.to change { knative.hostname }.to('example.com')
|
||||
end
|
||||
end
|
||||
|
||||
context 'when knative hostname exists' do
|
||||
let(:knative) { build(:clusters_applications_knative, cluster: cluster, hostname: 'hostname.com') }
|
||||
|
||||
it 'does not change hostname' do
|
||||
expect { subject.execute }.not_to change { knative.hostname }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
Loading…
Reference in New Issue