From 41a96519608b307fa73326b1254d3a2fb3c9e24f Mon Sep 17 00:00:00 2001 From: Thomas Schmidt Date: Mon, 26 Aug 2024 09:52:06 +0200 Subject: [PATCH 01/16] Revert "Revert "Use text instead of string as it can be very limiting for paths lenght"" --- db/migrate/20240821114908_change_local_path_type.rb | 13 +++++++++++++ db/schema.rb | 6 +++--- package/obs/rmt-server.changes | 1 + 3 files changed, 17 insertions(+), 3 deletions(-) create mode 100644 db/migrate/20240821114908_change_local_path_type.rb diff --git a/db/migrate/20240821114908_change_local_path_type.rb b/db/migrate/20240821114908_change_local_path_type.rb new file mode 100644 index 000000000..83e0789e9 --- /dev/null +++ b/db/migrate/20240821114908_change_local_path_type.rb @@ -0,0 +1,13 @@ +class ChangeLocalPathType < ActiveRecord::Migration[6.1] + def up + safety_assured do + change_column :repositories, :local_path, :text + change_column :downloaded_files, :local_path, :text + end + end + + def down + change_column :repositories, :local_path, :string + change_column :downloaded_files, :local_path, :string + end +end diff --git a/db/schema.rb b/db/schema.rb index 9607b8f9a..bf4ce342e 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 2024_07_29_103525) do +ActiveRecord::Schema.define(version: 2024_08_21_114908) do create_table "activations", charset: "utf8", force: :cascade do |t| t.bigint "service_id", null: false @@ -34,7 +34,7 @@ create_table "downloaded_files", charset: "utf8", force: :cascade do |t| t.string "checksum_type" t.string "checksum" - t.string "local_path" + t.text "local_path" t.bigint "file_size", unsigned: true t.index ["checksum_type", "checksum"], name: "index_downloaded_files_on_checksum_type_and_checksum" t.index ["local_path"], name: "index_downloaded_files_on_local_path", unique: true @@ -104,7 +104,7 @@ t.string "auth_token" t.boolean "installer_updates", default: false, null: false t.boolean "mirroring_enabled", default: false, null: false - t.string "local_path", null: false + t.text "local_path", null: false t.datetime "last_mirrored_at" t.string "friendly_id" t.index ["external_url"], name: "index_repositories_on_external_url", unique: true diff --git a/package/obs/rmt-server.changes b/package/obs/rmt-server.changes index febce557b..71ad51f29 100644 --- a/package/obs/rmt-server.changes +++ b/package/obs/rmt-server.changes @@ -3,6 +3,7 @@ Wed Aug 21 15:28:43 UTC 2024 - Jesús Bermúdez Velázquez - Version 2.19 * Fix for mirroring products that contain special characters (eg.: '$') in their path + * Fix for packages with path longer 255 characters (bsc#1229152) * rmt-server-pubcloud: * Support registration of extensions in BYOS mode on top of a PAYG system (hybrid mode) (jsc#PCT-400) * Validate repository and registy access for hybrid systems From fa31dc9d7cf7c5f73ce8a2a9f8a48222ff1dec27 Mon Sep 17 00:00:00 2001 From: Thomas Schmidt Date: Tue, 17 Sep 2024 17:03:52 +0200 Subject: [PATCH 02/16] Store the client's user agent --- .../api/connect/v3/systems/systems_controller.rb | 2 +- app/controllers/application_controller.rb | 10 ++++++++++ app/controllers/services_controller.rb | 4 +--- app/models/system.rb | 11 +++++++++-- 4 files changed, 21 insertions(+), 6 deletions(-) diff --git a/app/controllers/api/connect/v3/systems/systems_controller.rb b/app/controllers/api/connect/v3/systems/systems_controller.rb index b7622e252..a7c5cb958 100644 --- a/app/controllers/api/connect/v3/systems/systems_controller.rb +++ b/app/controllers/api/connect/v3/systems/systems_controller.rb @@ -21,7 +21,7 @@ def update # Since the payload is handled by rails all values are converted to string # e.g. cpus: 16 becomes cpus: "16". We save this as string for now and expect - # SCC to handle the convertation correctly + # SCC to handle the conversion correctly @system.system_information = hwinfo_params[:hwinfo].to_json if @system.save diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 12eaadef0..686972895 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -18,6 +18,7 @@ def authenticate_system(skip_on_duplicated: false) return true if skip_on_duplicated && @systems.size > 1 @system = find_system_by_token_header(@systems) + update_user_agent # If SYSTEM_TOKEN_HEADER is present, RMT assumes the client uses a SUSEConnect version # that supports this feature. In this case, refresh the token and include it in the response. @@ -42,6 +43,15 @@ def authenticate_system(skip_on_duplicated: false) private + def zypper_request? + user_agent = request.headers['HTTP_USER_AGENT'] + user_agent&.downcase&.starts_with?('zypp') + end + + def update_user_agent + @system.set_system_information('user_agent', request.headers['HTTP_USER_AGENT']) unless zypper_request? + end + # Token mechanism to detect duplicated systems. # 1: system doesn't send a token header (old SUSEConnect version) # 2: system sends a token, and it matches an existing system with that token diff --git a/app/controllers/services_controller.rb b/app/controllers/services_controller.rb index e28d94cbe..74f6d2f65 100644 --- a/app/controllers/services_controller.rb +++ b/app/controllers/services_controller.rb @@ -14,12 +14,10 @@ class ServicesController < ApplicationController # authenticate requests on this method for Zypper so we have a better picture # which systems are still being active (even if not using SUSEConnect). before_action only: %w[show] do - ua = request.headers['HTTP_USER_AGENT'] - # Zypper will never provide the `system_token` credentials for the system. # Hence, if there are duplicates, we will not be able to deterministically # tell which system is to be updated. Just skip it altogether on this case. - authenticate_system(skip_on_duplicated: true) if ua && ua.downcase.starts_with?('zypp') + authenticate_system(skip_on_duplicated: true) if zypper_request? end ZYPPER_SERVICE_TTL = 86400 diff --git a/app/models/system.rb b/app/models/system.rb index be98e602b..2d9f97808 100644 --- a/app/models/system.rb +++ b/app/models/system.rb @@ -37,10 +37,17 @@ def self.generate_secure_login end def cloud_provider + system_information_hash.fetch(:cloud_provider, nil) + end + + def system_information_hash # system_information is checked for valid JSON on save. It is safe # to assume the structure is valid. - info = JSON.parse(system_information).symbolize_keys - info.fetch(:cloud_provider, nil) + JSON.parse(system_information || '{}').symbolize_keys + end + + def set_system_information(key, value) + update(system_information: system_information_hash.update(key => value).to_json) end # Generate secure token for System password From bfc5a922930f09cd6c713b87fc9b64a9e81962d7 Mon Sep 17 00:00:00 2001 From: Adnilson Delgado Date: Fri, 20 Sep 2024 12:54:21 +0100 Subject: [PATCH 03/16] Use a limit of 512 --- db/migrate/20240821114908_change_local_path_type.rb | 4 ++-- db/schema.rb | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/db/migrate/20240821114908_change_local_path_type.rb b/db/migrate/20240821114908_change_local_path_type.rb index 83e0789e9..5c2f53cce 100644 --- a/db/migrate/20240821114908_change_local_path_type.rb +++ b/db/migrate/20240821114908_change_local_path_type.rb @@ -1,8 +1,8 @@ class ChangeLocalPathType < ActiveRecord::Migration[6.1] def up safety_assured do - change_column :repositories, :local_path, :text - change_column :downloaded_files, :local_path, :text + change_column :repositories, :local_path, :string, limit: 512 + change_column :downloaded_files, :local_path, :string, limit: 512 end end diff --git a/db/schema.rb b/db/schema.rb index bf4ce342e..70a68e684 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -34,7 +34,7 @@ create_table "downloaded_files", charset: "utf8", force: :cascade do |t| t.string "checksum_type" t.string "checksum" - t.text "local_path" + t.text "local_path", limit: 512 t.bigint "file_size", unsigned: true t.index ["checksum_type", "checksum"], name: "index_downloaded_files_on_checksum_type_and_checksum" t.index ["local_path"], name: "index_downloaded_files_on_local_path", unique: true @@ -104,7 +104,7 @@ t.string "auth_token" t.boolean "installer_updates", default: false, null: false t.boolean "mirroring_enabled", default: false, null: false - t.text "local_path", null: false + t.text "local_path", limit: 512, null: false t.datetime "last_mirrored_at" t.string "friendly_id" t.index ["external_url"], name: "index_repositories_on_external_url", unique: true From 48a4b478d7e811149a8134d4eed20a7e592a451d Mon Sep 17 00:00:00 2001 From: Thomas Schmidt Date: Fri, 20 Sep 2024 14:45:15 +0200 Subject: [PATCH 04/16] lock memory_profiler --- Gemfile | 2 +- Gemfile.lock | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Gemfile b/Gemfile index 82f2f5ec0..cc4b4508f 100644 --- a/Gemfile +++ b/Gemfile @@ -43,7 +43,7 @@ group :development, :test do gem 'gettext', require: false # needed for gettext_i18n_rails tasks gem 'ruby_parser', '< 3.20', require: false # needed for gettext_i18n_rails tasks, Locked because of Ruby >= 2.6 dependency gem 'gettext_test_log' - gem 'memory_profiler' + gem 'memory_profiler', '~> 1.0.2' # locked because 1.1.0 requires ruby version >= 3.1.0 gem 'awesome_print' end diff --git a/Gemfile.lock b/Gemfile.lock index 2d98fe38c..53f1197c0 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -351,7 +351,7 @@ DEPENDENCIES guard-rspec jwt (~> 2.1) listen (>= 3.0.5, <= 3.6.0) - memory_profiler + memory_profiler (~> 1.0.2) minitest (~> 5.15.0) mysql2 (~> 0.5.3) nokogiri (< 1.13) From 1646a9c0635539f3db6049a74d9a2a820116657b Mon Sep 17 00:00:00 2001 From: Thomas Schmidt Date: Fri, 20 Sep 2024 15:38:13 +0200 Subject: [PATCH 05/16] don't clear user-agent when updating hwinfo --- .../connect/v3/systems/systems_controller.rb | 2 +- .../v3/systems/systems_controller_spec.rb | 20 +++++++++++++++++++ spec/support/shared_contexts/headers.rb | 8 ++++++++ 3 files changed, 29 insertions(+), 1 deletion(-) diff --git a/app/controllers/api/connect/v3/systems/systems_controller.rb b/app/controllers/api/connect/v3/systems/systems_controller.rb index a7c5cb958..863557279 100644 --- a/app/controllers/api/connect/v3/systems/systems_controller.rb +++ b/app/controllers/api/connect/v3/systems/systems_controller.rb @@ -22,7 +22,7 @@ def update # Since the payload is handled by rails all values are converted to string # e.g. cpus: 16 becomes cpus: "16". We save this as string for now and expect # SCC to handle the conversion correctly - @system.system_information = hwinfo_params[:hwinfo].to_json + @system.system_information = @system.system_information_hash.update(hwinfo_params[:hwinfo]).to_json if @system.save logger.info(N_("Updated system information for host '%s'") % @system.hostname) diff --git a/spec/requests/api/connect/v3/systems/systems_controller_spec.rb b/spec/requests/api/connect/v3/systems/systems_controller_spec.rb index 14cb0f07e..95eede620 100644 --- a/spec/requests/api/connect/v3/systems/systems_controller_spec.rb +++ b/spec/requests/api/connect/v3/systems/systems_controller_spec.rb @@ -3,6 +3,8 @@ RSpec.describe Api::Connect::V3::Systems::SystemsController do include_context 'auth header', :system, :login, :password include_context 'version header', 3 + include_context 'user-agent header' + include_context 'zypp user-agent header' let(:system) { FactoryBot.create(:system, hostname: 'initial') } let(:url) { '/connect/systems' } @@ -105,6 +107,24 @@ expect(system.reload.hostname).to be_nil end end + + context 'stores client\'s user-agent' do + let(:headers) { auth_header.merge(user_agent_header) } + + it 'stores suseconnect version' do + update_action + expect(system.reload.system_information_hash[:user_agent]).to eq('suseconnect-ng/1.2') + end + end + + context 'doesn\'t store zypp user-agent' do + let(:headers) { auth_header.merge(zypp_user_agent_header) } + + it 'ignores zypp user-agent' do + update_action + expect(system.reload.system_information_hash[:user_agent]).to be_nil + end + end end describe '#deregister' do diff --git a/spec/support/shared_contexts/headers.rb b/spec/support/shared_contexts/headers.rb index 352363776..f1946a453 100644 --- a/spec/support/shared_contexts/headers.rb +++ b/spec/support/shared_contexts/headers.rb @@ -2,6 +2,14 @@ let(:version_header) { { 'Accept' => "application/vnd.scc.suse.com.v#{version}+json" } } end +shared_context 'user-agent header' do |_version| + let(:user_agent_header) { { 'User-Agent' => 'suseconnect-ng/1.2' } } +end + +shared_context 'zypp user-agent header' do |_version| + let(:zypp_user_agent_header) { { 'User-Agent' => 'zypp/1.0' } } +end + shared_context 'auth header' do |login_object, login_method, password_method| let(:auth_header) do basic_auth_header send(login_object).send(login_method), send(login_object).send(password_method) From 1af565ec6bc4ffe2d67a95eb44307b389756e402 Mon Sep 17 00:00:00 2001 From: Adnilson Delgado Date: Fri, 20 Sep 2024 12:54:21 +0100 Subject: [PATCH 06/16] Use a limit of 512 --- db/schema.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/db/schema.rb b/db/schema.rb index 70a68e684..1584f1814 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -34,7 +34,7 @@ create_table "downloaded_files", charset: "utf8", force: :cascade do |t| t.string "checksum_type" t.string "checksum" - t.text "local_path", limit: 512 + t.string "local_path", limit: 512 t.bigint "file_size", unsigned: true t.index ["checksum_type", "checksum"], name: "index_downloaded_files_on_checksum_type_and_checksum" t.index ["local_path"], name: "index_downloaded_files_on_local_path", unique: true @@ -104,7 +104,7 @@ t.string "auth_token" t.boolean "installer_updates", default: false, null: false t.boolean "mirroring_enabled", default: false, null: false - t.text "local_path", limit: 512, null: false + t.string "local_path", limit: 512, null: false t.datetime "last_mirrored_at" t.string "friendly_id" t.index ["external_url"], name: "index_repositories_on_external_url", unique: true From 42ceee0fc6b69b537b348cc45db24da0041a9a03 Mon Sep 17 00:00:00 2001 From: Natnael Getahun Date: Tue, 24 Sep 2024 16:54:22 +0200 Subject: [PATCH 07/16] Add new configuration files for timers --- package/obs/rmt-server.spec | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/package/obs/rmt-server.spec b/package/obs/rmt-server.spec index 6aa6b9a2f..5e357fbbf 100644 --- a/package/obs/rmt-server.spec +++ b/package/obs/rmt-server.spec @@ -269,6 +269,7 @@ chrpath -d %{buildroot}%{lib_dir}/vendor/bundle/ruby/*/extensions/*/*/mysql2-*/m %dir %{_sysconfdir}/slp.reg.d %config(noreplace) %attr(0640, %{rmt_user}, root) %{_sysconfdir}/rmt.conf %config(noreplace) %{_sysconfdir}/slp.reg.d/rmt-server.reg + %{_mandir}/man8/rmt-cli.8%{?ext_man} %{_bindir}/rmt-cli %{_bindir}/rmt-data-import @@ -289,6 +290,11 @@ chrpath -d %{buildroot}%{lib_dir}/vendor/bundle/ruby/*/extensions/*/*/mysql2-*/m %{_unitdir}/rmt-server-systems-scc-sync.timer %{_unitdir}/rmt-uptime-cleanup.service %{_unitdir}/rmt-uptime-cleanup.timer +%config(noreplace) %{_unitdir}/rmt-server-mirror.timer +%config(noreplace) %{_unitdir}/rmt-server-sync.timer +%config(noreplace) %{_unitdir}/rmt-server-systems-scc-sync.timer +%config(noreplace) %{_unitdir}/rmt-uptime-cleanup.timer + %dir %{_datadir}/bash-completion/ %dir %{_datadir}/bash-completion/completions/ %{_datadir}/bash-completion/completions/rmt-cli @@ -323,6 +329,8 @@ chrpath -d %{buildroot}%{lib_dir}/vendor/bundle/ruby/*/extensions/*/*/mysql2-*/m %{_unitdir}/rmt-server-regsharing.timer %{_unitdir}/rmt-server-trim-cache.service %{_unitdir}/rmt-server-trim-cache.timer +%config(noreplace) %{_unitdir}/rmt-server-regsharing.timer +%config(noreplace) %{_unitdir}/rmt-server-trim-cache.timer %pre getent group %{rmt_group} >/dev/null || %{_sbindir}/groupadd -r %{rmt_group} From 8a13dce40aa4b380774aee0288ea60fdcc38b718 Mon Sep 17 00:00:00 2001 From: "depfu[bot]" <23717796+depfu[bot]@users.noreply.github.com> Date: Tue, 1 Oct 2024 21:15:51 +0000 Subject: [PATCH 08/16] Update webmock to version 3.24.0 --- Gemfile.lock | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index d48697404..851760e46 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -30,8 +30,8 @@ GEM minitest (>= 5.1) tzinfo (~> 2.0) zeitwerk (~> 2.3) - addressable (2.8.6) - public_suffix (>= 2.0.2, < 6.0) + addressable (2.8.7) + public_suffix (>= 2.0.2, < 7.0) ast (2.4.2) awesome_print (1.9.2) base32 (0.3.4) @@ -136,7 +136,7 @@ GEM guard (~> 2.1) guard-compat (~> 1.1) rspec (>= 2.99.0, < 4.0) - hashdiff (1.1.0) + hashdiff (1.1.1) hpricot (0.8.6) i18n (1.14.5) concurrent-ruby (~> 1.0) @@ -209,8 +209,7 @@ GEM responders (3.1.1) actionpack (>= 5.2) railties (>= 5.2) - rexml (3.3.6) - strscan + rexml (3.3.8) ronn (0.7.3) hpricot (>= 0.8.2) mustache (>= 0.7.0) @@ -296,7 +295,6 @@ GEM sqlite3 (1.4.4) strong_migrations (0.7.9) activerecord (>= 5) - strscan (3.1.0) sync (0.5.0) term-ansicolor (1.7.1) tins (~> 1.0) @@ -317,7 +315,7 @@ GEM activesupport (>= 3) railties (>= 3) yard (~> 0.9.20) - webmock (3.23.1) + webmock (3.24.0) addressable (>= 2.8.0) crack (>= 0.3.2) hashdiff (>= 0.4.0, < 2.0.0) From d4be4b42241e16ca3a933d0012acc3a84251f3d6 Mon Sep 17 00:00:00 2001 From: Jesus Bermudez Velazquez Date: Wed, 2 Oct 2024 16:38:19 +0100 Subject: [PATCH 09/16] Make SccProxy method responsible for handling the SCC request response When calling SccProxy scc_activate_product method, the response should be handled in that method as well This helps reducing duplication and better responsibility separation Also, new method to announce hybrid system to SCC Update tests to reflect the changes --- engines/scc_proxy/lib/scc_proxy/engine.rb | 124 ++++++++---------- .../v3/systems/products_controller_spec.rb | 4 + 2 files changed, 58 insertions(+), 70 deletions(-) diff --git a/engines/scc_proxy/lib/scc_proxy/engine.rb b/engines/scc_proxy/lib/scc_proxy/engine.rb index 378b87a99..7c4e6a1bb 100644 --- a/engines/scc_proxy/lib/scc_proxy/engine.rb +++ b/engines/scc_proxy/lib/scc_proxy/engine.rb @@ -139,12 +139,23 @@ def announce_system_scc(auth, params) JSON.parse(response.body) end - def scc_activate_product(product, auth, params, mode) + def scc_activate_product(system, product, auth, params, mode) uri = URI.parse(SYSTEM_PRODUCTS_URL) http = Net::HTTP.new(uri.host, uri.port) http.use_ssl = true scc_request = prepare_scc_request(uri.path, product, auth, params, mode) - http.request(scc_request) + response = http.request(scc_request) + unless response.code_type == Net::HTTPCreated + error = JSON.parse(response.body) + Rails.logger.info "Could not activate #{product.product_string}, error: #{error['error']} #{response.code}" + error['error'] = SccProxy.parse_error(error['error']) if error['error'].include? 'json' + # if trying to activate first product on a hybrid system + # it means the system was "just" announced on this call + # if product activation failed, system should get de-register from SCC + SccProxy.deregister_system_scc(auth, system) if system.payg? + + raise ActionController::TranslatedError.new(error['error']) + end end def deactivate_product_scc(auth, product, params, logger) @@ -168,12 +179,19 @@ def deactivate_product_scc(auth, product, params, logger) response end - def deregister_system_scc(auth, system_token) + def deregister_system_scc(auth, system) uri = URI.parse(DEREGISTER_SYSTEM_URL) http = Net::HTTP.new(uri.host, uri.port) http.use_ssl = true - scc_request = Net::HTTP::Delete.new(uri.path, headers(auth, system_token)) - http.request(scc_request) + scc_request = Net::HTTP::Delete.new(uri.path, headers(auth, system.system_token)) + response = http.request(scc_request) + unless response.code_type == Net::HTTPNoContent + error = JSON.parse(response.body) + Rails.logger.info "Could not de-activate system #{system.login}, error: #{error['error']} #{response.code}" + error['error'] = SccProxy.parse_error(error['error'], params[:token], params[:email]) if error['error'].include? 'json' + raise ActionController::TranslatedError.new(error['error']) + end + Rails.logger.info 'System successfully deregistered from SCC' end def parse_error(error_message, token = nil, email = nil) @@ -346,46 +364,16 @@ def has_no_regcode?(auth_header) protected - # rubocop:disable Metrics/PerceivedComplexity - # rubocop:disable Metrics/CyclomaticComplexity def scc_activate_product - logger.info "Activating product #{@product.product_string} to SCC" - auth = nil - auth = request.headers['HTTP_AUTHORIZATION'] if request.headers.include?('HTTP_AUTHORIZATION') - mode = nil - if @system.byos? - mode = 'byos' - elsif !@product.free? && @product.extension? && params[:token].present? - mode = 'hybrid' - # the extensions must be the same version and arch - # than base product - base_prod = @system.products.find_by(product_type: :base) - if @system.payg? && base_prod.present? - raise 'Incompatible extension product' unless @product.arch == base_prod.arch && @product.version == base_prod.version - - update_params_system_info mode - announce_auth = "Token token=#{params[:token]}" - - response = SccProxy.announce_system_scc(announce_auth, params) - end - end - + mode = find_mode unless mode.nil? # if system is byos or hybrid and there is a token # make a request to SCC - response = SccProxy.scc_activate_product(@product, auth, params, mode) - unless response.code_type == Net::HTTPCreated - error = JSON.parse(response.body) - logger.info "Could not activate #{@product.product_string}, error: #{error['error']} #{response.code}" - error['error'] = SccProxy.parse_error(error['error']) if error['error'].include? 'json' - if @system.payg? - # if trying to activate first product on a hybrid system - # it means the system was "just" announced on this call - # if product activation failed, system should get de-register from SCC - deregister_hybrid(request.headers['HTTP_AUTHORIZATION']) - end - raise ActionController::TranslatedError.new(error['error']) - end + logger.info "Activating product #{@product.product_string} to SCC" + logger.info 'No token provided' if params[:token].blank? + SccProxy.scc_activate_product( + @system, @product, request.headers['HTTP_AUTHORIZATION'], params, mode + ) # if the system is PAYG and the registration code is valid for the extension, # then the system is hybrid # update the system to HYBRID mode if HYBRID MODE and system not HYBRID already @@ -394,20 +382,31 @@ def scc_activate_product logger.info "Product #{@product.product_string} successfully activated with SCC" InstanceVerification.update_cache(request.remote_ip, @system.login, @product.id) end - logger.info 'No token provided' if params[:token].blank? end - # rubocop:enable Metrics/CyclomaticComplexity - # rubocop:enable Metrics/PerceivedComplexity - - def deregister_hybrid(auth) - response = SccProxy.deregister_system_scc(auth, @system.system_token) - unless response.code_type == Net::HTTPNoContent - error = JSON.parse(response.body) - logger.info "Could not de-activate system #{@system.login}, error: #{error['error']} #{response.code}" - error['error'] = SccProxy.parse_error(error['error'], params[:token], params[:email]) if error['error'].include? 'json' - raise ActionController::TranslatedError.new(error['error']) + + def find_mode + if @system.byos? + 'byos' + elsif !@product.free? && @product.extension? && params[:token].present? + announce_base_product_hybrid 'hybrid' + 'hybrid' + end + end + + def announce_base_product_hybrid(mode) + # in order for SCC to be able activate the extension (i.e. LTSS) + # the system must be announced to SCC first + base_prod = @system.products.find_by(product_type: :base) + # the extensions must be the same version and arch + # than base product + if @system.payg? && base_prod.present? + raise 'Incompatible extension product' unless @product.arch == base_prod.arch && @product.version == base_prod.version + + update_params_system_info mode + SccProxy.announce_system_scc( + "Token token=#{params[:token]}", params + ) end - logger.info 'System successfully deregistered from SCC' end def scc_upgrade @@ -466,14 +465,7 @@ def make_system_payg(auth) # switch it back to payg # drop the just de-activated activation from the list to avoid another call to SCC # and check if there is any product - response = SccProxy.deregister_system_scc(auth, @system.system_token) - unless response.code_type == Net::HTTPNoContent - error = JSON.parse(response.body) - logger.info "Could not de-activate system #{@system.login}, error: #{error['error']} #{response.code}" - error['error'] = SccProxy.parse_error(error['error'], params[:token], params[:email]) if error['error'].include? 'json' - raise ActionController::TranslatedError.new(error['error']) - end - logger.info 'System successfully deregistered from SCC' + SccProxy.deregister_system_scc(auth, @system) @system.payg! end end @@ -486,15 +478,7 @@ def make_system_payg(auth) def scc_deregistration if @system.byos? || @system.hybrid? # byos and hybrid systems should get de-register from SCC - auth = request.headers['HTTP_AUTHORIZATION'] - response = SccProxy.deregister_system_scc(auth, @system.system_token) - unless response.code_type == Net::HTTPNoContent - error = JSON.parse(response.body) - logger.info "Could not de-activate system #{@system.login}, error: #{error['error']} #{response.code}" - error['error'] = SccProxy.parse_error(error['error'], params[:token], params[:email]) if error['error'].include? 'json' - raise ActionController::TranslatedError.new(error['error']) - end - logger.info 'System successfully deregistered from SCC' + SccProxy.deregister_system_scc(request.headers['HTTP_AUTHORIZATION'], @system) end end end diff --git a/engines/scc_proxy/spec/requests/api/connect/v3/systems/products_controller_spec.rb b/engines/scc_proxy/spec/requests/api/connect/v3/systems/products_controller_spec.rb index 9daced829..df9e7f2c3 100644 --- a/engines/scc_proxy/spec/requests/api/connect/v3/systems/products_controller_spec.rb +++ b/engines/scc_proxy/spec/requests/api/connect/v3/systems/products_controller_spec.rb @@ -474,7 +474,10 @@ end context 'when de-register system from SCC fails' do + let(:error_message) { "Could not activate #{product.product_string}, error: No product found on SCC for: foo bar x86_64 json api 401" } + before do + allow(Rails.logger).to receive(:info) stub_request(:delete, scc_systems_url) .to_return( status: 401, @@ -490,6 +493,7 @@ it 'renders an error with exception details' do data = JSON.parse(response.body) expect(data['error']).to eq('Could not de-register system') + expect(Rails.logger).to(have_received(:info).with(error_message)) # rubocop:disable RSpec/MessageSpies end end end From e79e4bae45534ea4500d59425d5276ba19c68d70 Mon Sep 17 00:00:00 2001 From: Jesus Bermudez Velazquez Date: Wed, 2 Oct 2024 17:04:40 +0100 Subject: [PATCH 10/16] Prefer receive over have_received --- .../api/connect/v3/systems/products_controller_spec.rb | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/engines/scc_proxy/spec/requests/api/connect/v3/systems/products_controller_spec.rb b/engines/scc_proxy/spec/requests/api/connect/v3/systems/products_controller_spec.rb index df9e7f2c3..0ff8417f3 100644 --- a/engines/scc_proxy/spec/requests/api/connect/v3/systems/products_controller_spec.rb +++ b/engines/scc_proxy/spec/requests/api/connect/v3/systems/products_controller_spec.rb @@ -486,14 +486,13 @@ ) stub_request(:post, scc_register_system_url) .to_return(status: 201, body: { ok: 'OK' }.to_json, headers: {}) - - post url, params: payload, headers: headers end it 'renders an error with exception details' do + expect(Rails.logger).to receive(:info).with(error_message) + post url, params: payload, headers: headers data = JSON.parse(response.body) expect(data['error']).to eq('Could not de-register system') - expect(Rails.logger).to(have_received(:info).with(error_message)) # rubocop:disable RSpec/MessageSpies end end end From af665df523e569752bf53ad06a778bd0495cbfae Mon Sep 17 00:00:00 2001 From: "depfu[bot]" <23717796+depfu[bot]@users.noreply.github.com> Date: Fri, 4 Oct 2024 07:16:09 +0000 Subject: [PATCH 11/16] Update jwt to version 2.9.3 --- Gemfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile.lock b/Gemfile.lock index d48697404..419d5dd30 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -142,7 +142,7 @@ GEM concurrent-ruby (~> 1.0) json (2.3.1) jsonapi-renderer (0.2.2) - jwt (2.9.0) + jwt (2.9.3) base64 listen (3.6.0) rb-fsevent (~> 0.10, >= 0.10.3) From fba80b625a65841acfec1b144d0e4417e510fb04 Mon Sep 17 00:00:00 2001 From: Adnilson Date: Tue, 8 Oct 2024 11:51:04 +0100 Subject: [PATCH 12/16] Update package documentation --- PACKAGE.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PACKAGE.md b/PACKAGE.md index 51f0e7e01..557f750d0 100644 --- a/PACKAGE.md +++ b/PACKAGE.md @@ -21,7 +21,7 @@ Note: Look below for direction on publishing to registry. ``` * Alternatively, if an OBS working copy is already checked out, update the working copy by running `osc up` 2. Run `make dist` in your RMT working directory to build a tarball. -3. Copy the files from the `package/obs` directory to the OBS working directory. +3. Copy the files from the `package/obs` directory to the OBS working directory `systemsmanagement:SCC:RMT/rmt-server`. 4. Examine the changes by running `osc status` and `osc diff`. 5. Stage the changes by running `osc addremove`. 6. Build the package with osc: From f43323b5a05afdd290c75b18bc02528053dd3986 Mon Sep 17 00:00:00 2001 From: "depfu[bot]" <23717796+depfu[bot]@users.noreply.github.com> Date: Mon, 28 Oct 2024 16:53:16 +0000 Subject: [PATCH 13/16] Update rexml to version 3.3.9 --- Gemfile.lock | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index d48697404..d0a30e82e 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -209,8 +209,7 @@ GEM responders (3.1.1) actionpack (>= 5.2) railties (>= 5.2) - rexml (3.3.6) - strscan + rexml (3.3.9) ronn (0.7.3) hpricot (>= 0.8.2) mustache (>= 0.7.0) @@ -296,7 +295,6 @@ GEM sqlite3 (1.4.4) strong_migrations (0.7.9) activerecord (>= 5) - strscan (3.1.0) sync (0.5.0) term-ansicolor (1.7.1) tins (~> 1.0) From 9a617a0754672bd57fb2f3ddd2658952de66ee3d Mon Sep 17 00:00:00 2001 From: Adnilson Delgado Date: Tue, 29 Oct 2024 19:41:27 +0000 Subject: [PATCH 14/16] Trigger Build From 3ce29b4a5b09a6a9be367570889a1a863da04489 Mon Sep 17 00:00:00 2001 From: Jesus Bermudez Velazquez Date: Mon, 4 Nov 2024 16:28:16 +0000 Subject: [PATCH 15/16] Add stub request Error gets built automagically by rspec, no need to { error: }.to_json --- .../v3/systems/products_controller_spec.rb | 27 ++++++++++++++----- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/engines/instance_verification/spec/requests/api/connect/v3/systems/products_controller_spec.rb b/engines/instance_verification/spec/requests/api/connect/v3/systems/products_controller_spec.rb index 6d6e15d97..243436c73 100644 --- a/engines/instance_verification/spec/requests/api/connect/v3/systems/products_controller_spec.rb +++ b/engines/instance_verification/spec/requests/api/connect/v3/systems/products_controller_spec.rb @@ -72,9 +72,15 @@ context 'when verification provider returns false' do before do - expect(InstanceVerification::Providers::Example).to receive(:new) - .with(be_a(ActiveSupport::Logger), be_a(ActionDispatch::Request), payload, instance_data).and_return(plugin_double) - expect(plugin_double).to receive(:instance_valid?).and_return(false) + stub_request(:post, scc_activate_url) + .to_return( + status: 200, + body: { error: 'Unexpected instance verification error has occurred' }.to_json, + headers: {} + ) + # expect(InstanceVerification::Providers::Example).to receive(:new) + # .with(be_a(ActiveSupport::Logger), be_a(ActionDispatch::Request), payload, instance_data).and_return(plugin_double) + # expect(plugin_double).to receive(:instance_valid?).and_return(false) post url, params: payload, headers: headers end @@ -86,9 +92,16 @@ context 'when verification provider raises an unhandled exception' do before do - expect(InstanceVerification::Providers::Example).to receive(:new) - .with(be_a(ActiveSupport::Logger), be_a(ActionDispatch::Request), payload, instance_data).and_return(plugin_double) - expect(plugin_double).to receive(:instance_valid?).and_raise('Custom plugin error') + stub_request(:post, scc_activate_url) + .to_return( + status: 422, + body: { error: 'Unexpected instance verification error has occurred' }.to_json, + headers: {} + ) + + # expect(InstanceVerification::Providers::Example).to receive(:new) + # .with(be_a(ActiveSupport::Logger), be_a(ActionDispatch::Request), payload, instance_data).and_return(plugin_double) + # expect(plugin_double).to receive(:instance_valid?).and_raise('Custom plugin error') post url, params: payload, headers: headers end @@ -623,7 +636,7 @@ .with({ headers: scc_headers, body: payload.merge({ byos_mode: 'byos' }) }) .and_return( status: 401, - body: { error: 'error_message' }.to_json, + body: 'Migration target not allowed on this instance type', headers: {} ) request From 3ceb78994a56db40d1e5284798d4eac525410a14 Mon Sep 17 00:00:00 2001 From: Jesus Bermudez Velazquez Date: Tue, 5 Nov 2024 14:45:59 +0000 Subject: [PATCH 16/16] Remove commented expects Expect was too granular and specific --- .../api/connect/v3/systems/products_controller_spec.rb | 6 ------ 1 file changed, 6 deletions(-) diff --git a/engines/instance_verification/spec/requests/api/connect/v3/systems/products_controller_spec.rb b/engines/instance_verification/spec/requests/api/connect/v3/systems/products_controller_spec.rb index 243436c73..9ccbcb1c2 100644 --- a/engines/instance_verification/spec/requests/api/connect/v3/systems/products_controller_spec.rb +++ b/engines/instance_verification/spec/requests/api/connect/v3/systems/products_controller_spec.rb @@ -78,9 +78,6 @@ body: { error: 'Unexpected instance verification error has occurred' }.to_json, headers: {} ) - # expect(InstanceVerification::Providers::Example).to receive(:new) - # .with(be_a(ActiveSupport::Logger), be_a(ActionDispatch::Request), payload, instance_data).and_return(plugin_double) - # expect(plugin_double).to receive(:instance_valid?).and_return(false) post url, params: payload, headers: headers end @@ -99,9 +96,6 @@ headers: {} ) - # expect(InstanceVerification::Providers::Example).to receive(:new) - # .with(be_a(ActiveSupport::Logger), be_a(ActionDispatch::Request), payload, instance_data).and_return(plugin_double) - # expect(plugin_double).to receive(:instance_valid?).and_raise('Custom plugin error') post url, params: payload, headers: headers end