diff options
113 files changed, 1383 insertions, 636 deletions
diff --git a/.gitlab/ci/frontend.gitlab-ci.yml b/.gitlab/ci/frontend.gitlab-ci.yml index 6afa668af22..75c79bb213f 100644 --- a/.gitlab/ci/frontend.gitlab-ci.yml +++ b/.gitlab/ci/frontend.gitlab-ci.yml @@ -1,11 +1,10 @@ .assets-compile-cache: &assets-compile-cache cache: - key: "assets-compile:vendor_ruby:.yarn-cache:tmp_cache_assets_sprockets:v5" + key: "assets-compile:vendor_ruby:.yarn-cache:tmp_cache_assets_sprockets:v6" paths: - vendor/ruby/ - .yarn-cache/ - tmp/cache/assets/sprockets - policy: pull-push .use-pg: &use-pg services: @@ -13,7 +12,7 @@ command: ["postgres", "-c", "fsync=off", "-c", "synchronous_commit=off", "-c", "full_page_writes=off"] - name: redis:alpine -gitlab:assets:compile: +.gitlab:assets:compile-metadata: <<: *assets-compile-cache extends: .dedicated-no-docs-pull-cache-job image: dev.gitlab.org:5005/gitlab/gitlab-build-images:ruby-2.6.3-git-2.21-chrome-73.0-node-12.x-yarn-1.16-graphicsmagick-1.3.29-docker-18.06.1 @@ -58,7 +57,23 @@ gitlab:assets:compile: - docker - gitlab-org -compile-assets: +gitlab:assets:compile: + extends: .gitlab:assets:compile-metadata + cache: + policy: pull-push + only: + - master@gitlab-org/gitlab-ce + - master@gitlab-org/gitlab-ee + +gitlab:assets:compile pull-cache: + extends: .gitlab:assets:compile-metadata + cache: + policy: pull + except: + - master@gitlab-org/gitlab-ce + - master@gitlab-org/gitlab-ee + +.compile-assets-metadata: extends: .dedicated-runner <<: *use-pg <<: *assets-compile-cache @@ -77,9 +92,23 @@ compile-assets: paths: - node_modules - public/assets + +compile-assets: + extends: .compile-assets-metadata + cache: + policy: pull-push + only: + - master@gitlab-org/gitlab-ce + - master@gitlab-org/gitlab-ee + +compile-assets pull-cache: + extends: .compile-assets-metadata + cache: + policy: pull except: - refs: - - /(^docs[\/-].*|.*-docs$)/ + - master@gitlab-org/gitlab-ce + - master@gitlab-org/gitlab-ee + - /(^docs[\/-].*|.*-docs$)/ gitlab:ui:visual: extends: .dedicated-runner @@ -87,6 +116,7 @@ gitlab:ui:visual: allow_failure: true dependencies: - compile-assets + - compile-assets pull-cache script: # Remove node modules from GitLab that may conflict with gitlab-ui - rm -r node_modules @@ -116,6 +146,7 @@ karma: <<: *use-pg dependencies: - compile-assets + - compile-assets pull-cache - setup-test-env variables: # we override the max_old_space_size to prevent OOM errors @@ -142,6 +173,7 @@ jest: <<: *use-pg dependencies: - compile-assets + - compile-assets pull-cache - setup-test-env script: - scripts/gitaly-test-spawn @@ -232,6 +264,7 @@ jsdoc: stage: post-test dependencies: - compile-assets + - compile-assets pull-cache before_script: [] script: - date diff --git a/.gitlab/ci/rails.gitlab-ci.yml b/.gitlab/ci/rails.gitlab-ci.yml index 68280506da2..8534b15e16b 100644 --- a/.gitlab/ci/rails.gitlab-ci.yml +++ b/.gitlab/ci/rails.gitlab-ci.yml @@ -236,6 +236,7 @@ static-analysis: extends: .dedicated-no-docs-no-db-pull-cache-job dependencies: - compile-assets + - compile-assets pull-cache - setup-test-env script: - scripts/static-analysis diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index 63b1685feda..698570efb07 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -799,7 +799,6 @@ Style/SelfAssignment: Exclude: - 'app/models/concerns/bulk_member_access_load.rb' - 'app/serializers/base_serializer.rb' - - 'spec/features/projects/clusters/interchangeability_spec.rb' - 'spec/support/import_export/configuration_helper.rb' # Offense count: 50 diff --git a/app/assets/javascripts/filtered_search/constants.js b/app/assets/javascripts/filtered_search/constants.js new file mode 100644 index 00000000000..8ca0e94cfeb --- /dev/null +++ b/app/assets/javascripts/filtered_search/constants.js @@ -0,0 +1,2 @@ +/* eslint-disable import/prefer-default-export */ +export const TOKEN_TYPES = ['author', 'assignee']; diff --git a/app/assets/javascripts/filtered_search/filtered_search_visual_tokens.js b/app/assets/javascripts/filtered_search/filtered_search_visual_tokens.js index 315cd6f64da..7f6457242ef 100644 --- a/app/assets/javascripts/filtered_search/filtered_search_visual_tokens.js +++ b/app/assets/javascripts/filtered_search/filtered_search_visual_tokens.js @@ -1,4 +1,4 @@ -import VisualTokenValue from 'ee_else_ce/filtered_search/visual_token_value'; +import VisualTokenValue from './visual_token_value'; import { objectToQueryString } from '~/lib/utils/common_utils'; import FilteredSearchContainer from './container'; diff --git a/app/assets/javascripts/filtered_search/visual_token_value.js b/app/assets/javascripts/filtered_search/visual_token_value.js index a54b445fb0a..018207541b3 100644 --- a/app/assets/javascripts/filtered_search/visual_token_value.js +++ b/app/assets/javascripts/filtered_search/visual_token_value.js @@ -6,6 +6,7 @@ import DropdownUtils from '~/filtered_search/dropdown_utils'; import Flash from '~/flash'; import UsersCache from '~/lib/utils/users_cache'; import { __ } from '~/locale'; +import { TOKEN_TYPES } from 'ee_else_ce/filtered_search/constants'; export default class VisualTokenValue { constructor(tokenValue, tokenType) { @@ -22,7 +23,7 @@ export default class VisualTokenValue { if (tokenType === 'label') { this.updateLabelTokenColor(tokenValueContainer); - } else if (tokenType === 'author' || tokenType === 'assignee') { + } else if (TOKEN_TYPES.includes(tokenType)) { this.updateUserTokenAppearance(tokenValueContainer, tokenValueElement); } else if (tokenType === 'my-reaction') { this.updateEmojiTokenAppearance(tokenValueContainer, tokenValueElement); diff --git a/app/assets/javascripts/ide/services/index.js b/app/assets/javascripts/ide/services/index.js index ba33b6826d6..840761f68db 100644 --- a/app/assets/javascripts/ide/services/index.js +++ b/app/assets/javascripts/ide/services/index.js @@ -56,7 +56,13 @@ export default { return Api.branchSingle(projectId, currentBranchId); }, commit(projectId, payload) { - return Api.commitMultiple(projectId, payload); + // Currently the `commit` endpoint does not support `start_sha` so we + // have to make the request in the FE. This is not ideal and will be + // resolved soon. https://gitlab.com/gitlab-org/gitlab-ce/issues/59023 + const { branch, start_sha: ref } = payload; + const branchPromise = ref ? Api.createBranch(projectId, { ref, branch }) : Promise.resolve(); + + return branchPromise.then(() => Api.commitMultiple(projectId, payload)); }, getFiles(projectUrl, branchId) { const url = `${projectUrl}/files/${branchId}`; diff --git a/app/assets/javascripts/ide/stores/modules/commit/actions.js b/app/assets/javascripts/ide/stores/modules/commit/actions.js index 51062f092ad..ff1255ce749 100644 --- a/app/assets/javascripts/ide/stores/modules/commit/actions.js +++ b/app/assets/javascripts/ide/stores/modules/commit/actions.js @@ -142,6 +142,7 @@ export const commitChanges = ({ commit, state, getters, dispatch, rootState, roo getters, state, rootState, + rootGetters, }); return service.commit(rootState.currentProjectId, payload); diff --git a/app/assets/javascripts/ide/stores/utils.js b/app/assets/javascripts/ide/stores/utils.js index bcc9ca60d9b..4e7a8765abe 100644 --- a/app/assets/javascripts/ide/stores/utils.js +++ b/app/assets/javascripts/ide/stores/utils.js @@ -135,7 +135,14 @@ export const getCommitFiles = stagedFiles => }); }, []); -export const createCommitPayload = ({ branch, getters, newBranch, state, rootState }) => ({ +export const createCommitPayload = ({ + branch, + getters, + newBranch, + state, + rootState, + rootGetters, +}) => ({ branch, commit_message: state.commitMessage || getters.preBuiltCommitMessage, actions: getCommitFiles(rootState.stagedFiles).map(f => ({ @@ -146,7 +153,7 @@ export const createCommitPayload = ({ branch, getters, newBranch, state, rootSta encoding: f.base64 ? 'base64' : 'text', last_commit_id: newBranch || f.deleted || f.prevPath ? undefined : f.lastCommitSha, })), - start_branch: newBranch ? rootState.currentBranchId : undefined, + start_sha: newBranch ? rootGetters.lastCommit.short_id : undefined, }); export const createNewMergeRequestUrl = (projectUrl, source, target) => diff --git a/app/assets/javascripts/mr_popover/queries/merge_request.graphql b/app/assets/javascripts/mr_popover/queries/merge_request.graphql index 0bb9bc03bc7..37d4bc88a69 100644 --- a/app/assets/javascripts/mr_popover/queries/merge_request.graphql +++ b/app/assets/javascripts/mr_popover/queries/merge_request.graphql @@ -1,4 +1,4 @@ -query mergeRequest($projectPath: ID!, $mergeRequestIID: ID!) { +query mergeRequest($projectPath: ID!, $mergeRequestIID: String!) { project(fullPath: $projectPath) { mergeRequest(iid: $mergeRequestIID) { createdAt diff --git a/app/assets/javascripts/vue_merge_request_widget/components/deployment.vue b/app/assets/javascripts/vue_merge_request_widget/components/deployment.vue index abe5bdd2901..34cdb70ce14 100644 --- a/app/assets/javascripts/vue_merge_request_widget/components/deployment.vue +++ b/app/assets/javascripts/vue_merge_request_widget/components/deployment.vue @@ -49,6 +49,7 @@ export default { required: false, default: () => ({ sourceProjectId: '', + sourceProjectPath: '', mergeRequestId: '', appUrl: '', }), @@ -184,11 +185,6 @@ export default { :link="deploymentExternalUrl" :css-class="`deploy-link js-deploy-url inline ${slotProps.className}`" /> - <visual-review-app-link - v-if="showVisualReviewApp" - :link="deploymentExternalUrl" - :app-metadata="visualReviewAppMeta" - /> </template> <template slot="result" slot-scope="slotProps"> @@ -213,12 +209,12 @@ export default { :link="deploymentExternalUrl" css-class="js-deploy-url js-deploy-url-feature-flag deploy-link btn btn-default btn-sm inline" /> - <visual-review-app-link - v-if="showVisualReviewApp" - :link="deploymentExternalUrl" - :app-metadata="visualReviewAppMeta" - /> </template> + <visual-review-app-link + v-if="showVisualReviewApp" + :link="deploymentExternalUrl" + :app-metadata="visualReviewAppMeta" + /> </template> <span v-if="deployment.stop_url" diff --git a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_pipeline_container.vue b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_pipeline_container.vue index ba9a4d2a187..17ac8ada32d 100644 --- a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_pipeline_container.vue +++ b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_pipeline_container.vue @@ -53,6 +53,7 @@ export default { appUrl: this.mr.appUrl, mergeRequestId: this.mr.iid, sourceProjectId: this.mr.sourceProjectId, + sourceProjectPath: this.mr.sourceProjectFullPath, }; }, pipeline() { @@ -85,7 +86,7 @@ export default { :class="deploymentClass" :deployment="deployment" :show-metrics="hasDeploymentMetrics" - :show-visual-review-app="true" + :show-visual-review-app="showVisualReviewAppLink" :visual-review-app-meta="visualReviewAppMeta" /> </div> diff --git a/app/assets/stylesheets/pages/settings.scss b/app/assets/stylesheets/pages/settings.scss index 0a9c56f5625..3b62121eb0d 100644 --- a/app/assets/stylesheets/pages/settings.scss +++ b/app/assets/stylesheets/pages/settings.scss @@ -340,6 +340,11 @@ .deprecated-service { cursor: default; + + a { + font-weight: $gl-font-weight-bold; + color: $white-light; + } } .personal-access-tokens-never-expires-label { diff --git a/app/controllers/clusters/clusters_controller.rb b/app/controllers/clusters/clusters_controller.rb index 80ee7c35906..ec8077d18e3 100644 --- a/app/controllers/clusters/clusters_controller.rb +++ b/app/controllers/clusters/clusters_controller.rb @@ -128,6 +128,7 @@ class Clusters::ClustersController < Clusters::BaseController :enabled, :name, :environment_scope, + :managed, :base_domain, platform_kubernetes_attributes: [ :api_url, @@ -140,6 +141,7 @@ class Clusters::ClustersController < Clusters::BaseController params.require(:cluster).permit( :enabled, :environment_scope, + :managed, :base_domain, platform_kubernetes_attributes: [ :namespace diff --git a/app/controllers/concerns/issuable_collections.rb b/app/controllers/concerns/issuable_collections.rb index 9cf25915e92..88a0690938a 100644 --- a/app/controllers/concerns/issuable_collections.rb +++ b/app/controllers/concerns/issuable_collections.rb @@ -104,6 +104,12 @@ module IssuableCollections # Used by view to highlight active option @sort = options[:sort] + # When a user looks for an exact iid, we do not filter by search but only by iid + if params[:search] =~ /^#(?<iid>\d+)\z/ + options[:iids] = Regexp.last_match[:iid] + params[:search] = nil + end + if @project options[:project_id] = @project.id options[:attempt_project_search_optimizations] = true diff --git a/app/helpers/services_helper.rb b/app/helpers/services_helper.rb index d4b50b7ecfb..01ccf163b45 100644 --- a/app/helpers/services_helper.rb +++ b/app/helpers/services_helper.rb @@ -39,7 +39,7 @@ module ServicesHelper end def disable_fields_service?(service) - !current_controller?("admin/services") && service.deprecated? + service.is_a?(KubernetesService) || (!current_controller?("admin/services") && service.deprecated?) end extend self diff --git a/app/models/clusters/cluster.rb b/app/models/clusters/cluster.rb index ccc877fb924..8c044c86c47 100644 --- a/app/models/clusters/cluster.rb +++ b/app/models/clusters/cluster.rb @@ -193,21 +193,40 @@ module Clusters platform_kubernetes.kubeclient if kubernetes? end + ## + # This is subtly different to #find_or_initialize_kubernetes_namespace_for_project + # below because it will ignore any namespaces that have not got a service account + # token. This provides a guarantee that any namespace selected here can be used + # for cluster operations - a namespace needs to have a service account configured + # before it it can be used. + # + # This is used for selecting a namespace to use when querying a cluster, or + # generating variables to pass to CI. def kubernetes_namespace_for(project) - find_or_initialize_kubernetes_namespace_for_project(project).namespace + find_or_initialize_kubernetes_namespace_for_project( + project, scope: kubernetes_namespaces.has_service_account_token + ).namespace end - def find_or_initialize_kubernetes_namespace_for_project(project) + ## + # This is subtly different to #kubernetes_namespace_for because it will include + # namespaces that have yet to receive a service account token. This allows + # the namespace configuration process to be repeatable - if a namespace has + # already been created without a token we don't need to create another + # record entirely, just set the token on the pre-existing namespace. + # + # This is used for configuring cluster namespaces. + def find_or_initialize_kubernetes_namespace_for_project(project, scope: kubernetes_namespaces) attributes = { project: project } attributes[:cluster_project] = cluster_project if project_type? - kubernetes_namespaces.find_or_initialize_by(attributes).tap do |namespace| + scope.find_or_initialize_by(attributes).tap do |namespace| namespace.set_defaults end end def allow_user_defined_namespace? - project_type? + project_type? || !managed? end def kube_ingress_domain diff --git a/app/models/clusters/platforms/kubernetes.rb b/app/models/clusters/platforms/kubernetes.rb index 8e06156c73d..272861cacf0 100644 --- a/app/models/clusters/platforms/kubernetes.rb +++ b/app/models/clusters/platforms/kubernetes.rb @@ -80,9 +80,18 @@ module Clusters .append(key: 'KUBE_CA_PEM_FILE', value: ca_pem, file: true) end - if kubernetes_namespace = cluster.kubernetes_namespaces.has_service_account_token.find_by(project: project) + if !cluster.managed? + project_namespace = namespace.presence || "#{project.path}-#{project.id}".downcase + + variables + .append(key: 'KUBE_URL', value: api_url) + .append(key: 'KUBE_TOKEN', value: token, public: false, masked: true) + .append(key: 'KUBE_NAMESPACE', value: project_namespace) + .append(key: 'KUBECONFIG', value: kubeconfig(project_namespace), public: false, file: true) + + elsif kubernetes_namespace = cluster.kubernetes_namespaces.has_service_account_token.find_by(project: project) variables.concat(kubernetes_namespace.predefined_variables) - elsif cluster.project_type? || !cluster.managed? + elsif cluster.project_type? # As of 11.11 a user can create a cluster that they manage themselves, # which replicates the existing project-level cluster behaviour. # Once we have marked all project-level clusters that make use of this diff --git a/app/models/project_services/deployment_service.rb b/app/models/project_services/deployment_service.rb deleted file mode 100644 index 80aa2101509..00000000000 --- a/app/models/project_services/deployment_service.rb +++ /dev/null @@ -1,39 +0,0 @@ -# frozen_string_literal: true - -# Base class for deployment services -# -# These services integrate with a deployment solution like Kubernetes/OpenShift, -# Mesosphere, etc, to provide additional features to environments. -class DeploymentService < Service - default_value_for :category, 'deployment' - - def self.supported_events - %w() - end - - def predefined_variables(project:) - [] - end - - # Environments may have a number of terminals. Should return an array of - # hashes describing them, e.g.: - # - # [{ - # :selectors => {"a" => "b", "foo" => "bar"}, - # :url => "wss://external.example.com/exec", - # :headers => {"Authorization" => "Token xxx"}, - # :subprotocols => ["foo"], - # :ca_pem => "----BEGIN CERTIFICATE...", # optional - # :created_at => Time.now.utc - # }] - # - # Selectors should be a set of values that uniquely identify a particular - # terminal - def terminals(environment) - raise NotImplementedError - end - - def can_test? - false - end -end diff --git a/app/models/project_services/kubernetes_service.rb b/app/models/project_services/kubernetes_service.rb index aa6b4aa1d5e..edf7e886e77 100644 --- a/app/models/project_services/kubernetes_service.rb +++ b/app/models/project_services/kubernetes_service.rb @@ -5,10 +5,12 @@ # We'll move this class to Clusters::Platforms::Kubernetes, which contains exactly the same logic. # After we've migrated data, we'll remove KubernetesService. This would happen in a few months. # If you're modyfiyng this class, please note that you should update the same change in Clusters::Platforms::Kubernetes. -class KubernetesService < DeploymentService +class KubernetesService < Service include Gitlab::Kubernetes include ReactiveCaching + default_value_for :category, 'deployment' + self.reactive_cache_key = ->(service) { [service.class.model_name.singular, service.project_id] } # Namespace defaults to the project path, but can be overridden in case that @@ -32,7 +34,10 @@ class KubernetesService < DeploymentService before_validation :enforce_namespace_to_lower_case - validate :deprecation_validation, unless: :template? + attr_accessor :skip_deprecation_validation + + validate :deprecation_validation, unless: :skip_deprecation_validation + validates :namespace, allow_blank: true, length: 1..63, @@ -44,6 +49,14 @@ class KubernetesService < DeploymentService after_save :clear_reactive_cache! + def self.supported_events + %w() + end + + def can_test? + false + end + def initialize_properties self.properties = {} if properties.nil? end @@ -56,11 +69,6 @@ class KubernetesService < DeploymentService 'Kubernetes / OpenShift integration' end - def help - 'To enable terminal access to Kubernetes environments, label your ' \ - 'deployments with `app=$CI_ENVIRONMENT_SLUG`' - end - def self.to_param 'kubernetes' end @@ -153,14 +161,25 @@ class KubernetesService < DeploymentService end def deprecated? - !active + true + end + + def editable? + false end def deprecation_message - content = _("Kubernetes service integration has been deprecated. %{deprecated_message_content} your Kubernetes clusters using the new <a href=\"%{url}\"/>Kubernetes Clusters</a> page") % { - deprecated_message_content: deprecated_message_content, - url: Gitlab::Routing.url_helpers.project_clusters_path(project) - } + content = if project + _("Kubernetes service integration has been deprecated. %{deprecated_message_content} your Kubernetes clusters using the new <a href=\"%{url}\"/>Kubernetes Clusters</a> page") % { + deprecated_message_content: deprecated_message_content, + url: Gitlab::Routing.url_helpers.project_clusters_path(project) + } + else + _("The instance-level Kubernetes service integration is deprecated. Your data has been migrated to an <a href=\"%{url}\"/>instance-level cluster</a>.") % { + url: Gitlab::Routing.url_helpers.admin_clusters_path + } + end + content.html_safe end @@ -243,10 +262,6 @@ class KubernetesService < DeploymentService end def deprecated_message_content - if active? - _("Your Kubernetes cluster information on this page is still editable, but you are advised to disable and reconfigure") - else - _("Fields on this page are now uneditable, you can configure") - end + _("Fields on this page are now uneditable, you can configure") end end diff --git a/app/models/project_services/mock_deployment_service.rb b/app/models/project_services/mock_deployment_service.rb index 7ab1687f8ba..1103cb11e73 100644 --- a/app/models/project_services/mock_deployment_service.rb +++ b/app/models/project_services/mock_deployment_service.rb @@ -1,6 +1,8 @@ # frozen_string_literal: true -class MockDeploymentService < DeploymentService +class MockDeploymentService < Service + default_value_for :category, 'deployment' + def title 'Mock deployment' end @@ -17,4 +19,16 @@ class MockDeploymentService < DeploymentService def terminals(environment) [] end + + def self.supported_events + %w() + end + + def predefined_variables(project:) + [] + end + + def can_test? + false + end end diff --git a/app/models/service.rb b/app/models/service.rb index 16fbe6648cc..40033003f3b 100644 --- a/app/models/service.rb +++ b/app/models/service.rb @@ -120,7 +120,7 @@ class Service < ApplicationRecord end def self.event_names - self.supported_events.map { |event| "#{event}_events" } + self.supported_events.map { |event| ServicesHelper.service_event_field_name(event) } end def event_field(event) @@ -152,7 +152,7 @@ class Service < ApplicationRecord end def self.supported_events - %w(push tag_push issue confidential_issue merge_request wiki_page) + %w(commit push tag_push issue confidential_issue merge_request wiki_page) end def execute(data) diff --git a/app/views/admin/services/_deprecated_message.html.haml b/app/views/admin/services/_deprecated_message.html.haml new file mode 100644 index 00000000000..fea9506a4bb --- /dev/null +++ b/app/views/admin/services/_deprecated_message.html.haml @@ -0,0 +1,3 @@ +.flash-container.flash-container-page + .flash-alert.deprecated-service + %span= @service.deprecation_message diff --git a/app/views/admin/services/_form.html.haml b/app/views/admin/services/_form.html.haml index 1798b44bbb7..97373a3c350 100644 --- a/app/views/admin/services/_form.html.haml +++ b/app/views/admin/services/_form.html.haml @@ -6,5 +6,6 @@ = form_for :service, url: admin_application_settings_service_path, method: :put, html: { class: 'fieldset-form' } do |form| = render 'shared/service_settings', form: form, subject: @service - .footer-block.row-content-block - = form.submit 'Save', class: 'btn btn-success' + - unless @service.is_a?(KubernetesService) + .footer-block.row-content-block + = form.submit 'Save', class: 'btn btn-success' diff --git a/app/views/admin/services/edit.html.haml b/app/views/admin/services/edit.html.haml index 512176649e6..79f5ab0d77d 100644 --- a/app/views/admin/services/edit.html.haml +++ b/app/views/admin/services/edit.html.haml @@ -1,4 +1,7 @@ - add_to_breadcrumbs "Service Templates", admin_application_settings_services_path - breadcrumb_title @service.title - page_title @service.title, "Service Templates" + += render 'deprecated_message' if @service.deprecation_message + = render 'form' diff --git a/app/views/clusters/clusters/gcp/_form.html.haml b/app/views/clusters/clusters/gcp/_form.html.haml index 70e2eaeaf3b..4d3e3359ea0 100644 --- a/app/views/clusters/clusters/gcp/_form.html.haml +++ b/app/views/clusters/clusters/gcp/_form.html.haml @@ -65,15 +65,6 @@ %p.form-text.text-muted = s_('ClusterIntegration|Learn more about %{help_link_start_machine_type}machine types%{help_link_end} and %{help_link_start_pricing}pricing%{help_link_end}.').html_safe % { help_link_start_machine_type: help_link_start % { url: machine_type_link_url }, help_link_start_pricing: help_link_start % { url: pricing_link_url }, help_link_end: help_link_end } - .form-group - = provider_gcp_field.check_box :legacy_abac, { label: s_('ClusterIntegration|RBAC-enabled cluster'), - label_class: 'label-bold' }, false, true - .form-text.text-muted - = s_('ClusterIntegration|Enable this setting if using role-based access control (RBAC).') - = s_('ClusterIntegration|This option will allow you to install applications on RBAC clusters.') - = link_to _('More information'), help_page_path('user/project/clusters/index.md', - anchor: 'role-based-access-control-rbac-core-only'), target: '_blank' - .form-group = field.check_box :managed, { label: s_('ClusterIntegration|GitLab-managed cluster'), label_class: 'label-bold' } diff --git a/app/views/clusters/platforms/kubernetes/_form.html.haml b/app/views/clusters/platforms/kubernetes/_form.html.haml index c1727cf9079..f2e44462226 100644 --- a/app/views/clusters/platforms/kubernetes/_form.html.haml +++ b/app/views/clusters/platforms/kubernetes/_form.html.haml @@ -48,7 +48,7 @@ = s_('ClusterIntegration|This option will allow you to install applications on RBAC clusters.') .form-group - = field.check_box :managed, { disabled: true, label: s_('ClusterIntegration|GitLab-managed cluster'), + = field.check_box :managed, { label: s_('ClusterIntegration|GitLab-managed cluster'), label_class: 'label-bold' } .form-text.text-muted = s_('ClusterIntegration|Allow GitLab to manage namespace and service accounts for this cluster.') diff --git a/app/views/projects/merge_requests/_mr_box.html.haml b/app/views/projects/merge_requests/_mr_box.html.haml index 7f2c9dcacfd..4f09f47d795 100644 --- a/app/views/projects/merge_requests/_mr_box.html.haml +++ b/app/views/projects/merge_requests/_mr_box.html.haml @@ -1,10 +1,10 @@ .detail-page-description - %h2.title + %h2.title.qa-title = markdown_field(@merge_request, :title) %div - if @merge_request.description.present? - .description{ class: can?(current_user, :update_merge_request, @merge_request) ? 'js-task-list-container' : '' } + .description.qa-description{ class: can?(current_user, :update_merge_request, @merge_request) ? 'js-task-list-container' : '' } .md = markdown_field(@merge_request, :description) %textarea.hidden.js-task-list-field diff --git a/app/views/shared/projects/_search_form.html.haml b/app/views/shared/projects/_search_form.html.haml index 7c7c0a363ac..4365e3f6877 100644 --- a/app/views/shared/projects/_search_form.html.haml +++ b/app/views/shared/projects/_search_form.html.haml @@ -1,7 +1,7 @@ - form_field_classes = local_assigns[:admin_view] || !Feature.enabled?(:project_list_filter_bar) ? 'input-short js-projects-list-filter' : '' - placeholder = local_assigns[:search_form_placeholder] ? search_form_placeholder : 'Filter by name...' -= form_tag filter_projects_path, method: :get, class: 'project-filter-form', id: 'project-filter-form' do |f| += form_tag filter_projects_path, method: :get, class: 'project-filter-form qa-project-filter-form', id: 'project-filter-form' do |f| = search_field_tag :name, params[:name], placeholder: placeholder, class: "project-filter-form-field form-control #{form_field_classes}", diff --git a/changelogs/unreleased/-30974-issue-search-by-number.yml b/changelogs/unreleased/-30974-issue-search-by-number.yml new file mode 100644 index 00000000000..1e6642ec102 --- /dev/null +++ b/changelogs/unreleased/-30974-issue-search-by-number.yml @@ -0,0 +1,5 @@ +--- +title: "Search issuables by iids" +merge_request: !28302 +author: Riccardo Padovani +type: fixed diff --git a/changelogs/unreleased/55362-refresh-blank-service-account-token.yml b/changelogs/unreleased/55362-refresh-blank-service-account-token.yml new file mode 100644 index 00000000000..3189de97e8b --- /dev/null +++ b/changelogs/unreleased/55362-refresh-blank-service-account-token.yml @@ -0,0 +1,5 @@ +--- +title: Refresh service_account_token for kubernetes_namespaces +merge_request: 29657 +author: +type: fixed diff --git a/changelogs/unreleased/55902-disable-creation-of-non-rbac-kubernetes-clusters.yml b/changelogs/unreleased/55902-disable-creation-of-non-rbac-kubernetes-clusters.yml new file mode 100644 index 00000000000..2af2d229c6c --- /dev/null +++ b/changelogs/unreleased/55902-disable-creation-of-non-rbac-kubernetes-clusters.yml @@ -0,0 +1,5 @@ +--- +title: Remove support for creating non-RBAC kubernetes clusters +merge_request: 29614 +author: +type: removed diff --git a/changelogs/unreleased/56737-commits-and-mr-events-on-jira-api.yml b/changelogs/unreleased/56737-commits-and-mr-events-on-jira-api.yml new file mode 100644 index 00000000000..ee2ce8acaeb --- /dev/null +++ b/changelogs/unreleased/56737-commits-and-mr-events-on-jira-api.yml @@ -0,0 +1,5 @@ +--- +title: Expose all current events properly on services API +merge_request: 29736 +author: Zsolt Kovari +type: fixed diff --git a/changelogs/unreleased/59023-fix-web-ide-creating-branches-off-new-commits.yml b/changelogs/unreleased/59023-fix-web-ide-creating-branches-off-new-commits.yml new file mode 100644 index 00000000000..f7e0ee333aa --- /dev/null +++ b/changelogs/unreleased/59023-fix-web-ide-creating-branches-off-new-commits.yml @@ -0,0 +1,5 @@ +--- +title: Fix IDE commit using latest ref in branch and overriding contents +merge_request: 29769 +author: +type: fixed diff --git a/changelogs/unreleased/60617-allow-switching-from-gitlab-managed-to-unmanaged-clusters.yml b/changelogs/unreleased/60617-allow-switching-from-gitlab-managed-to-unmanaged-clusters.yml new file mode 100644 index 00000000000..1127dde4fcf --- /dev/null +++ b/changelogs/unreleased/60617-allow-switching-from-gitlab-managed-to-unmanaged-clusters.yml @@ -0,0 +1,5 @@ +--- +title: Allow switching clusters between managed and unmanaged +merge_request: 29322 +author: +type: added diff --git a/changelogs/unreleased/62685-add-index-invite-email-to-members.yml b/changelogs/unreleased/62685-add-index-invite-email-to-members.yml new file mode 100644 index 00000000000..80bb05e1709 --- /dev/null +++ b/changelogs/unreleased/62685-add-index-invite-email-to-members.yml @@ -0,0 +1,5 @@ +--- +title: Add index on invite_email for members +merge_request: 29768 +author: +type: performance diff --git a/changelogs/unreleased/63079-exclude-k8s-namespaces-with-no-service-account-token.yml b/changelogs/unreleased/63079-exclude-k8s-namespaces-with-no-service-account-token.yml new file mode 100644 index 00000000000..9dc99c8a62f --- /dev/null +++ b/changelogs/unreleased/63079-exclude-k8s-namespaces-with-no-service-account-token.yml @@ -0,0 +1,6 @@ +--- +title: Ensure a Kubernetes namespace is not used for deployments if there is no service + account token associated with it +merge_request: 29643 +author: +type: fixed diff --git a/changelogs/unreleased/63261-the-graphql-query-for-the-mr-popover-failes-on-the-frontend.yml b/changelogs/unreleased/63261-the-graphql-query-for-the-mr-popover-failes-on-the-frontend.yml new file mode 100644 index 00000000000..87d74e3f4b4 --- /dev/null +++ b/changelogs/unreleased/63261-the-graphql-query-for-the-mr-popover-failes-on-the-frontend.yml @@ -0,0 +1,6 @@ +--- +title: Make sure we are receiving the proper information on the MR Popover by updating + the IID in the graphql query +merge_request: +author: +type: fixed diff --git a/changelogs/unreleased/hashed-storage-enabled-default.yml b/changelogs/unreleased/hashed-storage-enabled-default.yml new file mode 100644 index 00000000000..9a34505785c --- /dev/null +++ b/changelogs/unreleased/hashed-storage-enabled-default.yml @@ -0,0 +1,5 @@ +--- +title: Hashed Storage is enabled by default on new installations +merge_request: 29586 +author: +type: changed diff --git a/changelogs/unreleased/migrate_k8s_service_integration.yml b/changelogs/unreleased/migrate_k8s_service_integration.yml new file mode 100644 index 00000000000..57f03e6bdab --- /dev/null +++ b/changelogs/unreleased/migrate_k8s_service_integration.yml @@ -0,0 +1,5 @@ +--- +title: Migrate Kubernetes service integration templates to clusters +merge_request: 28534 +author: +type: added diff --git a/changelogs/unreleased/readonly_k8s_integration.yml b/changelogs/unreleased/readonly_k8s_integration.yml new file mode 100644 index 00000000000..718705e8750 --- /dev/null +++ b/changelogs/unreleased/readonly_k8s_integration.yml @@ -0,0 +1,5 @@ +--- +title: Make Kubernetes service templates readonly +merge_request: 29044 +author: +type: removed diff --git a/changelogs/unreleased/visual-review-apps-fix-dropdown.yml b/changelogs/unreleased/visual-review-apps-fix-dropdown.yml new file mode 100644 index 00000000000..ade68c26df9 --- /dev/null +++ b/changelogs/unreleased/visual-review-apps-fix-dropdown.yml @@ -0,0 +1,5 @@ +--- +title: Move Dropdown to Stick to MR View App Button +merge_request: 29767 +author: +type: fixed diff --git a/config/application.rb b/config/application.rb index c4831b1e638..c5ef6a2c60d 100644 --- a/config/application.rb +++ b/config/application.rb @@ -47,6 +47,19 @@ module Gitlab config.generators.templates.push("#{config.root}/generator_templates") + ee_paths = config.eager_load_paths.each_with_object([]) do |path, memo| + ee_path = config.root.join('ee', Pathname.new(path).relative_path_from(config.root)) + memo << ee_path.to_s if ee_path.exist? + end + + # Eager load should load CE first + config.eager_load_paths.push(*ee_paths) + config.helpers_paths.push "#{config.root}/ee/app/helpers" + + # Other than Ruby modules we load EE first + config.paths['lib/tasks'].unshift "#{config.root}/ee/lib/tasks" + config.paths['app/views'].unshift "#{config.root}/ee/app/views" + # Rake tasks ignore the eager loading settings, so we need to set the # autoload paths explicitly config.autoload_paths = config.eager_load_paths.dup @@ -162,6 +175,23 @@ module Gitlab config.assets.paths << "#{config.root}/node_modules/xterm/src/" config.assets.precompile << "xterm.css" + %w[images javascripts stylesheets].each do |path| + config.assets.paths << "#{config.root}/ee/app/assets/#{path}" + config.assets.precompile << "jira_connect.js" + config.assets.precompile << "pages/jira_connect.css" + end + + config.assets.paths << "#{config.root}/vendor/assets/javascripts/" + config.assets.precompile << "snowplow/sp.js" + + # Compile non-JS/CSS assets in the ee/app/assets folder by default + # Mimic sprockets-rails default: https://github.com/rails/sprockets-rails/blob/v3.2.1/lib/sprockets/railtie.rb#L84-L87 + LOOSE_EE_APP_ASSETS = lambda do |logical_path, filename| + filename.start_with?(config.root.join("ee/app/assets").to_s) && + !['.js', '.css', ''].include?(File.extname(logical_path)) + end + config.assets.precompile << LOOSE_EE_APP_ASSETS + # Version of your assets, change this if you want to expire all your assets config.assets.version = '1.0' diff --git a/db/migrate/20190527011309_add_required_template_name_to_application_settings.rb b/db/migrate/20190527011309_add_required_template_name_to_application_settings.rb new file mode 100644 index 00000000000..6cbac0ed507 --- /dev/null +++ b/db/migrate/20190527011309_add_required_template_name_to_application_settings.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +# See http://doc.gitlab.com/ce/development/migration_style_guide.html +# for more information on how to write migrations for GitLab. + +class AddRequiredTemplateNameToApplicationSettings < ActiveRecord::Migration[5.1] + include Gitlab::Database::MigrationHelpers + + # Set this constant to true if this migration requires downtime. + DOWNTIME = false + + def change + add_column :application_settings, :required_instance_ci_template, :string, null: true + end +end diff --git a/db/migrate/20190610142825_add_index_to_members_invite_email.rb b/db/migrate/20190610142825_add_index_to_members_invite_email.rb new file mode 100644 index 00000000000..58157cc5313 --- /dev/null +++ b/db/migrate/20190610142825_add_index_to_members_invite_email.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +# See http://doc.gitlab.com/ce/development/migration_style_guide.html +# for more information on how to write migrations for GitLab. + +class AddIndexToMembersInviteEmail < ActiveRecord::Migration[5.1] + include Gitlab::Database::MigrationHelpers + + # Set this constant to true if this migration requires downtime. + DOWNTIME = false + + disable_ddl_transaction! + + def up + add_concurrent_index :members, [:invite_email] + end + + def down + remove_concurrent_index :members, [:invite_email] + end +end diff --git a/db/migrate/20190613030606_enable_hashed_storage_by_default.rb b/db/migrate/20190613030606_enable_hashed_storage_by_default.rb new file mode 100644 index 00000000000..8edefd1273e --- /dev/null +++ b/db/migrate/20190613030606_enable_hashed_storage_by_default.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +class EnableHashedStorageByDefault < ActiveRecord::Migration[5.1] + include Gitlab::Database::MigrationHelpers + + DOWNTIME = false + + def up + change_column_default :application_settings, :hashed_storage_enabled, true + end + + def down + change_column_default :application_settings, :hashed_storage_enabled, false + end +end diff --git a/db/post_migrate/20190517153211_migrate_k8s_service_integration.rb b/db/post_migrate/20190517153211_migrate_k8s_service_integration.rb new file mode 100644 index 00000000000..4bd04edb239 --- /dev/null +++ b/db/post_migrate/20190517153211_migrate_k8s_service_integration.rb @@ -0,0 +1,104 @@ +# frozen_string_literal: true + +class MigrateK8sServiceIntegration < ActiveRecord::Migration[5.1] + include Gitlab::Database::MigrationHelpers + + DOWNTIME = false + + class Cluster < ActiveRecord::Base + self.table_name = 'clusters' + + has_one :platform_kubernetes, class_name: 'MigrateK8sServiceIntegration::PlatformsKubernetes' + + accepts_nested_attributes_for :platform_kubernetes + + enum cluster_type: { + instance_type: 1, + group_type: 2, + project_type: 3 + } + + enum platform_type: { + kubernetes: 1 + } + + enum provider_type: { + user: 0, + gcp: 1 + } + end + + class PlatformsKubernetes < ActiveRecord::Base + self.table_name = 'cluster_platforms_kubernetes' + + belongs_to :cluster, class_name: 'MigrateK8sServiceIntegration::Cluster' + + attr_encrypted :token, + mode: :per_attribute_iv, + key: Settings.attr_encrypted_db_key_base_truncated, + algorithm: 'aes-256-cbc' + end + + class Service < ActiveRecord::Base + include EachBatch + + self.table_name = 'services' + self.inheritance_column = :_type_disabled # Disable STI, otherwise KubernetesModel will be looked up + + belongs_to :project, class_name: 'MigrateK8sServiceIntegration::Project', foreign_key: :project_id + + scope :kubernetes_service_templates, -> do + where(category: 'deployment', type: 'KubernetesService', template: true) + end + + def api_url + parsed_properties['api_url'].presence + end + + def ca_pem + parsed_properties['ca_pem'] + end + + def namespace + parsed_properties['namespace'].presence + end + + def token + parsed_properties['token'].presence + end + + private + + def parsed_properties + @parsed_properties ||= JSON.parse(self.properties) + end + end + + def up + has_instance_cluster = Cluster.instance_type.where(enabled: true).exists? + + MigrateK8sServiceIntegration::Service.kubernetes_service_templates.find_each do |service| + next unless service.api_url && service.token + + MigrateK8sServiceIntegration::Cluster.create!( + enabled: !has_instance_cluster && service.active, + managed: false, + name: 'KubernetesService', + cluster_type: 'instance_type', + provider_type: 'user', + platform_type: 'kubernetes', + platform_kubernetes_attributes: { + api_url: service.api_url, + ca_cert: service.ca_pem, + namespace: service.namespace, + token: service.token + } + ) + end + end + + def down + # It is not possible to tell which instance-level clusters were created by + # this migration. The original data is intentionally left intact. + end +end diff --git a/db/post_migrate/20190611161642_add_index_to_events_and_audit_events_created_at_author_id.rb b/db/post_migrate/20190611161642_add_index_to_events_and_audit_events_created_at_author_id.rb new file mode 100644 index 00000000000..342e83d6322 --- /dev/null +++ b/db/post_migrate/20190611161642_add_index_to_events_and_audit_events_created_at_author_id.rb @@ -0,0 +1,25 @@ +# frozen_string_literal: true + +# See http://doc.gitlab.com/ce/development/migration_style_guide.html +# for more information on how to write migrations for GitLab. + +class AddIndexToEventsAndAuditEventsCreatedAtAuthorId < ActiveRecord::Migration[5.1] + include Gitlab::Database::MigrationHelpers + + DOWNTIME = false + INDEX_NAME = 'analytics_index_%s_on_created_at_and_author_id'.freeze + EVENTS_INDEX_NAME = (INDEX_NAME % 'events').freeze + AUDIT_EVENTS_INDEX_NAME = (INDEX_NAME % 'audit_events').freeze + + disable_ddl_transaction! + + def up + add_concurrent_index :events, [:created_at, :author_id], name: EVENTS_INDEX_NAME + add_concurrent_index :audit_events, [:created_at, :author_id], name: AUDIT_EVENTS_INDEX_NAME + end + + def down + remove_concurrent_index_by_name :events, EVENTS_INDEX_NAME + remove_concurrent_index_by_name :audit_events, AUDIT_EVENTS_INDEX_NAME + end +end diff --git a/db/schema.rb b/db/schema.rb index a50ab02f718..20e0050ac61 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20190611161641) do +ActiveRecord::Schema.define(version: 20190613030606) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -141,7 +141,7 @@ ActiveRecord::Schema.define(version: 20190611161641) do t.boolean "help_page_hide_commercial_content", default: false t.string "help_page_support_url" t.integer "performance_bar_allowed_group_id" - t.boolean "hashed_storage_enabled", default: false, null: false + t.boolean "hashed_storage_enabled", default: true, null: false t.boolean "project_export_enabled", default: true, null: false t.boolean "auto_devops_enabled", default: true, null: false t.boolean "throttle_unauthenticated_enabled", default: false, null: false @@ -194,6 +194,7 @@ ActiveRecord::Schema.define(version: 20190611161641) do t.integer "elasticsearch_replicas", default: 1, null: false t.text "encrypted_lets_encrypt_private_key" t.text "encrypted_lets_encrypt_private_key_iv" + t.string "required_instance_ci_template" t.boolean "dns_rebinding_protection_enabled", default: true, null: false t.boolean "default_project_deletion_protection", default: false, null: false t.boolean "lock_memberships_to_ldap", default: false, null: false @@ -335,6 +336,7 @@ ActiveRecord::Schema.define(version: 20190611161641) do t.text "details" t.datetime "created_at" t.datetime "updated_at" + t.index ["created_at", "author_id"], name: "analytics_index_audit_events_on_created_at_and_author_id", using: :btree t.index ["entity_id", "entity_type"], name: "index_audit_events_on_entity_id_and_entity_type", using: :btree end @@ -1219,6 +1221,7 @@ ActiveRecord::Schema.define(version: 20190611161641) do t.string "target_type" t.index ["action"], name: "index_events_on_action", using: :btree t.index ["author_id", "project_id"], name: "index_events_on_author_id_and_project_id", using: :btree + t.index ["created_at", "author_id"], name: "analytics_index_events_on_created_at_and_author_id", using: :btree t.index ["project_id", "created_at"], name: "index_events_on_project_id_and_created_at", using: :btree t.index ["project_id", "id"], name: "index_events_on_project_id_and_id", using: :btree t.index ["target_type", "target_id"], name: "index_events_on_target_type_and_target_id", using: :btree @@ -1848,6 +1851,7 @@ ActiveRecord::Schema.define(version: 20190611161641) do t.boolean "ldap", default: false, null: false t.boolean "override", default: false, null: false t.index ["access_level"], name: "index_members_on_access_level", using: :btree + t.index ["invite_email"], name: "index_members_on_invite_email", using: :btree t.index ["invite_token"], name: "index_members_on_invite_token", unique: true, using: :btree t.index ["requested_at"], name: "index_members_on_requested_at", using: :btree t.index ["source_id", "source_type"], name: "index_members_on_source_id_and_source_type", using: :btree diff --git a/doc/administration/repository_storage_types.md b/doc/administration/repository_storage_types.md index 38842693d73..834b41b3a2c 100644 --- a/doc/administration/repository_storage_types.md +++ b/doc/administration/repository_storage_types.md @@ -47,18 +47,12 @@ Any change in the URL will need to be reflected on disk (when groups / users or projects are renamed). This can add a lot of load in big installations, especially if using any type of network based filesystem. -CAUTION: **Caution:** -For Geo in particular: Geo does work with legacy storage, but in some -edge cases due to race conditions it can lead to errors when a project is -renamed multiple times in short succession, or a project is deleted and -recreated under the same name very quickly. We expect these race events to be -rare, and we have not observed a race condition side-effect happening yet. -This pattern also exists in other objects stored in GitLab, like issue -Attachments, GitLab Pages artifacts, Docker Containers for the integrated -Registry, etc. Hashed storage is a requirement for Geo. - ## Hashed Storage +CAUTION: **Important:** +Geo requires Hashed Storage since 12.0. If you haven't migrated yet, +check the [migration instructions](#how-to-migrate-to-hashed-storage) ASAP. + Hashed Storage is the new storage behavior we rolled out with 10.0. Instead of coupling project URL and the folder structure where the repository will be stored on disk, we are coupling a hash, based on the project's ID. This makes diff --git a/doc/api/graphql/index.md b/doc/api/graphql/index.md index 88e657a5d2f..6c1cce620ca 100644 --- a/doc/api/graphql/index.md +++ b/doc/api/graphql/index.md @@ -46,7 +46,7 @@ curl --data "value=100" --header "PRIVATE-TOKEN: <your_access_token>" https://gi A first iteration of a GraphQL API includes the following queries 1. `project` : Within a project it is also possible to fetch a `mergeRequest` by IID. -1. `group` : Only basic group information is currently supported. +1. `group` : Basic group information and epics **[ULTIMATE]** are currently supported. 1. `namespace` : Within a namespace it is also possible to fetch `projects`. ### Multiplex queries diff --git a/doc/api/services.md b/doc/api/services.md index 898cfad7254..f38f96f64ad 100644 --- a/doc/api/services.md +++ b/doc/api/services.md @@ -22,6 +22,7 @@ Parameters: | --------- | ---- | -------- | ----------- | | `api_key` | string | true | User API token. User must have access to task, all comments will be attributed to this user. | | `restrict_to_branch` | string | false | Comma-separated list of branches which will be automatically inspected. Leave blank to include all branches. | +| `push_events` | boolean | false | Enable notifications for push events | ### Delete Asana service @@ -57,6 +58,7 @@ Parameters: | --------- | ---- | -------- | ----------- | | `token` | string | true | The authentication token | `subdomain` | string | false | The subdomain setting | +| `push_events` | boolean | false | Enable notifications for push events | ### Delete Assembla service @@ -96,6 +98,7 @@ Parameters: | `build_key` | string | true | Bamboo build plan key like KEY | | `username` | string | true | A user with API access, if applicable | | `password` | string | true | Password of the user | +| `push_events` | boolean | false | Enable notifications for push events | ### Delete Atlassian Bamboo CI service @@ -134,6 +137,7 @@ Parameters: | `project_url` | string | true | Project url | | `description` | string | false | Description | | `title` | string | false | Title | +| `push_events` | boolean | false | Enable notifications for push events | ### Delete Bugzilla Service @@ -170,6 +174,7 @@ Parameters: | `token` | string | true | Buildkite project GitLab token | | `project_url` | string | true | `https://buildkite.com/example/project` | | `enable_ssl_verification` | boolean | false | Enable SSL verification | +| `push_events` | boolean | false | Enable notifications for push events | ### Delete Buildkite service @@ -206,6 +211,7 @@ Parameters: | `token` | string | true | Campfire token | | `subdomain` | string | false | Campfire subdomain | | `room` | string | false | Campfire room | +| `push_events` | boolean | false | Enable notifications for push events | ### Delete Campfire service @@ -244,6 +250,7 @@ Parameters: | `project_url` | string | true | Project url | `description` | string | false | Description | `title` | string | false | Title +| `push_events` | boolean | false | Enable notifications for push events | ### Delete Custom Issue Tracker service @@ -280,6 +287,10 @@ Parameters: | `token` | string | true | Drone CI project specific token | | `drone_url` | string | true | `http://drone.example.com` | | `enable_ssl_verification` | boolean | false | Enable SSL verification | +| `push_events` | boolean | false | Enable notifications for push events | +| `merge_requests_events` | boolean | false | Enable notifications for merge request events | +| `tag_push_events` | boolean | false | Enable notifications for tag push events | + ### Delete Drone CI service @@ -316,6 +327,8 @@ Parameters: | `recipients` | string | true | Emails separated by whitespace | | `disable_diffs` | boolean | false | Disable code diffs | | `send_from_committer_email` | boolean | false | Send from committer | +| `push_events` | boolean | false | Enable notifications for push events | +| `tag_push_events` | boolean | false | Enable notifications for tag push events | ### Delete Emails on push service @@ -384,6 +397,7 @@ Parameters: | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | | `token` | string | true | Flowdock Git source token | +| `push_events` | boolean | false | Enable notifications for push events | ### Delete Flowdock service @@ -471,6 +485,14 @@ Parameters: | `room` | string | false |Room name or ID | | `api_version` | string | false | Leave blank for default (v2) | | `server` | string | false | Leave blank for default. For example, `https://hipchat.example.com`. | +| `push_events` | boolean | false | Enable notifications for push events | +| `issues_events` | boolean | false | Enable notifications for issue events | +| `confidential_issues_events` | boolean | false | Enable notifications for confidential issue events | +| `merge_requests_events` | boolean | false | Enable notifications for merge request events | +| `tag_push_events` | boolean | false | Enable notifications for tag push events | +| `note_events` | boolean | false | Enable notifications for note events | +| `confidental_note_events` | boolean | false | Enable notifications for confidential note events | +| `pipeline_events` | boolean | false | Enable notifications for pipeline events | ### Delete HipChat service @@ -511,6 +533,7 @@ Parameters: | `server_host` | string | false | localhost | | `server_port` | integer | false | 6659 | | `colorize_messages` | boolean | false | Colorize messages | +| `push_events` | boolean | false | Enable notifications for push events | ### Delete Irker (IRC gateway) service @@ -562,6 +585,8 @@ Parameters: | `password` | string | yes | The password of the user created to be used with GitLab/JIRA. | | `active` | boolean | no | Activates or deactivates the service. Defaults to false (deactivated). | | `jira_issue_transition_id` | string | no | The ID of a transition that moves issues to a closed state. You can find this number under the JIRA workflow administration (**Administration > Issues > Workflows**) by selecting **View** under **Operations** of the desired workflow of your project. The ID of each state can be found inside the parenthesis of each transition name under the **Transitions (id)** column ([see screenshot][trans]). By default, this ID is set to `2`. | +| `commit_events` | boolean | false | Enable notifications for commit events | +| `merge_requests_events` | boolean | false | Enable notifications for merge request events | ### Delete JIRA service @@ -715,9 +740,14 @@ PUT /projects/:id/services/packagist Parameters: -- `username` (**required**) -- `token` (**required**) -- `server` (optional) +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `username` | string | yes | The username of a Packagist account | +| `token` | string | yes | API token to the Packagist server | +| `server` | boolean | no | URL of the Packagist server. Leave blank for default: https://packagist.org | +| `push_events` | boolean | false | Enable notifications for push events | +| `merge_requests_events` | boolean | false | Enable notifications for merge request events | +| `tag_push_events` | boolean | false | Enable notifications for tag push events | ### Delete Packagist service @@ -755,6 +785,7 @@ Parameters: | `add_pusher` | boolean | no | Add pusher to recipients list | | `notify_only_broken_pipelines` | boolean | no | Notify only broken pipelines | | `notify_only_default_branch` | boolean | no | Send notifications only for the default branch ([introduced in GitLab 12.0](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/28271)) | +| `pipeline_events` | boolean | false | Enable notifications for pipeline events | ### Delete Pipeline-Emails service @@ -790,6 +821,7 @@ Parameters: | --------- | ---- | -------- | ----------- | | `token` | string | true | The PivotalTracker token | | `restrict_to_branch` | boolean | false | Comma-separated list of branches which will be automatically inspected. Leave blank to include all branches. | +| `push_events` | boolean | false | Enable notifications for push events | ### Delete PivotalTracker service @@ -862,6 +894,7 @@ Parameters: | `priority` | string | true | The priority | | `device` | string | false | Leave blank for all active devices | | `sound` | string | false | The sound of the notification | +| `push_events` | boolean | false | Enable notifications for push events | ### Delete Pushover service @@ -899,6 +932,7 @@ Parameters: | `project_url` | string | true | Project url | | `issues_url` | string | true | Issue url | | `description` | string | false | Description | +| `push_events` | boolean | false | Enable notifications for push events | ### Delete Redmine service @@ -989,6 +1023,15 @@ Parameters: | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | | `webhook` | string | true | The Microsoft Teams webhook. For example, `https://outlook.office.com/webhook/...` | +| `push_events` | boolean | false | Enable notifications for push events | +| `issues_events` | boolean | false | Enable notifications for issue events | +| `confidential_issues_events` | boolean | false | Enable notifications for confidential issue events | +| `merge_requests_events` | boolean | false | Enable notifications for merge request events | +| `tag_push_events` | boolean | false | Enable notifications for tag push events | +| `note_events` | boolean | false | Enable notifications for note events | +| `confidental_note_events` | boolean | false | Enable notifications for confidential note events | +| `pipeline_events` | boolean | false | Enable notifications for pipeline events | +| `wiki_page_events` | boolean | false | Enable notifications for wiki page events | ### Delete Microsoft Teams service @@ -1084,6 +1127,7 @@ Parameters: | `build_type` | string | true | Build configuration ID | | `username` | string | true | A user with permissions to trigger a manual build | | `password` | string | true | The password of the user | +| `push_events` | boolean | false | Enable notifications for push events | ### Delete JetBrains TeamCity CI service @@ -1230,6 +1274,7 @@ Parameters: | `issues_url` | string | true | Issue url | | `project_url` | string | true | Project url | | `description` | string | false | Description | +| `push_events` | boolean | false | Enable notifications for push events | ### Delete YouTrack Service diff --git a/doc/ci/merge_request_pipelines/index.md b/doc/ci/merge_request_pipelines/index.md index d8c1cefc8df..5adb7ebd30d 100644 --- a/doc/ci/merge_request_pipelines/index.md +++ b/doc/ci/merge_request_pipelines/index.md @@ -124,7 +124,7 @@ otherwise pipelines for merged results won't run and your merge requests will be ## Merge Trains **[PREMIUM]** > [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/issues/9186) in [GitLab Premium](https://about.gitlab.com/pricing/) 12.0. -> This feature is disabled by default until we resolve issues with [contention handling](https://gitlab.com/gitlab-org/gitlab-ee/issues/11222), but [can be enabled manually](#enabling-merge-trains). +> This feature is disabled by default, but [can be enabled manually](#enabling-merge-trains). [Pipelines for merged results](#pipelines-for-merged-results-premium) introduces running a build on the result of the merged code prior to merging, as a way to keep master green. @@ -142,9 +142,8 @@ If the button is subsequently pressed in a different MR, instead of creating a n it creates a new pipeline targeting the merge result of the previous MR plus the target branch. Pipelines invalidated through failures are immediately canceled and requeued. -CAUTION: **Warning:** -At this moment, each merge train can generate a merge ref and run a pipeline **one at a time** due to a technical challenge. -We have [an issue](https://gitlab.com/gitlab-org/gitlab-ee/issues/11222) to tackle this problem to make the pipelines for merged results run in parallel. +CAUTION: **Caution:** +At the moment, each merge train can generate a merge ref and run a pipeline **one at a time**. We plan to make the pipelines for merged results [run in parallel](https://gitlab.com/gitlab-org/gitlab-ee/issues/11222) in a future release. ### Enabling Merge Trains @@ -186,11 +185,6 @@ button while the latest pipeline is running. ![Add to merge train when pipeline succeeds](img/merge_train_start_when_pipeline_succeeds.png) -### Merge Train's limitations - -- At this moment, each merge train can generate a merge ref and run a pipeline **one at a time** due to a technical challenge. - We have [an issue](https://gitlab.com/gitlab-org/gitlab-ee/issues/11222) to tackle this problem to make the pipelines for merged results run in parallel. - ## Excluding certain jobs The behavior of the `only: merge_requests` parameter is such that _only_ jobs with diff --git a/doc/ci/yaml/README.md b/doc/ci/yaml/README.md index 4c170056a49..2759f1c5160 100644 --- a/doc/ci/yaml/README.md +++ b/doc/ci/yaml/README.md @@ -2620,6 +2620,24 @@ test: - pwd ``` +### Nested paths + +The value of `GIT_CLONE_PATH` is expanded once and nesting variables +within it is not supported. + +For example, you define both the variables below in your +`.gitlab-ci.yml` file: + +```yml +variables: + GOPATH: $CI_BUILDS_DIR/go + GIT_CLONE_PATH: $GOPATH/src/namespace/project +``` + +The value of `GIT_CLONE_PATH` is expanded once into +`$CI_BUILDS_DIR/go/src/namespace/project`, and results in failure +because `$CI_BUILDS_DIR` is not expanded. + ## Special YAML features It's possible to use special YAML features like anchors (`&`), aliases (`*`) diff --git a/doc/integration/elasticsearch.md b/doc/integration/elasticsearch.md index 6064c417900..a2f38a2fcdf 100644 --- a/doc/integration/elasticsearch.md +++ b/doc/integration/elasticsearch.md @@ -275,19 +275,18 @@ You can also use the `gitlab:elastic:clear_index_status` Rake task to force the indexer to "forget" all progress, so retrying the indexing process from the start. -To index all wikis: +The `index_projects` command enqueues jobs to index all project and wiki +repositories, and most database content. However, snippets still need to be +indexed separately. To do so, run one of these commands: ```sh # Omnibus installations -sudo gitlab-rake gitlab:elastic:index_wikis +sudo gitlab-rake gitlab:elastic:index_snippets # Installations from source -bundle exec rake gitlab:elastic:index_wikis RAILS_ENV=production +bundle exec rake gitlab:elastic:index_snippets RAILS_ENV=production ``` -The wiki indexer also supports the `ID_FROM` and `ID_TO` parameters if you want -to limit a project set. - Enable replication and refreshing again after indexing (only if you previously disabled it): ```bash @@ -335,14 +334,11 @@ There are several rake tasks available to you via the command line: - `sudo gitlab-rake gitlab:elastic:create_empty_index` - `sudo gitlab-rake gitlab:elastic:clear_index_status` - `sudo gitlab-rake gitlab:elastic:index_projects` - - `sudo gitlab-rake gitlab:elastic:index_wikis` - `sudo gitlab-rake gitlab:elastic:index_snippets` - [sudo gitlab-rake gitlab:elastic:index_projects](https://gitlab.com/gitlab-org/gitlab-ee/blob/master/ee/lib/tasks/gitlab/elastic.rake) - This iterates over all projects and queues sidekiq jobs to index them in the background. - [sudo gitlab-rake gitlab:elastic:index_projects_status](https://gitlab.com/gitlab-org/gitlab-ee/blob/master/ee/lib/tasks/gitlab/elastic.rake) - This determines the overall status of the indexing. It is done by counting the total number of indexed projects, dividing by a count of the total number of projects, then multiplying by 100. -- [sudo gitlab-rake gitlab:elastic:index_wikis](https://gitlab.com/gitlab-org/gitlab-ee/blob/master/ee/lib/tasks/gitlab/elastic.rake) - - Iterates over every project, determines if said project contains wiki data, and then indexes the blobs (content) of said wiki data. - [sudo gitlab-rake gitlab:elastic:create_empty_index](https://gitlab.com/gitlab-org/gitlab-ee/blob/master/ee/lib/tasks/gitlab/elastic.rake) - This generates an empty index on the Elasticsearch side. - [sudo gitlab-rake gitlab:elastic:clear_index_status](https://gitlab.com/gitlab-org/gitlab-ee/blob/master/ee/lib/tasks/gitlab/elastic.rake) diff --git a/doc/user/group/clusters/index.md b/doc/user/group/clusters/index.md index 3c5e820c1ca..26d764fa2cf 100644 --- a/doc/user/group/clusters/index.md +++ b/doc/user/group/clusters/index.md @@ -57,10 +57,6 @@ differentiate the new cluster from the rest. > [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/22011) in GitLab 11.5. > Became [optional](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/26565) in GitLab 11.11. -NOTE: **Note:** -Only available when creating clusters. Existing clusters not managed by GitLab -cannot become GitLab-managed later. - You can choose to allow GitLab to manage your cluster for you. If your cluster is managed by GitLab, resources for your projects will be automatically created. See the [Access controls](../../project/clusters/index.md#access-controls) section for details on which resources will diff --git a/doc/user/project/clusters/index.md b/doc/user/project/clusters/index.md index dc21db603d6..a4d4fb91f71 100644 --- a/doc/user/project/clusters/index.md +++ b/doc/user/project/clusters/index.md @@ -71,7 +71,6 @@ new Kubernetes cluster to your project: - **Number of nodes** - Enter the number of nodes you wish the cluster to have. - **Machine type** - The [machine type](https://cloud.google.com/compute/docs/machine-types) of the Virtual Machine instance that the cluster will be based on. - - **RBAC-enabled cluster** - Leave this checked if using default GKE creation options, see the [RBAC section](#rbac-cluster-resources) for more information. - **GitLab-managed cluster** - Leave this checked if you want GitLab to manage namespaces and service accounts for this cluster. See the [Managed clusters section](#gitlab-managed-clusters) for more information. 1. Finally, click the **Create Kubernetes cluster** button. @@ -86,6 +85,9 @@ account](#access-controls). Starting from [GitLab creation process will explicitly request that basic authentication and client certificate is enabled. +NOTE: **Note:** +Starting from [GitLab 12.1](https://gitlab.com/gitlab-org/gitlab-ce/issues/55902), all GKE clusters created by GitLab are RBAC enabled. Take a look at the [RBAC section](#rbac-cluster-resources) for more information. + ## Adding an existing Kubernetes cluster To add an existing Kubernetes cluster to your project: @@ -225,10 +227,6 @@ applications running on the cluster. > [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/22011) in GitLab 11.5. > Became [optional](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/26565) in GitLab 11.11. -NOTE: **Note:** -Only available when creating clusters. Existing clusters not managed by GitLab -cannot become GitLab-managed later. - You can choose to allow GitLab to manage your cluster for you. If your cluster is managed by GitLab, resources for your projects will be automatically created. See the [Access controls](#access-controls) section for details on which resources will diff --git a/doc/user/project/repository/reducing_the_repo_size_using_git.md b/doc/user/project/repository/reducing_the_repo_size_using_git.md index 35ceb76dc0d..7765a3d7438 100644 --- a/doc/user/project/repository/reducing_the_repo_size_using_git.md +++ b/doc/user/project/repository/reducing_the_repo_size_using_git.md @@ -35,16 +35,16 @@ BFG Repo-Cleaner](#using-the-bfg-repo-cleaner). It's faster and simpler than `git filter-branch`, and GitLab can use its account of what has changed to clean up its own internal state, maximizing the space saved. -> **Warning:** -> Make sure to first make a copy of your repository since rewriting history will -> purge the files and information you are about to delete. Also make sure to -> inform any collaborators to not use `pull` after your changes, but use `rebase`. +CAUTION: **Caution:** +Make sure to first make a copy of your repository since rewriting history will +purge the files and information you are about to delete. Also make sure to +inform any collaborators to not use `pull` after your changes, but use `rebase`. -> **Warning:** -> This process is not suitable for removing sensitive data like password or keys -> from your repository. Information about commits, including file content, is -> cached in the database, and will remain visible even after they have been -> removed from the repository. +CAUTION: **Caution:** +This process is not suitable for removing sensitive data like password or keys +from your repository. Information about commits, including file content, is +cached in the database, and will remain visible even after they have been +removed from the repository. ## Using the BFG Repo-Cleaner @@ -99,10 +99,11 @@ up its own internal state, maximizing the space saved. `git gc` against the repository. You will receive an email once it has completed. +NOTE: **Note:** This process will remove some copies of the rewritten commits from GitLab's cache and database, but there are still numerous gaps in coverage - at present, -some of the copies may persist indefinitely. [Clearing the instance cache](../../../administration/raketasks/maintenance.md#clear-redis-cache) -may help to remove some of them, but it should not be depended on for security +some of the copies may persist indefinitely. [Clearing the instance cache](../../../administration/raketasks/maintenance.md#clear-redis-cache) +may help to remove some of them, but it should not be depended on for security purposes! ## Using `git filter-branch` diff --git a/doc/workflow/todos.md b/doc/workflow/todos.md index 32907db4f46..3eac79427cf 100644 --- a/doc/workflow/todos.md +++ b/doc/workflow/todos.md @@ -34,6 +34,8 @@ A Todo appears in your Todos dashboard when: - the author, or - have set it to automatically merge once pipeline succeeds. +Todo triggers are not affected by [GitLab Notification Email settings](notifications.md). + NOTE: **Note:** When an user no longer has access to a resource related to a Todo like an issue, merge request, project or group the related Todos, for security reasons, gets deleted within the next hour. The delete is delayed to prevent data loss in case user got their access revoked by mistake. diff --git a/lib/api/entities.rb b/lib/api/entities.rb index 8840accf675..25e9fdd5fce 100644 --- a/lib/api/entities.rb +++ b/lib/api/entities.rb @@ -997,7 +997,7 @@ module API class ProjectService < Grape::Entity expose :id, :title, :created_at, :updated_at, :active - expose :push_events, :issues_events, :confidential_issues_events + expose :commit_events, :push_events, :issues_events, :confidential_issues_events expose :merge_requests_events, :tag_push_events, :note_events expose :confidential_note_events, :pipeline_events, :wiki_page_events expose :job_events diff --git a/lib/gitlab/ci/build/prerequisite/kubernetes_namespace.rb b/lib/gitlab/ci/build/prerequisite/kubernetes_namespace.rb index dbdc59505ac..531c9ce4256 100644 --- a/lib/gitlab/ci/build/prerequisite/kubernetes_namespace.rb +++ b/lib/gitlab/ci/build/prerequisite/kubernetes_namespace.rb @@ -9,7 +9,7 @@ module Gitlab deployment_cluster.present? && deployment_cluster.managed? && !deployment_cluster.project_type? && - kubernetes_namespace.new_record? + (kubernetes_namespace.new_record? || kubernetes_namespace.service_account_token.blank?) end def complete! diff --git a/lib/gitlab/ci/config.rb b/lib/gitlab/ci/config.rb index f187e456993..a0275515906 100644 --- a/lib/gitlab/ci/config.rb +++ b/lib/gitlab/ci/config.rb @@ -8,6 +8,12 @@ module Gitlab class Config ConfigError = Class.new(StandardError) + RESCUE_ERRORS = [ + Gitlab::Config::Loader::FormatError, + Extendable::ExtensionError, + External::Processor::IncludeError + ].freeze + def initialize(config, project: nil, sha: nil, user: nil) @config = Config::Extendable .new(build_config(config, project: project, sha: sha, user: user)) @@ -15,9 +21,7 @@ module Gitlab @global = Entry::Global.new(@config) @global.compose! - rescue Gitlab::Config::Loader::FormatError, - Extendable::ExtensionError, - External::Processor::IncludeError => e + rescue *rescue_errors => e raise Config::ConfigError, e.message end @@ -83,6 +87,11 @@ module Gitlab user: user, expandset: Set.new).perform end + + # Overriden in EE + def rescue_errors + RESCUE_ERRORS + end end end end diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 186c4cb2dba..0cabaeabb9a 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -9999,6 +9999,9 @@ msgstr "" msgid "The import will time out after %{timeout}. For repositories that take longer, use a clone/push combination." msgstr "" +msgid "The instance-level Kubernetes service integration is deprecated. Your data has been migrated to an <a href=\"%{url}\"/>instance-level cluster</a>." +msgstr "" + msgid "The invitation could not be accepted." msgstr "" @@ -11943,9 +11946,6 @@ msgstr "" msgid "Your Groups" msgstr "" -msgid "Your Kubernetes cluster information on this page is still editable, but you are advised to disable and reconfigure" -msgstr "" - msgid "Your Primary Email will be used for avatar detection." msgstr "" diff --git a/qa/Rakefile b/qa/Rakefile index 7ac018f7286..7ba8a6d68ba 100644 --- a/qa/Rakefile +++ b/qa/Rakefile @@ -13,8 +13,9 @@ task :delete_subgroups do end desc "Generate Performance Testdata" -task :generate_perf_testdata do - QA::Tools::GeneratePerfTestdata.new.run +task :generate_perf_testdata, :type do |t, args| + args.with_defaults(type: :all) + QA::Tools::GeneratePerfTestdata.new.method(args[:type]).call end desc "Run artillery load tests" diff --git a/qa/qa/page/dashboard/projects.rb b/qa/qa/page/dashboard/projects.rb index 7ab8ee39f72..0c23d7cffbb 100644 --- a/qa/qa/page/dashboard/projects.rb +++ b/qa/qa/page/dashboard/projects.rb @@ -5,7 +5,7 @@ module QA module Dashboard class Projects < Page::Base view 'app/views/shared/projects/_search_form.html.haml' do - element :form_filter_by_name, /form_tag.+id: 'project-filter-form'/ # rubocop:disable QA/ElementWithPattern + element :project_filter_form, required: true end def go_to_project(name) @@ -14,10 +14,14 @@ module QA find_link(text: name).click end + def self.path + '/' + end + private def filter_by_name(name) - page.within('form#project-filter-form') do + within_element(:project_filter_form) do fill_in :name, with: name end end diff --git a/qa/qa/page/merge_request/show.rb b/qa/qa/page/merge_request/show.rb index 5aef868a805..6a415b56e50 100644 --- a/qa/qa/page/merge_request/show.rb +++ b/qa/qa/page/merge_request/show.rb @@ -111,6 +111,14 @@ module QA end end + def has_title?(title) + has_element?(:title, text: title) + end + + def has_description?(description) + has_element?(:description, text: description) + end + def merge! # The merge button is disabled on load wait do diff --git a/qa/qa/resource/label.rb b/qa/qa/resource/label.rb index 5a681a5fe9f..3750725c440 100644 --- a/qa/qa/resource/label.rb +++ b/qa/qa/resource/label.rb @@ -46,7 +46,7 @@ module QA end def api_post_path - "/projects/#{project}/labels" + "/projects/#{project.id}/labels" end def api_post_body diff --git a/qa/qa/resource/project.rb b/qa/qa/resource/project.rb index d706439a891..e8ea947581a 100644 --- a/qa/qa/resource/project.rb +++ b/qa/qa/resource/project.rb @@ -18,7 +18,11 @@ module QA end attribute :path_with_namespace do - "#{group.sandbox.path}/#{group.path}/#{name}" if group + "#{sandbox_path}#{group.path}/#{name}" if group + end + + def sandbox_path + group.respond_to?('sandbox') ? "#{group.sandbox.path}/" : '' end attribute :repository_ssh_location do diff --git a/qa/qa/resource/project_milestone.rb b/qa/qa/resource/project_milestone.rb index 8ace75f695a..70640eac095 100644 --- a/qa/qa/resource/project_milestone.rb +++ b/qa/qa/resource/project_milestone.rb @@ -31,6 +31,21 @@ module QA milestone_new.click_milestone_create_button end end + + def api_get_path + "/projects/#{project.id}/milestones/#{id}" + end + + def api_post_path + "/projects/#{project.id}/milestones" + end + + def api_post_body + { + description: @description, + title: @title + } + end end end end diff --git a/qa/qa/resource/sandbox.rb b/qa/qa/resource/sandbox.rb index 942eea5cc40..e2b1c4c0831 100644 --- a/qa/qa/resource/sandbox.rb +++ b/qa/qa/resource/sandbox.rb @@ -44,6 +44,10 @@ module QA "/groups/#{path}" end + def api_members_path + "#{api_get_path}/members" + end + def api_post_path '/groups' end diff --git a/qa/qa/runtime/address.rb b/qa/qa/runtime/address.rb index 98d042fb43a..c622051bb6d 100644 --- a/qa/qa/runtime/address.rb +++ b/qa/qa/runtime/address.rb @@ -5,7 +5,7 @@ module QA class Address attr_reader :address - def initialize(instance, page = nil) + def initialize(instance, page) @instance = instance @address = host + (page.is_a?(String) ? page : page&.path) end diff --git a/qa/qa/runtime/browser.rb b/qa/qa/runtime/browser.rb index 3bf4b3bbbfb..ed0779b93cc 100644 --- a/qa/qa/runtime/browser.rb +++ b/qa/qa/runtime/browser.rb @@ -1,6 +1,7 @@ # frozen_string_literal: true require 'rspec/core' +require 'rspec/expectations' require 'capybara/rspec' require 'capybara-screenshot/rspec' require 'selenium-webdriver' @@ -27,13 +28,12 @@ module QA # In case of an address that is a symbol we will try to guess address # based on `Runtime::Scenario#something_address`. # - def visit(address, page = nil, &block) - Browser::Session.new(address, page).perform(&block) + def visit(address, page_class, &block) + Browser::Session.new(address, page_class).perform(&block) end - def self.visit(address, page = nil, &block) - new.visit(address, page, &block) - page.validate_elements_present! + def self.visit(address, page_class, &block) + new.visit(address, page_class, &block) end def self.configure! @@ -128,8 +128,11 @@ module QA class Session include Capybara::DSL - def initialize(instance, page = nil) - @session_address = Runtime::Address.new(instance, page) + attr_reader :page_class + + def initialize(instance, page_class) + @session_address = Runtime::Address.new(instance, page_class) + @page_class = page_class end def url @@ -139,6 +142,8 @@ module QA def perform(&block) visit(url) + page_class.validate_elements_present! + if QA::Runtime::Env.qa_cookies browser = Capybara.current_session.driver.browser QA::Runtime::Env.qa_cookies.each do |cookie| diff --git a/qa/qa/runtime/env.rb b/qa/qa/runtime/env.rb index 82510dfa03c..96f337dc081 100644 --- a/qa/qa/runtime/env.rb +++ b/qa/qa/runtime/env.rb @@ -10,13 +10,26 @@ module QA # The environment variables used to indicate if the environment under test # supports the given feature SUPPORTED_FEATURES = { - git_protocol_v2: 'QA_CAN_TEST_GIT_PROTOCOL_V2' + git_protocol_v2: 'QA_CAN_TEST_GIT_PROTOCOL_V2', + admin: 'QA_CAN_TEST_ADMIN_FEATURES' }.freeze def supported_features SUPPORTED_FEATURES end + def admin_password + ENV['GITLAB_ADMIN_PASSWORD'] + end + + def admin_username + ENV['GITLAB_ADMIN_USERNAME'] + end + + def admin_personal_access_token + ENV['GITLAB_QA_ADMIN_ACCESS_TOKEN'] + end + def debug? enabled?(ENV['QA_DEBUG'], default: false) end @@ -92,14 +105,6 @@ module QA ENV['GITLAB_PASSWORD'] end - def admin_username - ENV['GITLAB_ADMIN_USERNAME'] - end - - def admin_password - ENV['GITLAB_ADMIN_PASSWORD'] - end - def github_username ENV['GITHUB_USERNAME'] end diff --git a/qa/qa/specs/features/browser_ui/3_create/merge_request/create_merge_request_spec.rb b/qa/qa/specs/features/browser_ui/3_create/merge_request/create_merge_request_spec.rb index cd1c7545944..6969f123f95 100644 --- a/qa/qa/specs/features/browser_ui/3_create/merge_request/create_merge_request_spec.rb +++ b/qa/qa/specs/features/browser_ui/3_create/merge_request/create_merge_request_spec.rb @@ -1,74 +1,64 @@ # frozen_string_literal: true module QA - # Failure issue: https://gitlab.com/gitlab-org/quality/staging/issues/50 - context 'Create', :quarantine do - describe 'Merge request creation' do - it 'user creates a new merge request', :smoke do + context 'Create' do + describe 'Create a new merge request' do + before do Runtime::Browser.visit(:gitlab, Page::Main::Login) - Page::Main::Login.act { sign_in_using_credentials } + Page::Main::Login.perform(&:sign_in_using_credentials) - current_project = Resource::Project.fabricate! do |project| - project.name = 'project-with-merge-request' + @project = Resource::Project.fabricate_via_api! do |project| + project.name = 'project' end - merge_request_title = 'This is a merge request' - merge_request_description = 'Great feature' + @merge_request_title = 'One merge request to rule them all' + @merge_request_description = '... to find them, to bring them all, and in the darkness bind them' + end - Resource::MergeRequest.fabricate! do |merge_request| - merge_request.title = merge_request_title - merge_request.description = merge_request_description - merge_request.project = current_project + it 'creates a basic merge request', :smoke do + Resource::MergeRequest.fabricate_via_browser_ui! do |merge_request| + merge_request.project = @project + merge_request.title = @merge_request_title + merge_request.description = @merge_request_description end - expect(page).to have_content(merge_request_title) - expect(page).to have_content(merge_request_description) - expect(page).to have_content('Opened just now') + Page::MergeRequest::Show.perform do |merge_request| + expect(merge_request).to have_title(@merge_request_title) + expect(merge_request).to have_description(@merge_request_description) + end end - it 'user creates a new merge request with a milestone and label' do + it 'creates a merge request with a milestone and label' do gitlab_account_username = "@#{Runtime::User.username}" - Runtime::Browser.visit(:gitlab, Page::Main::Login) - Page::Main::Login.act { sign_in_using_credentials } - - current_project = Resource::Project.fabricate! do |project| - project.name = 'project-with-merge-request-and-milestone' - end - - current_milestone = Resource::ProjectMilestone.fabricate! do |milestone| - milestone.title = 'unique-milestone' - milestone.project = current_project + milestone = Resource::ProjectMilestone.fabricate_via_api! do |milestone| + milestone.project = @project + milestone.title = 'milestone' end - new_label = Resource::Label.fabricate_via_browser_ui! do |label| - label.project = current_project - label.title = 'qa-mr-test-label' - label.description = 'Merge Request label' + label = Resource::Label.fabricate_via_api! do |label| + label.project = @project + label.title = 'label' end - merge_request_title = 'This is a merge request with a milestone and a label' - merge_request_description = 'Great feature with milestone' - - Resource::MergeRequest.fabricate! do |merge_request| - merge_request.title = merge_request_title - merge_request.description = merge_request_description - merge_request.project = current_project - merge_request.milestone = current_milestone + Resource::MergeRequest.fabricate_via_browser_ui! do |merge_request| + merge_request.title = @merge_request_title + merge_request.description = @merge_request_description + merge_request.project = @project + merge_request.milestone = milestone merge_request.assignee = 'me' - merge_request.labels.push(new_label) + merge_request.labels.push(label) end Page::MergeRequest::Show.perform do |merge_request| - expect(merge_request).to have_content(merge_request_title) - expect(merge_request).to have_content(merge_request_description) - expect(merge_request).to have_content('Opened just now') + expect(merge_request).to have_title(@merge_request_title) + expect(merge_request).to have_description(@merge_request_description) expect(merge_request).to have_assignee(gitlab_account_username) - expect(merge_request).to have_label(new_label.title) + expect(merge_request).to have_label(label.title) end Page::Issuable::Sidebar.perform do |sidebar| - expect(sidebar).to have_milestone(current_milestone.title) + expect(sidebar).to have_milestone(milestone.title) end end end diff --git a/qa/qa/specs/features/browser_ui/3_create/repository/push_over_http_file_size_spec.rb b/qa/qa/specs/features/browser_ui/3_create/repository/push_over_http_file_size_spec.rb index 5bfafdfa041..247cde38e52 100644 --- a/qa/qa/specs/features/browser_ui/3_create/repository/push_over_http_file_size_spec.rb +++ b/qa/qa/specs/features/browser_ui/3_create/repository/push_over_http_file_size_spec.rb @@ -1,74 +1,57 @@ # frozen_string_literal: true module QA - # Failure issue: https://gitlab.com/gitlab-org/quality/staging/issues/37 - context 'Create', :quarantine do + context 'Create', :requires_admin do describe 'push after setting the file size limit via admin/application_settings' do - before(:all) do - push = Resource::Repository::ProjectPush.fabricate! do |p| - p.file_name = 'README.md' - p.file_content = '# This is a test project' - p.commit_message = 'Add README.md' + before(:context) do + @project = Resource::Project.fabricate_via_api! do |p| + p.name = 'project-test-push-limit' + p.initialize_with_readme = true end - @project = push.project + @api_client = Runtime::API::Client.new(:gitlab, personal_access_token: Runtime::Env.admin_personal_access_token) end - before do - Runtime::Browser.visit(:gitlab, Page::Main::Login) - Page::Main::Login.perform(&:sign_in_using_credentials) - end - - after(:all) do + after(:context) do # need to set the default value after test # default value for file size limit is empty - Runtime::Browser.visit(:gitlab, Page::Main::Login) - Page::Main::Login.perform(&:sign_in_using_credentials) - - set_file_size_limit('') - - Page::Main::Menu.perform(&:sign_out) + set_file_size_limit(nil) end it 'push successful when the file size is under the limit' do set_file_size_limit(5) - expect(page).to have_content("Application settings saved successfully") - push = push_new_file('oversize_file_1.bin', wait_for_push: true) expect(push.output).not_to have_content 'remote: fatal: pack exceeds maximum allowed size' end it 'push fails when the file size is above the limit' do set_file_size_limit(1) - expect(page).to have_content("Application settings saved successfully") - expect { push_new_file('oversize_file_2.bin', wait_for_push: false) } .to raise_error(QA::Git::Repository::RepositoryCommandError, /remote: fatal: pack exceeds maximum allowed size/) end def set_file_size_limit(limit) - Page::Main::Menu.perform(&:click_admin_area) - Page::Admin::Menu.perform(&:go_to_general_settings) + request = Runtime::API::Request.new(@api_client, '/application/settings') + put request.url, receive_max_input_size: limit - Page::Admin::Settings::General.perform do |setting| - setting.expand_account_and_limit do |page| - page.set_max_file_size(limit) - page.save_settings - end - end + expect_status(200) + expect(json_body).to match( + a_hash_including(receive_max_input_size: limit) + ) end def push_new_file(file_name, wait_for_push: true) - @project.visit! - - Resource::Repository::ProjectPush.fabricate! do |p| - p.project = @project + commit_message = 'Adding a new file' + output = Resource::Repository::Push.fabricate! do |p| + p.repository_http_uri = @project.repository_http_location.uri p.file_name = file_name p.file_content = SecureRandom.random_bytes(2000000) - p.commit_message = 'Adding a new file' - p.wait_for_push = wait_for_push + p.commit_message = commit_message p.new_branch = false end + @project.wait_for_push commit_message + + output end end end diff --git a/qa/qa/tools/generate_perf_testdata.rb b/qa/qa/tools/generate_perf_testdata.rb index de8cfa1aed9..b0477951967 100644 --- a/qa/qa/tools/generate_perf_testdata.rb +++ b/qa/qa/tools/generate_perf_testdata.rb @@ -19,26 +19,30 @@ module QA raise ArgumentError, "Please provide GITLAB_QA_ACCESS_TOKEN" unless ENV['GITLAB_QA_ACCESS_TOKEN'] @api_client = Runtime::API::Client.new(ENV['GITLAB_ADDRESS'], personal_access_token: ENV['GITLAB_QA_ACCESS_TOKEN']) - @group_name = "gitlab-qa-perf-sandbox-#{SecureRandom.hex(8)}" - @project_name = "my-test-project-#{SecureRandom.hex(8)}" + @group_name = ENV['GROUP_NAME'] || "gitlab-qa-perf-sandbox-#{SecureRandom.hex(8)}" + @project_name = ENV['PROJECT_NAME'] || "my-test-project-#{SecureRandom.hex(8)}" @visibility = "public" @urls = { host: ENV['GITLAB_ADDRESS'] } end - def run + def all STDOUT.puts 'Running...' group_id = create_group create_project(group_id) - create_branch - add_new_file + + create_many_branches + create_many_new_files + create_mr_with_many_commits + methods_arr = [ - method(:create_issues), - method(:create_labels), - method(:create_todos), - method(:create_merge_requests), - method(:create_issue_with_500_discussions), - method(:create_mr_with_large_files) + method(:create_many_issues), + method(:create_many_labels), + method(:create_many_todos), + method(:create_many_merge_requests), + method(:create_an_issue_with_many_discussions), + method(:create_an_mr_with_large_files_and_many_mr_discussions) ] + threads_arr = [] methods_arr.each do |m| @@ -51,103 +55,102 @@ module QA STDOUT.puts "\nDone" end - private - def create_group - group_search_response = post Runtime::API::Request.new(@api_client, "/groups").url, "name=#{@group_name}&path=#{@group_name}&visibility=#{@visibility}" + group_search_response = create_a_group_api_req(@group_name, @visibility) group = JSON.parse(group_search_response.body) @urls[:group_page] = group["web_url"] group["id"] + STDOUT.puts "Created a group: #{@urls[:group_page]}" end def create_project(group_id) - create_project_response = post Runtime::API::Request.new(@api_client, "/projects").url, "name=#{@project_name}&namespace_id=#{group_id}&visibility=#{@visibility}" + create_project_response = create_a_project_api_req(@project_name, group_id, @visibility) @urls[:project_page] = JSON.parse(create_project_response.body)["web_url"] + STDOUT.puts "Created a project: #{@urls[:project_page]}" end - def create_issues + def create_many_issues 30.times do |i| - post Runtime::API::Request.new(@api_client, "/projects/#{@group_name}%2F#{@project_name}/issues").url, "title=issue#{i}&description=desc#{i}" + create_an_issue_api_req("#{@group_name}%2F#{@project_name}", "issue#{i}", "desc#{i}") end @urls[:issues_list_page] = @urls[:project_page] + "/issues" - STDOUT.puts "Created Issues" + STDOUT.puts "Created many issues: #{@urls[:issues_list_page]}" end - def create_todos + def create_many_todos 30.times do |i| - post Runtime::API::Request.new(@api_client, "/projects/#{@group_name}%2F#{@project_name}/issues/#{i + 1}/todo").url, nil + create_a_todo_api_req("#{@group_name}%2F#{@project_name}", "#{i + 1}") end @urls[:todos_page] = ENV['GITLAB_ADDRESS'] + "/dashboard/todos" - STDOUT.puts "Created todos" + STDOUT.puts "Created many todos: #{@urls[:todos_page]}" end - def create_labels + def create_many_labels 30.times do |i| - post Runtime::API::Request.new(@api_client, "/projects/#{@group_name}%2F#{@project_name}/labels").url, - "name=label#{i}&color=#{Faker::Color.hex_color}" + create_a_label_api_req("#{@group_name}%2F#{@project_name}", "label#{i}", "#{Faker::Color.hex_color}") end @urls[:labels_page] = @urls[:project_page] + "/labels" - STDOUT.puts "Created labels" + STDOUT.puts "Created many labels: #{@urls[:labels_page]}" end - def create_merge_requests + def create_many_merge_requests 30.times do |i| - post Runtime::API::Request.new(@api_client, "/projects/#{@group_name}%2F#{@project_name}/merge_requests").url, "source_branch=branch#{i}&target_branch=master&title=MR#{i}" + create_a_merge_request_api_req("#{@group_name}%2F#{@project_name}", "branch#{i}", "master", "MR#{i}") end @urls[:mr_list_page] = @urls[:project_page] + "/merge_requests" - STDOUT.puts "Created MRs" + STDOUT.puts "Created many MRs: #{@urls[:mr_list_page]}" end - def add_new_file - post Runtime::API::Request.new(@api_client, "/projects/#{@group_name}%2F#{@project_name}/repository/files/hello.txt").url, "branch=master&commit_message=\"hello\"&content=\"my new content\"" + def create_many_new_files + create_a_new_file_api_req("hello.txt", "master", "#{@group_name}%2F#{@project_name}", "hello", "my new content") 30.times do |i| - post Runtime::API::Request.new(@api_client, "/projects/#{@group_name}%2F#{@project_name}/repository/files/hello#{i}.txt").url, "branch=branch#{i}&commit_message=\"hello\"&content=\"my new content\"" + create_a_new_file_api_req("hello#{i}.txt", "branch#{i}", "#{@group_name}%2F#{@project_name}", "hello", "my new content") end - STDOUT.puts "Added Files" + + @urls[:files_page] = @urls[:project_page] + "/tree/master" + STDOUT.puts "Added many new files: #{@urls[:files_page]}" end - def create_branch + def create_many_branches 30.times do |i| - post Runtime::API::Request.new(@api_client, "/projects/#{@group_name}%2F#{@project_name}/repository/branches").url, "branch=branch#{i}&ref=master" + create_a_branch_api_req("branch#{i}", "#{@group_name}%2F#{@project_name}") end - STDOUT.puts "Created branches" + @urls[:branches_page] = @urls[:project_page] + "/-/branches" + STDOUT.puts "Created many branches: #{@urls[:branches_page]}" end - def create_issue_with_500_discussions + def create_an_issue_with_many_discussions issue_id = 1 500.times do - post Runtime::API::Request.new(@api_client, "/projects/#{@group_name}%2F#{@project_name}/issues/#{issue_id}/discussions").url, "body=\"Let us discuss\"" + create_a_discussion_on_issue_api_req("#{@group_name}%2F#{@project_name}", issue_id, "Let us discuss") end - labels_list = (0..15).map {|i| "label#{i}"}.join(',') + labels_list = (0..15).map { |i| "label#{i}" }.join(',') # Add description and labels - put Runtime::API::Request.new(@api_client, "/projects/#{@group_name}%2F#{@project_name}/issues/#{issue_id}").url, "description=#{Faker::Lorem.sentences(500).join(" ")}&labels=#{labels_list}" + update_an_issue_api_req("#{@group_name}%2F#{@project_name}", issue_id, "#{Faker::Lorem.sentences(500).join(" ")}", labels_list) @urls[:large_issue] = @urls[:project_page] + "/issues/#{issue_id}" - STDOUT.puts "Created Issue with 500 Discussions" + STDOUT.puts "Created an issue with many discussions: #{@urls[:large_issue]}" end - def create_mr_with_large_files + def create_an_mr_with_large_files_and_many_mr_discussions content_arr = [] 16.times do |i| faker_line_arr = Faker::Lorem.sentences(1500) content = faker_line_arr.join("\n\r") - post Runtime::API::Request.new(@api_client, "/projects/#{@group_name}%2F#{@project_name}/repository/files/hello#{i}.txt").url, - "branch=master&commit_message=\"Add hello#{i}.txt\"&content=#{content}" + create_a_new_file_api_req("hello#{i}.txt", "master", "#{@group_name}%2F#{@project_name}", "Add hello#{i}.txt", content) content_arr[i] = faker_line_arr end - post Runtime::API::Request.new(@api_client, "/projects/#{@group_name}%2F#{@project_name}/repository/branches").url, - "branch=performance&ref=master" + create_a_branch_api_req("performance", "#{@group_name}%2F#{@project_name}") 16.times do |i| missed_line_array = content_arr[i].each_slice(2).map(&:first) content = missed_line_array.join("\n\rIm new!:D \n\r ") - put Runtime::API::Request.new(@api_client, "/projects/#{@group_name}%2F#{@project_name}/repository/files/hello#{i}.txt").url, - "branch=performance&commit_message=\"Update hello#{i}.txt\"&content=#{content}" + + update_file_api_req("hello#{i}.txt", "performance", "#{@group_name}%2F#{@project_name}", "Update hello#{i}.txt", content) end - create_mr_response = post Runtime::API::Request.new(@api_client, """/projects/#{@group_name}%2F#{@project_name}/merge_requests""").url, - "source_branch=performance&target_branch=master&title=Large_MR" + create_mr_response = create_a_merge_request_api_req("#{@group_name}%2F#{@project_name}", "performance", "master", "Large_MR") iid = JSON.parse(create_mr_response.body)["iid"] diff_refs = JSON.parse(create_mr_response.body)["diff_refs"] @@ -161,8 +164,8 @@ module QA if should_resolve discussion_id = JSON.parse(create_diff_note_response.body)["id"] - put Runtime::API::Request.new(@api_client, """/projects/#{@group_name}%2F#{@project_name}/merge_requests/#{iid}/discussions/#{discussion_id}""").url, - "resolved=true" + + update_a_discussion_on_issue_api_req("#{@group_name}%2F#{@project_name}", iid, discussion_id, "true") end should_resolve ^= true @@ -171,23 +174,93 @@ module QA # Add discussions to main tab 100.times do - post Runtime::API::Request.new(@api_client, "/projects/#{@group_name}%2F#{@project_name}/merge_requests/#{iid}/discussions").url, - "body=\"Let us discuss\"" + create_a_discussion_on_mr_api_req("#{@group_name}%2F#{@project_name}", iid, "Let us discuss") end @urls[:large_mr] = JSON.parse(create_mr_response.body)["web_url"] - STDOUT.puts "Created MR with 500 Discussions and 20 Very Large Files" + STDOUT.puts "Created an MR with many discussions and many very large Files: #{@urls[:large_mr]}" end def create_diff_note(iid, file_count, line_count, head_sha, start_sha, base_sha, line_type) post Runtime::API::Request.new(@api_client, "/projects/#{@group_name}%2F#{@project_name}/merge_requests/#{iid}/discussions").url, - """body=\"Let us discuss\"& + "" "body=\"Let us discuss\"& position[position_type]=text& position[new_path]=hello#{file_count}.txt& position[old_path]=hello#{file_count}.txt& position[#{line_type}]=#{line_count * 100}& position[head_sha]=#{head_sha}& position[start_sha]=#{start_sha}& - position[base_sha]=#{base_sha}""" + position[base_sha]=#{base_sha}" "" + end + + def create_mr_with_many_commits + project_path = "#{@group_name}%2F#{@project_name}" + branch_name = "branch_with_many_commits-#{SecureRandom.hex(8)}" + file_name = "file_for_many_commits.txt" + create_a_branch_api_req(branch_name, project_path) + create_a_new_file_api_req(file_name, branch_name, project_path, "Initial commit for new file", "Initial file content") + create_mr_response = create_a_merge_request_api_req(project_path, branch_name, "master", "MR with many commits-#{SecureRandom.hex(8)}") + @urls[:mr_with_many_commits] = JSON.parse(create_mr_response.body)["web_url"] + 100.times do |i| + update_file_api_req(file_name, branch_name, project_path, Faker::Lorem.sentences(5).join(" "), Faker::Lorem.sentences(500).join("\n")) + end + STDOUT.puts "Created an MR with many commits: #{@urls[:mr_with_many_commits]}" + end + + private + + # API Requests + + def create_a_discussion_on_issue_api_req(project_path_or_id, issue_id, body) + post Runtime::API::Request.new(@api_client, "/projects/#{project_path_or_id}/issues/#{issue_id}/discussions").url, "body=\"#{body}\"" + end + + def update_a_discussion_on_issue_api_req(project_path_or_id, mr_iid, discussion_id, resolved_status) + put Runtime::API::Request.new(@api_client, "/projects/#{project_path_or_id}/merge_requests/#{mr_iid}/discussions/#{discussion_id}").url, "resolved=#{resolved_status}" + end + + def create_a_discussion_on_mr_api_req(project_path_or_id, mr_iid, body) + post Runtime::API::Request.new(@api_client, "/projects/#{project_path_or_id}/merge_requests/#{mr_iid}/discussions").url, + "body=\"#{body}\"" + end + + def create_a_label_api_req(project_path_or_id, name, color) + post Runtime::API::Request.new(@api_client, "/projects/#{project_path_or_id}/labels").url, "name=#{name}&color=#{color}" + end + + def create_a_todo_api_req(project_path_or_id, issue_id) + post Runtime::API::Request.new(@api_client, "/projects/#{project_path_or_id}/issues/#{issue_id}/todo").url, nil + end + + def create_an_issue_api_req(project_path_or_id, title, description) + post Runtime::API::Request.new(@api_client, "/projects/#{project_path_or_id}/issues").url, "title=#{title}&description=#{description}" + end + + def update_an_issue_api_req(project_path_or_id, issue_id, description, labels_list) + put Runtime::API::Request.new(@api_client, "/projects/#{project_path_or_id}/issues/#{issue_id}").url, "description=#{description}&labels=#{labels_list}" + end + + def create_a_project_api_req(project_name, group_id, visibility) + post Runtime::API::Request.new(@api_client, "/projects").url, "name=#{project_name}&namespace_id=#{group_id}&visibility=#{visibility}" + end + + def create_a_group_api_req(group_name, visibility) + post Runtime::API::Request.new(@api_client, "/groups").url, "name=#{group_name}&path=#{group_name}&visibility=#{visibility}" + end + + def create_a_branch_api_req(branch_name, project_path_or_id) + post Runtime::API::Request.new(@api_client, "/projects/#{project_path_or_id}/repository/branches").url, "branch=#{branch_name}&ref=master" + end + + def create_a_new_file_api_req(file_path, branch_name, project_path_or_id, commit_message, content) + post Runtime::API::Request.new(@api_client, "/projects/#{project_path_or_id}/repository/files/#{file_path}").url, "branch=#{branch_name}&commit_message=\"#{commit_message}\"&content=\"#{content}\"" + end + + def create_a_merge_request_api_req(project_path_or_id, source_branch, target_branch, mr_title) + post Runtime::API::Request.new(@api_client, "/projects/#{project_path_or_id}/merge_requests").url, "source_branch=#{source_branch}&target_branch=#{target_branch}&title=#{mr_title}" + end + + def update_file_api_req(file_path, branch_name, project_path_or_id, commit_message, content) + put Runtime::API::Request.new(@api_client, "/projects/#{project_path_or_id}/repository/files/#{file_path}").url, "branch=#{branch_name}&commit_message=\"#{commit_message}\"&content=\"#{content}\"" end end end diff --git a/qa/spec/runtime/env_spec.rb b/qa/spec/runtime/env_spec.rb index 2560695ef2e..caf96a213e1 100644 --- a/qa/spec/runtime/env_spec.rb +++ b/qa/spec/runtime/env_spec.rb @@ -227,6 +227,12 @@ describe QA::Runtime::Env do env_key: 'QA_CAN_TEST_GIT_PROTOCOL_V2', default: true + it_behaves_like 'boolean method with parameter', + method: :can_test?, + param: :admin, + env_key: 'QA_CAN_TEST_ADMIN_FEATURES', + default: true + it 'raises ArgumentError if feature is unknown' do expect { described_class.can_test? :foo }.to raise_error(ArgumentError, 'Unknown feature "foo"') end diff --git a/qa/spec/specs/runner_spec.rb b/qa/spec/specs/runner_spec.rb index 5c86c102105..f94145d148e 100644 --- a/qa/spec/specs/runner_spec.rb +++ b/qa/spec/specs/runner_spec.rb @@ -1,16 +1,22 @@ # frozen_string_literal: true +require 'active_support/core_ext/hash' + describe QA::Specs::Runner do + shared_examples 'excludes orchestrated' do + it 'excludes the orchestrated tag and includes default args' do + expect_rspec_runner_arguments(['--tag', '~orchestrated', *described_class::DEFAULT_TEST_PATH_ARGS]) + + subject.perform + end + end + context '#perform' do before do allow(QA::Runtime::Browser).to receive(:configure!) end - it 'excludes the orchestrated tag by default' do - expect_rspec_runner_arguments(['--tag', '~orchestrated', *described_class::DEFAULT_TEST_PATH_ARGS]) - - subject.perform - end + it_behaves_like 'excludes orchestrated' context 'when tty is set' do subject { described_class.new.tap { |runner| runner.tty = true } } @@ -67,8 +73,6 @@ describe QA::Specs::Runner do allow(QA::Runtime::Env).to receive(:signup_disabled?).and_return(true) end - subject { described_class.new } - it 'includes default args and excludes the skip_signup_disabled tag' do expect_rspec_runner_arguments(['--tag', '~orchestrated', '--tag', '~skip_signup_disabled', *described_class::DEFAULT_TEST_PATH_ARGS]) @@ -76,18 +80,54 @@ describe QA::Specs::Runner do end end - context 'when git protocol v2 is not supported' do - before do - allow(QA::Runtime::Env).to receive(:can_test?).with(:git_protocol_v2).and_return(false) + context 'testable features' do + shared_examples 'one supported feature' do |feature| + before do + QA::Runtime::Env.supported_features.each do |tag, _| + allow(QA::Runtime::Env).to receive(:can_test?).with(tag).and_return(false) + end + + allow(QA::Runtime::Env).to receive(:can_test?).with(feature).and_return(true) unless feature.nil? + end + + it 'includes default args and excludes all unsupported tags' do + expect_rspec_runner_arguments(['--tag', '~orchestrated', *excluded_feature_tags_except(feature), *described_class::DEFAULT_TEST_PATH_ARGS]) + + subject.perform + end end - subject { described_class.new } + context 'when only git protocol 2 is supported' do + it_behaves_like 'one supported feature', :git_protocol_v2 + end - it 'includes default args and excludes the requires_git_protocol_v2 tag' do - expect_rspec_runner_arguments(['--tag', '~orchestrated', '--tag', '~requires_git_protocol_v2', *described_class::DEFAULT_TEST_PATH_ARGS]) + context 'when only admin features are supported' do + it_behaves_like 'one supported feature', :admin + end - subject.perform + context 'when no features are supported' do + it_behaves_like 'one supported feature', nil end + + context 'when all features are supported' do + before do + QA::Runtime::Env.supported_features.each do |tag, _| + allow(QA::Runtime::Env).to receive(:can_test?).with(tag).and_return(true) + end + end + + it_behaves_like 'excludes orchestrated' + end + + context 'when features are not specified' do + it_behaves_like 'excludes orchestrated' + end + end + + def excluded_feature_tags_except(tag) + QA::Runtime::Env.supported_features.except(tag).map do |tag, _| + ['--tag', "~requires_#{tag}"] + end.flatten end def expect_rspec_runner_arguments(arguments) diff --git a/spec/controllers/admin/clusters_controller_spec.rb b/spec/controllers/admin/clusters_controller_spec.rb index 7b77cb186a4..7709f525119 100644 --- a/spec/controllers/admin/clusters_controller_spec.rb +++ b/spec/controllers/admin/clusters_controller_spec.rb @@ -396,6 +396,7 @@ describe Admin::ClustersController do cluster: { enabled: false, name: 'my-new-cluster-name', + managed: false, base_domain: domain } } @@ -409,6 +410,7 @@ describe Admin::ClustersController do expect(flash[:notice]).to eq('Kubernetes cluster was successfully updated.') expect(cluster.enabled).to be_falsey expect(cluster.name).to eq('my-new-cluster-name') + expect(cluster).not_to be_managed expect(cluster.domain).to eq('test-domain.com') end @@ -433,6 +435,7 @@ describe Admin::ClustersController do cluster: { enabled: false, name: 'my-new-cluster-name', + managed: false, domain: domain } } @@ -445,6 +448,7 @@ describe Admin::ClustersController do expect(response).to have_http_status(:no_content) expect(cluster.enabled).to be_falsey expect(cluster.name).to eq('my-new-cluster-name') + expect(cluster).not_to be_managed end end diff --git a/spec/controllers/concerns/issuable_collections_spec.rb b/spec/controllers/concerns/issuable_collections_spec.rb index fb2cd5ca955..f210537aad5 100644 --- a/spec/controllers/concerns/issuable_collections_spec.rb +++ b/spec/controllers/concerns/issuable_collections_spec.rb @@ -180,5 +180,16 @@ describe IssuableCollections do is_expected.not_to include('invalid_param', 'invalid_array') end end + + context 'search using an issue iid' do + let(:params) { { search: "#5" } } + + it 'mutates the search into a filter by iid' do + is_expected.to include({ + 'iids' => '5', + 'search' => nil + }) + end + end end end diff --git a/spec/controllers/groups/clusters_controller_spec.rb b/spec/controllers/groups/clusters_controller_spec.rb index 7349cb7094c..2f64c7f3460 100644 --- a/spec/controllers/groups/clusters_controller_spec.rb +++ b/spec/controllers/groups/clusters_controller_spec.rb @@ -463,6 +463,7 @@ describe Groups::ClustersController do cluster: { enabled: false, name: 'my-new-cluster-name', + managed: false, base_domain: domain } } @@ -476,6 +477,7 @@ describe Groups::ClustersController do expect(flash[:notice]).to eq('Kubernetes cluster was successfully updated.') expect(cluster.enabled).to be_falsey expect(cluster.name).to eq('my-new-cluster-name') + expect(cluster).not_to be_managed expect(cluster.domain).to eq('test-domain.com') end @@ -500,6 +502,7 @@ describe Groups::ClustersController do cluster: { enabled: false, name: 'my-new-cluster-name', + managed: false, domain: domain } } @@ -512,6 +515,7 @@ describe Groups::ClustersController do expect(response).to have_http_status(:no_content) expect(cluster.enabled).to be_falsey expect(cluster.name).to eq('my-new-cluster-name') + expect(cluster).not_to be_managed end end diff --git a/spec/controllers/projects/branches_controller_spec.rb b/spec/controllers/projects/branches_controller_spec.rb index c778b7888dc..cf201c9f735 100644 --- a/spec/controllers/projects/branches_controller_spec.rb +++ b/spec/controllers/projects/branches_controller_spec.rb @@ -123,7 +123,11 @@ describe Projects::BranchesController do expect(response).to redirect_to project_tree_path(project, branch) end - shared_examples 'same behavior between KubernetesService and Platform::Kubernetes' do + context 'when user configured kubernetes from CI/CD > Clusters' do + before do + create(:cluster, :provided_by_gcp, projects: [project]) + end + it 'redirects to autodeploy setup page' do result = { status: :success, branch: double(name: branch) } @@ -143,22 +147,6 @@ describe Projects::BranchesController do end end - context 'when user configured kubernetes from Integration > Kubernetes' do - before do - project.services << build(:kubernetes_service) - end - - it_behaves_like 'same behavior between KubernetesService and Platform::Kubernetes' - end - - context 'when user configured kubernetes from CI/CD > Clusters' do - before do - create(:cluster, :provided_by_gcp, projects: [project]) - end - - it_behaves_like 'same behavior between KubernetesService and Platform::Kubernetes' - end - it 'redirects to autodeploy setup page' do result = { status: :success, branch: double(name: branch) } diff --git a/spec/controllers/projects/clusters_controller_spec.rb b/spec/controllers/projects/clusters_controller_spec.rb index 8d37bd82d21..fa49438287f 100644 --- a/spec/controllers/projects/clusters_controller_spec.rb +++ b/spec/controllers/projects/clusters_controller_spec.rb @@ -449,6 +449,7 @@ describe Projects::ClustersController do cluster: { enabled: false, name: 'my-new-cluster-name', + managed: false, platform_kubernetes_attributes: { namespace: 'my-namespace' } @@ -464,6 +465,7 @@ describe Projects::ClustersController do expect(flash[:notice]).to eq('Kubernetes cluster was successfully updated.') expect(cluster.enabled).to be_falsey expect(cluster.name).to eq('my-new-cluster-name') + expect(cluster).not_to be_managed expect(cluster.platform_kubernetes.namespace).to eq('my-namespace') end @@ -475,6 +477,7 @@ describe Projects::ClustersController do cluster: { enabled: false, name: 'my-new-cluster-name', + managed: false, platform_kubernetes_attributes: { namespace: 'my-namespace' } @@ -489,6 +492,7 @@ describe Projects::ClustersController do expect(response).to have_http_status(:no_content) expect(cluster.enabled).to be_falsey expect(cluster.name).to eq('my-new-cluster-name') + expect(cluster).not_to be_managed expect(cluster.platform_kubernetes.namespace).to eq('my-namespace') end end diff --git a/spec/controllers/projects/services_controller_spec.rb b/spec/controllers/projects/services_controller_spec.rb index 3608d175d50..5c7f8d95f82 100644 --- a/spec/controllers/projects/services_controller_spec.rb +++ b/spec/controllers/projects/services_controller_spec.rb @@ -141,20 +141,6 @@ describe Projects::ServicesController do end end - context 'with a deprecated service' do - let(:service) { create(:kubernetes_service, project: project) } - - before do - put :update, - params: { namespace_id: project.namespace, project_id: project, id: service.to_param, service: { namespace: 'updated_namespace' } } - end - - it 'does not update the service' do - service.reload - expect(service.namespace).not_to eq('updated_namespace') - end - end - context 'when activating JIRA service from a template' do let(:template_service) { create(:jira_service, project: project, template: true) } @@ -168,20 +154,10 @@ describe Projects::ServicesController do describe "GET #edit" do before do - get :edit, params: { namespace_id: project.namespace, project_id: project, id: service_id } + get :edit, params: { namespace_id: project.namespace, project_id: project, id: 'jira' } end context 'with approved services' do - let(:service_id) { 'jira' } - - it 'renders edit page' do - expect(response).to be_success - end - end - - context 'with a deprecated service' do - let(:service_id) { 'kubernetes' } - it 'renders edit page' do expect(response).to be_success end diff --git a/spec/factories/clusters/kubernetes_namespaces.rb b/spec/factories/clusters/kubernetes_namespaces.rb index 3b50a57433f..042be7b4c4a 100644 --- a/spec/factories/clusters/kubernetes_namespaces.rb +++ b/spec/factories/clusters/kubernetes_namespaces.rb @@ -16,5 +16,9 @@ FactoryBot.define do trait :with_token do service_account_token { FFaker::Lorem.characters(10) } end + + trait :without_token do + service_account_token nil + end end end diff --git a/spec/factories/services.rb b/spec/factories/services.rb index 0d8c26a2ee9..763909f30bd 100644 --- a/spec/factories/services.rb +++ b/spec/factories/services.rb @@ -24,6 +24,8 @@ FactoryBot.define do api_url: 'https://kubernetes.example.com', token: 'a' * 40 }) + + skip_deprecation_validation true end factory :mock_deployment_service do diff --git a/spec/features/projects/clusters/gcp_spec.rb b/spec/features/projects/clusters/gcp_spec.rb index 83e582c34f0..e4a3a1a8c92 100644 --- a/spec/features/projects/clusters/gcp_spec.rb +++ b/spec/features/projects/clusters/gcp_spec.rb @@ -79,10 +79,6 @@ describe 'Gcp Cluster', :js do expect(page).to have_content('Something wrong!') end - - it 'user sees RBAC is enabled by default' do - expect(page).to have_checked_field('RBAC-enabled cluster') - end end context 'when user filled form with invalid parameters' do diff --git a/spec/features/projects/clusters/interchangeability_spec.rb b/spec/features/projects/clusters/interchangeability_spec.rb deleted file mode 100644 index 0033e12b6b1..00000000000 --- a/spec/features/projects/clusters/interchangeability_spec.rb +++ /dev/null @@ -1,16 +0,0 @@ -require 'spec_helper' - -describe 'Interchangeability between KubernetesService and Platform::Kubernetes' do - EXCEPT_METHODS = %i[test title description help fields initialize_properties namespace namespace= api_url api_url= deprecated? deprecation_message].freeze - EXCEPT_METHODS_GREP_V = %w[_touched? _changed? _was].freeze - - it 'Clusters::Platform::Kubernetes covers core interfaces in KubernetesService' do - expected_interfaces = KubernetesService.instance_methods(false) - expected_interfaces = expected_interfaces - EXCEPT_METHODS - EXCEPT_METHODS_GREP_V.each do |g| - expected_interfaces = expected_interfaces.grep_v(/#{Regexp.escape(g)}\z/) - end - - expect(expected_interfaces - Clusters::Platforms::Kubernetes.instance_methods).to be_empty - end -end diff --git a/spec/features/projects/environments/environment_spec.rb b/spec/features/projects/environments/environment_spec.rb index da4ef6428d4..fbaf12be64e 100644 --- a/spec/features/projects/environments/environment_spec.rb +++ b/spec/features/projects/environments/environment_spec.rb @@ -155,7 +155,10 @@ describe 'Environment' do end context 'with terminal' do - shared_examples 'same behavior between KubernetesService and Platform::Kubernetes' do + context 'when user configured kubernetes from CI/CD > Clusters' do + let!(:cluster) { create(:cluster, :project, :provided_by_gcp) } + let(:project) { cluster.project } + context 'for project maintainer' do let(:role) { :maintainer } @@ -191,19 +194,6 @@ describe 'Environment' do end end end - - context 'when user configured kubernetes from Integration > Kubernetes' do - let(:project) { create(:kubernetes_project, :test_repo) } - - it_behaves_like 'same behavior between KubernetesService and Platform::Kubernetes' - end - - context 'when user configured kubernetes from CI/CD > Clusters' do - let!(:cluster) { create(:cluster, :project, :provided_by_gcp) } - let(:project) { cluster.project } - - it_behaves_like 'same behavior between KubernetesService and Platform::Kubernetes' - end end context 'when environment is available' do diff --git a/spec/features/projects/environments/environments_spec.rb b/spec/features/projects/environments/environments_spec.rb index 7b7e45312d9..1b5d9083932 100644 --- a/spec/features/projects/environments/environments_spec.rb +++ b/spec/features/projects/environments/environments_spec.rb @@ -248,7 +248,10 @@ describe 'Environments page', :js do end context 'when kubernetes terminal is available' do - shared_examples 'same behavior between KubernetesService and Platform::Kubernetes' do + context 'when user configured kubernetes from CI/CD > Clusters' do + let(:cluster) { create(:cluster, :provided_by_gcp, projects: [create(:project, :repository)]) } + let(:project) { cluster.project } + context 'for project maintainer' do let(:role) { :maintainer } @@ -265,19 +268,6 @@ describe 'Environments page', :js do end end end - - context 'when user configured kubernetes from Integration > Kubernetes' do - let(:project) { create(:kubernetes_project, :test_repo) } - - it_behaves_like 'same behavior between KubernetesService and Platform::Kubernetes' - end - - context 'when user configured kubernetes from CI/CD > Clusters' do - let(:cluster) { create(:cluster, :provided_by_gcp, projects: [create(:project, :repository)]) } - let(:project) { cluster.project } - - it_behaves_like 'same behavior between KubernetesService and Platform::Kubernetes' - end end end diff --git a/spec/finders/fork_projects_finder_spec.rb b/spec/finders/fork_projects_finder_spec.rb index b3fdffc3331..98cff37205e 100644 --- a/spec/finders/fork_projects_finder_spec.rb +++ b/spec/finders/fork_projects_finder_spec.rb @@ -12,6 +12,8 @@ describe ForkProjectsFinder do let(:private_fork_member) { create(:user) } before do + stub_feature_flags(object_pools: { enabled: false, thing: source_project }) + private_fork.update!(visibility_level: Gitlab::VisibilityLevel::PRIVATE) private_fork.add_developer(private_fork_member) diff --git a/spec/frontend/ide/services/index_spec.js b/spec/frontend/ide/services/index_spec.js new file mode 100644 index 00000000000..499fa8fc012 --- /dev/null +++ b/spec/frontend/ide/services/index_spec.js @@ -0,0 +1,55 @@ +import services from '~/ide/services'; +import Api from '~/api'; + +jest.mock('~/api'); + +const TEST_PROJECT_ID = 'alice/wonderland'; +const TEST_BRANCH = 'master-patch-123'; +const TEST_COMMIT_SHA = '123456789'; + +describe('IDE services', () => { + describe('commit', () => { + let payload; + + beforeEach(() => { + payload = { + branch: TEST_BRANCH, + commit_message: 'Hello world', + actions: [], + start_sha: undefined, + }; + + Api.createBranch.mockReturnValue(Promise.resolve()); + Api.commitMultiple.mockReturnValue(Promise.resolve()); + }); + + describe.each` + startSha | shouldCreateBranch + ${undefined} | ${false} + ${TEST_COMMIT_SHA} | ${true} + `('when start_sha is $startSha', ({ startSha, shouldCreateBranch }) => { + beforeEach(() => { + payload.start_sha = startSha; + + return services.commit(TEST_PROJECT_ID, payload); + }); + + if (shouldCreateBranch) { + it('should create branch', () => { + expect(Api.createBranch).toHaveBeenCalledWith(TEST_PROJECT_ID, { + ref: TEST_COMMIT_SHA, + branch: TEST_BRANCH, + }); + }); + } else { + it('should not create branch', () => { + expect(Api.createBranch).not.toHaveBeenCalled(); + }); + } + + it('should commit', () => { + expect(Api.commitMultiple).toHaveBeenCalledWith(TEST_PROJECT_ID, payload); + }); + }); + }); +}); diff --git a/spec/javascripts/ide/stores/modules/commit/actions_spec.js b/spec/javascripts/ide/stores/modules/commit/actions_spec.js index 5f7272311c8..8a3c132972e 100644 --- a/spec/javascripts/ide/stores/modules/commit/actions_spec.js +++ b/spec/javascripts/ide/stores/modules/commit/actions_spec.js @@ -6,9 +6,11 @@ import eventHub from '~/ide/eventhub'; import consts from '~/ide/stores/modules/commit/constants'; import * as mutationTypes from '~/ide/stores/modules/commit/mutation_types'; import * as actions from '~/ide/stores/modules/commit/actions'; -import testAction from '../../../../helpers/vuex_action_helper'; import { commitActionTypes } from '~/ide/constants'; import { resetStore, file } from 'spec/ide/helpers'; +import testAction from '../../../../helpers/vuex_action_helper'; + +const TEST_COMMIT_SHA = '123456789'; describe('IDE commit module actions', () => { beforeEach(() => { @@ -139,6 +141,9 @@ describe('IDE commit module actions', () => { branches: { master: { workingReference: '', + commit: { + short_id: TEST_COMMIT_SHA, + }, }, }, }; @@ -239,6 +244,9 @@ describe('IDE commit module actions', () => { branches: { master: { workingReference: '1', + commit: { + short_id: TEST_COMMIT_SHA, + }, }, }, }; @@ -247,7 +255,7 @@ describe('IDE commit module actions', () => { ...file('changed'), type: 'blob', active: true, - lastCommitSha: '123456789', + lastCommitSha: TEST_COMMIT_SHA, }; store.state.stagedFiles.push(f); store.state.changedFiles = [ @@ -307,7 +315,7 @@ describe('IDE commit module actions', () => { previous_path: undefined, }, ], - start_branch: 'master', + start_sha: TEST_COMMIT_SHA, }); done(); @@ -330,11 +338,11 @@ describe('IDE commit module actions', () => { file_path: jasmine.anything(), content: undefined, encoding: jasmine.anything(), - last_commit_id: '123456789', + last_commit_id: TEST_COMMIT_SHA, previous_path: undefined, }, ], - start_branch: undefined, + start_sha: undefined, }); done(); diff --git a/spec/javascripts/ide/stores/utils_spec.js b/spec/javascripts/ide/stores/utils_spec.js index debe1c4acee..e3bf6d40245 100644 --- a/spec/javascripts/ide/stores/utils_spec.js +++ b/spec/javascripts/ide/stores/utils_spec.js @@ -132,7 +132,7 @@ describe('Multi-file store utils', () => { previous_path: undefined, }, ], - start_branch: undefined, + start_sha: undefined, }); }); @@ -187,7 +187,7 @@ describe('Multi-file store utils', () => { previous_path: undefined, }, ], - start_branch: undefined, + start_sha: undefined, }); }); }); diff --git a/spec/lib/gitlab/ci/build/policy/kubernetes_spec.rb b/spec/lib/gitlab/ci/build/policy/kubernetes_spec.rb index 4884d5f8ba4..4510b82ca9d 100644 --- a/spec/lib/gitlab/ci/build/policy/kubernetes_spec.rb +++ b/spec/lib/gitlab/ci/build/policy/kubernetes_spec.rb @@ -4,24 +4,14 @@ describe Gitlab::Ci::Build::Policy::Kubernetes do let(:pipeline) { create(:ci_pipeline, project: project) } context 'when kubernetes service is active' do - shared_examples 'same behavior between KubernetesService and Platform::Kubernetes' do - it 'is satisfied by a kubernetes pipeline' do - expect(described_class.new('active')) - .to be_satisfied_by(pipeline) - end - end - - context 'when user configured kubernetes from Integration > Kubernetes' do - let(:project) { create(:kubernetes_project) } - - it_behaves_like 'same behavior between KubernetesService and Platform::Kubernetes' - end - context 'when user configured kubernetes from CI/CD > Clusters' do let!(:cluster) { create(:cluster, :project, :provided_by_gcp) } let(:project) { cluster.project } - it_behaves_like 'same behavior between KubernetesService and Platform::Kubernetes' + it 'is satisfied by a kubernetes pipeline' do + expect(described_class.new('active')) + .to be_satisfied_by(pipeline) + end end end diff --git a/spec/lib/gitlab/ci/build/prerequisite/kubernetes_namespace_spec.rb b/spec/lib/gitlab/ci/build/prerequisite/kubernetes_namespace_spec.rb index 5387863bd07..5ac5122e800 100644 --- a/spec/lib/gitlab/ci/build/prerequisite/kubernetes_namespace_spec.rb +++ b/spec/lib/gitlab/ci/build/prerequisite/kubernetes_namespace_spec.rb @@ -35,9 +35,15 @@ describe Gitlab::Ci::Build::Prerequisite::KubernetesNamespace do end context 'and a namespace is already created for this project' do - let!(:kubernetes_namespace) { create(:cluster_kubernetes_namespace, cluster: cluster, project: build.project) } + let!(:kubernetes_namespace) { create(:cluster_kubernetes_namespace, :with_token, cluster: cluster, project: build.project) } it { is_expected.to be_falsey } + + context 'and the service_account_token is blank' do + let!(:kubernetes_namespace) { create(:cluster_kubernetes_namespace, :without_token, cluster: cluster, project: build.project) } + + it { is_expected.to be_truthy } + end end context 'and cluster is project type' do diff --git a/spec/migrations/migrate_k8s_service_integration_spec.rb b/spec/migrations/migrate_k8s_service_integration_spec.rb new file mode 100644 index 00000000000..4dd0c09632a --- /dev/null +++ b/spec/migrations/migrate_k8s_service_integration_spec.rb @@ -0,0 +1,161 @@ +# frozen_string_literal: true + +require 'spec_helper' +require Rails.root.join('db', 'post_migrate', '20190517153211_migrate_k8s_service_integration.rb') + +describe MigrateK8sServiceIntegration, :migration do + context 'template service' do + context 'with namespace' do + let!(:service) do + MigrateK8sServiceIntegration::Service.create!( + active: true, + template: true, + category: 'deployment', + type: 'KubernetesService', + properties: "{\"namespace\":\"prod\",\"api_url\":\"https://sample.kubernetes.com\",\"ca_pem\":\"ca_pem-sample\",\"token\":\"token-sample\"}" + ) + end + + let(:cluster) { MigrateK8sServiceIntegration::Cluster.instance_type.last! } + let(:platform) { cluster.platform_kubernetes } + + it 'migrates the KubernetesService template to Platform::Kubernetes' do + expect { migrate! }.to change { MigrateK8sServiceIntegration::Cluster.count }.by(1) + + expect(cluster).to be_enabled + expect(cluster).to be_user + expect(cluster).not_to be_managed + expect(cluster.environment_scope).to eq('*') + expect(platform.api_url).to eq('https://sample.kubernetes.com') + expect(platform.ca_cert).to eq('ca_pem-sample') + expect(platform.namespace).to eq('prod') + expect(platform.token).to eq('token-sample') + end + end + + context 'without namespace' do + let!(:service) do + MigrateK8sServiceIntegration::Service.create!( + active: true, + template: true, + category: 'deployment', + type: 'KubernetesService', + properties: "{\"namespace\":\"\",\"api_url\":\"https://sample.kubernetes.com\",\"ca_pem\":\"ca_pem-sample\",\"token\":\"token-sample\"}" + ) + end + + let(:cluster) { MigrateK8sServiceIntegration::Cluster.instance_type.last! } + let(:platform) { cluster.platform_kubernetes } + + it 'migrates the KubernetesService template to Platform::Kubernetes' do + expect { migrate! }.to change { MigrateK8sServiceIntegration::Cluster.count }.by(1) + + expect(cluster).to be_enabled + expect(cluster).to be_user + expect(cluster).not_to be_managed + expect(cluster.environment_scope).to eq('*') + expect(platform.api_url).to eq('https://sample.kubernetes.com') + expect(platform.ca_cert).to eq('ca_pem-sample') + expect(platform.namespace).to be_nil + expect(platform.token).to eq('token-sample') + end + end + + context 'with nullified parameters' do + let!(:service) do + MigrateK8sServiceIntegration::Service.create!( + active: true, + template: true, + category: 'deployment', + type: 'KubernetesService', + properties: "{}" + ) + end + + it 'does not migrate the KubernetesService' do + expect { migrate! }.not_to change { MigrateK8sServiceIntegration::Cluster.count } + end + end + + context 'when disabled' do + let!(:service) do + MigrateK8sServiceIntegration::Service.create!( + active: false, + template: true, + category: 'deployment', + type: 'KubernetesService', + properties: "{\"namespace\":\"prod\",\"api_url\":\"https://sample.kubernetes.com\",\"ca_pem\":\"ca_pem-sample\",\"token\":\"token-sample\"}" + ) + end + + let(:cluster) { MigrateK8sServiceIntegration::Cluster.instance_type.last! } + let(:platform) { cluster.platform_kubernetes } + + it 'migrates the KubernetesService template to Platform::Kubernetes' do + expect { migrate! }.to change { MigrateK8sServiceIntegration::Cluster.count }.by(1) + + expect(cluster).not_to be_enabled + expect(cluster).to be_user + expect(cluster).not_to be_managed + expect(cluster.environment_scope).to eq('*') + expect(platform.api_url).to eq('https://sample.kubernetes.com') + expect(platform.ca_cert).to eq('ca_pem-sample') + expect(platform.namespace).to eq('prod') + expect(platform.token).to eq('token-sample') + end + end + + context 'when an instance cluster already exists' do + let!(:service) do + MigrateK8sServiceIntegration::Service.create!( + active: true, + template: true, + category: 'deployment', + type: 'KubernetesService', + properties: "{\"namespace\":\"prod\",\"api_url\":\"https://sample.kubernetes.com\",\"ca_pem\":\"ca_pem-sample\",\"token\":\"token-sample\"}" + ) + end + + let!(:existing_cluster) do + MigrateK8sServiceIntegration::Cluster.create!( + name: 'test-cluster', + cluster_type: :instance_type, + managed: true, + provider_type: :user, + platform_type: :kubernetes + ) + end + let(:new_cluster) { MigrateK8sServiceIntegration::Cluster.instance_type.last! } + let(:platform) { new_cluster.platform_kubernetes } + + it 'migrates the KubernetesService template to disabled Platform::Kubernetes' do + expect { migrate! }.to change { MigrateK8sServiceIntegration::Cluster.count }.by(1) + + expect(new_cluster).not_to be_enabled + expect(new_cluster).to be_user + expect(new_cluster).not_to be_managed + expect(new_cluster.environment_scope).to eq('*') + expect(platform.api_url).to eq('https://sample.kubernetes.com') + expect(platform.ca_cert).to eq('ca_pem-sample') + expect(platform.namespace).to eq('prod') + expect(platform.token).to eq('token-sample') + end + end + end + + context 'non-template service' do + let!(:service) do + MigrateK8sServiceIntegration::Service.create!( + active: true, + template: false, + category: 'deployment', + type: 'KubernetesService', + properties: "{\"namespace\":\"prod\",\"api_url\":\"https://sample.kubernetes.com\",\"ca_pem\":\"ca_pem-sample\",\"token\":\"token-sample\"}" + ) + end + + it 'does not migrate the KubernetesService' do + expect { migrate! }.not_to change { MigrateK8sServiceIntegration::Cluster.count } + end + end +end diff --git a/spec/models/ci/pipeline_schedule_spec.rb b/spec/models/ci/pipeline_schedule_spec.rb index 227870eb27f..c4d8ad5317a 100644 --- a/spec/models/ci/pipeline_schedule_spec.rb +++ b/spec/models/ci/pipeline_schedule_spec.rb @@ -128,8 +128,10 @@ describe Ci::PipelineSchedule do context 'when pipeline schedule runs every minute' do let(:pipeline_schedule) { create(:ci_pipeline_schedule, :every_minute) } - it "updates next_run_at to the sidekiq worker's execution time", :quarantine do - expect(pipeline_schedule.next_run_at).to eq(cron_worker_next_run_at) + it "updates next_run_at to the sidekiq worker's execution time" do + Timecop.freeze do + expect(pipeline_schedule.next_run_at).to eq(cron_worker_next_run_at) + end end end diff --git a/spec/models/ci/pipeline_spec.rb b/spec/models/ci/pipeline_spec.rb index c4e54be673f..6ebc6337d50 100644 --- a/spec/models/ci/pipeline_spec.rb +++ b/spec/models/ci/pipeline_spec.rb @@ -962,7 +962,11 @@ describe Ci::Pipeline, :mailer do end context 'when kubernetes is active' do - shared_examples 'same behavior between KubernetesService and Platform::Kubernetes' do + context 'when user configured kubernetes from CI/CD > Clusters' do + let!(:cluster) { create(:cluster, :project, :provided_by_gcp) } + let(:project) { cluster.project } + let(:pipeline) { build(:ci_pipeline, project: project, config: config) } + it 'returns seeds for kubernetes dependent job' do seeds = pipeline.stage_seeds @@ -971,21 +975,6 @@ describe Ci::Pipeline, :mailer do expect(seeds.dig(1, 0, :name)).to eq 'production' end end - - context 'when user configured kubernetes from Integration > Kubernetes' do - let(:project) { create(:kubernetes_project) } - let(:pipeline) { build(:ci_pipeline, project: project, config: config) } - - it_behaves_like 'same behavior between KubernetesService and Platform::Kubernetes' - end - - context 'when user configured kubernetes from CI/CD > Clusters' do - let!(:cluster) { create(:cluster, :project, :provided_by_gcp) } - let(:project) { cluster.project } - let(:pipeline) { build(:ci_pipeline, project: project, config: config) } - - it_behaves_like 'same behavior between KubernetesService and Platform::Kubernetes' - end end context 'when kubernetes is not active' do @@ -1679,23 +1668,13 @@ describe Ci::Pipeline, :mailer do describe '#has_kubernetes_active?' do context 'when kubernetes is active' do - shared_examples 'same behavior between KubernetesService and Platform::Kubernetes' do - it 'returns true' do - expect(pipeline).to have_kubernetes_active - end - end - - context 'when user configured kubernetes from Integration > Kubernetes' do - let(:project) { create(:kubernetes_project) } - - it_behaves_like 'same behavior between KubernetesService and Platform::Kubernetes' - end - context 'when user configured kubernetes from CI/CD > Clusters' do let!(:cluster) { create(:cluster, :project, :provided_by_gcp) } let(:project) { cluster.project } - it_behaves_like 'same behavior between KubernetesService and Platform::Kubernetes' + it 'returns true' do + expect(pipeline).to have_kubernetes_active + end end end diff --git a/spec/models/clusters/cluster_spec.rb b/spec/models/clusters/cluster_spec.rb index f206bb41f45..52661178d76 100644 --- a/spec/models/clusters/cluster_spec.rb +++ b/spec/models/clusters/cluster_spec.rb @@ -514,19 +514,43 @@ describe Clusters::Cluster, :use_clean_rails_memory_store_caching do subject { cluster.allow_user_defined_namespace? } context 'project type cluster' do - it { is_expected.to be_truthy } + context 'gitlab managed' do + it { is_expected.to be_truthy } + end + + context 'not managed' do + let(:cluster) { create(:cluster, :provided_by_gcp, managed: false) } + + it { is_expected.to be_truthy } + end end context 'group type cluster' do - let(:cluster) { create(:cluster, :provided_by_gcp, :group) } + context 'gitlab managed' do + let(:cluster) { create(:cluster, :provided_by_gcp, :group) } - it { is_expected.to be_falsey } + it { is_expected.to be_falsey } + end + + context 'not managed' do + let(:cluster) { create(:cluster, :provided_by_gcp, :group, managed: false) } + + it { is_expected.to be_truthy } + end end context 'instance type cluster' do - let(:cluster) { create(:cluster, :provided_by_gcp, :instance) } + context 'gitlab managed' do + let(:cluster) { create(:cluster, :provided_by_gcp, :instance) } - it { is_expected.to be_falsey } + it { is_expected.to be_falsey } + end + + context 'not managed' do + let(:cluster) { create(:cluster, :provided_by_gcp, :instance, managed: false) } + + it { is_expected.to be_truthy } + end end end @@ -555,6 +579,63 @@ describe Clusters::Cluster, :use_clean_rails_memory_store_caching do end end + describe '#find_or_initialize_kubernetes_namespace_for_project' do + let(:cluster) { create(:cluster, :project, :provided_by_gcp) } + let(:project) { cluster.projects.first } + + subject { cluster.find_or_initialize_kubernetes_namespace_for_project(project) } + + context 'kubernetes namespace exists' do + context 'with no service account token' do + let!(:kubernetes_namespace) { create(:cluster_kubernetes_namespace, project: project, cluster: cluster) } + + it { is_expected.to eq kubernetes_namespace } + end + + context 'with a service account token' do + let!(:kubernetes_namespace) { create(:cluster_kubernetes_namespace, :with_token, project: project, cluster: cluster) } + + it { is_expected.to eq kubernetes_namespace } + end + end + + context 'kubernetes namespace does not exist' do + it 'initializes a new namespace and sets default values' do + expect(subject).to be_new_record + expect(subject.project).to eq project + expect(subject.cluster).to eq cluster + expect(subject.namespace).to be_present + expect(subject.service_account_name).to be_present + end + end + + context 'a custom scope is provided' do + let(:scope) { cluster.kubernetes_namespaces.has_service_account_token } + + subject { cluster.find_or_initialize_kubernetes_namespace_for_project(project, scope: scope) } + + context 'kubernetes namespace exists' do + context 'with no service account token' do + let!(:kubernetes_namespace) { create(:cluster_kubernetes_namespace, project: project, cluster: cluster) } + + it 'initializes a new namespace and sets default values' do + expect(subject).to be_new_record + expect(subject.project).to eq project + expect(subject.cluster).to eq cluster + expect(subject.namespace).to be_present + expect(subject.service_account_name).to be_present + end + end + + context 'with a service account token' do + let!(:kubernetes_namespace) { create(:cluster_kubernetes_namespace, :with_token, project: project, cluster: cluster) } + + it { is_expected.to eq kubernetes_namespace } + end + end + end + end + describe '#predefined_variables' do subject { cluster.predefined_variables } diff --git a/spec/models/clusters/platforms/kubernetes_spec.rb b/spec/models/clusters/platforms/kubernetes_spec.rb index c485850c16e..1fb3a8de808 100644 --- a/spec/models/clusters/platforms/kubernetes_spec.rb +++ b/spec/models/clusters/platforms/kubernetes_spec.rb @@ -223,19 +223,33 @@ describe Clusters::Platforms::Kubernetes, :use_clean_rails_memory_store_caching let(:namespace) { 'namespace-123' } it { is_expected.to eq(namespace) } + + context 'kubernetes namespace is present but has no service account token' do + let!(:kubernetes_namespace) { create(:cluster_kubernetes_namespace, cluster: cluster) } + + it { is_expected.to eq(namespace) } + end end context 'with no namespace assigned' do let(:namespace) { nil } context 'when kubernetes namespace is present' do - let(:kubernetes_namespace) { create(:cluster_kubernetes_namespace, cluster: cluster) } + let(:kubernetes_namespace) { create(:cluster_kubernetes_namespace, :with_token, cluster: cluster) } before do kubernetes_namespace end it { is_expected.to eq(kubernetes_namespace.namespace) } + + context 'kubernetes namespace has no service account token' do + before do + kubernetes_namespace.update!(namespace: 'old-namespace', service_account_token: nil) + end + + it { is_expected.to eq("#{project.path}-#{project.id}") } + end end context 'when kubernetes namespace is not present' do @@ -284,6 +298,46 @@ describe Clusters::Platforms::Kubernetes, :use_clean_rails_memory_store_caching { key: 'KUBE_TOKEN', value: kubernetes_namespace.service_account_token, public: false, masked: true } ) end + + context 'the cluster has been set to unmanaged after the namespace was created' do + before do + cluster.update!(managed: false) + end + + it_behaves_like 'setting variables' + + it 'sets KUBE_TOKEN from the platform' do + expect(subject).to include( + { key: 'KUBE_TOKEN', value: kubernetes.token, public: false, masked: true } + ) + end + + context 'the platform has a custom namespace set' do + before do + kubernetes.update!(namespace: 'custom-namespace') + end + + it 'sets KUBE_NAMESPACE from the platform' do + expect(subject).to include( + { key: 'KUBE_NAMESPACE', value: kubernetes.namespace, public: true, masked: false } + ) + end + end + + context 'there is no namespace specified on the platform' do + let(:project) { cluster.project } + + before do + kubernetes.update!(namespace: nil) + end + + it 'sets KUBE_NAMESPACE to a default for the project' do + expect(subject).to include( + { key: 'KUBE_NAMESPACE', value: "#{project.path}-#{project.id}", public: true, masked: false } + ) + end + end + end end context 'namespace is provided' do diff --git a/spec/models/environment_spec.rb b/spec/models/environment_spec.rb index 7233d2454c6..379dda1f5c4 100644 --- a/spec/models/environment_spec.rb +++ b/spec/models/environment_spec.rb @@ -515,29 +515,19 @@ describe Environment do context 'when the environment is available' do context 'with a deployment service' do - shared_examples 'same behavior between KubernetesService and Platform::Kubernetes' do - context 'and a deployment' do + context 'when user configured kubernetes from CI/CD > Clusters' do + let!(:cluster) { create(:cluster, :project, :provided_by_gcp) } + let(:project) { cluster.project } + + context 'with deployment' do let!(:deployment) { create(:deployment, :success, environment: environment) } it { is_expected.to be_truthy } end - context 'but no deployments' do + context 'without deployments' do it { is_expected.to be_falsy } end end - - context 'when user configured kubernetes from Integration > Kubernetes' do - let(:project) { create(:kubernetes_project) } - - it_behaves_like 'same behavior between KubernetesService and Platform::Kubernetes' - end - - context 'when user configured kubernetes from CI/CD > Clusters' do - let!(:cluster) { create(:cluster, :project, :provided_by_gcp) } - let(:project) { cluster.project } - - it_behaves_like 'same behavior between KubernetesService and Platform::Kubernetes' - end end context 'without a deployment service' do @@ -546,8 +536,6 @@ describe Environment do end context 'when the environment is unavailable' do - let(:project) { create(:kubernetes_project) } - before do environment.stop end @@ -590,7 +578,10 @@ describe Environment do allow(environment).to receive(:has_terminals?).and_return(true) end - shared_examples 'same behavior between KubernetesService and Platform::Kubernetes' do + context 'when user configured kubernetes from CI/CD > Clusters' do + let!(:cluster) { create(:cluster, :project, :provided_by_gcp) } + let(:project) { cluster.project } + it 'returns the terminals from the deployment service' do expect(environment.deployment_platform) .to receive(:terminals).with(environment) @@ -599,19 +590,6 @@ describe Environment do is_expected.to eq(:fake_terminals) end end - - context 'when user configured kubernetes from Integration > Kubernetes' do - let(:project) { create(:kubernetes_project) } - - it_behaves_like 'same behavior between KubernetesService and Platform::Kubernetes' - end - - context 'when user configured kubernetes from CI/CD > Clusters' do - let!(:cluster) { create(:cluster, :project, :provided_by_gcp) } - let(:project) { cluster.project } - - it_behaves_like 'same behavior between KubernetesService and Platform::Kubernetes' - end end context 'when the environment does not have terminals' do diff --git a/spec/models/project_services/kubernetes_service_spec.rb b/spec/models/project_services/kubernetes_service_spec.rb index 2fce120381b..34ee1eafd5c 100644 --- a/spec/models/project_services/kubernetes_service_spec.rb +++ b/spec/models/project_services/kubernetes_service_spec.rb @@ -17,6 +17,7 @@ describe KubernetesService, :use_clean_rails_memory_store_caching do context 'when service is active' do before do subject.active = true + subject.skip_deprecation_validation = true end it { is_expected.not_to validate_presence_of(:namespace) } @@ -67,6 +68,7 @@ describe KubernetesService, :use_clean_rails_memory_store_caching do before do kubernetes_service.update_attribute(:active, false) + kubernetes_service.skip_deprecation_validation = false kubernetes_service.properties['namespace'] = "foo" end @@ -80,19 +82,11 @@ describe KubernetesService, :use_clean_rails_memory_store_caching do end end - context 'with a non-deprecated service' do - let(:kubernetes_service) { create(:kubernetes_service) } - - it 'updates attributes' do - kubernetes_service.properties['namespace'] = 'foo' - expect(kubernetes_service.save).to be_truthy - end - end - context 'with an active and deprecated service' do let(:kubernetes_service) { create(:kubernetes_service) } before do + kubernetes_service.skip_deprecation_validation = false kubernetes_service.active = false kubernetes_service.properties['namespace'] = 'foo' kubernetes_service.save @@ -110,19 +104,6 @@ describe KubernetesService, :use_clean_rails_memory_store_caching do expect(kubernetes_service.properties['namespace']).to eq("foo") end end - - context 'with a template service' do - let(:kubernetes_service) { create(:kubernetes_service, template: true, active: false) } - - before do - kubernetes_service.properties['namespace'] = 'foo' - end - - it 'updates attributes' do - expect(kubernetes_service.save).to be_truthy - expect(kubernetes_service.properties['namespace']).to eq('foo') - end - end end describe '#initialize_properties' do @@ -393,17 +374,8 @@ describe KubernetesService, :use_clean_rails_memory_store_caching do describe "#deprecated?" do let(:kubernetes_service) { create(:kubernetes_service) } - context 'with an active kubernetes service' do - it 'returns false' do - expect(kubernetes_service.deprecated?).to be_falsy - end - end - - context 'with a inactive kubernetes service' do - it 'returns true' do - kubernetes_service.update_attribute(:active, false) - expect(kubernetes_service.deprecated?).to be_truthy - end + it 'returns true' do + expect(kubernetes_service.deprecated?).to be_truthy end end @@ -414,12 +386,6 @@ describe KubernetesService, :use_clean_rails_memory_store_caching do expect(kubernetes_service.deprecation_message).to match(/Kubernetes service integration has been deprecated/) end - context 'if the services is active' do - it 'returns a message' do - expect(kubernetes_service.deprecation_message).to match(/Your Kubernetes cluster information on this page is still editable/) - end - end - context 'if the service is not active' do it 'returns a message' do kubernetes_service.update_attribute(:active, false) diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index e6d5e8fc320..20b98b5eb85 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -2652,7 +2652,10 @@ describe Project do end context 'when project has a deployment service' do - shared_examples 'same behavior between KubernetesService and Platform::Kubernetes' do + context 'when user configured kubernetes from CI/CD > Clusters and KubernetesNamespace migration has not been executed' do + let!(:cluster) { create(:cluster, :project, :provided_by_gcp) } + let(:project) { cluster.project } + it 'returns variables from this service' do expect(project.deployment_variables).to include( { key: 'KUBE_TOKEN', value: project.deployment_platform.token, public: false, masked: true } @@ -2660,19 +2663,6 @@ describe Project do end end - context 'when user configured kubernetes from Integration > Kubernetes' do - let(:project) { create(:kubernetes_project) } - - it_behaves_like 'same behavior between KubernetesService and Platform::Kubernetes' - end - - context 'when user configured kubernetes from CI/CD > Clusters and KubernetesNamespace migration has not been executed' do - let!(:cluster) { create(:cluster, :project, :provided_by_gcp) } - let(:project) { cluster.project } - - it_behaves_like 'same behavior between KubernetesService and Platform::Kubernetes' - end - context 'when user configured kubernetes from CI/CD > Clusters and KubernetesNamespace migration has been executed' do let!(:kubernetes_namespace) { create(:cluster_kubernetes_namespace, :with_token) } let!(:cluster) { kubernetes_namespace.cluster } @@ -3478,6 +3468,7 @@ describe Project do before do allow(project).to receive(:gitlab_shell).and_return(gitlab_shell) + stub_application_setting(hashed_storage_enabled: false) end describe '#base_dir' do @@ -3584,10 +3575,6 @@ describe Project do let(:hashed_prefix) { File.join('@hashed', hash[0..1], hash[2..3]) } let(:hashed_path) { File.join(hashed_prefix, hash) } - before do - stub_application_setting(hashed_storage_enabled: true) - end - describe '#legacy_storage?' do it 'returns false' do expect(project.legacy_storage?).to be_falsey @@ -4729,10 +4716,6 @@ describe Project do subject { project.object_pool_params } - before do - stub_application_setting(hashed_storage_enabled: true) - end - context 'when the objects cannot be pooled' do let(:project) { create(:project, :repository, :private) } @@ -4778,10 +4761,6 @@ describe Project do context 'when objects are poolable' do let(:project) { create(:project, :repository, :public) } - before do - stub_application_setting(hashed_storage_enabled: true) - end - it { is_expected.to be_git_objects_poolable } end end diff --git a/spec/models/service_spec.rb b/spec/models/service_spec.rb index c9439b0846d..d442c73c118 100644 --- a/spec/models/service_spec.rb +++ b/spec/models/service_spec.rb @@ -82,7 +82,7 @@ describe Service do context 'when template is invalid' do it 'sets service template to inactive when template is invalid' do project = create(:project) - template = KubernetesService.new(template: true, active: true) + template = build(:prometheus_service, template: true, active: true, properties: {}) template.save(validate: false) service = described_class.build_from_template(project.id, template) @@ -309,10 +309,10 @@ describe Service do end describe '.find_by_template' do - let!(:kubernetes_service) { create(:kubernetes_service, template: true) } + let!(:service) { create(:service, template: true) } it 'returns service template' do - expect(KubernetesService.find_by_template).to eq(kubernetes_service) + expect(described_class.find_by_template).to eq(service) end end diff --git a/spec/requests/api/services_spec.rb b/spec/requests/api/services_spec.rb index e260aa21e25..3f79e332b90 100644 --- a/spec/requests/api/services_spec.rb +++ b/spec/requests/api/services_spec.rb @@ -10,7 +10,10 @@ describe API::Services do end Service.available_services_names.each do |service| - describe "PUT /projects/:id/services/#{service.dasherize}" do + # TODO: Remove below `if: (service != "kubernetes")` in the next release + # KubernetesService was deprecated and it can't be updated. Right now it's + # only readable. It should be completely removed in the next iteration. + describe "PUT /projects/:id/services/#{service.dasherize}", if: (service != "kubernetes") do include_context service it "updates #{service} settings" do @@ -19,13 +22,22 @@ describe API::Services do expect(response).to have_gitlab_http_status(200) current_service = project.services.first - event = current_service.event_names.empty? ? "foo" : current_service.event_names.first - state = current_service[event] || false + events = current_service.event_names.empty? ? ["foo"].freeze : current_service.event_names + query_strings = [] + events.each do |event| + query_strings << "#{event}=#{!current_service[event]}" + end + query_strings = query_strings.join('&') - put api("/projects/#{project.id}/services/#{dashed_service}?#{event}=#{!state}", user), params: service_attrs + put api("/projects/#{project.id}/services/#{dashed_service}?#{query_strings}", user), params: service_attrs expect(response).to have_gitlab_http_status(200) - expect(project.services.first[event]).not_to eq(state) unless event == "foo" + events.each do |event| + next if event == "foo" + + expect(project.services.first[event]).not_to eq(current_service[event]), + "expected #{!current_service[event]} for event #{event} for service #{current_service.title}, got #{current_service[event]}" + end end it "returns if required fields missing" do @@ -50,7 +62,10 @@ describe API::Services do end end - describe "DELETE /projects/:id/services/#{service.dasherize}" do + # TODO: Remove below `if: (service != "kubernetes")` in the next release + # KubernetesService was deprecated and it can't be updated. Right now it's + # only readable. It should be completely removed in the next iteration. + describe "DELETE /projects/:id/services/#{service.dasherize}", if: (service != "kubernetes") do include_context service before do diff --git a/spec/serializers/environment_entity_spec.rb b/spec/serializers/environment_entity_spec.rb index c2312734042..906449f470b 100644 --- a/spec/serializers/environment_entity_spec.rb +++ b/spec/serializers/environment_entity_spec.rb @@ -59,15 +59,5 @@ describe EnvironmentEntity do expect(subject[:cluster_type]).to eq('project_type') end end - - context 'when deployment platform is a Kubernetes Service' do - before do - create(:kubernetes_service, project: project) - end - - it 'does not include cluster_type' do - expect(subject).not_to include(:cluster_type) - end - end end end diff --git a/spec/services/projects/after_rename_service_spec.rb b/spec/services/projects/after_rename_service_spec.rb index b8055a285f2..8585d495ffb 100644 --- a/spec/services/projects/after_rename_service_spec.rb +++ b/spec/services/projects/after_rename_service_spec.rb @@ -23,6 +23,7 @@ describe Projects::AfterRenameService do allow(project).to receive(:gitlab_shell).and_return(gitlab_shell) stub_feature_flags(skip_hashed_storage_upgrade: false) + stub_application_setting(hashed_storage_enabled: false) end it 'renames a repository' do diff --git a/spec/services/projects/create_service_spec.rb b/spec/services/projects/create_service_spec.rb index a4c48991807..b0b74407812 100644 --- a/spec/services/projects/create_service_spec.rb +++ b/spec/services/projects/create_service_spec.rb @@ -228,6 +228,7 @@ describe Projects::CreateService, '#execute' do context 'with legacy storage' do before do + stub_application_setting(hashed_storage_enabled: false) gitlab_shell.create_repository(repository_storage, "#{user.namespace.full_path}/existing", 'group/project') end @@ -259,7 +260,6 @@ describe Projects::CreateService, '#execute' do let(:hashed_path) { '@hashed/6b/86/6b86b273ff34fce19d6b804eff5a3f5747ada4eaa22f1d49c01e52ddb7875b4b' } before do - stub_application_setting(hashed_storage_enabled: true) allow(Digest::SHA2).to receive(:hexdigest) { hash } end diff --git a/spec/services/projects/fork_service_spec.rb b/spec/services/projects/fork_service_spec.rb index 6afc91d5e95..0c109e26a6a 100644 --- a/spec/services/projects/fork_service_spec.rb +++ b/spec/services/projects/fork_service_spec.rb @@ -116,11 +116,12 @@ describe Projects::ForkService do end end - context 'repository already exists' do + context 'repository in legacy storage already exists' do let(:repository_storage) { 'default' } let(:repository_storage_path) { Gitlab.config.repositories.storages[repository_storage].legacy_disk_path } before do + stub_application_setting(hashed_storage_enabled: false) gitlab_shell.create_repository(repository_storage, "#{@to_user.namespace.full_path}/#{@from_project.path}", "#{@to_user.namespace.full_path}/#{@from_project.path}") end diff --git a/spec/support/prometheus/additional_metrics_shared_examples.rb b/spec/support/prometheus/additional_metrics_shared_examples.rb index 8044b061ca5..de21e808932 100644 --- a/spec/support/prometheus/additional_metrics_shared_examples.rb +++ b/spec/support/prometheus/additional_metrics_shared_examples.rb @@ -44,7 +44,9 @@ RSpec.shared_examples 'additional metrics query' do end describe 'project has Kubernetes service' do - shared_examples 'same behavior between KubernetesService and Platform::Kubernetes' do + context 'when user configured kubernetes from CI/CD > Clusters' do + let!(:cluster) { create(:cluster, :project, :provided_by_gcp) } + let(:project) { cluster.project } let(:environment) { create(:environment, slug: 'environment-slug', project: project) } let(:kube_namespace) { project.deployment_platform.kubernetes_namespace_for(project) } @@ -56,19 +58,6 @@ RSpec.shared_examples 'additional metrics query' do subject.query(*query_params) end end - - context 'when user configured kubernetes from Integration > Kubernetes' do - let(:project) { create(:kubernetes_project) } - - it_behaves_like 'same behavior between KubernetesService and Platform::Kubernetes' - end - - context 'when user configured kubernetes from CI/CD > Clusters' do - let!(:cluster) { create(:cluster, :project, :provided_by_gcp) } - let(:project) { cluster.project } - - it_behaves_like 'same behavior between KubernetesService and Platform::Kubernetes' - end end describe 'project without Kubernetes service' do diff --git a/spec/support/shared_contexts/services_shared_context.rb b/spec/support/shared_contexts/services_shared_context.rb index 089f1798cd2..0c3a24d206f 100644 --- a/spec/support/shared_contexts/services_shared_context.rb +++ b/spec/support/shared_contexts/services_shared_context.rb @@ -37,8 +37,7 @@ Service.available_services_names.each do |service| def initialize_service(service) service_item = project.find_or_initialize_service(service) service_item.properties = service_attrs - service_item.active = true if service == "kubernetes" - service_item.save + service_item.save! service_item end end diff --git a/spec/workers/reactive_caching_worker_spec.rb b/spec/workers/reactive_caching_worker_spec.rb index 2395e6ec947..b8ca6063ccd 100644 --- a/spec/workers/reactive_caching_worker_spec.rb +++ b/spec/workers/reactive_caching_worker_spec.rb @@ -6,16 +6,6 @@ describe ReactiveCachingWorker do let(:service) { project.deployment_platform } describe '#perform' do - context 'when user configured kubernetes from Integration > Kubernetes' do - let(:project) { create(:kubernetes_project) } - - it 'calls #exclusively_update_reactive_cache!' do - expect_any_instance_of(KubernetesService).to receive(:exclusively_update_reactive_cache!) - - described_class.new.perform("KubernetesService", service.id) - end - end - context 'when user configured kubernetes from CI/CD > Clusters' do let!(:cluster) { create(:cluster, :project, :provided_by_gcp) } let(:project) { cluster.project } |