Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2022-09-23 00:10:38 +00:00
parent aaedbff77d
commit a7698afc6e
37 changed files with 349 additions and 73 deletions

View File

@ -1 +1 @@
e4d8f69ffa2efd3f2cb0adff5fa66f367f66f6fb
9498ab9459048cc595d8e2e411b027d080c0ab0f

View File

@ -2,6 +2,12 @@
source 'https://rubygems.org'
if ENV['BUNDLER_CHECKSUM_VERIFICATION_OPT_IN'] # this verification is still experimental
$:.unshift(File.expand_path("vendor/gems/bundler-checksum/lib", __dir__))
require 'bundler-checksum'
Bundler::Checksum.patch!
end
gem 'bundler-checksum', '~> 0.1.0', path: 'vendor/gems/bundler-checksum', require: false
gem 'rails', '~> 6.1.6.1'

View File

@ -205,11 +205,11 @@
{"name":"gitlab-labkit","version":"0.24.0","platform":"ruby","checksum":"8f16e5aa4e0a05be58958fe880bdd53c84b659a081ea9981d2b510922a4a0548"},
{"name":"gitlab-license","version":"2.2.1","platform":"ruby","checksum":"39fcf6be8b2887df8afe01b5dcbae8d08b7c5d937ff56b0fb40484a8c4f02d30"},
{"name":"gitlab-mail_room","version":"0.0.9","platform":"ruby","checksum":"6700374b5c0aa9d9ad4e711aeb677f0b7d415a6d01d3baa699efab25349d851c"},
{"name":"gitlab-markup","version":"1.8.0","platform":"ruby","checksum":"fb629369dca5dd343e47ebf5fa2e0a0fc146012fc49c35eff5ca826ae4186c86"},
{"name":"gitlab-markup","version":"1.8.1","platform":"ruby","checksum":"ab1f9fd016977497c2af25b76341dea670533014f406861834a0bd99f646707b"},
{"name":"gitlab-net-dns","version":"0.9.1","platform":"ruby","checksum":"bcd1a08dcb31b731e8ff602d828de619d2d9f53f5812f6abacf11c720873d4cb"},
{"name":"gitlab-omniauth-openid-connect","version":"0.10.0","platform":"ruby","checksum":"ea44a23ea93457057bba6a9912e883f5aefab36a941c6c58512c8a7095fb1153"},
{"name":"gitlab-sidekiq-fetcher","version":"0.8.0","platform":"ruby","checksum":"9c564caa2a958d44a8d78672dc23b2a206102d0223b41b77b58626a945e37362"},
{"name":"gitlab-styles","version":"8.0.0","platform":"ruby","checksum":"ff77d8543b7093e387430ebdaa2adce9dc3caaa8b10ce9bf9b40242d5f7b66da"},
{"name":"gitlab-styles","version":"9.0.0","platform":"ruby","checksum":"ef0edfab8f807a5be2309ba24dfc44fec5ba52ed68b87167c051e9ffdadb3bad"},
{"name":"gitlab_chronic_duration","version":"0.10.6.2","platform":"ruby","checksum":"6dda4cfe7dca9b958f163ac8835c3d9cc70cf8df8cbb89bb2fbf9ba4375105fb"},
{"name":"gitlab_omniauth-ldap","version":"2.2.0","platform":"ruby","checksum":"bb4d20acb3b123ed654a8f6a47d3fac673ece7ed0b6992edb92dca14bad2838c"},
{"name":"globalid","version":"1.0.0","platform":"ruby","checksum":"1253641b1dc3392721c964351773755d75135d3d3c5cc65d88b0a3880a60bed8"},
@ -364,7 +364,7 @@
{"name":"notiffany","version":"0.1.3","platform":"ruby","checksum":"d37669605b7f8dcb04e004e6373e2a780b98c776f8eb503ac9578557d7808738"},
{"name":"numerizer","version":"0.2.0","platform":"ruby","checksum":"e58076d5ee5370417b7e52d9cb25836d62acd1b8d9a194c308707986c1705d7b"},
{"name":"oauth","version":"0.5.6","platform":"ruby","checksum":"4085fe28e0c5e2434135e00a6555294fd2a4ff96a98d1bdecdcd619fc6368dff"},
{"name":"oauth2","version":"2.0.3","platform":"ruby","checksum":"b343d8665a936b4223b335b38f8640134ce14e07c540b8d17e8bbac0b4e5c41b"},
{"name":"oauth2","version":"2.0.9","platform":"ruby","checksum":"b21f9defcf52dc1610e0dfab4c868342173dcd707fd15c777d9f4f04e153f7fb"},
{"name":"octokit","version":"4.25.1","platform":"ruby","checksum":"c02092ee82dcdfe84db0e0ea630a70d32becc54245a4f0bacfd21c010df09b96"},
{"name":"ohai","version":"16.10.6","platform":"ruby","checksum":"b835806e585faea4ac8346b68c722fb5fc29a29f73fd7e3a022f9073132dec22"},
{"name":"oj","version":"3.13.21","platform":"ruby","checksum":"aef31a8dcc6f0b9b4bb5cc7ac6cc5272b2d851deb11a1804c2ed6b5501b50e46"},
@ -380,7 +380,6 @@
{"name":"omniauth-oauth","version":"1.2.0","platform":"ruby","checksum":"e7a78658498dc83aa3f3be1a776425c0f06a60d45d9236dbe5e98e61fadf827b"},
{"name":"omniauth-oauth2","version":"1.7.3","platform":"ruby","checksum":"3f5a8f99fa72e0f91d2abd7475ceb972a4ae67ed59e049f314c0c1bad81f4745"},
{"name":"omniauth-oauth2-generic","version":"0.2.2","platform":"ruby","checksum":"e30814f6c472e04f3d9e4a3ddc03bc9a46f53f9333f8d443bf3ad43c6ebcdbd4"},
{"name":"omniauth-rails_csrf_protection","version":"1.0.1","platform":"ruby","checksum":"fc546aeb7d43b7b9d7737051c380156e61c8f080b898cd4934d523eaa7e59acf"},
{"name":"omniauth-saml","version":"2.0.0","platform":"ruby","checksum":"02594fd6630de26a9e65a2e64223e9ad32324fa97a6c7f1f22a1553ea3dd44c7"},
{"name":"omniauth-shibboleth","version":"1.3.0","platform":"ruby","checksum":"b0bb725ced5cb76fbfc187ddbb8ad6864d0cd5df714cab36a528df8ee4b1d113"},
{"name":"omniauth-twitter","version":"1.4.0","platform":"ruby","checksum":"c5cc6c77cd767745ffa9ebbd5fbd694a3fa99d1d2d82a4d7def0bf3b6131b264"},
@ -405,7 +404,7 @@
{"name":"pg","version":"1.4.1","platform":"x64-mingw-ucrt","checksum":"de35769d4e7c25daa035f2dc33447e74711ab0dc8b73f685a846987e0080d030"},
{"name":"pg","version":"1.4.1","platform":"x64-mingw32","checksum":"3457bf6bfdda7144097ef23d490a83980ba4572c78c58689aadaf58940a1989d"},
{"name":"pg","version":"1.4.1","platform":"x86-mingw32","checksum":"323d09138b7bbfc6ae8eb427774d3639fc0e995983e65bb729527bf8e859fc29"},
{"name":"pg_query","version":"2.1.3","platform":"ruby","checksum":"f3dd4b4c88c638eab48e9274f0dd88c584b60f8da58e3008b873192fe1e47001"},
{"name":"pg_query","version":"2.1.4","platform":"ruby","checksum":"48f1363f88cf9d86fa11d76d1b0f839ca3723b8bd397b7cbc4b578e1ca82d0bb"},
{"name":"plist","version":"3.6.0","platform":"ruby","checksum":"f468bcf6b72ec6d1585ed6744eb4817c1932a5bf91895ed056e69b7f12ca10f2"},
{"name":"png_quantizator","version":"0.2.1","platform":"ruby","checksum":"6023d4d064125c3a7e02929c95b7320ed6ac0d7341f9e8de0c9ea6576ef3106b"},
{"name":"po_to_json","version":"1.0.1","platform":"ruby","checksum":"6a7188aa6c42a22c9718f9b39062862ef7f3d8f6a7b4177cae058c3308b56af7"},
@ -444,7 +443,6 @@
{"name":"rainbow","version":"3.1.1","platform":"ruby","checksum":"039491aa3a89f42efa1d6dec2fc4e62ede96eb6acd95e52f1ad581182b79bc6a"},
{"name":"rake","version":"13.0.6","platform":"ruby","checksum":"5ce4bf5037b4196c24ac62834d8db1ce175470391026bd9e557d669beeb19097"},
{"name":"randexp","version":"0.1.7","platform":"ruby","checksum":"3026510ecf6a8e8642b9b96fa44bb41af6d24058023b7df77cf280f08e14e4c8"},
{"name":"rash_alt","version":"0.4.12","platform":"ruby","checksum":"1d4a6dd5b8fdcecc6b777c0b924dbf31c125ddd8d805e72dc9359db8324e1607"},
{"name":"rb-fsevent","version":"0.11.2","platform":"ruby","checksum":"43900b972e7301d6570f64b850a5aa67833ee7d87b458ee92805d56b7318aefe"},
{"name":"rb-inotify","version":"0.10.1","platform":"ruby","checksum":"050062d4f31d307cca52c3f6a7f4b946df8de25fc4bd373e1a5142e41034a7ca"},
{"name":"rbtrace","version":"0.4.14","platform":"ruby","checksum":"162bbf89cecabfc4f09c869b655f6f3a679c4870ebb7cbdcadf7393a81cc1769"},
@ -457,7 +455,7 @@
{"name":"redcarpet","version":"3.5.1","platform":"ruby","checksum":"717f64cb6ec11c8d9ec9b521ed26ca2eeda68b4fe1fc3388a641176dbd47732f"},
{"name":"redis","version":"4.7.1","platform":"ruby","checksum":"ecb256d4e53ead3eca05bf394dd100e6a162c136f461fe752ddf5d35b64a2df6"},
{"name":"redis-actionpack","version":"5.3.0","platform":"ruby","checksum":"3fb1ad0a8fd9d26a289c9399bb609dcaef38bf37711e6f677a53ca728fc19140"},
{"name":"redis-namespace","version":"1.8.1","platform":"ruby","checksum":"c81707d693e5e754c690b4e1fa68ecfa8bd1028fbf306e533b3832e44e76c54c"},
{"name":"redis-namespace","version":"1.9.0","platform":"ruby","checksum":"0923961f38cf15b86cb57d92507e0a3b32480729eb5033249f5de8b12e0d8612"},
{"name":"redis-rack","version":"2.1.4","platform":"ruby","checksum":"0872eecb303e483c3863d6bd0d47323d230640d41c1a4ac4a2c7596ec0b1774c"},
{"name":"redis-store","version":"1.9.1","platform":"ruby","checksum":"7b4c7438d46f7b7ce8f67fc0eda3a04fc67d32d28cf606cc98a5df4d2b77071d"},
{"name":"regexp_parser","version":"2.5.0","platform":"ruby","checksum":"a076d2d35ab8d11feab5fecf8aa09ec6df68c2429810748cba079f7b021ecde5"},
@ -489,13 +487,13 @@
{"name":"rspec-support","version":"3.10.2","platform":"ruby","checksum":"74315f89069fbaf2a710e2117340373b77ee45eceb026de87e0cad9dd5154596"},
{"name":"rspec_junit_formatter","version":"0.4.1","platform":"ruby","checksum":"3788f9b3fabc6284b93493cf4b3a80cba2b59b3a774b95f39dd7886d5faed6ab"},
{"name":"rspec_profiling","version":"0.0.6","platform":"ruby","checksum":"7a45697f79dcec9a174a0e26703465f6bd52ee78e8d798741240bfcef38f6e6e"},
{"name":"rubocop","version":"0.93.1","platform":"ruby","checksum":"73b44fbbe872edbd3f14487175b6369a0f48e952c155f305896ffa56c48b195e"},
{"name":"rubocop","version":"1.36.0","platform":"ruby","checksum":"368e47dcab8417419949bbadb11ec41fd94e6b785f8bff4f9cc56a1ddf60ffac"},
{"name":"rubocop-ast","version":"1.21.0","platform":"ruby","checksum":"8f5d98611343498602de2d41bc583aca71599daad16daeadaeeee60f134c9568"},
{"name":"rubocop-gitlab-security","version":"0.1.1","platform":"ruby","checksum":"96f6ed727847a5876ddfc89ee0399438a1aef7934db773c7efce907e2720006c"},
{"name":"rubocop-graphql","version":"0.14.6","platform":"ruby","checksum":"b40f2cbac9990ece44eb85eec5c5ae04fca1e197c07c790ac1ca60600b55bdad"},
{"name":"rubocop-performance","version":"1.9.2","platform":"ruby","checksum":"3373ad82b70189fa16b593b6237eb06186da669d468b7d6483bca64c0a844a05"},
{"name":"rubocop-rails","version":"2.9.1","platform":"ruby","checksum":"2d8d113c3ae074c78c89cb706b4a08116d730bf92dbef148798498171435c540"},
{"name":"rubocop-rspec","version":"1.44.1","platform":"ruby","checksum":"7b2238e7d6cf17a925a90992914f3cd8ecd68b65b31710d60a3f7f647f8a8b2a"},
{"name":"rubocop-performance","version":"1.14.3","platform":"ruby","checksum":"ee45ae3e40388ff809d9c5e2ef6ef9d59dc86c59c97110f96d5540267f860751"},
{"name":"rubocop-rails","version":"2.15.2","platform":"ruby","checksum":"1891ab46a6eaf36b841ad27c9c8a22e77a2c3ae85bc334111d3f8075e417643c"},
{"name":"rubocop-rspec","version":"2.12.1","platform":"ruby","checksum":"9278d22d4525261caf30d591eef3d47910a125e74f75f41ffa470acd208423f9"},
{"name":"ruby-fogbugz","version":"0.2.1","platform":"ruby","checksum":"15b2e7fe7e95b021a94ee6e9d8bb32fdad6ae44e820c2ce0dc312fe6e77d40ca"},
{"name":"ruby-magic","version":"0.5.4","platform":"ruby","checksum":"2c17b185130d10a83791f63a40baa358c4b138af37da3f4dab53690121c421d5"},
{"name":"ruby-prof","version":"1.3.1","platform":"ruby","checksum":"e735d20c92954e1fa2a4475539c99dfc8d0166b4cc6915ca309e8ee2dd19323c"},
@ -544,6 +542,7 @@
{"name":"simplecov_json_formatter","version":"0.1.4","platform":"ruby","checksum":"529418fbe8de1713ac2b2d612aa3daa56d316975d307244399fa4838c601b428"},
{"name":"sixarm_ruby_unaccent","version":"1.2.0","platform":"ruby","checksum":"0043a6077bdf2c4b03040152676a07f8bf77144f9b007b1960ee5c94d13a4384"},
{"name":"slack-messenger","version":"2.3.4","platform":"ruby","checksum":"49c611d2be5b0f9c250a3a957b9cc09b9c07b81dacb9843642d87b6fa35609c1"},
{"name":"snaky_hash","version":"2.0.0","platform":"ruby","checksum":"fe8b2e39e8ff69320f7812af73ea06401579e29ff1734a7009567391600687de"},
{"name":"snowplow-tracker","version":"0.6.1","platform":"ruby","checksum":"9cec52fd060619f4974b3dc1f7d9a2776c5e31b668a6ead53145b9780e312314"},
{"name":"solargraph","version":"0.46.0","platform":"ruby","checksum":"1da9fd8c364501f18b0454e54506e7098bc38dae719219713fe5f246dfc91465"},
{"name":"sorted_set","version":"1.0.3","platform":"java","checksum":"996283f2e5c6e838825bcdcee31d6306515ae5f24bcb0ee4ce09dfff32919b8c"},
@ -614,7 +613,7 @@
{"name":"validate_email","version":"0.1.6","platform":"ruby","checksum":"9dfe9016d527b17a8d3a6e95e4dc50a125400eef899d13d4cc2a254393f82ee4"},
{"name":"validate_url","version":"1.0.15","platform":"ruby","checksum":"72fe164c0713d63a9970bd6700bea948babbfbdcec392f2342b6704042f57451"},
{"name":"validates_hostname","version":"1.0.11","platform":"ruby","checksum":"d506bae0342ec14c920eb319e057fc1886c321a59b85b4b6e966ee4b88fab8c3"},
{"name":"version_gem","version":"1.0.0","platform":"ruby","checksum":"929c93a4d46482bb3b0359980c7a5fb1b5a833548f1202a480b08a6f0a5f8f2f"},
{"name":"version_gem","version":"1.1.0","platform":"ruby","checksum":"6b009518020db57f51ec7b410213fae2bf692baea9f1b51770db97fbc93d9a80"},
{"name":"version_sorter","version":"2.2.4","platform":"ruby","checksum":"7ad071609edfaa3cf28c42d83b1a03096e43512244ae5a9e2fce1404f7e06d41"},
{"name":"view_component","version":"2.71.0","platform":"ruby","checksum":"c1880647800d9cfb03ff4ba92313db624a4a4b3d5753e137effe86e5f2b3662b"},
{"name":"vmstat","version":"2.3.0","platform":"ruby","checksum":"ab5446a3e3bd0a9cdb9d9ac69a0bbd119c4f161d945a0846a519dd7018af656d"},

View File

@ -18,7 +18,8 @@ module IncidentManagement
validates :project, :incident, :occurred_at, presence: true
validates :action, presence: true, length: { maximum: 128 }
validates :note, :note_html, presence: true, length: { maximum: 10_000 }
validates :note, presence: true, length: { maximum: 10_000 }
validates :note_html, length: { maximum: 10_000 }
scope :order_occurred_at_asc_id_asc, -> { reorder(occurred_at: :asc, id: :asc) }
end

View File

@ -3,6 +3,7 @@
module Ml
class CandidateParam < ApplicationRecord
validates :candidate, presence: true
validates :name, uniqueness: { scope: :candidate }
validates :name, :value, length: { maximum: 250 }, presence: true
belongs_to :candidate, class_name: 'Ml::Candidate'

View File

@ -1,8 +0,0 @@
---
name: escape_gitaly_refs
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/91058
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/366437
milestone: '15.2'
type: development
group: group::source code
default_enabled: true

View File

@ -5,6 +5,8 @@ en:
hello: "Hello world"
activerecord:
attributes:
incident_management/timeline_event:
note: 'Timeline text'
issue_link:
source: Source issue
target: Target issue

View File

@ -11,6 +11,11 @@ value_type: number
status: active
time_frame: all
data_source: redis
instrumentation_class: RedisMetric
options:
prefix: null
event: all_searches_count
include_usage_prefix: false
distribution:
- ce
- ee

View File

@ -11,6 +11,11 @@ value_type: number
status: active
time_frame: all
data_source: redis
instrumentation_class: RedisMetric
options:
prefix: null
event: navbar_searches_count
include_usage_prefix: false
distribution:
- ce
- ee

View File

@ -12,6 +12,10 @@ milestone: '13.12'
introduced_by_url:
time_frame: all
data_source: redis
instrumentation_class: RedisMetric
options:
prefix: kubernetes_agent
event: k8s_api_proxy_request
distribution:
- ce
- ee

View File

@ -0,0 +1,15 @@
# frozen_string_literal: true
class AddIndexToCandidateIdAndNameOnMlCandidateParams < Gitlab::Database::Migration[2.0]
disable_ddl_transaction!
INDEX_NAME = 'index_ml_candidate_params_on_candidate_id_on_name'
def up
add_concurrent_index :ml_candidate_params, [:candidate_id, :name], unique: true, name: INDEX_NAME
end
def down
remove_concurrent_index_by_name(:ml_candidate_params, INDEX_NAME)
end
end

View File

@ -0,0 +1 @@
ffa421a7ffc279cb2751ed9e74c212ab60a3b6f7722d15023d39f7ad97ff1e6c

View File

@ -29410,6 +29410,8 @@ CREATE INDEX index_ml_candidate_metrics_on_candidate_id ON ml_candidate_metrics
CREATE INDEX index_ml_candidate_params_on_candidate_id ON ml_candidate_params USING btree (candidate_id);
CREATE UNIQUE INDEX index_ml_candidate_params_on_candidate_id_on_name ON ml_candidate_params USING btree (candidate_id, name);
CREATE UNIQUE INDEX index_ml_candidates_on_experiment_id_and_iid ON ml_candidates USING btree (experiment_id, iid);
CREATE INDEX index_ml_candidates_on_user_id ON ml_candidates USING btree (user_id);

View File

@ -147,6 +147,13 @@ for details.
- [Viewing projects and designs data from a primary site is not possible when using a unified URL](../index.md#view-replication-data-on-the-primary-site).
- When secondary proxying is used together with separate URLs, registering [GitLab runners](https://docs.gitlab.com/runner/) to clone from
secondary sites is not supported. The runner registration will succeed, but the clone URL will default to the primary site. The runner
[clone URL](https://docs.gitlab.com/runner/configuration/advanced-configuration.html#the-runners-section) is configured per GitLab deployment
and cannot be configured per Geo site. Therefore, all runners will clone from the primary site (or configured clone URL) irrespective of
which Geo site they register on. For information about GitLab CI using a specific Geo secondary to clone from, see issue
[3294](https://gitlab.com/gitlab-org/gitlab/-/issues/3294#note_1009488466).
## Behavior of secondary sites when the primary Geo site is down
Considering that web traffic is proxied to the primary, the behavior of the secondary sites differs when the primary

View File

@ -6,9 +6,43 @@ info: To determine the technical writer assigned to the Stage/Group associated w
# Gemfile guidelines
When adding a new entry to `Gemfile` or upgrading an existing dependency pay
When adding a new entry to `Gemfile`, or upgrading an existing dependency pay
attention to the following rules.
## Bundler checksum verification
In [GitLab 15.5 and later](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/98508), gem
checksums are checked before installation. This verification is still
experimental so it is only active for CI.
If the downloaded gem's checksum does not match the checksum record in
`Gemfile.checksum`, you will see an error saying that Bundler cannot continue
installing a gem because there is a potential security issue.
You will see this error as well if you updated, or added a new gem without
updating `Gemfile.checksum`. To fix this error,
[update the Gemfile.checksum](#updating-the-checksum-file).
You can opt-in to this verification locally by setting the
`BUNDLER_CHECKSUM_VERIFICATION_OPT_IN` environment variable:
```shell
export BUNDLER_CHECKSUM_VERIFICATION_OPT_IN=1
bundle install
```
### Updating the checksum file
This needs to be done for any new, or updated gems.
1. When updating `Gemfile.lock`, make sure to also update `Gemfile.checksum` with:
```shell
bundle exec bundler-checksum init
```
1. Check and commit the changes for `Gemfile.checksum`.
## No gems fetched from Git repositories
We do not allow gems that are fetched from Git repositories. All gems have

View File

@ -9,6 +9,7 @@ module API
expose(:info) { |candidate| RunInfo.represent(candidate) }
expose :data do
expose :metrics, using: Metric
expose :params, using: RunParam
end
end
end

View File

@ -0,0 +1,14 @@
# frozen_string_literal: true
module API
module Entities
module Ml
module Mlflow
class RunParam < Grape::Entity
expose :name, as: :key
expose :value
end
end
end
end
end

View File

@ -160,7 +160,7 @@ module API
@candidate.status = params[:status].downcase if params[:status]
@candidate.end_time = params[:end_time] if params[:end_time]
@candidate.save if @candidate.valid?
@candidate.save
present @candidate, with: Entities::Ml::Mlflow::UpdateRun
end
@ -188,6 +188,74 @@ module API
{}
end
desc 'Logs a parameter to a run.' do
summary 'Log a param used for a run. A param is a key-value pair (string key, string value). '\
'Examples include hyperparameters used for ML model training and constant dates and values '\
'used in an ETL pipeline. A param can be logged only once for a run, duplicate will be .'\
'ignored'
detail 'https://www.mlflow.org/docs/1.28.0/rest-api.html#log-param'
end
params do
requires :run_id, type: String, desc: 'UUID of the run.'
requires :key, type: String, desc: 'Name for the parameter.'
requires :value, type: String, desc: 'Value for the parameter.'
end
post 'log-parameter', urgency: :low do
::Ml::CandidateParam.create(candidate: @candidate, name: params[:key], value: params[:value])
{}
end
desc 'Logs multiple parameters and metrics.' do
summary 'Log a batch of metrics and params for a run. Validation errors will block the entire batch, '\
'duplicate errors will be ignored.'
detail 'https://www.mlflow.org/docs/1.28.0/rest-api.html#log-param'
end
params do
requires :run_id, type: String, desc: 'UUID of the run.'
optional :metrics, type: Array, default: [] do
requires :key, type: String, desc: 'Name for the metric.'
requires :value, type: Float, desc: 'Value of the metric.'
requires :timestamp, type: Integer, desc: 'Unix timestamp in milliseconds when metric was recorded'
optional :step, type: Integer, desc: 'Step at which the metric was recorded'
end
optional :params, type: Array, default: [] do
requires :key, type: String, desc: 'Name for the metric.'
requires :value, type: String, desc: 'Value of the metric.'
end
end
post 'log-batch', urgency: :low do
times = { created_at: Time.zone.now, updated_at: Time.zone.now }
metrics = params[:metrics].map do |metric|
{
candidate_id: @candidate.id,
name: metric[:key],
value: metric[:value],
tracked_at: metric[:timestamp],
step: metric[:step],
**times
}
end
::Ml::CandidateMetric.insert_all(metrics, returning: false) unless metrics.empty?
parameters = params[:params].map do |p|
{
candidate_id: @candidate.id,
name: p[:key],
value: p[:value],
**times
}
end
::Ml::CandidateParam.insert_all(parameters, returning: false) unless parameters.empty?
{}
end
end
end
end

View File

@ -73,8 +73,6 @@ module Gitlab
# This method escapes unsupported UTF-8 characters instead of deleting them
def encode_utf8_with_escaping!(message)
return encode!(message) if Feature.disabled?(:escape_gitaly_refs)
message = force_encode_utf8(message)
return message if message.valid_encoding?

View File

@ -17,12 +17,13 @@ module Gitlab
include Gitlab::UsageDataCounters::RedisCounter
USAGE_PREFIX = "USAGE_"
OPTIONS_PREFIX_KEY = :prefix
def initialize(time_frame:, options: {})
super
raise ArgumentError, "'event' option is required" unless metric_event.present?
raise ArgumentError, "'prefix' option is required" unless prefix.present?
raise ArgumentError, "'prefix' option is required" unless options.has_key?(OPTIONS_PREFIX_KEY)
end
def metric_event
@ -30,7 +31,7 @@ module Gitlab
end
def prefix
options[:prefix]
options[OPTIONS_PREFIX_KEY]
end
def include_usage_prefix?
@ -50,9 +51,10 @@ module Gitlab
private
def redis_key
key = "#{prefix}_#{metric_event}".upcase
key = metric_event.dup
key.prepend("#{prefix}_") if prefix
key.prepend(USAGE_PREFIX) if include_usage_prefix?
key
key.upcase
end
end
end

View File

@ -4,8 +4,6 @@ module Gitlab
module UsageDataCounters
COUNTERS = [
NoteCounter,
SearchCounter,
KubernetesAgentCounter,
MergeRequestWidgetExtensionCounter
].freeze
@ -14,6 +12,8 @@ module Gitlab
MergeRequestCounter,
DesignsCounter,
DiffsCounter,
KubernetesAgentCounter,
SearchCounter,
ServiceUsageDataCounter,
WebIdeCounter,
WikiPageCounter,

View File

@ -40327,9 +40327,6 @@ msgstr ""
msgid "There is no data available. Please change your selection."
msgstr ""
msgid "There is no seat left to activate the member"
msgstr ""
msgid "There is no table data available."
msgstr ""

View File

@ -0,0 +1,10 @@
# frozen_string_literal: true
FactoryBot.define do
factory :ml_candidate_params, class: '::Ml::CandidateParam' do
association :candidate, factory: :ml_candidates
sequence(:name) { |n| "metric#{n}" }
sequence(:value) { |n| "value#{n}" }
end
end

View File

@ -4,9 +4,10 @@ FactoryBot.define do
association :experiment, factory: :ml_experiments
association :user
trait :with_metrics do
trait :with_metrics_and_params do
after(:create) do |candidate|
candidate.metrics = FactoryBot.create_list(:ml_candidate_metrics, 2, candidate: candidate )
candidate.params = FactoryBot.create_list(:ml_candidate_params, 2, candidate: candidate )
end
end
end

View File

@ -37,7 +37,7 @@ RSpec.describe Mutations::IncidentManagement::TimelineEvent::Create do
let(:args) { {} }
it_behaves_like 'responding with an incident timeline errors',
errors: ["Occurred at can't be blank, Note can't be blank, and Note html can't be blank"]
errors: ["Occurred at can't be blank and Timeline text can't be blank"]
end
end

View File

@ -65,7 +65,7 @@ RSpec.describe Mutations::IncidentManagement::TimelineEvent::Update do
end
it 'responds with error' do
expect(resolve).to eq(timeline_event: nil, errors: ["Note can't be blank"])
expect(resolve).to eq(timeline_event: nil, errors: ["Timeline text can't be blank"])
end
end

View File

@ -3,7 +3,7 @@
require 'spec_helper'
RSpec.describe API::Entities::Ml::Mlflow::Run do
let_it_be(:candidate) { create(:ml_candidates, :with_metrics) }
let_it_be(:candidate) { create(:ml_candidates, :with_metrics_and_params) }
subject { described_class.new(candidate).as_json }
@ -29,6 +29,18 @@ RSpec.describe API::Entities::Ml::Mlflow::Run do
expect(presented_metric[:step]).to eq(metric.step)
end
it 'presents the params' do
expect(subject.dig(:run, :data, :params).size).to eq(candidate.params.size)
end
it 'presents params correctly' do
presented_param = subject.dig(:run, :data, :params)[0]
param = candidate.params[0]
expect(presented_param[:key]).to eq(param.name)
expect(presented_param[:value]).to eq(param.value)
end
context 'when candidate has no metrics' do
before do
allow(candidate).to receive(:metrics).and_return([])
@ -38,4 +50,14 @@ RSpec.describe API::Entities::Ml::Mlflow::Run do
expect(subject.dig(:run, :data, :metrics)).to be_empty
end
end
context 'when candidate has no params' do
before do
allow(candidate).to receive(:params).and_return([])
end
it 'data is empty' do
expect(subject.dig(:run, :data, :params)).to be_empty
end
end
end

View File

@ -114,18 +114,6 @@ RSpec.describe Gitlab::EncodingHelper do
expect(ext_class.encode_utf8_with_escaping!(input)).to eq(expected)
end
end
context 'when feature flag is disabled' do
before do
stub_feature_flags(escape_gitaly_refs: false)
end
it 'uses #encode! method' do
expect(ext_class).to receive(:encode!).with('String')
ext_class.encode_utf8_with_escaping!('String')
end
end
end
describe '#encode_utf8' do

View File

@ -58,4 +58,18 @@ RSpec.describe Gitlab::Usage::Metrics::Instrumentations::RedisMetric, :clean_git
options: { event: 'merge_requests_count', prefix: 'web_ide', include_usage_prefix: false }
}
end
context "with prefix disabled" do
let(:expected_value) { 3 }
before do
3.times do
Gitlab::UsageDataCounters::SearchCounter.count(:all_searches)
end
end
it_behaves_like 'a correct instrumented metric value', {
options: { event: 'all_searches_count', prefix: nil, include_usage_prefix: false }
}
end
end

View File

@ -758,13 +758,6 @@ RSpec.describe Gitlab::UsageData, :aggregate_failures do
end
end
describe '.usage_counters' do
subject { described_class.usage_counters }
it { is_expected.to include(:kubernetes_agent_gitops_sync) }
it { is_expected.to include(:kubernetes_agent_k8s_api_proxy_request) }
end
describe '.usage_data_counters' do
subject { described_class.usage_data_counters }

View File

@ -22,7 +22,6 @@ RSpec.describe IncidentManagement::TimelineEvent do
it { is_expected.to validate_presence_of(:incident) }
it { is_expected.to validate_presence_of(:note) }
it { is_expected.to validate_length_of(:note).is_at_most(10_000) }
it { is_expected.to validate_presence_of(:note_html) }
it { is_expected.to validate_length_of(:note_html).is_at_most(10_000) }
it { is_expected.to validate_presence_of(:occurred_at) }
it { is_expected.to validate_presence_of(:action) }

View File

@ -3,7 +3,7 @@
require 'spec_helper'
RSpec.describe Ml::Candidate, factory_default: :keep do
let_it_be(:candidate) { create_default(:ml_candidates, :with_metrics) }
let_it_be(:candidate) { create_default(:ml_candidates, :with_metrics_and_params) }
describe 'associations' do
it { is_expected.to belong_to(:experiment) }

View File

@ -16,7 +16,8 @@ RSpec.describe API::Ml::Mlflow do
end
let_it_be(:candidate) do
create(:ml_candidates, :with_metrics, user: experiment.user, start_time: 1234, experiment: experiment)
create(:ml_candidates,
:with_metrics_and_params, user: experiment.user, start_time: 1234, experiment: experiment)
end
let_it_be(:tokens) do
@ -142,7 +143,7 @@ RSpec.describe API::Ml::Mlflow do
let(:experiment_iid) { experiment.iid.to_s }
let(:route) { "/projects/#{project_id}/ml/mflow/api/2.0/mlflow/experiments/get?experiment_id=#{experiment_iid}" }
it 'returns the experiment' do
it 'returns the experiment', :aggregate_failures do
expect(response).to have_gitlab_http_status(:ok)
expect(response).to match_response_schema('ml/get_experiment')
expect(json_response).to include({
@ -181,7 +182,7 @@ RSpec.describe API::Ml::Mlflow do
"/projects/#{project_id}/ml/mflow/api/2.0/mlflow/experiments/get-by-name?experiment_name=#{experiment_name}"
end
it 'returns the experiment' do
it 'returns the experiment', :aggregate_failures do
expect(response).to have_gitlab_http_status(:ok)
expect(response).to match_response_schema('ml/get_experiment')
expect(json_response).to include({
@ -220,7 +221,7 @@ RSpec.describe API::Ml::Mlflow do
let(:params) { { name: 'new_experiment' } }
let(:request) { post api(route), params: params, headers: headers }
it 'creates the experiment' do
it 'creates the experiment', :aggregate_failures do
expect(response).to have_gitlab_http_status(:created)
expect(json_response).to include('experiment_id' )
end
@ -259,7 +260,7 @@ RSpec.describe API::Ml::Mlflow do
let(:params) { { experiment_id: experiment.iid.to_s, start_time: Time.now.to_i } }
let(:request) { post api(route), params: params, headers: headers }
it 'creates the run' do
it 'creates the run', :aggregate_failures do
expected_properties = {
'experiment_id' => params[:experiment_id],
'user_id' => current_user.id.to_s,
@ -272,7 +273,7 @@ RSpec.describe API::Ml::Mlflow do
expect(response).to have_gitlab_http_status(:created)
expect(response).to match_response_schema('ml/run')
expect(json_response['run']).to include('info' => hash_including(**expected_properties),
'data' => { 'metrics' => [] })
'data' => { 'metrics' => [], 'params' => [] })
end
describe 'Error States' do
@ -303,7 +304,7 @@ RSpec.describe API::Ml::Mlflow do
let(:route) { "/projects/#{project_id}/ml/mflow/api/2.0/mlflow/runs/get" }
let(:default_params) { { 'run_id' => candidate.iid } }
it 'gets the run' do
it 'gets the run', :aggregate_failures do
expected_properties = {
'experiment_id' => candidate.experiment.iid.to_s,
'user_id' => candidate.user.id.to_s,
@ -321,6 +322,10 @@ RSpec.describe API::Ml::Mlflow do
'metrics' => [
hash_including('key' => candidate.metrics[0].name),
hash_including('key' => candidate.metrics[1].name)
],
'params' => [
{ 'key' => candidate.params[0].name, 'value' => candidate.params[0].value },
{ 'key' => candidate.params[1].name, 'value' => candidate.params[1].value }
]
})
end
@ -337,7 +342,7 @@ RSpec.describe API::Ml::Mlflow do
let(:request) { post api(route), params: params, headers: headers }
let(:route) { "/projects/#{project_id}/ml/mflow/api/2.0/mlflow/runs/update" }
it 'updates the run' do
it 'updates the run', :aggregate_failures do
expected_properties = {
'experiment_id' => candidate.experiment.iid.to_s,
'user_id' => candidate.user.id.to_s,
@ -377,7 +382,7 @@ RSpec.describe API::Ml::Mlflow do
let(:default_params) { { run_id: candidate.iid.to_s, key: 'some_key', value: 10.0, timestamp: Time.now.to_i } }
let(:request) { post api(route), params: params, headers: headers }
it 'logs the metric' do
it 'logs the metric', :aggregate_failures do
candidate.metrics.reload
expect(response).to have_gitlab_http_status(:success)
@ -392,5 +397,96 @@ RSpec.describe API::Ml::Mlflow do
it_behaves_like 'Bad Request on missing required', [:key, :value, :timestamp]
end
end
describe 'POST /projects/:id/ml/mflow/api/2.0/mlflow/runs/log-parameter' do
let(:route) { "/projects/#{project_id}/ml/mflow/api/2.0/mlflow/runs/log-parameter" }
let(:default_params) { { run_id: candidate.iid.to_s, key: 'some_key', value: 'value' } }
let(:request) { post api(route), params: params, headers: headers }
it 'logs the parameter', :aggregate_failures do
candidate.params.reload
expect(response).to have_gitlab_http_status(:success)
expect(json_response).to be_empty
expect(candidate.params.length).to eq(3)
end
context 'when parameter was already logged' do
let(:params) { default_params.tap { |p| p[:key] = candidate.params[0].name } }
it 'does not log', :aggregate_failures do
candidate.params.reload
expect(response).to have_gitlab_http_status(:success)
expect(json_response).to be_empty
expect(candidate.params.length).to eq(2)
end
end
describe 'Error Cases' do
it_behaves_like 'shared error cases'
it_behaves_like 'Requires api scope'
it_behaves_like 'run_id param error cases'
it_behaves_like 'Bad Request on missing required', [:key, :value]
end
end
describe 'POST /projects/:id/ml/mflow/api/2.0/mlflow/runs/log-batch' do
let(:candidate2) do
create(:ml_candidates, user: experiment.user, start_time: 1234, experiment: experiment)
end
let(:route) { "/projects/#{project_id}/ml/mflow/api/2.0/mlflow/runs/log-batch" }
let(:default_params) do
{
run_id: candidate2.iid.to_s,
metrics: [
{ key: 'mae', value: 2.5, timestamp: 1552550804 },
{ key: 'rmse', value: 2.7, timestamp: 1552550804 }
],
params: [{ key: 'model_class', value: 'LogisticRegression' }]
}
end
let(:request) { post api(route), params: params, headers: headers }
it 'logs parameters and metrics', :aggregate_failures do
expect(response).to have_gitlab_http_status(:success)
expect(json_response).to be_empty
expect(candidate2.params.size).to eq(1)
expect(candidate2.metrics.size).to eq(2)
end
context 'when parameter was already logged' do
let(:params) do
default_params.tap { |p| p[:params] = [{ key: 'hello', value: 'a' }, { key: 'hello', value: 'b' }] }
end
it 'does not log', :aggregate_failures do
candidate.params.reload
expect(response).to have_gitlab_http_status(:success)
expect(candidate2.params.size).to eq(1)
end
end
describe 'Error Cases' do
context 'when required metric key is missing' do
let(:params) { default_params.tap { |p| p[:metrics] = [p[:metrics][0].delete(:key)] } }
it_behaves_like 'Bad Request'
end
context 'when required param key is missing' do
let(:params) { default_params.tap { |p| p[:params] = [p[:params][0].delete(:key)] } }
it_behaves_like 'Bad Request'
end
it_behaves_like 'shared error cases'
it_behaves_like 'Requires api scope'
it_behaves_like 'run_id param error cases'
end
end
end
end

View File

@ -71,7 +71,7 @@ RSpec.describe IncidentManagement::TimelineEvents::CreateService do
context 'when error occurs during creation' do
let(:args) { {} }
it_behaves_like 'error response', "Occurred at can't be blank, Note can't be blank, and Note html can't be blank"
it_behaves_like 'error response', "Occurred at can't be blank and Timeline text can't be blank"
end
context 'with default action' do

View File

@ -48,7 +48,7 @@ RSpec.describe IncidentManagement::TimelineEvents::DestroyService do
timeline_event.errors.add(:note, 'cannot be removed')
end
it_behaves_like 'error response', 'Note cannot be removed'
it_behaves_like 'error response', 'Timeline text cannot be removed'
end
context 'success response' do

View File

@ -98,7 +98,7 @@ RSpec.describe IncidentManagement::TimelineEvents::UpdateService do
context 'when note is blank' do
let(:params) { { note: '', occurred_at: occurred_at } }
it_behaves_like 'error response', "Note can't be blank"
it_behaves_like 'error response', "Timeline text can't be blank"
end
context 'when occurred_at is nil' do

View File

@ -5,7 +5,6 @@ module UsageDataHelpers
snippet_comment
merge_request_comment
commit_comment
navbar_searches
).freeze
COUNTS_KEYS = %i(