Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
6867eff1f9
commit
060c842402
4
Gemfile
4
Gemfile
|
|
@ -457,9 +457,9 @@ end
|
|||
# Gitaly GRPC protocol definitions
|
||||
gem 'gitaly', '~> 12.9.0.pre.rc4'
|
||||
|
||||
gem 'grpc', '~> 1.24.0'
|
||||
gem 'grpc', '~> 1.27.0'
|
||||
|
||||
gem 'google-protobuf', '~> 3.8.0'
|
||||
gem 'google-protobuf', '~> 3.11.2'
|
||||
|
||||
gem 'toml-rb', '~> 1.0.0'
|
||||
|
||||
|
|
|
|||
10
Gemfile.lock
10
Gemfile.lock
|
|
@ -427,7 +427,7 @@ GEM
|
|||
mime-types (~> 3.0)
|
||||
representable (~> 3.0)
|
||||
retriable (>= 2.0, < 4.0)
|
||||
google-protobuf (3.8.0)
|
||||
google-protobuf (3.11.4)
|
||||
googleapis-common-protos-types (1.0.4)
|
||||
google-protobuf (~> 3.0)
|
||||
googleauth (0.6.6)
|
||||
|
|
@ -468,8 +468,8 @@ GEM
|
|||
graphql (~> 1.6)
|
||||
html-pipeline (~> 2.8)
|
||||
sass (~> 3.4)
|
||||
grpc (1.24.0)
|
||||
google-protobuf (~> 3.8)
|
||||
grpc (1.27.0)
|
||||
google-protobuf (~> 3.11)
|
||||
googleapis-common-protos-types (~> 1.0)
|
||||
gssapi (1.2.0)
|
||||
ffi (>= 1.0.1)
|
||||
|
|
@ -1251,7 +1251,7 @@ DEPENDENCIES
|
|||
gitlab_omniauth-ldap (~> 2.1.1)
|
||||
gon (~> 6.2)
|
||||
google-api-client (~> 0.23)
|
||||
google-protobuf (~> 3.8.0)
|
||||
google-protobuf (~> 3.11.2)
|
||||
gpgme (~> 2.0.19)
|
||||
grape (~> 1.1.0)
|
||||
grape-entity (~> 0.7.1)
|
||||
|
|
@ -1260,7 +1260,7 @@ DEPENDENCIES
|
|||
graphiql-rails (~> 1.4.10)
|
||||
graphql (~> 1.10.5)
|
||||
graphql-docs (~> 1.6.0)
|
||||
grpc (~> 1.24.0)
|
||||
grpc (~> 1.27.0)
|
||||
gssapi
|
||||
guard-rspec
|
||||
haml_lint (~> 0.34.0)
|
||||
|
|
|
|||
|
|
@ -222,7 +222,7 @@ class Projects::EnvironmentsController < Projects::ApplicationController
|
|||
|
||||
def metrics_dashboard_params
|
||||
params
|
||||
.permit(:embedded, :group, :title, :y_label, :dashboard_path, :environment, :sample_metrics)
|
||||
.permit(:embedded, :group, :title, :y_label, :dashboard_path, :environment, :sample_metrics, :embed_json)
|
||||
.merge(dashboard_path: params[:dashboard], environment: environment)
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -3,6 +3,8 @@
|
|||
class EnvironmentsFinder
|
||||
attr_reader :project, :current_user, :params
|
||||
|
||||
InvalidStatesError = Class.new(StandardError)
|
||||
|
||||
def initialize(project, current_user, params = {})
|
||||
@project, @current_user, @params = project, current_user, params
|
||||
end
|
||||
|
|
@ -45,6 +47,9 @@ class EnvironmentsFinder
|
|||
environments = by_name(environments)
|
||||
environments = by_search(environments)
|
||||
|
||||
# Raises InvalidStatesError if params[:states] contains invalid states.
|
||||
environments = by_states(environments)
|
||||
|
||||
environments
|
||||
end
|
||||
|
||||
|
|
@ -91,4 +96,27 @@ class EnvironmentsFinder
|
|||
environments
|
||||
end
|
||||
end
|
||||
|
||||
def by_states(environments)
|
||||
if params[:states].present?
|
||||
environments_with_states(environments)
|
||||
else
|
||||
environments
|
||||
end
|
||||
end
|
||||
|
||||
def environments_with_states(environments)
|
||||
# Convert to array of strings
|
||||
states = Array(params[:states]).map(&:to_s)
|
||||
|
||||
raise InvalidStatesError, _('Requested states are invalid') unless valid_states?(states)
|
||||
|
||||
environments.with_states(states)
|
||||
end
|
||||
|
||||
def valid_states?(states)
|
||||
valid_states = Environment.valid_states.map(&:to_s)
|
||||
|
||||
(states - valid_states).empty?
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -10,6 +10,10 @@ module Resolvers
|
|||
required: false,
|
||||
description: 'Search query'
|
||||
|
||||
argument :states, [GraphQL::STRING_TYPE],
|
||||
required: false,
|
||||
description: 'States of environments that should be included in result'
|
||||
|
||||
type Types::EnvironmentType, null: true
|
||||
|
||||
alias_method :project, :object
|
||||
|
|
@ -18,6 +22,8 @@ module Resolvers
|
|||
return unless project.present?
|
||||
|
||||
EnvironmentsFinder.new(project, context[:current_user], args).find
|
||||
rescue EnvironmentsFinder::InvalidStatesError => exception
|
||||
raise Gitlab::Graphql::Errors::ArgumentError, exception.message
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -119,6 +119,10 @@ class Environment < ApplicationRecord
|
|||
find_or_create_by(name: name)
|
||||
end
|
||||
|
||||
def self.valid_states
|
||||
self.state_machine.states.map(&:name)
|
||||
end
|
||||
|
||||
class << self
|
||||
##
|
||||
# This method returns stop actions (jobs) for multiple environments within one
|
||||
|
|
|
|||
|
|
@ -12,21 +12,48 @@ class UsersStatistics < ApplicationRecord
|
|||
:blocked
|
||||
].freeze
|
||||
|
||||
private
|
||||
class << self
|
||||
def create_current_stats!
|
||||
stats_by_role = highest_role_stats
|
||||
|
||||
def highest_role_stats
|
||||
return unless Feature.enabled?(:users_statistics)
|
||||
create!(
|
||||
without_groups_and_projects: without_groups_and_projects_stats,
|
||||
with_highest_role_guest: stats_by_role[:guest],
|
||||
with_highest_role_reporter: stats_by_role[:reporter],
|
||||
with_highest_role_developer: stats_by_role[:developer],
|
||||
with_highest_role_maintainer: stats_by_role[:maintainer],
|
||||
with_highest_role_owner: stats_by_role[:owner],
|
||||
bots: bot_stats,
|
||||
blocked: blocked_stats
|
||||
)
|
||||
end
|
||||
|
||||
{
|
||||
owner: batch_count_for_access_level(Gitlab::Access::OWNER),
|
||||
maintainer: batch_count_for_access_level(Gitlab::Access::MAINTAINER),
|
||||
developer: batch_count_for_access_level(Gitlab::Access::DEVELOPER),
|
||||
reporter: batch_count_for_access_level(Gitlab::Access::REPORTER),
|
||||
guest: batch_count_for_access_level(Gitlab::Access::GUEST)
|
||||
}
|
||||
end
|
||||
private
|
||||
|
||||
def batch_count_for_access_level(access_level)
|
||||
Gitlab::Database::BatchCount.batch_count(UserHighestRole.with_highest_access_level(access_level))
|
||||
def highest_role_stats
|
||||
{
|
||||
owner: batch_count_for_access_level(Gitlab::Access::OWNER),
|
||||
maintainer: batch_count_for_access_level(Gitlab::Access::MAINTAINER),
|
||||
developer: batch_count_for_access_level(Gitlab::Access::DEVELOPER),
|
||||
reporter: batch_count_for_access_level(Gitlab::Access::REPORTER),
|
||||
guest: batch_count_for_access_level(Gitlab::Access::GUEST)
|
||||
}
|
||||
end
|
||||
|
||||
def without_groups_and_projects_stats
|
||||
batch_count_for_access_level(nil)
|
||||
end
|
||||
|
||||
def bot_stats
|
||||
Gitlab::Database::BatchCount.batch_count(User.bots)
|
||||
end
|
||||
|
||||
def blocked_stats
|
||||
Gitlab::Database::BatchCount.batch_count(User.blocked)
|
||||
end
|
||||
|
||||
def batch_count_for_access_level(access_level)
|
||||
Gitlab::Database::BatchCount.batch_count(UserHighestRole.with_highest_access_level(access_level))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -0,0 +1,35 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
# Acts as a pass-through to allow embeddable dashboards to be
|
||||
# generated based on external data, but still processed with the
|
||||
# required attributes that allow the FE to render them appropriately.
|
||||
#
|
||||
# Use Gitlab::Metrics::Dashboard::Finder to retrive dashboards.
|
||||
module Metrics
|
||||
module Dashboard
|
||||
class TransientEmbedService < ::Metrics::Dashboard::BaseEmbedService
|
||||
extend ::Gitlab::Utils::Override
|
||||
|
||||
class << self
|
||||
def valid_params?(params)
|
||||
[
|
||||
embedded?(params[:embedded]),
|
||||
params[:embed_json]
|
||||
].all?
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
override :get_raw_dashboard
|
||||
def get_raw_dashboard
|
||||
JSON.parse(params[:embed_json])
|
||||
end
|
||||
|
||||
override :sequence
|
||||
def sequence
|
||||
[STAGES::EndpointInserter]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -262,6 +262,13 @@
|
|||
:resource_boundary: :unknown
|
||||
:weight: 1
|
||||
:idempotent:
|
||||
- :name: cronjob:users_create_statistics
|
||||
:feature_category: :users
|
||||
:has_external_dependencies:
|
||||
:urgency: :low
|
||||
:resource_boundary: :unknown
|
||||
:weight: 1
|
||||
:idempotent:
|
||||
- :name: deployment:deployments_finished
|
||||
:feature_category: :continuous_delivery
|
||||
:has_external_dependencies:
|
||||
|
|
|
|||
|
|
@ -0,0 +1,19 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Users
|
||||
class CreateStatisticsWorker # rubocop:disable Scalability/IdempotentWorker
|
||||
include ApplicationWorker
|
||||
# rubocop:disable Scalability/CronWorkerContext
|
||||
# This worker does not perform work scoped to a context
|
||||
include CronjobQueue
|
||||
# rubocop:enable Scalability/CronWorkerContext
|
||||
|
||||
feature_category :users
|
||||
|
||||
def perform
|
||||
UsersStatistics.create_current_stats!
|
||||
rescue ActiveRecord::RecordInvalid => exception
|
||||
Gitlab::ErrorTracking.track_exception(exception)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Add ability to search by environment state in environments GraphQL API
|
||||
merge_request: 28567
|
||||
author:
|
||||
type: changed
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Add Fluentd table for cluster apps
|
||||
merge_request: 28844
|
||||
author:
|
||||
type: added
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Add support for database-independent embedded metric charts
|
||||
merge_request: 28618
|
||||
author:
|
||||
type: added
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Add daily job to create users statistics
|
||||
merge_request: 27883
|
||||
author:
|
||||
type: added
|
||||
|
|
@ -552,6 +552,9 @@ Gitlab.ee do
|
|||
Settings.cron_jobs['sync_seat_link_worker'] ||= Settingslogic.new({})
|
||||
Settings.cron_jobs['sync_seat_link_worker']['cron'] ||= "#{rand(60)} 0 * * *"
|
||||
Settings.cron_jobs['sync_seat_link_worker']['job_class'] = 'SyncSeatLinkWorker'
|
||||
Settings.cron_jobs['users_create_statistics_worker'] ||= Settingslogic.new({})
|
||||
Settings.cron_jobs['users_create_statistics_worker']['cron'] ||= '2 15 * * *'
|
||||
Settings.cron_jobs['users_create_statistics_worker']['job_class'] = 'Users::CreateStatisticsWorker'
|
||||
end
|
||||
|
||||
#
|
||||
|
|
|
|||
|
|
@ -0,0 +1,18 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class CreateClustersApplicationsFluentd < ActiveRecord::Migration[6.0]
|
||||
DOWNTIME = false
|
||||
|
||||
def change
|
||||
create_table :clusters_applications_fluentd do |t|
|
||||
t.integer :protocol, null: false, limit: 2
|
||||
t.integer :status, null: false
|
||||
t.integer :port, null: false
|
||||
t.references :cluster, null: false, index: { unique: true }, foreign_key: { on_delete: :cascade }
|
||||
t.timestamps_with_timezone null: false
|
||||
t.string :version, null: false, limit: 255
|
||||
t.string :host, null: false, limit: 255
|
||||
t.text :status_reason
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -1649,6 +1649,28 @@ CREATE SEQUENCE public.clusters_applications_elastic_stacks_id_seq
|
|||
|
||||
ALTER SEQUENCE public.clusters_applications_elastic_stacks_id_seq OWNED BY public.clusters_applications_elastic_stacks.id;
|
||||
|
||||
CREATE TABLE public.clusters_applications_fluentd (
|
||||
id bigint NOT NULL,
|
||||
protocol smallint NOT NULL,
|
||||
status integer NOT NULL,
|
||||
port integer NOT NULL,
|
||||
cluster_id bigint NOT NULL,
|
||||
created_at timestamp with time zone NOT NULL,
|
||||
updated_at timestamp with time zone NOT NULL,
|
||||
version character varying(255) NOT NULL,
|
||||
host character varying(255) NOT NULL,
|
||||
status_reason text
|
||||
);
|
||||
|
||||
CREATE SEQUENCE public.clusters_applications_fluentd_id_seq
|
||||
START WITH 1
|
||||
INCREMENT BY 1
|
||||
NO MINVALUE
|
||||
NO MAXVALUE
|
||||
CACHE 1;
|
||||
|
||||
ALTER SEQUENCE public.clusters_applications_fluentd_id_seq OWNED BY public.clusters_applications_fluentd.id;
|
||||
|
||||
CREATE TABLE public.clusters_applications_helm (
|
||||
id integer NOT NULL,
|
||||
cluster_id integer NOT NULL,
|
||||
|
|
@ -7018,6 +7040,8 @@ ALTER TABLE ONLY public.clusters_applications_crossplane ALTER COLUMN id SET DEF
|
|||
|
||||
ALTER TABLE ONLY public.clusters_applications_elastic_stacks ALTER COLUMN id SET DEFAULT nextval('public.clusters_applications_elastic_stacks_id_seq'::regclass);
|
||||
|
||||
ALTER TABLE ONLY public.clusters_applications_fluentd ALTER COLUMN id SET DEFAULT nextval('public.clusters_applications_fluentd_id_seq'::regclass);
|
||||
|
||||
ALTER TABLE ONLY public.clusters_applications_helm ALTER COLUMN id SET DEFAULT nextval('public.clusters_applications_helm_id_seq'::regclass);
|
||||
|
||||
ALTER TABLE ONLY public.clusters_applications_ingress ALTER COLUMN id SET DEFAULT nextval('public.clusters_applications_ingress_id_seq'::regclass);
|
||||
|
|
@ -7687,6 +7711,9 @@ ALTER TABLE ONLY public.clusters_applications_crossplane
|
|||
ALTER TABLE ONLY public.clusters_applications_elastic_stacks
|
||||
ADD CONSTRAINT clusters_applications_elastic_stacks_pkey PRIMARY KEY (id);
|
||||
|
||||
ALTER TABLE ONLY public.clusters_applications_fluentd
|
||||
ADD CONSTRAINT clusters_applications_fluentd_pkey PRIMARY KEY (id);
|
||||
|
||||
ALTER TABLE ONLY public.clusters_applications_helm
|
||||
ADD CONSTRAINT clusters_applications_helm_pkey PRIMARY KEY (id);
|
||||
|
||||
|
|
@ -8892,6 +8919,8 @@ CREATE UNIQUE INDEX index_clusters_applications_crossplane_on_cluster_id ON publ
|
|||
|
||||
CREATE UNIQUE INDEX index_clusters_applications_elastic_stacks_on_cluster_id ON public.clusters_applications_elastic_stacks USING btree (cluster_id);
|
||||
|
||||
CREATE UNIQUE INDEX index_clusters_applications_fluentd_on_cluster_id ON public.clusters_applications_fluentd USING btree (cluster_id);
|
||||
|
||||
CREATE UNIQUE INDEX index_clusters_applications_helm_on_cluster_id ON public.clusters_applications_helm USING btree (cluster_id);
|
||||
|
||||
CREATE UNIQUE INDEX index_clusters_applications_ingress_on_cluster_id ON public.clusters_applications_ingress USING btree (cluster_id);
|
||||
|
|
@ -11164,6 +11193,9 @@ ALTER TABLE ONLY public.ci_refs
|
|||
ALTER TABLE ONLY public.ci_resources
|
||||
ADD CONSTRAINT fk_rails_430336af2d FOREIGN KEY (resource_group_id) REFERENCES public.ci_resource_groups(id) ON DELETE CASCADE;
|
||||
|
||||
ALTER TABLE ONLY public.clusters_applications_fluentd
|
||||
ADD CONSTRAINT fk_rails_4319b1dcd2 FOREIGN KEY (cluster_id) REFERENCES public.clusters(id) ON DELETE CASCADE;
|
||||
|
||||
ALTER TABLE ONLY public.lfs_file_locks
|
||||
ADD CONSTRAINT fk_rails_43df7a0412 FOREIGN KEY (project_id) REFERENCES public.projects(id) ON DELETE CASCADE;
|
||||
|
||||
|
|
@ -12976,6 +13008,7 @@ COPY "schema_migrations" (version) FROM STDIN;
|
|||
20200331220930
|
||||
20200402123926
|
||||
20200402135250
|
||||
20200402185044
|
||||
20200403184110
|
||||
20200403185127
|
||||
20200403185422
|
||||
|
|
|
|||
|
|
@ -5875,6 +5875,11 @@ type Project {
|
|||
Search query
|
||||
"""
|
||||
search: String
|
||||
|
||||
"""
|
||||
States of environments that should be included in result
|
||||
"""
|
||||
states: [String!]
|
||||
): EnvironmentConnection
|
||||
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -17785,6 +17785,24 @@
|
|||
},
|
||||
"defaultValue": null
|
||||
},
|
||||
{
|
||||
"name": "states",
|
||||
"description": "States of environments that should be included in result",
|
||||
"type": {
|
||||
"kind": "LIST",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "NON_NULL",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "SCALAR",
|
||||
"name": "String",
|
||||
"ofType": null
|
||||
}
|
||||
}
|
||||
},
|
||||
"defaultValue": null
|
||||
},
|
||||
{
|
||||
"name": "after",
|
||||
"description": "Returns the elements in the list that come after the specified cursor.",
|
||||
|
|
|
|||
|
|
@ -182,7 +182,7 @@ before_script:
|
|||
## Assuming you created the SSH_KNOWN_HOSTS variable, uncomment the
|
||||
## following two lines.
|
||||
##
|
||||
- echo "$SSH_KNOWN_HOSTS" > ~/.ssh/known_hosts
|
||||
- echo "$SSH_KNOWN_HOSTS" >> ~/.ssh/known_hosts
|
||||
- chmod 644 ~/.ssh/known_hosts
|
||||
|
||||
##
|
||||
|
|
@ -199,7 +199,7 @@ before_script:
|
|||
## WARNING: Use this only with the Docker executor, if you use it with shell
|
||||
## you will overwrite your user's SSH config.
|
||||
##
|
||||
#- '[[ -f /.dockerenv ]] && echo -e "Host *\n\tStrictHostKeyChecking no\n\n" > ~/.ssh/config'
|
||||
#- '[[ -f /.dockerenv ]] && echo -e "Host *\n\tStrictHostKeyChecking no\n\n" >> ~/.ssh/config'
|
||||
```
|
||||
|
||||
## Example project
|
||||
|
|
|
|||
|
|
@ -298,6 +298,9 @@ Some analyzers make it possible to filter out vulnerabilities under a given thre
|
|||
| `SAST_FLAWFINDER_LEVEL` | 1 | Ignore Flawfinder vulnerabilities under given risk level. Integer, 0=No risk, 5=High risk. |
|
||||
| `SAST_GITLEAKS_ENTROPY_LEVEL` | 8.0 | Minimum entropy for secret detection. Float, 0.0 = low, 8.0 = high. |
|
||||
| `SAST_GOSEC_LEVEL` | 0 | Ignore gosec vulnerabilities under given confidence level. Integer, 0=Undefined, 1=Low, 2=Medium, 3=High. |
|
||||
| `SAST_GITLEAKS_COMMIT_FROM` | - | The commit a gitleaks scan starts at. |
|
||||
| `SAST_GITLEAKS_COMMIT_TO` | - | The commit a gitleaks scan ends at. |
|
||||
| `SAST_GITLEAKS_HISTORIC_SCAN` | false | Flag to enable a historic gitleaks scan. |
|
||||
|
||||
#### Timeouts
|
||||
|
||||
|
|
|
|||
|
|
@ -110,7 +110,7 @@ module API
|
|||
return unless %w[git-receive-pack git-upload-pack git-upload-archive].include?(action)
|
||||
|
||||
{
|
||||
repository: repository.gitaly_repository,
|
||||
repository: repository.gitaly_repository.to_h,
|
||||
address: Gitlab::GitalyClient.address(container.repository_storage),
|
||||
token: Gitlab::GitalyClient.token(container.repository_storage),
|
||||
features: Feature::Gitaly.server_feature_flags
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ module Gitlab
|
|||
::Metrics::Dashboard::GitlabAlertEmbedService,
|
||||
::Metrics::Dashboard::CustomMetricEmbedService,
|
||||
::Metrics::Dashboard::GrafanaMetricEmbedService,
|
||||
::Metrics::Dashboard::TransientEmbedService,
|
||||
::Metrics::Dashboard::DynamicEmbedService,
|
||||
::Metrics::Dashboard::DefaultEmbedService,
|
||||
::Metrics::Dashboard::SystemDashboardService,
|
||||
|
|
|
|||
|
|
@ -16997,6 +16997,9 @@ msgstr ""
|
|||
msgid "Requested design version does not exist"
|
||||
msgstr ""
|
||||
|
||||
msgid "Requested states are invalid"
|
||||
msgstr ""
|
||||
|
||||
msgid "Requests Profiles"
|
||||
msgstr ""
|
||||
|
||||
|
|
|
|||
|
|
@ -7,6 +7,14 @@ FactoryBot.define do
|
|||
association :project, :repository
|
||||
sequence(:external_url) { |n| "https://env#{n}.example.gitlab.com" }
|
||||
|
||||
trait :available do
|
||||
state { :available }
|
||||
end
|
||||
|
||||
trait :stopped do
|
||||
state { :stopped }
|
||||
end
|
||||
|
||||
trait :with_review_app do |environment|
|
||||
transient do
|
||||
ref { 'master' }
|
||||
|
|
|
|||
|
|
@ -0,0 +1,6 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
FactoryBot.define do
|
||||
factory :users_statistics do
|
||||
end
|
||||
end
|
||||
|
|
@ -136,6 +136,36 @@ describe 'Metrics rendering', :js, :use_clean_rails_memory_store_caching, :sidek
|
|||
end
|
||||
end
|
||||
|
||||
context 'transient metrics embeds' do
|
||||
let(:metrics_url) { urls.metrics_project_environment_url(project, environment, embed_json: embed_json) }
|
||||
let(:title) { 'Important Metrics' }
|
||||
let(:embed_json) do
|
||||
{
|
||||
panel_groups: [{
|
||||
panels: [{
|
||||
type: "line-graph",
|
||||
title: title,
|
||||
y_label: "metric",
|
||||
metrics: [{
|
||||
query_range: "metric * 0.5 < 1"
|
||||
}]
|
||||
}]
|
||||
}]
|
||||
}.to_json
|
||||
end
|
||||
|
||||
before do
|
||||
stub_any_prometheus_request_with_response
|
||||
end
|
||||
|
||||
it 'shows embedded metrics' do
|
||||
visit project_issue_path(project, issue)
|
||||
|
||||
expect(page).to have_css('div.prometheus-graph')
|
||||
expect(page).to have_text(title)
|
||||
end
|
||||
end
|
||||
|
||||
def import_common_metrics
|
||||
::Gitlab::DatabaseImporters::CommonMetrics::Importer.new.execute
|
||||
end
|
||||
|
|
|
|||
|
|
@ -3,15 +3,15 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe EnvironmentsFinder do
|
||||
let(:project) { create(:project, :repository) }
|
||||
let(:user) { project.creator }
|
||||
let(:environment) { create(:environment, :available, project: project) }
|
||||
|
||||
before do
|
||||
project.add_maintainer(user)
|
||||
end
|
||||
|
||||
describe '#execute' do
|
||||
let(:project) { create(:project, :repository) }
|
||||
let(:user) { project.creator }
|
||||
let(:environment) { create(:environment, project: project) }
|
||||
|
||||
before do
|
||||
project.add_maintainer(user)
|
||||
end
|
||||
|
||||
context 'tagged deployment' do
|
||||
let(:environment_two) { create(:environment, project: project) }
|
||||
# Environments need to include commits, so rewind two commits to fit
|
||||
|
|
@ -124,4 +124,53 @@ describe EnvironmentsFinder do
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#find' do
|
||||
context 'with states parameter' do
|
||||
let(:stopped_environment) { create(:environment, :stopped, project: project) }
|
||||
|
||||
it 'returns environments with the requested state' do
|
||||
result = described_class.new(project, user, states: 'available').find
|
||||
|
||||
expect(result).to contain_exactly(environment)
|
||||
end
|
||||
|
||||
it 'returns environments with any of the requested states' do
|
||||
result = described_class.new(project, user, states: %w(available stopped)).find
|
||||
|
||||
expect(result).to contain_exactly(environment, stopped_environment)
|
||||
end
|
||||
|
||||
it 'raises exception when requested state is invalid' do
|
||||
expect { described_class.new(project, user, states: %w(invalid stopped)).find }.to(
|
||||
raise_error(described_class::InvalidStatesError, 'Requested states are invalid')
|
||||
)
|
||||
end
|
||||
|
||||
context 'works with symbols' do
|
||||
it 'returns environments with the requested state' do
|
||||
result = described_class.new(project, user, states: :available).find
|
||||
|
||||
expect(result).to contain_exactly(environment)
|
||||
end
|
||||
|
||||
it 'returns environments with any of the requested states' do
|
||||
result = described_class.new(project, user, states: [:available, :stopped]).find
|
||||
|
||||
expect(result).to contain_exactly(environment, stopped_environment)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'with search and states' do
|
||||
let(:environment2) { create(:environment, :stopped, name: 'test2', project: project) }
|
||||
let(:environment3) { create(:environment, :available, name: 'test3', project: project) }
|
||||
|
||||
it 'searches environments by name and state' do
|
||||
result = described_class.new(project, user, search: 'test', states: :available).find
|
||||
|
||||
expect(result).to contain_exactly(environment3)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -10,9 +10,9 @@ describe Resolvers::EnvironmentsResolver do
|
|||
context "with a group" do
|
||||
let(:group) { create(:group) }
|
||||
let(:project) { create(:project, :public, group: group) }
|
||||
let!(:environment1) { create(:environment, name: 'production', project: project) }
|
||||
let!(:environment2) { create(:environment, name: 'test', project: project) }
|
||||
let!(:environment3) { create(:environment, name: 'test2', project: project) }
|
||||
let!(:environment1) { create(:environment, :available, name: 'production', project: project) }
|
||||
let!(:environment2) { create(:environment, :stopped, name: 'test', project: project) }
|
||||
let!(:environment3) { create(:environment, :available, name: 'test2', project: project) }
|
||||
|
||||
before do
|
||||
group.add_developer(current_user)
|
||||
|
|
@ -41,6 +41,18 @@ describe Resolvers::EnvironmentsResolver do
|
|||
end
|
||||
end
|
||||
|
||||
context 'with states' do
|
||||
it 'searches environments by state' do
|
||||
expect(resolve_environments(states: ['available'])).to contain_exactly(environment1, environment3)
|
||||
end
|
||||
|
||||
it 'returns error if requested state is invalid' do
|
||||
expect { resolve_environments(states: ['invalid']) }.to(
|
||||
raise_error(Gitlab::Graphql::Errors::ArgumentError)
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when project is nil' do
|
||||
subject { resolve(described_class, obj: nil, args: {}, ctx: { current_user: current_user }) }
|
||||
|
||||
|
|
|
|||
|
|
@ -98,6 +98,17 @@ describe Gitlab::Metrics::Dashboard::ServiceSelector do
|
|||
|
||||
it { is_expected.to be Metrics::Dashboard::GrafanaMetricEmbedService }
|
||||
end
|
||||
|
||||
context 'with the embed defined in the arguments' do
|
||||
let(:arguments) do
|
||||
{
|
||||
embedded: true,
|
||||
embed_json: '{}'
|
||||
}
|
||||
end
|
||||
|
||||
it { is_expected.to be Metrics::Dashboard::TransientEmbedService }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -0,0 +1,43 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe UsersStatistics do
|
||||
describe '.create_current_stats!' do
|
||||
before do
|
||||
create_list(:user_highest_role, 4)
|
||||
create_list(:user_highest_role, 2, :guest)
|
||||
create_list(:user_highest_role, 3, :reporter)
|
||||
create_list(:user_highest_role, 4, :developer)
|
||||
create_list(:user_highest_role, 3, :maintainer)
|
||||
create_list(:user_highest_role, 2, :owner)
|
||||
create_list(:user, 2, :bot)
|
||||
create_list(:user, 1, :blocked)
|
||||
|
||||
allow(ActiveRecord::Base.connection).to receive(:transaction_open?).and_return(false)
|
||||
end
|
||||
|
||||
context 'when successful' do
|
||||
it 'creates an entry with the current statistics values' do
|
||||
expect(described_class.create_current_stats!).to have_attributes(
|
||||
without_groups_and_projects: 4,
|
||||
with_highest_role_guest: 2,
|
||||
with_highest_role_reporter: 3,
|
||||
with_highest_role_developer: 4,
|
||||
with_highest_role_maintainer: 3,
|
||||
with_highest_role_owner: 2,
|
||||
bots: 2,
|
||||
blocked: 1
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when unsuccessful' do
|
||||
it 'raises an ActiveRecord::RecordInvalid exception' do
|
||||
allow(UsersStatistics).to receive(:create!).and_raise(ActiveRecord::RecordInvalid)
|
||||
|
||||
expect { described_class.create_current_stats! }.to raise_error(ActiveRecord::RecordInvalid)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,72 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
describe Metrics::Dashboard::TransientEmbedService, :use_clean_rails_memory_store_caching do
|
||||
let_it_be(:project) { build(:project) }
|
||||
let_it_be(:user) { create(:user) }
|
||||
let_it_be(:environment) { create(:environment, project: project) }
|
||||
|
||||
before do
|
||||
project.add_maintainer(user)
|
||||
end
|
||||
|
||||
describe '.valid_params?' do
|
||||
let(:params) { { embedded: 'true', embed_json: '{}' } }
|
||||
|
||||
subject { described_class.valid_params?(params) }
|
||||
|
||||
it { is_expected.to be_truthy }
|
||||
|
||||
context 'missing embedded' do
|
||||
let(:params) { { embed_json: '{}' } }
|
||||
|
||||
it { is_expected.to be_falsey }
|
||||
end
|
||||
|
||||
context 'not embedded' do
|
||||
let(:params) { { embedded: 'false', embed_json: '{}' } }
|
||||
|
||||
it { is_expected.to be_falsey }
|
||||
end
|
||||
|
||||
context 'missing embed_json' do
|
||||
let(:params) { { embedded: 'true' } }
|
||||
|
||||
it { is_expected.to be_falsey }
|
||||
end
|
||||
end
|
||||
|
||||
describe '#get_dashboard' do
|
||||
let(:embed_json) do
|
||||
{
|
||||
panel_groups: [{
|
||||
panels: [{
|
||||
type: 'line-graph',
|
||||
title: 'title',
|
||||
y_label: 'y_label',
|
||||
metrics: [{
|
||||
query_range: 'up',
|
||||
label: 'y_label'
|
||||
}]
|
||||
}]
|
||||
}]
|
||||
}.to_json
|
||||
end
|
||||
let(:service_params) { [project, user, { environment: environment, embedded: 'true', embed_json: embed_json }] }
|
||||
let(:service_call) { described_class.new(*service_params).get_dashboard }
|
||||
|
||||
it_behaves_like 'valid embedded dashboard service response'
|
||||
it_behaves_like 'raises error for users with insufficient permissions'
|
||||
|
||||
it 'caches the unprocessed dashboard for subsequent calls' do
|
||||
expect_any_instance_of(described_class)
|
||||
.to receive(:get_raw_dashboard)
|
||||
.once
|
||||
.and_call_original
|
||||
|
||||
described_class.new(*service_params).get_dashboard
|
||||
described_class.new(*service_params).get_dashboard
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
describe Users::CreateStatisticsWorker do
|
||||
describe '#perform' do
|
||||
subject { described_class.new.perform }
|
||||
|
||||
before do
|
||||
allow(ActiveRecord::Base.connection).to receive(:transaction_open?).and_return(false)
|
||||
end
|
||||
|
||||
context 'when successful' do
|
||||
it 'create an users statistics entry' do
|
||||
expect { subject }.to change { UsersStatistics.count }.from(0).to(1)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when unsuccessful' do
|
||||
it 'logs an error' do
|
||||
users_statistics = build(:users_statistics)
|
||||
users_statistics.errors.add(:base, 'This is an error')
|
||||
exception = ActiveRecord::RecordInvalid.new(users_statistics)
|
||||
|
||||
allow(UsersStatistics).to receive(:create_current_stats!).and_raise(exception)
|
||||
|
||||
expect(Gitlab::ErrorTracking).to receive(:track_exception).with(exception).and_call_original
|
||||
|
||||
subject
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
Loading…
Reference in New Issue