diff --git a/.gitlab/ci/global.gitlab-ci.yml b/.gitlab/ci/global.gitlab-ci.yml index 5d2be0f03ae..3ae7e961aa5 100644 --- a/.gitlab/ci/global.gitlab-ci.yml +++ b/.gitlab/ci/global.gitlab-ci.yml @@ -158,15 +158,6 @@ - *node-modules-cache-push - *assets-cache-push -.use-pg11: - image: "registry.gitlab.com/gitlab-org/gitlab-build-images:ruby-2.7.2.patched-golang-1.16-git-2.31-lfs-2.9-chrome-89-node-14.15-yarn-1.22-postgresql-11-graphicsmagick-1.3.36" - services: - - name: postgres:11.6 - command: ["postgres", "-c", "fsync=off", "-c", "synchronous_commit=off", "-c", "full_page_writes=off"] - - name: redis:5.0-alpine - variables: - POSTGRES_HOST_AUTH_METHOD: trust - .use-pg12: image: "registry.gitlab.com/gitlab-org/gitlab-build-images:ruby-2.7.2.patched-golang-1.16-git-2.31-lfs-2.9-chrome-89-node-14.15-yarn-1.22-postgresql-12-graphicsmagick-1.3.36" services: @@ -176,17 +167,6 @@ variables: POSTGRES_HOST_AUTH_METHOD: trust -.use-pg11-ee: - image: "registry.gitlab.com/gitlab-org/gitlab-build-images:ruby-2.7.2.patched-golang-1.16-git-2.31-lfs-2.9-chrome-89-node-14.15-yarn-1.22-postgresql-11-graphicsmagick-1.3.36" - services: - - name: postgres:11.6 - command: ["postgres", "-c", "fsync=off", "-c", "synchronous_commit=off", "-c", "full_page_writes=off"] - - name: redis:5.0-alpine - - name: elasticsearch:7.11.1 - command: ["elasticsearch", "-E", "discovery.type=single-node"] - variables: - POSTGRES_HOST_AUTH_METHOD: trust - .use-pg12-ee: image: "registry.gitlab.com/gitlab-org/gitlab-build-images:ruby-2.7.2.patched-golang-1.16-git-2.31-lfs-2.9-chrome-89-node-14.15-yarn-1.22-postgresql-12-graphicsmagick-1.3.36" services: diff --git a/.gitlab/ci/rails.gitlab-ci.yml b/.gitlab/ci/rails.gitlab-ci.yml index 3cca098a5c5..a30c4ebd413 100644 --- a/.gitlab/ci/rails.gitlab-ci.yml +++ b/.gitlab/ci/rails.gitlab-ci.yml @@ -58,11 +58,6 @@ - !reference [.base-script, script] - rspec_paralellized_job "--tag ~quarantine --tag ~geo --tag level:migration" -.rspec-base-pg11: - extends: - - .rspec-base - - .use-pg11 - .rspec-base-pg12: extends: - .rspec-base @@ -75,11 +70,6 @@ - .use-pg12 needs: ["setup-test-env", "retrieve-tests-metadata", "compile-test-assets as-if-foss", "detect-tests"] -.rspec-ee-base-pg11: - extends: - - .rspec-base - - .use-pg11-ee - .rspec-ee-base-pg12: extends: - .rspec-base @@ -91,11 +81,6 @@ - !reference [.base-script, script] - rspec_paralellized_job "--tag ~quarantine --tag geo" -.rspec-ee-base-geo-pg11: - extends: - - .rspec-ee-base-geo - - .use-pg11-ee - .rspec-ee-base-geo-pg12: extends: - .rspec-ee-base-geo @@ -298,16 +283,6 @@ rspec system pg12 minimal: - .minimal-rspec-tests - .rails:rules:ee-and-foss-system:minimal -# Dedicated job to test DB library code against PG11. -# Note that these are already tested against PG12 in the `rspec unit pg12` / `rspec-ee unit pg12` jobs. -rspec db-library-code pg11: - extends: - - .rspec-base-pg11 - - .rails:rules:ee-and-foss-db-library-code - script: - - !reference [.base-script, script] - - rspec_db_library_code - rspec fast_spec_helper: extends: - .rspec-base-pg12 @@ -668,80 +643,6 @@ db:rollback geo: # EE: default refs (MRs, default branch, schedules) jobs # ################################################## -########################################## -# EE/FOSS: default branch nightly scheduled jobs # -rspec migration pg11: - extends: - - .rspec-base-pg11 - - .rspec-base-migration - - .rails:rules:default-branch-schedule-nightly--code-backstage - - .rspec-migration-parallel - -rspec unit pg11: - extends: - - .rspec-base-pg11 - - .rails:rules:default-branch-schedule-nightly--code-backstage - - .rspec-unit-parallel - -rspec integration pg11: - extends: - - .rspec-base-pg11 - - .rails:rules:default-branch-schedule-nightly--code-backstage - - .rspec-integration-parallel - -rspec system pg11: - extends: - - .rspec-base-pg11 - - .rails:rules:default-branch-schedule-nightly--code-backstage - - .rspec-system-parallel -# EE/FOSS: default branch nightly scheduled jobs # -########################################## - -##################################### -# EE: default branch nightly scheduled jobs # -rspec-ee migration pg11: - extends: - - .rspec-ee-base-pg11 - - .rspec-base-migration - - .rails:rules:default-branch-schedule-nightly--code-backstage-ee-only - - .rspec-ee-migration-parallel - -rspec-ee unit pg11: - extends: - - .rspec-ee-base-pg11 - - .rails:rules:default-branch-schedule-nightly--code-backstage-ee-only - - .rspec-ee-unit-parallel - -rspec-ee integration pg11: - extends: - - .rspec-ee-base-pg11 - - .rails:rules:default-branch-schedule-nightly--code-backstage-ee-only - - .rspec-ee-integration-parallel - -rspec-ee system pg11: - extends: - - .rspec-ee-base-pg11 - - .rails:rules:default-branch-schedule-nightly--code-backstage-ee-only - - .rspec-ee-system-parallel - -rspec-ee unit pg11 geo: - extends: - - .rspec-ee-base-geo-pg11 - - .rails:rules:default-branch-schedule-nightly--code-backstage-ee-only - - .rspec-ee-unit-geo-parallel - -rspec-ee integration pg11 geo: - extends: - - .rspec-ee-base-geo-pg11 - - .rails:rules:default-branch-schedule-nightly--code-backstage-ee-only - -rspec-ee system pg11 geo: - extends: - - .rspec-ee-base-geo-pg11 - - .rails:rules:default-branch-schedule-nightly--code-backstage-ee-only -# EE: default branch nightly scheduled jobs # -##################################### - ################################################## # EE: Canonical MR pipelines rspec fail-fast: diff --git a/app/assets/javascripts/clusters/forms/components/integration_form.vue b/app/assets/javascripts/clusters/forms/components/integration_form.vue index a344f9578fd..3f61a1b18a7 100644 --- a/app/assets/javascripts/clusters/forms/components/integration_form.vue +++ b/app/assets/javascripts/clusters/forms/components/integration_form.vue @@ -41,17 +41,10 @@ export default { toggleEnabled: true, envScope: '*', baseDomainField: '', - externalIp: '', }; }, computed: { - ...mapState([ - 'enabled', - 'editable', - 'environmentScope', - 'baseDomain', - 'applicationIngressExternalIp', - ]), + ...mapState(['enabled', 'editable', 'environmentScope', 'baseDomain']), canSubmit() { return ( this.enabled !== this.toggleEnabled || @@ -64,7 +57,6 @@ export default { this.toggleEnabled = this.enabled; this.envScope = this.environmentScope; this.baseDomainField = this.baseDomain; - this.externalIp = this.applicationIngressExternalIp; }, }; @@ -135,13 +127,6 @@ export default { {{ content }} -
- {{ s__('ClusterIntegration|Alternatively, ') }} - - - - {{ s__('ClusterIntegration|can be used instead of a custom domain. ') }} -
{ editable: parseBoolean(initialState.editable), environmentScope: initialState.environmentScope, baseDomain: initialState.baseDomain, - applicationIngressExternalIp: initialState.applicationIngressExternalIp, autoDevopsHelpPath: initialState.autoDevopsHelpPath, externalEndpointHelpPath: initialState.externalEndpointHelpPath, }; diff --git a/app/assets/javascripts/pages/users/activity_calendar.js b/app/assets/javascripts/pages/users/activity_calendar.js index c416106fdd8..03dba699461 100644 --- a/app/assets/javascripts/pages/users/activity_calendar.js +++ b/app/assets/javascripts/pages/users/activity_calendar.js @@ -1,4 +1,3 @@ -import { scaleLinear, scaleThreshold } from 'd3-scale'; import { select } from 'd3-selection'; import dateFormat from 'dateformat'; import $ from 'jquery'; @@ -8,7 +7,7 @@ import axios from '~/lib/utils/axios_utils'; import { getDayName, getDayDifference } from '~/lib/utils/datetime_utility'; import { n__, s__, __ } from '~/locale'; -const d3 = { select, scaleLinear, scaleThreshold }; +const d3 = { select }; const firstDayOfWeekChoices = Object.freeze({ sunday: 0, @@ -16,6 +15,14 @@ const firstDayOfWeekChoices = Object.freeze({ saturday: 6, }); +const CONTRIB_LEGENDS = [ + { title: __('No contributions'), min: 0 }, + { title: __('1-9 contributions'), min: 1 }, + { title: __('10-19 contributions'), min: 10 }, + { title: __('20-29 contributions'), min: 20 }, + { title: __('30+ contributions'), min: 30 }, +]; + const LOADING_HTML = `
@@ -42,7 +49,17 @@ function formatTooltipText({ date, count }) { return `${contribText}
${dateDayName} ${dateText}`; } -const initColorKey = () => d3.scaleLinear().range(['#acd5f2', '#254e77']).domain([0, 3]); +// Return the contribution level from the number of contributions +export const getLevelFromContributions = (count) => { + if (count <= 0) { + return 0; + } + + const nextLevel = CONTRIB_LEGENDS.findIndex(({ min }) => count < min); + + // If there is no higher level, we are at the end + return nextLevel >= 0 ? nextLevel - 1 : CONTRIB_LEGENDS.length - 1; +}; export default class ActivityCalendar { constructor( @@ -111,10 +128,6 @@ export default class ActivityCalendar { innerArray.push({ count, date, day }); } - // Init color functions - this.colorKey = initColorKey(); - this.color = this.initColor(); - // Init the svg element this.svg = this.renderSvg(container, group); this.renderDays(); @@ -180,9 +193,7 @@ export default class ActivityCalendar { .attr('y', (stamp) => this.dayYPos(stamp.day)) .attr('width', this.daySize) .attr('height', this.daySize) - .attr('fill', (stamp) => - stamp.count !== 0 ? this.color(Math.min(stamp.count, 40)) : '#ededed', - ) + .attr('data-level', (stamp) => getLevelFromContributions(stamp.count)) .attr('title', (stamp) => formatTooltipText(stamp)) .attr('class', 'user-contrib-cell has-tooltip') .attr('data-html', true) @@ -246,50 +257,24 @@ export default class ActivityCalendar { } renderKey() { - const keyValues = [ - __('No contributions'), - __('1-9 contributions'), - __('10-19 contributions'), - __('20-29 contributions'), - __('30+ contributions'), - ]; - const keyColors = [ - '#ededed', - this.colorKey(0), - this.colorKey(1), - this.colorKey(2), - this.colorKey(3), - ]; - this.svg .append('g') .attr('transform', `translate(18, ${this.daySizeWithSpace * 8 + 16})`) .selectAll('rect') - .data(keyColors) + .data(CONTRIB_LEGENDS) .enter() .append('rect') .attr('width', this.daySize) .attr('height', this.daySize) - .attr('x', (color, i) => this.daySizeWithSpace * i) + .attr('x', (_, i) => this.daySizeWithSpace * i) .attr('y', 0) - .attr('fill', (color) => color) - .attr('class', 'has-tooltip') - .attr('title', (color, i) => keyValues[i]) + .attr('data-level', (_, i) => i) + .attr('class', 'user-contrib-cell has-tooltip contrib-legend') + .attr('title', (x) => x.title) .attr('data-container', 'body') .attr('data-html', true); } - initColor() { - const colorRange = [ - '#ededed', - this.colorKey(0), - this.colorKey(1), - this.colorKey(2), - this.colorKey(3), - ]; - return d3.scaleThreshold().domain([0, 10, 20, 30]).range(colorRange); - } - clickDay(stamp) { if (this.currentSelectedDate !== stamp.date) { this.currentSelectedDate = stamp.date; diff --git a/app/assets/stylesheets/framework/calendar.scss b/app/assets/stylesheets/framework/calendar.scss index 5b7f1a3f38b..1fa03d66f32 100644 --- a/app/assets/stylesheets/framework/calendar.scss +++ b/app/assets/stylesheets/framework/calendar.scss @@ -30,6 +30,16 @@ cursor: pointer; stroke: $black; } + + // `app/assets/javascripts/pages/users/activity_calendar.js` sets this attribute + @for $i from 1 through length($calendar-activity-colors) { + $color: nth($calendar-activity-colors, $i); + $level: $i - 1; + + &[data-level='#{$level}'] { + fill: $color; + } + } } .user-contrib-text { diff --git a/app/assets/stylesheets/framework/variables.scss b/app/assets/stylesheets/framework/variables.scss index eb5cb19facd..d3976cfa8c7 100644 --- a/app/assets/stylesheets/framework/variables.scss +++ b/app/assets/stylesheets/framework/variables.scss @@ -716,6 +716,18 @@ $job-line-number-width: 50px; $job-line-number-margin: 43px; $job-arrow-margin: 55px; +/* + * Calendar + */ +// See https://gitlab.com/gitlab-org/gitlab/-/issues/332150 to align with Pajamas Design System +$calendar-activity-colors: ( + #ededed, + #acd5f2, + #7fa8c9, + #527ba0, + #254e77, +); + /* * Commit Page */ diff --git a/app/finders/ci/auth_job_finder.rb b/app/finders/ci/auth_job_finder.rb index aee7dd16341..d207a522aa8 100644 --- a/app/finders/ci/auth_job_finder.rb +++ b/app/finders/ci/auth_job_finder.rb @@ -15,6 +15,10 @@ module Ci next unless job validate_job!(job) + + if job.user && Feature.enabled?(:ci_scoped_job_token, job.project, default_enabled: :yaml) + job.user.set_ci_job_token_scope!(job) + end end end diff --git a/app/models/ci/job_token/project_scope_link.rb b/app/models/ci/job_token/project_scope_link.rb new file mode 100644 index 00000000000..283ad4a190d --- /dev/null +++ b/app/models/ci/job_token/project_scope_link.rb @@ -0,0 +1,33 @@ +# frozen_string_literal: true + +# The connection between a source project (which defines the job token scope) +# and a target project which is the one allowed to be accessed by the job token. + +module Ci + module JobToken + class ProjectScopeLink < ApplicationRecord + self.table_name = 'ci_job_token_project_scope_links' + + belongs_to :source_project, class_name: 'Project' + belongs_to :target_project, class_name: 'Project' + belongs_to :added_by, class_name: 'User' + + scope :from_project, ->(project) { where(source_project: project) } + scope :to_project, ->(project) { where(target_project: project) } + + validates :source_project, presence: true + validates :target_project, presence: true + validate :not_self_referential_link + + private + + def not_self_referential_link + return unless source_project && target_project + + if source_project == target_project + self.errors.add(:target_project, _("can't be the same as the source project")) + end + end + end + end +end diff --git a/app/models/ci/job_token/scope.rb b/app/models/ci/job_token/scope.rb new file mode 100644 index 00000000000..fb1d5952a4c --- /dev/null +++ b/app/models/ci/job_token/scope.rb @@ -0,0 +1,40 @@ +# frozen_string_literal: true + +# This model represents the surface where a CI_JOB_TOKEN can be used. +# A Scope is initialized with the project that the job token belongs to, +# and indicates what are all the other projects that the token could access. +# +# By default a job token can only access its own project, which is the same +# project that defines the scope. +# By adding ScopeLinks to the scope we can allow other projects to be accessed +# by the job token. This works as an allowlist of projects for a job token. +# +# If a project is not included in the scope we should not allow the job user +# to access it since operations using CI_JOB_TOKEN should be considered untrusted. + +module Ci + module JobToken + class Scope + attr_reader :source_project + + def initialize(project) + @source_project = project + end + + def includes?(target_project) + target_project.id == source_project.id || + Ci::JobToken::ProjectScopeLink.from_project(source_project).to_project(target_project).exists? + end + + def all_projects + Project.from_union([ + Project.id_in(source_project), + Project.where_exists( + Ci::JobToken::ProjectScopeLink + .from_project(source_project) + .where('projects.id = ci_job_token_project_scope_links.target_project_id')) + ], remove_duplicates: false) + end + end + end +end diff --git a/app/models/hooks/web_hook_log_archived.rb b/app/models/hooks/web_hook_log_archived.rb deleted file mode 100644 index a1c8a44f5ba..00000000000 --- a/app/models/hooks/web_hook_log_archived.rb +++ /dev/null @@ -1,12 +0,0 @@ -# frozen_string_literal: true - -# This model is not intended to be used. -# It is a temporary reference to the old non-partitioned -# web_hook_logs table. -# Please refer to https://gitlab.com/groups/gitlab-org/-/epics/5558 -# for details. -# rubocop:disable Gitlab/NamespacedClass: This is a temporary class with no relevant namespace -# WebHook, WebHookLog and all hooks are defined outside of a namespace -class WebHookLogArchived < ApplicationRecord - self.table_name = 'web_hook_logs_archived' -end diff --git a/app/models/user.rb b/app/models/user.rb index 7a6bc50639c..8ee0421e45f 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -1923,6 +1923,20 @@ class User < ApplicationRecord confirmed? && !blocked? && !ghost? end + # This attribute hosts a Ci::JobToken::Scope object which is set when + # the user is authenticated successfully via CI_JOB_TOKEN. + def ci_job_token_scope + Gitlab::SafeRequestStore[ci_job_token_scope_cache_key] + end + + def set_ci_job_token_scope!(job) + Gitlab::SafeRequestStore[ci_job_token_scope_cache_key] = Ci::JobToken::Scope.new(job.project) + end + + def from_ci_job_token? + ci_job_token_scope.present? + end + protected # override, from Devise::Validatable @@ -2086,6 +2100,10 @@ class User < ApplicationRecord def update_highest_role_attribute id end + + def ci_job_token_scope_cache_key + "users:#{id}:ci:job_token_scope" + end end User.prepend_mod_with('User') diff --git a/app/policies/concerns/policy_actor.rb b/app/policies/concerns/policy_actor.rb index 08a26da6673..cbc34bdeed3 100644 --- a/app/policies/concerns/policy_actor.rb +++ b/app/policies/concerns/policy_actor.rb @@ -84,6 +84,10 @@ module PolicyActor def password_expired? false end + + def from_ci_job_token? + false + end end PolicyActor.prepend_mod_with('PolicyActor') diff --git a/app/policies/project_policy.rb b/app/policies/project_policy.rb index df06f0a95fe..164d73fe332 100644 --- a/app/policies/project_policy.rb +++ b/app/policies/project_policy.rb @@ -75,6 +75,11 @@ class ProjectPolicy < BasePolicy user.is_a?(DeployToken) && user.has_access_to?(project) && user.write_package_registry end + desc "If user is authenticated via CI job token then the target project should be in scope" + condition(:project_allowed_for_job_token) do + !@user&.from_ci_job_token? || @user.ci_job_token_scope.includes?(project) + end + with_scope :subject condition(:forking_allowed) do @subject.feature_available?(:forking, @user) @@ -508,6 +513,8 @@ class ProjectPolicy < BasePolicy enable :read_project_for_iids end + rule { ~project_allowed_for_job_token }.prevent_all + rule { can?(:public_access) }.policy do enable :read_package enable :read_project diff --git a/app/views/clusters/clusters/show.html.haml b/app/views/clusters/clusters/show.html.haml index 8e27b6e5c75..654fcab8bf1 100644 --- a/app/views/clusters/clusters/show.html.haml +++ b/app/views/clusters/clusters/show.html.haml @@ -27,9 +27,6 @@ provider_type: @cluster.provider_type, pre_installed_knative: @cluster.knative_pre_installed? ? 'true': 'false', help_path: help_page_path('user/project/clusters/index.md'), - helm_help_path: help_page_path('user/clusters/applications.md', anchor: 'helm'), - ingress_help_path: help_page_path('user/clusters/applications.md', anchor: 'determining-the-external-endpoint-automatically'), - ingress_dns_help_path: help_page_path('user/clusters/applications.md', anchor: 'pointing-your-dns-at-the-external-endpoint'), environments_help_path: help_page_path('ci/environments/index.md', anchor: 'create-a-static-environment'), 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.md', anchor: 'enabling-deploy-boards'), @@ -51,13 +48,20 @@ = render 'banner' + .gl-alert.gl-alert-warning{ role: 'alert' } + = sprite_icon('warning', css_class: "gl-alert-icon gl-alert-icon-no-title gl-icon") + %button.js-close.gl-alert-dismiss{ type: 'button', 'aria-label' => _('Dismiss'), data: { testid: 'dismiss-one-click-application-removal' } } + = sprite_icon('close', css_class: 'gl-icon') + .gl-alert-body + = s_('ClusterApplicationsRemoved|One-click application management was removed in GitLab 14.0. Your applications are still installed in your cluster, and integrations continue working.') + = link_to _('More information.'), help_page_path("user/clusters/applications"), target: '_blank' + - if cluster_created?(@cluster) .js-toggle-container %ul.nav-links.mobile-separator.nav.nav-tabs{ role: 'tablist' } = render 'details_tab' = render_if_exists 'clusters/clusters/environments_tab' = render 'clusters/clusters/health_tab' - = render 'applications_tab' = render 'integrations_tab' = render 'advanced_settings_tab' diff --git a/config/feature_flags/development/ci_scoped_job_token.yml b/config/feature_flags/development/ci_scoped_job_token.yml new file mode 100644 index 00000000000..a7fa0244839 --- /dev/null +++ b/config/feature_flags/development/ci_scoped_job_token.yml @@ -0,0 +1,8 @@ +--- +name: ci_scoped_job_token +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/62733 +rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/332272 +milestone: '14.0' +type: development +group: group::pipeline execution +default_enabled: false diff --git a/db/migrate/20210527194558_create_ci_job_token_project_scope_links.rb b/db/migrate/20210527194558_create_ci_job_token_project_scope_links.rb new file mode 100644 index 00000000000..aaa94b9a231 --- /dev/null +++ b/db/migrate/20210527194558_create_ci_job_token_project_scope_links.rb @@ -0,0 +1,24 @@ +# frozen_string_literal: true + +class CreateCiJobTokenProjectScopeLinks < ActiveRecord::Migration[6.0] + include Gitlab::Database::MigrationHelpers + + def up + with_lock_retries do + create_table :ci_job_token_project_scope_links, if_not_exists: true do |t| + t.belongs_to :source_project, index: false, null: false, foreign_key: { to_table: :projects, on_delete: :cascade } + t.belongs_to :target_project, null: false, foreign_key: { to_table: :projects, on_delete: :cascade } + t.belongs_to :added_by, foreign_key: { to_table: :users, on_delete: :nullify } + t.datetime_with_timezone :created_at, null: false + + t.index [:source_project_id, :target_project_id], unique: true, name: 'i_ci_job_token_project_scope_links_on_source_and_target_project' + end + end + end + + def down + with_lock_retries do + drop_table :ci_job_token_project_scope_links, if_exists: true + end + end +end diff --git a/db/post_migrate/20210609125005_drop_non_partitioned_web_hook_logs.rb b/db/post_migrate/20210609125005_drop_non_partitioned_web_hook_logs.rb new file mode 100644 index 00000000000..596b643e079 --- /dev/null +++ b/db/post_migrate/20210609125005_drop_non_partitioned_web_hook_logs.rb @@ -0,0 +1,43 @@ +# frozen_string_literal: true + +class DropNonPartitionedWebHookLogs < ActiveRecord::Migration[6.1] + include Gitlab::Database::MigrationHelpers + include Gitlab::Database::PartitioningMigrationHelpers::TableManagementHelpers + + DOWNTIME = false + + def up + drop_nonpartitioned_archive_table(:web_hook_logs) + end + + def down + execute(<<~SQL) + CREATE TABLE web_hook_logs_archived ( + id integer NOT NULL, + web_hook_id integer NOT NULL, + trigger character varying, + url character varying, + request_headers text, + request_data text, + response_headers text, + response_body text, + response_status character varying, + execution_duration double precision, + internal_error_message character varying, + created_at timestamp without time zone NOT NULL, + updated_at timestamp without time zone NOT NULL + ); + + ALTER TABLE ONLY web_hook_logs_archived ADD CONSTRAINT web_hook_logs_archived_pkey PRIMARY KEY (id); + + CREATE INDEX index_web_hook_logs_on_created_at_and_web_hook_id ON web_hook_logs_archived USING btree (created_at, web_hook_id); + CREATE INDEX index_web_hook_logs_on_web_hook_id ON web_hook_logs_archived USING btree (web_hook_id); + + ALTER TABLE ONLY web_hook_logs_archived ADD CONSTRAINT fk_rails_666826e111 FOREIGN KEY (web_hook_id) REFERENCES web_hooks(id) ON DELETE CASCADE; + SQL + + with_lock_retries do + create_trigger_to_sync_tables(:web_hook_logs, :web_hook_logs_archived, 'id') + end + end +end diff --git a/db/schema_migrations/20210527194558 b/db/schema_migrations/20210527194558 new file mode 100644 index 00000000000..089b97aa90e --- /dev/null +++ b/db/schema_migrations/20210527194558 @@ -0,0 +1 @@ +8c0661a42edbdb79be283df0e88879707ef34ba3fe21b6756b21cd99ea9f05de \ No newline at end of file diff --git a/db/schema_migrations/20210609125005 b/db/schema_migrations/20210609125005 new file mode 100644 index 00000000000..3e26956bdb8 --- /dev/null +++ b/db/schema_migrations/20210609125005 @@ -0,0 +1 @@ +de8468173d8a7499d03b84913cf071af8842a2f47d5f85908af20bf7c71dc96b \ No newline at end of file diff --git a/db/structure.sql b/db/structure.sql index 93b8c8029a8..49563bdb895 100644 --- a/db/structure.sql +++ b/db/structure.sql @@ -41,62 +41,6 @@ RETURN NULL; END $$; -CREATE FUNCTION table_sync_function_29bc99d6db() RETURNS trigger - LANGUAGE plpgsql - AS $$ -BEGIN -IF (TG_OP = 'DELETE') THEN - DELETE FROM web_hook_logs_archived where id = OLD.id; -ELSIF (TG_OP = 'UPDATE') THEN - UPDATE web_hook_logs_archived - SET web_hook_id = NEW.web_hook_id, - trigger = NEW.trigger, - url = NEW.url, - request_headers = NEW.request_headers, - request_data = NEW.request_data, - response_headers = NEW.response_headers, - response_body = NEW.response_body, - response_status = NEW.response_status, - execution_duration = NEW.execution_duration, - internal_error_message = NEW.internal_error_message, - created_at = NEW.created_at, - updated_at = NEW.updated_at - WHERE web_hook_logs_archived.id = NEW.id; -ELSIF (TG_OP = 'INSERT') THEN - INSERT INTO web_hook_logs_archived (id, - web_hook_id, - trigger, - url, - request_headers, - request_data, - response_headers, - response_body, - response_status, - execution_duration, - internal_error_message, - created_at, - updated_at) - VALUES (NEW.id, - NEW.web_hook_id, - NEW.trigger, - NEW.url, - NEW.request_headers, - NEW.request_data, - NEW.response_headers, - NEW.response_body, - NEW.response_status, - NEW.execution_duration, - NEW.internal_error_message, - NEW.created_at, - NEW.updated_at); -END IF; -RETURN NULL; - -END -$$; - -COMMENT ON FUNCTION table_sync_function_29bc99d6db() IS 'Partitioning migration: table sync for web_hook_logs table'; - CREATE FUNCTION trigger_07c94931164e() RETURNS trigger LANGUAGE plpgsql AS $$ @@ -10777,6 +10721,23 @@ CREATE SEQUENCE ci_job_artifacts_id_seq ALTER SEQUENCE ci_job_artifacts_id_seq OWNED BY ci_job_artifacts.id; +CREATE TABLE ci_job_token_project_scope_links ( + id bigint NOT NULL, + source_project_id bigint NOT NULL, + target_project_id bigint NOT NULL, + added_by_id bigint, + created_at timestamp with time zone NOT NULL +); + +CREATE SEQUENCE ci_job_token_project_scope_links_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + +ALTER SEQUENCE ci_job_token_project_scope_links_id_seq OWNED BY ci_job_token_project_scope_links.id; + CREATE TABLE ci_job_variables ( id bigint NOT NULL, key character varying NOT NULL, @@ -19351,22 +19312,6 @@ CREATE SEQUENCE vulnerability_user_mentions_id_seq ALTER SEQUENCE vulnerability_user_mentions_id_seq OWNED BY vulnerability_user_mentions.id; -CREATE TABLE web_hook_logs_archived ( - id integer NOT NULL, - web_hook_id integer NOT NULL, - trigger character varying, - url character varying, - request_headers text, - request_data text, - response_headers text, - response_body text, - response_status character varying, - execution_duration double precision, - internal_error_message character varying, - created_at timestamp without time zone NOT NULL, - updated_at timestamp without time zone NOT NULL -); - CREATE SEQUENCE web_hook_logs_id_seq START WITH 1 INCREMENT BY 1 @@ -19699,6 +19644,8 @@ ALTER TABLE ONLY ci_instance_variables ALTER COLUMN id SET DEFAULT nextval('ci_i ALTER TABLE ONLY ci_job_artifacts ALTER COLUMN id SET DEFAULT nextval('ci_job_artifacts_id_seq'::regclass); +ALTER TABLE ONLY ci_job_token_project_scope_links ALTER COLUMN id SET DEFAULT nextval('ci_job_token_project_scope_links_id_seq'::regclass); + ALTER TABLE ONLY ci_job_variables ALTER COLUMN id SET DEFAULT nextval('ci_job_variables_id_seq'::regclass); ALTER TABLE ONLY ci_minutes_additional_packs ALTER COLUMN id SET DEFAULT nextval('ci_minutes_additional_packs_id_seq'::regclass); @@ -20881,6 +20828,9 @@ ALTER TABLE ONLY ci_instance_variables ALTER TABLE ONLY ci_job_artifacts ADD CONSTRAINT ci_job_artifacts_pkey PRIMARY KEY (id); +ALTER TABLE ONLY ci_job_token_project_scope_links + ADD CONSTRAINT ci_job_token_project_scope_links_pkey PRIMARY KEY (id); + ALTER TABLE ONLY ci_job_variables ADD CONSTRAINT ci_job_variables_pkey PRIMARY KEY (id); @@ -22129,9 +22079,6 @@ ALTER TABLE ONLY vulnerability_statistics ALTER TABLE ONLY vulnerability_user_mentions ADD CONSTRAINT vulnerability_user_mentions_pkey PRIMARY KEY (id); -ALTER TABLE ONLY web_hook_logs_archived - ADD CONSTRAINT web_hook_logs_archived_pkey PRIMARY KEY (id); - ALTER TABLE ONLY web_hook_logs ADD CONSTRAINT web_hook_logs_pkey PRIMARY KEY (id, created_at); @@ -22335,6 +22282,8 @@ CREATE INDEX finding_evidences_on_vulnerability_occurrence_id ON vulnerability_f CREATE INDEX finding_links_on_vulnerability_occurrence_id ON vulnerability_finding_links USING btree (vulnerability_occurrence_id); +CREATE UNIQUE INDEX i_ci_job_token_project_scope_links_on_source_and_target_project ON ci_job_token_project_scope_links USING btree (source_project_id, target_project_id); + CREATE INDEX idx_analytics_devops_adoption_segments_on_namespace_id ON analytics_devops_adoption_segments USING btree (namespace_id); CREATE INDEX idx_audit_events_part_on_entity_id_desc_author_id_created_at ON ONLY audit_events USING btree (entity_id, entity_type, id DESC, author_id, created_at); @@ -22815,6 +22764,10 @@ CREATE INDEX index_ci_job_artifacts_on_project_id ON ci_job_artifacts USING btre CREATE INDEX index_ci_job_artifacts_on_project_id_for_security_reports ON ci_job_artifacts USING btree (project_id) WHERE (file_type = ANY (ARRAY[5, 6, 7, 8])); +CREATE INDEX index_ci_job_token_project_scope_links_on_added_by_id ON ci_job_token_project_scope_links USING btree (added_by_id); + +CREATE INDEX index_ci_job_token_project_scope_links_on_target_project_id ON ci_job_token_project_scope_links USING btree (target_project_id); + CREATE INDEX index_ci_job_variables_on_job_id ON ci_job_variables USING btree (job_id); CREATE UNIQUE INDEX index_ci_job_variables_on_key_and_job_id ON ci_job_variables USING btree (key, job_id); @@ -24973,10 +24926,6 @@ CREATE UNIQUE INDEX index_vulns_user_mentions_on_vulnerability_id ON vulnerabili CREATE UNIQUE INDEX index_vulns_user_mentions_on_vulnerability_id_and_note_id ON vulnerability_user_mentions USING btree (vulnerability_id, note_id); -CREATE INDEX index_web_hook_logs_on_created_at_and_web_hook_id ON web_hook_logs_archived USING btree (created_at, web_hook_id); - -CREATE INDEX index_web_hook_logs_on_web_hook_id ON web_hook_logs_archived USING btree (web_hook_id); - CREATE INDEX index_web_hook_logs_part_on_created_at_and_web_hook_id ON ONLY web_hook_logs USING btree (created_at, web_hook_id); CREATE INDEX index_web_hook_logs_part_on_web_hook_id ON ONLY web_hook_logs USING btree (web_hook_id); @@ -25359,8 +25308,6 @@ ALTER INDEX product_analytics_events_experimental_pkey ATTACH PARTITION gitlab_p ALTER INDEX product_analytics_events_experimental_pkey ATTACH PARTITION gitlab_partitions_static.product_analytics_events_experimental_63_pkey; -CREATE TRIGGER table_sync_trigger_b99eb6998c AFTER INSERT OR DELETE OR UPDATE ON web_hook_logs FOR EACH ROW EXECUTE FUNCTION table_sync_function_29bc99d6db(); - CREATE TRIGGER trigger_07c94931164e BEFORE INSERT OR UPDATE ON push_event_payloads FOR EACH ROW EXECUTE FUNCTION trigger_07c94931164e(); CREATE TRIGGER trigger_21e7a2602957 BEFORE INSERT OR UPDATE ON ci_build_needs FOR EACH ROW EXECUTE FUNCTION trigger_21e7a2602957(); @@ -26493,6 +26440,9 @@ ALTER TABLE ONLY metrics_dashboard_annotations ALTER TABLE ONLY wiki_page_slugs ADD CONSTRAINT fk_rails_358b46be14 FOREIGN KEY (wiki_page_meta_id) REFERENCES wiki_page_meta(id) ON DELETE CASCADE; +ALTER TABLE ONLY ci_job_token_project_scope_links + ADD CONSTRAINT fk_rails_35f7f506ce FOREIGN KEY (added_by_id) REFERENCES users(id) ON DELETE SET NULL; + ALTER TABLE ONLY board_labels ADD CONSTRAINT fk_rails_362b0600a3 FOREIGN KEY (label_id) REFERENCES labels(id) ON DELETE CASCADE; @@ -26628,6 +26578,9 @@ ALTER TABLE ONLY upcoming_reconciliations ALTER TABLE ONLY ci_pipeline_artifacts ADD CONSTRAINT fk_rails_4a70390ca6 FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE; +ALTER TABLE ONLY ci_job_token_project_scope_links + ADD CONSTRAINT fk_rails_4b2ee3290b FOREIGN KEY (source_project_id) REFERENCES projects(id) ON DELETE CASCADE; + ALTER TABLE ONLY group_deletion_schedules ADD CONSTRAINT fk_rails_4b8c694a6c FOREIGN KEY (group_id) REFERENCES namespaces(id) ON DELETE CASCADE; @@ -26823,9 +26776,6 @@ ALTER TABLE ONLY operations_feature_flags_clients ALTER TABLE ONLY namespace_admin_notes ADD CONSTRAINT fk_rails_666166ea7b FOREIGN KEY (namespace_id) REFERENCES namespaces(id) ON DELETE CASCADE; -ALTER TABLE ONLY web_hook_logs_archived - ADD CONSTRAINT fk_rails_666826e111 FOREIGN KEY (web_hook_id) REFERENCES web_hooks(id) ON DELETE CASCADE; - ALTER TABLE ONLY analytics_cycle_analytics_project_value_streams ADD CONSTRAINT fk_rails_669f4ba293 FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE; @@ -26844,6 +26794,9 @@ ALTER TABLE ONLY vulnerability_finding_evidence_headers ALTER TABLE ONLY geo_hashed_storage_migrated_events ADD CONSTRAINT fk_rails_687ed7d7c5 FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE; +ALTER TABLE ONLY ci_job_token_project_scope_links + ADD CONSTRAINT fk_rails_6904b38465 FOREIGN KEY (target_project_id) REFERENCES projects(id) ON DELETE CASCADE; + ALTER TABLE ONLY plan_limits ADD CONSTRAINT fk_rails_69f8b6184f FOREIGN KEY (plan_id) REFERENCES plans(id) ON DELETE CASCADE; diff --git a/doc/ci/runners/configure_runners.md b/doc/ci/runners/configure_runners.md index f0fd4aba8b8..775de26b772 100644 --- a/doc/ci/runners/configure_runners.md +++ b/doc/ci/runners/configure_runners.md @@ -142,8 +142,9 @@ different places. To view the IP address of a shared runner you must have admin access to the GitLab instance. To determine this: -1. Visit **Admin Area > Overview > Runners**. -1. Look for the runner in the table and you should see a column for **IP Address**. +1. On the top bar, select **Menu >** **{admin}** **Admin**. +1. On the left sidebar, select **Overview > Runners**. +1. Find the runner in the table and view the **IP Address** column. ![shared runner IP address](img/shared_runner_ip_address.png) diff --git a/doc/development/architecture.md b/doc/development/architecture.md index 215b8fad92b..67d5c02799b 100644 --- a/doc/development/architecture.md +++ b/doc/development/architecture.md @@ -215,7 +215,7 @@ click NodeExporter "./architecture.html#node-exporter" ### Component legend - ✅ - Installed by default -- ⚙ - Requires additional configuration, or GitLab Managed Apps +- ⚙ - Requires additional configuration - ⤓ - Manual installation required - ❌ - Not supported or no instructions available - N/A - Not applicable diff --git a/doc/development/pipelines.md b/doc/development/pipelines.md index a3c9d77f573..367fa659150 100644 --- a/doc/development/pipelines.md +++ b/doc/development/pipelines.md @@ -480,11 +480,7 @@ because of 2 reasons: ### PostgreSQL versions testing Our test suite runs against PG12 as GitLab.com runs on PG12 and -[Omnibus defaults to PG12 for new installs and upgrades](https://docs.gitlab.com/omnibus/package-information/postgresql_versions.html), -Our test suite is currently running against PG11, since GitLab.com still runs on PG11. - -We do run our test suite against PG11 on nightly scheduled pipelines as well as upon specific -database library changes in MRs and `master` pipelines (with the `rspec db-library-code pg11` job). +[Omnibus defaults to PG12 for new installs and upgrades](https://docs.gitlab.com/omnibus/package-information/postgresql_versions.html). #### Current versions testing @@ -502,7 +498,6 @@ We follow the [PostgreSQL versions shipped with Omnibus GitLab](https://docs.git | PostgreSQL version | 13.11 (April 2021) | 13.12 (May 2021) | 14.0 (June 2021?) | | -------------------| ---------------------- | ---------------------- | ---------------------- | | PG12 | `nightly` | MRs/`2-hour`/`nightly` | MRs/`2-hour`/`nightly` | -| PG11 | MRs/`2-hour`/`nightly` | `nightly` | `nightly` | ### Test jobs @@ -730,8 +725,6 @@ that are scoped to a single [configuration keyword](../ci/yaml/README.md#job-key | `.qa-cache` | Allows a job to use a default `cache` definition suitable for QA tasks. | | `.yarn-cache` | Allows a job to use a default `cache` definition suitable for frontend jobs that do a `yarn install`. | | `.assets-compile-cache` | Allows a job to use a default `cache` definition suitable for frontend jobs that compile assets. | -| `.use-pg11` | Allows a job to run the `postgres` 11 and `redis` services (see [`.gitlab/ci/global.gitlab-ci.yml`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/.gitlab/ci/global.gitlab-ci.yml) for the specific versions of the services). | -| `.use-pg11-ee` | Same as `.use-pg11` but also use an `elasticsearch` service (see [`.gitlab/ci/global.gitlab-ci.yml`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/.gitlab/ci/global.gitlab-ci.yml) for the specific version of the service). | | `.use-pg12` | Allows a job to use the `postgres` 12 and `redis` services (see [`.gitlab/ci/global.gitlab-ci.yml`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/.gitlab/ci/global.gitlab-ci.yml) for the specific versions of the services). | | `.use-pg12-ee` | Same as `.use-pg12` but also use an `elasticsearch` service (see [`.gitlab/ci/global.gitlab-ci.yml`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/.gitlab/ci/global.gitlab-ci.yml) for the specific version of the service). | | `.use-kaniko` | Allows a job to use the `kaniko` tool to build Docker images. | diff --git a/doc/operations/index.md b/doc/operations/index.md index c2f3ce0a427..d170e82dd7f 100644 --- a/doc/operations/index.md +++ b/doc/operations/index.md @@ -73,7 +73,7 @@ production services. GitLab provides centralized, aggregated log storage for you distributed application, enabling you to collect logs across multiple services and infrastructure. -- [View logs of pods or managed applications](../user/project/clusters/kubernetes_pod_logs.md) +- [View logs of pods](../user/project/clusters/kubernetes_pod_logs.md) in connected Kubernetes clusters. ## Manage your infrastructure in code diff --git a/doc/operations/metrics/dashboards/index.md b/doc/operations/metrics/dashboards/index.md index 7b056020a99..95075e2b0e4 100644 --- a/doc/operations/metrics/dashboards/index.md +++ b/doc/operations/metrics/dashboards/index.md @@ -217,8 +217,46 @@ links: ## Troubleshooting -When troubleshooting issues with a managed Prometheus app, it is often useful to -[view the Prometheus UI](../../../user/project/integrations/prometheus.md#access-the-ui-of-a-prometheus-managed-application-in-kubernetes). +### Accessing the UI of Prometheus in Kubernetes + +When troubleshooting issues with an in-cluster Prometheus, it can help to +view the Prometheus UI. In the example below, we assume the Prometheus +server to be the pod `prometheus-prometheus-server` in the `gitlab-managed-apps` +namespace: + +1. Find the name of the Prometheus pod in the user interface of your Kubernetes + provider, such as GKE, or by running the following `kubectl` command in your + terminal. For example: + + ```shell + kubectl get pods -n gitlab-managed-apps | grep 'prometheus-prometheus-server' + ``` + + The command should return a result like the following example, where + `prometheus-prometheus-server-55b4bd64c9-dpc6b` is the name of the Prometheus pod: + + ```plaintext + gitlab-managed-apps prometheus-prometheus-server-55b4bd64c9-dpc6b 2/2 Running 0 71d + ``` + +1. Run a `kubectl port-forward` command. In the following example, `9090` is the + Prometheus server's listening port: + + ```shell + kubectl port-forward prometheus-prometheus-server-55b4bd64c9-dpc6b 9090:9090 -n gitlab-managed-apps + ``` + + The `port-forward` command forwards all requests sent to your system's `9090` port + to the `9090` port of the Prometheus pod. If the `9090` port on your system is used + by another application, you can change the port number before the colon to your + desired port. For example, to forward port `8080` of your local system, change the + command to: + + ```shell + kubectl port-forward prometheus-prometheus-server-55b4bd64c9-dpc6b 8080:9090 -n gitlab-managed-apps + ``` + +1. Open `localhost:9090` in your browser to display the Prometheus user interface. ### "No data found" error on Metrics dashboard page diff --git a/doc/operations/metrics/index.md b/doc/operations/metrics/index.md index 8ff45ac4015..8151bf97bd9 100644 --- a/doc/operations/metrics/index.md +++ b/doc/operations/metrics/index.md @@ -16,10 +16,10 @@ critical. For GitLab to display your information in charts, you must: For an overview, see [How to instrument Prometheus metrics in GitLab](https://www.youtube.com/watch?v=tuI2oJ3TTB4). 1. **Expose metrics for capture** - Make logs, metrics, and traces available for capture. 1. [**Configure Prometheus to gather metrics**](#configure-prometheus-to-gather-metrics) - - Deploy managed applications like Elasticsearch, Prometheus, and Jaeger to gather + Use applications like Elasticsearch, Prometheus, and Jaeger to gather the data you've exposed. 1. **GitLab collects metrics** - GitLab uses Prometheus to scrape the data you've - captured in your managed apps, and prepares the data for display. To learn more, read + captured in your applications, and prepares the data for display. To learn more, read [Collect and process metrics](#collect-and-process-metrics). 1. **Display charts in the GitLab user interface** - GitLab converts your metrics into easy-to-read charts on a default dashboard. You can create as many custom charts @@ -34,30 +34,10 @@ your Prometheus integration depends on where your apps are running: - **For manually-configured Prometheus** - [Specify your Prometheus server](../../user/project/integrations/prometheus.md#manual-configuration-of-prometheus), and define at least one environment. -- **For GitLab-managed Prometheus** - GitLab can - [deploy and manage Prometheus](../../user/project/integrations/prometheus.md#managed-prometheus-on-kubernetes) for you. - You must also complete a code deployment, as described in - [Deploy code with GitLab-managed Prometheus](#deploy-code-with-gitlab-managed-prometheus), - for the **Operations > Metrics** page to contain data. - -### Deploy code with GitLab-managed Prometheus - -For GitLab-managed Prometheus, you can set up [Auto DevOps](../../topics/autodevops/index.md) -to quickly create a deployment: - -1. Navigate to your project's **Operations > Kubernetes** page. -1. Ensure that, in addition to Prometheus, you also have GitLab Runner and Ingress - installed. -1. After installing Ingress, copy its endpoint. -1. Navigate to your project's **Settings > CI/CD** page. In the - **Auto DevOps** section, select a deployment strategy and save your changes. -1. On the same page, in the **Variables** section, add a variable named - `KUBE_INGRESS_BASE_DOMAIN` with the value of the Ingress endpoint you - copied previously. Leave the type as **Variable**. -1. Navigate to your project's **{rocket}** **CI/CD > Pipelines** page, and run a - pipeline on any branch. -1. When the pipeline has run successfully, graphs are available on the - **Operations > Metrics** page. +- **For a cluster integrated Prometheus** - GitLab can query + [an in-cluster Prometheus](../../user/clusters/integrations.md#prometheus-cluster-integration). + You must also complete a code deployment to your cluster for the **Operations > Metrics** + page to contain data. You can do this using [Auto DevOps](../../topics/autodevops/quick_start_guide.md). ![Monitoring Dashboard](img/prometheus_monitoring_dashboard_v13_3.png) diff --git a/doc/topics/autodevops/requirements.md b/doc/topics/autodevops/requirements.md index f39f33c7730..ccffb5913f5 100644 --- a/doc/topics/autodevops/requirements.md +++ b/doc/topics/autodevops/requirements.md @@ -66,8 +66,7 @@ To make full use of Auto DevOps with Kubernetes, you need: Runners should be registered as [shared runners](../../ci/runners/runners_scope.md#shared-runners) for the entire GitLab instance, or [specific runners](../../ci/runners/runners_scope.md#specific-runners) - that are assigned to specific projects (the default if you've installed the - GitLab Runner managed application). + that are assigned to specific projects. - **Prometheus** (for [Auto Monitoring](stages.md#auto-monitoring)) diff --git a/doc/user/clusters/applications.md b/doc/user/clusters/applications.md index 495767cea69..f712dcace73 100644 --- a/doc/user/clusters/applications.md +++ b/doc/user/clusters/applications.md @@ -6,12 +6,15 @@ info: To determine the technical writer assigned to the Stage/Group associated w # GitLab Managed Apps (DEPRECATED) **(FREE)** +NOTE: +The new recommended way to manage cluster applications is to use the [cluster management project template](management_project_template.md). +If you want to migrate your GitLab managed apps management to this template, reference to [migrating from GitLab managed apps to project template](migrating_from_gma_to_project_template.md). + **GitLab Managed Apps** was created to help you configure applications in your cluster directly from GitLab. You could use this feature through two different methods: "one-click install" and "CI/CD template". Both methods are **deprecated**: -- The **one-click install** method was deprecated in GitLab 13.9 and **will be - removed** in GitLab 14.0. +- The **one-click install** method was [removed](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/63348) in GitLab 14.0. - The **CI/CD template method** was deprecated in GitLab 13.12 and is scheduled to be removed in GitLab 15.0. @@ -19,15 +22,6 @@ Both methods were limiting as you couldn't fully customize your third-party apps through GitLab Managed Apps. Therefore, we decided to deprecate this feature and provide better [GitOps-driven alternatives](https://about.gitlab.com/direction/configure/kubernetes_management/#gitlab-managed-applications) to our users, such as [cluster integrations](integrations.md#cluster-integrations) and [cluster management project](management_project.md). -Read the sections below according to the installation method you chose to -learn how to proceed to keep your apps up and running: - -- [One-click install method](#install-with-one-click-deprecated) -- [CI/CD template method](#install-using-gitlab-cicd-deprecated) - -NOTE: -Despite being deprecated, the recommended way for installing GitLab integrated applications is by the GitLab CI/CD method presented below. We are working on a [cluster management project template](https://gitlab.com/gitlab-org/gitlab/-/issues/327908) with a simple upgrade path from the CI/CD based method. - ## Install using GitLab CI/CD (DEPRECATED) > - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/20822) in GitLab 12.6. @@ -37,8 +31,8 @@ WARNING: The GitLab Managed Apps CI/CD installation method was [deprecated in 13.12](https://gitlab.com/gitlab-org/gitlab/-/issues/327908). Your applications continue to work. However, we no longer support and maintain the GitLab CI/CD template for Managed Apps (`Managed-Cluster-Applications.gitlab-ci.yml`). -As a replacement, we are working on a [cluster management project template](https://gitlab.com/gitlab-org/gitlab/-/issues/327908), -still to be released. +The new recommended way to manage cluster applications is to use the [cluster management project template](management_project_template.md). +If you want to migrate your GitLab managed apps management to this template, reference to [migrating from GitLab managed apps to project template](migrating_from_gma_to_project_template.md). The CI/CD template was the primary method for installing applications to clusters via GitLab Managed Apps and customize them through Helm. @@ -821,11 +815,6 @@ management project. Refer to the [chart](https://gitlab.com/gitlab-org/charts/elastic-stack) for all available configuration options. -NOTE: -In this alpha implementation of installing Elastic Stack through CI, reading the -environment logs through Elasticsearch is unsupported. This is supported if -[installed with the UI](#elastic-stack). - Support for installing the Elastic Stack managed application is provided by the GitLab APM group. If you run into unknown issues, [open a new issue](https://gitlab.com/gitlab-org/gitlab/-/issues/new), and ping at @@ -1033,6 +1022,17 @@ GitLab Container Security group. If you run into unknown issues, at least 2 people from the [Container Security group](https://about.gitlab.com/handbook/product/categories/#container-security-group). +## Install with one click (REMOVED) + +> [Removed](https://gitlab.com/groups/gitlab-org/-/epics/4280) in GitLab 14.0. + +The one-click installation method was deprecated in GitLab 13.9 and removed in [GitLab 14.0](https://gitlab.com/groups/gitlab-org/-/epics/4280). +The removal does not break nor uninstall any apps you have installed, it only +removes the "Applications" tab from the cluster page. +Follow the process to [take ownership of your GitLab Managed Apps](#take-ownership-of-your-gitlab-managed-apps). + +If you are not yet on GitLab 14.0 or later, you can refer to [an older version of this document](https://docs.gitlab.com/13.12/ee/user/clusters/applications.html#install-with-one-click-deprecated). + ## Browse applications logs > [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/36769) in GitLab 13.2. @@ -1040,558 +1040,11 @@ at least 2 people from the Logs produced by pods running **GitLab Managed Apps** can be browsed using [**Log Explorer**](../project/clusters/kubernetes_pod_logs.md). -## Install with one click (DEPRECATED) - -WARNING: -The one-click installation method was deprecated in GitLab 13.9 and will be removed in [GitLab 14.0](https://gitlab.com/groups/gitlab-org/-/epics/4280). -The removal does not break nor uninstall any apps you have installed but removes the GitLab UI page -for installing and updating your GitLab Managed Apps. -Follow the process to [take ownership of your GitLab Managed Apps](#take-ownership-of-your-gitlab-managed-apps). - -Applications managed by GitLab are installed onto the `gitlab-managed-apps` -namespace. This namespace: - -- Is different from the namespace used for project deployments. -- Is created once. -- Has a non-configurable name. - -To view a list of available applications to install for a: - -- [Project-level cluster](../project/clusters/index.md), navigate to your project's - **Operations > Kubernetes**. -- [Group-level cluster](../group/clusters/index.md), navigate to your group's - **Kubernetes** page. - -You can install the following applications with one click: - -- [Helm](#helm) -- [Ingress](#ingress) -- [cert-manager](#cert-manager) -- [Prometheus](#prometheus) -- [GitLab Runner](#gitlab-runner) -- [JupyterHub](#jupyterhub) -- [Knative](#knative) -- [Crossplane](#crossplane) -- [Elastic Stack](#elastic-stack) - -With the exception of Knative, the applications are installed in a dedicated -namespace called `gitlab-managed-apps`. - -Some applications are installable only for a project-level cluster. -Support for installing these applications in a group-level cluster is -planned for future releases. -For updates, see the [issue tracking progress](https://gitlab.com/gitlab-org/gitlab/-/issues/24411). - -WARNING: -If you have an existing Kubernetes cluster with Helm already installed, -you should be careful as GitLab cannot detect it. In this case, installing -Helm with the applications results in the cluster having it twice, which -can lead to confusion during deployments. - -In GitLab versions 11.6 and greater, Helm is upgraded to the latest version -supported by GitLab before installing any of the applications. - -### Helm - -> - Introduced in GitLab 10.2 for project-level clusters. -> - Introduced in GitLab 11.6 for group-level clusters. -> - [Uses a local Tiller](https://gitlab.com/gitlab-org/gitlab/-/issues/209736) in GitLab 13.2 and later. -> - [Uses Helm 3](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/46267) for clusters created with GitLab 13.6 and later. -> - [Offers legacy Tiller removal](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/47457) in GitLab 13.7 and later. - -[Helm](https://helm.sh/docs/) is a package manager for Kubernetes and is -used to install the GitLab-managed apps. GitLab runs each `helm` command -in a pod in the `gitlab-managed-apps` namespace inside the cluster. - -- For clusters created in GitLab 13.6 and newer, GitLab uses Helm 3 to manage - applications. -- For clusters created on versions of GitLab prior to 13.6, GitLab uses Helm 2 - with a local [Tiller](https://v2.helm.sh/docs/glossary/#tiller) server. Prior - to [GitLab 13.2](https://gitlab.com/gitlab-org/gitlab/-/issues/209736), GitLab - used an in-cluster Tiller server in the `gitlab-managed-apps` namespace. You - can safely uninstall the server from the GitLab application page if you have - previously installed it. This doesn't affect your other applications. - -The GitLab Helm integration does not support installing applications behind a proxy, -but a [workaround](../../topics/autodevops/index.md#install-applications-behind-a-proxy) -is available. - -#### Upgrade a cluster to Helm 3 - -GitLab does not offer a way to migrate existing application management -on existing clusters from Helm 2 to Helm 3. To migrate a cluster to Helm 3: - -1. Uninstall all applications on your cluster. -1. [Remove the cluster integration](../project/clusters/add_remove_clusters.md#removing-integration). -1. [Re-add the cluster](../project/clusters/add_remove_clusters.md#existing-kubernetes-cluster) as - an existing cluster. - -### cert-manager - -> Introduced in GitLab 11.6 for project- and group-level clusters. - -[cert-manager](https://cert-manager.io/docs/) is a native Kubernetes certificate -management controller that helps with issuing certificates. Installing -cert-manager on your cluster issues a certificate by [Let's Encrypt](https://letsencrypt.org/) -and ensures that certificates are valid and up-to-date. - -The chart used to install this application depends on the version of GitLab used. In: - -- GitLab 12.3 and newer, the [`jetstack/cert-manager`](https://github.com/jetstack/cert-manager) - chart is used with a - [`values.yaml`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/vendor/cert_manager/values.yaml) - file. -- GitLab 12.2 and older, the - [`stable/cert-manager`](https://github.com/helm/charts/tree/master/stable/cert-manager) - chart was used. - -If you installed cert-manager prior to GitLab 12.3, Let's Encrypt -[blocks requests](https://community.letsencrypt.org/t/blocking-old-cert-manager-versions/98753) -from older versions of `cert-manager`. To resolve this: - -1. [Back up any additional configuration](https://cert-manager.io/docs/tutorials/backup/). -1. Uninstall cert-manager. -1. Install cert-manager again. - -### GitLab Runner - -> - Introduced in GitLab 10.6 for project-level clusters. -> - Introduced in GitLab 11.10 for group-level clusters. - -[GitLab Runner](https://docs.gitlab.com/runner/) is the open source project that -is used to run your jobs and send the results back to GitLab. It's used in -conjunction with [GitLab CI/CD](../../ci/README.md), the open-source continuous -integration service included with GitLab that coordinates the jobs. - -If the project is on GitLab.com, [shared runners](../gitlab_com/index.md#shared-runners) -are available. You don't have to deploy one if they are enough for your -needs. If a project-specific runner is desired, or there are no shared runners, -you can deploy one. - -The deployed runner is set as **privileged**. Root access to the underlying -server is required to build Docker images, so it's the default. Be sure to read -the [security implications](../project/clusters/index.md#security-implications) -before deploying one. - -The [`runner/gitlab-runner`](https://gitlab.com/gitlab-org/charts/gitlab-runner) -chart is used to install this application, using -[a preconfigured `values.yaml`](https://gitlab.com/gitlab-org/charts/gitlab-runner/-/blob/main/values.yaml) -file. Customizing the installation by modifying this file is not supported. This -also means you cannot modify `config.toml` file for this Runner. If you want to -have that possibility and still deploy Runner in Kubernetes, consider using the -[Cluster management project](management_project.md) or installing Runner manually -via [GitLab Runner Helm Chart](https://docs.gitlab.com/runner/install/kubernetes.html). - -### Ingress - -> - Introduced in GitLab 10.2 for project-level clusters. -> - Introduced in GitLab 11.6 for group-level clusters. - -[Ingress](https://kubernetes.io/docs/concepts/services-networking/ingress/) -provides load balancing, SSL termination, and name-based virtual hosting -out of the box. It acts as a web proxy for your applications and is useful -if you want to use [Auto DevOps](../../topics/autodevops/index.md) or deploy your own web apps. - -The Ingress Controller installed is -[Ingress-NGINX](https://kubernetes.io/docs/concepts/services-networking/ingress/), -which is supported by the Kubernetes community. - -With the following procedure, a load balancer must be installed in your cluster -to obtain the endpoint. You can use either -Ingress, or Knative's own load balancer ([Istio](https://istio.io)) if using Knative. - -To publish your web application, you first need to find the endpoint, which is either an IP -address or a hostname associated with your load balancer. - -To install it, click on the **Install** button for Ingress. GitLab attempts -to determine the external endpoint and it should be available in a few minutes. - -#### Determining the external endpoint automatically - -> [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/17052) in GitLab 10.6. - -After you install Ingress, the external endpoint should be available in a few minutes. - -NOTE: -This endpoint can be used for the -[Auto DevOps base domain](../../topics/autodevops/index.md#auto-devops-base-domain) -using the `KUBE_INGRESS_BASE_DOMAIN` environment variable. - -If the endpoint doesn't appear and your cluster runs on Google Kubernetes Engine: - -1. [Examine your Kubernetes cluster](https://console.cloud.google.com/kubernetes) - on Google Kubernetes Engine to ensure there are no errors on its nodes. -1. Ensure you have enough [Quotas](https://console.cloud.google.com/iam-admin/quotas) - on Google Kubernetes Engine. For more information, see - [Resource Quotas](https://cloud.google.com/compute/quotas). -1. Review [Google Cloud's Status](https://status.cloud.google.com/) for service - disruptions. - -The [`stable/nginx-ingress`](https://github.com/helm/charts/tree/master/stable/nginx-ingress) -chart is used to install this application with a -[`values.yaml`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/vendor/ingress/values.yaml) -file. - -After installing, you may see a `?` for **Ingress IP Address** depending on the -cloud provider. For EKS specifically, this is because the ELB is created -with a DNS name, not an IP address. If GitLab is still unable to -determine the endpoint of your Ingress or Knative application, you can -[determine it manually](#determining-the-external-endpoint-manually). - -#### Determining the external endpoint manually - -See the [Base domain section](../project/clusters/index.md#base-domain) for a -guide on how to determine the external endpoint manually. - -#### Using a static IP - -By default, an ephemeral external IP address is associated to the cluster's load -balancer. If you associate the ephemeral IP with your DNS and the IP changes, -your apps aren't reachable, and you'd have to change the DNS record again. -To avoid that, change it into a static reserved IP. - -Read how to [promote an ephemeral external IP address in GKE](https://cloud.google.com/compute/docs/ip-addresses/reserve-static-external-ip-address#promote_ephemeral_ip). - -#### Pointing your DNS at the external endpoint - -After you have set up the external endpoint, associate it with a -[wildcard DNS record](https://en.wikipedia.org/wiki/Wildcard_DNS_record) (such -as `*.example.com.`) to reach your apps. If your external endpoint is an IP -address, use an A record. If your external endpoint is a hostname, use a CNAME -record. - -### JupyterHub - -> - Introduced in GitLab 11.0 for project-level clusters. -> - Introduced in GitLab 12.3 for group and instance-level clusters. - -[JupyterHub](https://jupyterhub.readthedocs.io/en/stable/) is a multi-user service -for managing notebooks across a team. [Jupyter Notebooks](https://jupyter-notebook.readthedocs.io/en/latest/) -provide a web-based interactive programming environment used for data analysis, -visualization, and machine learning. - -The [`jupyter/jupyterhub`](https://jupyterhub.github.io/helm-chart/) -chart is used to install this application with a -[`values.yaml`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/vendor/jupyter/values.yaml) -file. - -Authentication is enabled only for [project members](../project/members/index.md) -for project-level clusters and group members for group-level clusters with -[Developer or higher](../permissions.md) access to the associated project or group. - -GitLab uses a [custom Jupyter image](https://gitlab.com/gitlab-org/jupyterhub-user-image/blob/master/Dockerfile) -that installs additional relevant packages on top of the base Jupyter. Ready-to-use -DevOps Runbooks built with Nurtch's [Rubix library](https://github.com/Nurtch/rubix) -are also available. - -More information on creating executable runbooks can be found in -[our Runbooks documentation](../project/clusters/runbooks/index.md#configure-an-executable-runbook-with-gitlab). -Ingress must be installed and have an IP address assigned before -JupyterHub can be installed. - -#### Jupyter Git Integration - -> - [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/28783) in GitLab 12.0 for project-level clusters. -> - [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/32512) in GitLab 12.3 for group and instance-level clusters. - -When installing JupyterHub onto your Kubernetes cluster, -[JupyterLab's Git extension](https://github.com/jupyterlab/jupyterlab-git) -is provisioned and configured using the authenticated user's: - -- Name. -- Email. -- Newly created access token. - -JupyterLab's Git extension enables full version control of your notebooks, and -issuance of Git commands in Jupyter. You can issue Git commands through the -**Git** tab on the left panel, or through Jupyter's command-line prompt. - -JupyterLab's Git extension stores the user token in the JupyterHub DB in encrypted -format, and in the single user Jupyter instance as plain text, because -[Git requires storing credentials as plain text](https://git-scm.com/docs/git-credential-store) -Potentially, if a nefarious user finds a way to read from the file system in the -single-user Jupyter instance, they could retrieve the token. - -![Jupyter's Git Extension](img/jupyter-git-extension.gif) - -You can clone repositories from the files tab in Jupyter: - -![Jupyter clone repository](img/jupyter-gitclone.png) - -### Knative - -> - Introduced in GitLab 11.5 for project-level clusters. -> - Introduced in GitLab 12.3 for group- and instance-level clusters. - -[Knative](https://cloud.google.com/knative/) provides a platform to -create, deploy, and manage serverless workloads from a Kubernetes -cluster. It's used in conjunction with, and includes -[Istio](https://istio.io) to provide an external IP address for all -programs hosted by Knative. - -The [`knative/knative`](https://storage.googleapis.com/triggermesh-charts) -chart is used to install this application. - -During installation, you must enter a wildcard domain where your applications -are exposed. Configure your DNS server to use the external IP address for that -domain. Applications created and installed are accessible as -`..`, which requires -your Kubernetes cluster to have -[RBAC enabled](../project/clusters/add_remove_clusters.md#rbac-cluster-resources). - -### Prometheus - -> - Introduced in GitLab 10.4 for project-level clusters. -> - Introduced in GitLab 11.11 for group-level clusters. - -[Prometheus](https://prometheus.io/docs/introduction/overview/) is an -open-source monitoring and alerting system you can use to supervise your -deployed applications. - -GitLab is able to monitor applications by using the -[Prometheus integration](../project/integrations/prometheus.md). Kubernetes container CPU and -memory metrics are collected, and response metrics are also retrieved -from NGINX Ingress. - -The [`stable/prometheus`](https://github.com/helm/charts/tree/master/stable/prometheus) -chart is used to install this application with a -[`values.yaml`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/vendor/prometheus/values.yaml) -file. - -To enable monitoring, install Prometheus into the cluster with the **Install** -button. - -### Crossplane - -> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/34702) in GitLab 12.5 for project-level clusters. - -[Crossplane](https://crossplane.github.io/docs/v0.9/) is a multi-cloud control plane -to help you manage applications and infrastructure across multiple clouds. It extends the -Kubernetes API using: - -- Custom resources. -- Controllers that watch those custom resources. - -Crossplane allows provisioning and lifecycle management of infrastructure components -across cloud providers in a uniform manner by abstracting cloud provider-specific -configurations. - -The Crossplane GitLab-managed application: - -- Installs Crossplane with a provider of choice on a Kubernetes cluster attached to the - project repository. -- Can then be used to provision infrastructure or managed applications such as - PostgreSQL (for example, CloudSQL from GCP or RDS from AWS) and other services - required by the application with the Auto DevOps pipeline. - -[`alpha/crossplane`](https://github.com/crossplane/crossplane/tree/v0.4.1/cluster/charts/crossplane) chart v0.4.1 is used to -install Crossplane using the -[`values.yaml`](https://github.com/crossplane/crossplane/blob/master/cluster/charts/crossplane/values.yaml.tmpl) -file. - -For information about configuring Crossplane installed on the cluster, see -[Crossplane configuration](crossplane.md). - -### Elastic Stack - -> Introduced in GitLab 12.7 for project- and group-level clusters. - -[Elastic Stack](https://www.elastic.co/elastic-stack) is a complete end-to-end -log analysis solution which helps in deep searching, analyzing and visualizing the logs -generated from different machines. - -GitLab can gather logs from pods in your cluster. Filebeat runs as a DaemonSet -on each node in your cluster, and ships container logs to Elasticsearch for -querying. GitLab then connects to Elasticsearch for logs, instead of the -Kubernetes API, giving you access to more advanced querying capabilities. Log -data is deleted after 30 days, using [Curator](https://www.elastic.co/guide/en/elasticsearch/client/curator/5.5/about.html). - -The Elastic Stack cluster application is intended as a log aggregation solution -and is not related to our [Advanced Search](../search/advanced_search.md) -functionality, which uses a separate Elasticsearch cluster. - -To enable log shipping: - -1. Ensure your cluster contains at least three nodes of instance types larger - than `f1-micro`, `g1-small`, or `n1-standard-1`. -1. Navigate to **Operations > Kubernetes**. -1. In **Kubernetes Cluster**, select a cluster. -1. In the **Applications** section, find **Elastic Stack**, and then select - **Install**. - -The [`gitlab/elastic-stack`](https://gitlab.com/gitlab-org/charts/elastic-stack) -chart is used to install this application with a -[`values.yaml`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/vendor/elastic_stack/values.yaml) -file. The chart deploys three identical Elasticsearch pods which can't be -colocated, and each requires one CPU and 2 GB of RAM, making them -incompatible with clusters containing fewer than three nodes, or consisting of -`f1-micro`, `g1-small`, `n1-standard-1`, or `*-highcpu-2` instance types. - -#### Optional: deploy Kibana to perform advanced queries - -If you are an advanced user and have direct access to your Kubernetes cluster -using `kubectl` and `helm`, you can deploy Kibana manually. The following assumes -that `helm` has been [initialized](https://v2.helm.sh/docs/helm/) with `helm init`. - -Save the following to `kibana.yml`: - -```yaml -elasticsearch: - enabled: false - -filebeat: - enabled: false - -kibana: - enabled: true - elasticsearchHosts: http://elastic-stack-elasticsearch-master.gitlab-managed-apps.svc.cluster.local:9200 -``` - -Then install it on your cluster: - -```shell -helm repo add gitlab https://charts.gitlab.io -helm install --name kibana gitlab/elastic-stack --values kibana.yml -``` - -To access Kibana, forward the port to your local machine: - -```shell -kubectl port-forward svc/kibana-kibana 5601:5601 -``` - -Then, you can visit Kibana at `http://localhost:5601`. - -## Upgrading applications - -> [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/24789) in GitLab 11.8. - -The applications below can be upgraded. - -| Application | GitLab version | -| ----------- | -------------- | -| GitLab Runner | 11.8+ | - -To upgrade an application: - -1. For a: - - [Project-level cluster](../project/clusters/index.md), - navigate to your project's **Operations > Kubernetes**. - - [Group-level cluster](../group/clusters/index.md), - navigate to your group's **Kubernetes** page. -1. Select your cluster. -1. If an upgrade is available, the **Upgrade** button is displayed. Click the button to upgrade. - -Upgrades reset values back to the values built into the `runner` chart, plus the values set by -[`values.yaml`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/vendor/runner/values.yaml) - -## Uninstalling applications - -> [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/60665) in GitLab 11.11. - -The applications below can be uninstalled. - -| Application | GitLab version | Notes | -| ----------- | -------------- | ----- | -| cert-manager | 12.2+ | The associated private key is deleted and cannot be restored. Deployed applications continue to use HTTPS, but certificates aren't renewed. Before uninstalling, you may want to [back up your configuration](https://cert-manager.io/docs/tutorials/backup/) or [revoke your certificates](https://letsencrypt.org/docs/revoking/). | -| GitLab Runner | 12.2+ | Any running pipelines are canceled. | -| Helm | 12.2+ | The associated Tiller pod, the `gitlab-managed-apps` namespace, and all of its resources are deleted and cannot be restored. | -| Ingress | 12.1+ | The associated load balancer and IP are deleted and cannot be restored. Furthermore, it can only be uninstalled if JupyterHub is not installed. | -| JupyterHub | 12.1+ | All data not committed to GitLab are deleted and cannot be restored. | -| Knative | 12.1+ | The associated IP are deleted and cannot be restored. | -| Prometheus | 11.11+ | All data are deleted and cannot be restored. | -| Crossplane | 12.5+ | All data are deleted and cannot be restored. | -| Elastic Stack | 12.7+ | All data are deleted and cannot be restored. | -| Sentry | 12.6+ | The PostgreSQL persistent volume remains and should be manually removed for complete uninstall. | - -To uninstall an application: - -1. For a: - - [Project-level cluster](../project/clusters/index.md), - navigate to your project's **Operations > Kubernetes**. - - [Group-level cluster](../group/clusters/index.md), - navigate to your group's **Kubernetes** page. -1. Select your cluster. -1. Click the **Uninstall** button for the application. - -Support for uninstalling all applications is planned for progressive rollout. -To follow progress, see the [relevant epic](https://gitlab.com/groups/gitlab-org/-/epics/1201). - -## Troubleshooting applications - -Applications can fail with the following error: - -```plaintext -Error: remote error: tls: bad certificate -``` - -To avoid installation errors: - -- Before starting the installation of applications, make sure that time is synchronized - between your GitLab server and your Kubernetes cluster. -- Ensure certificates are not out of sync. When installing applications, GitLab - expects a new cluster with no previous installation of Helm. - - You can confirm that the certificates match by using `kubectl`: - - ```shell - kubectl get configmaps/values-content-configuration-ingress -n gitlab-managed-apps -o \ - "jsonpath={.data['cert\.pem']}" | base64 -d > a.pem - kubectl get secrets/tiller-secret -n gitlab-managed-apps -o "jsonpath={.data['ca\.crt']}" | base64 -d > b.pem - diff a.pem b.pem - ``` - -### Error installing managed apps on EKS cluster - -If you're using a managed cluster on AWS EKS, and you are not able to install some of the managed -apps, consider checking the logs. - -You can check the logs by running the following commands: - -```shell -kubectl get pods --all-namespaces -kubectl get services --all-namespaces -``` - -If you are getting the `Failed to assign an IP address to container` error, it's probably due to the -instance type you've specified in the AWS configuration. -The number and size of nodes might not have enough IP addresses to run or install those pods. - -For reference, all the AWS instance IP limits are found -[in this AWS repository on GitHub](https://github.com/aws/amazon-vpc-cni-k8s/blob/master/pkg/awsutils/vpc_ip_resource_limit.go) (search for `InstanceENIsAvailable`). - -### Unable to install Prometheus - -Installing Prometheus is failing with the following error: - -```shell -# kubectl -n gitlab-managed-apps logs install-prometheus -... -Error: Could not get apiVersions from Kubernetes: unable to retrieve the complete list of server APIs: admission.certmanager.k8s.io/v1beta1: the server is currently unable to handle the request -``` - -This is a bug that was introduced in Helm `2.15` and fixed in `3.0.2`. As a workaround, -ensure [`cert-manager`](#cert-manager) is installed successfully prior to installing Prometheus. - -### Unable to create a Persistent Volume Claim with DigitalOcean - -Trying to create additional block storage volumes might lead to the following error when using DigitalOcean: - -```plaintext -Server requested -[Warning] pod has unbound immediate PersistentVolumeClaims (repeated 2 times) -[Normal] pod didn't trigger scale-up (it wouldn't fit if a new node is added): -Spawn failed: Timeout -``` - -This is due to DigitalOcean imposing a few limits with regards to creating additional block storage volumes. -[Learn more about DigitalOcean Block Storage Volumes limits.](https://www.digitalocean.com/docs/volumes/#limits) - ## Take ownership of your GitLab Managed Apps > [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/327803) in GitLab 13.12. -With the removal of the [One-click install method](#install-with-one-click-deprecated) in GitLab 14.0, +With the removal of the One-click install method in GitLab 14.0, the **Applications** tab (under your project's **Operations > Kubernetes**) will no longer be displayed: @@ -1664,10 +1117,10 @@ If you choose to keep using Helm v2 (B), follow the steps below to manage your a ### Cluster integrations -Some applications were not only installed in your cluster by GitLab through Managed Apps but were also -directly integrated with GitLab so that you could benefit from seeing, controlling, or getting notified -about them through GitLab. -To keep them integrated, read the documentation for: +Some applications were not only installed in your cluster by GitLab through +Managed Apps but were also directly integrated with GitLab. If you had one of +these applications installed before GitLab 14.0, then a corresponding [cluster +integration](integrations.md) has been automatically enabled: - [Prometheus cluster integration](integrations.md#prometheus-cluster-integration) - [Elastic Stack cluster integration](integrations.md#elastic-stack-cluster-integration) diff --git a/doc/user/clusters/crossplane.md b/doc/user/clusters/crossplane.md index bdf9d582b93..8906d1224b1 100644 --- a/doc/user/clusters/crossplane.md +++ b/doc/user/clusters/crossplane.md @@ -6,7 +6,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w # Crossplane configuration **(FREE)** -After [installing](applications.md#crossplane) Crossplane, you must configure it for use. +After [installing](applications.md#install-crossplane-using-gitlab-cicd) Crossplane, you must configure it for use. The process of configuring Crossplane includes: 1. [Configure RBAC permissions](#configure-rbac-permissions). diff --git a/doc/user/clusters/integrations.md b/doc/user/clusters/integrations.md index a8b181f8726..8abdfdeb7fd 100644 --- a/doc/user/clusters/integrations.md +++ b/doc/user/clusters/integrations.md @@ -14,6 +14,17 @@ To enable cluster integrations, first add a Kubernetes cluster to a GitLab [group](../group/clusters/index.md#group-level-kubernetes-clusters) or [instance](../instance/clusters/index.md). +You can install your applications manually as shown in the following sections, or use the +[Cluster management project template](management_project_template.md) that automates the +installation. + +Although, the [Cluster management project template](management_project_template.md) still +requires that you manually do the last steps of these sections, +[Enable Prometheus integration for your cluster](#enable-prometheus-integration-for-your-cluster) +or [Enable Elastic Stack integration for your cluster](#enable-elastic-stack-integration-for-your-cluster) +depending on which application you are installing. We plan to also automate this step in the future, +see the [opened issue](https://gitlab.com/gitlab-org/gitlab/-/issues/326565). + ## Prometheus cluster integration > [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/55244) in GitLab 13.11. @@ -43,10 +54,8 @@ it up using [Helm](https://helm.sh/) as follows: kubectl create ns gitlab-managed-apps # Download Helm chart values that is compatible with the requirements above. -# You should substitute the tag that corresponds to the GitLab version in the URL -# - https://gitlab.com/gitlab-org/gitlab/-/raw//vendor/prometheus/values.yaml -# -wget https://gitlab.com/gitlab-org/gitlab/-/raw/v13.9.0-ee/vendor/prometheus/values.yaml +# These are included in the Cluster Management project template. +wget https://gitlab.com/gitlab-org/project-templates/cluster-management/-/raw/master/applications/prometheus/values.yaml # Add the Prometheus community Helm chart repository helm repo add prometheus-community https://prometheus-community.github.io/helm-charts @@ -103,10 +112,8 @@ running: kubectl create namespace gitlab-managed-apps # Download Helm chart values that is compatible with the requirements above. -# You should substitute the tag that corresponds to the GitLab version in the URL -# - https://gitlab.com/gitlab-org/gitlab/-/raw//vendor/elastic_stack/values.yaml -# -wget https://gitlab.com/gitlab-org/gitlab/-/raw/v13.9.0-ee/vendor/elastic_stack/values.yaml +# These are included in the Cluster Management project template. +wget https://gitlab.com/gitlab-org/project-templates/cluster-management/-/raw/master/applications/elastic-stack/values.yaml # Add the GitLab Helm chart repository helm repo add gitlab https://charts.gitlab.io diff --git a/doc/user/clusters/management_project.md b/doc/user/clusters/management_project.md index 0a24bca67a5..eca9721796d 100644 --- a/doc/user/clusters/management_project.md +++ b/doc/user/clusters/management_project.md @@ -16,7 +16,7 @@ privileges. This can be useful for: -- Creating pipelines to install cluster-wide applications into your cluster, see [Install using GitLab CI/CD (beta)](applications.md#install-using-gitlab-cicd-deprecated) for details. +- Creating pipelines to install cluster-wide applications into your cluster, see [management project template](management_project_template.md) for details. - Any jobs that require `cluster-admin` privileges. ## Permissions diff --git a/doc/user/clusters/management_project_template.md b/doc/user/clusters/management_project_template.md new file mode 100644 index 00000000000..52390cb18b0 --- /dev/null +++ b/doc/user/clusters/management_project_template.md @@ -0,0 +1,86 @@ +--- +stage: Configure +group: Configure +info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments +--- + +# Cluster Management Project Template **(FREE)** + +> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/25318) in GitLab 12.10 with Helmfile support via Helm v2. +> - [Improved](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/63577) in GitLab 14.0 with Helmfile support via Helm v3 instead, and a much more flexible usage of Helmfile. This introduces breaking changes that are detailed below. + +This [GitLab built-in project template](../project/working_with_projects.md#built-in-templates) +provides a quicker start for users interested in managing cluster +applications via [Helm v3](https://helm.sh/) charts. More specifically, taking advantage of the +[Helmfile](https://github.com/roboll/helmfile) utility client. The template consists of some pre-configured apps that +should help you get started quickly using various GitLab features. Still, you have all the flexibility to remove the ones you do not +need, or even add new ones that are not built-in. + +## How to use this template + +1. Create a new project, choosing "GitLab Cluster Management" from the list of [built-in project templates](../project/working_with_projects.md#built-in-templates). +1. Make this project a [cluster management project](management_project.md). +1. If you used the [GitLab Managed Apps](applications.md), refer to + [Migrating from GitLab Manged Apps](migrating_from_gma_to_project_template.md). + +### Components + +In the repository of the newly-created project, you will find: + +- A predefined [`.gitlab-ci.yml`](https://gitlab.com/gitlab-org/project-templates/cluster-management/-/blob/master/.gitlab-ci.yml) + file, with a CI pipeline already configured. +- A main [`helmfile.yaml`](https://gitlab.com/gitlab-org/project-templates/cluster-management/-/blob/master/helmfile.yaml) to toggle which applications you would like to manage. +- An `applications` directory with a `helmfile.yaml` configured for each application GitLab provides. + +#### The `.gitlab-ci.yml` file + +The base image used in your pipeline is built by the [cluster-applications](https://gitlab.com/gitlab-org/cluster-integration/cluster-applications) +project. This image consists of a set of Bash utility scripts to support [Helm v3 releases](https://helm.sh/docs/intro/using_helm/#three-big-concepts): + +- `gl-fail-if-helm2-releases-exist {namespace}`: It tries to detect whether you have apps deployed through Helm v2 + releases for a given namespace. If so, it will fail the pipeline and ask you to manually + [migrate your Helm v2 releases to Helm v3](https://helm.sh/docs/topics/v2_v3_migration/). +- `gl-ensure-namespace {namespace}`: It creates the given namespace if it does not exist and adds the necessary label + for the [Cilium](https://github.com/cilium/cilium/) app network policies to work. +- `gl-adopt-resource-with-helm-v3 {arguments}`: Used only internally in the [cert-manager's](https://cert-manager.io/) Helmfile to + facilitate the GitLab Managed Apps adoption. +- `gl-adopt-crds-with-helm-v3 {arguments}`: Used only internally in the [cert-manager's](https://cert-manager.io/) Helmfile to + facilitate the GitLab Managed Apps adoption. +- `gl-helmfile {arguments}`: A thin wrapper that triggers the [Helmfile](https://github.com/roboll/helmfile) command. + +#### The main `helmfile.yml` file + +This file has a list of paths to other Helmfiles for each app. They're all commented out by default, so you must uncomment +the paths for the apps that you would like to manage. + +By default, each `helmfile.yaml` in these sub-paths will have the attribute `installed: true`, which signifies that everytime +the pipeline runs, Helmfile will try to either install or update your apps according to the current state of your +cluster and Helm releases. If you change this attribute to `installed: false`, Helmfile will try to uninstall this app +from your cluster. [Read more](https://github.com/roboll/helmfile) about how Helmfile works. + +Furthermore, each app has an `applications/{app}/values.yaml` file. This is the +place where you can define some default values for your app's Helm chart. Some apps will already have defaults +pre-defined by GitLab. + +#### Built-in applications + +The built-in applications are intended to provide an easy way to get started with various Kubernetes oriented GitLab features. + +The [built-in supported applications](https://gitlab.com/gitlab-org/project-templates/cluster-management/-/tree/master/applications) are: + +- Apparmor +- Cert-manager +- Cilium +- Elastic Stack +- Falco +- Fluentd +- GitLab Runner +- Ingress +- Prometheus +- Sentry +- Vault + +### Migrating from GitLab Managed Apps + +If you had GitLab Managed Apps, either One-Click or CI/CD install, read the docs on how to +[migrate from GitLab Managed Apps to project template](migrating_from_gma_to_project_template.md) diff --git a/doc/user/clusters/migrating_from_gma_to_project_template.md b/doc/user/clusters/migrating_from_gma_to_project_template.md new file mode 100644 index 00000000000..7fa6ccea433 --- /dev/null +++ b/doc/user/clusters/migrating_from_gma_to_project_template.md @@ -0,0 +1,95 @@ +--- +stage: Configure +group: Configure +info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments +--- + +# Migrating from GitLab Managed Apps to a management project template + +The [GitLab Managed Apps](applications.md) are deprecated in GitLab 14.0. To migrate to the new way of managing them: + +1. Read how the [management project template](management_project_template.md) works, and + create a new project based on the "GitLab Cluster Management" template. +1. Create a new project as explained in the [management project template](management_project_template.md). +1. Detect apps deployed through Helm v2 releases by using the pre-configured [`.gitlab-ci.yml`](management_project_template.md#the-gitlab-ciyml-file) file: + - In case you had overwritten the default GitLab Managed Apps namespace, edit `.gitlab-ci.yml`, + and make sure the script is receiving the correct namespace as an argument: + + ```yaml + script: + - gl-fail-if-helm2-releases-exist + ``` + + - If you kept the default name (`gitlab-managed-apps`), then the script is already + set up. + + Either way, [run a pipeline manually](../../ci/pipelines/index.md#run-a-pipeline-manually) and read the logs of the + `detect-helm2-releases` job to know if you have any Helm v2 releases and which are they. + +1. If you have no Helm v2 releases, skip this step. Otherwise, follow the official Helm docs on + [how to migrate from Helm v2 to Helm v3](https://helm.sh/blog/migrate-from-helm-v2-to-helm-v3/), + and clean up the Helm v2 releases after you are confident that they have been successfully migrated. + +1. In this step you should already have only Helm v3 releases. + Uncomment from the main [`./helmfile.yaml`](management_project_template.md#the-main-helmfileyml-file) the paths for the + applications that you would like to manage with this project. Although you could uncomment all the ones you want to + managed at once, we recommend you repeat the following steps separately for each app, so you do not get lost during + the process. +1. Edit the associated `applications/{app}/helmfiles.yaml` to match the chart version currently deployed + for your app. Take a GitLab Runner Helm v3 release as an example: + + The following command lists the releases and their versions: + + ```shell + helm ls -n gitlab-managed-apps + + NAME NAMESPACE REVISION UPDATED STATUS CHART APP VERSION + runner gitlab-managed-apps 1 2021-06-09 19:36:55.739141644 +0000 UTC deployed gitlab-runner-0.28.0 13.11.0 + ``` + + Take the version from the `CHART` column which is in the format `{release}-v{chart_version}`, + then edit the `version:` attribute in the `./applications/gitlab-runner/helmfile.yaml`, so that it matches the version + you have currently deployed. This is a safe step to avoid upgrading versions during this migration. + Make sure you replace `gitlab-managed-apps` from the above command if you have your apps deployed to a different + namespace. + +1. Edit the `applications/{app}/values.yaml` associated with your app to match the currently + deployed values. For example, for GitLab Runner: + + 1. Copy the output of the following command (it might be big): + + ```shell + helm get values runner -n gitlab-managed-apps -a --output yaml + ``` + + 1. Overwrite `applications/gitlab-runner/values.yaml` with the output of the previous command. + + This safe step will guarantee that no unexpected default values overwrite your currently deployed values. + For instance, your GitLab Runner could have its `gitlabUrl` or `runnerRegistrationToken` overwritten by mistake. + +1. Some apps require special attention: + + - Ingress: Due to an existing [chart issue](https://github.com/helm/charts/pull/13646), you might see + `spec.clusterIP: Invalid value` when trying to run the [`./gl-helmfile`](management_project_template.md#the-gitlab-ciyml-file) + command. To work around this, after overwriting the release values in `applications/ingress/values.yaml`, + you might need to overwrite all the occurrences of `omitClusterIP: false`, setting it to `omitClusterIP: true`. + Another approach,could be to collect these IPs by running `kubectl get services -n gitlab-managed-apps` + and then overwriting each `ClusterIP` that it complains about with the value you got from that command. + + - Vault: This application introduces a breaking change from the chart we used in Helm v2 to the chart + used in Helm v3. So, the only way to integrate it with this Cluster Management Project is to actually uninstall this app and accept the + chart version proposed in `applications/vault/values.yaml`. + +1. After following all the previous steps, [run a pipeline manually](../../ci/pipelines/index.md#run-a-pipeline-manually) + and watch the `apply` job logs to see if any of your applications were successfully detected, installed, and whether they got any + unexpected updates. + + Some annotation checksums are expected to be updated, as well as this attribute: + + ```diff + --- heritage: Tiller + +++ heritage: Tiller + ``` + +After getting a successful pipeline, repeat these steps for any other deployed apps +you want to manage with the Cluster Management Project. diff --git a/doc/user/project/clusters/kubernetes_pod_logs.md b/doc/user/project/clusters/kubernetes_pod_logs.md index 0ad32adffb1..b14412eb1d6 100644 --- a/doc/user/project/clusters/kubernetes_pod_logs.md +++ b/doc/user/project/clusters/kubernetes_pod_logs.md @@ -9,7 +9,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w > - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/4752) in [GitLab Ultimate](https://about.gitlab.com/pricing/) 11.0. > - [Moved](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/26383) to [GitLab Free](https://about.gitlab.com/pricing/) 12.9. -GitLab makes it easy to view the logs of running pods or managed applications in +GitLab makes it easy to view the logs of running pods in [connected Kubernetes clusters](index.md). By displaying the logs directly in GitLab in the **Log Explorer**, developers can avoid managing console tools or jumping to a different interface. The **Log Explorer** interface provides a set of filters @@ -18,10 +18,11 @@ above the log file data, depending on your configuration: ![Pod logs](img/kubernetes_pod_logs_v12_10.png) - **Namespace** - Select the environment to display. Users with Maintainer or - greater [permissions](../../permissions.md) can also select Managed Apps. -- **Search** - Only available if the Elastic Stack managed application is installed. -- **Select time range** - Select the range of time to display. Only available if the - Elastic Stack managed application is installed. + greater [permissions](../../permissions.md) can also see pods in the + `gitlab-managed-apps` namespace. +- **Search** - Only available if the [Elastic Stack integration](../../clusters/integrations.md#elastic-stack-cluster-integration) is enabled. +- **Select time range** - Select the range of time to display. + Only available if the [Elastic Stack integration](../../clusters/integrations.md#elastic-stack-cluster-integration) is enabled. - **Scroll to bottom** **{scroll_down}** - Scroll to the end of the displayed logs. - **Refresh** **{retry}** - Reload the displayed logs. diff --git a/doc/user/project/clusters/protect/container_host_security/quick_start_guide.md b/doc/user/project/clusters/protect/container_host_security/quick_start_guide.md index b999ae5b933..ebcd56078ae 100644 --- a/doc/user/project/clusters/protect/container_host_security/quick_start_guide.md +++ b/doc/user/project/clusters/protect/container_host_security/quick_start_guide.md @@ -20,7 +20,6 @@ The following steps are recommended to install and use Container Host Security t 1. Install and configure an Ingress node: - [Install the Ingress node via CI/CD (Cluster Management Project)](../../../../clusters/applications.md#install-ingress-using-gitlab-cicd). - - [Determine the external endpoint via the manual method](../../../../clusters/applications.md#determining-the-external-endpoint-manually). - Navigate to the Kubernetes page and enter the [DNS address for the external endpoint](../../index.md#base-domain) into the **Base domain** field on the **Details** tab. Save the changes to the Kubernetes cluster. diff --git a/doc/user/project/clusters/protect/container_network_security/quick_start_guide.md b/doc/user/project/clusters/protect/container_network_security/quick_start_guide.md index 3acb44c61ac..33aefec224a 100644 --- a/doc/user/project/clusters/protect/container_network_security/quick_start_guide.md +++ b/doc/user/project/clusters/protect/container_network_security/quick_start_guide.md @@ -20,7 +20,6 @@ The following steps are recommended to install and use Container Network Securit 1. Install and configure an Ingress node: - [Install the Ingress node via CI/CD (Cluster Management Project)](../../../../clusters/applications.md#install-ingress-using-gitlab-cicd). - - [Determine the external endpoint via the manual method](../../../../clusters/applications.md#determining-the-external-endpoint-manually). - Navigate to the Kubernetes page and enter the [DNS address for the external endpoint](../../index.md#base-domain) into the **Base domain** field on the **Details** tab. Save the changes to the Kubernetes cluster. diff --git a/doc/user/project/integrations/prometheus.md b/doc/user/project/integrations/prometheus.md index 2b4c6c6ca87..adc98151ce4 100644 --- a/doc/user/project/integrations/prometheus.md +++ b/doc/user/project/integrations/prometheus.md @@ -17,7 +17,7 @@ in the GitLab interface. There are two ways to set up Prometheus integration, depending on where your apps are running: -- For deployments on Kubernetes, GitLab can automatically [deploy and manage Prometheus](#managed-prometheus-on-kubernetes). +- For deployments on Kubernetes, GitLab can be [integrated with an in-cluster Prometheus](#prometheus-cluster-integration) - For other deployment targets, [specify the Prometheus server](#manual-configuration-of-prometheus). Once enabled, GitLab detects metrics from known services in the @@ -27,137 +27,13 @@ Once enabled, GitLab detects metrics from known services in the ## Enabling Prometheus Integration -### Managed Prometheus on Kubernetes +### Prometheus cluster integration -> [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/28916) in GitLab 10.5. +> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/55244) in GitLab 13.11. +> - [Replaced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/62725) the Prometheus cluster applications in GitLab 14.0. -**Deprecated:** Managed Prometheus on Kubernetes is deprecated, and -scheduled for removal in [GitLab -14.0](https://gitlab.com/groups/gitlab-org/-/epics/4280). - -GitLab can seamlessly deploy and manage Prometheus on a -[connected Kubernetes cluster](../clusters/index.md), to help you monitor your apps. - -#### Requirements - -- A [connected Kubernetes cluster](../clusters/index.md) - -#### Getting started - -After you have a connected Kubernetes cluster, you can deploy a managed Prometheus with a single click. - -1. Go to the **Operations > Kubernetes** page to view your connected clusters -1. Select the cluster you would like to deploy Prometheus to -1. Click the **Install** button to deploy Prometheus to the cluster - -![Managed Prometheus Deploy](img/prometheus_deploy.png) - -#### About managed Prometheus deployments - -Prometheus is deployed into the `gitlab-managed-apps` namespace, using the -[official Helm chart](https://github.com/helm/charts/tree/master/stable/prometheus). -Prometheus is only accessible in the cluster, with GitLab communicating through the -[Kubernetes API](https://kubernetes.io/docs/concepts/overview/kubernetes-api/). - -The Prometheus server -[automatically detects and monitors](https://prometheus.io/docs/prometheus/latest/configuration/configuration/#kubernetes_sd_config) -nodes, pods, and endpoints. To configure a resource to be monitored by Prometheus, -set the following [Kubernetes annotations](https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/): - -- `prometheus.io/scrape` to `true` to enable monitoring of the resource. -- `prometheus.io/port` to define the port of the metrics endpoint. -- `prometheus.io/path` to define the path of the metrics endpoint. Defaults to `/metrics`. - -CPU and Memory consumption is monitored, but requires -[naming conventions](prometheus_library/kubernetes.md#specifying-the-environment) -to determine the environment. If you are using -[Auto DevOps](../../../topics/autodevops/index.md), this is handled automatically. - -##### Example of Kubernetes service annotations and labels - -As an example, to activate Prometheus monitoring of a service: - -1. Add at least this annotation: `prometheus.io/scrape: 'true'`. -1. Add two labels so GitLab can retrieve metrics dynamically for any environment: - - `application: ${CI_ENVIRONMENT_SLUG}` - - `release: ${CI_ENVIRONMENT_SLUG}` -1. Create a dynamic PromQL query. For example, a query like - `temperature{application="{{ci_environment_slug}}",release="{{ci_environment_slug}}"}` to either: - - Add [custom metrics](../../../operations/metrics/index.md#adding-custom-metrics). - - Add [custom dashboards](../../../operations/metrics/dashboards/index.md). - -The following is a service definition to accomplish this: - -```yaml ---- -# Service -apiVersion: v1 -kind: Service -metadata: - name: service-${CI_PROJECT_NAME}-${CI_COMMIT_REF_SLUG} - # === Prometheus annotations === - annotations: - prometheus.io/scrape: 'true' - labels: - application: ${CI_ENVIRONMENT_SLUG} - release: ${CI_ENVIRONMENT_SLUG} - # === End of Prometheus === -spec: - selector: - app: ${CI_PROJECT_NAME} - ports: - - port: ${EXPOSED_PORT} - targetPort: ${CONTAINER_PORT} -``` - -#### Access the UI of a Prometheus managed application in Kubernetes - -You can connect directly to Prometheus, and view the Prometheus user interface, when -using a Prometheus managed application in Kubernetes: - -1. Find the name of the Prometheus pod in the user interface of your Kubernetes - provider, such as GKE, or by running the following `kubectl` command in your - terminal: - - ```shell - kubectl get pods -n gitlab-managed-apps | grep 'prometheus-prometheus-server' - ``` - - The command should return a result like the following example, where - `prometheus-prometheus-server-55b4bd64c9-dpc6b` is the name of the Prometheus pod: - - ```plaintext - gitlab-managed-apps prometheus-prometheus-server-55b4bd64c9-dpc6b 2/2 Running 0 71d - ``` - -1. Run a `kubectl port-forward` command. In the following example, `9090` is the - Prometheus server's listening port: - - ```shell - kubectl port-forward prometheus-prometheus-server-55b4bd64c9-dpc6b 9090:9090 -n gitlab-managed-apps - ``` - - The `port-forward` command forwards all requests sent to your system's `9090` port - to the `9090` port of the Prometheus pod. If the `9090` port on your system is used - by another application, you can change the port number before the colon to your - desired port. For example, to forward port `8080` of your local system, change the - command to: - - ```shell - kubectl port-forward prometheus-prometheus-server-55b4bd64c9-dpc6b 8080:9090 -n gitlab-managed-apps - ``` - -1. Open `localhost:9090` in your browser to display the Prometheus user interface. - -#### Script access to Prometheus - -You can script the access to Prometheus, extracting the name of the pod automatically like this: - -```shell -POD_INFORMATION=$(kubectl get pods -n gitlab-managed-apps | grep 'prometheus-prometheus-server') -POD_NAME=$(echo $POD_INFORMATION | awk '{print $1;}') -kubectl port-forward $POD_NAME 9090:9090 -n gitlab-managed-apps -``` +GitLab can query an in-cluster Prometheus for your metrics. +See [Prometheus cluster integration](../../clusters/integrations.md#prometheus-cluster-integration) for details. ### Manual configuration of Prometheus @@ -219,12 +95,12 @@ to integrate with. ### Precedence with multiple Prometheus configurations Although you can enable both a [manual configuration](#manual-configuration-of-prometheus) -and [auto configuration](#managed-prometheus-on-kubernetes) of Prometheus, you +and [cluster integration](#prometheus-cluster-integration) of Prometheus, you can use only one: - If you have enabled a [Prometheus manual configuration](#manual-configuration-of-prometheus) - and a [managed Prometheus on Kubernetes](#managed-prometheus-on-kubernetes), + and a [Prometheus cluster integration](#prometheus-cluster-integration), the manual configuration takes precedence and is used to run queries from [custom dashboards](../../../operations/metrics/dashboards/index.md) and [custom metrics](../../../operations/metrics/index.md#adding-custom-metrics). diff --git a/doc/user/project/integrations/prometheus_library/kubernetes.md b/doc/user/project/integrations/prometheus_library/kubernetes.md index e14c1c0f6fd..1bafa4938af 100644 --- a/doc/user/project/integrations/prometheus_library/kubernetes.md +++ b/doc/user/project/integrations/prometheus_library/kubernetes.md @@ -33,7 +33,7 @@ integration services must be enabled. Prometheus needs to be deployed into the cluster and configured properly in order to gather Kubernetes metrics. GitLab supports two methods for doing so: -- GitLab [integrates with Kubernetes](../../clusters/index.md), and can [deploy Prometheus into a connected cluster](../prometheus.md#managed-prometheus-on-kubernetes). It is automatically configured to collect Kubernetes metrics. +- GitLab [integrates with Kubernetes](../../clusters/index.md), and can [query a Prometheus in a connected cluster](../../../clusters/integrations.md#prometheus-cluster-integration). The in-cluster Prometheus can be configured to automatically collect application metrics from your cluster. - To configure your own Prometheus server, you can follow the [Prometheus documentation](https://prometheus.io/docs/introduction/overview/). ## Specifying the Environment diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 18c548d1400..2967ca71c83 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -6901,15 +6901,15 @@ msgstr "" msgid "ClusterAgent|You have insufficient permissions to delete this cluster agent" msgstr "" +msgid "ClusterApplicationsRemoved|One-click application management was removed in GitLab 14.0. Your applications are still installed in your cluster, and integrations continue working." +msgstr "" + msgid "ClusterIntegration|%{appList} was successfully installed on your Kubernetes cluster" msgstr "" msgid "ClusterIntegration|%{boldStart}Note:%{boldEnd} Requires Ingress to be installed." msgstr "" -msgid "ClusterIntegration|%{externalIp}.nip.io" -msgstr "" - msgid "ClusterIntegration|%{linkStart}More information%{linkEnd}" msgstr "" @@ -6973,9 +6973,6 @@ msgstr "" msgid "ClusterIntegration|Allows GitLab to query a specifically configured in-cluster Prometheus for metrics." msgstr "" -msgid "ClusterIntegration|Alternatively, " -msgstr "" - msgid "ClusterIntegration|Amazon EKS" msgstr "" @@ -7861,9 +7858,6 @@ msgstr "" msgid "ClusterIntegration|access to Google Kubernetes Engine" msgstr "" -msgid "ClusterIntegration|can be used instead of a custom domain. " -msgstr "" - msgid "ClusterIntegration|installed via %{linkStart}Cloud Run%{linkEnd}" msgstr "" @@ -25487,9 +25481,6 @@ msgstr "" msgid "Project path" msgstr "" -msgid "Project scanning help page" -msgstr "" - msgid "Project security status" msgstr "" @@ -34818,21 +34809,6 @@ msgstr "" msgid "UnscannedProjects|60 or more days" msgstr "" -msgid "UnscannedProjects|Default branch scanning by project" -msgstr "" - -msgid "UnscannedProjects|Out of date" -msgstr "" - -msgid "UnscannedProjects|Project scanning" -msgstr "" - -msgid "UnscannedProjects|Untested" -msgstr "" - -msgid "UnscannedProjects|Your projects are up do date! Nice job!" -msgstr "" - msgid "Unschedule job" msgstr "" @@ -38000,6 +37976,9 @@ msgstr "" msgid "can't be enabled because signed commits are required for this project" msgstr "" +msgid "can't be the same as the source project" +msgstr "" + msgid "can't include: %{invalid_storages}" msgstr "" diff --git a/package.json b/package.json index d5de6a60924..e251ac6122d 100644 --- a/package.json +++ b/package.json @@ -110,7 +110,6 @@ "css-loader": "^2.1.1", "d3": "^5.16.0", "d3-sankey": "^0.12.3", - "d3-scale": "^2.2.2", "d3-selection": "^1.2.0", "dateformat": "^4.5.1", "deckar01-task_list": "^2.3.1", diff --git a/spec/factories/ci/job_token/project_scope_links.rb b/spec/factories/ci/job_token/project_scope_links.rb new file mode 100644 index 00000000000..a11edd87e4e --- /dev/null +++ b/spec/factories/ci/job_token/project_scope_links.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +FactoryBot.define do + factory :ci_job_token_project_scope_link, class: 'Ci::JobToken::ProjectScopeLink' do + association :source_project, factory: :project + association :target_project, factory: :project + association :added_by, factory: :user + end +end diff --git a/spec/factories_spec.rb b/spec/factories_spec.rb index 994bf066cf0..80e94fa1628 100644 --- a/spec/factories_spec.rb +++ b/spec/factories_spec.rb @@ -66,6 +66,7 @@ RSpec.describe 'factories' do # associations must be unique and cannot be reused, or the factory default # is being mutated. skip_factory_defaults = %i[ + ci_job_token_project_scope_link evidence exported_protected_branch fork_network_member diff --git a/spec/features/admin/clusters/applications_spec.rb b/spec/features/admin/clusters/applications_spec.rb deleted file mode 100644 index e083e4fee4c..00000000000 --- a/spec/features/admin/clusters/applications_spec.rb +++ /dev/null @@ -1,22 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' -require_relative '../../../../spec/features/clusters/installing_applications_shared_examples' - -RSpec.describe 'Instance-level Cluster Applications', :js do - include GoogleApi::CloudPlatformHelpers - - let(:user) { create(:admin) } - - before do - sign_in(user) - gitlab_enable_admin_mode_sign_in(user) - end - - describe 'Installing applications' do - include_examples "installing applications on a cluster" do - let(:cluster_path) { admin_cluster_path(cluster) } - let(:cluster_factory_args) { [:instance] } - end - end -end diff --git a/spec/features/calendar_spec.rb b/spec/features/calendar_spec.rb index 1281d890ef7..21da92c9f43 100644 --- a/spec/features/calendar_spec.rb +++ b/spec/features/calendar_spec.rb @@ -15,10 +15,9 @@ RSpec.describe 'Contributions Calendar', :js do issue_title = 'Bug in old browser' issue_params = { title: issue_title } - def get_cell_color_selector(contributions) - activity_colors = ["#ededed", "rgb(172, 213, 242)", "rgb(127, 168, 201)", "rgb(82, 123, 160)", "rgb(37, 78, 119)"] + def get_cell_level_selector(contributions) # We currently don't actually test the cases with contributions >= 20 - activity_colors_index = + activity_level_index = if contributions > 0 && contributions < 10 1 elsif contributions >= 10 && contributions < 20 @@ -31,7 +30,7 @@ RSpec.describe 'Contributions Calendar', :js do 0 end - ".user-contrib-cell[fill='#{activity_colors[activity_colors_index]}']" + ".user-contrib-cell:not(.contrib-legend)[data-level='#{activity_level_index}']" end def get_cell_date_selector(contributions, date) @@ -42,7 +41,7 @@ RSpec.describe 'Contributions Calendar', :js do "#{contributions} #{'contribution'.pluralize(contributions)}" end - "#{get_cell_color_selector(contributions)}[title='#{contribution_text}
#{date}']" + "#{get_cell_level_selector(contributions)}[title='#{contribution_text}
#{date}']" end def push_code_contribution @@ -137,7 +136,7 @@ RSpec.describe 'Contributions Calendar', :js do include_context 'visit user page' it 'displays calendar activity square for 1 contribution', :sidekiq_might_not_need_inline do - expect(find('#js-overview')).to have_selector(get_cell_color_selector(contribution_count), count: 1) + expect(find('#js-overview')).to have_selector(get_cell_level_selector(contribution_count), count: 1) today = Date.today.strftime(date_format) expect(find('#js-overview')).to have_selector(get_cell_date_selector(contribution_count, today), count: 1) @@ -187,7 +186,7 @@ RSpec.describe 'Contributions Calendar', :js do include_context 'visit user page' it 'displays calendar activity squares for both days', :sidekiq_might_not_need_inline do - expect(find('#js-overview')).to have_selector(get_cell_color_selector(1), count: 2) + expect(find('#js-overview')).to have_selector(get_cell_level_selector(1), count: 2) end it 'displays calendar activity square for yesterday', :sidekiq_might_not_need_inline do diff --git a/spec/features/clusters/cluster_detail_page_spec.rb b/spec/features/clusters/cluster_detail_page_spec.rb index 84a18a45d35..cba8aaef1ef 100644 --- a/spec/features/clusters/cluster_detail_page_spec.rb +++ b/spec/features/clusters/cluster_detail_page_spec.rb @@ -31,30 +31,6 @@ RSpec.describe 'Clusterable > Show page' do expect(page).to have_content('Kubernetes cluster was successfully updated.') end - context 'when there is a cluster with ingress and external ip', :js do - before do - cluster.create_application_ingress!(external_ip: '192.168.1.100') - - visit cluster_path - end - - it 'shows help text with the domain as an alternative to custom domain', :js do - within '.js-cluster-details-form' do - expect(find(cluster_ingress_help_text_selector).text).to include('192.168.1.100') - end - end - end - - context 'when there is no ingress' do - it 'alternative to custom domain is not shown' do - visit cluster_path - - within '.js-cluster-details-form' do - expect(page).not_to have_selector(cluster_ingress_help_text_selector) - end - end - end - it 'does not show the environments tab' do visit cluster_path diff --git a/spec/features/clusters/cluster_health_dashboard_spec.rb b/spec/features/clusters/cluster_health_dashboard_spec.rb index 0e2739a45b1..20c07f4d6ac 100644 --- a/spec/features/clusters/cluster_health_dashboard_spec.rb +++ b/spec/features/clusters/cluster_health_dashboard_spec.rb @@ -27,8 +27,8 @@ RSpec.describe 'Cluster Health board', :js, :kubeclient, :use_clean_rails_memory expect(page).to have_css('.cluster-health-graphs') end - context 'no prometheus enabled' do - it 'shows install prometheus message' do + context 'no prometheus available' do + it 'shows enable Prometheus message' do visit cluster_path click_link 'Health' @@ -82,12 +82,12 @@ RSpec.describe 'Cluster Health board', :js, :kubeclient, :use_clean_rails_memory def stub_empty_response stub_prometheus_request(/prometheus-prometheus-server/, status: 204, body: {}) - stub_prometheus_request(/prometheus\/api\/v1/, status: 204, body: {}) + stub_prometheus_request(%r{prometheus/api/v1}, status: 204, body: {}) end def stub_connected stub_prometheus_request(/prometheus-prometheus-server/, body: prometheus_values_body) - stub_prometheus_request(/prometheus\/api\/v1/, body: prometheus_values_body) + stub_prometheus_request(%r{prometheus/api/v1}, body: prometheus_values_body) end end end diff --git a/spec/features/clusters/installing_applications_shared_examples.rb b/spec/features/clusters/installing_applications_shared_examples.rb deleted file mode 100644 index c422aa2be72..00000000000 --- a/spec/features/clusters/installing_applications_shared_examples.rb +++ /dev/null @@ -1,252 +0,0 @@ -# frozen_string_literal: true - -RSpec.shared_examples "installing applications for a cluster" do - before do - # Reduce interval from 10 seconds which is too long for an automated test - stub_const("#{Clusters::ClustersController}::STATUS_POLLING_INTERVAL", 500) - - visit cluster_path - end - - context 'when cluster is being created' do - let(:cluster) { create(:cluster, :providing_by_gcp, *cluster_factory_args) } - - it 'user is unable to install applications' do - expect(page).not_to have_text('Helm') - expect(page).not_to have_text('Install') - end - end - - context 'when cluster is created' do - let(:cluster) { create(:cluster, :provided_by_gcp, *cluster_factory_args) } - - before do - page.within('.js-edit-cluster-form') do - click_link 'Applications' - end - end - - it 'user can install applications' do - wait_for_requests - - application_row = '.js-cluster-application-row-ingress' - - page.within(application_row) do - expect(page).not_to have_css('.js-cluster-application-install-button[disabled]') - expect(page).to have_css('.js-cluster-application-install-button', exact_text: 'Install') - end - end - - it 'does not show the Helm application' do - expect(page).not_to have_selector(:css, '.js-cluster-application-row-helm') - end - - context 'when user installs Knative' do - context 'on an abac cluster' do - let(:cluster) { create(:cluster, :provided_by_gcp, :rbac_disabled, *cluster_factory_args) } - - it 'shows info block and not be installable' do - page.within('.js-cluster-application-row-knative') do - expect(page).to have_css('.rbac-notice') - expect(page.find(:css, '.js-cluster-application-install-button')['disabled']).to eq('true') - end - end - end - - context 'on an rbac cluster' do - let(:cluster) { create(:cluster, :provided_by_gcp, *cluster_factory_args) } - - it 'does not show callout block and be installable' do - page.within('.js-cluster-application-row-knative') do - expect(page).not_to have_css('p', text: 'You must have an RBAC-enabled cluster', visible: :all) - expect(page).to have_css('.js-cluster-application-install-button:not([disabled])') - end - end - - describe 'when user clicks install button' do - before do - allow(ClusterInstallAppWorker).to receive(:perform_async) - allow(ClusterWaitForIngressIpAddressWorker).to receive(:perform_in) - allow(ClusterWaitForIngressIpAddressWorker).to receive(:perform_async) - - page.within('.js-cluster-application-row-knative') do - expect(page).to have_css('.js-cluster-application-install-button:not([disabled])') - - page.find('.js-knative-domainname').set("domain.example.org") - - click_button 'Install' - - wait_for_requests - - expect(page).to have_css('.js-cluster-application-install-button', exact_text: 'Installing') - - Clusters::Cluster.last.application_knative.make_installing! - Clusters::Cluster.last.application_knative.make_installed! - Clusters::Cluster.last.application_knative.update_attribute(:external_ip, '127.0.0.1') - end - end - - it 'shows status transition' do - page.within('.js-cluster-application-row-knative') do - expect(page).to have_field('Knative Domain Name:', with: 'domain.example.org') - expect(page).to have_css('.js-cluster-application-uninstall-button', exact_text: 'Uninstall') - end - - expect(page).to have_content('Knative was successfully installed on your Kubernetes cluster') - expect(page).to have_css('.js-knative-save-domain-button'), exact_text: 'Save changes' - end - - it 'can then update the domain' do - page.within('.js-cluster-application-row-knative') do - expect(ClusterPatchAppWorker).to receive(:perform_async) - - expect(page).to have_field('Knative Domain Name:', with: 'domain.example.org') - - page.find('.js-knative-domainname').set("new.domain.example.org") - - click_button 'Save changes' - - wait_for_requests - - expect(page).to have_field('Knative Domain Name:', with: 'new.domain.example.org') - end - end - end - end - end - - context 'when user installs Cert Manager' do - before do - allow(ClusterInstallAppWorker).to receive(:perform_async) - allow(ClusterWaitForIngressIpAddressWorker).to receive(:perform_in) - allow(ClusterWaitForIngressIpAddressWorker).to receive(:perform_async) - end - - it 'shows status transition' do - page.within('.js-cluster-application-row-cert_manager') do - click_button 'Install' - wait_for_requests - - expect(page).to have_field('Issuer Email', with: cluster.user.email) - expect(page).to have_css('.js-cluster-application-install-button', exact_text: 'Installing') - - Clusters::Cluster.last.application_cert_manager.make_installing! - - expect(page).to have_field('Issuer Email', with: cluster.user.email) - expect(page).to have_css('.js-cluster-application-install-button', exact_text: 'Installing') - - Clusters::Cluster.last.application_cert_manager.make_installed! - - expect(page).to have_field('Issuer Email', with: cluster.user.email) - expect(page).to have_css('.js-cluster-application-uninstall-button', exact_text: 'Uninstall') - end - - expect(page).to have_content('Cert-Manager was successfully installed on your Kubernetes cluster') - end - - it 'installs with custom email' do - custom_email = 'new_email@example.org' - - page.within('.js-cluster-application-row-cert_manager') do - # Wait for the polling to finish - wait_for_requests - - page.find('.js-email').set(custom_email) - click_button 'Install' - wait_for_requests - - expect(page).to have_field('Issuer Email', with: custom_email) - expect(page).to have_css('.js-cluster-application-install-button', exact_text: 'Installing') - - Clusters::Cluster.last.application_cert_manager.make_installing! - - expect(page).to have_field('Issuer Email', with: custom_email) - expect(page).to have_css('.js-cluster-application-install-button', exact_text: 'Installing') - - Clusters::Cluster.last.application_cert_manager.make_installed! - - expect(page).to have_field('Issuer Email', with: custom_email) - expect(page).to have_css('.js-cluster-application-uninstall-button', exact_text: 'Uninstall') - end - end - end - - context 'when user installs Elastic Stack' do - before do - allow(ClusterInstallAppWorker).to receive(:perform_async) - - page.within('.js-cluster-application-row-elastic_stack') do - click_button 'Install' - end - - wait_for_requests - end - - it 'shows status transition' do - page.within('.js-cluster-application-row-elastic_stack') do - expect(page).to have_css('.js-cluster-application-install-button', exact_text: 'Installing') - - Clusters::Cluster.last.application_elastic_stack.make_installing! - - expect(page).to have_css('.js-cluster-application-install-button', exact_text: 'Installing') - - Clusters::Cluster.last.application_elastic_stack.make_installed! - - expect(page).to have_css('.js-cluster-application-uninstall-button', exact_text: 'Uninstall') - end - - expect(page).to have_content('Elastic Stack was successfully installed on your Kubernetes cluster') - end - end - - context 'when user installs Ingress' do - before do - allow(ClusterInstallAppWorker).to receive(:perform_async) - allow(ClusterWaitForIngressIpAddressWorker).to receive(:perform_in) - allow(ClusterWaitForIngressIpAddressWorker).to receive(:perform_async) - - page.within('.js-cluster-application-row-ingress') do - expect(page).to have_css('.js-cluster-application-install-button:not([disabled])') - page.find(:css, '.js-cluster-application-install-button').click - - wait_for_requests - end - end - - it 'shows the status transition' do - page.within('.js-cluster-application-row-ingress') do - # FE sends request and gets the response, then the buttons is "Installing" - expect(page).to have_css('.js-cluster-application-install-button[disabled]', exact_text: 'Installing') - - Clusters::Cluster.last.application_ingress.make_installing! - - # FE starts polling and update the buttons to "Installing" - expect(page).to have_css('.js-cluster-application-install-button[disabled]', exact_text: 'Installing') - - # The application becomes installed but we keep waiting for external IP address - Clusters::Cluster.last.application_ingress.make_installed! - - expect(page).to have_css('.js-cluster-application-install-button[disabled]', exact_text: 'Installed') - expect(page).to have_selector('.js-no-endpoint-message') - expect(page).to have_selector('.js-ingress-ip-loading-icon') - - # We receive the external IP address and display - Clusters::Cluster.last.application_ingress.update!(external_ip: '192.168.1.100') - - expect(page).not_to have_css('button', exact_text: 'Install', visible: :all) - expect(page).not_to have_css('button', exact_text: 'Installing', visible: :all) - expect(page).to have_css('.js-cluster-application-uninstall-button:not([disabled])', exact_text: 'Uninstall') - expect(page).not_to have_css('p', text: 'The endpoint is in the process of being assigned', visible: :all) - expect(page.find('.js-endpoint').value).to eq('192.168.1.100') - end - - expect(page).to have_content('Ingress was successfully installed on your Kubernetes cluster') - end - end - end -end - -RSpec.shared_examples "installing applications on a cluster" do - it_behaves_like "installing applications for a cluster", false - it_behaves_like "installing applications for a cluster", true -end diff --git a/spec/features/groups/clusters/applications_spec.rb b/spec/features/groups/clusters/applications_spec.rb deleted file mode 100644 index 324ef24efc4..00000000000 --- a/spec/features/groups/clusters/applications_spec.rb +++ /dev/null @@ -1,23 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' -require_relative '../../../../spec/features/clusters/installing_applications_shared_examples' - -RSpec.describe 'Group-level Cluster Applications', :js do - include GoogleApi::CloudPlatformHelpers - - let(:group) { create(:group) } - let(:user) { create(:user) } - - before do - group.add_maintainer(user) - sign_in(user) - end - - describe 'Installing applications' do - include_examples "installing applications on a cluster" do - let(:cluster_path) { group_cluster_path(group, cluster) } - let(:cluster_factory_args) { [:group, groups: [group]] } - end - end -end diff --git a/spec/features/projects/clusters/applications_spec.rb b/spec/features/projects/clusters/applications_spec.rb deleted file mode 100644 index 74b477dd85d..00000000000 --- a/spec/features/projects/clusters/applications_spec.rb +++ /dev/null @@ -1,23 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' -require_relative '../../../../spec/features/clusters/installing_applications_shared_examples' - -RSpec.describe 'Project-level Cluster Applications', :js do - include GoogleApi::CloudPlatformHelpers - - let(:project) { create(:project) } - let(:user) { create(:user) } - - before do - project.add_maintainer(user) - sign_in(user) - end - - describe 'Installing applications' do - include_examples "installing applications on a cluster" do - let(:cluster_path) { project_cluster_path(project, cluster) } - let(:cluster_factory_args) { [projects: [project]] } - end - end -end diff --git a/spec/finders/ci/auth_job_finder_spec.rb b/spec/finders/ci/auth_job_finder_spec.rb index 6cd58f5cd01..78827c9ddee 100644 --- a/spec/finders/ci/auth_job_finder_spec.rb +++ b/spec/finders/ci/auth_job_finder_spec.rb @@ -2,7 +2,8 @@ require 'spec_helper' RSpec.describe Ci::AuthJobFinder do - let_it_be(:job, reload: true) { create(:ci_build, status: :running) } + let_it_be(:user, reload: true) { create(:user) } + let_it_be(:job, reload: true) { create(:ci_build, status: :running, user: user) } let(:token) { job.token } @@ -55,10 +56,31 @@ RSpec.describe Ci::AuthJobFinder do describe '#execute' do subject(:execute) { finder.execute } - before do - job.success! + context 'when job is not running' do + before do + job.success! + end + + it { is_expected.to be_nil } end - it { is_expected.to be_nil } + context 'when job is running', :request_store do + it 'sets ci_job_token_scope on the job user', :aggregate_failures do + expect(subject).to eq(job) + expect(subject.user).to be_from_ci_job_token + expect(subject.user.ci_job_token_scope.source_project).to eq(job.project) + end + + context 'when feature flag ci_scoped_job_token is disabled' do + before do + stub_feature_flags(ci_scoped_job_token: false) + end + + it 'does not set ci_job_token_scope on the job user' do + expect(subject).to eq(job) + expect(subject.user).not_to be_from_ci_job_token + end + end + end end end diff --git a/spec/frontend/clusters/forms/components/integration_form_spec.js b/spec/frontend/clusters/forms/components/integration_form_spec.js index c5cec4c4fdb..b129baa2d83 100644 --- a/spec/frontend/clusters/forms/components/integration_form_spec.js +++ b/spec/frontend/clusters/forms/components/integration_form_spec.js @@ -15,7 +15,6 @@ describe('ClusterIntegrationForm', () => { editable: true, environmentScope: '*', baseDomain: 'testDomain', - applicationIngressExternalIp: null, }; const createWrapper = (storeValues = defaultStoreValues) => { @@ -72,18 +71,6 @@ describe('ClusterIntegrationForm', () => { expect(findSubmitButton().exists()).toBe(false); }); }); - - it('does not render external IP block if applicationIngressExternalIp was not passed', () => { - createWrapper({ ...defaultStoreValues }); - - expect(wrapper.find('.js-ingress-domain-help-text').exists()).toBe(false); - }); - - it('renders external IP block if applicationIngressExternalIp was passed', () => { - createWrapper({ ...defaultStoreValues, applicationIngressExternalIp: '127.0.0.1' }); - - expect(wrapper.find('.js-ingress-domain-help-text').exists()).toBe(true); - }); }); describe('reactivity', () => { diff --git a/spec/frontend/pages/users/activity_calendar_spec.js b/spec/frontend/pages/users/activity_calendar_spec.js new file mode 100644 index 00000000000..b33e92e14b2 --- /dev/null +++ b/spec/frontend/pages/users/activity_calendar_spec.js @@ -0,0 +1,16 @@ +import { getLevelFromContributions } from '~/pages/users/activity_calendar'; + +describe('getLevelFromContributions', () => { + it.each([ + [0, 0], + [1, 1], + [9, 1], + [10, 2], + [19, 2], + [20, 3], + [30, 4], + [99, 4], + ])('.getLevelFromContributions(%i, %i)', (count, expected) => { + expect(getLevelFromContributions(count)).toBe(expected); + }); +}); diff --git a/spec/lib/gitlab/auth/auth_finders_spec.rb b/spec/lib/gitlab/auth/auth_finders_spec.rb index cddcaf09b74..7475ed2796f 100644 --- a/spec/lib/gitlab/auth/auth_finders_spec.rb +++ b/spec/lib/gitlab/auth/auth_finders_spec.rb @@ -50,7 +50,7 @@ RSpec.describe Gitlab::Auth::AuthFinders do end shared_examples 'find user from job token' do |without_job_token_allowed| - context 'when route is allowed to be authenticated' do + context 'when route is allowed to be authenticated', :request_store do let(:route_authentication_setting) { { job_token_allowed: true } } context 'for an invalid token' do @@ -68,6 +68,8 @@ RSpec.describe Gitlab::Auth::AuthFinders do it 'return user' do expect(subject).to eq(user) expect(@current_authenticated_job).to eq job + expect(subject).to be_from_ci_job_token + expect(subject.ci_job_token_scope.source_project).to eq(job.project) end end @@ -81,7 +83,7 @@ RSpec.describe Gitlab::Auth::AuthFinders do end end - context 'when route is not allowed to be authenticated' do + context 'when route is not allowed to be authenticated', :request_store do let(:route_authentication_setting) { { job_token_allowed: false } } context 'with a running job' do @@ -96,6 +98,8 @@ RSpec.describe Gitlab::Auth::AuthFinders do it 'returns the user' do expect(subject).to eq(user) expect(@current_authenticated_job).to eq job + expect(subject).to be_from_ci_job_token + expect(subject.ci_job_token_scope.source_project).to eq(job.project) end else it 'returns nil' do diff --git a/spec/models/ci/job_token/project_scope_link_spec.rb b/spec/models/ci/job_token/project_scope_link_spec.rb new file mode 100644 index 00000000000..d18495b9312 --- /dev/null +++ b/spec/models/ci/job_token/project_scope_link_spec.rb @@ -0,0 +1,68 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Ci::JobToken::ProjectScopeLink do + it { is_expected.to belong_to(:source_project) } + it { is_expected.to belong_to(:target_project) } + it { is_expected.to belong_to(:added_by) } + + let_it_be(:project) { create(:project) } + + describe 'unique index' do + let!(:link) { create(:ci_job_token_project_scope_link) } + + it 'raises an error' do + expect do + create(:ci_job_token_project_scope_link, + source_project: link.source_project, + target_project: link.target_project) + end.to raise_error(ActiveRecord::RecordNotUnique) + end + end + + describe 'validations' do + it 'must have a source project', :aggregate_failures do + link = build(:ci_job_token_project_scope_link, source_project: nil) + + expect(link).not_to be_valid + expect(link.errors[:source_project]).to contain_exactly("can't be blank") + end + + it 'must have a target project', :aggregate_failures do + link = build(:ci_job_token_project_scope_link, target_project: nil) + + expect(link).not_to be_valid + expect(link.errors[:target_project]).to contain_exactly("can't be blank") + end + + it 'must have a target project different than source project', :aggregate_failures do + link = build(:ci_job_token_project_scope_link, target_project: project, source_project: project) + + expect(link).not_to be_valid + expect(link.errors[:target_project]).to contain_exactly("can't be the same as the source project") + end + end + + describe '.from_project' do + subject { described_class.from_project(project) } + + let!(:source_link) { create(:ci_job_token_project_scope_link, source_project: project) } + let!(:target_link) { create(:ci_job_token_project_scope_link, target_project: project) } + + it 'returns only the links having the given source project' do + expect(subject).to contain_exactly(source_link) + end + end + + describe '.to_project' do + subject { described_class.to_project(project) } + + let!(:source_link) { create(:ci_job_token_project_scope_link, source_project: project) } + let!(:target_link) { create(:ci_job_token_project_scope_link, target_project: project) } + + it 'returns only the links having the given target project' do + expect(subject).to contain_exactly(target_link) + end + end +end diff --git a/spec/models/ci/job_token/scope_spec.rb b/spec/models/ci/job_token/scope_spec.rb new file mode 100644 index 00000000000..2fbfbac64f8 --- /dev/null +++ b/spec/models/ci/job_token/scope_spec.rb @@ -0,0 +1,55 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Ci::JobToken::Scope do + let_it_be(:project) { create(:project) } + + let(:scope) { described_class.new(project) } + + describe '#all_projects' do + subject(:all_projects) { scope.all_projects } + + context 'when no projects are added to the scope' do + it 'returns the project defining the scope' do + expect(all_projects).to contain_exactly(project) + end + end + + context 'when other projects are added to the scope' do + let_it_be(:scoped_project) { create(:project) } + let_it_be(:unscoped_project) { create(:project) } + + let!(:link_in_scope) { create(:ci_job_token_project_scope_link, source_project: project, target_project: scoped_project) } + let!(:link_out_of_scope) { create(:ci_job_token_project_scope_link, target_project: unscoped_project) } + + it 'returns all projects that can be accessed from a given scope' do + expect(subject).to contain_exactly(project, scoped_project) + end + end + end + + describe 'includes?' do + subject { scope.includes?(target_project) } + + context 'when param is the project defining the scope' do + let(:target_project) { project } + + it { is_expected.to be_truthy } + end + + context 'when param is a project in scope' do + let(:target_link) { create(:ci_job_token_project_scope_link, source_project: project) } + let(:target_project) { target_link.target_project } + + it { is_expected.to be_truthy } + end + + context 'when param is a project in another scope' do + let(:scope_link) { create(:ci_job_token_project_scope_link) } + let(:target_project) { scope_link.target_project } + + it { is_expected.to be_falsey } + end + end +end diff --git a/spec/models/hooks/web_hook_log_archived_spec.rb b/spec/models/hooks/web_hook_log_archived_spec.rb deleted file mode 100644 index ac726dbaf4f..00000000000 --- a/spec/models/hooks/web_hook_log_archived_spec.rb +++ /dev/null @@ -1,52 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -RSpec.describe WebHookLogArchived do - let(:source_table) { WebHookLog } - let(:destination_table) { described_class } - - it 'has the same columns as the source table' do - column_names_from_source_table = column_names(source_table) - column_names_from_destination_table = column_names(destination_table) - - expect(column_names_from_destination_table).to match_array(column_names_from_source_table) - end - - it 'has the same null constraints as the source table' do - constraints_from_source_table = null_constraints(source_table) - constraints_from_destination_table = null_constraints(destination_table) - - expect(constraints_from_destination_table.to_a).to match_array(constraints_from_source_table.to_a) - end - - it 'inserts the same record as the one in the source table', :aggregate_failures do - expect { create(:web_hook_log) }.to change { destination_table.count }.by(1) - - event_from_source_table = source_table.connection.select_one( - "SELECT * FROM #{source_table.table_name} ORDER BY created_at desc LIMIT 1" - ) - event_from_destination_table = destination_table.connection.select_one( - "SELECT * FROM #{destination_table.table_name} ORDER BY created_at desc LIMIT 1" - ) - - expect(event_from_destination_table).to eq(event_from_source_table) - end - - def column_names(table) - table.connection.select_all(<<~SQL) - SELECT c.column_name - FROM information_schema.columns c - WHERE c.table_name = '#{table.table_name}' - SQL - end - - def null_constraints(table) - table.connection.select_all(<<~SQL) - SELECT c.column_name, c.is_nullable - FROM information_schema.columns c - WHERE c.table_name = '#{table.table_name}' - AND c.column_name != 'created_at' - SQL - end -end diff --git a/spec/policies/project_policy_spec.rb b/spec/policies/project_policy_spec.rb index d0abcfbd091..53d9bdb01a3 100644 --- a/spec/policies/project_policy_spec.rb +++ b/spec/policies/project_policy_spec.rb @@ -1385,4 +1385,53 @@ RSpec.describe ProjectPolicy do end end end + + describe 'when user is authenticated via CI_JOB_TOKEN', :request_store do + let(:current_user) { developer } + let(:job) { build_stubbed(:ci_build, project: scope_project, user: current_user) } + + before do + current_user.set_ci_job_token_scope!(job) + end + + context 'when accessing a private project' do + let(:project) { private_project } + + context 'when the job token comes from the same project' do + let(:scope_project) { project } + + it { is_expected.to be_allowed(:developer_access) } + end + + context 'when the job token comes from another project' do + let(:scope_project) { create(:project, :private) } + + before do + scope_project.add_developer(current_user) + end + + it { is_expected.to be_disallowed(:guest_access) } + end + end + + context 'when accessing a public project' do + let(:project) { public_project } + + context 'when the job token comes from the same project' do + let(:scope_project) { project } + + it { is_expected.to be_allowed(:developer_access) } + end + + context 'when the job token comes from another project' do + let(:scope_project) { create(:project, :private) } + + before do + scope_project.add_developer(current_user) + end + + it { is_expected.to be_disallowed(:public_access) } + end + end + end end diff --git a/spec/requests/api/generic_packages_spec.rb b/spec/requests/api/generic_packages_spec.rb index a5e40eec919..6886fff2282 100644 --- a/spec/requests/api/generic_packages_spec.rb +++ b/spec/requests/api/generic_packages_spec.rb @@ -18,7 +18,7 @@ RSpec.describe API::GenericPackages do let_it_be(:project_deploy_token_wo) { create(:project_deploy_token, deploy_token: deploy_token_wo, project: project) } let(:user) { personal_access_token.user } - let(:ci_build) { create(:ci_build, :running, user: user) } + let(:ci_build) { create(:ci_build, :running, user: user, project: project) } def auth_header return {} if user_role == :anonymous diff --git a/spec/requests/api/go_proxy_spec.rb b/spec/requests/api/go_proxy_spec.rb index e678b6cf1c8..0143340de11 100644 --- a/spec/requests/api/go_proxy_spec.rb +++ b/spec/requests/api/go_proxy_spec.rb @@ -11,7 +11,7 @@ RSpec.describe API::GoProxy do let_it_be(:base) { "#{Settings.build_gitlab_go_url}/#{project.full_path}" } let_it_be(:oauth) { create :oauth_access_token, scopes: 'api', resource_owner: user } - let_it_be(:job) { create :ci_build, user: user, status: :running } + let_it_be(:job) { create :ci_build, user: user, status: :running, project: project } let_it_be(:pa_token) { create :personal_access_token, user: user } let_it_be(:modules) do diff --git a/spec/requests/api/maven_packages_spec.rb b/spec/requests/api/maven_packages_spec.rb index 2bb6d05f54b..d9f11b19e6e 100644 --- a/spec/requests/api/maven_packages_spec.rb +++ b/spec/requests/api/maven_packages_spec.rb @@ -15,7 +15,7 @@ RSpec.describe API::MavenPackages do let_it_be(:package_file) { package.package_files.with_file_name_like('%.xml').first } let_it_be(:jar_file) { package.package_files.with_file_name_like('%.jar').first } let_it_be(:personal_access_token) { create(:personal_access_token, user: user) } - let_it_be(:job, reload: true) { create(:ci_build, user: user, status: :running) } + let_it_be(:job, reload: true) { create(:ci_build, user: user, status: :running, project: project) } let_it_be(:deploy_token) { create(:deploy_token, read_package_registry: true, write_package_registry: true) } let_it_be(:project_deploy_token) { create(:project_deploy_token, deploy_token: deploy_token, project: project) } let_it_be(:deploy_token_for_group) { create(:deploy_token, :group, read_package_registry: true, write_package_registry: true) } diff --git a/spec/requests/api/npm_project_packages_spec.rb b/spec/requests/api/npm_project_packages_spec.rb index 8230061546f..ab74da4bda4 100644 --- a/spec/requests/api/npm_project_packages_spec.rb +++ b/spec/requests/api/npm_project_packages_spec.rb @@ -78,7 +78,7 @@ RSpec.describe API::NpmProjectPackages do context 'with a job token for a different user' do let_it_be(:other_user) { create(:user) } - let_it_be_with_reload(:other_job) { create(:ci_build, :running, user: other_user) } + let_it_be_with_reload(:other_job) { create(:ci_build, :running, user: other_user, project: project) } let(:headers) { build_token_auth_header(other_job.token) } diff --git a/spec/requests/api/nuget_project_packages_spec.rb b/spec/requests/api/nuget_project_packages_spec.rb index 98458cb8dfa..572736cfc86 100644 --- a/spec/requests/api/nuget_project_packages_spec.rb +++ b/spec/requests/api/nuget_project_packages_spec.rb @@ -192,7 +192,7 @@ RSpec.describe API::NugetProjectPackages do it_behaves_like 'deploy token for package uploads' it_behaves_like 'job token for package uploads', authorize_endpoint: true do - let_it_be(:job) { create(:ci_build, :running, user: user) } + let_it_be(:job) { create(:ci_build, :running, user: user, project: project) } end it_behaves_like 'rejects nuget access with unknown target id' @@ -260,7 +260,7 @@ RSpec.describe API::NugetProjectPackages do it_behaves_like 'deploy token for package uploads' it_behaves_like 'job token for package uploads' do - let_it_be(:job) { create(:ci_build, :running, user: user) } + let_it_be(:job) { create(:ci_build, :running, user: user, project: project) } end it_behaves_like 'rejects nuget access with unknown target id' diff --git a/spec/requests/api/pypi_packages_spec.rb b/spec/requests/api/pypi_packages_spec.rb index 552ef2b2120..86925e6a0ba 100644 --- a/spec/requests/api/pypi_packages_spec.rb +++ b/spec/requests/api/pypi_packages_spec.rb @@ -13,7 +13,7 @@ RSpec.describe API::PypiPackages do let_it_be(:personal_access_token) { create(:personal_access_token, user: user) } let_it_be(:deploy_token) { create(:deploy_token, read_package_registry: true, write_package_registry: true) } let_it_be(:project_deploy_token) { create(:project_deploy_token, deploy_token: deploy_token, project: project) } - let_it_be(:job) { create(:ci_build, :running, user: user) } + let_it_be(:job) { create(:ci_build, :running, user: user, project: project) } let(:headers) { {} } context 'simple API endpoint' do diff --git a/spec/requests/api/releases_spec.rb b/spec/requests/api/releases_spec.rb index dad3e34404b..81a4fcdbcac 100644 --- a/spec/requests/api/releases_spec.rb +++ b/spec/requests/api/releases_spec.rb @@ -775,7 +775,7 @@ RSpec.describe API::Releases do end context 'when using JOB-TOKEN auth' do - let(:job) { create(:ci_build, user: maintainer) } + let(:job) { create(:ci_build, user: maintainer, project: project) } let(:params) do { name: 'Another release', diff --git a/spec/requests/api/rubygem_packages_spec.rb b/spec/requests/api/rubygem_packages_spec.rb index 09b63139e54..7d863b55bbe 100644 --- a/spec/requests/api/rubygem_packages_spec.rb +++ b/spec/requests/api/rubygem_packages_spec.rb @@ -10,7 +10,7 @@ RSpec.describe API::RubygemPackages do let_it_be_with_reload(:project) { create(:project) } let_it_be(:personal_access_token) { create(:personal_access_token) } let_it_be(:user) { personal_access_token.user } - let_it_be(:job) { create(:ci_build, :running, user: user) } + let_it_be(:job) { create(:ci_build, :running, user: user, project: project) } let_it_be(:deploy_token) { create(:deploy_token, read_package_registry: true, write_package_registry: true) } let_it_be(:project_deploy_token) { create(:project_deploy_token, deploy_token: deploy_token, project: project) } let_it_be(:headers) { {} } diff --git a/spec/requests/api/terraform/modules/v1/packages_spec.rb b/spec/requests/api/terraform/modules/v1/packages_spec.rb index 6803c09b8c2..b04f5ad9a94 100644 --- a/spec/requests/api/terraform/modules/v1/packages_spec.rb +++ b/spec/requests/api/terraform/modules/v1/packages_spec.rb @@ -12,7 +12,7 @@ RSpec.describe API::Terraform::Modules::V1::Packages do let_it_be(:package) { create(:terraform_module_package, project: project) } let_it_be(:personal_access_token) { create(:personal_access_token) } let_it_be(:user) { personal_access_token.user } - let_it_be(:job) { create(:ci_build, :running, user: user) } + let_it_be(:job) { create(:ci_build, :running, user: user, project: project) } let_it_be(:deploy_token) { create(:deploy_token, read_package_registry: true, write_package_registry: true) } let_it_be(:project_deploy_token) { create(:project_deploy_token, deploy_token: deploy_token, project: project) } diff --git a/spec/requests/git_http_spec.rb b/spec/requests/git_http_spec.rb index 279c65fc2f4..7cf46f6adc6 100644 --- a/spec/requests/git_http_spec.rb +++ b/spec/requests/git_http_spec.rb @@ -883,10 +883,10 @@ RSpec.describe 'Git HTTP requests' do context 'when admin mode is enabled', :enable_admin_mode do it_behaves_like 'can download code only' - it 'downloads from other project get status 403' do + it 'downloads from other project get status 404' do clone_get "#{other_project.full_path}.git", user: 'gitlab-ci-token', password: build.token - expect(response).to have_gitlab_http_status(:forbidden) + expect(response).to have_gitlab_http_status(:not_found) end end diff --git a/spec/requests/lfs_http_spec.rb b/spec/requests/lfs_http_spec.rb index 0e3a0252638..fda8b2ecec6 100644 --- a/spec/requests/lfs_http_spec.rb +++ b/spec/requests/lfs_http_spec.rb @@ -569,7 +569,7 @@ RSpec.describe 'Git LFS API and storage' do let(:pipeline) { create(:ci_empty_pipeline, project: other_project) } # I'm not sure what this tests that is different from the previous test - it_behaves_like 'LFS http 403 response' + it_behaves_like 'LFS http 404 response' end end @@ -1043,7 +1043,7 @@ RSpec.describe 'Git LFS API and storage' do let(:pipeline) { create(:ci_empty_pipeline, project: other_project) } # I'm not sure what this tests that is different from the previous test - it_behaves_like 'LFS http 403 response' + it_behaves_like 'LFS http 404 response' end end diff --git a/spec/support/shared_contexts/requests/api/conan_packages_shared_context.rb b/spec/support/shared_contexts/requests/api/conan_packages_shared_context.rb index ac53be1a1cb..c69a987c00d 100644 --- a/spec/support/shared_contexts/requests/api/conan_packages_shared_context.rb +++ b/spec/support/shared_contexts/requests/api/conan_packages_shared_context.rb @@ -8,11 +8,11 @@ RSpec.shared_context 'conan api setup' do let_it_be(:personal_access_token) { create(:personal_access_token) } let_it_be(:user) { personal_access_token.user } let_it_be(:base_secret) { SecureRandom.base64(64) } - let_it_be(:job) { create(:ci_build, :running, user: user) } - let_it_be(:job_token) { job.token } let_it_be(:deploy_token) { create(:deploy_token, read_package_registry: true, write_package_registry: true) } let(:project) { package.project } + let(:job) { create(:ci_build, :running, user: user, project: project) } + let(:job_token) { job.token } let(:auth_token) { personal_access_token.token } let(:project_deploy_token) { create(:project_deploy_token, deploy_token: deploy_token, project: project) } diff --git a/spec/support/shared_contexts/requests/api/npm_packages_shared_context.rb b/spec/support/shared_contexts/requests/api/npm_packages_shared_context.rb index 815108be447..c737091df48 100644 --- a/spec/support/shared_contexts/requests/api/npm_packages_shared_context.rb +++ b/spec/support/shared_contexts/requests/api/npm_packages_shared_context.rb @@ -11,7 +11,7 @@ RSpec.shared_context 'npm api setup' do let_it_be(:package, reload: true) { create(:npm_package, project: project, name: "@#{group.path}/scoped_package") } let_it_be(:token) { create(:oauth_access_token, scopes: 'api', resource_owner: user) } let_it_be(:personal_access_token) { create(:personal_access_token, user: user) } - let_it_be(:job, reload: true) { create(:ci_build, user: user, status: :running) } + let_it_be(:job, reload: true) { create(:ci_build, user: user, status: :running, project: project) } let_it_be(:deploy_token) { create(:deploy_token, read_package_registry: true, write_package_registry: true) } let_it_be(:project_deploy_token) { create(:project_deploy_token, deploy_token: deploy_token, project: project) } diff --git a/yarn.lock b/yarn.lock index 2100dad5b9a..f9a1e6161f8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4014,7 +4014,7 @@ d3-scale-chromatic@1: d3-color "1" d3-interpolate "1" -d3-scale@2, d3-scale@^2.2.2: +d3-scale@2: version "2.2.2" resolved "https://registry.yarnpkg.com/d3-scale/-/d3-scale-2.2.2.tgz#4e880e0b2745acaaddd3ede26a9e908a9e17b81f" integrity sha512-LbeEvGgIb8UMcAa0EATLNX0lelKWGYDQiPdHj+gLblGVhGLyNbaCn3EvrJf0A3Y/uOOU5aD6MTh5ZFCdEwGiCw==