From a7698afc6e7a5a6e4e5044f5b310a2c69c554053 Mon Sep 17 00:00:00 2001 From: GitLab Bot Date: Fri, 23 Sep 2022 00:10:38 +0000 Subject: [PATCH] Add latest changes from gitlab-org/gitlab@master --- GITALY_SERVER_VERSION | 2 +- Gemfile | 6 + Gemfile.checksum | 23 ++-- .../incident_management/timeline_event.rb | 3 +- app/models/ml/candidate_param.rb | 1 + .../development/escape_gitaly_refs.yml | 8 -- config/locales/en.yml | 2 + .../20210216180413_all_searches.yml | 5 + .../20210216180414_navbar_searches.yml | 5 + ...kubernetes_agent_k8s_api_proxy_request.yml | 4 + ...date_id_and_name_on_ml_candidate_params.rb | 15 +++ db/schema_migrations/20220914080716 | 1 + db/structure.sql | 2 + .../geo/secondary_proxy/index.md | 7 ++ doc/development/gemfile.md | 36 +++++- lib/api/entities/ml/mlflow/run.rb | 1 + lib/api/entities/ml/mlflow/run_param.rb | 14 +++ lib/api/ml/mlflow.rb | 70 ++++++++++- lib/gitlab/encoding_helper.rb | 2 - .../metrics/instrumentations/redis_metric.rb | 10 +- lib/gitlab/usage_data_counters.rb | 4 +- locale/gitlab.pot | 3 - spec/factories/ml/candidate_params.rb | 10 ++ spec/factories/ml/candidates.rb | 3 +- .../timeline_event/create_spec.rb | 2 +- .../timeline_event/update_spec.rb | 2 +- spec/lib/api/entities/ml/mlflow/run_spec.rb | 24 +++- spec/lib/gitlab/encoding_helper_spec.rb | 12 -- .../instrumentations/redis_metric_spec.rb | 14 +++ spec/lib/gitlab/usage_data_spec.rb | 7 -- .../timeline_event_spec.rb | 1 - spec/models/ml/candidate_spec.rb | 2 +- spec/requests/api/ml/mlflow_spec.rb | 114 ++++++++++++++++-- .../timeline_events/create_service_spec.rb | 2 +- .../timeline_events/destroy_service_spec.rb | 2 +- .../timeline_events/update_service_spec.rb | 2 +- spec/support/helpers/usage_data_helpers.rb | 1 - 37 files changed, 349 insertions(+), 73 deletions(-) delete mode 100644 config/feature_flags/development/escape_gitaly_refs.yml create mode 100644 db/migrate/20220914080716_add_index_to_candidate_id_and_name_on_ml_candidate_params.rb create mode 100644 db/schema_migrations/20220914080716 create mode 100644 lib/api/entities/ml/mlflow/run_param.rb create mode 100644 spec/factories/ml/candidate_params.rb diff --git a/GITALY_SERVER_VERSION b/GITALY_SERVER_VERSION index beb66fcc2b4..80c8a866e1f 100644 --- a/GITALY_SERVER_VERSION +++ b/GITALY_SERVER_VERSION @@ -1 +1 @@ -e4d8f69ffa2efd3f2cb0adff5fa66f367f66f6fb +9498ab9459048cc595d8e2e411b027d080c0ab0f diff --git a/Gemfile b/Gemfile index 53b5d96b476..8cf345a5829 100644 --- a/Gemfile +++ b/Gemfile @@ -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' diff --git a/Gemfile.checksum b/Gemfile.checksum index 542a8d25fb6..31845fc2f3b 100644 --- a/Gemfile.checksum +++ b/Gemfile.checksum @@ -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"}, diff --git a/app/models/incident_management/timeline_event.rb b/app/models/incident_management/timeline_event.rb index dd0d3c6585d..de193308558 100644 --- a/app/models/incident_management/timeline_event.rb +++ b/app/models/incident_management/timeline_event.rb @@ -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 diff --git a/app/models/ml/candidate_param.rb b/app/models/ml/candidate_param.rb index cbdddcc8a1a..a259e059379 100644 --- a/app/models/ml/candidate_param.rb +++ b/app/models/ml/candidate_param.rb @@ -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' diff --git a/config/feature_flags/development/escape_gitaly_refs.yml b/config/feature_flags/development/escape_gitaly_refs.yml deleted file mode 100644 index b42cc4c07e5..00000000000 --- a/config/feature_flags/development/escape_gitaly_refs.yml +++ /dev/null @@ -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 diff --git a/config/locales/en.yml b/config/locales/en.yml index 56df8f93113..3c33cdac3f1 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -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 diff --git a/config/metrics/counts_all/20210216180413_all_searches.yml b/config/metrics/counts_all/20210216180413_all_searches.yml index 2f45e0c6e20..abd2e31b8d8 100644 --- a/config/metrics/counts_all/20210216180413_all_searches.yml +++ b/config/metrics/counts_all/20210216180413_all_searches.yml @@ -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 diff --git a/config/metrics/counts_all/20210216180414_navbar_searches.yml b/config/metrics/counts_all/20210216180414_navbar_searches.yml index b2af79bb43c..9ba086ed271 100644 --- a/config/metrics/counts_all/20210216180414_navbar_searches.yml +++ b/config/metrics/counts_all/20210216180414_navbar_searches.yml @@ -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 diff --git a/config/metrics/counts_all/20210505015532_kubernetes_agent_k8s_api_proxy_request.yml b/config/metrics/counts_all/20210505015532_kubernetes_agent_k8s_api_proxy_request.yml index 185e8a199b6..fcb2be99fab 100644 --- a/config/metrics/counts_all/20210505015532_kubernetes_agent_k8s_api_proxy_request.yml +++ b/config/metrics/counts_all/20210505015532_kubernetes_agent_k8s_api_proxy_request.yml @@ -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 diff --git a/db/migrate/20220914080716_add_index_to_candidate_id_and_name_on_ml_candidate_params.rb b/db/migrate/20220914080716_add_index_to_candidate_id_and_name_on_ml_candidate_params.rb new file mode 100644 index 00000000000..caf30961ace --- /dev/null +++ b/db/migrate/20220914080716_add_index_to_candidate_id_and_name_on_ml_candidate_params.rb @@ -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 diff --git a/db/schema_migrations/20220914080716 b/db/schema_migrations/20220914080716 new file mode 100644 index 00000000000..7c807e61c26 --- /dev/null +++ b/db/schema_migrations/20220914080716 @@ -0,0 +1 @@ +ffa421a7ffc279cb2751ed9e74c212ab60a3b6f7722d15023d39f7ad97ff1e6c \ No newline at end of file diff --git a/db/structure.sql b/db/structure.sql index c26af7af0da..fb1f550625e 100644 --- a/db/structure.sql +++ b/db/structure.sql @@ -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); diff --git a/doc/administration/geo/secondary_proxy/index.md b/doc/administration/geo/secondary_proxy/index.md index b088d8a8af2..2786982bb51 100644 --- a/doc/administration/geo/secondary_proxy/index.md +++ b/doc/administration/geo/secondary_proxy/index.md @@ -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 diff --git a/doc/development/gemfile.md b/doc/development/gemfile.md index dd687763356..7d3531afb49 100644 --- a/doc/development/gemfile.md +++ b/doc/development/gemfile.md @@ -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 diff --git a/lib/api/entities/ml/mlflow/run.rb b/lib/api/entities/ml/mlflow/run.rb index 937d222ea37..a8e1cfe08dd 100644 --- a/lib/api/entities/ml/mlflow/run.rb +++ b/lib/api/entities/ml/mlflow/run.rb @@ -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 diff --git a/lib/api/entities/ml/mlflow/run_param.rb b/lib/api/entities/ml/mlflow/run_param.rb new file mode 100644 index 00000000000..75fee738f8b --- /dev/null +++ b/lib/api/entities/ml/mlflow/run_param.rb @@ -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 diff --git a/lib/api/ml/mlflow.rb b/lib/api/ml/mlflow.rb index a5a22927eb0..d1f8daaa93d 100644 --- a/lib/api/ml/mlflow.rb +++ b/lib/api/ml/mlflow.rb @@ -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 diff --git a/lib/gitlab/encoding_helper.rb b/lib/gitlab/encoding_helper.rb index 34c674c3003..b1fd35184ac 100644 --- a/lib/gitlab/encoding_helper.rb +++ b/lib/gitlab/encoding_helper.rb @@ -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? diff --git a/lib/gitlab/usage/metrics/instrumentations/redis_metric.rb b/lib/gitlab/usage/metrics/instrumentations/redis_metric.rb index 26d963e2407..57a417b4cf3 100644 --- a/lib/gitlab/usage/metrics/instrumentations/redis_metric.rb +++ b/lib/gitlab/usage/metrics/instrumentations/redis_metric.rb @@ -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 diff --git a/lib/gitlab/usage_data_counters.rb b/lib/gitlab/usage_data_counters.rb index 65f72e229a7..8198628b1fa 100644 --- a/lib/gitlab/usage_data_counters.rb +++ b/lib/gitlab/usage_data_counters.rb @@ -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, diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 248dced98d0..9698d43f6a5 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -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 "" diff --git a/spec/factories/ml/candidate_params.rb b/spec/factories/ml/candidate_params.rb new file mode 100644 index 00000000000..73cb0c54089 --- /dev/null +++ b/spec/factories/ml/candidate_params.rb @@ -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 diff --git a/spec/factories/ml/candidates.rb b/spec/factories/ml/candidates.rb index c8649f80ff2..4fbcdc46103 100644 --- a/spec/factories/ml/candidates.rb +++ b/spec/factories/ml/candidates.rb @@ -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 diff --git a/spec/graphql/mutations/incident_management/timeline_event/create_spec.rb b/spec/graphql/mutations/incident_management/timeline_event/create_spec.rb index ea74e427dd6..9254d84b29c 100644 --- a/spec/graphql/mutations/incident_management/timeline_event/create_spec.rb +++ b/spec/graphql/mutations/incident_management/timeline_event/create_spec.rb @@ -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 diff --git a/spec/graphql/mutations/incident_management/timeline_event/update_spec.rb b/spec/graphql/mutations/incident_management/timeline_event/update_spec.rb index 102d33378c6..7081fb7117e 100644 --- a/spec/graphql/mutations/incident_management/timeline_event/update_spec.rb +++ b/spec/graphql/mutations/incident_management/timeline_event/update_spec.rb @@ -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 diff --git a/spec/lib/api/entities/ml/mlflow/run_spec.rb b/spec/lib/api/entities/ml/mlflow/run_spec.rb index 5511861991e..b8d38093681 100644 --- a/spec/lib/api/entities/ml/mlflow/run_spec.rb +++ b/spec/lib/api/entities/ml/mlflow/run_spec.rb @@ -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 diff --git a/spec/lib/gitlab/encoding_helper_spec.rb b/spec/lib/gitlab/encoding_helper_spec.rb index 690396d4dbc..c62e3071fc1 100644 --- a/spec/lib/gitlab/encoding_helper_spec.rb +++ b/spec/lib/gitlab/encoding_helper_spec.rb @@ -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 diff --git a/spec/lib/gitlab/usage/metrics/instrumentations/redis_metric_spec.rb b/spec/lib/gitlab/usage/metrics/instrumentations/redis_metric_spec.rb index 80ae5c6fd21..8f9c3573897 100644 --- a/spec/lib/gitlab/usage/metrics/instrumentations/redis_metric_spec.rb +++ b/spec/lib/gitlab/usage/metrics/instrumentations/redis_metric_spec.rb @@ -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 diff --git a/spec/lib/gitlab/usage_data_spec.rb b/spec/lib/gitlab/usage_data_spec.rb index 46ed4b57d3a..745aa20a6a2 100644 --- a/spec/lib/gitlab/usage_data_spec.rb +++ b/spec/lib/gitlab/usage_data_spec.rb @@ -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 } diff --git a/spec/models/incident_management/timeline_event_spec.rb b/spec/models/incident_management/timeline_event_spec.rb index fea391acda3..c68bcd4a9ed 100644 --- a/spec/models/incident_management/timeline_event_spec.rb +++ b/spec/models/incident_management/timeline_event_spec.rb @@ -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) } diff --git a/spec/models/ml/candidate_spec.rb b/spec/models/ml/candidate_spec.rb index 7a2dd4b1892..3bf1e80a152 100644 --- a/spec/models/ml/candidate_spec.rb +++ b/spec/models/ml/candidate_spec.rb @@ -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) } diff --git a/spec/requests/api/ml/mlflow_spec.rb b/spec/requests/api/ml/mlflow_spec.rb index 1377bb365f7..859fb9de936 100644 --- a/spec/requests/api/ml/mlflow_spec.rb +++ b/spec/requests/api/ml/mlflow_spec.rb @@ -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 diff --git a/spec/services/incident_management/timeline_events/create_service_spec.rb b/spec/services/incident_management/timeline_events/create_service_spec.rb index b999403e168..0e6864387d6 100644 --- a/spec/services/incident_management/timeline_events/create_service_spec.rb +++ b/spec/services/incident_management/timeline_events/create_service_spec.rb @@ -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 diff --git a/spec/services/incident_management/timeline_events/destroy_service_spec.rb b/spec/services/incident_management/timeline_events/destroy_service_spec.rb index 09026f87116..2fffe75b53c 100644 --- a/spec/services/incident_management/timeline_events/destroy_service_spec.rb +++ b/spec/services/incident_management/timeline_events/destroy_service_spec.rb @@ -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 diff --git a/spec/services/incident_management/timeline_events/update_service_spec.rb b/spec/services/incident_management/timeline_events/update_service_spec.rb index f612c72e2a8..21aa983dbd1 100644 --- a/spec/services/incident_management/timeline_events/update_service_spec.rb +++ b/spec/services/incident_management/timeline_events/update_service_spec.rb @@ -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 diff --git a/spec/support/helpers/usage_data_helpers.rb b/spec/support/helpers/usage_data_helpers.rb index 90903f16bd7..89bb9492125 100644 --- a/spec/support/helpers/usage_data_helpers.rb +++ b/spec/support/helpers/usage_data_helpers.rb @@ -5,7 +5,6 @@ module UsageDataHelpers snippet_comment merge_request_comment commit_comment - navbar_searches ).freeze COUNTS_KEYS = %i(