diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index ba0bc3b8c3e..5f0aa51a805 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -10,7 +10,6 @@ stages: - review - qa - post-qa - - notification - pages variables: @@ -36,7 +35,6 @@ include: - local: .gitlab/ci/frontend.gitlab-ci.yml - local: .gitlab/ci/global.gitlab-ci.yml - local: .gitlab/ci/memory.gitlab-ci.yml - - local: .gitlab/ci/notifications.gitlab-ci.yml - local: .gitlab/ci/pages.gitlab-ci.yml - local: .gitlab/ci/qa.gitlab-ci.yml - local: .gitlab/ci/reports.gitlab-ci.yml diff --git a/.gitlab/ci/notifications.gitlab-ci.yml b/.gitlab/ci/notifications.gitlab-ci.yml deleted file mode 100644 index 1ec61acdfca..00000000000 --- a/.gitlab/ci/notifications.gitlab-ci.yml +++ /dev/null @@ -1,27 +0,0 @@ -# Make sure to update all the similar conditions in other CI config files if you modify these conditions -.if-canonical-gitlab-schedule: &if-canonical-gitlab-schedule - if: '$CI_SERVER_HOST == "gitlab.com" && $CI_PROJECT_NAMESPACE =~ /^gitlab-org($|\/)/ && $CI_PIPELINE_SOURCE == "schedule"' - -.notify: - image: ruby:2.6-alpine - stage: notification - dependencies: [] - cache: {} - before_script: - - apk update && apk add git curl bash - - source scripts/utils.sh - - source scripts/notifications.sh - - install_gitlab_gem - variables: - COMMIT_NOTES_URL: "https://${CI_SERVER_HOST}/${CI_PROJECT_PATH}/commit/${CI_COMMIT_SHA}#notes-list" - -package-and-qa:notify-failure: - extends: .notify - rules: - - <<: *if-canonical-gitlab-schedule - when: manual # TODO: remove notify job if not necessary - script: - - 'export NOTIFICATION_MESSAGE=":skull_and_crossbones: Scheduled QA against master failed! :skull_and_crossbones: See ${CI_PIPELINE_URL}. For downstream pipelines, see ${COMMIT_NOTES_URL}"' - - 'notify_on_job_failure package-and-qa qa-master "${NOTIFICATION_MESSAGE}" ci_failing' - needs: ["package-and-qa"] - allow_failure: true diff --git a/GITALY_SERVER_VERSION b/GITALY_SERVER_VERSION index 54227249d1f..b3a8c61e6a8 100644 --- a/GITALY_SERVER_VERSION +++ b/GITALY_SERVER_VERSION @@ -1 +1 @@ -1.78.0 +1.79.0 diff --git a/app/assets/javascripts/clusters/clusters_bundle.js b/app/assets/javascripts/clusters/clusters_bundle.js index d990d2677a8..b764348eb3c 100644 --- a/app/assets/javascripts/clusters/clusters_bundle.js +++ b/app/assets/javascripts/clusters/clusters_bundle.js @@ -53,6 +53,7 @@ export default class Clusters { helpPath, ingressHelpPath, ingressDnsHelpPath, + ingressModSecurityHelpPath, environmentsHelpPath, clustersHelpPath, deployBoardsHelpPath, @@ -69,6 +70,7 @@ export default class Clusters { helpPath, ingressHelpPath, ingressDnsHelpPath, + ingressModSecurityHelpPath, environmentsHelpPath, clustersHelpPath, deployBoardsHelpPath, @@ -169,6 +171,7 @@ export default class Clusters { ingressHelpPath: this.state.ingressHelpPath, managePrometheusPath: this.state.managePrometheusPath, ingressDnsHelpPath: this.state.ingressDnsHelpPath, + ingressModSecurityHelpPath: this.state.ingressModSecurityHelpPath, cloudRunHelpPath: this.state.cloudRunHelpPath, providerType: this.state.providerType, preInstalledKnative: this.state.preInstalledKnative, diff --git a/app/assets/javascripts/clusters/components/applications.vue b/app/assets/javascripts/clusters/components/applications.vue index 99844a356c8..af50ca7361d 100644 --- a/app/assets/javascripts/clusters/components/applications.vue +++ b/app/assets/javascripts/clusters/components/applications.vue @@ -56,6 +56,11 @@ export default { required: false, default: '', }, + ingressModSecurityHelpPath: { + type: String, + required: false, + default: '', + }, cloudRunHelpPath: { type: String, required: false, @@ -112,6 +117,9 @@ export default { ingressInstalled() { return this.applications.ingress.status === APPLICATION_STATUS.INSTALLED; }, + ingressEnableModsecurity() { + return this.applications.ingress.modsecurity_enabled; + }, ingressExternalEndpoint() { return this.applications.ingress.externalIp || this.applications.ingress.externalHostname; }, @@ -127,6 +135,18 @@ export default { enableClusterApplicationElasticStack() { return gon.features && gon.features.enableClusterApplicationElasticStack; }, + ingressModSecurityDescription() { + const escapedUrl = _.escape(this.ingressModSecurityHelpPath); + + return sprintf( + s__('ClusterIntegration|Learn more about %{startLink}ModSecurity%{endLink}'), + { + startLink: ``, + endLink: '', + }, + false, + ); + }, ingressDescription() { return sprintf( _.escape( @@ -135,9 +155,9 @@ export default { ), ), { - pricingLink: ` - ${_.escape(s__('ClusterIntegration|pricing'))}`, + ${_.escape(s__('ClusterIntegration|pricing'))}`, }, false, ); @@ -311,6 +331,9 @@ Crossplane runs inside your Kubernetes cluster and supports secure connectivity :request-reason="applications.ingress.requestReason" :installed="applications.ingress.installed" :install-failed="applications.ingress.installFailed" + :install-application-request-params="{ + modsecurity_enabled: applications.ingress.modsecurity_enabled, + }" :uninstallable="applications.ingress.uninstallable" :uninstall-successful="applications.ingress.uninstallSuccessful" :uninstall-failed="applications.ingress.uninstallFailed" @@ -326,6 +349,26 @@ Crossplane runs inside your Kubernetes cluster and supports secure connectivity }}

+ + diff --git a/app/assets/javascripts/clusters/stores/clusters_store.js b/app/assets/javascripts/clusters/stores/clusters_store.js index 35dbf951551..9c8563e8f77 100644 --- a/app/assets/javascripts/clusters/stores/clusters_store.js +++ b/app/assets/javascripts/clusters/stores/clusters_store.js @@ -52,6 +52,7 @@ export default class ClusterStore { ingress: { ...applicationInitialState, title: s__('ClusterIntegration|Ingress'), + modsecurity_enabled: false, externalIp: null, externalHostname: null, }, @@ -108,6 +109,7 @@ export default class ClusterStore { helpPath, ingressHelpPath, ingressDnsHelpPath, + ingressModSecurityHelpPath, environmentsHelpPath, clustersHelpPath, deployBoardsHelpPath, @@ -116,6 +118,7 @@ export default class ClusterStore { this.state.helpPath = helpPath; this.state.ingressHelpPath = ingressHelpPath; this.state.ingressDnsHelpPath = ingressDnsHelpPath; + this.state.ingressModSecurityHelpPath = ingressModSecurityHelpPath; this.state.environmentsHelpPath = environmentsHelpPath; this.state.clustersHelpPath = clustersHelpPath; this.state.deployBoardsHelpPath = deployBoardsHelpPath; @@ -207,6 +210,8 @@ export default class ClusterStore { if (appId === INGRESS) { this.state.applications.ingress.externalIp = serverAppEntry.external_ip; this.state.applications.ingress.externalHostname = serverAppEntry.external_hostname; + this.state.applications.ingress.modsecurity_enabled = + serverAppEntry.modsecurity_enabled || this.state.applications.ingress.modsecurity_enabled; } else if (appId === CERT_MANAGER) { this.state.applications.cert_manager.email = this.state.applications.cert_manager.email || serverAppEntry.email; diff --git a/app/controllers/clusters/applications_controller.rb b/app/controllers/clusters/applications_controller.rb index be68d0d0a1d..788ebb14fec 100644 --- a/app/controllers/clusters/applications_controller.rb +++ b/app/controllers/clusters/applications_controller.rb @@ -47,7 +47,7 @@ class Clusters::ApplicationsController < Clusters::BaseController end def cluster_application_params - params.permit(:application, :hostname, :kibana_hostname, :email, :stack) + params.permit(:application, :hostname, :kibana_hostname, :email, :stack, :modsecurity_enabled) end def cluster_application_destroy_params diff --git a/app/models/clusters/applications/ingress.rb b/app/models/clusters/applications/ingress.rb index d140649af3c..d41fc72ae68 100644 --- a/app/models/clusters/applications/ingress.rb +++ b/app/models/clusters/applications/ingress.rb @@ -14,6 +14,7 @@ module Clusters include AfterCommitQueue default_value_for :ingress_type, :nginx + default_value_for :modsecurity_enabled, false default_value_for :version, VERSION enum ingress_type: { @@ -73,7 +74,7 @@ module Clusters private def specification - return {} unless Feature.enabled?(:ingress_modsecurity) + return {} unless modsecurity_enabled { "controller" => { diff --git a/app/serializers/cluster_application_entity.rb b/app/serializers/cluster_application_entity.rb index 218bdd21e37..e7e4f5767e9 100644 --- a/app/serializers/cluster_application_entity.rb +++ b/app/serializers/cluster_application_entity.rb @@ -11,6 +11,7 @@ class ClusterApplicationEntity < Grape::Entity expose :kibana_hostname, if: -> (e, _) { e.respond_to?(:kibana_hostname) } expose :email, if: -> (e, _) { e.respond_to?(:email) } expose :stack, if: -> (e, _) { e.respond_to?(:stack) } + expose :modsecurity_enabled, if: -> (e, _) { e.respond_to?(:modsecurity_enabled) } expose :update_available?, as: :update_available, if: -> (e, _) { e.respond_to?(:update_available?) } expose :can_uninstall?, as: :can_uninstall end diff --git a/app/services/clusters/applications/base_service.rb b/app/services/clusters/applications/base_service.rb index c9f7917938f..4b6c937fd5d 100644 --- a/app/services/clusters/applications/base_service.rb +++ b/app/services/clusters/applications/base_service.rb @@ -31,6 +31,10 @@ module Clusters application.stack = params[:stack] end + if application.has_attribute?(:modsecurity_enabled) + application.modsecurity_enabled = params[:modsecurity_enabled] || false + end + if application.respond_to?(:oauth_application) application.oauth_application = create_oauth_application(application, request) end diff --git a/app/views/clusters/clusters/show.html.haml b/app/views/clusters/clusters/show.html.haml index 5beeaf7259a..4b295cd022d 100644 --- a/app/views/clusters/clusters/show.html.haml +++ b/app/views/clusters/clusters/show.html.haml @@ -30,6 +30,7 @@ help_path: help_page_path('user/project/clusters/index.md', anchor: 'installing-applications'), ingress_help_path: help_page_path('user/project/clusters/index.md', anchor: 'getting-the-external-endpoint'), ingress_dns_help_path: help_page_path('user/project/clusters/index.md', anchor: 'manually-determining-the-external-endpoint'), + ingress_mod_security_help_path: help_page_path('user/clusters/applications.md', anchor: 'web-application-firewall-modsecurity'), environments_help_path: help_page_path('ci/environments', anchor: 'defining-environments'), clusters_help_path: help_page_path('user/project/clusters/index.md', anchor: 'deploying-to-a-kubernetes-cluster'), deploy_boards_help_path: help_page_path('user/project/deploy_boards.html', anchor: 'enabling-deploy-boards'), diff --git a/app/workers/all_queues.yml b/app/workers/all_queues.yml index 96d75b2fd33..ca050887e0f 100644 --- a/app/workers/all_queues.yml +++ b/app/workers/all_queues.yml @@ -187,3 +187,4 @@ - project_daily_statistics - create_evidence - group_export +- self_monitoring_project_create diff --git a/app/workers/self_monitoring_project_create_worker.rb b/app/workers/self_monitoring_project_create_worker.rb new file mode 100644 index 00000000000..69bc5431ff7 --- /dev/null +++ b/app/workers/self_monitoring_project_create_worker.rb @@ -0,0 +1,40 @@ +# frozen_string_literal: true + +class SelfMonitoringProjectCreateWorker + include ApplicationWorker + include ExclusiveLeaseGuard + + # This worker falls under Self-monitoring with Monitor::APM group. However, + # self-monitoring is not classified as a feature category but rather as + # Other Functionality. Metrics seems to be the closest feature_category for + # this worker. + feature_category :metrics + + LEASE_TIMEOUT = 15.minutes.to_i + + EXCLUSIVE_LEASE_KEY = 'self_monitoring_service_creation_deletion' + + def perform + try_obtain_lease do + Gitlab::DatabaseImporters::SelfMonitoring::Project::CreateService.new.execute + end + end + + # @param job_id [String] + # Job ID that is used to construct the cache keys. + # @return [Hash] + # Returns true if the job is enqueued or in progress and false otherwise. + def self.in_progress?(job_id) + Gitlab::SidekiqStatus.job_status(Array.wrap(job_id)).first + end + + private + + def lease_key + EXCLUSIVE_LEASE_KEY + end + + def lease_timeout + LEASE_TIMEOUT + end +end diff --git a/changelogs/unreleased/39140-toggle-modsecurity-enabled-for-managed-ingress.yml b/changelogs/unreleased/39140-toggle-modsecurity-enabled-for-managed-ingress.yml new file mode 100644 index 00000000000..3d4d5ca23f3 --- /dev/null +++ b/changelogs/unreleased/39140-toggle-modsecurity-enabled-for-managed-ingress.yml @@ -0,0 +1,5 @@ +--- +title: Add enable_modsecurity setting to managed ingress +merge_request: 21966 +author: +type: added diff --git a/changelogs/unreleased/gitaly-version-v1.79.0.yml b/changelogs/unreleased/gitaly-version-v1.79.0.yml new file mode 100644 index 00000000000..7741f6b74ee --- /dev/null +++ b/changelogs/unreleased/gitaly-version-v1.79.0.yml @@ -0,0 +1,5 @@ +--- +title: Upgrade to Gitaly v1.79.0 +merge_request: 22515 +author: +type: changed diff --git a/config/sidekiq_queues.yml b/config/sidekiq_queues.yml index 68ad819d48b..e0885bc5ca5 100644 --- a/config/sidekiq_queues.yml +++ b/config/sidekiq_queues.yml @@ -99,6 +99,7 @@ - [chaos, 2] - [create_evidence, 2] - [group_export, 1] + - [self_monitoring_project_create, 2] # EE-specific queues - [analytics, 1] diff --git a/doc/development/pipelines.md b/doc/development/pipelines.md index 1269087c38d..ba50b013c9f 100644 --- a/doc/development/pipelines.md +++ b/doc/development/pipelines.md @@ -264,10 +264,6 @@ subgraph "`qa` stage" dast -.-> |needs and depends on| G; end -subgraph "`notification` stage" - NOTIFICATION2["package-and-qa:notify-failure
(manual)"] -.-> |needs| Q; - end - subgraph "`post-test` stage" M end diff --git a/doc/user/clusters/applications.md b/doc/user/clusters/applications.md index a7753e3f3a4..0b889a29674 100644 --- a/doc/user/clusters/applications.md +++ b/doc/user/clusters/applications.md @@ -248,10 +248,10 @@ use an A record. If your external endpoint is a hostname, use a CNAME record. #### Web Application Firewall (ModSecurity) -> [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/issues/65192) in GitLab 12.3 (enabled using `ingress_modsecurity` [feature flag](../../development/feature_flags/development.md#enabling-a-feature-flag-in-development)). +> [Introduced](https://gitlab.com/gitlab-org/gitlab/merge_requests/21966) in GitLab 12.7. Out of the box, GitLab provides you real-time security monitoring with -[`modsecurity`](https://kubernetes.github.io/ingress-nginx/user-guide/nginx-configuration/annotations/#modsecurity) +[ModSecurity](https://kubernetes.github.io/ingress-nginx/user-guide/nginx-configuration/annotations/#modsecurity). Modsecurity is a toolkit for real-time web application monitoring, logging, and access control. With GitLab's offering, the [OWASP's Core Rule Set](https://www.modsecurity.org/CRS/Documentation/), which provides generic attack detection capabilities, @@ -267,22 +267,18 @@ This feature: kubectl -n gitlab-managed-apps exec -it $(kubectl get pods -n gitlab-managed-apps | grep 'ingress-controller' | awk '{print $1}') -- tail -f /var/log/modsec/audit.log ``` -There is a small performance overhead by enabling `modsecurity`. If this is -considered significant for your application, you can either: +To enable ModSecurity, check the **Enable Web Application Firewall** checkbox +when installing your [Ingress application](#ingress). -- Disable ModSecurity's rule engine for your deployed application by setting - [the deployment variable](../../topics/autodevops/index.md) - `AUTO_DEVOPS_MODSECURITY_SEC_RULE_ENGINE` to `Off`. This will prevent ModSecurity from - processing any requests for the given application or environment. -- Toggle the feature flag to false by running the following command within your - instance's Rails console: +There is a small performance overhead by enabling ModSecurity. If this is +considered significant for your application, you can disable ModSecurity's +rule engine for your deployed application by setting +[the deployment variable](../../topics/autodevops/index.md) +`AUTO_DEVOPS_MODSECURITY_SEC_RULE_ENGINE` to `Off`. This will prevent ModSecurity +from processing any requests for the given application or environment. - ```ruby - Feature.disable(:ingress_modsecurity) - ``` - -Once disabled, you must [uninstall](#uninstalling-applications) and reinstall your Ingress -application for the changes to take effect. +To permanently disable it, you must [uninstall](#uninstalling-applications) and +reinstall your Ingress application for the changes to take effect. ### JupyterHub diff --git a/lib/gitlab/database_importers/self_monitoring/helpers.rb b/lib/gitlab/database_importers/self_monitoring/helpers.rb new file mode 100644 index 00000000000..d7e90967e89 --- /dev/null +++ b/lib/gitlab/database_importers/self_monitoring/helpers.rb @@ -0,0 +1,25 @@ +# frozen_string_literal: true + +module Gitlab + module DatabaseImporters + module SelfMonitoring + module Helpers + def application_settings + @application_settings ||= ApplicationSetting.current_without_cache + end + + def project_created? + self_monitoring_project.present? + end + + def self_monitoring_project + application_settings.instance_administration_project + end + + def self_monitoring_project_id + application_settings.instance_administration_project_id + end + end + end + end +end diff --git a/lib/gitlab/database_importers/self_monitoring/project/create_service.rb b/lib/gitlab/database_importers/self_monitoring/project/create_service.rb index fbf252b7ec3..92a2e504a11 100644 --- a/lib/gitlab/database_importers/self_monitoring/project/create_service.rb +++ b/lib/gitlab/database_importers/self_monitoring/project/create_service.rb @@ -6,38 +6,26 @@ module Gitlab module Project class CreateService < ::BaseService include Stepable - - STEPS_ALLOWED_TO_FAIL = [ - :validate_application_settings, :validate_project_created, :validate_admins - ].freeze + include SelfMonitoring::Helpers VISIBILITY_LEVEL = Gitlab::VisibilityLevel::INTERNAL PROJECT_NAME = 'GitLab Instance Administration' steps :validate_application_settings, - :validate_project_created, :validate_admins, :create_group, :create_project, :save_project_id, :add_group_members, - :add_prometheus_manual_configuration + :add_prometheus_manual_configuration, + :track_event def initialize super(nil) end - def execute! - result = execute_steps - if result[:status] == :success - ::Gitlab::Tracking.event("self_monitoring", "project_created") - result - elsif STEPS_ALLOWED_TO_FAIL.include?(result[:last_step]) - ::Gitlab::Tracking.event("self_monitoring", "project_created") - success - else - raise StandardError, result[:message] - end + def execute + execute_steps end private @@ -49,13 +37,6 @@ module Gitlab error(_('No application_settings found')) end - def validate_project_created(result) - return success(result) unless project_created? - - log_error('Project already created') - error(_('Project already created')) - end - def validate_admins(result) unless instance_admins.any? log_error('No active admin user found') @@ -68,7 +49,7 @@ module Gitlab def create_group(result) if project_created? log_info(_('Instance administrators group already exists')) - result[:group] = application_settings.instance_administration_project.owner + result[:group] = self_monitoring_project.owner return success(result) end @@ -84,7 +65,7 @@ module Gitlab def create_project(result) if project_created? log_info('Instance administration project already exists') - result[:project] = application_settings.instance_administration_project + result[:project] = self_monitoring_project return success(result) end @@ -99,7 +80,7 @@ module Gitlab end def save_project_id(result) - return success if project_created? + return success(result) if project_created? response = application_settings.update( instance_administration_project_id: result[:project].id @@ -140,12 +121,10 @@ module Gitlab success(result) end - def application_settings - @application_settings ||= ApplicationSetting.current_without_cache - end + def track_event(result) + ::Gitlab::Tracking.event("self_monitoring", "project_created") - def project_created? - application_settings.instance_administration_project.present? + success(result) end def parse_url(uri_string) diff --git a/locale/gitlab.pot b/locale/gitlab.pot index e110ccf8544..8967ce8b994 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -2567,6 +2567,9 @@ msgstr "" msgid "BambooService|You must set up automatic revision labeling and a repository trigger in Bamboo." msgstr "" +msgid "Batch operations" +msgstr "" + msgid "BatchComments|Delete all pending comments" msgstr "" @@ -3848,6 +3851,9 @@ msgstr "" msgid "ClusterIntegration|Enable Cloud Run on GKE (beta)" msgstr "" +msgid "ClusterIntegration|Enable Web Application Firewall" +msgstr "" + msgid "ClusterIntegration|Enable or disable GitLab's connection to your Kubernetes cluster." msgstr "" @@ -4034,6 +4040,9 @@ msgstr "" msgid "ClusterIntegration|Learn more about %{help_link_start}zones%{help_link_end}." msgstr "" +msgid "ClusterIntegration|Learn more about %{startLink}ModSecurity%{endLink}" +msgstr "" + msgid "ClusterIntegration|Learn more about %{startLink}Regions %{externalLinkIcon}%{endLink}." msgstr "" @@ -13827,9 +13836,6 @@ msgstr "" msgid "Project access must be granted explicitly to each user." msgstr "" -msgid "Project already created" -msgstr "" - msgid "Project already deleted" msgstr "" @@ -15434,6 +15440,12 @@ msgstr "" msgid "Resume replication" msgstr "" +msgid "Resync" +msgstr "" + +msgid "Resync all designs" +msgstr "" + msgid "Retry" msgstr "" @@ -18332,6 +18344,9 @@ msgstr "" msgid "There was an error subscribing to this label." msgstr "" +msgid "There was an error syncing the Design Repositories." +msgstr "" + msgid "There was an error trying to validate your query" msgstr "" diff --git a/scripts/notifications.sh b/scripts/notifications.sh deleted file mode 100755 index d1b11d44e88..00000000000 --- a/scripts/notifications.sh +++ /dev/null @@ -1,27 +0,0 @@ -# Sends Slack notification MSG to CI_SLACK_WEBHOOK_URL (which needs to be set). -# ICON_EMOJI needs to be set to an icon emoji name (without the `:` around it). -function notify_slack() { - CHANNEL=$1 - MSG=$2 - ICON_EMOJI=$3 - - if [ -z "$CHANNEL" ] || [ -z "$CI_SLACK_WEBHOOK_URL" ] || [ -z "$MSG" ] || [ -z "$ICON_EMOJI" ]; then - echo "Missing argument(s) - Use: $0 channel message icon_emoji" - echo "and set CI_SLACK_WEBHOOK_URL environment variable." - else - curl -X POST --data-urlencode 'payload={"channel": "#'"${CHANNEL}"'", "username": "GitLab QA Bot", "text": "'"${MSG}"'", "icon_emoji": "'":${ICON_EMOJI}:"'"}' "${CI_SLACK_WEBHOOK_URL}" - fi -} - -function notify_on_job_failure() { - JOB_NAME=$1 - CHANNEL=$2 - MSG=$3 - ICON_EMOJI=$4 - - local job_id - job_id=$(scripts/get-job-id "$CI_PROJECT_ID" "$CI_PIPELINE_ID" "$JOB_NAME" -s failed) - if [ -n "${job_id}" ]; then - notify_slack "${CHANNEL}" "${MSG}" "${ICON_EMOJI}" - fi -} diff --git a/spec/factories/clusters/applications/helm.rb b/spec/factories/clusters/applications/helm.rb index 0e59f8cb9ec..8858675c2b8 100644 --- a/spec/factories/clusters/applications/helm.rb +++ b/spec/factories/clusters/applications/helm.rb @@ -71,6 +71,7 @@ FactoryBot.define do end factory :clusters_applications_ingress, class: Clusters::Applications::Ingress do + modsecurity_enabled { false } cluster factory: %i(cluster with_installed_helm provided_by_gcp) end diff --git a/spec/fixtures/api/schemas/cluster_status.json b/spec/fixtures/api/schemas/cluster_status.json index f978baa2026..fcb4f2b94cd 100644 --- a/spec/fixtures/api/schemas/cluster_status.json +++ b/spec/fixtures/api/schemas/cluster_status.json @@ -38,6 +38,7 @@ "kibana_hostname": { "type": ["string", "null"] }, "email": { "type": ["string", "null"] }, "stack": { "type": ["string", "null"] }, + "modsecurity_enabled": { "type": ["boolean", "null"] }, "update_available": { "type": ["boolean", "null"] }, "can_uninstall": { "type": "boolean" } }, diff --git a/spec/frontend/clusters/components/applications_spec.js b/spec/frontend/clusters/components/applications_spec.js index 2d7958a6b65..e8c5a2bd242 100644 --- a/spec/frontend/clusters/components/applications_spec.js +++ b/spec/frontend/clusters/components/applications_spec.js @@ -190,6 +190,7 @@ describe('Applications', () => { title: 'Ingress', status: 'installed', externalHostname: 'localhost.localdomain', + modsecurity_enabled: false, }, helm: { title: 'Helm Tiller' }, cert_manager: { title: 'Cert-Manager' }, @@ -473,7 +474,12 @@ describe('Applications', () => { vm = mountComponent(Applications, { applications: { ...APPLICATIONS_MOCK_STATE, - ingress: { title: 'Ingress', status: 'installed', externalIp: '1.1.1.1' }, + ingress: { + title: 'Ingress', + status: 'installed', + externalIp: '1.1.1.1', + modsecurity_enabled: false, + }, elastic_stack: { title: 'Elastic Stack', status: 'installed', kibana_hostname: '' }, }, }); diff --git a/spec/frontend/clusters/services/mock_data.js b/spec/frontend/clusters/services/mock_data.js index 016f5a259b5..98fa0ec3b07 100644 --- a/spec/frontend/clusters/services/mock_data.js +++ b/spec/frontend/clusters/services/mock_data.js @@ -150,7 +150,7 @@ const DEFAULT_APPLICATION_STATE = { const APPLICATIONS_MOCK_STATE = { helm: { title: 'Helm Tiller', status: 'installable' }, - ingress: { title: 'Ingress', status: 'installable' }, + ingress: { title: 'Ingress', status: 'installable', modsecurity_enabled: false }, crossplane: { title: 'Crossplane', status: 'installable', stack: '' }, cert_manager: { title: 'Cert-Manager', status: 'installable' }, runner: { title: 'GitLab Runner' }, diff --git a/spec/frontend/clusters/stores/clusters_store_spec.js b/spec/frontend/clusters/stores/clusters_store_spec.js index 71d4daceb75..c7ec4ddc464 100644 --- a/spec/frontend/clusters/stores/clusters_store_spec.js +++ b/spec/frontend/clusters/stores/clusters_store_spec.js @@ -86,6 +86,7 @@ describe('Clusters Store', () => { uninstallSuccessful: false, uninstallFailed: false, validationError: null, + modsecurity_enabled: false, }, runner: { title: 'GitLab Runner', diff --git a/spec/frontend/sentry_error_stack_trace/components/sentry_error_stack_trace_spec.js b/spec/frontend/sentry_error_stack_trace/components/sentry_error_stack_trace_spec.js index fb50b31a3da..e5f83b6fa49 100644 --- a/spec/frontend/sentry_error_stack_trace/components/sentry_error_stack_trace_spec.js +++ b/spec/frontend/sentry_error_stack_trace/components/sentry_error_stack_trace_spec.js @@ -69,15 +69,17 @@ describe('Sentry Error Stack Trace', () => { }); describe('Stack trace', () => { - it('should show stacktrace', () => { + beforeEach(() => { store.state.details.loadingStacktrace = false; + }); + + it('should show stacktrace', () => { mountComponent({ stubs: {} }); expect(wrapper.find(GlLoadingIcon).exists()).toBe(false); expect(wrapper.find(Stacktrace).exists()).toBe(true); }); it('should not show stacktrace if it does not exist', () => { - store.state.details.loadingStacktrace = false; expect(wrapper.find(GlLoadingIcon).exists()).toBe(false); expect(wrapper.find(Stacktrace).exists()).toBe(false); }); diff --git a/spec/lib/gitlab/database_importers/self_monitoring/project/create_service_spec.rb b/spec/lib/gitlab/database_importers/self_monitoring/project/create_service_spec.rb index ee3c99afdf1..082485f5ddd 100644 --- a/spec/lib/gitlab/database_importers/self_monitoring/project/create_service_spec.rb +++ b/spec/lib/gitlab/database_importers/self_monitoring/project/create_service_spec.rb @@ -4,7 +4,7 @@ require 'spec_helper' describe Gitlab::DatabaseImporters::SelfMonitoring::Project::CreateService do describe '#execute' do - let(:result) { subject.execute! } + let(:result) { subject.execute } let(:prometheus_settings) do { @@ -18,10 +18,12 @@ describe Gitlab::DatabaseImporters::SelfMonitoring::Project::CreateService do end context 'without application_settings' do - it 'does not fail' do + it 'returns error' do expect(subject).to receive(:log_error).and_call_original expect(result).to eq( - status: :success + status: :error, + message: 'No application_settings found', + last_step: :validate_application_settings ) expect(Project.count).to eq(0) @@ -36,10 +38,12 @@ describe Gitlab::DatabaseImporters::SelfMonitoring::Project::CreateService do allow(ApplicationSetting).to receive(:current_without_cache) { application_setting } end - it 'does not fail' do + it 'returns error' do expect(subject).to receive(:log_error).and_call_original expect(result).to eq( - status: :success + status: :error, + message: 'No active admin user found', + last_step: :validate_admins ) expect(Project.count).to eq(0) @@ -47,7 +51,7 @@ describe Gitlab::DatabaseImporters::SelfMonitoring::Project::CreateService do end end - context 'with admin users' do + context 'with application settings and admin users' do let(:project) { result[:project] } let(:group) { result[:group] } let(:application_setting) { Gitlab::CurrentSettings.current_application_settings } @@ -73,6 +77,13 @@ describe Gitlab::DatabaseImporters::SelfMonitoring::Project::CreateService do it_behaves_like 'has prometheus service', 'http://localhost:9090' + it "tracks successful install" do + expect(::Gitlab::Tracking).to receive(:event) + expect(::Gitlab::Tracking).to receive(:event).with("self_monitoring", "project_created") + + result + end + it 'creates group' do expect(result[:status]).to eq(:success) expect(group).to be_persisted @@ -132,7 +143,11 @@ describe Gitlab::DatabaseImporters::SelfMonitoring::Project::CreateService do it 'returns error when saving project ID fails' do allow(application_setting).to receive(:save) { false } - expect { result }.to raise_error(StandardError, 'Could not save project ID') + expect(result).to eq( + status: :error, + message: 'Could not save project ID', + last_step: :save_project_id + ) end context 'when project already exists' do @@ -149,9 +164,8 @@ describe Gitlab::DatabaseImporters::SelfMonitoring::Project::CreateService do application_setting.instance_administration_project_id = existing_project.id end - it 'does not fail' do - expect(subject).to receive(:log_error).and_call_original - expect(result[:status]).to eq(:success) + it 'returns success' do + expect(result).to include(status: :success) expect(Project.count).to eq(1) expect(Group.count).to eq(1) @@ -250,7 +264,11 @@ describe Gitlab::DatabaseImporters::SelfMonitoring::Project::CreateService do it 'returns error' do expect(subject).to receive(:log_error).and_call_original - expect { result }.to raise_error(StandardError, 'Could not create project') + expect(result).to eq( + status: :error, + message: 'Could not create project', + last_step: :create_project + ) end end @@ -261,7 +279,11 @@ describe Gitlab::DatabaseImporters::SelfMonitoring::Project::CreateService do it 'returns error' do expect(subject).to receive(:log_error).and_call_original - expect { result }.to raise_error(StandardError, 'Could not add admins as members') + expect(result).to eq( + status: :error, + message: 'Could not add admins as members', + last_step: :add_group_members + ) end end @@ -275,15 +297,13 @@ describe Gitlab::DatabaseImporters::SelfMonitoring::Project::CreateService do it 'returns error' do expect(subject).to receive(:log_error).and_call_original - expect { result }.to raise_error(StandardError, 'Could not save prometheus manual configuration') + expect(result).to eq( + status: :error, + message: 'Could not save prometheus manual configuration', + last_step: :add_prometheus_manual_configuration + ) end end end - - it "tracks successful install" do - expect(Gitlab::Tracking).to receive(:event).with("self_monitoring", "project_created") - - result - end end end diff --git a/spec/models/clusters/applications/ingress_spec.rb b/spec/models/clusters/applications/ingress_spec.rb index d7ad7867e1a..c1158698601 100644 --- a/spec/models/clusters/applications/ingress_spec.rb +++ b/spec/models/clusters/applications/ingress_spec.rb @@ -142,11 +142,11 @@ describe Clusters::Applications::Ingress do let(:project) { build(:project) } let(:cluster) { build(:cluster, projects: [project]) } - context 'when ingress_modsecurity is enabled' do + context 'when modsecurity_enabled is enabled' do before do - stub_feature_flags(ingress_modsecurity: true) - allow(subject).to receive(:cluster).and_return(cluster) + + allow(subject).to receive(:modsecurity_enabled).and_return(true) end it 'includes modsecurity module enablement' do @@ -173,10 +173,8 @@ describe Clusters::Applications::Ingress do end end - context 'when ingress_modsecurity is disabled' do + context 'when modsecurity_enabled is disabled' do before do - stub_feature_flags(ingress_modsecurity: false) - allow(subject).to receive(:cluster).and_return(cluster) end diff --git a/spec/services/clusters/applications/create_service_spec.rb b/spec/services/clusters/applications/create_service_spec.rb index bdacb9ce071..d5ad03a94ac 100644 --- a/spec/services/clusters/applications/create_service_spec.rb +++ b/spec/services/clusters/applications/create_service_spec.rb @@ -47,6 +47,33 @@ describe Clusters::Applications::CreateService do create(:clusters_applications_helm, :installed, cluster: cluster) end + context 'ingress application' do + let(:params) do + { + application: 'ingress', + modsecurity_enabled: true + } + end + + before do + expect_any_instance_of(Clusters::Applications::Ingress) + .to receive(:make_scheduled!) + .and_call_original + end + + it 'creates the application' do + expect do + subject + + cluster.reload + end.to change(cluster, :application_ingress) + end + + it 'sets modsecurity_enabled' do + expect(subject.modsecurity_enabled).to eq(true) + end + end + context 'cert manager application' do let(:params) do { diff --git a/spec/workers/self_monitoring_project_create_worker_spec.rb b/spec/workers/self_monitoring_project_create_worker_spec.rb new file mode 100644 index 00000000000..75c4f5d49d1 --- /dev/null +++ b/spec/workers/self_monitoring_project_create_worker_spec.rb @@ -0,0 +1,28 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe SelfMonitoringProjectCreateWorker do + describe '#perform' do + let(:service_class) { Gitlab::DatabaseImporters::SelfMonitoring::Project::CreateService } + let(:service) { instance_double(service_class) } + + before do + allow(service_class).to receive(:new) { service } + end + + it 'runs the SelfMonitoring::Project::CreateService' do + expect(service).to receive(:execute) + + subject.perform + end + end + + describe '.in_progress?', :clean_gitlab_redis_shared_state do + it 'returns in_progress when job is enqueued' do + jid = described_class.perform_async + + expect(described_class.in_progress?(jid)).to eq(true) + end + end +end