提交 27b73daa 编写于 作者: G GitLab Bot

Add latest changes from gitlab-org/gitlab@master

上级 691ed55a
<script>
import { GlSingleStat } from '@gitlab/ui/dist/charts';
import { __ } from '~/locale';
import { SUPPORTED_FORMATS, getFormatter } from '~/lib/utils/unit_format';
import { graphDataValidatorForValues } from '../../utils';
const defaultPrecision = 2;
const emptyStateMsg = __('No data to display');
export default {
components: {
......@@ -21,6 +23,9 @@ export default {
queryInfo() {
return this.graphData.metrics[0];
},
queryMetric() {
return this.queryInfo.result[0]?.metric;
},
queryResult() {
return this.queryInfo.result[0]?.value[1];
},
......@@ -33,6 +38,12 @@ export default {
statValue() {
let formatter;
// if field is present the metric value is not displayed. Hence
// the early exit without formatting.
if (this.graphData?.field) {
return this.queryMetric?.[this.graphData.field] ?? emptyStateMsg;
}
if (this.graphData?.maxValue) {
formatter = getFormatter(SUPPORTED_FORMATS.percent);
return formatter(this.queryResult / Number(this.graphData.maxValue), defaultPrecision);
......
......@@ -173,6 +173,7 @@ const mapPanelToViewModel = ({
x_label,
y_label,
y_axis = {},
field,
metrics = [],
links = [],
max_value,
......@@ -193,6 +194,7 @@ const mapPanelToViewModel = ({
y_label: yAxis.name, // Changing y_label to yLabel is pending https://gitlab.com/gitlab-org/gitlab/issues/207198
yAxis,
xAxis,
field,
maxValue: max_value,
links: links.map(mapLinksToViewModel),
metrics: mapToMetricsViewModel(metrics),
......
---
title: Display metric label in single stat
merge_request: 35289
author:
type: added
......@@ -254,6 +254,13 @@ These results can also be placed into a PostgreSQL database by setting the
`RSPEC_PROFILING_POSTGRES_URL` variable. This is used to profile the test suite
when running in the CI environment.
We store these results also when running CI jobs on the default branch on
`gitlab.com`. Statistics of these profiling data are [available
online](https://gitlab-org.gitlab.io/rspec_profiling_stats/). For example,
you can find which tests take longest to run or which execute the most
queries. This can be handy for optimizing our tests or identifying performance
issues in our code.
## Memory profiling
One of the reasons of the increased memory footprint could be Ruby memory fragmentation.
......
......@@ -545,6 +545,99 @@ In order to ensure that a clean wrapper object and DOM are being used in each te
See also the [Vue Test Utils documentation on `destroy`](https://vue-test-utils.vuejs.org/api/wrapper/#destroy).
### Jest best practices
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/34209) in GitLab 13.2.
#### Prefer `toBe` over `toEqual` when comparing primitive values
Jest has [`toBe`](https://jestjs.io/docs/en/expect#tobevalue) and
[`toEqual`](https://jestjs.io/docs/en/expect#toequalvalue) matchers.
As [`toBe`](https://jestjs.io/docs/en/expect#tobevalue) uses
[`Object.is`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is)
to compare values, it's faster (by default) than using `toEqual`.
While the latter will eventually fallback to leverage [`Object.is`](https://github.com/facebook/jest/blob/master/packages/expect/src/jasmineUtils.ts#L91),
for primitive values, it should only be used when complex objects need a comparison.
Examples:
```javascript
const foo = 1;
// good
expect(foo).toBe(1);
// bad
expect(foo).toEqual(1);
```
#### Prefer more befitting matchers
Jest provides useful matchers like `toHaveLength` or `toBeUndefined` to make your tests more
readable and to produce more understandable error messages. Check their docs for the
[full list of matchers](https://jestjs.io/docs/en/expect#methods).
Examples:
```javascript
const arr = [1, 2];
// prints:
// Expected length: 1
// Received length: 2
expect(arr).toHaveLength(1);
// prints:
// Expected: 1
// Received: 2
expect(arr.length).toBe(1);
// prints:
// expect(received).toBe(expected) // Object.is equality
// Expected: undefined
// Received: "bar"
const foo = 'bar';
expect(foo).toBe(undefined);
// prints:
// expect(received).toBeUndefined()
// Received: "bar"
const foo = 'bar';
expect(foo).toBeUndefined();
```
#### Avoid using `toBeTruthy` or `toBeFalsy`
Jest also provides following matchers: `toBeTruthy` and `toBeFalsy`. We should not use them because
they make tests weaker and produce false-positive results.
For example, `expect(someBoolean).toBeFalsy()` passes when `someBoolean === null`, and when
`someBoolean === false`.
#### Tricky `toBeDefined` matcher
Jest has the tricky `toBeDefined` matcher that can produce false positive test. Because it
[validates](https://github.com/facebook/jest/blob/master/packages/expect/src/matchers.ts#L204)
the given value for `undefined` only.
```javascript
// good
expect(wrapper.find('foo').exists()).toBe(true);
// bad
// if finder returns null, the test will pass
expect(wrapper.find('foo')).toBeDefined();
```
#### Avoid using `setImmediate`
Try to avoid using `setImmediate`. `setImmediate` is an ad-hoc solution to run your callback after
the I/O completes. And it's not part of the Web API, hence, we target NodeJS environments in our
unit tests.
Instead of `setImmediate`, use `jest.runAllTimers` or `jest.runOnlyPendingTimers` to run pending timers.
The latter is useful when you have `setInterval` in the code. **Remember:** our Jest configuration uses fake timers.
## Factories
TBU
......
......@@ -74,7 +74,7 @@ You can also dismiss vulnerabilities in the table:
1. Select the checkbox for each vulnerability you want to dismiss.
1. In the menu that appears, select the reason for dismissal and click **Dismiss Selected**.
![Project Security Dashboard](img/project_security_dashboard_v13_2_noNav.png)
![Project Security Dashboard](img/project_security_dashboard_v13_2.png)
## Group Security Dashboard
......
......@@ -139,13 +139,13 @@ module Gitlab
%r{\Arubocop/cop/migration(/|\.rb)} => :database,
%r{\A(\.gitlab-ci\.yml\z|\.gitlab\/ci)} => :engineering_productivity,
%r{\A\.codeclimate\.yml\z} => :engineering_productivity,
%r{\A\.overcommit\.yml\.example\z} => :engineering_productivity,
%r{\Atooling/overcommit/} => :engineering_productivity,
%r{\A.editorconfig\z} => :engineering_productivity,
%r{\A\.editorconfig\z} => :engineering_productivity,
%r{Dangerfile\z} => :engineering_productivity,
%r{\A(ee/)?(danger/|lib/gitlab/danger/)} => :engineering_productivity,
%r{\A(ee/)?scripts/} => :engineering_productivity,
%r{\A\.codeclimate\.yml\z} => :engineering_productivity,
%r{\Atooling/} => :engineering_productivity,
%r{\A(ee/)?app/(?!assets|views)[^/]+} => :backend,
%r{\A(ee/)?(bin|config|generator_templates|lib|rubocop)/} => :backend,
......
......@@ -752,6 +752,9 @@ msgstr ""
msgid "(external source)"
msgstr ""
msgid "(line: %{startLine})"
msgstr ""
msgid "(removed)"
msgstr ""
......@@ -25677,6 +25680,9 @@ msgstr ""
msgid "Vulnerability|File"
msgstr ""
msgid "Vulnerability|Identifier"
msgstr ""
msgid "Vulnerability|Identifiers"
msgstr ""
......
......@@ -13,6 +13,10 @@ module QA
@secondary_node = @second_node
end
def enable_writes
shell "docker exec praefect bash -c '/opt/gitlab/embedded/bin/praefect -config /var/opt/gitlab/praefect/config.toml enable-writes -virtual-storage default'"
end
def stop_primary_node
shell "docker stop #{@primary_node}"
@secondary_node, @primary_node = @primary_node, @secondary_node
......
......@@ -10,7 +10,7 @@ module QA
end
end
let(:initial_file) { 'pushed_to_primary.txt' }
let(:final_file) { 'pushed_to_secondary.txt' }
let(:final_file) { 'committed_to_primary.txt' }
let(:praefect_manager) { Service::PraefectManager.new }
before do
......@@ -41,11 +41,13 @@ module QA
expect(show).to have_file(initial_file)
end
praefect_manager.enable_writes
Resource::Repository::Commit.fabricate_via_api! do |commit|
commit.project = project
commit.add_files([
{
file_path: 'committed_to_primary.txt',
file_path: final_file,
content: 'This should exist on both nodes too'
}
])
......
......@@ -3,7 +3,7 @@
require 'pathname'
module QA
context 'Configure' do
RSpec.describe 'Configure' do
let(:project) do
Resource::Project.fabricate_via_api! do |project|
project.name = Runtime::Env.auto_devops_project_name || 'autodevops-project'
......
# frozen_string_literal: true
module QA
context 'Configure' do
RSpec.describe 'Configure' do
describe 'Kubernetes Cluster Integration', :orchestrated, :kubernetes, :requires_admin, :skip_live_env do
context 'Project Clusters' do
let!(:cluster) { Service::KubernetesCluster.new(provider_class: Service::ClusterProvider::K3s).create! }
......
# frozen_string_literal: true
require 'gitlab'
require_relative File.expand_path('../../lib/quality/helm3_client.rb', __dir__)
require_relative File.expand_path('../../lib/quality/kubernetes_client.rb', __dir__)
require_relative File.expand_path('../../tooling/lib/tooling/helm3_client.rb', __dir__)
require_relative File.expand_path('../../tooling/lib/tooling/kubernetes_client.rb', __dir__)
class AutomatedCleanup
attr_reader :project_path, :gitlab_token
......@@ -44,11 +44,11 @@ class AutomatedCleanup
end
def helm
@helm ||= Quality::Helm3Client.new(namespace: review_apps_namespace)
@helm ||= Tooling::Helm3Client.new(namespace: review_apps_namespace)
end
def kubernetes
@kubernetes ||= Quality::KubernetesClient.new(namespace: review_apps_namespace)
@kubernetes ||= Tooling::KubernetesClient.new(namespace: review_apps_namespace)
end
def perform_gitlab_environment_cleanup!(days_for_stop:, days_for_delete:)
......@@ -76,7 +76,7 @@ class AutomatedCleanup
if deployed_at < delete_threshold
deleted_environment = delete_environment(environment, deployment)
if deleted_environment
release = Quality::Helm3Client::Release.new(environment.slug, 1, deployed_at.to_s, nil, nil, review_apps_namespace)
release = Tooling::Helm3Client::Release.new(environment.slug, 1, deployed_at.to_s, nil, nil, review_apps_namespace)
releases_to_delete << release
end
else
......@@ -157,11 +157,11 @@ class AutomatedCleanup
helm.delete(release_name: releases_names)
kubernetes.cleanup(release_name: releases_names, wait: false)
rescue Quality::Helm3Client::CommandFailedError => ex
rescue Tooling::Helm3Client::CommandFailedError => ex
raise ex unless ignore_exception?(ex.message, IGNORED_HELM_ERRORS)
puts "Ignoring the following Helm error:\n#{ex}\n"
rescue Quality::KubernetesClient::CommandFailedError => ex
rescue Tooling::KubernetesClient::CommandFailedError => ex
raise ex unless ignore_exception?(ex.message, IGNORED_KUBERNETES_ERRORS)
puts "Ignoring the following Kubernetes error:\n#{ex}\n"
......
import { shallowMount } from '@vue/test-utils';
import SingleStatChart from '~/monitoring/components/charts/single_stat.vue';
import { singleStatMetricsResult } from '../../mock_data';
import { singleStatMetricsResult, singleStatMetricsWithFieldResult } from '../../mock_data';
describe('Single Stat Chart component', () => {
let singleStatChart;
......@@ -66,6 +66,31 @@ describe('Single Stat Chart component', () => {
expect(singleStatChart.vm.statValue).toContain('NaN');
});
describe('field attribute', () => {
it('displays a label value instead of metric value when field attribute is used', () => {
singleStatChart.setProps({
graphData: singleStatMetricsWithFieldResult,
});
return singleStatChart.vm.$nextTick(() => {
expect(singleStatChart.vm.statValue).toContain('prometheus');
});
});
it('displays No data to display if field attribute is not present', () => {
singleStatChart.setProps({
graphData: {
...singleStatMetricsWithFieldResult,
field: 'this-does-not-exist',
},
});
return singleStatChart.vm.$nextTick(() => {
expect(singleStatChart.vm.statValue).toContain('No data to display');
});
});
});
});
});
});
......@@ -359,6 +359,11 @@ export const singleStatMetricsResult = {
],
};
export const singleStatMetricsWithFieldResult = {
...singleStatMetricsResult,
field: 'job',
};
export const graphDataPrometheusQueryRangeMultiTrack = {
title: 'Super Chart A3',
type: 'heatmap',
......
......@@ -10,6 +10,7 @@ describe('MRWidgetSuggestPipeline', () => {
let trackingSpy;
const mockTrackingOnWrapper = () => {
unmockTracking();
trackingSpy = mockTracking('_category_', wrapper.element, jest.spyOn);
};
......
# frozen_string_literal: true
require 'fast_spec_helper'
require_relative '../../../../tooling/lib/tooling/helm3_client'
RSpec.describe Quality::Helm3Client do
RSpec.describe Tooling::Helm3Client do
let(:namespace) { 'review-apps' }
let(:release_name) { 'my-release' }
let(:raw_helm_list_page1) do
......
# frozen_string_literal: true
require 'fast_spec_helper'
require_relative '../../../../tooling/lib/tooling/kubernetes_client'
RSpec.describe Quality::KubernetesClient do
RSpec.describe Tooling::KubernetesClient do
let(:namespace) { 'review-apps' }
let(:release_name) { 'my-release' }
let(:pod_for_release) { "pod-my-release-abcd" }
......
......@@ -3,7 +3,7 @@
require_relative '../../../../tooling/lib/tooling/test_file_finder'
RSpec.describe Tooling::TestFileFinder do
subject { Tooling::TestFileFinder.new(file) }
subject { described_class.new(file) }
describe '#test_files' do
context 'when given non .rb files' do
......
# frozen_string_literal: true
require 'time'
require_relative '../gitlab/popen' unless defined?(Gitlab::Popen)
require 'json'
require_relative '../../../lib/gitlab/popen' unless defined?(Gitlab::Popen)
require_relative '../../../lib/gitlab/json' unless defined?(Gitlab::Json)
module Quality
module Tooling
class Helm3Client
CommandFailedError = Class.new(StandardError)
......@@ -65,12 +67,12 @@ module Quality
%(--output json),
*args
]
releases = JSON.parse(run_command(command))
releases = Gitlab::Json.parse(run_command(command))
releases.map do |release|
Release.new(*release.values_at(*RELEASE_JSON_ATTRIBUTES))
end
rescue JSON::ParserError => ex
rescue ::JSON::ParserError => ex
puts "Ignoring this JSON parsing error: #{ex}" # rubocop:disable Rails/Output
[]
end
......
# frozen_string_literal: true
require_relative '../gitlab/popen' unless defined?(Gitlab::Popen)
require_relative '../../../lib/gitlab/popen' unless defined?(Gitlab::Popen)
require_relative '../../../lib/gitlab/json' unless defined?(Gitlab::JSON)
module Quality
module Tooling
class KubernetesClient
RESOURCE_LIST = 'ingress,svc,pdb,hpa,deploy,statefulset,job,pod,secret,configmap,pvc,secret,clusterrole,clusterrolebinding,role,rolebinding,sa,crd'
CommandFailedError = Class.new(StandardError)
......
......@@ -843,10 +843,10 @@
eslint-plugin-vue "^6.2.1"
vue-eslint-parser "^7.0.0"
"@gitlab/svgs@1.148.0":
version "1.148.0"
resolved "https://registry.yarnpkg.com/@gitlab/svgs/-/svgs-1.148.0.tgz#cb3fd68249d7e97d0c578bf443459a32370a6dba"
integrity sha512-5GJtUNjCBzEdfi1J3jZPr7UUsvZ1KYnzK3VkMPmp+t2GNWHtdqBmi3Y6WKTOWJo8qFIAJO0tIs6w7XMMCIUBCg==
"@gitlab/svgs@1.150.0":
version "1.150.0"
resolved "https://registry.yarnpkg.com/@gitlab/svgs/-/svgs-1.150.0.tgz#7dbbdf1b50c4409adf30d23710bbe4515608e245"
integrity sha512-jfD1EiawNlBM1XNEz7hriPJg2UOX6zE2/lKMIocSpkg9R58VGyIr+oyWOTn2AfknrepsLfnTiGJNveLdRYcy4w==
"@gitlab/ui@17.16.0":
version "17.16.0"
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册