diff --git a/.rubocop.yml b/.rubocop.yml index fc29bf071c5..a3f1f57f140 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -56,7 +56,7 @@ Style/FrozenStringLiteralComment: - 'qa/**/*' - 'rubocop/**/*' - 'scripts/**/*' - - 'spec/lib/**/*' + - 'spec/lib/gitlab/**/*' RSpec/FilePath: Exclude: diff --git a/app/assets/javascripts/helpers/monitor_helper.js b/app/assets/javascripts/helpers/monitor_helper.js index 2c2a04d5b5e..900f0cf5bb8 100644 --- a/app/assets/javascripts/helpers/monitor_helper.js +++ b/app/assets/javascripts/helpers/monitor_helper.js @@ -1,17 +1,30 @@ /* eslint-disable import/prefer-default-export */ +import _ from 'underscore'; +/** + * @param {Array} queryResults - Array of Result objects + * @param {Object} defaultConfig - Default chart config values (e.g. lineStyle, name) + * @returns {Array} The formatted values + */ export const makeDataSeries = (queryResults, defaultConfig) => - queryResults.reduce((acc, result) => { - const data = result.values.filter(([, value]) => !Number.isNaN(value)); - if (!data.length) { - return acc; - } - const relevantMetric = defaultConfig.name.toLowerCase().replace(' ', '_'); - const name = result.metric[relevantMetric]; - const series = { data }; - if (name) { - series.name = `${defaultConfig.name}: ${name}`; - } + queryResults + .map(result => { + const data = result.values.filter(([, value]) => !Number.isNaN(value)); + if (!data.length) { + return null; + } + const relevantMetric = defaultConfig.name.toLowerCase().replace(' ', '_'); + const name = result.metric[relevantMetric]; + const series = { data }; + if (name) { + series.name = `${defaultConfig.name}: ${name}`; + } else { + const template = _.template(defaultConfig.name, { + interpolate: /\{\{(.+?)\}\}/g, + }); + series.name = template(result.metric); + } - return acc.concat({ ...defaultConfig, ...series }); - }, []); + return { ...defaultConfig, ...series }; + }) + .filter(series => series !== null); diff --git a/app/assets/javascripts/lib/utils/datetime_utility.js b/app/assets/javascripts/lib/utils/datetime_utility.js index e2d188103bc..e9d8d0a4184 100644 --- a/app/assets/javascripts/lib/utils/datetime_utility.js +++ b/app/assets/javascripts/lib/utils/datetime_utility.js @@ -553,14 +553,10 @@ export const calculateRemainingMilliseconds = endDate => { * * @param {Date} date the date that we will substract days from * @param {number} daysInPast number of days that are subtracted from a given date - * @returns {String} Date string in ISO format + * @returns {Date} Date in past as Date object */ -export const getDateInPast = (date, daysInPast) => { - const dateClone = newDate(date); - return new Date( - dateClone.setTime(dateClone.getTime() - daysInPast * 24 * 60 * 60 * 1000), - ).toISOString(); -}; +export const getDateInPast = (date, daysInPast) => + new Date(newDate(date).setDate(date.getDate() - daysInPast)); export const beginOfDayTime = 'T00:00:00Z'; export const endOfDayTime = 'T23:59:59Z'; diff --git a/app/assets/javascripts/monitoring/stores/actions.js b/app/assets/javascripts/monitoring/stores/actions.js index 2f793a9e162..c1fe7aa7e71 100644 --- a/app/assets/javascripts/monitoring/stores/actions.js +++ b/app/assets/javascripts/monitoring/stores/actions.js @@ -7,7 +7,7 @@ import { s__, __ } from '../../locale'; const MAX_REQUESTS = 3; -function backOffRequest(makeRequestCallback) { +export function backOffRequest(makeRequestCallback) { let requestCounter = 0; return backOff((next, stop) => { makeRequestCallback() @@ -111,8 +111,7 @@ export const fetchDashboard = ({ state, dispatch }, params) => { params.dashboard = state.currentDashboard; } - return axios - .get(state.dashboardEndpoint, { params }) + return backOffRequest(() => axios.get(state.dashboardEndpoint, { params })) .then(resp => resp.data) .then(response => { dispatch('receiveMetricsDashboardSuccess', { response, params }); diff --git a/app/models/application_setting.rb b/app/models/application_setting.rb index cfae79ec016..07335b6a883 100644 --- a/app/models/application_setting.rb +++ b/app/models/application_setting.rb @@ -18,12 +18,6 @@ class ApplicationSetting < ApplicationRecord # fix a lot of tests using allow_any_instance_of include ApplicationSettingImplementation - attr_encrypted :asset_proxy_secret_key, - mode: :per_attribute_iv, - insecure_mode: true, - key: Settings.attr_encrypted_db_key_base_truncated, - algorithm: 'aes-256-cbc' - serialize :restricted_visibility_levels # rubocop:disable Cop/ActiveRecordSerialize serialize :import_sources # rubocop:disable Cop/ActiveRecordSerialize serialize :disabled_oauth_sign_in_sources, Array # rubocop:disable Cop/ActiveRecordSerialize @@ -286,6 +280,12 @@ class ApplicationSetting < ApplicationRecord pass: :external_auth_client_key_pass, if: -> (setting) { setting.external_auth_client_cert.present? } + attr_encrypted :asset_proxy_secret_key, + mode: :per_attribute_iv, + key: Settings.attr_encrypted_db_key_base_truncated, + algorithm: 'aes-256-cbc', + insecure_mode: true + attr_encrypted :external_auth_client_key, mode: :per_attribute_iv, key: Settings.attr_encrypted_db_key_base_truncated, diff --git a/changelogs/unreleased/34755-refactor-getdateinpast-to-return-date-object.yml b/changelogs/unreleased/34755-refactor-getdateinpast-to-return-date-object.yml new file mode 100644 index 00000000000..0a374daa7d2 --- /dev/null +++ b/changelogs/unreleased/34755-refactor-getdateinpast-to-return-date-object.yml @@ -0,0 +1,5 @@ +--- +title: Change return type of getDateInPast to Date +merge_request: 19081 +author: +type: changed diff --git a/doc/development/i18n/translation.md b/doc/development/i18n/translation.md index c1a6fd8983c..50a417e9996 100644 --- a/doc/development/i18n/translation.md +++ b/doc/development/i18n/translation.md @@ -83,7 +83,7 @@ Therefore "create a new user" would translate into "Benutzer(in) anlegen". ### Updating the glossary To propose additions to the glossary please -[open an issue](https://gitlab.com/gitlab-org/gitlab-foss/issues). +[open an issue](https://gitlab.com/gitlab-org/gitlab/issues?scope=all&utf8=✓&state=all&label_name[]=Category%3AInternationalization). ## French Translation Guidelines diff --git a/doc/policy/maintenance.md b/doc/policy/maintenance.md index d118c2f40cb..ef94236d711 100644 --- a/doc/policy/maintenance.md +++ b/doc/policy/maintenance.md @@ -30,21 +30,68 @@ The following table describes the version types and their release cadence: ## Patch releases -Patch releases usually only include bug fixes and are only done for the current -stable release. That said, in some cases, we may backport it to previous stable +Our current policy is to support **only the current stable release** at any given time. + +Patch releases **only include bug fixes** for the current stable released version of +GitLab. + +These two policies are in place because: + +1. GitLab has Community and Enterprise distributions, doubling the amount of work +necessary to test/release the software. +1. Backporting to more than one release creates a high development, quality assurance, +and support cost. +1. Supporting parallel version discourages incremental upgrades which over time accumulate in +complexity and create upgrade challenges for all users. GitLab has a dedicated team ensuring that +incremental upgrades (and installations) are as simple as possible. +1. The number of changes created in the GitLab application is high, which contributes to backporting complexity to older releases. In number of cases, backporting has to go through the same +review process a new change goes through. +1. Ensuring that tests pass on older release is a considerable challenge in some cases, and as such is very time consuming. + +Including new features in patch releases is not possible as that would break [Semantic Versioning]. +Breaking [Semantic Versioning] has the following consequences for users that +have to adhere to various internal requirements (e.g. org. compliance, verifying new features and similar): + +1. Inability to quickly upgrade to leverage bug fixes included in patch versions. +1. Inability to quickly upgrade to leverage security fixes included in patch versions. +1. Requirements consisting of extensive testing for not only stable GitLab release, but every patch version. + +In cases where a strategic user has a requirement to test a feature before it is +officially released, we can offer to create a Release Candidate (RC) version that will +include the specific feature. This should be needed only in extreme cases, and can be requested for consideration by raising an issue in [release/tasks] issue tracker. +It is important to note that the Release Candidate will also contain other +features and changes as it is not possible to easily isolate a specific feature (similar reasons as noted above). The Release Candidate will be no different than any code that is deployed to GitLab.com or is publicly accessible. + +### Backporting to older releases + +Backporting to more than one stable release is reserved for [security releases](#security-releases). +In some cases however, we may need to backport *a bug fix* to more than one stable release, depending on the severity of the bug. -For instance, if we release `10.1.1` with a fix for a severe bug introduced in -`10.0.0`, we could backport the fix to a new `10.0.x` patch release. +Decision on whether backporting a change will be performed is done at the discretion of the [current release managers][release-managers], similar to what is described in the [managing bugs] process, based on *all* of the following: + +1. Estimated [severity][severity-labels] of the bug: Highest possible impact to users based on the current definition of severity. + +1. Estimated [priority][priority-labels] of the bug: Immediate impact on all impacted users based on the above estimated severity. + +1. Potentially incurring data loss and/or security breach. + +1. Potentially affecting one or more strategic accounts due to a proven inability by the user to upgrade to the current stable version. + +If *all* of the above are satisfied, the backport releases can be created for +the current stable stable release, and two previous monthly releases. +For instance, if we release `11.2.1` with a fix for a severe bug introduced in +`11.0.0`, we could backport the fix to a new `11.0.x`, and `11.1.x` patch release. + +To request backporting to more than one stable release for consideration, raise an issue in [release/tasks] issue tracker. ### Security releases Security releases are a special kind of patch release that only include security fixes and patches (see below). -Our current policy is to support one stable release at any given time, but for -medium-level security issues, we may backport security fixes to the previous two -monthly releases. +Our current policy is to backport security fixes to the previous two +monthly releases in addition to the current stable release. For very serious security issues, there is [precedent](https://about.gitlab.com/blog/2016/05/02/cve-2016-4340-patches/) @@ -91,3 +138,9 @@ Please see the table below for some examples: More information about the release procedures can be found in our [release documentation](https://gitlab.com/gitlab-org/release/docs). You may also want to read our [Responsible Disclosure Policy](https://about.gitlab.com/security/disclosure/). + +[release-managers]: https://about.gitlab.com/community/release-managers/ +[priority-definition]: ../development/contributing/issue_workflow.md#priority-labels +[severity-labels]: ../development/contributing/issue_workflow.html#severity-labels +[managing bugs]: https://gitlab.com/gitlab-org/gitlab/blob/master/PROCESS.md#managing-bugs +[release/tasks]: https://gitlab.com/gitlab-org/release/tasks/issues diff --git a/doc/user/project/integrations/prometheus.md b/doc/user/project/integrations/prometheus.md index 3be403bab86..841f558d25c 100644 --- a/doc/user/project/integrations/prometheus.md +++ b/doc/user/project/integrations/prometheus.md @@ -139,7 +139,7 @@ GitLab supports a limited set of [CI variables](../../../ci/variables/README.htm - CI_ENVIRONMENT_SLUG - KUBE_NAMESPACE -To specify a variable in a query, enclose it in curly braces with a leading percent. For example: `%{ci_environment_slug}`. +To specify a variable in a query, enclose it in quotation marks with curly braces with a leading percent. For example: `"%{ci_environment_slug}"`. ### Defining custom dashboards per project diff --git a/lib/gitlab/daemon.rb b/lib/gitlab/daemon.rb index 8a253893892..ddb9d907640 100644 --- a/lib/gitlab/daemon.rb +++ b/lib/gitlab/daemon.rb @@ -28,6 +28,10 @@ module Gitlab true end + def thread_name + self.class.name.demodulize.underscore + end + def start return unless enabled? @@ -35,7 +39,10 @@ module Gitlab break thread if thread? if start_working - @thread = Thread.new { run_thread } + @thread = Thread.new do + Thread.current.name = thread_name + run_thread + end end end end diff --git a/lib/gitlab/sidekiq_daemon/monitor.rb b/lib/gitlab/sidekiq_daemon/monitor.rb index a3d61c69ae1..0723b514c90 100644 --- a/lib/gitlab/sidekiq_daemon/monitor.rb +++ b/lib/gitlab/sidekiq_daemon/monitor.rb @@ -4,6 +4,7 @@ module Gitlab module SidekiqDaemon class Monitor < Daemon include ::Gitlab::Utils::StrongMemoize + extend ::Gitlab::Utils::Override NOTIFICATION_CHANNEL = 'sidekiq:cancel:notifications' CANCEL_DEADLINE = 24.hours.seconds @@ -24,6 +25,11 @@ module Gitlab @jobs_mutex = Mutex.new end + override :thread_name + def thread_name + "job_monitor" + end + def within_job(worker_class, jid, queue) jobs_mutex.synchronize do jobs[jid] = { worker_class: worker_class, thread: Thread.current, started_at: Gitlab::Metrics::System.monotonic_time } diff --git a/spec/frontend/helpers/monitor_helper_spec.js b/spec/frontend/helpers/monitor_helper_spec.js index 2e8bff298c4..c36b603b251 100644 --- a/spec/frontend/helpers/monitor_helper_spec.js +++ b/spec/frontend/helpers/monitor_helper_spec.js @@ -41,5 +41,76 @@ describe('monitor helper', () => { ), ).toEqual([{ ...expectedDataSeries[0], data: [[1, 1]] }]); }); + + it('updates series name from templates', () => { + const config = { + ...defaultConfig, + name: '{{cmd}}', + }; + + const [result] = monitorHelper.makeDataSeries( + [{ metric: { cmd: 'brpop' }, values: series }], + config, + ); + + expect(result.name).toEqual('brpop'); + }); + + it('supports space-padded template expressions', () => { + const config = { + ...defaultConfig, + name: 'backend: {{ backend }}', + }; + + const [result] = monitorHelper.makeDataSeries( + [{ metric: { backend: 'HA Server' }, values: series }], + config, + ); + + expect(result.name).toEqual('backend: HA Server'); + }); + + it('supports repeated template variables', () => { + const config = { ...defaultConfig, name: '{{cmd}}, {{cmd}}' }; + + const [result] = monitorHelper.makeDataSeries( + [{ metric: { cmd: 'brpop' }, values: series }], + config, + ); + + expect(result.name).toEqual('brpop, brpop'); + }); + + it('updates multiple series names from templates', () => { + const config = { + ...defaultConfig, + name: '{{job}}: {{cmd}}', + }; + + const [result] = monitorHelper.makeDataSeries( + [{ metric: { cmd: 'brpop', job: 'redis' }, values: series }], + config, + ); + + expect(result.name).toEqual('redis: brpop'); + }); + + it('updates name for each series', () => { + const config = { + ...defaultConfig, + name: '{{cmd}}', + }; + + const [firstSeries, secondSeries] = monitorHelper.makeDataSeries( + [ + { metric: { cmd: 'brpop' }, values: series }, + { metric: { cmd: 'zrangebyscore' }, values: series }, + ], + config, + ); + + expect(firstSeries.name).toEqual('brpop'); + expect(secondSeries.name).toEqual('zrangebyscore'); + }); }); }); diff --git a/spec/frontend/lib/utils/datetime_utility_spec.js b/spec/frontend/lib/utils/datetime_utility_spec.js index 149ce331ae5..e4c97543b03 100644 --- a/spec/frontend/lib/utils/datetime_utility_spec.js +++ b/spec/frontend/lib/utils/datetime_utility_spec.js @@ -428,17 +428,19 @@ describe('newDate', () => { }); describe('getDateInPast', () => { - const date = new Date(1563235200000); // 2019-07-16T00:00:00.000Z; + const date = new Date('2019-07-16T00:00:00.000Z'); const daysInPast = 90; it('returns the correct date in the past', () => { const dateInPast = datetimeUtility.getDateInPast(date, daysInPast); - expect(dateInPast).toBe('2019-04-17T00:00:00.000Z'); + const expectedDateInPast = new Date('2019-04-17T00:00:00.000Z'); + + expect(dateInPast).toStrictEqual(expectedDateInPast); }); it('does not modifiy the original date', () => { datetimeUtility.getDateInPast(date, daysInPast); - expect(date).toStrictEqual(new Date(1563235200000)); + expect(date).toStrictEqual(new Date('2019-07-16T00:00:00.000Z')); }); }); diff --git a/spec/frontend/monitoring/store/actions_spec.js b/spec/frontend/monitoring/store/actions_spec.js new file mode 100644 index 00000000000..513a0e0d103 --- /dev/null +++ b/spec/frontend/monitoring/store/actions_spec.js @@ -0,0 +1,53 @@ +import axios from '~/lib/utils/axios_utils'; +import MockAdapter from 'axios-mock-adapter'; +import { TEST_HOST } from 'helpers/test_constants'; +import { backOffRequest } from '~/monitoring/stores/actions'; +import statusCodes from '~/lib/utils/http_status'; +import { backOff } from '~/lib/utils/common_utils'; + +jest.mock('~/lib/utils/common_utils'); + +const MAX_REQUESTS = 3; + +describe('Monitoring store helpers', () => { + let mock; + + // Mock underlying `backOff` function to remove in-built delay. + backOff.mockImplementation( + callback => + new Promise((resolve, reject) => { + const stop = arg => (arg instanceof Error ? reject(arg) : resolve(arg)); + const next = () => callback(next, stop); + callback(next, stop); + }), + ); + + beforeEach(() => { + mock = new MockAdapter(axios); + }); + + afterEach(() => { + mock.restore(); + }); + + describe('backOffRequest', () => { + it('returns immediately when recieving a 200 status code', () => { + mock.onGet(TEST_HOST).reply(200); + + return backOffRequest(() => axios.get(TEST_HOST)).then(() => { + expect(mock.history.get.length).toBe(1); + }); + }); + + it(`repeats the network call ${MAX_REQUESTS} times when receiving a 204 response`, done => { + mock.onGet(TEST_HOST).reply(statusCodes.NO_CONTENT, {}); + + backOffRequest(() => axios.get(TEST_HOST)) + .then(done.fail) + .catch(() => { + expect(mock.history.get.length).toBe(MAX_REQUESTS); + done(); + }); + }); + }); +}); diff --git a/spec/frontend/releases/list/components/__snapshots__/release_block_spec.js.snap b/spec/frontend/releases/list/components/__snapshots__/release_block_spec.js.snap deleted file mode 100644 index 8f2c0427c83..00000000000 --- a/spec/frontend/releases/list/components/__snapshots__/release_block_spec.js.snap +++ /dev/null @@ -1,332 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`Release block with default props matches the snapshot 1`] = ` -
-
-
-

- - New release - - -

- - - - -
- -
-
- - - - c22b0728 - -
- -
- - - - v0.3 - -
- -
- - - - Milestones - -
- - - - 13.6 - - - - • - - - - 13.5 - - - - - -
- - • - - - - released 1 month ago - - -
- - -
- -
- - - Assets - - - 5 - - - - - - -
- -
-
-

- A super nice release! -

-
-
-
-
-`; diff --git a/spec/frontend/releases/list/components/release_block_spec.js b/spec/frontend/releases/list/components/release_block_spec.js index 93f202b2977..6601c4265f6 100644 --- a/spec/frontend/releases/list/components/release_block_spec.js +++ b/spec/frontend/releases/list/components/release_block_spec.js @@ -39,34 +39,18 @@ describe('Release block', () => { const milestoneListLabel = () => wrapper.find('.js-milestone-list-label'); const editButton = () => wrapper.find('.js-edit-button'); - const RealDate = Date; beforeEach(() => { - // timeago.js calls Date(), so let's mock that case to avoid time-dependent test failures. - const constantDate = new Date('2019-10-25T00:12:00'); - - /* eslint no-global-assign:off */ - global.Date = jest.fn((...props) => - props.length ? new RealDate(...props) : new RealDate(constantDate), - ); - - Object.assign(Date, RealDate); - releaseClone = JSON.parse(JSON.stringify(release)); }); afterEach(() => { wrapper.destroy(); - global.Date = RealDate; }); describe('with default props', () => { beforeEach(() => factory(release)); - it('matches the snapshot', () => { - expect(wrapper.element).toMatchSnapshot(); - }); - it("renders the block with an id equal to the release's tag name", () => { expect(wrapper.attributes().id).toBe('v0.3'); }); diff --git a/spec/lib/banzai/filter/asset_proxy_filter_spec.rb b/spec/lib/banzai/filter/asset_proxy_filter_spec.rb index 0c4ccbf28f4..ff2346fe1ba 100644 --- a/spec/lib/banzai/filter/asset_proxy_filter_spec.rb +++ b/spec/lib/banzai/filter/asset_proxy_filter_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Banzai::Filter::AssetProxyFilter do diff --git a/spec/lib/omni_auth/strategies/saml_spec.rb b/spec/lib/omni_auth/strategies/saml_spec.rb index 3c59de86d98..73e86872308 100644 --- a/spec/lib/omni_auth/strategies/saml_spec.rb +++ b/spec/lib/omni_auth/strategies/saml_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe OmniAuth::Strategies::SAML, type: :strategy do