Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2023-03-14 09:08:45 +00:00
parent 3612694ce3
commit 6f9218ac84
60 changed files with 489 additions and 1501 deletions

View File

@ -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'

View File

@ -1 +1 @@
906fdaa2fd6f4dc4baec1b36b9341f4157e2042a
220959f0ffc3d01fa448cc2c7b45b082d56690ef

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -1,4 +1,3 @@
- @hide_breadcrumbs = true
- page_title _('Enter 2FA for Admin Mode')
.row.justify-content-center

View File

@ -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)

View File

@ -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"

View File

@ -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

View File

@ -0,0 +1 @@
877ea1462505cfc9986353e5fb5f8cfc68a7557140bdc162bcfbd7a68c266f97

View File

@ -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

View File

@ -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.
![GitLab ChatOps bot icon](img/gitlab-chatops-icon-small.png)
<!-- ## 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).

View File

@ -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.

View File

@ -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

View File

@ -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
```

View File

@ -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).

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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 ""

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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 @@
}
}
}
}
}

View File

@ -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) }

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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') }

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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') }

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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