From 30b2bd973025a727e66de5f3aeb61caac7de9c0b Mon Sep 17 00:00:00 2001 From: Thomas Schmidt Date: Tue, 12 Mar 2024 17:26:24 +0100 Subject: [PATCH 001/103] skip tests that don't work with sqlite --- spec/lib/rmt/cli/smt_importer_spec.rb | 2 +- spec/lib/rmt/lockfile_spec.rb | 2 +- spec/lib/rmt/scc_spec.rb | 2 +- spec/models/repository_spec.rb | 6 +++--- spec/rails_helper.rb | 6 ++++++ spec/services/repository_service_spec.rb | 2 +- 6 files changed, 13 insertions(+), 7 deletions(-) diff --git a/spec/lib/rmt/cli/smt_importer_spec.rb b/spec/lib/rmt/cli/smt_importer_spec.rb index d77404a52..17063bbc0 100644 --- a/spec/lib/rmt/cli/smt_importer_spec.rb +++ b/spec/lib/rmt/cli/smt_importer_spec.rb @@ -1,7 +1,7 @@ require 'rails_helper' require 'rmt/cli/smt_importer' -describe RMT::CLI::SMTImporter do +describe RMT::CLI::SMTImporter, :skip_sqlite do let(:data_dir) { File.join(Dir.pwd, 'spec/fixtures/files/csv') } let(:no_systems) { false } let(:importer) { described_class.new(data_dir, no_systems) } diff --git a/spec/lib/rmt/lockfile_spec.rb b/spec/lib/rmt/lockfile_spec.rb index d1b86f968..c06644bc3 100644 --- a/spec/lib/rmt/lockfile_spec.rb +++ b/spec/lib/rmt/lockfile_spec.rb @@ -3,7 +3,7 @@ RSpec.describe RMT::Lockfile do let(:lock_name) { nil } - describe '#lock' do + describe '#lock', :skip_sqlite do subject(:lock) { described_class.lock(lock_name) { nil } } context 'with an unnamed lock' do diff --git a/spec/lib/rmt/scc_spec.rb b/spec/lib/rmt/scc_spec.rb index a4732b0cd..94b2a739d 100644 --- a/spec/lib/rmt/scc_spec.rb +++ b/spec/lib/rmt/scc_spec.rb @@ -238,7 +238,7 @@ described_class.new.sync end - it 'removes existing predecessor associations' do + it 'removes existing predecessor associations', :skip_sqlite do expect { ProductPredecessorAssociation.find(existing_association.id) }.to raise_error(ActiveRecord::RecordNotFound) end end diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb index 3c958ac15..3c49cd6fa 100644 --- a/spec/models/repository_spec.rb +++ b/spec/models/repository_spec.rb @@ -146,7 +146,7 @@ expect(friendly_id).to eq('my-repo-100000') end - it 'appends with unicode within the chain' do + it 'appends with unicode within the chain', :skip_sqlite do create(:repository, friendly_id: 'my-repo') create(:repository, friendly_id: 'my-repö-1') expect(friendly_id).to eq('my-repo-2') @@ -165,13 +165,13 @@ expect(friendly_id).to eq('dümmy-repö') end - it 'appends with unicode within the chain' do + it 'appends with unicode within the chain', :skip_sqlite do create(:repository, friendly_id: 'dummy-repo') create(:repository, friendly_id: 'dümmy-repö-1') expect(friendly_id).to eq('dümmy-repö-2') end - it 'correctly appends' do + it 'correctly appends', :skip_sqlite do create(:repository, friendly_id: 'dummy-repo') create(:repository, friendly_id: 'dummy-repo-1') expect(friendly_id).to eq('dümmy-repö-2') diff --git a/spec/rails_helper.rb b/spec/rails_helper.rb index 5d71fea9f..f5b47b1e6 100644 --- a/spec/rails_helper.rb +++ b/spec/rails_helper.rb @@ -58,6 +58,12 @@ config.filter_rails_from_backtrace! # arbitrary gems may also be filtered via: # config.filter_gems_from_backtrace("gem name") + + # Skipping some tests when running with (experimental) sqlite backend. + # Some tests / code parts are using specific mysql behavior + if ActiveRecord::Base.connection.adapter_name == 'SQLite' + config.filter_run_excluding :skip_sqlite => true + end end Shoulda::Matchers.configure do |config| diff --git a/spec/services/repository_service_spec.rb b/spec/services/repository_service_spec.rb index 1387ea3b0..0035395b5 100644 --- a/spec/services/repository_service_spec.rb +++ b/spec/services/repository_service_spec.rb @@ -88,7 +88,7 @@ it('is not custom') { expect(repository.custom?).to eq(true) } - context 'already existing repositories with changing URL' do + context 'already existing repositories with changing URL', :skip_sqlite do subject(:repository) do service.create_repository!(product, url, attributes, custom: custom).reload url = 'https://foo.bar.com/bar/foo' From d380414ae3bd537c464ed06ffaaf5fd439bd4f1a Mon Sep 17 00:00:00 2001 From: Thomas Schmidt Date: Tue, 12 Mar 2024 17:28:16 +0100 Subject: [PATCH 002/103] add step to ci to run tests with sqlite --- .github/workflows/lint-unit.yml | 5 +++++ spec/rails_helper.rb | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/workflows/lint-unit.yml b/.github/workflows/lint-unit.yml index 9ac44b6eb..66bf3f77c 100644 --- a/.github/workflows/lint-unit.yml +++ b/.github/workflows/lint-unit.yml @@ -83,6 +83,11 @@ jobs: run: | bundle exec rspec --format documentation + - name: Run core tests with sqlite + run: | + sed -i 's/adapter: mysql2/adapter: sqlite3/' config/rmt.yml + bundle exec rspec --format documentation + - name: Run PubCloud engines tests run: | bundle exec rake test:engines diff --git a/spec/rails_helper.rb b/spec/rails_helper.rb index f5b47b1e6..9c8bffe57 100644 --- a/spec/rails_helper.rb +++ b/spec/rails_helper.rb @@ -62,7 +62,7 @@ # Skipping some tests when running with (experimental) sqlite backend. # Some tests / code parts are using specific mysql behavior if ActiveRecord::Base.connection.adapter_name == 'SQLite' - config.filter_run_excluding :skip_sqlite => true + config.filter_run_excluding skip_sqlite: true end end From ddd57ff370a98a8376c24841c461eadc8caaab81 Mon Sep 17 00:00:00 2001 From: Thomas Schmidt Date: Sat, 24 Aug 2024 22:15:18 +0200 Subject: [PATCH 003/103] 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 +++--- 2 files changed, 3 insertions(+), 16 deletions(-) delete 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 deleted file mode 100644 index 83e0789e9..000000000 --- a/db/migrate/20240821114908_change_local_path_type.rb +++ /dev/null @@ -1,13 +0,0 @@ -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 bf4ce342e..9607b8f9a 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_08_21_114908) do +ActiveRecord::Schema.define(version: 2024_07_29_103525) 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.text "local_path" + t.string "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.text "local_path", null: false + t.string "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 From 69f7ab35cb02f8fad9f7be3faa0742304f1ae654 Mon Sep 17 00:00:00 2001 From: Thomas Schmidt Date: Sat, 24 Aug 2024 22:17:08 +0200 Subject: [PATCH 004/103] drop changelog line --- package/obs/rmt-server.changes | 1 - 1 file changed, 1 deletion(-) diff --git a/package/obs/rmt-server.changes b/package/obs/rmt-server.changes index 71ad51f29..febce557b 100644 --- a/package/obs/rmt-server.changes +++ b/package/obs/rmt-server.changes @@ -3,7 +3,6 @@ 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 41a96519608b307fa73326b1254d3a2fb3c9e24f Mon Sep 17 00:00:00 2001 From: Thomas Schmidt Date: Mon, 26 Aug 2024 09:52:06 +0200 Subject: [PATCH 005/103] 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 35e3ac2239d2115cb5f248ea013974aae2a79f41 Mon Sep 17 00:00:00 2001 From: Jesus Bermudez Velazquez Date: Mon, 26 Aug 2024 15:31:15 +0100 Subject: [PATCH 006/103] pick instance data from hwinfo params in scc_proxy engine --- engines/scc_proxy/lib/scc_proxy/engine.rb | 14 ++++++++++---- .../v3/subscriptions/systems_controller_spec.rb | 8 ++++---- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/engines/scc_proxy/lib/scc_proxy/engine.rb b/engines/scc_proxy/lib/scc_proxy/engine.rb index 08e5cae1f..ca35627e5 100644 --- a/engines/scc_proxy/lib/scc_proxy/engine.rb +++ b/engines/scc_proxy/lib/scc_proxy/engine.rb @@ -66,7 +66,7 @@ def get_instance_id(params) nil, nil, nil, - params['instance_data'] + params['hwinfo']['instance_data'] ) instance_id_key = INSTANCE_ID_KEYS[params['hwinfo']['cloud_provider'].downcase.to_sym] iid = verification_provider.parse_instance_data @@ -261,11 +261,16 @@ def announce_system auth_header = nil auth_header = request.headers['HTTP_AUTHORIZATION'] if request.headers.include?('HTTP_AUTHORIZATION') system_information = hwinfo_params[:hwinfo].to_json - + instance_data = hwinfo_params[:hwinfo] && params[:hwinfo].key?('instance_data') ? hwinfo_params[:hwinfo][:instance_data] : nil if has_no_regcode?(auth_header) # no token sent to check with SCC # standard announce case - @system = System.create!(hostname: params[:hostname], system_information: system_information, proxy_byos_mode: :payg) + @system = System.create!( + hostname: params[:hostname], + system_information: system_information, + proxy_byos_mode: :payg, + instance_data: instance_data + ) else request.request_parameters['proxy_byos_mode'] = 'byos' response = SccProxy.announce_system_scc(auth_header, request.request_parameters) @@ -275,7 +280,8 @@ def announce_system password: response['password'], hostname: params[:hostname], proxy_byos_mode: :byos, - system_information: system_information + system_information: system_information, + instance_data: instance_data ) end logger.info("System '#{@system.hostname}' announced") diff --git a/engines/scc_proxy/spec/requests/api/connect/v3/subscriptions/systems_controller_spec.rb b/engines/scc_proxy/spec/requests/api/connect/v3/subscriptions/systems_controller_spec.rb index 442bfd9cf..b9fbe3864 100644 --- a/engines/scc_proxy/spec/requests/api/connect/v3/subscriptions/systems_controller_spec.rb +++ b/engines/scc_proxy/spec/requests/api/connect/v3/subscriptions/systems_controller_spec.rb @@ -18,7 +18,6 @@ { hostname: 'test', proxy_byos_mode: :byos, - instance_data: instance_data, hwinfo: { hostname: 'test', @@ -27,7 +26,8 @@ hypervisor: 'Xen', arch: 'x86_64', uuid: 'ec235f7d-b435-e27d-86c6-c8fef3180a01', - cloud_provider: 'super_cloud' + cloud_provider: 'super_cloud', + instance_data: instance_data } } end @@ -101,7 +101,6 @@ { hostname: 'test', proxy_byos_mode: :payg, - instance_data: instance_data, hwinfo: { hostname: 'test', @@ -110,7 +109,8 @@ hypervisor: 'Xen', arch: 'x86_64', uuid: 'ec235f7d-b435-e27d-86c6-c8fef3180a01', - cloud_provider: 'super_cloud' + cloud_provider: 'super_cloud', + instance_data: instance_data, } } end From 24346aa7ce0e0ef68f7242c30df497fd4a46b96e Mon Sep 17 00:00:00 2001 From: Jesus Bermudez Velazquez Date: Mon, 26 Aug 2024 16:24:34 +0100 Subject: [PATCH 007/103] Remove trailing comma --- .../api/connect/v3/subscriptions/systems_controller_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/engines/scc_proxy/spec/requests/api/connect/v3/subscriptions/systems_controller_spec.rb b/engines/scc_proxy/spec/requests/api/connect/v3/subscriptions/systems_controller_spec.rb index b9fbe3864..fc7c9adcf 100644 --- a/engines/scc_proxy/spec/requests/api/connect/v3/subscriptions/systems_controller_spec.rb +++ b/engines/scc_proxy/spec/requests/api/connect/v3/subscriptions/systems_controller_spec.rb @@ -110,7 +110,7 @@ arch: 'x86_64', uuid: 'ec235f7d-b435-e27d-86c6-c8fef3180a01', cloud_provider: 'super_cloud', - instance_data: instance_data, + instance_data: instance_data } } end From 99cb12a427ea9620b0fb9ceaf9b22e80f576d320 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Berm=C3=BAdez=20Vel=C3=A1zquez?= Date: Mon, 26 Aug 2024 16:59:15 +0100 Subject: [PATCH 008/103] Update engines/scc_proxy/lib/scc_proxy/engine.rb Co-authored-by: Thomas Schmidt --- engines/scc_proxy/lib/scc_proxy/engine.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/engines/scc_proxy/lib/scc_proxy/engine.rb b/engines/scc_proxy/lib/scc_proxy/engine.rb index ca35627e5..8dfd205fb 100644 --- a/engines/scc_proxy/lib/scc_proxy/engine.rb +++ b/engines/scc_proxy/lib/scc_proxy/engine.rb @@ -261,7 +261,7 @@ def announce_system auth_header = nil auth_header = request.headers['HTTP_AUTHORIZATION'] if request.headers.include?('HTTP_AUTHORIZATION') system_information = hwinfo_params[:hwinfo].to_json - instance_data = hwinfo_params[:hwinfo] && params[:hwinfo].key?('instance_data') ? hwinfo_params[:hwinfo][:instance_data] : nil + instance_data = hwinfo_params[:hwinfo].fetch('instance_data', nil) if has_no_regcode?(auth_header) # no token sent to check with SCC # standard announce case From eddd08465a94c8cb07b4089982594ca6eae41c4b Mon Sep 17 00:00:00 2001 From: Jesus Bermudez Velazquez Date: Mon, 26 Aug 2024 17:04:18 +0100 Subject: [PATCH 009/103] Update fetch instance data to a symbol --- engines/scc_proxy/lib/scc_proxy/engine.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/engines/scc_proxy/lib/scc_proxy/engine.rb b/engines/scc_proxy/lib/scc_proxy/engine.rb index 8dfd205fb..b9873747e 100644 --- a/engines/scc_proxy/lib/scc_proxy/engine.rb +++ b/engines/scc_proxy/lib/scc_proxy/engine.rb @@ -261,7 +261,7 @@ def announce_system auth_header = nil auth_header = request.headers['HTTP_AUTHORIZATION'] if request.headers.include?('HTTP_AUTHORIZATION') system_information = hwinfo_params[:hwinfo].to_json - instance_data = hwinfo_params[:hwinfo].fetch('instance_data', nil) + instance_data = hwinfo_params[:hwinfo].fetch(:instance_data, nil) if has_no_regcode?(auth_header) # no token sent to check with SCC # standard announce case From de80920b07cfa7571dc5aceb8b8e8f7d24ee0dd8 Mon Sep 17 00:00:00 2001 From: Jesus Bermudez Velazquez Date: Mon, 26 Aug 2024 17:09:02 +0100 Subject: [PATCH 010/103] Handle nil hwinfo as fetch does not cover it --- engines/scc_proxy/lib/scc_proxy/engine.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/engines/scc_proxy/lib/scc_proxy/engine.rb b/engines/scc_proxy/lib/scc_proxy/engine.rb index b9873747e..798b1b72a 100644 --- a/engines/scc_proxy/lib/scc_proxy/engine.rb +++ b/engines/scc_proxy/lib/scc_proxy/engine.rb @@ -261,7 +261,7 @@ def announce_system auth_header = nil auth_header = request.headers['HTTP_AUTHORIZATION'] if request.headers.include?('HTTP_AUTHORIZATION') system_information = hwinfo_params[:hwinfo].to_json - instance_data = hwinfo_params[:hwinfo].fetch(:instance_data, nil) + instance_data = hwinfo_params[:hwinfo].fetch(:instance_data, nil) if hwinfo_params[:hwinfo] if has_no_regcode?(auth_header) # no token sent to check with SCC # standard announce case From f510547f13981be6eedfcf9e067da48edcaffada Mon Sep 17 00:00:00 2001 From: Jesus Bermudez Velazquez Date: Tue, 27 Aug 2024 10:09:30 +0100 Subject: [PATCH 011/103] Place instance_data at the right level on params for announce system call --- .../api/connect/v3/systems/products_controller_spec.rb | 2 +- engines/scc_proxy/lib/scc_proxy/engine.rb | 6 +++--- .../connect/v3/subscriptions/systems_controller_spec.rb | 8 ++++---- 3 files changed, 8 insertions(+), 8 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 261775111..a2679292c 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 @@ -315,7 +315,7 @@ let(:scc_annouce_body) do { hostname: system.hostname, - hwinfo: JSON.parse(system.system_information).merge({ instance_data: system.instance_data }), + hwinfo: JSON.parse(system.system_information), byos_mode: 'hybrid', login: system.login, password: system.password diff --git a/engines/scc_proxy/lib/scc_proxy/engine.rb b/engines/scc_proxy/lib/scc_proxy/engine.rb index 798b1b72a..1114de827 100644 --- a/engines/scc_proxy/lib/scc_proxy/engine.rb +++ b/engines/scc_proxy/lib/scc_proxy/engine.rb @@ -66,7 +66,7 @@ def get_instance_id(params) nil, nil, nil, - params['hwinfo']['instance_data'] + params['instance_data'] ) instance_id_key = INSTANCE_ID_KEYS[params['hwinfo']['cloud_provider'].downcase.to_sym] iid = verification_provider.parse_instance_data @@ -261,7 +261,7 @@ def announce_system auth_header = nil auth_header = request.headers['HTTP_AUTHORIZATION'] if request.headers.include?('HTTP_AUTHORIZATION') system_information = hwinfo_params[:hwinfo].to_json - instance_data = hwinfo_params[:hwinfo].fetch(:instance_data, nil) if hwinfo_params[:hwinfo] + instance_data = params.fetch(:instance_data, nil) if has_no_regcode?(auth_header) # no token sent to check with SCC # standard announce case @@ -335,7 +335,7 @@ def scc_activate_product params['scc_login'] = @system.login params['scc_password'] = @system.password params['hwinfo'] = JSON.parse(@system.system_information) - params['hwinfo']['instance_data'] = @system.instance_data + params['instance_data'] = @system.instance_data announce_auth = "Token token=#{params[:token]}" response = SccProxy.announce_system_scc(announce_auth, params) diff --git a/engines/scc_proxy/spec/requests/api/connect/v3/subscriptions/systems_controller_spec.rb b/engines/scc_proxy/spec/requests/api/connect/v3/subscriptions/systems_controller_spec.rb index fc7c9adcf..442bfd9cf 100644 --- a/engines/scc_proxy/spec/requests/api/connect/v3/subscriptions/systems_controller_spec.rb +++ b/engines/scc_proxy/spec/requests/api/connect/v3/subscriptions/systems_controller_spec.rb @@ -18,6 +18,7 @@ { hostname: 'test', proxy_byos_mode: :byos, + instance_data: instance_data, hwinfo: { hostname: 'test', @@ -26,8 +27,7 @@ hypervisor: 'Xen', arch: 'x86_64', uuid: 'ec235f7d-b435-e27d-86c6-c8fef3180a01', - cloud_provider: 'super_cloud', - instance_data: instance_data + cloud_provider: 'super_cloud' } } end @@ -101,6 +101,7 @@ { hostname: 'test', proxy_byos_mode: :payg, + instance_data: instance_data, hwinfo: { hostname: 'test', @@ -109,8 +110,7 @@ hypervisor: 'Xen', arch: 'x86_64', uuid: 'ec235f7d-b435-e27d-86c6-c8fef3180a01', - cloud_provider: 'super_cloud', - instance_data: instance_data + cloud_provider: 'super_cloud' } } end From ea4d09f4690369e2cf6b847608e4ad51b7987e61 Mon Sep 17 00:00:00 2001 From: Jesus Bermudez Velazquez Date: Tue, 27 Aug 2024 17:56:49 +0100 Subject: [PATCH 012/103] Update system from hybrid to payg if no more non free products active When deactivating a product on a hybrid system, if there are no more non free product active for that system on SCC, that system is no longer hybrid but payg and should get updated on the db --- engines/scc_proxy/lib/scc_proxy/engine.rb | 12 ++ .../v4/systems/products_controller_spec.rb | 104 ++++++++++++++++++ 2 files changed, 116 insertions(+) diff --git a/engines/scc_proxy/lib/scc_proxy/engine.rb b/engines/scc_proxy/lib/scc_proxy/engine.rb index 1114de827..7a52c8ab0 100644 --- a/engines/scc_proxy/lib/scc_proxy/engine.rb +++ b/engines/scc_proxy/lib/scc_proxy/engine.rb @@ -405,6 +405,9 @@ def scc_deactivate_product # it is OK to remove it from SCC response = SccProxy.deactivate_product_scc(auth, @product, @system.system_token) handle_response(response) + # if the system does not have more non free products activated on SCC + # system should turn to hybrid + @system.payg! if no_more_activations scc_systems_activations elsif result[:message].downcase.include?('unexpected error') raise ActionController::TranslatedError.new(result[:message]) end @@ -429,6 +432,15 @@ def handle_response(response) raise ActionController::TranslatedError.new(error['error']) end end + + def no_more_activations(scc_systems_activations) + active_products_classes = scc_systems_activations.map do |act| + if act['status'].casecmp('active').zero? && act['service']['product']['product_class'] != @product.product_class + act['service']['product']['product_class'] + end + end.flatten.compact + active_products_classes.empty? + end end Api::Connect::V3::Systems::SystemsController.class_eval do diff --git a/engines/scc_proxy/spec/requests/api/connect/v4/systems/products_controller_spec.rb b/engines/scc_proxy/spec/requests/api/connect/v4/systems/products_controller_spec.rb index 4a38985e5..586921c26 100644 --- a/engines/scc_proxy/spec/requests/api/connect/v4/systems/products_controller_spec.rb +++ b/engines/scc_proxy/spec/requests/api/connect/v4/systems/products_controller_spec.rb @@ -187,6 +187,110 @@ end end + context 'when SCC API suceeds for HYBRiD system' do + let(:product) do + FactoryBot.create(:product, :product_sles, :extension, :with_mirrored_repositories, :with_mirrored_extensions, :activated, system: system_hybrid) + end + let(:payload) do + { + identifier: product.identifier, + version: product.version, + arch: product.arch + } + end + let(:serialized_service_json) do + V3::ServiceSerializer.new( + product.service, + base_url: URI::HTTP.build({ scheme: response.request.scheme, host: response.request.host }).to_s + ).to_json + end + + let(:scc_systems_activations_url) { 'https://scc.suse.com/connect/systems/activations' } + + before do + stub_request(:delete, scc_systems_products_url) + .to_return( + status: 200, + body: "{\"error\": \"Could not de-activate product \'#{product.friendly_name}\'\"}", + headers: {} + ) + stub_request(:get, scc_systems_activations_url).to_return(status: 200, body: body_active, headers: {}) + delete url, params: payload, headers: headers + end + + context 'when only one product was active' do + let(:body_active) do + [{ + id: 1, + regcode: '631dc51f', + name: 'Subscription 1', + type: 'FULL', + status: 'ACTIVE', + starts_at: 'null', + expires_at: DateTime.parse((Time.zone.today + 1).to_s), + system_limit: 6, + systems_count: 1, + service: { + product: { + id: system_hybrid.activations.first.product.id, + product_class: system_hybrid.activations.first.product.product_class + } + } + }].to_json + end + + it 'makes the hybrid system payg' do + updated_system = System.find_by(login: system_hybrid.login) + expect(updated_system.payg?).to eq(true) + end + end + + context 'when more activations are left' do + let(:body_active) do + [ + { + id: 1, + regcode: '631dc51f', + name: 'Subscription 1', + type: 'FULL', + status: 'ACTIVE', + starts_at: 'null', + expires_at: DateTime.parse((Time.zone.today + 1).to_s), + system_limit: 6, + systems_count: 1, + service: { + product: { + id: system_hybrid.activations.first.product.id, + product_class: system_hybrid.activations.first.product.product_class + } + } + }, { + id: 2, + regcode: '631dc51f', + name: 'Subscription 1', + type: 'FULL', + status: 'ACTIVE', + starts_at: 'null', + expires_at: DateTime.parse((Time.zone.today + 1).to_s), + system_limit: 6, + systems_count: 1, + service: { + product: { + id: '30', + product_class: '23' + } + } + } + ].to_json + end + + it 'keeps the system as hybrid' do + updated_system = System.find_by(login: system_hybrid.login) + expect(updated_system.hybrid?).to eq(true) + end + end + end + context 'when SCC API returns an error' do let(:product) do FactoryBot.create(:product, :product_sles, :extension, :with_mirrored_repositories, :with_mirrored_extensions, :activated, system: system_hybrid) From 0e1095ee9635a1fca8e0ada5ebd8f60932cc4635 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Berm=C3=BAdez=20Vel=C3=A1zquez?= Date: Wed, 28 Aug 2024 09:06:35 +0100 Subject: [PATCH 013/103] Update comment wording Co-authored-by: Thomas Schmidt --- engines/scc_proxy/lib/scc_proxy/engine.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/engines/scc_proxy/lib/scc_proxy/engine.rb b/engines/scc_proxy/lib/scc_proxy/engine.rb index 7a52c8ab0..c059f2440 100644 --- a/engines/scc_proxy/lib/scc_proxy/engine.rb +++ b/engines/scc_proxy/lib/scc_proxy/engine.rb @@ -405,8 +405,8 @@ def scc_deactivate_product # it is OK to remove it from SCC response = SccProxy.deactivate_product_scc(auth, @product, @system.system_token) handle_response(response) - # if the system does not have more non free products activated on SCC - # system should turn to hybrid + # if the system does not have more products activated on SCC + # switch it back to payg @system.payg! if no_more_activations scc_systems_activations elsif result[:message].downcase.include?('unexpected error') raise ActionController::TranslatedError.new(result[:message]) From 8284043115650973e54d4e67b6f1a07f718e4d3a Mon Sep 17 00:00:00 2001 From: Jesus Bermudez Velazquez Date: Wed, 28 Aug 2024 10:07:57 +0100 Subject: [PATCH 014/103] Rename method to a positive name --- engines/scc_proxy/lib/scc_proxy/engine.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/engines/scc_proxy/lib/scc_proxy/engine.rb b/engines/scc_proxy/lib/scc_proxy/engine.rb index c059f2440..8e1847454 100644 --- a/engines/scc_proxy/lib/scc_proxy/engine.rb +++ b/engines/scc_proxy/lib/scc_proxy/engine.rb @@ -407,7 +407,7 @@ def scc_deactivate_product handle_response(response) # if the system does not have more products activated on SCC # switch it back to payg - @system.payg! if no_more_activations scc_systems_activations + @system.payg! unless system_has_paid_extension? scc_systems_activations elsif result[:message].downcase.include?('unexpected error') raise ActionController::TranslatedError.new(result[:message]) end @@ -433,13 +433,13 @@ def handle_response(response) end end - def no_more_activations(scc_systems_activations) + def system_has_paid_extension?(scc_systems_activations) active_products_classes = scc_systems_activations.map do |act| if act['status'].casecmp('active').zero? && act['service']['product']['product_class'] != @product.product_class act['service']['product']['product_class'] end end.flatten.compact - active_products_classes.empty? + active_products_classes.present? end end From c78ef783772cac7d45ce448f8f60457280801be1 Mon Sep 17 00:00:00 2001 From: Natnael Getahun Date: Wed, 28 Aug 2024 11:16:07 +0200 Subject: [PATCH 015/103] Update helm-chart release process --- PACKAGE.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PACKAGE.md b/PACKAGE.md index c58ab2695..51f0e7e01 100644 --- a/PACKAGE.md +++ b/PACKAGE.md @@ -109,4 +109,4 @@ RMT helm chart is defined [here](https://github.com/SUSE/helm-charts.git) and pu Edit `rmt-helm/Chart.yaml` to update the chart version (`version`) and rmt-version (`appVersion`). The `BuildTag` version needs to be updated. Look at this example [pull-request](https://github.com/SUSE/helm-charts/pull/5) bumping the version. -Please reach out to the BCI team if you have build related questions. +Reach out to the BCI team (Dirk Mueller or `#proj-bci` slack channel) to trigger the release of the helm-chart. From cfccb5cc204bb115a531db74ba9113748c68170d Mon Sep 17 00:00:00 2001 From: Jesus Bermudez Velazquez Date: Wed, 28 Aug 2024 11:25:32 +0100 Subject: [PATCH 016/103] Remove method and use inline condition Remove one line method and move the check to the method call --- engines/scc_proxy/lib/scc_proxy/engine.rb | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/engines/scc_proxy/lib/scc_proxy/engine.rb b/engines/scc_proxy/lib/scc_proxy/engine.rb index 8e1847454..a6713906a 100644 --- a/engines/scc_proxy/lib/scc_proxy/engine.rb +++ b/engines/scc_proxy/lib/scc_proxy/engine.rb @@ -407,7 +407,9 @@ def scc_deactivate_product handle_response(response) # if the system does not have more products activated on SCC # switch it back to payg - @system.payg! unless system_has_paid_extension? scc_systems_activations + # drop the just de-activated activation from the list to avoid another call to SCC + # and check if there is any product + @system.payg! if scc_systems_activations.reject! { |act| act['service']['product']['id'] == @product.id }.blank? elsif result[:message].downcase.include?('unexpected error') raise ActionController::TranslatedError.new(result[:message]) end @@ -432,15 +434,6 @@ def handle_response(response) raise ActionController::TranslatedError.new(error['error']) end end - - def system_has_paid_extension?(scc_systems_activations) - active_products_classes = scc_systems_activations.map do |act| - if act['status'].casecmp('active').zero? && act['service']['product']['product_class'] != @product.product_class - act['service']['product']['product_class'] - end - end.flatten.compact - active_products_classes.present? - end end Api::Connect::V3::Systems::SystemsController.class_eval do From 4b1421d7bbf09f9d78d5b002fadd93d6ccd9a40b Mon Sep 17 00:00:00 2001 From: Jesus Bermudez Velazquez Date: Wed, 28 Aug 2024 11:28:44 +0100 Subject: [PATCH 017/103] Fix linter --- engines/scc_proxy/lib/scc_proxy/engine.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/engines/scc_proxy/lib/scc_proxy/engine.rb b/engines/scc_proxy/lib/scc_proxy/engine.rb index a6713906a..13789cccd 100644 --- a/engines/scc_proxy/lib/scc_proxy/engine.rb +++ b/engines/scc_proxy/lib/scc_proxy/engine.rb @@ -389,6 +389,7 @@ def deregister_hybrid(auth) protected + # rubocop:disable Metrics/PerceivedComplexity def scc_deactivate_product auth = request.headers['HTTP_AUTHORIZATION'] if @system.byos? && @product[:product_type] != 'base' @@ -416,6 +417,7 @@ def scc_deactivate_product end logger.info "Product '#{@product.friendly_name}' successfully deactivated from SCC" end + # rubocop:enable Metrics/PerceivedComplexity def find_hybrid_product_on_scc(headers) response = SccProxy.get_scc_activations(headers, @system.system_token, @system.proxy_byos_mode) From f16feb2cf883ada29207cce9c6690e942a3ccf31 Mon Sep 17 00:00:00 2001 From: Jesus Bermudez Velazquez Date: Wed, 28 Aug 2024 11:54:02 +0100 Subject: [PATCH 018/103] Refactor deactivate product of a hybrid system Move the exception handling to SccProxy's deactive_product_scc method Rename method for accuracy --- engines/scc_proxy/lib/scc_proxy/engine.rb | 36 ++++++++++------------- 1 file changed, 16 insertions(+), 20 deletions(-) diff --git a/engines/scc_proxy/lib/scc_proxy/engine.rb b/engines/scc_proxy/lib/scc_proxy/engine.rb index 13789cccd..5f30ed65a 100644 --- a/engines/scc_proxy/lib/scc_proxy/engine.rb +++ b/engines/scc_proxy/lib/scc_proxy/engine.rb @@ -136,7 +136,7 @@ def scc_activate_product(product, auth, params, mode) http.request(scc_request) end - def deactivate_product_scc(auth, product, params) + def deactivate_product_scc(auth, product, params, logger) uri = URI.parse(DEREGISTER_PRODUCT_URL) http = Net::HTTP.new(uri.host, uri.port) http.use_ssl = true @@ -147,7 +147,14 @@ def deactivate_product_scc(auth, product, params) arch: product.arch, byos: true }.to_json - http.request(scc_request) + response = http.request(scc_request) + unless response.code_type == Net::HTTPOK + error = JSON.parse(response.body) + error['error'] = SccProxy.parse_error(error['error'], params[:token], params[:email]) if error['error'].include? 'json' + logger.info "Could not de-activate product '#{product.friendly_name}', error: #{error['error']} #{response.code}" + raise ActionController::TranslatedError.new(error['error']) + end + response end def deregister_system_scc(auth, system_token) @@ -393,24 +400,21 @@ def deregister_hybrid(auth) def scc_deactivate_product auth = request.headers['HTTP_AUTHORIZATION'] if @system.byos? && @product[:product_type] != 'base' - response = SccProxy.deactivate_product_scc(auth, @product, @system.system_token) - handle_response(response) + SccProxy.deactivate_product_scc(auth, @product, @system.system_token, logger) elsif @system.hybrid? && @product.extension? # check if product is on SCC and # if it is -> de-activate it - scc_systems_activations = find_hybrid_product_on_scc(request.headers) - result = SccProxy.product_class_access(scc_systems_activations, @product.product_class) + result = find_hybrid_activations_on_scc(request.headers) if result[:is_active] || (!result[:is_active] && result[:message].downcase.include?('expired')) # if product is active on SCC or # product subscription expired # it is OK to remove it from SCC - response = SccProxy.deactivate_product_scc(auth, @product, @system.system_token) - handle_response(response) + SccProxy.deactivate_product_scc(auth, @product, @system.system_token, logger) # if the system does not have more products activated on SCC # 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 - @system.payg! if scc_systems_activations.reject! { |act| act['service']['product']['id'] == @product.id }.blank? + @system.payg! if @scc_systems_activations.reject! { |act| act['service']['product']['id'] == @product.id }.blank? elsif result[:message].downcase.include?('unexpected error') raise ActionController::TranslatedError.new(result[:message]) end @@ -419,22 +423,14 @@ def scc_deactivate_product end # rubocop:enable Metrics/PerceivedComplexity - def find_hybrid_product_on_scc(headers) + def find_hybrid_activations_on_scc(headers) response = SccProxy.get_scc_activations(headers, @system.system_token, @system.proxy_byos_mode) unless response.code_type == Net::HTTPOK logger.info "Could not get the system (#{@system.login}) activations, error: #{response.message} #{response.code}" raise ActionController::TranslatedError.new(response.body) end - JSON.parse(response.body) - end - - def handle_response(response) - unless response.code_type == Net::HTTPOK - error = JSON.parse(response.body) - error['error'] = SccProxy.parse_error(error['error'], params[:token], params[:email]) if error['error'].include? 'json' - logger.info "Could not de-activate product '#{@product.friendly_name}', error: #{error['error']} #{response.code}" - raise ActionController::TranslatedError.new(error['error']) - end + @scc_systems_activations = JSON.parse(response.body) + SccProxy.product_class_access(@scc_systems_activations, @product.product_class) end end From d28f64be2290476a305c8d5b71f2980d7dbce5d5 Mon Sep 17 00:00:00 2001 From: Jesus Bermudez Velazquez Date: Wed, 28 Aug 2024 11:57:30 +0100 Subject: [PATCH 019/103] No need to alter the same array --- engines/scc_proxy/lib/scc_proxy/engine.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/engines/scc_proxy/lib/scc_proxy/engine.rb b/engines/scc_proxy/lib/scc_proxy/engine.rb index 5f30ed65a..2c7bb1b54 100644 --- a/engines/scc_proxy/lib/scc_proxy/engine.rb +++ b/engines/scc_proxy/lib/scc_proxy/engine.rb @@ -414,7 +414,7 @@ def scc_deactivate_product # 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 - @system.payg! if @scc_systems_activations.reject! { |act| act['service']['product']['id'] == @product.id }.blank? + @system.payg! if @scc_systems_activations.reject { |act| act['service']['product']['id'] == @product.id }.blank? elsif result[:message].downcase.include?('unexpected error') raise ActionController::TranslatedError.new(result[:message]) end From e4dc3f8a71952860cb889c5ce9bf97fab94df810 Mon Sep 17 00:00:00 2001 From: Jesus Bermudez Velazquez Date: Wed, 28 Aug 2024 13:28:59 +0100 Subject: [PATCH 020/103] Deregister system from SCC when switching to PAYG If a hybrid system deactivates a product from SCC and has no more products on SCC, the system becomes PAYG and, as such, it should be deregister from SCC --- engines/scc_proxy/lib/scc_proxy/engine.rb | 36 ++- .../v4/systems/products_controller_spec.rb | 259 +++--------------- 2 files changed, 60 insertions(+), 235 deletions(-) diff --git a/engines/scc_proxy/lib/scc_proxy/engine.rb b/engines/scc_proxy/lib/scc_proxy/engine.rb index 2c7bb1b54..cc84ad2c5 100644 --- a/engines/scc_proxy/lib/scc_proxy/engine.rb +++ b/engines/scc_proxy/lib/scc_proxy/engine.rb @@ -396,7 +396,6 @@ def deregister_hybrid(auth) protected - # rubocop:disable Metrics/PerceivedComplexity def scc_deactivate_product auth = request.headers['HTTP_AUTHORIZATION'] if @system.byos? && @product[:product_type] != 'base' @@ -404,24 +403,16 @@ def scc_deactivate_product elsif @system.hybrid? && @product.extension? # check if product is on SCC and # if it is -> de-activate it - result = find_hybrid_activations_on_scc(request.headers) - if result[:is_active] || (!result[:is_active] && result[:message].downcase.include?('expired')) - # if product is active on SCC or - # product subscription expired + scc_systems_activations = find_hybrid_activations_on_scc(request.headers) + if scc_systems_activations.map { |act| act['service']['product']['id'] == @product.id }.present? + # if product is found on SCC, regardless of the state # it is OK to remove it from SCC SccProxy.deactivate_product_scc(auth, @product, @system.system_token, logger) - # if the system does not have more products activated on SCC - # 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 - @system.payg! if @scc_systems_activations.reject { |act| act['service']['product']['id'] == @product.id }.blank? - elsif result[:message].downcase.include?('unexpected error') - raise ActionController::TranslatedError.new(result[:message]) + handle_system_mode(auth) if scc_systems_activations.reject { |act| act['service']['product']['id'] == @product.id }.blank? end end logger.info "Product '#{@product.friendly_name}' successfully deactivated from SCC" end - # rubocop:enable Metrics/PerceivedComplexity def find_hybrid_activations_on_scc(headers) response = SccProxy.get_scc_activations(headers, @system.system_token, @system.proxy_byos_mode) @@ -429,8 +420,23 @@ def find_hybrid_activations_on_scc(headers) logger.info "Could not get the system (#{@system.login}) activations, error: #{response.message} #{response.code}" raise ActionController::TranslatedError.new(response.body) end - @scc_systems_activations = JSON.parse(response.body) - SccProxy.product_class_access(@scc_systems_activations, @product.product_class) + JSON.parse(response.body) + end + + def handle_system_mode(auth) + # if the system does not have more products activated on SCC + # 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' + @system.payg! end end diff --git a/engines/scc_proxy/spec/requests/api/connect/v4/systems/products_controller_spec.rb b/engines/scc_proxy/spec/requests/api/connect/v4/systems/products_controller_spec.rb index 586921c26..8c1801c33 100644 --- a/engines/scc_proxy/spec/requests/api/connect/v4/systems/products_controller_spec.rb +++ b/engines/scc_proxy/spec/requests/api/connect/v4/systems/products_controller_spec.rb @@ -187,7 +187,7 @@ end end - context 'when SCC API suceeds for HYBRiD system' do + context 'when SCC API suceeds for HYBRID system' do let(:product) do FactoryBot.create(:product, :product_sles, :extension, :with_mirrored_repositories, :with_mirrored_extensions, :activated, system: system_hybrid) end @@ -204,8 +204,8 @@ base_url: URI::HTTP.build({ scheme: response.request.scheme, host: response.request.host }).to_s ).to_json end - let(:scc_systems_activations_url) { 'https://scc.suse.com/connect/systems/activations' } + let(:scc_systems_url) { 'https://scc.suse.com/connect/systems' } before do stub_request(:delete, scc_systems_products_url) @@ -215,7 +215,6 @@ headers: {} ) stub_request(:get, scc_systems_activations_url).to_return(status: 200, body: body_active, headers: {}) - delete url, params: payload, headers: headers end context 'when only one product was active' do @@ -239,9 +238,38 @@ }].to_json end - it 'makes the hybrid system payg' do - updated_system = System.find_by(login: system_hybrid.login) - expect(updated_system.payg?).to eq(true) + context 'when deactivating the system succeeds' do + before do + stub_request(:delete, scc_systems_url).to_return(status: 204, body: '', headers: {}) + delete url, params: payload, headers: headers + end + + it 'makes the hybrid system payg' do + updated_system = System.find_by(login: system_hybrid.login) + expect(updated_system.payg?).to eq(true) + end + end + + context 'when deactivating the system fails' do + before do + allow(Rails.logger).to receive(:info) + stub_request(:delete, scc_systems_url).to_return( + status: 422, + body: '{"error": "Oh oh, something went wrong"}', + headers: {} + ) + delete url, params: payload, headers: headers + end + + it 'makes the hybrid system payg' do + expect(Rails.logger).to( + have_received(:info).with( + "Could not de-activate system #{system_hybrid.login}, error: Oh oh, something went wrong 422" + ).once + ) + data = JSON.parse(response.body) + expect(data['error']).to eq('Oh oh, something went wrong') + end end end @@ -284,226 +312,17 @@ ].to_json end + before do + stub_request(:delete, scc_systems_url).to_return(status: 204, body: '', headers: {}) + delete url, params: payload, headers: headers + end + it 'keeps the system as hybrid' do updated_system = System.find_by(login: system_hybrid.login) expect(updated_system.hybrid?).to eq(true) end end end - - context 'when SCC API returns an error' do - let(:product) do - FactoryBot.create(:product, :product_sles, :extension, :with_mirrored_repositories, :with_mirrored_extensions, :activated, system: system_hybrid) - end - let(:payload) do - { - identifier: product.identifier, - version: product.version, - arch: product.arch - } - end - let(:serialized_service_json) do - V3::ServiceSerializer.new( - product.service, - base_url: URI::HTTP.build({ scheme: response.request.scheme, host: response.request.host }).to_s - ).to_json - end - let(:body_active) do - { - id: 1, - regcode: '631dc51f', - name: 'Subscription 1', - type: 'FULL', - status: 'ACTIVE', - starts_at: 'null', - expires_at: DateTime.parse((Time.zone.today + 1).to_s), - system_limit: 6, - systems_count: 1, - service: { - product: { - id: system_hybrid.activations.first.product.id, - product_class: system_hybrid.activations.first.product.product_class - } - } - } - end - let(:scc_systems_activations_url) { 'https://scc.suse.com/connect/systems/activations' } - - before do - stub_request(:delete, scc_systems_products_url) - .to_return( - status: 422, - body: "{\"error\": \"Could not de-activate product \'#{product.friendly_name}\'\"}", - headers: {} - ) - stub_request(:get, scc_systems_activations_url).to_return(status: 200, body: [body_active].to_json, headers: {}) - delete url, params: payload, headers: headers - end - - it 'reports an error' do - data = JSON.parse(response.body) - expect(data['error']).to eq('Could not de-activate product \'SUSE Linux Enterprise Server 15 SP3 x86_64\'') - end - end - - context 'when product is expired' do - let(:product) do - FactoryBot.create(:product, :product_sles, :extension, :with_mirrored_repositories, :with_mirrored_extensions, :activated, system: system_hybrid) - end - let(:payload) do - { - identifier: product.identifier, - version: product.version, - arch: product.arch - } - end - let(:serialized_service_json) do - V3::ServiceSerializer.new( - product.service, - base_url: URI::HTTP.build({ scheme: response.request.scheme, host: response.request.host }).to_s - ).to_json - end - let(:body_expired) do - { - id: 1, - regcode: '631dc51f', - name: 'Subscription 1', - type: 'FULL', - status: 'EXPIRED', - starts_at: 'null', - expires_at: DateTime.parse((Time.zone.today - 1).to_s), - system_limit: 6, - systems_count: 1, - service: { - product: { - id: system_hybrid.activations.first.product.id, - product_class: system_hybrid.activations.first.product.product_class - } - } - } - end - let(:scc_systems_activations_url) { 'https://scc.suse.com/connect/systems/activations' } - - before do - stub_request(:delete, scc_systems_products_url) - .to_return( - status: 422, - body: "{\"error\": \"Could not de-activate product \'#{product.friendly_name}\'\"}", - headers: {} - ) - stub_request(:get, scc_systems_activations_url).to_return(status: 200, body: [body_expired].to_json, headers: {}) - delete url, params: payload, headers: headers - end - - it 'reports an error' do - data = JSON.parse(response.body) - expect(data['error']).to eq('Could not de-activate product \'SUSE Linux Enterprise Server 15 SP3 x86_64\'') - end - end - - context 'when product is not active' do - let(:product) do - FactoryBot.create(:product, :product_sles, :extension, :with_mirrored_repositories, :with_mirrored_extensions, :activated, system: system_hybrid) - end - let(:payload) do - { - identifier: product.identifier, - version: product.version, - arch: product.arch - } - end - let(:serialized_service_json) do - V3::ServiceSerializer.new( - product.service, - base_url: URI::HTTP.build({ scheme: response.request.scheme, host: response.request.host }).to_s - ).to_json - end - let(:body_not_activated) do - { - id: 1, - regcode: '631dc51f', - name: 'Subscription 1', - type: 'FULL', - status: 'ACTIVE', - starts_at: 'null', - expires_at: DateTime.parse((Time.zone.today - 1).to_s), - system_limit: 6, - systems_count: 1, - service: { - product: { - id: 1, - product_class: product.product_class + 'FOO' - } - } - } - end - let(:scc_systems_activations_url) { 'https://scc.suse.com/connect/systems/activations' } - - before do - stub_request(:get, scc_systems_activations_url).to_return(status: 200, body: [body_not_activated].to_json, headers: {}) - delete url, params: payload, headers: headers - end - - it 'reports an error' do - data = JSON.parse(response.body) - expect(data['error']).to eq(nil) - end - end - - context 'when product has unknown status' do - let(:product) do - FactoryBot.create(:product, :product_sles, :extension, :with_mirrored_repositories, :with_mirrored_extensions, :activated, system: system_hybrid) - end - let(:payload) do - { - identifier: product.identifier, - version: product.version, - arch: product.arch - } - end - let(:serialized_service_json) do - V3::ServiceSerializer.new( - product.service, - base_url: URI::HTTP.build({ scheme: response.request.scheme, host: response.request.host }).to_s - ).to_json - end - let(:body_unknown_status) do - { - id: 1, - regcode: '631dc51f', - name: 'Subscription 1', - type: 'FULL', - status: 'FOO', - starts_at: 'null', - expires_at: DateTime.parse((Time.zone.today - 1).to_s), - system_limit: 6, - systems_count: 1, - service: { - product: { - id: system_hybrid.activations.first.product.id, - product_class: system_hybrid.activations.first.product.product_class + 'FOO' - } - } - } - end - let(:scc_systems_activations_url) { 'https://scc.suse.com/connect/systems/activations' } - - before do - stub_request(:delete, scc_systems_products_url) - .to_return( - status: 422, - body: "{\"error\": \"Could not de-activate product \'#{product.friendly_name}\'\"}", - headers: {} - ) - stub_request(:get, scc_systems_activations_url).to_return(status: 200, body: [body_unknown_status].to_json, headers: {}) - delete url, params: payload, headers: headers - end - - it 'reports an error' do - data = JSON.parse(response.body) - expect(data['error']).to eq('Unexpected error when checking product subscription.') - end - end end context 'an activated base module with right credentials' do From 1c2929c4dbcd84f9f2efa7ea7108d4d5cd89bcb8 Mon Sep 17 00:00:00 2001 From: Jesus Bermudez Velazquez Date: Wed, 28 Aug 2024 14:55:50 +0100 Subject: [PATCH 021/103] rename method --- engines/scc_proxy/lib/scc_proxy/engine.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/engines/scc_proxy/lib/scc_proxy/engine.rb b/engines/scc_proxy/lib/scc_proxy/engine.rb index cc84ad2c5..f551e4500 100644 --- a/engines/scc_proxy/lib/scc_proxy/engine.rb +++ b/engines/scc_proxy/lib/scc_proxy/engine.rb @@ -408,7 +408,7 @@ def scc_deactivate_product # if product is found on SCC, regardless of the state # it is OK to remove it from SCC SccProxy.deactivate_product_scc(auth, @product, @system.system_token, logger) - handle_system_mode(auth) if scc_systems_activations.reject { |act| act['service']['product']['id'] == @product.id }.blank? + make_system_payg(auth) if scc_systems_activations.reject { |act| act['service']['product']['id'] == @product.id }.blank? end end logger.info "Product '#{@product.friendly_name}' successfully deactivated from SCC" @@ -423,7 +423,7 @@ def find_hybrid_activations_on_scc(headers) JSON.parse(response.body) end - def handle_system_mode(auth) + def make_system_payg(auth) # if the system does not have more products activated on SCC # switch it back to payg # drop the just de-activated activation from the list to avoid another call to SCC From 8383b19d4dfe0aa3774321bd210c74f7d8bf0a99 Mon Sep 17 00:00:00 2001 From: Jesus Bermudez Velazquez Date: Wed, 28 Aug 2024 18:45:40 +0100 Subject: [PATCH 022/103] Use the right header for SCC activations --- engines/registry/app/models/access_scope.rb | 2 +- engines/registry/spec/app/models/access_scope_spec.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/engines/registry/app/models/access_scope.rb b/engines/registry/app/models/access_scope.rb index fa565cdbb..7e5c7b7ad 100644 --- a/engines/registry/app/models/access_scope.rb +++ b/engines/registry/app/models/access_scope.rb @@ -90,7 +90,7 @@ def allowed_paths(system = nil) allowed_non_free_product_classes = allowed_product_classes.map { |s| s unless Product.find_by(product_class: s).free? } unless allowed_non_free_product_classes.empty? auth_header = { - Authorization: ActionController::HttpAuthentication::Basic.encode_credentials(system.login, system.password) + 'HTTP_AUTHORIZATION' => ActionController::HttpAuthentication::Basic.encode_credentials(system.login, system.password) } allowed_non_free_product_classes.each do |non_free_prod_class| activation_state = SccProxy.scc_check_subscription_expiration( diff --git a/engines/registry/spec/app/models/access_scope_spec.rb b/engines/registry/spec/app/models/access_scope_spec.rb index 0840efb53..9b0f6dff0 100644 --- a/engines/registry/spec/app/models/access_scope_spec.rb +++ b/engines/registry/spec/app/models/access_scope_spec.rb @@ -208,7 +208,7 @@ } end let(:header_expected) do - { Authorization: ActionController::HttpAuthentication::Basic.encode_credentials(system.login, system.password) } + { 'HTTP_AUTHORIZATION' => ActionController::HttpAuthentication::Basic.encode_credentials(system.login, system.password) } end before do From a770596e5379e32ea5ea4f4a2cdf1e0198b26427 Mon Sep 17 00:00:00 2001 From: Jesus Bermudez Velazquez Date: Thu, 29 Aug 2024 13:48:02 +0100 Subject: [PATCH 023/103] Propagate the upgrate call to SCC Proxy the zypper migration upgrade call to SCC when the system is BYOS --- .../v3/systems/products_controller_spec.rb | 195 ++++++++++++++---- engines/scc_proxy/lib/scc_proxy/engine.rb | 67 ++++-- 2 files changed, 208 insertions(+), 54 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 a2679292c..53b172b71 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 @@ -551,65 +551,176 @@ describe '#upgrade' do subject { response } - let(:system) { FactoryBot.create(:system) } + let(:instance_data) { 'dummy_instance_data' } let(:request) { put url, headers: headers, params: payload } - let!(:old_product) { FactoryBot.create(:product, :with_mirrored_repositories, :activated, system: system) } - let(:payload) do - { - identifier: new_product.identifier, - version: new_product.version, - arch: new_product.arch - } - end - before { request } + context 'when system is byos' do + let(:system) { FactoryBot.create(:system, :byos, :with_system_information, instance_data: instance_data) } + let!(:old_product) { FactoryBot.create(:product, :with_mirrored_repositories, :activated, system: system) } + let(:payload) do + { + identifier: new_product.identifier, + version: new_product.version, + arch: new_product.arch + } + end + let(:scc_systems_products_url) { 'https://scc.suse.com/connect/systems/products' } + let(:scc_headers) do + { + 'Accept' => 'application/json,application/vnd.scc.suse.com.v4+json', + 'Accept-Encoding' => 'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', + 'Authorization' => headers['HTTP_AUTHORIZATION'], + 'Content-Type' => 'application/json', + 'User-Agent' => 'Ruby' + } + end + + context 'when SCC upgrade success' do + before do + # pp headers + stub_request(:put, scc_systems_products_url) + .with({ headers: scc_headers, body: payload.merge({ byos_mode: 'byos' }) }) + .and_return(status: 200, body: '', headers: {}) + request + end + + context "when migration target base product doesn't have an activated successor/predecessor" do + let(:new_product) { FactoryBot.create(:product, :with_mirrored_repositories) } + + it 'HTTP response code is 422' do + expect(response).to have_http_status(422) + end + + it 'renders an error' do + data = JSON.parse(response.body) + expect(data['error']).to eq('Migration target not allowed on this instance type') + end + end + + context 'when migration target base product has the same identifier' do + let(:new_product) do + FactoryBot.create( + :product, :with_mirrored_repositories, identifier: old_product.identifier, + version: '999', predecessors: [ old_product ] + ) + end - context "when migration target base product doesn't have an activated successor/predecessor" do - let(:new_product) { FactoryBot.create(:product, :with_mirrored_repositories) } + it 'HTTP response code is 201' do + expect(response).to have_http_status(201) + end - it 'HTTP response code is 422' do - expect(response).to have_http_status(422) + it "doesn't render an error" do + data = JSON.parse(response.body) + expect(data).not_to have_key('error') + end + end end - it 'renders an error' do - data = JSON.parse(response.body) - expect(data['error']).to eq('Migration target not allowed on this instance type') + context 'when SCC upgrade fails' do + before do + stub_request(:put, scc_systems_products_url) + .with({ headers: scc_headers, body: payload.merge({ byos_mode: 'byos' }) }) + .and_return( + status: 401, + body: { error: 'error_message' }.to_json, + headers: {} + ) + request + end + + context "when migration target base product doesn't have an activated successor/predecessor" do + let(:new_product) { FactoryBot.create(:product, :with_mirrored_repositories) } + + it 'HTTP response code is 422' do + expect(response).to have_http_status(422) + end + + it 'renders an error' do + data = JSON.parse(response.body) + expect(data['error']).to eq('Migration target not allowed on this instance type') + end + end + + context 'when migration target base product has the same identifier' do + let(:new_product) do + FactoryBot.create( + :product, :with_mirrored_repositories, identifier: old_product.identifier, + version: '999', predecessors: [ old_product ] + ) + end + + it 'HTTP response code is 422' do + expect(response).to have_http_status(422) + end + + it 'renders an error' do + data = JSON.parse(response.body) + expect(data).to have_key('error') + end + end end end - context 'when migration target base product has a different identifier' do - let(:new_product) do - FactoryBot.create( - :product, :with_mirrored_repositories, - identifier: old_product.identifier + '-foo', predecessors: [ old_product ] - ) + context 'when system is payg' do + let(:system) { FactoryBot.create(:system, :payg, :with_system_information, instance_data: instance_data) } + let!(:old_product) { FactoryBot.create(:product, :with_mirrored_repositories, :activated, system: system) } + let(:payload) do + { + identifier: new_product.identifier, + version: new_product.version, + arch: new_product.arch + } end - it 'HTTP response code is 422' do - expect(response).to have_http_status(422) - end + before { request } - it 'renders an error' do - data = JSON.parse(response.body) - expect(data['error']).to eq('Migration target not allowed on this instance type') - end - end + context "when migration target base product doesn't have an activated successor/predecessor" do + let(:new_product) { FactoryBot.create(:product, :with_mirrored_repositories) } - context 'when migration target base product has the same identifier' do - let(:new_product) do - FactoryBot.create( - :product, :with_mirrored_repositories, identifier: old_product.identifier, - version: '999', predecessors: [ old_product ] - ) + it 'HTTP response code is 422' do + expect(response).to have_http_status(422) + end + + it 'renders an error' do + data = JSON.parse(response.body) + expect(data['error']).to eq('Migration target not allowed on this instance type') + end end - it 'HTTP response code is 201' do - expect(response).to have_http_status(201) + context 'when migration target base product has a different identifier' do + let(:new_product) do + FactoryBot.create( + :product, :with_mirrored_repositories, + identifier: old_product.identifier + '-foo', predecessors: [ old_product ] + ) + end + + it 'HTTP response code is 422' do + expect(response).to have_http_status(422) + end + + it 'renders an error' do + data = JSON.parse(response.body) + expect(data['error']).to eq('Migration target not allowed on this instance type') + end end - it "doesn't render an error" do - data = JSON.parse(response.body) - expect(data).not_to have_key('error') + context 'when migration target base product has the same identifier' do + let(:new_product) do + FactoryBot.create( + :product, :with_mirrored_repositories, identifier: old_product.identifier, + version: '999', predecessors: [ old_product ] + ) + end + + it 'HTTP response code is 201' do + expect(response).to have_http_status(201) + end + + it "doesn't render an error" do + data = JSON.parse(response.body) + expect(data).not_to have_key('error') + end end end end diff --git a/engines/scc_proxy/lib/scc_proxy/engine.rb b/engines/scc_proxy/lib/scc_proxy/engine.rb index f551e4500..9946111a2 100644 --- a/engines/scc_proxy/lib/scc_proxy/engine.rb +++ b/engines/scc_proxy/lib/scc_proxy/engine.rb @@ -2,7 +2,7 @@ require 'net/http' ANNOUNCE_URL = 'https://scc.suse.com/connect/subscriptions/systems'.freeze -ACTIVATE_PRODUCT_URL = 'https://scc.suse.com/connect/systems/products'.freeze +SYSTEM_PRODUCTS_URL = 'https://scc.suse.com/connect/systems/products'.freeze SYSTEMS_ACTIVATIONS_URL = 'https://scc.suse.com/connect/systems/activations'.freeze DEREGISTER_SYSTEM_URL = 'https://scc.suse.com/connect/systems'.freeze DEREGISTER_PRODUCT_URL = 'https://scc.suse.com/connect/systems/products'.freeze @@ -117,6 +117,17 @@ def prepare_scc_request(uri_path, product, auth, params, mode) scc_request end + def prepare_scc_update_request(uri_path, product, auth, mode) + scc_request = Net::HTTP::Put.new(uri_path, headers(auth, nil)) + scc_request.body = { + identifier: product.identifier, + version: product.version, + arch: product.arch, + byos_mode: mode + }.to_json + scc_request + end + def announce_system_scc(auth, params) uri = URI.parse(ANNOUNCE_URL) http = Net::HTTP.new(uri.host, uri.port) @@ -129,7 +140,7 @@ def announce_system_scc(auth, params) end def scc_activate_product(product, auth, params, mode) - uri = URI.parse(ACTIVATE_PRODUCT_URL) + 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) @@ -254,6 +265,20 @@ def scc_check_subscription_expiration(headers, login, system_token, logger, mode SccProxy.activations_fail_state(scc_systems_activations, headers, product) end + + def scc_upgrade(auth, product, system_login, mode, logger) + uri = URI.parse(SYSTEM_PRODUCTS_URL) + http = Net::HTTP.new(uri.host, uri.port) + http.use_ssl = true + scc_request = prepare_scc_update_request(uri.path, product, auth, mode) + response = http.request(scc_request) + unless response.code_type == Net::HTTPOK + logger.info "Could not upgrade the system (#{system_login}), error: #{response.message} #{response.code}" + response.message = SccProxy.parse_error(response.message) if response.message.include? 'json' + raise ActionController::TranslatedError.new(response.body) + end + response + end end # rubocop:disable Metrics/ClassLength @@ -316,12 +341,12 @@ def has_no_regcode?(auth_header) Api::Connect::V3::Systems::ProductsController.class_eval do before_action :scc_activate_product, only: %i[activate] + before_action :scc_upgrade, only: %i[upgrade], if: -> { @system.byos? } protected # rubocop:disable Metrics/PerceivedComplexity # rubocop:disable Metrics/CyclomaticComplexity - # rubocop:disable Metrics/AbcSize def scc_activate_product logger.info "Activating product #{@product.product_string} to SCC" auth = nil @@ -337,12 +362,13 @@ def scc_activate_product if @system.payg? && base_prod.present? raise 'Incompatible extension product' unless @product.arch == base_prod.arch && @product.version == base_prod.version - params['hostname'] = @system.hostname - params['proxy_byos_mode'] = mode - params['scc_login'] = @system.login - params['scc_password'] = @system.password - params['hwinfo'] = JSON.parse(@system.system_information) - params['instance_data'] = @system.instance_data + update_params_system_info mode + # params['hostname'] = @system.hostname + # params['proxy_byos_mode'] = mode + # params['scc_login'] = @system.login + # params['scc_password'] = @system.password + # params['hwinfo'] = JSON.parse(@system.system_information) + # params['instance_data'] = @system.instance_data announce_auth = "Token token=#{params[:token]}" response = SccProxy.announce_system_scc(announce_auth, params) @@ -375,6 +401,8 @@ def scc_activate_product 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) @@ -386,10 +414,25 @@ def deregister_hybrid(auth) end logger.info 'System successfully deregistered from SCC' end + + def scc_upgrade + logger.info "Upgrading system to product #{@product.product_string} to SCC" + auth = nil + auth = request.headers['HTTP_AUTHORIZATION'] if request.headers.include?('HTTP_AUTHORIZATION') + mode = 'byos' if @system.byos? + SccProxy.scc_upgrade(auth, @product, @system.login, mode, logger) + logger.info "System #{@system.login} successfully upgraded with SCC" + end + + def update_params_system_info(mode) + params['hostname'] = @system.hostname + params['proxy_byos_mode'] = mode + params['scc_login'] = @system.login + params['scc_password'] = @system.password + params['hwinfo'] = JSON.parse(@system.system_information) + params['instance_data'] = @system.instance_data + end end - # rubocop:enable Metrics/AbcSize - # rubocop:enable Metrics/CyclomaticComplexity - # rubocop:enable Metrics/PerceivedComplexity Api::Connect::V4::Systems::ProductsController.class_eval do before_action :scc_deactivate_product, only: %i[destroy] From 7e139ad975aacefda82d0cc3a97ae250b875240c Mon Sep 17 00:00:00 2001 From: Jesus Bermudez Velazquez Date: Fri, 30 Aug 2024 10:59:51 +0100 Subject: [PATCH 024/103] Update the http status code to 201 created --- .../requests/api/connect/v3/systems/products_controller_spec.rb | 2 +- engines/scc_proxy/lib/scc_proxy/engine.rb | 2 +- 2 files changed, 2 insertions(+), 2 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 53b172b71..3a002f4d1 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 @@ -580,7 +580,7 @@ # pp headers stub_request(:put, scc_systems_products_url) .with({ headers: scc_headers, body: payload.merge({ byos_mode: 'byos' }) }) - .and_return(status: 200, body: '', headers: {}) + .and_return(status: 201, body: '', headers: {}) request end diff --git a/engines/scc_proxy/lib/scc_proxy/engine.rb b/engines/scc_proxy/lib/scc_proxy/engine.rb index 9946111a2..554aea8e8 100644 --- a/engines/scc_proxy/lib/scc_proxy/engine.rb +++ b/engines/scc_proxy/lib/scc_proxy/engine.rb @@ -272,7 +272,7 @@ def scc_upgrade(auth, product, system_login, mode, logger) http.use_ssl = true scc_request = prepare_scc_update_request(uri.path, product, auth, mode) response = http.request(scc_request) - unless response.code_type == Net::HTTPOK + unless response.code_type == Net::HTTPCreated logger.info "Could not upgrade the system (#{system_login}), error: #{response.message} #{response.code}" response.message = SccProxy.parse_error(response.message) if response.message.include? 'json' raise ActionController::TranslatedError.new(response.body) From f429365a215255e45a42e744e7cf8451acd4ac5c Mon Sep 17 00:00:00 2001 From: Jesus Bermudez Velazquez Date: Fri, 30 Aug 2024 11:01:05 +0100 Subject: [PATCH 025/103] rename method from prepare_scc_update_request to prepare_scc_upgrade_request --- engines/scc_proxy/lib/scc_proxy/engine.rb | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/engines/scc_proxy/lib/scc_proxy/engine.rb b/engines/scc_proxy/lib/scc_proxy/engine.rb index 554aea8e8..52532ad7b 100644 --- a/engines/scc_proxy/lib/scc_proxy/engine.rb +++ b/engines/scc_proxy/lib/scc_proxy/engine.rb @@ -117,7 +117,7 @@ def prepare_scc_request(uri_path, product, auth, params, mode) scc_request end - def prepare_scc_update_request(uri_path, product, auth, mode) + def prepare_scc_upgrade_request(uri_path, product, auth, mode) scc_request = Net::HTTP::Put.new(uri_path, headers(auth, nil)) scc_request.body = { identifier: product.identifier, @@ -270,7 +270,7 @@ def scc_upgrade(auth, product, system_login, mode, logger) uri = URI.parse(SYSTEM_PRODUCTS_URL) http = Net::HTTP.new(uri.host, uri.port) http.use_ssl = true - scc_request = prepare_scc_update_request(uri.path, product, auth, mode) + scc_request = prepare_scc_upgrade_request(uri.path, product, auth, mode) response = http.request(scc_request) unless response.code_type == Net::HTTPCreated logger.info "Could not upgrade the system (#{system_login}), error: #{response.message} #{response.code}" @@ -363,12 +363,6 @@ def scc_activate_product raise 'Incompatible extension product' unless @product.arch == base_prod.arch && @product.version == base_prod.version update_params_system_info mode - # params['hostname'] = @system.hostname - # params['proxy_byos_mode'] = mode - # params['scc_login'] = @system.login - # params['scc_password'] = @system.password - # params['hwinfo'] = JSON.parse(@system.system_information) - # params['instance_data'] = @system.instance_data announce_auth = "Token token=#{params[:token]}" response = SccProxy.announce_system_scc(announce_auth, params) From 3cb6578d2a8fc4b66c12fee3e65578391e291494 Mon Sep 17 00:00:00 2001 From: Jesus Bermudez Velazquez Date: Wed, 4 Sep 2024 09:37:15 +0100 Subject: [PATCH 026/103] Set old proxy BYOS value until we drop that column --- engines/scc_proxy/lib/scc_proxy/engine.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/engines/scc_proxy/lib/scc_proxy/engine.rb b/engines/scc_proxy/lib/scc_proxy/engine.rb index 52532ad7b..378b87a99 100644 --- a/engines/scc_proxy/lib/scc_proxy/engine.rb +++ b/engines/scc_proxy/lib/scc_proxy/engine.rb @@ -312,6 +312,7 @@ def announce_system password: response['password'], hostname: params[:hostname], proxy_byos_mode: :byos, + proxy_byos: true, system_information: system_information, instance_data: instance_data ) From c2f30af04fcd0db9809dc3fe3a8644ba3935e5e7 Mon Sep 17 00:00:00 2001 From: Jesus Bermudez Velazquez Date: Wed, 4 Sep 2024 11:06:07 +0100 Subject: [PATCH 027/103] Update the product serializer Before LTSS, the product serializer returned true for the free field of any product That is no longer true in the Public Cloud scenario, where LTSS is not free This Fixes that case --- app/serializers/v3/product_serializer.rb | 5 ++++- .../api/connect/v3/systems/products_controller_spec.rb | 1 + 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/app/serializers/v3/product_serializer.rb b/app/serializers/v3/product_serializer.rb index d0ccea8b4..90747f838 100644 --- a/app/serializers/v3/product_serializer.rb +++ b/app/serializers/v3/product_serializer.rb @@ -33,8 +33,11 @@ def eula_url end def free - # Everything is free on RMT :-) + # Everything is free on RMT :-) outside of the Public Cloud (i.e. LTSS) # Otherwise Yast and SUSEConnect will request a regcode when activating an extension + # FIXME + return object.free if defined?(SccProxy::Engine) && object.extension? + true end 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 3a002f4d1..6d6e15d97 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 @@ -540,6 +540,7 @@ context 'when no regcode is provided' do it 'activates the product' do data = JSON.parse(response.body) + expect(data['product']['free']).to eq(false) expect(data['id']).to eq(product.id) end end From 98a473c7c76f85496acfb7c7e9a36a947f86afea Mon Sep 17 00:00:00 2001 From: Jesus Bermudez Velazquez Date: Wed, 4 Sep 2024 12:31:36 +0100 Subject: [PATCH 028/103] Check only extensions for SCC activations When checking the registry access if system is hybrid, the check with SCC for the state of its activations should be done only with non free extensions, as base product is not free This Fixes that scenario --- engines/registry/app/models/access_scope.rb | 2 +- engines/registry/spec/app/models/access_scope_spec.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/engines/registry/app/models/access_scope.rb b/engines/registry/app/models/access_scope.rb index 7e5c7b7ad..511dbbca5 100644 --- a/engines/registry/app/models/access_scope.rb +++ b/engines/registry/app/models/access_scope.rb @@ -87,7 +87,7 @@ def allowed_paths(system = nil) allowed_product_classes = (active_product_classes & access_policies_yml.keys) if system && system.hybrid? # if the system is hybrid => check if the non free product subscription is still valid for accessing images - allowed_non_free_product_classes = allowed_product_classes.map { |s| s unless Product.find_by(product_class: s).free? } + allowed_non_free_product_classes = allowed_product_classes.map { |s| s unless Product.find_by(product_class: s, product_type: 'extension').free? }.compact unless allowed_non_free_product_classes.empty? auth_header = { 'HTTP_AUTHORIZATION' => ActionController::HttpAuthentication::Basic.encode_credentials(system.login, system.password) diff --git a/engines/registry/spec/app/models/access_scope_spec.rb b/engines/registry/spec/app/models/access_scope_spec.rb index 9b0f6dff0..1448388e7 100644 --- a/engines/registry/spec/app/models/access_scope_spec.rb +++ b/engines/registry/spec/app/models/access_scope_spec.rb @@ -194,7 +194,7 @@ system end let(:product1) do - product = FactoryBot.create(:product, :with_mirrored_repositories) + product = FactoryBot.create(:product, :with_mirrored_repositories, :extension) product.repositories.where(enabled: false).update(mirroring_enabled: false) product.update(product_class: 'SLES15-SP4-LTSS-X86') product From 7e12f4af14dc6e58446290f1cb059b8b261f8314 Mon Sep 17 00:00:00 2001 From: Jesus Bermudez Velazquez Date: Wed, 4 Sep 2024 18:59:46 +0100 Subject: [PATCH 029/103] Check LTSS product to verify After a non free extension has been activated successfully on a PAYG system, RMT calls ZypperAuth verify_instance method without any other information, the SCC verification of the activation would fail as there is no product info to check against Adding the product id present in the request params, that way RMT can fetch the product class and verify properly This Fixes bsc#1230154 --- engines/zypper_auth/lib/zypper_auth/engine.rb | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/engines/zypper_auth/lib/zypper_auth/engine.rb b/engines/zypper_auth/lib/zypper_auth/engine.rb index db43ac907..e7df82363 100644 --- a/engines/zypper_auth/lib/zypper_auth/engine.rb +++ b/engines/zypper_auth/lib/zypper_auth/engine.rb @@ -6,7 +6,7 @@ def auth_logger Thread.current[:logger] end - def verify_instance(request, logger, system) + def verify_instance(request, logger, system, params_product_id = nil) return false unless request.headers['X-Instance-Data'] instance_data = Base64.decode64(request.headers['X-Instance-Data'].to_s) @@ -31,7 +31,7 @@ def verify_instance(request, logger, system) ) is_valid = verification_provider.instance_valid? - return false if is_valid && system.hybrid? && !handle_scc_subscription(request, system, verification_provider, logger) + return false if is_valid && system.hybrid? && !handle_scc_subscription(request, system, verification_provider, logger, params_product_id) # update repository and registry cache InstanceVerification.update_cache(request.remote_ip, system.login, base_product.id) is_valid @@ -49,8 +49,9 @@ def verify_instance(request, logger, system) false end - def handle_scc_subscription(request, system, verification_provider, logger) - result = SccProxy.scc_check_subscription_expiration(request.headers, system.login, system.system_token, logger, system.proxy_byos_mode) + def handle_scc_subscription(request, system, verification_provider, logger, params_product_id = nil) + product_class = Product.find_by(id: params_product_id).product_class if params_product_id.present? + result = SccProxy.scc_check_subscription_expiration(request.headers, system.login, system.system_token, logger, system.proxy_byos_mode, product_class) return true if result[:is_active] ZypperAuth.zypper_auth_message(request, system, verification_provider, result[:message]) @@ -128,7 +129,7 @@ def make_repo_url(base_url, repo_local_path, service_name = nil) # additional validation for zypper service XML controller before_action :verify_instance def verify_instance - unless ZypperAuth.verify_instance(request, logger, @system) + unless ZypperAuth.verify_instance(request, logger, @system, params.fetch('id', nil)) render(xml: { error: 'Instance verification failed' }, status: 403) end end From d39fe0c66bbcf33288990863528e4c62bac81ed0 Mon Sep 17 00:00:00 2001 From: Jesus Bermudez Velazquez Date: Wed, 4 Sep 2024 20:32:09 +0100 Subject: [PATCH 030/103] Fix SCC activations check when no product info is available When instance verification reaches SCC activations check without any product information available, the validation agreement is that all the activations must be active This Fixes bsc#1230157 --- .../v3/systems/activations_controller_spec.rb | 65 ++++++++++++++++++- engines/scc_proxy/lib/scc_proxy/engine.rb | 7 ++ 2 files changed, 71 insertions(+), 1 deletion(-) diff --git a/engines/registry/spec/requests/api/connect/v3/systems/activations_controller_spec.rb b/engines/registry/spec/requests/api/connect/v3/systems/activations_controller_spec.rb index a16896fbb..dd2cfcb73 100644 --- a/engines/registry/spec/requests/api/connect/v3/systems/activations_controller_spec.rb +++ b/engines/registry/spec/requests/api/connect/v3/systems/activations_controller_spec.rb @@ -3,7 +3,7 @@ include_context 'version header', 3 describe '#activations' do - let(:system) { FactoryBot.create(:system, :with_activated_product) } + let(:system) { FactoryBot.create(:system, :payg, :with_activated_product) } let(:headers) { auth_header.merge(version_header) } context 'without valid repository cache' do @@ -57,5 +57,68 @@ expect(data[0]['service']['url']).to match(%r{^plugin:/susecloud}) end end + + context 'system is hybrid' do + let(:system) { FactoryBot.create(:system, :hybrid, :with_activated_product) } + let(:plugin_double) { instance_double('InstanceVerification::Providers::Example') } + let(:cache_name) { "repo/cache/127.0.0.1-#{system.login}-#{system.products.first.id}" } + let(:scc_systems_activations_url) { 'https://scc.suse.com/connect/systems/activations' } + let(:body_active) do + { + id: 1, + regcode: '631dc51f', + name: 'Subscription 1', + type: 'FULL', + status: 'ACTIVE', + starts_at: 'null', + expires_at: '2014-03-14T13:10:21.164Z', + system_limit: 6, + systems_count: 1, + service: { + product: { + id: system.activations.first.product.id + } + } + } + end + + before do + allow(InstanceVerification::Providers::Example).to receive(:new).and_return(plugin_double) + + allow(plugin_double).to( + receive(:instance_valid?).and_return(true) + ) + allow(File).to receive(:join).and_call_original + allow(InstanceVerification).to receive(:update_cache) + allow(ZypperAuth).to receive(:verify_instance).and_call_original + stub_request(:get, scc_systems_activations_url).to_return(status: 200, body: [body_active].to_json, headers: {}) + headers['X-Instance-Data'] = 'IMDS' + end + + context 'no registry' do + it 'refreshes registry cache key only' do + FileUtils.mkdir_p('repo/cache') + expect(InstanceVerification).to receive(:update_cache).with('127.0.0.1', system.login, system.activations.first.product.id) + get '/connect/systems/activations', headers: headers + FileUtils.rm_rf('repo/cache') + data = JSON.parse(response.body) + expect(SccProxy).not_to receive(:product_path_access) + expect(data[0]['service']['url']).to match(%r{^plugin:/susecloud}) + end + end + + context 'registry' do + it 'refreshes registry cache key only' do + FileUtils.mkdir_p('repo/cache') + FileUtils.touch(cache_name) + expect(InstanceVerification).to receive(:update_cache).with('127.0.0.1', system.login, nil, registry: true) + get '/connect/systems/activations', headers: headers + FileUtils.rm_rf('repo/cache') + data = JSON.parse(response.body) + expect(SccProxy).not_to receive(:product_path_access) + expect(data[0]['service']['url']).to match(%r{^plugin:/susecloud}) + end + end + end end end diff --git a/engines/scc_proxy/lib/scc_proxy/engine.rb b/engines/scc_proxy/lib/scc_proxy/engine.rb index 378b87a99..d79e9479c 100644 --- a/engines/scc_proxy/lib/scc_proxy/engine.rb +++ b/engines/scc_proxy/lib/scc_proxy/engine.rb @@ -218,11 +218,17 @@ def product_class_access(scc_systems_activations, product) end end + # rubocop:disable Metrics/PerceivedComplexity def activations_fail_state(scc_systems_activations, headers, product = nil) return SccProxy.product_class_access(scc_systems_activations, product) unless product.nil? active_products_ids = scc_systems_activations.map { |act| act['service']['product']['id'] if act['status'].casecmp('active').zero? }.flatten x_original_uri = headers.fetch('X-Original-URI', '') + # if there is no product info to compare the activations with + # probably means the query is to refresh credentials + # in any case, verification is true if ALL activations are ACTIVE + return { is_active: (scc_systems_activations.length == active_products_ids.length) } if x_original_uri.empty? + if SccProxy.product_path_access(x_original_uri, active_products_ids) { is_active: true } else @@ -247,6 +253,7 @@ def activations_fail_state(scc_systems_activations, headers, product = nil) end end end + # rubocop:enable Metrics/PerceivedComplexity def scc_check_subscription_expiration(headers, login, system_token, logger, mode, product = nil) # rubocop:disable Metrics/ParameterLists response = SccProxy.get_scc_activations(headers, system_token, mode) From f0806ae511d02183333ef374bba05084924e95af Mon Sep 17 00:00:00 2001 From: "depfu[bot]" <23717796+depfu[bot]@users.noreply.github.com> Date: Mon, 16 Sep 2024 17:15:42 +0000 Subject: [PATCH 031/103] Update jwt to version 2.9.0 --- Gemfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile.lock b/Gemfile.lock index 3d9f1c966..72c3d67ed 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.8.2) + jwt (2.9.0) base64 listen (3.6.0) rb-fsevent (~> 0.10, >= 0.10.3) From fa31dc9d7cf7c5f73ce8a2a9f8a48222ff1dec27 Mon Sep 17 00:00:00 2001 From: Thomas Schmidt Date: Tue, 17 Sep 2024 17:03:52 +0200 Subject: [PATCH 032/103] 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 472631fbc9a2276a498a4c0b826c6a4d034028cc Mon Sep 17 00:00:00 2001 From: "depfu[bot]" <23717796+depfu[bot]@users.noreply.github.com> Date: Fri, 20 Sep 2024 07:20:18 +0000 Subject: [PATCH 033/103] Update puma to version 5.6.9 --- Gemfile.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 3d9f1c966..2d98fe38c 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -160,7 +160,7 @@ GEM mustache (1.1.1) mysql2 (0.5.6) nenv (0.3.0) - nio4r (2.7.0) + nio4r (2.7.3) nokogiri (1.12.5) mini_portile2 (~> 2.6.1) racc (~> 1.4) @@ -177,7 +177,7 @@ GEM coderay (~> 1.1) method_source (~> 1.0) public_suffix (4.0.7) - puma (5.6.8) + puma (5.6.9) nio4r (~> 2.0) racc (1.8.0) rack (2.2.9) From bfc5a922930f09cd6c713b87fc9b64a9e81962d7 Mon Sep 17 00:00:00 2001 From: Adnilson Delgado Date: Fri, 20 Sep 2024 12:54:21 +0100 Subject: [PATCH 034/103] 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 035/103] 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 036/103] 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 037/103] 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 038/103] 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 039/103] 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 040/103] 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 041/103] 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 d00aa5e4b97c537a7a90406edce225b831e26fa0 Mon Sep 17 00:00:00 2001 From: Jesus Bermudez Velazquez Date: Thu, 3 Oct 2024 11:00:14 +0100 Subject: [PATCH 042/103] Move error handling to the proper method The method that gets the activations from SCC by making a request, should be responsible to handle if the request failed Use Rails.logger instead of carrying logger all over the methods --- engines/scc_proxy/lib/scc_proxy/engine.rb | 40 ++++++++++------------- 1 file changed, 18 insertions(+), 22 deletions(-) diff --git a/engines/scc_proxy/lib/scc_proxy/engine.rb b/engines/scc_proxy/lib/scc_proxy/engine.rb index 7c4e6a1bb..ac6d5da56 100644 --- a/engines/scc_proxy/lib/scc_proxy/engine.rb +++ b/engines/scc_proxy/lib/scc_proxy/engine.rb @@ -201,14 +201,19 @@ def parse_error(error_message, token = nil, email = nil) error_message end - def get_scc_activations(headers, system_token, mode) + def get_scc_activations(headers, system_token, mode, login) auth = headers['HTTP_AUTHORIZATION'] if headers && headers.include?('HTTP_AUTHORIZATION') uri = URI.parse(SYSTEMS_ACTIVATIONS_URL) http = Net::HTTP.new(uri.host, uri.port) http.use_ssl = true uri.query = URI.encode_www_form({ byos_mode: mode }) scc_request = Net::HTTP::Get.new(uri.path, headers(auth, system_token)) - http.request(scc_request) + response = http.request(scc_request) + unless response.code_type == Net::HTTPOK + Rails.logger.info "Could not get the system (#{login}) activations, error: #{response.message} #{response.code}" + raise ActionController::TranslatedError.new(response.body) + end + JSON.parse(response.body) end def product_path_access(x_original_uri, products_ids) @@ -266,14 +271,10 @@ def activations_fail_state(scc_systems_activations, headers, product = nil) end end - def scc_check_subscription_expiration(headers, login, system_token, logger, mode, product = nil) # rubocop:disable Metrics/ParameterLists - response = SccProxy.get_scc_activations(headers, system_token, mode) - unless response.code_type == Net::HTTPOK - logger.info "Could not get the system (#{login}) activations, error: #{response.message} #{response.code}" - response.message = SccProxy.parse_error(response.message) if response.message.include? 'json' - return { is_active: false, message: response.message } - end - scc_systems_activations = JSON.parse(response.body) + def scc_check_subscription_expiration(headers, login, system_token, mode, product = nil) + scc_systems_activations = SccProxy.get_scc_activations( + headers, system_token, mode, login + ) return { is_active: false, message: 'No activations.' } if scc_systems_activations.empty? no_status_products_ids = scc_systems_activations.map do |act| @@ -282,6 +283,8 @@ def scc_check_subscription_expiration(headers, login, system_token, logger, mode return { is_active: true } unless no_status_products_ids.all?(&:nil?) SccProxy.activations_fail_state(scc_systems_activations, headers, product) + rescue StandardError + { is_active: false, message: 'Could not check the activations from SCC' } end def scc_upgrade(auth, product, system_login, mode, logger) @@ -440,26 +443,19 @@ def scc_deactivate_product elsif @system.hybrid? && @product.extension? # check if product is on SCC and # if it is -> de-activate it - scc_systems_activations = find_hybrid_activations_on_scc(request.headers) - if scc_systems_activations.map { |act| act['service']['product']['id'] == @product.id }.present? + scc_hybrid_system_activations = SccProxy.get_scc_activations( + headers, @system.system_token, @system.proxy_byos_mode, @system.login + ) + if scc_hybrid_system_activations.map { |act| act['service']['product']['id'] == @product.id }.present? # if product is found on SCC, regardless of the state # it is OK to remove it from SCC SccProxy.deactivate_product_scc(auth, @product, @system.system_token, logger) - make_system_payg(auth) if scc_systems_activations.reject { |act| act['service']['product']['id'] == @product.id }.blank? + make_system_payg(auth) if scc_hybrid_system_activations.reject { |act| act['service']['product']['id'] == @product.id }.blank? end end logger.info "Product '#{@product.friendly_name}' successfully deactivated from SCC" end - def find_hybrid_activations_on_scc(headers) - response = SccProxy.get_scc_activations(headers, @system.system_token, @system.proxy_byos_mode) - unless response.code_type == Net::HTTPOK - logger.info "Could not get the system (#{@system.login}) activations, error: #{response.message} #{response.code}" - raise ActionController::TranslatedError.new(response.body) - end - JSON.parse(response.body) - end - def make_system_payg(auth) # if the system does not have more products activated on SCC # switch it back to payg From 78ab2058f3ea4a9ee439e18042c8112a9486cccb Mon Sep 17 00:00:00 2001 From: Jesus Bermudez Velazquez Date: Thu, 3 Oct 2024 11:02:09 +0100 Subject: [PATCH 043/103] Remove the logger param from the methods because of the Rails.logger change --- engines/registry/app/models/access_scope.rb | 4 ++-- engines/registry/spec/app/models/access_scope_spec.rb | 2 -- engines/zypper_auth/lib/zypper_auth/engine.rb | 10 ++++++---- .../authentication_controller_spec.rb | 4 ---- 4 files changed, 8 insertions(+), 12 deletions(-) diff --git a/engines/registry/app/models/access_scope.rb b/engines/registry/app/models/access_scope.rb index 511dbbca5..f66d760ef 100644 --- a/engines/registry/app/models/access_scope.rb +++ b/engines/registry/app/models/access_scope.rb @@ -94,8 +94,8 @@ def allowed_paths(system = nil) } allowed_non_free_product_classes.each do |non_free_prod_class| activation_state = SccProxy.scc_check_subscription_expiration( - auth_header, system.login, system.system_token, Rails.logger, system.proxy_byos_mode, non_free_prod_class - ) + auth_header, system.login, system.system_token, system.proxy_byos_mode, non_free_prod_class + ) unless activation_state[:is_active] Rails.logger.info( "Access to #{non_free_prod_class} from system #{system.login} denied: #{activation_state[:message]}" diff --git a/engines/registry/spec/app/models/access_scope_spec.rb b/engines/registry/spec/app/models/access_scope_spec.rb index 1448388e7..90af1c45e 100644 --- a/engines/registry/spec/app/models/access_scope_spec.rb +++ b/engines/registry/spec/app/models/access_scope_spec.rb @@ -217,7 +217,6 @@ header_expected, system.login, system.system_token, - Rails.logger, system.proxy_byos_mode, 'SLES15-SP4-LTSS-X86' ).and_return(scc_response) @@ -229,7 +228,6 @@ header_expected, system.login, system.system_token, - Rails.logger, system.proxy_byos_mode, 'SLES15-SP4-LTSS-X86' ) diff --git a/engines/zypper_auth/lib/zypper_auth/engine.rb b/engines/zypper_auth/lib/zypper_auth/engine.rb index db43ac907..915d20683 100644 --- a/engines/zypper_auth/lib/zypper_auth/engine.rb +++ b/engines/zypper_auth/lib/zypper_auth/engine.rb @@ -31,12 +31,12 @@ def verify_instance(request, logger, system) ) is_valid = verification_provider.instance_valid? - return false if is_valid && system.hybrid? && !handle_scc_subscription(request, system, verification_provider, logger) + return false if is_valid && system.hybrid? && !handle_scc_subscription(request, system, verification_provider) # update repository and registry cache InstanceVerification.update_cache(request.remote_ip, system.login, base_product.id) is_valid rescue InstanceVerification::Exception => e - return handle_scc_subscription(request, system, verification_provider, logger) if system.byos? + return handle_scc_subscription(request, system, verification_provider) if system.byos? ZypperAuth.zypper_auth_message(request, system, verification_provider, e.message) false @@ -49,8 +49,10 @@ def verify_instance(request, logger, system) false end - def handle_scc_subscription(request, system, verification_provider, logger) - result = SccProxy.scc_check_subscription_expiration(request.headers, system.login, system.system_token, logger, system.proxy_byos_mode) + def handle_scc_subscription(request, system, verification_provider) + result = SccProxy.scc_check_subscription_expiration( + request.headers, system.login, system.system_token, system.proxy_byos_mode + ) return true if result[:is_active] ZypperAuth.zypper_auth_message(request, system, verification_provider, result[:message]) diff --git a/engines/zypper_auth/spec/requests/strict_authentication/authentication_controller_spec.rb b/engines/zypper_auth/spec/requests/strict_authentication/authentication_controller_spec.rb index 643fe3f6f..2a3ed4084 100644 --- a/engines/zypper_auth/spec/requests/strict_authentication/authentication_controller_spec.rb +++ b/engines/zypper_auth/spec/requests/strict_authentication/authentication_controller_spec.rb @@ -258,7 +258,6 @@ headers, system_hybrid.login, system_hybrid.system_token, - nil, system_hybrid.proxy_byos_mode, system_hybrid.activations.first.product.product_class + '-LTSS' ) @@ -300,7 +299,6 @@ headers, system_hybrid.login, system_hybrid.system_token, - nil, system_hybrid.proxy_byos_mode, system_hybrid.activations.first.product.product_class + '-LTSS' ) @@ -343,7 +341,6 @@ headers, system_hybrid.login, system_hybrid.system_token, - nil, system_hybrid.proxy_byos_mode, system_hybrid.activations.first.product.product_class + '-LTSS' ) @@ -386,7 +383,6 @@ headers, system_hybrid.login, system_hybrid.system_token, - nil, system_hybrid.proxy_byos_mode, system_hybrid.activations.first.product.product_class + '-LTSS' ) 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 044/103] 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 045/103] 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 046/103] 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 54b42c4bd83d4ffa2a98d86d42e15d68c88a7a8b Mon Sep 17 00:00:00 2001 From: Natnael Getahun Date: Tue, 29 Oct 2024 08:14:20 +0100 Subject: [PATCH 047/103] POC: Yabeda rmt integration --- .env.example | 1 + Gemfile | 7 +++ Gemfile.lock | 37 +++++++++++++++ config/application.rb | 1 + config/initializers/yabeda.rb | 82 ++++++++++++++++++++++++++++++++++ config/puma.rb | 5 +++ config/puma/prometheus.rb | 7 +++ config/rmt.yml | 8 ++++ config/routes.rb | 6 +++ lib/yabeda/prometheus_patch.rb | 25 +++++++++++ package/obs/rmt.conf | 7 +++ 11 files changed, 186 insertions(+) create mode 100644 config/initializers/yabeda.rb create mode 100644 config/puma/prometheus.rb create mode 100644 lib/yabeda/prometheus_patch.rb diff --git a/.env.example b/.env.example index 2229a9035..519a6834f 100644 --- a/.env.example +++ b/.env.example @@ -2,3 +2,4 @@ SECRET_KEY_BASE=8ea53ad3bc6c03923e376c8bdd85059c1885524947a7efe53d5e9c9d4e398611 EXTERNAL_PORT=8080 SCC_USERNAME= SCC_PASSWORD= +COLLECT_PROMETHEUS_METRICS= \ No newline at end of file diff --git a/Gemfile b/Gemfile index cc4b4508f..295c24165 100644 --- a/Gemfile +++ b/Gemfile @@ -32,6 +32,13 @@ gem 'repomd_parser', '~> 1.1.0' # Use Rack CORS for handling Cross-Origin Resource Sharing (CORS), making cross-origin AJAX possible # gem 'rack-cors' +# Prometheus Exporter: +gem 'yabeda' +gem 'yabeda-rails' +gem 'yabeda-puma-plugin' +gem 'yabeda-sidekiq' +gem 'yabeda-prometheus' + gem 'strong_migrations' group :development, :test do diff --git a/Gemfile.lock b/Gemfile.lock index d48697404..3445bfe5d 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -32,6 +32,8 @@ GEM zeitwerk (~> 2.3) addressable (2.8.6) public_suffix (>= 2.0.2, < 6.0) + anyway_config (2.6.4) + ruby-next-core (~> 1.0) ast (2.4.2) awesome_print (1.9.2) base32 (0.3.4) @@ -48,6 +50,7 @@ GEM config (3.0.0) deep_merge (~> 1.2, >= 1.2.1) dry-validation (~> 1.0, >= 1.0.0) + connection_pool (2.4.1) coveralls (0.8.23) json (>= 1.8, < 3) simplecov (~> 0.16.1) @@ -173,6 +176,8 @@ GEM prime (0.1.2) forwardable singleton + prometheus-client (4.2.3) + base64 pry (0.14.0) coderay (~> 1.1) method_source (~> 1.0) @@ -201,6 +206,7 @@ GEM rb-inotify (0.10.1) ffi (~> 1.0) rdiscount (2.2.0.2) + redis (4.8.1) regexp_parser (2.6.0) repomd_parser (1.1.0) bzip2-ffi (~> 1.1.1) @@ -267,6 +273,7 @@ GEM rubocop (~> 1.19) rubocop-thread_safety (0.4.4) rubocop (>= 0.53.0) + ruby-next-core (1.0.3) ruby-progressbar (1.11.0) ruby_parser (3.19.2) sexp_processor (~> 4.16) @@ -281,6 +288,10 @@ GEM shellany (0.0.1) shoulda-matchers (4.5.1) activesupport (>= 4.2.0) + sidekiq (6.5.12) + connection_pool (>= 2.2.5, < 3) + rack (~> 2.0) + redis (>= 4.5.0, < 5) simplecov (0.16.1) docile (~> 1.1) json (>= 1.8, < 3) @@ -321,6 +332,27 @@ GEM addressable (>= 2.8.0) crack (>= 0.3.2) hashdiff (>= 0.4.0, < 2.0.0) + yabeda (0.13.1) + anyway_config (>= 1.0, < 3) + concurrent-ruby + dry-initializer + yabeda-prometheus (0.9.1) + prometheus-client (>= 3.0, < 5.0) + rack + yabeda (~> 0.10) + yabeda-puma-plugin (0.7.1) + json + puma + yabeda (~> 0.5) + yabeda-rails (0.9.0) + activesupport + anyway_config (>= 1.3, < 3) + railties + yabeda (~> 0.8) + yabeda-sidekiq (0.12.0) + anyway_config (>= 1.3, < 3) + sidekiq + yabeda (~> 0.6) yard (0.9.35) zeitwerk (2.6.15) zstd-ruby (1.5.6.1) @@ -382,6 +414,11 @@ DEPENDENCIES vcr (~> 6.0) versionist webmock + yabeda + yabeda-prometheus + yabeda-puma-plugin + yabeda-rails + yabeda-sidekiq BUNDLED WITH 1.17.3 diff --git a/config/application.rb b/config/application.rb index 581f9eac3..ffbae9be9 100644 --- a/config/application.rb +++ b/config/application.rb @@ -15,6 +15,7 @@ # require "action_cable/engine" # require "sprockets/railtie" # require "rails/test_unit/railtie" +require_relative '../lib/yabeda/prometheus_patch' # Require the gems listed in Gemfile, including any gems # you've limited to :test, :development, or :production. diff --git a/config/initializers/yabeda.rb b/config/initializers/yabeda.rb new file mode 100644 index 000000000..e4cf51684 --- /dev/null +++ b/config/initializers/yabeda.rb @@ -0,0 +1,82 @@ +# frozen_string_literal: true + +return if Settings.scc.try(:metrics) && (Settings.scc.metrics.try(:enabled) && (!!Settings.scc.metrics.enabled == false)) + +# require 'yabeda' +# require 'yabeda/rails' +# require 'yabeda/sidekiq' +# require 'yabeda/prometheus' + +# Configure prometheus client +Prometheus::Client.config.data_store = Prometheus::Client::DataStores::DirectFileStore.new( + dir: './tmp/prometheus/' +) + +# Configure yabeda +Yabeda.configure do + assign_labels = lambda { + default_tag :environment, Rails.env + default_tag :application, 'rmt' + } + + group :sidekiq, &assign_labels + group :rails, &assign_labels + group :rake, &assign_labels + group :scc, &assign_labels + + group :rails do + counter :started_requests_total, + comment: 'A counter of the total number of HTTP requests rails has started to process.', + tags: %i[controller action format method] + end + + group :rake do + gauge :task_started_at, + comment: 'Time when the task started: unix time with decimals', + tags: %i[task_name] + + gauge :task_exit_status, + comment: 'Task exit status. 1 means OK, 0 means failed, not present means no data', + tags: %i[task_name] + + gauge :task_duration_ms, + comment: 'Time taken running the task', + tags: %i[task_name] + + gauge :task_finished_at, + comment: 'Time when the task finished: unix time with decimals', + tags: %i[task_name] + end + + group :pct do + + end +end + +# Instrument the request from the start +ActiveSupport::Notifications.subscribe 'start_processing.action_controller' do |*args| + # Match the same event as Yabeda + event = Yabeda::Rails::Event.new(*args) + + Yabeda.rails.started_requests_total.tap do |metric| + labels = event.labels.slice(*metric.tags) + + metric.increment(labels, by: 1) + end +end + +ActiveSupport::Notifications.subscribe 'task_run.rake' do |*args| + event = ActiveSupport::Notifications::Event.new(*args) + task_name = event.payload[:task_name] + + success = 1 + if !!(event.payload[:exception] || event.payload[:exception_class]) + success = 0 + end + + Yabeda.rake.task_duration_ms.set({ task_name: task_name }, event.duration) + Yabeda.rake.task_started_at.set({ task_name: task_name }, event.time / 1000.0) + Yabeda.rake.task_finished_at.set({ task_name: task_name }, event.end / 1000.0) + + Yabeda.rake.task_exit_status.set({ task_name: task_name }, success) +end \ No newline at end of file diff --git a/config/puma.rb b/config/puma.rb index c1fec1414..2e92007f8 100644 --- a/config/puma.rb +++ b/config/puma.rb @@ -1,4 +1,5 @@ require 'rmt/config' +require_relative 'puma/prometheus' # Puma can serve each request in a thread from an internal thread pool. # The `threads` method setting takes two numbers: a minimum and maximum. @@ -36,3 +37,7 @@ # Allow puma to be restarted by `rails restart` command. plugin :tmp_restart + +if Settings.scc.try(:metrics) && (Settings.scc.metrics.try(:enabled) && (!!Settings.scc.metrics.enabled == false)) + configure_prometheus!(self) +end \ No newline at end of file diff --git a/config/puma/prometheus.rb b/config/puma/prometheus.rb new file mode 100644 index 000000000..3e7f7b48f --- /dev/null +++ b/config/puma/prometheus.rb @@ -0,0 +1,7 @@ +def configure_prometheus!(puma) + ENV['STARTED_FROM_PUMA'] = '1' + + puma.activate_control_app + puma.plugin :yabeda + puma.plugin :yabeda_prometheus +end \ No newline at end of file diff --git a/config/rmt.yml b/config/rmt.yml index 3b1dabb2f..a3cd93a85 100644 --- a/config/rmt.yml +++ b/config/rmt.yml @@ -24,6 +24,14 @@ scc: username: <%= ENV['SCC_USERNAME'] %> password: <%= ENV['SCC_PASSWORD'] %> sync_systems: true + metrics: + enabled: true + interval: 60 + job_name: yabeda + url: <%= ENV.fetch('PROMETHEUS_ENDPOINT') { 'http://localhost:9091' } %> + username: <%= ENV.fetch('PROMETHEUS_USERNAME') { 'admin' }%> + password: <%= ENV.fetch('PROMETHEUS_PASSWORD') { 'admin' }%> + mirroring: mirror_src: false diff --git a/config/routes.rb b/config/routes.rb index 9cea050f2..7c43d4b87 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -69,6 +69,12 @@ get '/v2/_catalog', to: 'registry/registry#catalog' end + # For bin/rails s users: expose the metrics endpoint via rails instead of puma + if Rails.env.development? && ENV['STARTED_FROM_PUMA'].blank? + Rails.logger.info('Mounting Yabeda in Rails.routes') + mount Yabeda::Prometheus::Exporter, at: '/metrics' + end + if defined?(SccSumaApi::Engine) mount SccSumaApi::Engine, at: '/api/scc' diff --git a/lib/yabeda/prometheus_patch.rb b/lib/yabeda/prometheus_patch.rb new file mode 100644 index 000000000..ba4f7e392 --- /dev/null +++ b/lib/yabeda/prometheus_patch.rb @@ -0,0 +1,25 @@ +# frozen_string_literal: true + +require 'yabeda' +require 'yabeda/prometheus' +require 'prometheus/client/push' + +module Yabeda + module PrometheusPatch + def patched_push_gateway(grouping_key: {}) + @push_gateway ||= # rubocop:disable Naming/MemoizedInstanceVariableName + ::Prometheus::Client::Push.new( + job: Settings.scc.metrics.job_name, + gateway:Settings.scc.metrics.url, + grouping_key: grouping_key, + open_timeout: 5, read_timeout: 30 + ) + end + + def self.apply!(mod) + mod.extend(self) + end + end +end + +Yabeda::PrometheusPatch.apply!(Yabeda::Prometheus) \ No newline at end of file diff --git a/package/obs/rmt.conf b/package/obs/rmt.conf index c2f410e24..b583cfdf1 100644 --- a/package/obs/rmt.conf +++ b/package/obs/rmt.conf @@ -12,6 +12,13 @@ scc: username: password: sync_systems: true + metrics: + enabled: false + interval: 60 + job_name: yabeda + url: https://foo.com/metrics + username: + password: mirroring: mirror_src: false From 8e00dd5204edbc4f5a3c4cae7206104f03c4ef14 Mon Sep 17 00:00:00 2001 From: Natnael Getahun Date: Tue, 29 Oct 2024 14:57:33 +0100 Subject: [PATCH 048/103] Remove yabeda-sidekiq gem and update yabeda.rb initializer to use Settings.dig for metrics.enabled --- Gemfile | 1 - config/initializers/yabeda.rb | 15 ++------------- config/puma.rb | 4 ++-- config/puma/prometheus.rb | 2 +- config/rmt.yml | 5 ++--- lib/yabeda/prometheus_patch.rb | 25 ------------------------- package/obs/rmt.conf | 5 ++--- 7 files changed, 9 insertions(+), 48 deletions(-) delete mode 100644 lib/yabeda/prometheus_patch.rb diff --git a/Gemfile b/Gemfile index 295c24165..d57185c60 100644 --- a/Gemfile +++ b/Gemfile @@ -36,7 +36,6 @@ gem 'repomd_parser', '~> 1.1.0' gem 'yabeda' gem 'yabeda-rails' gem 'yabeda-puma-plugin' -gem 'yabeda-sidekiq' gem 'yabeda-prometheus' gem 'strong_migrations' diff --git a/config/initializers/yabeda.rb b/config/initializers/yabeda.rb index e4cf51684..331161ee8 100644 --- a/config/initializers/yabeda.rb +++ b/config/initializers/yabeda.rb @@ -1,11 +1,6 @@ # frozen_string_literal: true -return if Settings.scc.try(:metrics) && (Settings.scc.metrics.try(:enabled) && (!!Settings.scc.metrics.enabled == false)) - -# require 'yabeda' -# require 'yabeda/rails' -# require 'yabeda/sidekiq' -# require 'yabeda/prometheus' +return unless Settings.dig(:scc, :metrics, :enabled) # Configure prometheus client Prometheus::Client.config.data_store = Prometheus::Client::DataStores::DirectFileStore.new( @@ -19,10 +14,8 @@ default_tag :application, 'rmt' } - group :sidekiq, &assign_labels group :rails, &assign_labels group :rake, &assign_labels - group :scc, &assign_labels group :rails do counter :started_requests_total, @@ -47,10 +40,6 @@ comment: 'Time when the task finished: unix time with decimals', tags: %i[task_name] end - - group :pct do - - end end # Instrument the request from the start @@ -79,4 +68,4 @@ Yabeda.rake.task_finished_at.set({ task_name: task_name }, event.end / 1000.0) Yabeda.rake.task_exit_status.set({ task_name: task_name }, success) -end \ No newline at end of file +end diff --git a/config/puma.rb b/config/puma.rb index 2e92007f8..39ace4d2b 100644 --- a/config/puma.rb +++ b/config/puma.rb @@ -38,6 +38,6 @@ # Allow puma to be restarted by `rails restart` command. plugin :tmp_restart -if Settings.scc.try(:metrics) && (Settings.scc.metrics.try(:enabled) && (!!Settings.scc.metrics.enabled == false)) +if Settings.dig(:scc, :metrics, :enabled) configure_prometheus!(self) -end \ No newline at end of file +end diff --git a/config/puma/prometheus.rb b/config/puma/prometheus.rb index 3e7f7b48f..df5dcd9b9 100644 --- a/config/puma/prometheus.rb +++ b/config/puma/prometheus.rb @@ -4,4 +4,4 @@ def configure_prometheus!(puma) puma.activate_control_app puma.plugin :yabeda puma.plugin :yabeda_prometheus -end \ No newline at end of file +end diff --git a/config/rmt.yml b/config/rmt.yml index a3cd93a85..e64266999 100644 --- a/config/rmt.yml +++ b/config/rmt.yml @@ -25,9 +25,8 @@ scc: password: <%= ENV['SCC_PASSWORD'] %> sync_systems: true metrics: - enabled: true - interval: 60 - job_name: yabeda + enabled: <%= ENV.fetch('RMT_METRICS_ENABLED') { false } %> + job_name: <%= ENV.fetch('PROMETHEUS_JOB_NAME') { 'rmt-webserver' } %> url: <%= ENV.fetch('PROMETHEUS_ENDPOINT') { 'http://localhost:9091' } %> username: <%= ENV.fetch('PROMETHEUS_USERNAME') { 'admin' }%> password: <%= ENV.fetch('PROMETHEUS_PASSWORD') { 'admin' }%> diff --git a/lib/yabeda/prometheus_patch.rb b/lib/yabeda/prometheus_patch.rb deleted file mode 100644 index ba4f7e392..000000000 --- a/lib/yabeda/prometheus_patch.rb +++ /dev/null @@ -1,25 +0,0 @@ -# frozen_string_literal: true - -require 'yabeda' -require 'yabeda/prometheus' -require 'prometheus/client/push' - -module Yabeda - module PrometheusPatch - def patched_push_gateway(grouping_key: {}) - @push_gateway ||= # rubocop:disable Naming/MemoizedInstanceVariableName - ::Prometheus::Client::Push.new( - job: Settings.scc.metrics.job_name, - gateway:Settings.scc.metrics.url, - grouping_key: grouping_key, - open_timeout: 5, read_timeout: 30 - ) - end - - def self.apply!(mod) - mod.extend(self) - end - end -end - -Yabeda::PrometheusPatch.apply!(Yabeda::Prometheus) \ No newline at end of file diff --git a/package/obs/rmt.conf b/package/obs/rmt.conf index b583cfdf1..ab1bb257e 100644 --- a/package/obs/rmt.conf +++ b/package/obs/rmt.conf @@ -14,9 +14,8 @@ scc: sync_systems: true metrics: enabled: false - interval: 60 - job_name: yabeda - url: https://foo.com/metrics + job_name: rmt + url: http://localhost:9394/metrics username: password: From 94b219f7e627485f05eddf8b9402597d16bad117 Mon Sep 17 00:00:00 2001 From: Natnael Getahun Date: Tue, 29 Oct 2024 17:13:36 +0100 Subject: [PATCH 049/103] Update metrics configuration in .env.example and config/rmt.yml --- .env.example | 3 ++- Gemfile.lock | 11 ----------- config/application.rb | 1 - config/rmt.yml | 4 ---- package/obs/rmt.conf | 5 +---- 5 files changed, 3 insertions(+), 21 deletions(-) diff --git a/.env.example b/.env.example index 519a6834f..68abdc7b0 100644 --- a/.env.example +++ b/.env.example @@ -2,4 +2,5 @@ SECRET_KEY_BASE=8ea53ad3bc6c03923e376c8bdd85059c1885524947a7efe53d5e9c9d4e398611 EXTERNAL_PORT=8080 SCC_USERNAME= SCC_PASSWORD= -COLLECT_PROMETHEUS_METRICS= \ No newline at end of file +RMT_METRICS_ENABLED= +PROMETHEUS_JOB_NAME= diff --git a/Gemfile.lock b/Gemfile.lock index 3445bfe5d..0f663bea7 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -50,7 +50,6 @@ GEM config (3.0.0) deep_merge (~> 1.2, >= 1.2.1) dry-validation (~> 1.0, >= 1.0.0) - connection_pool (2.4.1) coveralls (0.8.23) json (>= 1.8, < 3) simplecov (~> 0.16.1) @@ -206,7 +205,6 @@ GEM rb-inotify (0.10.1) ffi (~> 1.0) rdiscount (2.2.0.2) - redis (4.8.1) regexp_parser (2.6.0) repomd_parser (1.1.0) bzip2-ffi (~> 1.1.1) @@ -288,10 +286,6 @@ GEM shellany (0.0.1) shoulda-matchers (4.5.1) activesupport (>= 4.2.0) - sidekiq (6.5.12) - connection_pool (>= 2.2.5, < 3) - rack (~> 2.0) - redis (>= 4.5.0, < 5) simplecov (0.16.1) docile (~> 1.1) json (>= 1.8, < 3) @@ -349,10 +343,6 @@ GEM anyway_config (>= 1.3, < 3) railties yabeda (~> 0.8) - yabeda-sidekiq (0.12.0) - anyway_config (>= 1.3, < 3) - sidekiq - yabeda (~> 0.6) yard (0.9.35) zeitwerk (2.6.15) zstd-ruby (1.5.6.1) @@ -418,7 +408,6 @@ DEPENDENCIES yabeda-prometheus yabeda-puma-plugin yabeda-rails - yabeda-sidekiq BUNDLED WITH 1.17.3 diff --git a/config/application.rb b/config/application.rb index ffbae9be9..581f9eac3 100644 --- a/config/application.rb +++ b/config/application.rb @@ -15,7 +15,6 @@ # require "action_cable/engine" # require "sprockets/railtie" # require "rails/test_unit/railtie" -require_relative '../lib/yabeda/prometheus_patch' # Require the gems listed in Gemfile, including any gems # you've limited to :test, :development, or :production. diff --git a/config/rmt.yml b/config/rmt.yml index e64266999..811e44ff2 100644 --- a/config/rmt.yml +++ b/config/rmt.yml @@ -27,10 +27,6 @@ scc: metrics: enabled: <%= ENV.fetch('RMT_METRICS_ENABLED') { false } %> job_name: <%= ENV.fetch('PROMETHEUS_JOB_NAME') { 'rmt-webserver' } %> - url: <%= ENV.fetch('PROMETHEUS_ENDPOINT') { 'http://localhost:9091' } %> - username: <%= ENV.fetch('PROMETHEUS_USERNAME') { 'admin' }%> - password: <%= ENV.fetch('PROMETHEUS_PASSWORD') { 'admin' }%> - mirroring: mirror_src: false diff --git a/package/obs/rmt.conf b/package/obs/rmt.conf index ab1bb257e..a1d6eca60 100644 --- a/package/obs/rmt.conf +++ b/package/obs/rmt.conf @@ -15,10 +15,7 @@ scc: metrics: enabled: false job_name: rmt - url: http://localhost:9394/metrics - username: - password: - + mirroring: mirror_src: false dedup_method: hardlink From 9a617a0754672bd57fb2f3ddd2658952de66ee3d Mon Sep 17 00:00:00 2001 From: Adnilson Delgado Date: Tue, 29 Oct 2024 19:41:27 +0000 Subject: [PATCH 050/103] Trigger Build From 0dc9e0614f4da3223d748309d7273a53b6d0bb22 Mon Sep 17 00:00:00 2001 From: Natnael Getahun Date: Wed, 30 Oct 2024 09:10:13 +0100 Subject: [PATCH 051/103] Add :nocov: comments to RMT module and RMT::Config module --- lib/rmt.rb | 2 ++ lib/rmt/config.rb | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/rmt.rb b/lib/rmt.rb index 08a1fa97a..7d62b9bce 100644 --- a/lib/rmt.rb +++ b/lib/rmt.rb @@ -1,3 +1,4 @@ +# :nocov: module RMT VERSION ||= '2.19'.freeze @@ -7,3 +8,4 @@ module RMT DEFAULT_MIRROR_URL_PREFIX = '/repo/'.freeze CREDENTIALS_FILE_LOCATION = '/etc/zypp/credentials.d/SCCcredentials'.freeze end +# :nocov: diff --git a/lib/rmt/config.rb b/lib/rmt/config.rb index 650a316ad..a967e8f53 100644 --- a/lib/rmt/config.rb +++ b/lib/rmt/config.rb @@ -1,3 +1,4 @@ +# :nocov: require 'config' require_relative '../rmt' @@ -11,7 +12,6 @@ File.join(__dir__, '../../config/rmt.local.yml') ) - module RMT::Config class << self def db_config(key = 'database') @@ -77,3 +77,4 @@ def validate_int(value) end end end +# :nocov: From 59e62dff406f89d9a9dc170a973b4f4c9fe253b4 Mon Sep 17 00:00:00 2001 From: Natnael Getahun Date: Wed, 30 Oct 2024 10:03:04 +0100 Subject: [PATCH 052/103] Prepare rmt 2.20 release --- package/obs/rmt-server.changes | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/package/obs/rmt-server.changes b/package/obs/rmt-server.changes index febce557b..836bf566d 100644 --- a/package/obs/rmt-server.changes +++ b/package/obs/rmt-server.changes @@ -1,3 +1,9 @@ +------------------------------------------------------------------- +Wed Oct 30 09:01:32 UTC 2024 - Natnael Getahun + +- Version 2.20 (unreleased) + * RMT packaging: make sync/mirror timer changes survive upgrade + ------------------------------------------------------------------- Wed Aug 21 15:28:43 UTC 2024 - Jesús Bermúdez Velázquez From 40bcfc19eda4e497f3bb64140bc9a7a623b17500 Mon Sep 17 00:00:00 2001 From: Natnael Getahun Date: Mon, 4 Nov 2024 11:10:43 +0100 Subject: [PATCH 053/103] Add filters to .simplecov --- .simplecov | 5 +++++ lib/rmt.rb | 2 -- lib/rmt/config.rb | 3 +-- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/.simplecov b/.simplecov index e08fb8ee6..3757d9b5c 100644 --- a/.simplecov +++ b/.simplecov @@ -12,6 +12,11 @@ unless ENV['NO_COVERAGE'] track_files('app/**/*.rb') track_files('lib/**/*.rb') + + add_filter('lib/rmt/config.rb') + add_filter('lib/rmt/db.rb') + add_filter('lib/rmt.rb') + add_filter('config/**/*(.rb|.yml)') end end diff --git a/lib/rmt.rb b/lib/rmt.rb index 7d62b9bce..08a1fa97a 100644 --- a/lib/rmt.rb +++ b/lib/rmt.rb @@ -1,4 +1,3 @@ -# :nocov: module RMT VERSION ||= '2.19'.freeze @@ -8,4 +7,3 @@ module RMT DEFAULT_MIRROR_URL_PREFIX = '/repo/'.freeze CREDENTIALS_FILE_LOCATION = '/etc/zypp/credentials.d/SCCcredentials'.freeze end -# :nocov: diff --git a/lib/rmt/config.rb b/lib/rmt/config.rb index a967e8f53..650a316ad 100644 --- a/lib/rmt/config.rb +++ b/lib/rmt/config.rb @@ -1,4 +1,3 @@ -# :nocov: require 'config' require_relative '../rmt' @@ -12,6 +11,7 @@ File.join(__dir__, '../../config/rmt.local.yml') ) + module RMT::Config class << self def db_config(key = 'database') @@ -77,4 +77,3 @@ def validate_int(value) end end end -# :nocov: From 4aec66202235842c48855d2c0cb27cb124418fe7 Mon Sep 17 00:00:00 2001 From: Natnael Getahun Date: Mon, 4 Nov 2024 11:59:31 +0100 Subject: [PATCH 054/103] Temporary: Add coverage report export and version checks --- .github/workflows/lint-unit.yml | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/.github/workflows/lint-unit.yml b/.github/workflows/lint-unit.yml index fc5842d18..0f15078e9 100644 --- a/.github/workflows/lint-unit.yml +++ b/.github/workflows/lint-unit.yml @@ -60,6 +60,12 @@ jobs: with: ruby-version: ${{ matrix.ruby-version }} bundler-cache: true + + - name: Export coverage report + uses: actions/upload-artifact@v2 + with: + name: coverage + path: ${{ github.workspace }}/coverage - name: Setup database config file run: | @@ -82,6 +88,16 @@ jobs: - name: Run core tests run: | bundle exec rspec --format documentation + + - name: Run PubCloud engines tests + run: | + bundle exec rake test:engines + + - name: Run version checks + run: | + echo "::group::Version verification checks" + ruby ci/check-version-matches.rb + echo "::endgroup::" - name: Run PubCloud engines tests run: | From 63119c37f936b809385b114f4f681fd7ff7824dd Mon Sep 17 00:00:00 2001 From: Natnael Getahun Date: Mon, 4 Nov 2024 12:10:20 +0100 Subject: [PATCH 055/103] Update Ruby version and use latest artifact upload action --- .github/workflows/lint-unit.yml | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/.github/workflows/lint-unit.yml b/.github/workflows/lint-unit.yml index 0f15078e9..43ae648f2 100644 --- a/.github/workflows/lint-unit.yml +++ b/.github/workflows/lint-unit.yml @@ -30,7 +30,7 @@ jobs: matrix: # we need to test the Ruby versions of the products we release RMT for, # those are: SLE15 (2.5.8, 2.5.9) - ruby-version: ['2.5.8', '2.5.9'] + ruby-version: [ '2.5.9'] # skipping Ruby3 tests for now, because RMT isn't Ruby3 compatible yet, and the failing # tests confuse the team. # ruby-version: ['2.5.8', '2.5.9', '3.0.3'] @@ -60,12 +60,6 @@ jobs: with: ruby-version: ${{ matrix.ruby-version }} bundler-cache: true - - - name: Export coverage report - uses: actions/upload-artifact@v2 - with: - name: coverage - path: ${{ github.workspace }}/coverage - name: Setup database config file run: | @@ -108,3 +102,10 @@ jobs: echo "::group::Version verification checks" ruby ci/check-version-matches.rb echo "::endgroup::" + + - name: Export coverage report + uses: actions/upload-artifact@v4 + if: always() + with: + name: coverage + path: ${{ github.workspace }}/coverage \ No newline at end of file From b09244d0f32cd6c66a37aceb16601d70634225e4 Mon Sep 17 00:00:00 2001 From: Natnael Getahun Date: Mon, 4 Nov 2024 14:14:38 +0100 Subject: [PATCH 056/103] Refactor test commands and coverage configuration --- .github/workflows/lint-unit.yml | 12 +----------- .simplecov | 9 ++++----- 2 files changed, 5 insertions(+), 16 deletions(-) diff --git a/.github/workflows/lint-unit.yml b/.github/workflows/lint-unit.yml index 43ae648f2..e4e8ea349 100644 --- a/.github/workflows/lint-unit.yml +++ b/.github/workflows/lint-unit.yml @@ -81,7 +81,7 @@ jobs: - name: Run core tests run: | - bundle exec rspec --format documentation + bundle exec rake test:core - name: Run PubCloud engines tests run: | @@ -93,16 +93,6 @@ jobs: ruby ci/check-version-matches.rb echo "::endgroup::" - - name: Run PubCloud engines tests - run: | - bundle exec rake test:engines - - - name: Run version checks - run: | - echo "::group::Version verification checks" - ruby ci/check-version-matches.rb - echo "::endgroup::" - - name: Export coverage report uses: actions/upload-artifact@v4 if: always() diff --git a/.simplecov b/.simplecov index 3757d9b5c..052bcb2d8 100644 --- a/.simplecov +++ b/.simplecov @@ -10,13 +10,12 @@ unless ENV['NO_COVERAGE'] # omit registration sharing (removing systems using rmt-cli) add_filter('engines/registration_sharing/lib/registration_sharing.rb') - track_files('app/**/*.rb') - track_files('lib/**/*.rb') - - add_filter('lib/rmt/config.rb') add_filter('lib/rmt/db.rb') add_filter('lib/rmt.rb') - add_filter('config/**/*(.rb|.yml)') + add_filter('config') + + track_files('app/**/*.rb') + track_files('lib/**/*.rb') end end From e51bea58302d29815e57a916885e53e3e9419be5 Mon Sep 17 00:00:00 2001 From: Natnael Getahun Date: Mon, 4 Nov 2024 17:42:10 +0100 Subject: [PATCH 057/103] Revert "Update Ruby version and use latest artifact upload action" This reverts commit 63119c37f936b809385b114f4f681fd7ff7824dd. --- .github/workflows/lint-unit.yml | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/.github/workflows/lint-unit.yml b/.github/workflows/lint-unit.yml index e4e8ea349..b4e7ab855 100644 --- a/.github/workflows/lint-unit.yml +++ b/.github/workflows/lint-unit.yml @@ -30,7 +30,7 @@ jobs: matrix: # we need to test the Ruby versions of the products we release RMT for, # those are: SLE15 (2.5.8, 2.5.9) - ruby-version: [ '2.5.9'] + ruby-version: ['2.5.8', '2.5.9'] # skipping Ruby3 tests for now, because RMT isn't Ruby3 compatible yet, and the failing # tests confuse the team. # ruby-version: ['2.5.8', '2.5.9', '3.0.3'] @@ -92,10 +92,3 @@ jobs: echo "::group::Version verification checks" ruby ci/check-version-matches.rb echo "::endgroup::" - - - name: Export coverage report - uses: actions/upload-artifact@v4 - if: always() - with: - name: coverage - path: ${{ github.workspace }}/coverage \ No newline at end of file From 3ce29b4a5b09a6a9be367570889a1a863da04489 Mon Sep 17 00:00:00 2001 From: Jesus Bermudez Velazquez Date: Mon, 4 Nov 2024 16:28:16 +0000 Subject: [PATCH 058/103] 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 059/103] 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 From 7d84d7aefdda2fc4e64f0babe8da4fc93aaa04ae Mon Sep 17 00:00:00 2001 From: Jesus Bermudez Velazquez Date: Mon, 4 Nov 2024 16:28:16 +0000 Subject: [PATCH 060/103] 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 c9b6ed7227a2783b3b63754e1c04eff7eca289fb Mon Sep 17 00:00:00 2001 From: Jesus Bermudez Velazquez Date: Tue, 5 Nov 2024 14:45:59 +0000 Subject: [PATCH 061/103] 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 From 8a7d37fd06c1aa21874fb39cb835d663fb0d5a84 Mon Sep 17 00:00:00 2001 From: Natnael Getahun Date: Tue, 5 Nov 2024 17:07:51 +0100 Subject: [PATCH 062/103] Remove unused rake metrics --- config/initializers/yabeda.rb | 35 ----------------------------------- 1 file changed, 35 deletions(-) diff --git a/config/initializers/yabeda.rb b/config/initializers/yabeda.rb index 331161ee8..f2631fbf3 100644 --- a/config/initializers/yabeda.rb +++ b/config/initializers/yabeda.rb @@ -15,31 +15,12 @@ } group :rails, &assign_labels - group :rake, &assign_labels group :rails do counter :started_requests_total, comment: 'A counter of the total number of HTTP requests rails has started to process.', tags: %i[controller action format method] end - - group :rake do - gauge :task_started_at, - comment: 'Time when the task started: unix time with decimals', - tags: %i[task_name] - - gauge :task_exit_status, - comment: 'Task exit status. 1 means OK, 0 means failed, not present means no data', - tags: %i[task_name] - - gauge :task_duration_ms, - comment: 'Time taken running the task', - tags: %i[task_name] - - gauge :task_finished_at, - comment: 'Time when the task finished: unix time with decimals', - tags: %i[task_name] - end end # Instrument the request from the start @@ -53,19 +34,3 @@ metric.increment(labels, by: 1) end end - -ActiveSupport::Notifications.subscribe 'task_run.rake' do |*args| - event = ActiveSupport::Notifications::Event.new(*args) - task_name = event.payload[:task_name] - - success = 1 - if !!(event.payload[:exception] || event.payload[:exception_class]) - success = 0 - end - - Yabeda.rake.task_duration_ms.set({ task_name: task_name }, event.duration) - Yabeda.rake.task_started_at.set({ task_name: task_name }, event.time / 1000.0) - Yabeda.rake.task_finished_at.set({ task_name: task_name }, event.end / 1000.0) - - Yabeda.rake.task_exit_status.set({ task_name: task_name }, success) -end From 6a474253bf1b5d72e2ee5e1d3bebbf7a2a82d7af Mon Sep 17 00:00:00 2001 From: Jesus Bermudez Velazquez Date: Tue, 5 Nov 2024 18:16:00 +0000 Subject: [PATCH 063/103] Use system object Instead of passing system attributes, send system itself and use its attributes inside --- engines/registry/app/models/access_scope.rb | 2 +- .../spec/app/models/access_scope_spec.rb | 10 ++-------- engines/scc_proxy/lib/scc_proxy/engine.rb | 18 +++++++----------- engines/zypper_auth/lib/zypper_auth/engine.rb | 4 +--- .../authentication_controller_spec.rb | 18 ++++-------------- 5 files changed, 15 insertions(+), 37 deletions(-) diff --git a/engines/registry/app/models/access_scope.rb b/engines/registry/app/models/access_scope.rb index f66d760ef..b15c0b17b 100644 --- a/engines/registry/app/models/access_scope.rb +++ b/engines/registry/app/models/access_scope.rb @@ -94,7 +94,7 @@ def allowed_paths(system = nil) } allowed_non_free_product_classes.each do |non_free_prod_class| activation_state = SccProxy.scc_check_subscription_expiration( - auth_header, system.login, system.system_token, system.proxy_byos_mode, non_free_prod_class + auth_header, system, non_free_prod_class ) unless activation_state[:is_active] Rails.logger.info( diff --git a/engines/registry/spec/app/models/access_scope_spec.rb b/engines/registry/spec/app/models/access_scope_spec.rb index 90af1c45e..0d3c84a22 100644 --- a/engines/registry/spec/app/models/access_scope_spec.rb +++ b/engines/registry/spec/app/models/access_scope_spec.rb @@ -215,20 +215,15 @@ allow(SccProxy).to receive(:scc_check_subscription_expiration) .with( header_expected, - system.login, - system.system_token, - system.proxy_byos_mode, + system, 'SLES15-SP4-LTSS-X86' ).and_return(scc_response) end - # rubocop:disable RSpec/ExampleLength it 'returns no actions allowed' do expect(SccProxy).to receive(:scc_check_subscription_expiration).with( header_expected, - system.login, - system.system_token, - system.proxy_byos_mode, + system, 'SLES15-SP4-LTSS-X86' ) yaml_string = access_policy_content @@ -248,7 +243,6 @@ } ) end - # rubocop:enable RSpec/ExampleLength end end diff --git a/engines/scc_proxy/lib/scc_proxy/engine.rb b/engines/scc_proxy/lib/scc_proxy/engine.rb index ac6d5da56..e3faad42e 100644 --- a/engines/scc_proxy/lib/scc_proxy/engine.rb +++ b/engines/scc_proxy/lib/scc_proxy/engine.rb @@ -201,16 +201,16 @@ def parse_error(error_message, token = nil, email = nil) error_message end - def get_scc_activations(headers, system_token, mode, login) + def get_scc_activations(headers, system) auth = headers['HTTP_AUTHORIZATION'] if headers && headers.include?('HTTP_AUTHORIZATION') uri = URI.parse(SYSTEMS_ACTIVATIONS_URL) http = Net::HTTP.new(uri.host, uri.port) http.use_ssl = true - uri.query = URI.encode_www_form({ byos_mode: mode }) - scc_request = Net::HTTP::Get.new(uri.path, headers(auth, system_token)) + uri.query = URI.encode_www_form({ byos_mode: system.proxy_byos_mode }) + scc_request = Net::HTTP::Get.new(uri.path, headers(auth, system.system_token)) response = http.request(scc_request) unless response.code_type == Net::HTTPOK - Rails.logger.info "Could not get the system (#{login}) activations, error: #{response.message} #{response.code}" + Rails.logger.info "Could not get the system (#{system.login}) activations, error: #{response.message} #{response.code}" raise ActionController::TranslatedError.new(response.body) end JSON.parse(response.body) @@ -271,10 +271,8 @@ def activations_fail_state(scc_systems_activations, headers, product = nil) end end - def scc_check_subscription_expiration(headers, login, system_token, mode, product = nil) - scc_systems_activations = SccProxy.get_scc_activations( - headers, system_token, mode, login - ) + def scc_check_subscription_expiration(headers, system, product = nil) + scc_systems_activations = SccProxy.get_scc_activations(headers, system) return { is_active: false, message: 'No activations.' } if scc_systems_activations.empty? no_status_products_ids = scc_systems_activations.map do |act| @@ -443,9 +441,7 @@ def scc_deactivate_product elsif @system.hybrid? && @product.extension? # check if product is on SCC and # if it is -> de-activate it - scc_hybrid_system_activations = SccProxy.get_scc_activations( - headers, @system.system_token, @system.proxy_byos_mode, @system.login - ) + scc_hybrid_system_activations = SccProxy.get_scc_activations(headers, @system) if scc_hybrid_system_activations.map { |act| act['service']['product']['id'] == @product.id }.present? # if product is found on SCC, regardless of the state # it is OK to remove it from SCC diff --git a/engines/zypper_auth/lib/zypper_auth/engine.rb b/engines/zypper_auth/lib/zypper_auth/engine.rb index 915d20683..d5622f994 100644 --- a/engines/zypper_auth/lib/zypper_auth/engine.rb +++ b/engines/zypper_auth/lib/zypper_auth/engine.rb @@ -50,9 +50,7 @@ def verify_instance(request, logger, system) end def handle_scc_subscription(request, system, verification_provider) - result = SccProxy.scc_check_subscription_expiration( - request.headers, system.login, system.system_token, system.proxy_byos_mode - ) + result = SccProxy.scc_check_subscription_expiration(request.headers, system) return true if result[:is_active] ZypperAuth.zypper_auth_message(request, system, verification_provider, result[:message]) diff --git a/engines/zypper_auth/spec/requests/strict_authentication/authentication_controller_spec.rb b/engines/zypper_auth/spec/requests/strict_authentication/authentication_controller_spec.rb index 2a3ed4084..f0ab69371 100644 --- a/engines/zypper_auth/spec/requests/strict_authentication/authentication_controller_spec.rb +++ b/engines/zypper_auth/spec/requests/strict_authentication/authentication_controller_spec.rb @@ -245,20 +245,16 @@ before do stub_request(:get, scc_systems_activations_url).to_return(status: 200, body: [body_active].to_json, headers: {}) - # allow(SccProxy).to receive(:get_scc_activations).and_return(status: 200, body: [body_active].to_json, headers: {}) expect(URI).to receive(:encode_www_form).with({ byos_mode: 'hybrid' }) allow(File).to receive(:directory?).and_return(true) allow(Dir).to receive(:mkdir) allow(FileUtils).to receive(:touch) - # get '/api/auth/check', headers: headers end it 'returns true' do result = SccProxy.scc_check_subscription_expiration( headers, - system_hybrid.login, - system_hybrid.system_token, - system_hybrid.proxy_byos_mode, + system_hybrid, system_hybrid.activations.first.product.product_class + '-LTSS' ) expect(result[:is_active]).to be(true) @@ -297,9 +293,7 @@ it 'returns false, expired' do result = SccProxy.scc_check_subscription_expiration( headers, - system_hybrid.login, - system_hybrid.system_token, - system_hybrid.proxy_byos_mode, + system_hybrid, system_hybrid.activations.first.product.product_class + '-LTSS' ) expect(result[:is_active]).to eq(false) @@ -339,9 +333,7 @@ it 'returns product not activated' do result = SccProxy.scc_check_subscription_expiration( headers, - system_hybrid.login, - system_hybrid.system_token, - system_hybrid.proxy_byos_mode, + system_hybrid, system_hybrid.activations.first.product.product_class + '-LTSS' ) expect(result[:is_active]).to eq(false) @@ -381,9 +373,7 @@ it 'returns unexpected error' do result = SccProxy.scc_check_subscription_expiration( headers, - system_hybrid.login, - system_hybrid.system_token, - system_hybrid.proxy_byos_mode, + system_hybrid, system_hybrid.activations.first.product.product_class + '-LTSS' ) expect(result[:is_active]).to eq(false) From ed98758f9f6cb67cbd2080976ecd513dc6282ae6 Mon Sep 17 00:00:00 2001 From: Jesus Bermudez Velazquez Date: Tue, 5 Nov 2024 18:23:40 +0000 Subject: [PATCH 064/103] Remove unnecesary disabling rule --- engines/scc_proxy/lib/scc_proxy/engine.rb | 2 -- 1 file changed, 2 deletions(-) diff --git a/engines/scc_proxy/lib/scc_proxy/engine.rb b/engines/scc_proxy/lib/scc_proxy/engine.rb index e3faad42e..becd25b63 100644 --- a/engines/scc_proxy/lib/scc_proxy/engine.rb +++ b/engines/scc_proxy/lib/scc_proxy/engine.rb @@ -300,7 +300,6 @@ def scc_upgrade(auth, product, system_login, mode, logger) end end - # rubocop:disable Metrics/ClassLength class Engine < ::Rails::Engine isolate_namespace SccProxy config.generators.api_only = true @@ -534,6 +533,5 @@ def get_system(systems) end end end - # rubocop:enable Metrics/ClassLength end # rubocop:enable Metrics/ModuleLength From 09e876b1c48e97aef766c61473801e6ca4b1adbc Mon Sep 17 00:00:00 2001 From: Jesus Bermudez Velazquez Date: Wed, 6 Nov 2024 16:20:38 +0000 Subject: [PATCH 065/103] Adjust after merging master branch and solve conflicts --- engines/zypper_auth/lib/zypper_auth/engine.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/engines/zypper_auth/lib/zypper_auth/engine.rb b/engines/zypper_auth/lib/zypper_auth/engine.rb index 657865ae5..8478ee7c1 100644 --- a/engines/zypper_auth/lib/zypper_auth/engine.rb +++ b/engines/zypper_auth/lib/zypper_auth/engine.rb @@ -31,7 +31,7 @@ def verify_instance(request, logger, system, params_product_id = nil) ) is_valid = verification_provider.instance_valid? - return false if is_valid && system.hybrid? && !handle_scc_subscription(request, system, verification_provider, logger, params_product_id) + return false if is_valid && system.hybrid? && !handle_scc_subscription(request, system, verification_provider, params_product_id) # update repository and registry cache InstanceVerification.update_cache(request.remote_ip, system.login, base_product.id) @@ -50,9 +50,9 @@ def verify_instance(request, logger, system, params_product_id = nil) false end - def handle_scc_subscription(request, system, verification_provider, logger, params_product_id = nil) + def handle_scc_subscription(request, system, verification_provider, params_product_id = nil) product_class = Product.find_by(id: params_product_id).product_class if params_product_id.present? - result = SccProxy.scc_check_subscription_expiration(request.headers, system.login, system.system_token, logger, system.proxy_byos_mode, product_class) + result = SccProxy.scc_check_subscription_expiration(request.headers, system, product_class) return true if result[:is_active] ZypperAuth.zypper_auth_message(request, system, verification_provider, result[:message]) From d364f1e35d9b616b6db97345c31c9ccc601cfcf1 Mon Sep 17 00:00:00 2001 From: Natnael Getahun Date: Thu, 7 Nov 2024 11:15:13 +0100 Subject: [PATCH 066/103] Add export artifacts step to lint-unit workflow --- .github/workflows/lint-unit.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.github/workflows/lint-unit.yml b/.github/workflows/lint-unit.yml index b4e7ab855..0315a9ea2 100644 --- a/.github/workflows/lint-unit.yml +++ b/.github/workflows/lint-unit.yml @@ -92,3 +92,10 @@ jobs: echo "::group::Version verification checks" ruby ci/check-version-matches.rb echo "::endgroup::" + + - name: Export artifacts + uses: actions/upload-artifact@v2 + if: always() + with: + name: test-results + path: ${{ github.workspace }}/coverage \ No newline at end of file From ddbb38f4e07a72d5bf8d13d6927a97d3754bb110 Mon Sep 17 00:00:00 2001 From: Natnael Getahun Date: Thu, 7 Nov 2024 11:35:32 +0100 Subject: [PATCH 067/103] Add :nocov: comments to ignore code coverage for specific sections --- .github/workflows/lint-unit.yml | 9 +-------- config/initializers/yabeda.rb | 2 ++ lib/rmt/config.rb | 2 ++ 3 files changed, 5 insertions(+), 8 deletions(-) diff --git a/.github/workflows/lint-unit.yml b/.github/workflows/lint-unit.yml index f96a1ec40..f9d1eaebc 100644 --- a/.github/workflows/lint-unit.yml +++ b/.github/workflows/lint-unit.yml @@ -91,11 +91,4 @@ jobs: run: | echo "::group::Version verification checks" ruby ci/check-version-matches.rb - echo "::endgroup::" - - - name: Export artifacts - uses: actions/upload-artifact@v4 - if: always() - with: - name: test-results - path: ${{ github.workspace }}/coverage \ No newline at end of file + echo "::endgroup::" \ No newline at end of file diff --git a/config/initializers/yabeda.rb b/config/initializers/yabeda.rb index f2631fbf3..0396c27e8 100644 --- a/config/initializers/yabeda.rb +++ b/config/initializers/yabeda.rb @@ -1,3 +1,4 @@ +# :nocov: # frozen_string_literal: true return unless Settings.dig(:scc, :metrics, :enabled) @@ -34,3 +35,4 @@ metric.increment(labels, by: 1) end end +# :nocov: diff --git a/lib/rmt/config.rb b/lib/rmt/config.rb index 650a316ad..4c7ebe27c 100644 --- a/lib/rmt/config.rb +++ b/lib/rmt/config.rb @@ -1,3 +1,4 @@ +# :nocov: require 'config' require_relative '../rmt' @@ -77,3 +78,4 @@ def validate_int(value) end end end +# :nocov: From 8d99b17075e8545dd44428b811edc03d7c65fe91 Mon Sep 17 00:00:00 2001 From: Jesus Bermudez Velazquez Date: Thu, 7 Nov 2024 18:01:51 +0000 Subject: [PATCH 068/103] Add request to billing check The verification code will check the headers of @request object in order to access the content of SUMA headers to grant access Currently @request is nil coming from this code flow This add the request to the call --- .../instance_verification/billing_check_controller.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/engines/instance_verification/app/controllers/instance_verification/billing_check_controller.rb b/engines/instance_verification/app/controllers/instance_verification/billing_check_controller.rb index 9d00eee26..0217e54d7 100644 --- a/engines/instance_verification/app/controllers/instance_verification/billing_check_controller.rb +++ b/engines/instance_verification/app/controllers/instance_verification/billing_check_controller.rb @@ -5,7 +5,7 @@ def check # belongs to a PAYG or BYOS instance verification_provider = InstanceVerification.provider.new( logger, - nil, + request, nil, params[:metadata] ) From e4d40a6d643ece35c69cf0d3a0c4191c5ade875e Mon Sep 17 00:00:00 2001 From: Natnael Getahun Date: Mon, 11 Nov 2024 11:24:49 +0100 Subject: [PATCH 069/103] Update RandomizedDelaySec in systemd timers --- package/files/systemd/rmt-server-mirror.timer | 2 +- package/files/systemd/rmt-server-sync.timer | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package/files/systemd/rmt-server-mirror.timer b/package/files/systemd/rmt-server-mirror.timer index 33e37ea93..fb5bef680 100644 --- a/package/files/systemd/rmt-server-mirror.timer +++ b/package/files/systemd/rmt-server-mirror.timer @@ -3,7 +3,7 @@ Description=RMT Mirror timer [Timer] OnCalendar=*-*-* 02:00:00 -RandomizedDelaySec=9h +RandomizedDelaySec=15m Unit=rmt-server-mirror.service [Install] diff --git a/package/files/systemd/rmt-server-sync.timer b/package/files/systemd/rmt-server-sync.timer index 7a34b7545..7b393aebe 100644 --- a/package/files/systemd/rmt-server-sync.timer +++ b/package/files/systemd/rmt-server-sync.timer @@ -3,7 +3,7 @@ Description=RMT Sync timer [Timer] OnCalendar=*-*-* 01:00:00 -RandomizedDelaySec=9h +RandomizedDelaySec=15m Unit=rmt-server-sync.service [Install] From 1f685eb6e42783af5335567e68b3b1554eb16597 Mon Sep 17 00:00:00 2001 From: Natnael Getahun Date: Tue, 12 Nov 2024 10:30:50 +0100 Subject: [PATCH 070/103] Revert "Update RandomizedDelaySec in systemd timers" This reverts commit e4d40a6d643ece35c69cf0d3a0c4191c5ade875e. --- package/files/systemd/rmt-server-mirror.timer | 2 +- package/files/systemd/rmt-server-sync.timer | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package/files/systemd/rmt-server-mirror.timer b/package/files/systemd/rmt-server-mirror.timer index fb5bef680..33e37ea93 100644 --- a/package/files/systemd/rmt-server-mirror.timer +++ b/package/files/systemd/rmt-server-mirror.timer @@ -3,7 +3,7 @@ Description=RMT Mirror timer [Timer] OnCalendar=*-*-* 02:00:00 -RandomizedDelaySec=15m +RandomizedDelaySec=9h Unit=rmt-server-mirror.service [Install] diff --git a/package/files/systemd/rmt-server-sync.timer b/package/files/systemd/rmt-server-sync.timer index 7b393aebe..7a34b7545 100644 --- a/package/files/systemd/rmt-server-sync.timer +++ b/package/files/systemd/rmt-server-sync.timer @@ -3,7 +3,7 @@ Description=RMT Sync timer [Timer] OnCalendar=*-*-* 01:00:00 -RandomizedDelaySec=15m +RandomizedDelaySec=9h Unit=rmt-server-sync.service [Install] From 78646d29902cf67463075aa5acaf3e3f5f8385ef Mon Sep 17 00:00:00 2001 From: Natnael Getahun Date: Tue, 12 Nov 2024 10:35:01 +0100 Subject: [PATCH 071/103] Update RMT packaging --- package/obs/rmt-server.changes | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/package/obs/rmt-server.changes b/package/obs/rmt-server.changes index d1c4244f2..869af4633 100644 --- a/package/obs/rmt-server.changes +++ b/package/obs/rmt-server.changes @@ -2,8 +2,11 @@ Wed Oct 30 09:01:32 UTC 2024 - Natnael Getahun - Version 2.20 (unreleased) - * RMT packaging: make sync/mirror timer changes survive upgrade - + * RMT packaging: don't overwrite custom sync/mirror timer config on package update + * Extend column size for repository and file paths (bsc#1229152) + * rmt-server-pubcloud: + * Fix LTSS product verification (bsc#1230154) + * Fix activations check when no product info is available (bsc#1230157) ------------------------------------------------------------------- Wed Aug 21 15:28:43 UTC 2024 - Jesús Bermúdez Velázquez From f5679ae1430189000f7591e3aeaba63d1a6be1e6 Mon Sep 17 00:00:00 2001 From: Thomas Schmidt Date: Tue, 12 Nov 2024 17:03:42 +0100 Subject: [PATCH 072/103] Update rmt-server.changes --- package/obs/rmt-server.changes | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/package/obs/rmt-server.changes b/package/obs/rmt-server.changes index 869af4633..ed80834ce 100644 --- a/package/obs/rmt-server.changes +++ b/package/obs/rmt-server.changes @@ -3,10 +3,12 @@ Wed Oct 30 09:01:32 UTC 2024 - Natnael Getahun - Version 2.20 (unreleased) * RMT packaging: don't overwrite custom sync/mirror timer config on package update - * Extend column size for repository and file paths (bsc#1229152) + * Extend column size for repository and file paths (bsc#1229152) + * Forward suseconnect client user-agents to SCC * rmt-server-pubcloud: * Fix LTSS product verification (bsc#1230154) * Fix activations check when no product info is available (bsc#1230157) + ------------------------------------------------------------------- Wed Aug 21 15:28:43 UTC 2024 - Jesús Bermúdez Velázquez From 4064c1e8e8a6efed18e862913534cab81783d98b Mon Sep 17 00:00:00 2001 From: Jesus Bermudez Velazquez Date: Fri, 8 Nov 2024 15:30:41 +0000 Subject: [PATCH 073/103] Check for SLE Micro and SUMa headers to grant access SUMA 5.0 is based on SLE Micro, to access older SUMA versions we are using new headers present in the request to grant access to SUMA 4.X repositories --- .../authentication_controller.rb | 20 +++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/engines/strict_authentication/app/controllers/strict_authentication/authentication_controller.rb b/engines/strict_authentication/app/controllers/strict_authentication/authentication_controller.rb index 311c38fc0..ec671999b 100644 --- a/engines/strict_authentication/app/controllers/strict_authentication/authentication_controller.rb +++ b/engines/strict_authentication/app/controllers/strict_authentication/authentication_controller.rb @@ -7,27 +7,28 @@ class AuthenticationController < ::ApplicationController # This is the endpoint for nginx subrequest auth check def check request_uri = request.headers['X-Original-URI'] - auth_result = path_allowed?(request.headers['X-Original-URI']) + auth_result = path_allowed?(request.headers) logger.info "Authentication subrequest for #{request_uri} -- #{auth_result ? 'allowed' : 'denied'}" head auth_result ? :ok : :forbidden end protected - def path_allowed?(path) + def path_allowed?(headers) + path = headers['X-Original-URI'] return false if path.blank? + return true if path =~ %r{/product\.license/} path = '/' + path.gsub(/^#{RMT::DEFAULT_MIRROR_URL_PREFIX}/, '') - # Allow access to SLES 12 and 12-SP1 repos for systems migrating from SLES 11 has_sles11 = @system.products.where(identifier: 'SUSE_SLES').first return true if (has_sles11 && (path =~ %r{/12/} || path =~ %r{/12-SP1/})) - all_allowed_paths.find { |allowed_path| path =~ /^#{Regexp.escape(allowed_path)}/ } + all_allowed_paths(headers).find { |allowed_path| path =~ /^#{Regexp.escape(allowed_path)}/ } end - def all_allowed_paths + def all_allowed_paths(headers) # return all versions of the same product and arch # (that the system has available with that subscription) # in order to validate access not only for current product but others @@ -39,7 +40,14 @@ def all_allowed_paths # for the SUMa PAYG offers, RMT access verification code allows access # to the SUMa Client Tools channels and SUMa Proxy channels # when product is SUMA_Server and PAYG or SUMA_Server and used as SCC proxy - manager_prod = @system.products.any? { |p| p.identifier.downcase.include?('manager-server') } + manager_prod = @system.products.any? do |p| + manager = p.identifier.downcase.include?('manager-server') + # SUMA 5.0 must have access to SUMA 4.3, 4.2 and so on + micro = p.identifier.downcase.include?('sle-micro') + instance_id_header = headers.fetch('X-Instance-Identifier', '').casecmp('suse-manager-server').zero? + instance_version_header = headers.fetch('X-Instance-Version', '') == '5.0' + manager || (micro && instance_id_header && instance_version_header) + end if manager_prod # add all SUMA products paths From 74ddfb87b167005a2d7c18ba21923aa28e16a72e Mon Sep 17 00:00:00 2001 From: Jesus Bermudez Velazquez Date: Thu, 14 Nov 2024 10:24:44 +0000 Subject: [PATCH 074/103] Allow SLE Micro system to access free SLES repositories A SLE Micro system must have access to all free SLES repositories This Fixes bsc#1230419 --- .../connect/v3/systems/products_controller.rb | 8 ++++- .../authentication_controller.rb | 14 ++++++++ .../authentication_controller_spec.rb | 32 ++++++++++++++++++- spec/factories/products.rb | 16 ++++++++++ spec/factories/systems.rb | 11 +++++++ .../v3/systems/products_controller_spec.rb | 27 ++++++++++++++++ 6 files changed, 106 insertions(+), 2 deletions(-) diff --git a/app/controllers/api/connect/v3/systems/products_controller.rb b/app/controllers/api/connect/v3/systems/products_controller.rb index 2545a9e92..62f5d9508 100644 --- a/app/controllers/api/connect/v3/systems/products_controller.rb +++ b/app/controllers/api/connect/v3/systems/products_controller.rb @@ -12,7 +12,13 @@ def activate end def show - if @system.products.include? @product + if @product.identifier.casecmp('sles').zero? + # if system has SLE Micro + # it should access to SLES products + sle_micro = @system.products.any? { |p| p.identifier.downcase.include?('sle-micro') } + sle_micro_same_arch = @system.products.pluck(:arch).include?(@product.arch) if sle_micro + end + if @system.products.include?(@product) || sle_micro_same_arch respond_with( @product, serializer: ::V3::ProductSerializer, diff --git a/engines/strict_authentication/app/controllers/strict_authentication/authentication_controller.rb b/engines/strict_authentication/app/controllers/strict_authentication/authentication_controller.rb index 311c38fc0..66351a2f5 100644 --- a/engines/strict_authentication/app/controllers/strict_authentication/authentication_controller.rb +++ b/engines/strict_authentication/app/controllers/strict_authentication/authentication_controller.rb @@ -36,6 +36,20 @@ def all_allowed_paths # to them or verify paths all_product_versions = @system.products.map { |p| Product.where(identifier: p.identifier, arch: p.arch) }.flatten allowed_paths = all_product_versions.map { |prod| prod.repositories.pluck(:local_path) }.flatten + # Allow SLE Micro to access all free SLES repositories + sle_micro = @system.products.any? { |p| p.identifier.downcase.include?('sle-micro') } + if sle_micro + system_products_archs = @system.products.pluck(:arch) + product_free_sles_modules_only = Product.where( + "(lower(identifier) like 'sle-module%' or lower(identifier) like 'packagehub') + and lower(identifier) not like '%sap%' + and arch = '#{system_products_archs.first}' + and free = 1" + ) + end + same_arch = product_free_sles_modules_only.any? { |p| system_products_archs.include?(p.arch) } if product_free_sles_modules_only.present? + allowed_paths += product_free_sles_modules_only.map { |prod| prod.repositories.pluck(:local_path) }.flatten if same_arch + # for the SUMa PAYG offers, RMT access verification code allows access # to the SUMa Client Tools channels and SUMa Proxy channels # when product is SUMA_Server and PAYG or SUMA_Server and used as SCC proxy diff --git a/engines/strict_authentication/spec/requests/strict_authentication/authentication_controller_spec.rb b/engines/strict_authentication/spec/requests/strict_authentication/authentication_controller_spec.rb index c921d3855..fcb5f4e03 100644 --- a/engines/strict_authentication/spec/requests/strict_authentication/authentication_controller_spec.rb +++ b/engines/strict_authentication/spec/requests/strict_authentication/authentication_controller_spec.rb @@ -5,7 +5,7 @@ module StrictAuthentication RSpec.describe AuthenticationController, type: :request do subject { response } - let(:system) { FactoryBot.create(:system, :with_activated_product) } + let(:system) { FactoryBot.create(:system, :with_activated_product_sle_micro) } describe '#check' do context 'without authentication' do @@ -39,6 +39,36 @@ module StrictAuthentication its(:code) { is_expected.to eq '403' } end + context 'when requesting a file in an activated SLES repo on a SLE Micro system' do + let(:free_product) do + prod = FactoryBot.create( + :product, :module, :with_mirrored_repositories + ) + prod.identifier = 'sle-module-foo' + prod.arch = system.products.first.arch + prod.save! + prod + end + let(:requested_uri) { '/repo' + free_product.repositories.first[:local_path] + '/repodata/repomd.xml' } + + its(:code) { is_expected.to eq '200' } + end + + context 'when requesting a file in an activated SLES SAP repo on a SLE Micro system' do + let(:free_product) do + prod = FactoryBot.create( + :product, :module, :with_mirrored_repositories + ) + prod.identifier = 'sle-module-foo-sap' + prod.arch = system.products.first.arch + prod.save! + prod + end + let(:requested_uri) { '/repo' + free_product.repositories.first[:local_path] + '/repodata/repomd.xml' } + + its(:code) { is_expected.to eq '403' } + end + context 'when requesting a file in an activated repo' do let(:requested_uri) { '/repo' + system.repositories.first[:local_path] + '/repodata/repomd.xml' } diff --git a/spec/factories/products.rb b/spec/factories/products.rb index 30532e1fc..0f8701fce 100644 --- a/spec/factories/products.rb +++ b/spec/factories/products.rb @@ -73,6 +73,22 @@ friendly_version { '15 SP3' } end + trait :product_sle_micro do + identifier { 'SLE-Micro' } + name { 'SUSE Linux Enterprise Server' } + description { 'SUSE Linux Enterprise offers a comprehensive suite of products...' } + shortname { 'SLES15-SP6' } + former_identifier { 'SUSE_SLES_MICRO' } + product_type { :base } + release_type { nil } + release_stage { 'released' } + version { '15.6' } + arch { 'x86_64' } + free { false } + cpe { 'cpe:/o:suse:sles_sap:15:sp6' } + friendly_version { '15 SP6' } + end + trait :extension do product_type { 'extension' } end diff --git a/spec/factories/systems.rb b/spec/factories/systems.rb index 8fb81aa38..ceab7d02e 100644 --- a/spec/factories/systems.rb +++ b/spec/factories/systems.rb @@ -57,6 +57,17 @@ end end + trait :with_activated_product_sle_micro do + transient do + product { create(:product, :product_sle_micro, :with_mirrored_repositories) } + subscription { nil } + end + + after :create do |system, evaluator| + create(:activation, system: system, service: evaluator.product.service, subscription: evaluator.subscription) + end + end + trait :with_system_information do system_information do { diff --git a/spec/requests/api/connect/v3/systems/products_controller_spec.rb b/spec/requests/api/connect/v3/systems/products_controller_spec.rb index 60c1bbb0d..457a77d65 100644 --- a/spec/requests/api/connect/v3/systems/products_controller_spec.rb +++ b/spec/requests/api/connect/v3/systems/products_controller_spec.rb @@ -246,6 +246,33 @@ end end + context 'when SLE Micro product is activated' do + let(:system) { FactoryBot.create(:system, :with_activated_product_sle_micro) } + let(:product) { FactoryBot.create(:product, :product_sles, :with_mirrored_repositories) } + let(:payload) do + { + identifier: product.identifier, + version: product.version, + arch: system.products.first.arch + } + end + let(:serialized_json) do + V3::ProductSerializer.new( + product, + base_url: URI::HTTP.build({ scheme: response.request.scheme, host: response.request.host }).to_s + ).to_json + end + + describe 'response' do + subject { response } + + before { get url, headers: headers, params: payload } + + its(:code) { is_expected.to eq('200') } + its(:body) { is_expected.to eq(serialized_json) } + end + end + context 'with eula_url' do subject { response } From f126c1e12ae1bdbff647973275a867c62fc6972e Mon Sep 17 00:00:00 2001 From: Jesus Bermudez Velazquez Date: Thu, 21 Nov 2024 03:03:32 +0000 Subject: [PATCH 075/103] Azure basic images do not get access to LTSS --- .../providers/example.rb | 4 + .../v3/systems/products_controller_spec.rb | 4 +- engines/scc_proxy/lib/scc_proxy/engine.rb | 9 ++ .../v3/systems/products_controller_spec.rb | 117 +++++++++++++++++- spec/factories/products.rb | 17 +++ spec/factories/systems.rb | 13 ++ 6 files changed, 160 insertions(+), 4 deletions(-) diff --git a/engines/instance_verification/lib/instance_verification/providers/example.rb b/engines/instance_verification/lib/instance_verification/providers/example.rb index b773d7855..1d49f92d9 100644 --- a/engines/instance_verification/lib/instance_verification/providers/example.rb +++ b/engines/instance_verification/lib/instance_verification/providers/example.rb @@ -49,4 +49,8 @@ def payg_billing_code?(iid, identifier) return true if (identifier.casecmp('sles').zero? && instance_billing_info[:billing_product] == SLES_PRODUCT_IDENTIFIER) return true if (identifier.casecmp('sles_sap').zero? && SLES4SAP_PRODUCT_IDENTIFIER.include?(instance_billing_info[:marketplace_code])) end + + def basic? + false + end end 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 9ccbcb1c2..f3e2940c6 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 @@ -123,7 +123,7 @@ context 'when system has hw_info' do let(:instance_data) { 'dummy_instance_data' } - let(:system) { FactoryBot.create(:system, :payg, :with_system_information, instance_data: instance_data) } + let(:system) { FactoryBot.create(:system, :payg, :with_system_information_az, instance_data: instance_data) } let(:serialized_service_json) do V3::ServiceSerializer.new( product.service, @@ -188,7 +188,7 @@ let(:instance_data) { 'dummy_instance_data' } let(:system) do FactoryBot.create( - :system, :payg, :with_system_information, :with_activated_product, product: base_product, instance_data: instance_data + :system, :payg, :with_system_information_az, :with_activated_product, product: base_product, instance_data: instance_data ) end let(:serialized_service_json) do diff --git a/engines/scc_proxy/lib/scc_proxy/engine.rb b/engines/scc_proxy/lib/scc_proxy/engine.rb index e684dc910..d89967837 100644 --- a/engines/scc_proxy/lib/scc_proxy/engine.rb +++ b/engines/scc_proxy/lib/scc_proxy/engine.rb @@ -372,6 +372,15 @@ def has_no_regcode?(auth_header) protected def scc_activate_product + if (@system.system_information && + JSON.parse(@system.system_information)['cloud_provider'].casecmp('microsoft').zero? && + @product.product_class.downcase.include?('ltss') && + InstanceVerification.provider.new(logger, request, nil, @system.instance_data).basic? + ) + error = ActionController::TranslatedError.new(N_('Product not supported for this instance')) + error.status = :forbidden + raise error + end mode = find_mode unless mode.nil? # if system is byos or hybrid and there is a token 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 0ff8417f3..6494c9bd7 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 @@ -349,12 +349,12 @@ context 'when system is connected to SCC' do let(:system_payg) do - FactoryBot.create(:system, :payg, :with_system_information, :with_activated_base_product, instance_data: instance_data, + FactoryBot.create(:system, :payg, :with_system_information_az, :with_activated_base_product, instance_data: instance_data, system_token: new_system_token) end let(:product) do FactoryBot.create( - :product, :product_sles, :extension, :with_mirrored_repositories, :with_mirrored_extensions, + :product, :product_sles_ltss, :extension, :with_mirrored_repositories, :with_mirrored_extensions, base_products: [system_payg.products.first] ) end @@ -415,7 +415,120 @@ allow(File).to receive(:directory?) allow(FileUtils).to receive(:mkdir_p) allow(FileUtils).to receive(:touch) + allow(InstanceVerification::Providers::Example).to receive(:new).and_return(plugin_double) + allow(plugin_double).to receive(:basic?).and_return(false) + allow(InstanceVerification).to receive(:write_cache_file).twice.with( + Rails.application.config.repo_cache_dir, "127.0.0.1-#{system_payg.login}-#{product.id}" + ) + allow(InstanceVerification).to receive(:write_cache_file).twice.with( + Rails.application.config.registry_cache_dir, "127.0.0.1-#{system_payg.login}" + ) + allow(plugin_double).to receive(:instance_valid?).and_return(true) + end + + context 'when LTSS not allowed' do + before do + allow(plugin_double).to receive(:basic?).and_return(true) + end + + it 'raises an error' do + stub_request(:post, scc_register_system_url) + .to_return(status: 403, body: { ok: 'OK' }.to_json, headers: {}) + + post url, params: payload, headers: headers + data = JSON.parse(response.body) + expect(data['error']).to include('Product not supported for this instance') + end + end + end + end + end + + context 'when system has hw_info' do + let(:instance_data) { '{"instanceId": "dummy_instance_data"}' } + let(:new_system_token) { 'BBBBBBBB-BBBB-4BBB-9BBB-BBBBBBBBBBBB' } + let(:serialized_service_json) do + V3::ServiceSerializer.new( + product.service, + base_url: URI::HTTP.build({ scheme: response.request.scheme, host: response.request.host }).to_s + ).to_json + end + + let(:serialized_service_sap_json) do + V3::ServiceSerializer.new( + product_sap.service, + base_url: URI::HTTP.build({ scheme: response.request.scheme, host: response.request.host }).to_s + ).to_json + end + + context 'when system is connected to SCC' do + let(:system_payg) do + FactoryBot.create(:system, :payg, :with_system_information_az, :with_activated_base_product, instance_data: instance_data, + system_token: new_system_token) + end + let(:product) do + FactoryBot.create( + :product, :product_sles_ltss, :extension, :with_mirrored_repositories, :with_mirrored_extensions, + base_products: [system_payg.products.first] + ) + end + let(:subscription_response) do + { + id: 4206714, + regcode: 'bar', + name: 'SUSE Employee subscription for SUSE Linux Enterprise Server for SAP Applications', + type: 'internal', + status: 'ACTIVE', + starts_at: '2019-03-20T09:48:52.658Z', + expires_at: '2024-03-20T09:48:52.658Z', + system_limit: '100', + systems_count: '156', + virtual_count: nil, + product_classes: [ + 'AiO', + '7261', + 'SLE-HAE-X86', + '7261-BETA', + 'SLE-HAE-X86-BETA', + 'AiO-BETA', + '7261-ALPHA', + 'SLE-HAE-X86-ALPHA', + 'AiO-ALPHA' + ], + product_ids: [ + 1959, + 1421 + ], + skus: [], + systems: [ + { + id: 3021957, + login: 'SCC_foo', + password: '5ee7273ac6ac4d7f', + last_seen_at: '2019-03-20T14:01:05.424Z' + } + ] + } + end + + before do + allow(plugin_double).to( + receive(:instance_valid?) + .and_raise(InstanceVerification::Exception, 'Custom plugin error') + ) + end + context 'with a valid registration code' do + before do + stub_request(:post, scc_activate_url) + .to_return( + status: 201, + body: { id: 'bar' }.to_json, + headers: {} + ) + allow(File).to receive(:directory?) + allow(FileUtils).to receive(:mkdir_p) + allow(FileUtils).to receive(:touch) allow(InstanceVerification).to receive(:write_cache_file).twice.with( Rails.application.config.repo_cache_dir, "127.0.0.1-#{system_payg.login}-#{product.id}" ) diff --git a/spec/factories/products.rb b/spec/factories/products.rb index 30532e1fc..e11965e7a 100644 --- a/spec/factories/products.rb +++ b/spec/factories/products.rb @@ -57,6 +57,23 @@ friendly_version { '15 SP3' } end + trait :product_sles_ltss do + identifier { 'SLES-LTSS' } + name { 'SUSE Linux Enterprise Server LTSS' } + description { 'SUSE Linux Enterprise offers a comprehensive suite of products...' } + shortname { 'SLES15-SP3-LTSS' } + former_identifier { 'SLES_LTSS' } + product_type { 'extension' } + product_class { 'LTSS' } + release_type { nil } + release_stage { 'released' } + version { '15.3' } + arch { 'x86_64' } + free { false } + cpe { 'cpe:/o:suse:sles:15:sp3' } + friendly_version { '15 SP3' } + end + trait :product_sles_sap do identifier { 'SLES_SAP' } name { 'SUSE Linux Enterprise Server' } diff --git a/spec/factories/systems.rb b/spec/factories/systems.rb index 8fb81aa38..2f3197f36 100644 --- a/spec/factories/systems.rb +++ b/spec/factories/systems.rb @@ -70,6 +70,19 @@ end end + trait :with_system_information_az do + system_information do + { + cpus: 2, + sockets: 1, + hypervisor: nil, + arch: 'x86_64', + uuid: SecureRandom.uuid, + cloud_provider: 'Microsoft' + }.to_json + end + end + trait :with_system_token do sequence(:system_token) { |n| "00000000-0000-4000-9000-#{n.to_s.rjust(12, '0')}" } end From 50d74dc426f4888c436727829d74e6731f6895bd Mon Sep 17 00:00:00 2001 From: Jesus Bermudez Velazquez Date: Thu, 21 Nov 2024 03:10:57 +0000 Subject: [PATCH 076/103] Fix rubocop --- engines/scc_proxy/lib/scc_proxy/engine.rb | 2 ++ .../requests/api/connect/v3/systems/products_controller_spec.rb | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/engines/scc_proxy/lib/scc_proxy/engine.rb b/engines/scc_proxy/lib/scc_proxy/engine.rb index d89967837..8a744a036 100644 --- a/engines/scc_proxy/lib/scc_proxy/engine.rb +++ b/engines/scc_proxy/lib/scc_proxy/engine.rb @@ -307,6 +307,7 @@ def scc_upgrade(auth, product, system_login, mode, logger) end end + # rubocop:disable Metrics/ClassLength class Engine < ::Rails::Engine isolate_namespace SccProxy config.generators.api_only = true @@ -549,5 +550,6 @@ def get_system(systems) end end end + # rubocop:enable Metrics/ClassLength end # rubocop:enable Metrics/ModuleLength 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 6494c9bd7..1e11f91e9 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 @@ -444,7 +444,7 @@ end end - context 'when system has hw_info' do + context 'when system has hw info' do let(:instance_data) { '{"instanceId": "dummy_instance_data"}' } let(:new_system_token) { 'BBBBBBBB-BBBB-4BBB-9BBB-BBBBBBBBBBBB' } let(:serialized_service_json) do From 91552ef18baafa52d82605482d18b169cf1b3a6e Mon Sep 17 00:00:00 2001 From: Jesus Bermudez Velazquez Date: Thu, 21 Nov 2024 17:53:33 +0000 Subject: [PATCH 077/103] Allow extension no CSP specific Prepare the condition for any CSPs Rename method to be generic --- .../providers/example.rb | 6 +++-- .../v3/systems/products_controller_spec.rb | 25 ++++++++++++------- engines/scc_proxy/lib/scc_proxy/engine.rb | 7 ++---- 3 files changed, 22 insertions(+), 16 deletions(-) diff --git a/engines/instance_verification/lib/instance_verification/providers/example.rb b/engines/instance_verification/lib/instance_verification/providers/example.rb index 1d49f92d9..46bca7fc0 100644 --- a/engines/instance_verification/lib/instance_verification/providers/example.rb +++ b/engines/instance_verification/lib/instance_verification/providers/example.rb @@ -50,7 +50,9 @@ def payg_billing_code?(iid, identifier) return true if (identifier.casecmp('sles_sap').zero? && SLES4SAP_PRODUCT_IDENTIFIER.include?(instance_billing_info[:marketplace_code])) end - def basic? - false + def allowed_extension? + # method to check if a product (extension) meet the criteria + # to be acivated on SCC or not, i.e. LTSS in Azure Basic VM + true end end 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 f3e2940c6..da9f6bc4a 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 @@ -38,7 +38,7 @@ it 'class instance verification provider' do expect(InstanceVerification::Providers::Example).to receive(:new) - .with(be_a(ActiveSupport::Logger), be_a(ActionDispatch::Request), payload, nil).and_call_original + .with(be_a(ActiveSupport::Logger), be_a(ActionDispatch::Request), payload, nil).and_call_original.at_least(:once) allow(File).to receive(:directory?) allow(Dir).to receive(:mkdir) allow(FileUtils).to receive(:touch) @@ -71,13 +71,17 @@ end context 'when verification provider returns false' do + # let(:plugin_double) { instance_double('InstanceVerification::Providers::Example') } + before do stub_request(:post, scc_activate_url) .to_return( status: 200, body: { error: 'Unexpected instance verification error has occurred' }.to_json, headers: {} - ) + ) + # allow(InstanceVerification::Providers::Example).to receive(:new).and_return(plugin_double) + # allow(plugin_double).to receive(:allowed_extension?).and_return(true) post url, params: payload, headers: headers end @@ -113,7 +117,7 @@ it 'class instance verification provider' do expect(InstanceVerification::Providers::Example).to receive(:new) - .with(be_a(ActiveSupport::Logger), be_a(ActionDispatch::Request), payload, nil).and_call_original + .with(be_a(ActiveSupport::Logger), be_a(ActionDispatch::Request), payload, nil).and_call_original.at_least(:once) allow(File).to receive(:directory?) allow(Dir).to receive(:mkdir) allow(FileUtils).to receive(:touch) @@ -123,7 +127,7 @@ context 'when system has hw_info' do let(:instance_data) { 'dummy_instance_data' } - let(:system) { FactoryBot.create(:system, :payg, :with_system_information_az, instance_data: instance_data) } + let(:system) { FactoryBot.create(:system, :payg, :with_system_information, instance_data: instance_data) } let(:serialized_service_json) do V3::ServiceSerializer.new( product.service, @@ -188,7 +192,7 @@ let(:instance_data) { 'dummy_instance_data' } let(:system) do FactoryBot.create( - :system, :payg, :with_system_information_az, :with_activated_product, product: base_product, instance_data: instance_data + :system, :payg, :with_system_information, :with_activated_product, product: base_product, instance_data: instance_data ) end let(:serialized_service_json) do @@ -340,8 +344,9 @@ before do allow(InstanceVerification::Providers::Example).to receive(:new) - .with(nil, nil, nil, instance_data).and_return(plugin_double) + .and_return(plugin_double) allow(plugin_double).to receive(:parse_instance_data).and_return({ InstanceId: 'foo' }) + allow(plugin_double).to receive(:allowed_extension?).and_return(true) allow(InstanceVerification).to receive(:update_cache).with('127.0.0.1', system.login, product.id) FactoryBot.create(:subscription, product_classes: product_classes) @@ -380,8 +385,9 @@ before do allow(InstanceVerification::Providers::Example).to receive(:new) - .with(nil, nil, nil, instance_data).and_return(plugin_double) + .and_return(plugin_double) allow(plugin_double).to receive(:parse_instance_data).and_return({ InstanceId: 'foo' }) + allow(plugin_double).to receive(:allowed_extension?).and_return(true) allow(InstanceVerification).to receive(:update_cache).with('127.0.0.1', system.login, product.id) FactoryBot.create(:subscription, product_classes: product_classes) @@ -414,7 +420,7 @@ it 'class instance verification provider' do expect(InstanceVerification::Providers::Example).to receive(:new) - .with(be_a(ActiveSupport::Logger), be_a(ActionDispatch::Request), payload, nil).and_call_original + .and_call_original.at_least(:once) allow(File).to receive(:directory?) allow(Dir).to receive(:mkdir) allow(FileUtils).to receive(:touch) @@ -514,8 +520,9 @@ before do allow(InstanceVerification::Providers::Example).to receive(:new) - .with(nil, nil, nil, instance_data).and_return(plugin_double) + .and_return(plugin_double) allow(plugin_double).to receive(:parse_instance_data).and_return({ InstanceId: 'foo' }) + allow(plugin_double).to receive(:allowed_extension?).and_return(true) FactoryBot.create(:subscription, product_classes: product_classes) stub_request(:post, scc_activate_url) diff --git a/engines/scc_proxy/lib/scc_proxy/engine.rb b/engines/scc_proxy/lib/scc_proxy/engine.rb index 8a744a036..95be2a642 100644 --- a/engines/scc_proxy/lib/scc_proxy/engine.rb +++ b/engines/scc_proxy/lib/scc_proxy/engine.rb @@ -373,11 +373,8 @@ def has_no_regcode?(auth_header) protected def scc_activate_product - if (@system.system_information && - JSON.parse(@system.system_information)['cloud_provider'].casecmp('microsoft').zero? && - @product.product_class.downcase.include?('ltss') && - InstanceVerification.provider.new(logger, request, nil, @system.instance_data).basic? - ) + product_hash = @product.attributes.symbolize_keys.slice(:identifier, :version, :arch) + unless InstanceVerification.provider.new(logger, request, product_hash, @system.instance_data).allowed_extension? error = ActionController::TranslatedError.new(N_('Product not supported for this instance')) error.status = :forbidden raise error From f544bb04766999d1d7f7db8b28c5a3e7ab65fc35 Mon Sep 17 00:00:00 2001 From: Jesus Bermudez Velazquez Date: Mon, 25 Nov 2024 10:04:00 +0000 Subject: [PATCH 078/103] Update tests --- .../connect/v3/systems/products_controller_spec.rb | 8 ++++---- spec/factories/systems.rb | 13 ------------- 2 files changed, 4 insertions(+), 17 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 1e11f91e9..f023a15b9 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 @@ -349,7 +349,7 @@ context 'when system is connected to SCC' do let(:system_payg) do - FactoryBot.create(:system, :payg, :with_system_information_az, :with_activated_base_product, instance_data: instance_data, + FactoryBot.create(:system, :payg, :with_system_information, :with_activated_base_product, instance_data: instance_data, system_token: new_system_token) end let(:product) do @@ -416,7 +416,7 @@ allow(FileUtils).to receive(:mkdir_p) allow(FileUtils).to receive(:touch) allow(InstanceVerification::Providers::Example).to receive(:new).and_return(plugin_double) - allow(plugin_double).to receive(:basic?).and_return(false) + allow(plugin_double).to receive(:allowed_extension?).and_return(true) allow(InstanceVerification).to receive(:write_cache_file).twice.with( Rails.application.config.repo_cache_dir, "127.0.0.1-#{system_payg.login}-#{product.id}" ) @@ -428,7 +428,7 @@ context 'when LTSS not allowed' do before do - allow(plugin_double).to receive(:basic?).and_return(true) + allow(plugin_double).to receive(:allowed_extension?).and_return(false) end it 'raises an error' do @@ -463,7 +463,7 @@ context 'when system is connected to SCC' do let(:system_payg) do - FactoryBot.create(:system, :payg, :with_system_information_az, :with_activated_base_product, instance_data: instance_data, + FactoryBot.create(:system, :payg, :with_system_information, :with_activated_base_product, instance_data: instance_data, system_token: new_system_token) end let(:product) do diff --git a/spec/factories/systems.rb b/spec/factories/systems.rb index 2f3197f36..8fb81aa38 100644 --- a/spec/factories/systems.rb +++ b/spec/factories/systems.rb @@ -70,19 +70,6 @@ end end - trait :with_system_information_az do - system_information do - { - cpus: 2, - sockets: 1, - hypervisor: nil, - arch: 'x86_64', - uuid: SecureRandom.uuid, - cloud_provider: 'Microsoft' - }.to_json - end - end - trait :with_system_token do sequence(:system_token) { |n| "00000000-0000-4000-9000-#{n.to_s.rjust(12, '0')}" } end From f7c3ae5d01d1fd68d57fe479dbfe310d97018d0f Mon Sep 17 00:00:00 2001 From: Jesus Bermudez Velazquez Date: Mon, 25 Nov 2024 11:26:36 +0000 Subject: [PATCH 079/103] Send the instance ID in the token for Azure When activating an extension, i.e. LTSS in Azure, the header must contain the the instance identifier This Fixes bsc#1233314 --- engines/scc_proxy/lib/scc_proxy/engine.rb | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/engines/scc_proxy/lib/scc_proxy/engine.rb b/engines/scc_proxy/lib/scc_proxy/engine.rb index e684dc910..10eede7bf 100644 --- a/engines/scc_proxy/lib/scc_proxy/engine.rb +++ b/engines/scc_proxy/lib/scc_proxy/engine.rb @@ -68,9 +68,10 @@ def get_instance_id(params) nil, params['instance_data'] ) - instance_id_key = INSTANCE_ID_KEYS[params['hwinfo']['cloud_provider'].downcase.to_sym] - iid = verification_provider.parse_instance_data - iid[instance_id_key] + csp = params['hwinfo']['cloud_provider'].downcase + instance_id_key = INSTANCE_ID_KEYS[csp.to_sym] + instance_data = verification_provider.parse_instance_data + csp.casecmp('microsoft').zero? ? instance_data['attestedData'][instance_id_key] : instance_data[instance_id_key] end def prepare_scc_announce_request(uri_path, auth, params) From ddcb893660e08ae49a81d66edaa55659d18ddfea Mon Sep 17 00:00:00 2001 From: Jesus Bermudez Velazquez Date: Mon, 25 Nov 2024 16:13:35 +0000 Subject: [PATCH 080/103] Add instance_identifier method In order to keep RMT implementation generic add a CSP specific method in the CSP instance verification class --- .../providers/example.rb | 8 ++++---- .../v3/systems/products_controller_spec.rb | 2 +- engines/scc_proxy/lib/scc_proxy/engine.rb | 20 ++++++------------- 3 files changed, 11 insertions(+), 19 deletions(-) diff --git a/engines/instance_verification/lib/instance_verification/providers/example.rb b/engines/instance_verification/lib/instance_verification/providers/example.rb index b773d7855..780c57884 100644 --- a/engines/instance_verification/lib/instance_verification/providers/example.rb +++ b/engines/instance_verification/lib/instance_verification/providers/example.rb @@ -27,10 +27,6 @@ def validate_instance_data(_instance_data) end def parse_instance_data - if @instance_data.include? '' - return { 'instance_data' => 'parsed_instance_data' } - end - if @instance_data.include?('SUSE') if @instance_data.include?('SAP') return { 'billingProducts' => nil, 'marketplaceProductCodes' => ['6789_SUSE_SAP'] } @@ -49,4 +45,8 @@ def payg_billing_code?(iid, identifier) return true if (identifier.casecmp('sles').zero? && instance_billing_info[:billing_product] == SLES_PRODUCT_IDENTIFIER) return true if (identifier.casecmp('sles_sap').zero? && SLES4SAP_PRODUCT_IDENTIFIER.include?(instance_billing_info[:marketplace_code])) end + + def instance_identifier + 'foo' + end end 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 9ccbcb1c2..40b079e2f 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 @@ -341,7 +341,7 @@ before do allow(InstanceVerification::Providers::Example).to receive(:new) .with(nil, nil, nil, instance_data).and_return(plugin_double) - allow(plugin_double).to receive(:parse_instance_data).and_return({ InstanceId: 'foo' }) + allow(plugin_double).to receive(:instance_identifier).and_return('foo') allow(InstanceVerification).to receive(:update_cache).with('127.0.0.1', system.login, product.id) FactoryBot.create(:subscription, product_classes: product_classes) diff --git a/engines/scc_proxy/lib/scc_proxy/engine.rb b/engines/scc_proxy/lib/scc_proxy/engine.rb index 10eede7bf..5da34dd14 100644 --- a/engines/scc_proxy/lib/scc_proxy/engine.rb +++ b/engines/scc_proxy/lib/scc_proxy/engine.rb @@ -43,7 +43,12 @@ class << self # rubocop:disable ThreadSafety/InstanceVariableInClassMethod def headers(auth, params) @instance_id = if params && params.class != String - get_instance_id(params) + InstanceVerification.provider.new( + nil, + nil, + nil, + params['instance_data'] + ).instance_identifier else # if it is not JSON, it is the system_token already # announce system has metadata @@ -61,19 +66,6 @@ def headers(auth, params) end # rubocop:enable ThreadSafety/InstanceVariableInClassMethod - def get_instance_id(params) - verification_provider = InstanceVerification.provider.new( - nil, - nil, - nil, - params['instance_data'] - ) - csp = params['hwinfo']['cloud_provider'].downcase - instance_id_key = INSTANCE_ID_KEYS[csp.to_sym] - instance_data = verification_provider.parse_instance_data - csp.casecmp('microsoft').zero? ? instance_data['attestedData'][instance_id_key] : instance_data[instance_id_key] - end - def prepare_scc_announce_request(uri_path, auth, params) scc_request = Net::HTTP::Post.new(uri_path, headers(auth, params)) From 40fb93f7803838d08a1b20614c71300d3eb4423b Mon Sep 17 00:00:00 2001 From: Jesus Bermudez Velazquez Date: Mon, 25 Nov 2024 16:16:18 +0000 Subject: [PATCH 081/103] Remove variable as it will be CSP specific --- engines/scc_proxy/lib/scc_proxy/engine.rb | 6 ------ 1 file changed, 6 deletions(-) diff --git a/engines/scc_proxy/lib/scc_proxy/engine.rb b/engines/scc_proxy/lib/scc_proxy/engine.rb index 5da34dd14..b6905d4a0 100644 --- a/engines/scc_proxy/lib/scc_proxy/engine.rb +++ b/engines/scc_proxy/lib/scc_proxy/engine.rb @@ -25,12 +25,6 @@ Net::HTTPRetriableError ].freeze -INSTANCE_ID_KEYS = { - amazon: 'instanceId', - google: 'instance_id', - microsoft: 'vmId' -}.freeze - # rubocop:disable Metrics/ModuleLength module SccProxy class << self From a778f30e302145ca2499cc2024a0c10c151db70f Mon Sep 17 00:00:00 2001 From: Jesus Bermudez Velazquez Date: Tue, 26 Nov 2024 10:00:14 +0000 Subject: [PATCH 082/103] Fix CI test --- .../v3/systems/products_controller_spec.rb | 33 ++++++++++++------- 1 file changed, 21 insertions(+), 12 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 da9f6bc4a..5d121c8f5 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 @@ -71,7 +71,7 @@ end context 'when verification provider returns false' do - # let(:plugin_double) { instance_double('InstanceVerification::Providers::Example') } + let(:plugin_double) { instance_double('InstanceVerification::Providers::Example') } before do stub_request(:post, scc_activate_url) @@ -80,8 +80,9 @@ body: { error: 'Unexpected instance verification error has occurred' }.to_json, headers: {} ) - # allow(InstanceVerification::Providers::Example).to receive(:new).and_return(plugin_double) - # allow(plugin_double).to receive(:allowed_extension?).and_return(true) + allow(InstanceVerification::Providers::Example).to receive(:new).and_return(plugin_double) + allow(plugin_double).to receive(:allowed_extension?).and_return(true) + allow(plugin_double).to receive(:instance_valid?).and_return(false) post url, params: payload, headers: headers end @@ -145,8 +146,9 @@ 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) + .with(be_a(ActiveSupport::Logger), be_a(ActionDispatch::Request), payload, instance_data).and_return(plugin_double).at_least(:once) expect(plugin_double).to receive(:instance_valid?).and_return(false) + allow(plugin_double).to receive(:allowed_extension?).and_return(true) post url, params: payload, headers: headers end @@ -159,8 +161,9 @@ 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) + .with(be_a(ActiveSupport::Logger), be_a(ActionDispatch::Request), payload, instance_data).and_return(plugin_double).at_least(:once) expect(plugin_double).to receive(:instance_valid?).and_raise('Custom plugin error') + allow(plugin_double).to receive(:allowed_extension?).and_return(true) post url, params: payload, headers: headers end @@ -175,9 +178,9 @@ 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) + .with(be_a(ActiveSupport::Logger), be_a(ActionDispatch::Request), payload, instance_data).and_return(plugin_double).at_least(:once) expect(plugin_double).to receive(:instance_valid?).and_raise(InstanceVerification::Exception, 'Custom plugin error') - + allow(plugin_double).to receive(:allowed_extension?).and_return(true) post url, params: payload, headers: headers end @@ -231,9 +234,9 @@ end before do - allow(InstanceVerification::Providers::Example).to receive(:new) - .with(nil, nil, nil, instance_data).and_return(plugin_double) + allow(InstanceVerification::Providers::Example).to receive(:new).and_return(plugin_double) allow(plugin_double).to receive(:parse_instance_data).and_return({ InstanceId: 'foo' }) + allow(plugin_double).to receive(:allowed_extension?).and_return(true) FactoryBot.create(:subscription, product_classes: product_classes) stub_request(:post, scc_activate_url) @@ -403,7 +406,7 @@ .to_return(status: 201, body: scc_response_body, headers: {}) expect(InstanceVerification).not_to receive(:update_cache).with('127.0.0.1', system.login, product.id) - + allow(plugin_double).to receive(:allowed_extension?).and_return(true) post url, params: payload_no_token, headers: headers end @@ -415,6 +418,10 @@ end context 'when the system is hybrid' do + before do + allow_any_instance_of(InstanceVerification::Providers::Example).to receive(:allowed_extension?).and_return(true) + end + context "when system doesn't have hw_info" do let(:system) { FactoryBot.create(:system, :hybrid) } @@ -448,7 +455,8 @@ 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) + .with(be_a(ActiveSupport::Logger), be_a(ActionDispatch::Request), payload, instance_data).and_return(plugin_double).at_least(:once) + allow(plugin_double).to receive(:allowed_extension?).and_return(true) expect(plugin_double).to receive(:instance_valid?).and_return(false) post url, params: payload, headers: headers end @@ -462,7 +470,8 @@ 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) + .with(be_a(ActiveSupport::Logger), be_a(ActionDispatch::Request), payload, instance_data).and_return(plugin_double).at_least(:once) + allow(plugin_double).to receive(:allowed_extension?).and_return(true) expect(plugin_double).to receive(:instance_valid?).and_raise('Custom plugin error') post url, params: payload, headers: headers end From 3e03f2261af9e9d60bc884877229fc6404a2530b Mon Sep 17 00:00:00 2001 From: Jesus Bermudez Velazquez Date: Tue, 26 Nov 2024 17:07:18 +0000 Subject: [PATCH 083/103] Allowing future SUMA versions in the check --- .../strict_authentication/authentication_controller.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/engines/strict_authentication/app/controllers/strict_authentication/authentication_controller.rb b/engines/strict_authentication/app/controllers/strict_authentication/authentication_controller.rb index ec671999b..a0109ad34 100644 --- a/engines/strict_authentication/app/controllers/strict_authentication/authentication_controller.rb +++ b/engines/strict_authentication/app/controllers/strict_authentication/authentication_controller.rb @@ -45,7 +45,7 @@ def all_allowed_paths(headers) # SUMA 5.0 must have access to SUMA 4.3, 4.2 and so on micro = p.identifier.downcase.include?('sle-micro') instance_id_header = headers.fetch('X-Instance-Identifier', '').casecmp('suse-manager-server').zero? - instance_version_header = headers.fetch('X-Instance-Version', '') == '5.0' + instance_version_header = headers.fetch('X-Instance-Version', '0').split('.')[0] >= '5' manager || (micro && instance_id_header && instance_version_header) end From 6b8f7b2375368cfbf16caa768c0d3213f4a59449 Mon Sep 17 00:00:00 2001 From: Jesus Bermudez Velazquez Date: Wed, 27 Nov 2024 10:39:11 +0000 Subject: [PATCH 084/103] Simpler and clearer check --- app/controllers/api/connect/v3/systems/products_controller.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/api/connect/v3/systems/products_controller.rb b/app/controllers/api/connect/v3/systems/products_controller.rb index 62f5d9508..5688cc9b4 100644 --- a/app/controllers/api/connect/v3/systems/products_controller.rb +++ b/app/controllers/api/connect/v3/systems/products_controller.rb @@ -12,7 +12,7 @@ def activate end def show - if @product.identifier.casecmp('sles').zero? + if @product.identifier.casecmp?('sles') # if system has SLE Micro # it should access to SLES products sle_micro = @system.products.any? { |p| p.identifier.downcase.include?('sle-micro') } From a7e6212241ea58dbcfbe40fa930dff0cb72a735a Mon Sep 17 00:00:00 2001 From: Jesus Bermudez Velazquez Date: Wed, 27 Nov 2024 11:06:08 +0000 Subject: [PATCH 085/103] Fix linter --- .../strict_authentication/authentication_controller.rb | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/engines/strict_authentication/app/controllers/strict_authentication/authentication_controller.rb b/engines/strict_authentication/app/controllers/strict_authentication/authentication_controller.rb index 966475c1b..b3fabfab6 100644 --- a/engines/strict_authentication/app/controllers/strict_authentication/authentication_controller.rb +++ b/engines/strict_authentication/app/controllers/strict_authentication/authentication_controller.rb @@ -28,6 +28,8 @@ def path_allowed?(headers) all_allowed_paths(headers).find { |allowed_path| path =~ /^#{Regexp.escape(allowed_path)}/ } end + # rubocop:disable Metrics/CyclomaticComplexity + # rubocop:disable Metrics/PerceivedComplexity def all_allowed_paths(headers) # return all versions of the same product and arch # (that the system has available with that subscription) @@ -71,5 +73,7 @@ def all_allowed_paths(headers) end allowed_paths end + # rubocop:enable Metrics/CyclomaticComplexity + # rubocop:enable Metrics/PerceivedComplexity end end From a4697936fe2f02dc004468dd4f67a7975a1136ed Mon Sep 17 00:00:00 2001 From: Jesus Bermudez Velazquez Date: Fri, 29 Nov 2024 10:59:54 +0000 Subject: [PATCH 086/103] Add changes for v2.20 for pubcloud --- package/obs/rmt-server.changes | 3 +++ 1 file changed, 3 insertions(+) diff --git a/package/obs/rmt-server.changes b/package/obs/rmt-server.changes index ed80834ce..cb1e13732 100644 --- a/package/obs/rmt-server.changes +++ b/package/obs/rmt-server.changes @@ -8,6 +8,9 @@ Wed Oct 30 09:01:32 UTC 2024 - Natnael Getahun * rmt-server-pubcloud: * Fix LTSS product verification (bsc#1230154) * Fix activations check when no product info is available (bsc#1230157) + * Fix Azure SCC connection (bsc#1233314) + * Deny access to Azure Basic type images to LTSS + * Allow SLE Micro system to access free SLES repositories (bsc#1230419) ------------------------------------------------------------------- Wed Aug 21 15:28:43 UTC 2024 - Jesús Bermúdez Velázquez From bef95d6ce357a2da9f37ac4049b77655941dc64d Mon Sep 17 00:00:00 2001 From: Natnael Getahun Date: Fri, 29 Nov 2024 14:47:38 +0100 Subject: [PATCH 087/103] Update SCC host configuration to use environment variable --- config/rmt.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/rmt.yml b/config/rmt.yml index 811e44ff2..b1f7e6c6e 100644 --- a/config/rmt.yml +++ b/config/rmt.yml @@ -20,7 +20,7 @@ database_test: database: rmt_test scc: - host: https://scc.suse.com/connect + host: <%= ENV.fetch('SCC_HOST'){ 'https://scc.suse.com/connect' } username: <%= ENV['SCC_USERNAME'] %> password: <%= ENV['SCC_PASSWORD'] %> sync_systems: true From 608defaf0f4d45675219c118aa3a13aa0207850b Mon Sep 17 00:00:00 2001 From: Natnael Getahun Date: Fri, 29 Nov 2024 14:47:38 +0100 Subject: [PATCH 088/103] Update SCC host configuration to use environment variable --- config/rmt.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/rmt.yml b/config/rmt.yml index b1f7e6c6e..6a2df5b5c 100644 --- a/config/rmt.yml +++ b/config/rmt.yml @@ -20,7 +20,7 @@ database_test: database: rmt_test scc: - host: <%= ENV.fetch('SCC_HOST'){ 'https://scc.suse.com/connect' } + host: <%= ENV.fetch('SCC_HOST'){ 'https://scc.suse.com/connect' } %> username: <%= ENV['SCC_USERNAME'] %> password: <%= ENV['SCC_PASSWORD'] %> sync_systems: true From 2728fc73333596f9711e9862d71ab0a900d523f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Berm=C3=BAdez=20Vel=C3=A1zquez?= Date: Mon, 2 Dec 2024 08:33:36 +0000 Subject: [PATCH 089/103] Update package/obs/rmt-server.changes Co-authored-by: Thomas Schmidt --- package/obs/rmt-server.changes | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package/obs/rmt-server.changes b/package/obs/rmt-server.changes index cb1e13732..cbee6e2ff 100644 --- a/package/obs/rmt-server.changes +++ b/package/obs/rmt-server.changes @@ -9,7 +9,7 @@ Wed Oct 30 09:01:32 UTC 2024 - Natnael Getahun * Fix LTSS product verification (bsc#1230154) * Fix activations check when no product info is available (bsc#1230157) * Fix Azure SCC connection (bsc#1233314) - * Deny access to Azure Basic type images to LTSS + * Deny access of Azure Basic type images to LTSS * Allow SLE Micro system to access free SLES repositories (bsc#1230419) ------------------------------------------------------------------- From acde3707577044a65b12f5910a7bd0a58498bc86 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Berm=C3=BAdez=20Vel=C3=A1zquez?= Date: Mon, 2 Dec 2024 08:33:47 +0000 Subject: [PATCH 090/103] Update package/obs/rmt-server.changes Co-authored-by: Thomas Schmidt --- package/obs/rmt-server.changes | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package/obs/rmt-server.changes b/package/obs/rmt-server.changes index cbee6e2ff..16e598617 100644 --- a/package/obs/rmt-server.changes +++ b/package/obs/rmt-server.changes @@ -10,7 +10,7 @@ Wed Oct 30 09:01:32 UTC 2024 - Natnael Getahun * Fix activations check when no product info is available (bsc#1230157) * Fix Azure SCC connection (bsc#1233314) * Deny access of Azure Basic type images to LTSS - * Allow SLE Micro system to access free SLES repositories (bsc#1230419) + * Allow SLE Micro system to access SLES repositories (bsc#1230419) ------------------------------------------------------------------- Wed Aug 21 15:28:43 UTC 2024 - Jesús Bermúdez Velázquez From 0e430ea67dbf4eba08648a56734b7edabfbc6385 Mon Sep 17 00:00:00 2001 From: Jesus Bermudez Velazquez Date: Mon, 2 Dec 2024 10:34:30 +0000 Subject: [PATCH 091/103] Fix typo in comment --- .../lib/instance_verification/providers/example.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/engines/instance_verification/lib/instance_verification/providers/example.rb b/engines/instance_verification/lib/instance_verification/providers/example.rb index 393236e4b..ac1523135 100644 --- a/engines/instance_verification/lib/instance_verification/providers/example.rb +++ b/engines/instance_verification/lib/instance_verification/providers/example.rb @@ -52,7 +52,7 @@ def instance_identifier def allowed_extension? # method to check if a product (extension) meet the criteria - # to be acivated on SCC or not, i.e. LTSS in Azure Basic VM + # to be activated on SCC or not, i.e. LTSS in Azure Basic VM true end end From 7002ee6124d5c93c6bbe72c141ba1f35c2551fd3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Berm=C3=BAdez=20Vel=C3=A1zquez?= Date: Mon, 2 Dec 2024 12:18:47 +0000 Subject: [PATCH 092/103] Update engines/instance_verification/lib/instance_verification/providers/example.rb Co-authored-by: Thomas Schmidt --- .../lib/instance_verification/providers/example.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/engines/instance_verification/lib/instance_verification/providers/example.rb b/engines/instance_verification/lib/instance_verification/providers/example.rb index ac1523135..e4379c39b 100644 --- a/engines/instance_verification/lib/instance_verification/providers/example.rb +++ b/engines/instance_verification/lib/instance_verification/providers/example.rb @@ -51,7 +51,7 @@ def instance_identifier end def allowed_extension? - # method to check if a product (extension) meet the criteria + # method to check if a product (extension) meets the criteria # to be activated on SCC or not, i.e. LTSS in Azure Basic VM true end From 798ecf9f02d5c2a2c28216f7a4dd7c060a1107f7 Mon Sep 17 00:00:00 2001 From: "parag.jain" Date: Mon, 2 Dec 2024 20:13:17 +0530 Subject: [PATCH 093/103] skip rotation for read Apis --- .../api/connect/base_controller.rb | 8 ++++++++ .../connect/v3/systems/products_controller.rb | 1 + .../connect/v3/systems/systems_controller.rb | 1 + .../connect/v4/systems/products_controller.rb | 1 + app/controllers/application_controller.rb | 6 +++--- .../api/connect/base_controller_spec.rb | 19 +++---------------- 6 files changed, 17 insertions(+), 19 deletions(-) diff --git a/app/controllers/api/connect/base_controller.rb b/app/controllers/api/connect/base_controller.rb index 1fbeaff07..1983fb267 100644 --- a/app/controllers/api/connect/base_controller.rb +++ b/app/controllers/api/connect/base_controller.rb @@ -44,4 +44,12 @@ def authenticate_with_token end end + def system_token_header + headers[SYSTEM_TOKEN_HEADER] = @system.system_token + end + + def refresh_system_token + @system.update(system_token: SecureRandom.uuid) + system_token_header + end end diff --git a/app/controllers/api/connect/v3/systems/products_controller.rb b/app/controllers/api/connect/v3/systems/products_controller.rb index 2545a9e92..fcad5de58 100644 --- a/app/controllers/api/connect/v3/systems/products_controller.rb +++ b/app/controllers/api/connect/v3/systems/products_controller.rb @@ -5,6 +5,7 @@ class Api::Connect::V3::Systems::ProductsController < Api::Connect::BaseControll before_action :check_product_service_and_repositories, only: %i[show activate] before_action :load_subscription, only: %i[activate upgrade] before_action :check_base_product_dependencies, only: %i[activate upgrade show] + after_action :refresh_system_token, only: %i[activate upgrade], if: -> { request.headers.key?(SYSTEM_TOKEN_HEADER) } def activate create_product_activation diff --git a/app/controllers/api/connect/v3/systems/systems_controller.rb b/app/controllers/api/connect/v3/systems/systems_controller.rb index 863557279..d9a17154d 100644 --- a/app/controllers/api/connect/v3/systems/systems_controller.rb +++ b/app/controllers/api/connect/v3/systems/systems_controller.rb @@ -1,6 +1,7 @@ class Api::Connect::V3::Systems::SystemsController < Api::Connect::BaseController before_action :authenticate_system + after_action :refresh_system_token, only: [:update], if: -> { request.headers.key?(SYSTEM_TOKEN_HEADER) } def update if params[:online_at].present? diff --git a/app/controllers/api/connect/v4/systems/products_controller.rb b/app/controllers/api/connect/v4/systems/products_controller.rb index 7237e5a80..c92df74d5 100644 --- a/app/controllers/api/connect/v4/systems/products_controller.rb +++ b/app/controllers/api/connect/v4/systems/products_controller.rb @@ -1,5 +1,6 @@ class Api::Connect::V4::Systems::ProductsController < Api::Connect::V3::Systems::ProductsController + after_action :refresh_system_token, only: %i[synchronize destroy], if: -> { request.headers.key?(SYSTEM_TOKEN_HEADER) } def destroy if @product.base? raise ActionController::TranslatedError.new(N_('The product "%s" is a base product and cannot be deactivated'), @product.name) diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 686972895..ba11c3a75 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -21,11 +21,11 @@ def authenticate_system(skip_on_duplicated: false) 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. + # that supports this feature. if system_tokens_enabled? && request.headers.key?(SYSTEM_TOKEN_HEADER) - @system.update(last_seen_at: Time.zone.now, system_token: SecureRandom.uuid) + @system.update(last_seen_at: Time.zone.now) headers[SYSTEM_TOKEN_HEADER] = @system.system_token - # only update last_seen_at each 3 minutes, + # only update last_seen_at each 3 minutes, # so that a system that calls SCC every second doesn't write + lock the database row elsif !@system.last_seen_at || @system.last_seen_at < 3.minutes.ago @system.touch(:last_seen_at) diff --git a/spec/requests/api/connect/base_controller_spec.rb b/spec/requests/api/connect/base_controller_spec.rb index 70ef9aaff..76b1f4b6b 100644 --- a/spec/requests/api/connect/base_controller_spec.rb +++ b/spec/requests/api/connect/base_controller_spec.rb @@ -55,15 +55,6 @@ def require_product end end - shared_examples 'updates the system token' do - it 'updates the system token' do - allow(SecureRandom).to receive(:uuid).and_return(new_system_token) - - expect { get :service, params: { id: 1 } } - .to change { system.reload.system_token } - .from(current_system_token).to(new_system_token) - end - end shared_examples "does not update the old system's token" do it 'does not update the system token' do @@ -74,7 +65,6 @@ def require_product shared_examples 'creates a duplicate system' do it 'creates a new System (duplicate)' do - allow(SecureRandom).to receive(:uuid).and_return(new_system_token) expect { get :service, params: { id: 1 } } .to change { System.get_by_credentials(system.login, system.password).count } @@ -85,7 +75,6 @@ def require_product expect(duplicate_system).not_to eq(system) expect(duplicate_system.activations.count).to eq(system.activations.count) expect(duplicate_system.system_token).not_to eq(system.system_token) - expect(duplicate_system.system_token).to eq(new_system_token) end end @@ -182,8 +171,7 @@ def require_product let(:system) { create(:system, hostname: 'system') } include_examples 'does not create a duplicate system' - include_examples 'updates the system token' - include_examples 'responds with a new token' + include_examples "does not update the old system's token" end context 'when the system has a token and the header matches it' do @@ -193,8 +181,8 @@ def require_product let(:system) { create(:system, hostname: 'system', system_token: current_system_token) } include_examples 'does not create a duplicate system' - include_examples 'updates the system token' - include_examples 'responds with a new token' + include_examples "does not update the old system's token" + end context 'when the system has a token and the header is blank' do @@ -208,7 +196,6 @@ def require_product include_examples "does not update the old system's token" include_examples 'creates a duplicate system' - include_examples 'responds with a new token' end context 'when the system has a token and the header does not match it' do From 3f5a6e8d3d8fcf2b8ff7503e26a37652718642dd Mon Sep 17 00:00:00 2001 From: Thomas Schmidt Date: Wed, 4 Dec 2024 00:09:08 +0100 Subject: [PATCH 094/103] fix rubocop --- app/controllers/application_controller.rb | 2 +- spec/requests/api/connect/base_controller_spec.rb | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index ba11c3a75..bb7504888 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -25,7 +25,7 @@ def authenticate_system(skip_on_duplicated: false) if system_tokens_enabled? && request.headers.key?(SYSTEM_TOKEN_HEADER) @system.update(last_seen_at: Time.zone.now) headers[SYSTEM_TOKEN_HEADER] = @system.system_token - # only update last_seen_at each 3 minutes, + # only update last_seen_at each 3 minutes, # so that a system that calls SCC every second doesn't write + lock the database row elsif !@system.last_seen_at || @system.last_seen_at < 3.minutes.ago @system.touch(:last_seen_at) diff --git a/spec/requests/api/connect/base_controller_spec.rb b/spec/requests/api/connect/base_controller_spec.rb index 76b1f4b6b..8832909ea 100644 --- a/spec/requests/api/connect/base_controller_spec.rb +++ b/spec/requests/api/connect/base_controller_spec.rb @@ -65,7 +65,6 @@ def require_product shared_examples 'creates a duplicate system' do it 'creates a new System (duplicate)' do - expect { get :service, params: { id: 1 } } .to change { System.get_by_credentials(system.login, system.password).count } .by(1) @@ -182,7 +181,6 @@ def require_product include_examples 'does not create a duplicate system' include_examples "does not update the old system's token" - end context 'when the system has a token and the header is blank' do From 59131b6353fd1b2c29a17a4f158e34306dbfcf8f Mon Sep 17 00:00:00 2001 From: "parag.jain" Date: Wed, 4 Dec 2024 12:22:50 +0530 Subject: [PATCH 095/103] add testcases supporting token rotation for write api; add testcases for header token addition in read api --- app/controllers/application_controller.rb | 2 +- .../v3/systems/activations_controller_spec.rb | 23 ++++++ .../v3/systems/products_controller_spec.rb | 55 ++++++++++++++ .../v3/systems/systems_controller_spec.rb | 19 +++++ .../v4/systems/products_controller_spec.rb | 73 +++++++++++++++++++ 5 files changed, 171 insertions(+), 1 deletion(-) diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index bb7504888..63458615a 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -24,7 +24,7 @@ def authenticate_system(skip_on_duplicated: false) # that supports this feature. if system_tokens_enabled? && request.headers.key?(SYSTEM_TOKEN_HEADER) @system.update(last_seen_at: Time.zone.now) - headers[SYSTEM_TOKEN_HEADER] = @system.system_token + system_token_header # only update last_seen_at each 3 minutes, # so that a system that calls SCC every second doesn't write + lock the database row elsif !@system.last_seen_at || @system.last_seen_at < 3.minutes.ago diff --git a/spec/requests/api/connect/v3/systems/activations_controller_spec.rb b/spec/requests/api/connect/v3/systems/activations_controller_spec.rb index a4ff0a562..6a62ee2bd 100644 --- a/spec/requests/api/connect/v3/systems/activations_controller_spec.rb +++ b/spec/requests/api/connect/v3/systems/activations_controller_spec.rb @@ -69,5 +69,28 @@ expect(system.scc_synced_at).to be_nil end end + + context 'system token header' do + context 'when system token header is present in request' do + let(:token_headers) do + authenticated_headers.merge({ 'System-Token' => 'some_token' }) + end + + it 'sets system token in response headers' do + get url, headers: token_headers + expect(response.code).to eq '200' + expect(response.headers).to include('System-Token') + expect(response.headers['System-Token']).not_to be_nil + expect(response.headers['System-Token']).not_to be_empty + end + + it 'does not set system token header if no system token header in request' do + get url, headers: authenticated_headers + + expect(response.code).to eq '200' + expect(response.headers).not_to include('System-Token') + end + end + end end end diff --git a/spec/requests/api/connect/v3/systems/products_controller_spec.rb b/spec/requests/api/connect/v3/systems/products_controller_spec.rb index 457a77d65..9f6c47916 100644 --- a/spec/requests/api/connect/v3/systems/products_controller_spec.rb +++ b/spec/requests/api/connect/v3/systems/products_controller_spec.rb @@ -135,6 +135,26 @@ end end + shared_context 'activate with token in request headers' do + let(:payload) do + { + identifier: product.identifier, + version: product.version, + arch: product.arch, + token: regcode + } + end + + before { post url, headers: { 'System-Token' => 'existing_token' }.merge(headers), params: payload } + subject do + Struct.new(:body, :code, :headers).new( + JSON.parse(response.body, symbolize_names: true), + response.status, + response.headers + ) + end + end + context 'unknown subscription' do include_context 'with subscriptions' let(:regcode) { 'NOT-EXISTING-SUBSCRIPTION' } @@ -173,6 +193,17 @@ expect(activation.product).to eq(product) end end + + context 'token update after activation is success' do + let(:subscription) { create :subscription, :with_products } + let(:product) { subscription.products.first } + let(:regcode) { subscription.regcode } + + include_context 'activate with token in request headers' + its(:code) { is_expected.to eq(201) } + its(:headers) { is_expected.to include('System-Token') } + its(:headers['System-Token']) { is_expected.not_to eq('existing_token') } + end end end @@ -225,6 +256,18 @@ its(:body) { is_expected.to eq(serialized_json) } end + describe 'response header should contain token' do + subject { response } + + let(:token_headers) do + headers.merge({ 'System-Token' => 'some_token' }) + end + + before { get url, headers: token_headers, params: payload } + its(:code) { is_expected.to eq('200') } + its(:headers) { is_expected.to include('System-Token') } + end + describe 'response with "-" in version' do subject { response } @@ -339,6 +382,18 @@ system.reload end + it 'calls refresh_system_token after upgrade action when system token header is present' do + put url, headers: headers.merge('System-Token' => 'test_token'), params: payload + expect(response.code).to eq('201') + expect(response.headers).to include('System-Token') + expect(response.headers['System-Token']).not_to eq('test_token') + end + + it 'No update in token after upgrade action when system token header is absent' do + put url, headers: headers, params: payload + expect(response.code).to eq('201') + expect(response.headers).not_to include('System-Token') + end context 'new product' do its(:code) { is_expected.to eq('201') } its(:body) { is_expected.to eq(serialized_json) } 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 95eede620..91f73d4f4 100644 --- a/spec/requests/api/connect/v3/systems/systems_controller_spec.rb +++ b/spec/requests/api/connect/v3/systems/systems_controller_spec.rb @@ -125,6 +125,25 @@ expect(system.reload.system_information_hash[:user_agent]).to be_nil end end + + context 'response header should contain token' do + let(:headers) { auth_header.merge('System-Token': 'existing-token') } + + it 'contains refreshed token in response' do + update_action + expect(response.headers).to include('System-Token') + expect(response.headers['System-Token']).not_to equal('existing-token') + end + end + + context 'response header should not contain token' do + let(:headers) { auth_header } + + it 'contains refreshed token in response' do + update_action + expect(response.headers).not_to include('System-Token') + end + end end describe '#deregister' do diff --git a/spec/requests/api/connect/v4/systems/products_controller_spec.rb b/spec/requests/api/connect/v4/systems/products_controller_spec.rb index 8bf9a64f4..6f960a941 100644 --- a/spec/requests/api/connect/v4/systems/products_controller_spec.rb +++ b/spec/requests/api/connect/v4/systems/products_controller_spec.rb @@ -140,4 +140,77 @@ end end end + + describe 'system token refresh' do + let(:system) { FactoryBot.create(:system, :with_activated_base_product) } + let(:headers) { auth_header.merge(version_header).merge('System-Token' => 'existing_token') } + + context 'token refresh for destroy action' do + let(:product) { FactoryBot.create(:product, :extension, :with_mirrored_repositories, :activated, system: system) } + let(:payload) { { identifier: product.identifier, version: product.version, arch: product.arch } } + + it 'refreshes system token when System-Token header is present' do + delete connect_systems_products_url, + headers: headers, + params: payload + + expect(response.status).to eq(200) + expect(response.headers).to include('System-Token') + expect(response.headers['System-Token']).not_to eq('existing_token') + end + + it 'does not refresh token when System-Token header is absent' do + headers_without_token = auth_header.merge(version_header) + expect_any_instance_of(described_class).not_to receive(:refresh_system_token) + + delete connect_systems_products_url, + headers: headers_without_token, + params: payload + + expect(response.status).to eq(200) + expect(response.headers).not_to include('System-Token') + end + end + + context 'token refresh for synchronize action' do + let(:path) { '/connect/systems/products/synchronize' } + + it 'refreshes system token when System-Token header is present' do + params = system.products.map do |product| + { + identifier: product.identifier, + version: product.version, + arch: product.arch, + release_type: product.release_type + } + end + post path, + params: { products: params }, + headers: headers + + expect(response.status).to eq(200) + expect(response.headers).to include('System-Token') + expect(response.headers['System-Token']).not_to eq('existing_token') + end + + it 'does not refresh token when System-Token header is absent' do + headers_without_token = auth_header.merge(version_header) + + params = system.products.map do |product| + { + identifier: product.identifier, + version: product.version, + arch: product.arch, + release_type: product.release_type + } + end + post path, + params: { products: params }, + headers: headers_without_token + + expect(response.status).to eq(200) + expect(response.headers).not_to include('System-Token') + end + end + end end From c0be53cd3e758fa7ba9b026d6d5e64c332f81c31 Mon Sep 17 00:00:00 2001 From: "parag.jain" Date: Wed, 4 Dec 2024 15:41:57 +0530 Subject: [PATCH 096/103] add system token enable check --- app/controllers/api/connect/base_controller.rb | 6 ++++-- .../api/connect/v4/systems/products_controller.rb | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/app/controllers/api/connect/base_controller.rb b/app/controllers/api/connect/base_controller.rb index 1983fb267..84de46574 100644 --- a/app/controllers/api/connect/base_controller.rb +++ b/app/controllers/api/connect/base_controller.rb @@ -49,7 +49,9 @@ def system_token_header end def refresh_system_token - @system.update(system_token: SecureRandom.uuid) - system_token_header + if system_tokens_enabled? + @system.update(system_token: SecureRandom.uuid) + system_token_header + end end end diff --git a/app/controllers/api/connect/v4/systems/products_controller.rb b/app/controllers/api/connect/v4/systems/products_controller.rb index c92df74d5..d4c87adfc 100644 --- a/app/controllers/api/connect/v4/systems/products_controller.rb +++ b/app/controllers/api/connect/v4/systems/products_controller.rb @@ -1,6 +1,6 @@ class Api::Connect::V4::Systems::ProductsController < Api::Connect::V3::Systems::ProductsController - after_action :refresh_system_token, only: %i[synchronize destroy], if: -> { request.headers.key?(SYSTEM_TOKEN_HEADER) } + after_action :refresh_system_token, only: %i[activate upgrade synchronize destroy], if: -> { request.headers.key?(SYSTEM_TOKEN_HEADER) } def destroy if @product.base? raise ActionController::TranslatedError.new(N_('The product "%s" is a base product and cannot be deactivated'), @product.name) From 07961c69f578ae92e5b5163d2a8a1221753d65cf Mon Sep 17 00:00:00 2001 From: "parag.jain" Date: Thu, 5 Dec 2024 18:26:05 +0530 Subject: [PATCH 097/103] Add entry to .changes file for skipping system token rotation in read-only APIs --- package/obs/rmt-server.changes | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package/obs/rmt-server.changes b/package/obs/rmt-server.changes index ed80834ce..6e048aba3 100644 --- a/package/obs/rmt-server.changes +++ b/package/obs/rmt-server.changes @@ -8,7 +8,7 @@ Wed Oct 30 09:01:32 UTC 2024 - Natnael Getahun * rmt-server-pubcloud: * Fix LTSS product verification (bsc#1230154) * Fix activations check when no product info is available (bsc#1230157) - + * Skip system token rotation in read-only APIs ------------------------------------------------------------------- Wed Aug 21 15:28:43 UTC 2024 - Jesús Bermúdez Velázquez From 992c0a0b23121a70f82af8b26ffebdd7cb797ac2 Mon Sep 17 00:00:00 2001 From: "parag.jain" Date: Wed, 11 Dec 2024 13:02:53 +0530 Subject: [PATCH 098/103] Refactor SUSE repo cleanup and local path generation logic supporting akamai url --- app/models/repository.rb | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/app/models/repository.rb b/app/models/repository.rb index 6a7719aa2..6fedd7945 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -23,14 +23,16 @@ class Repository < ApplicationRecord class << self def remove_suse_repos_without_tokens! - where(auth_token: nil).where('external_url LIKE ?', 'https://updates.suse.com%').delete_all + where(auth_token: nil) + .where("external_url LIKE 'https://updates.suse.com%' OR external_url LIKE 'https://dl.suse.com%'") + .delete_all end # Mangles remote repo URL to make a nicer local path, see specs for examples def make_local_path(url) uri = URI(url) path = uri.path.to_s - path.gsub!(%r{^/repo}, '') if (uri.hostname == 'updates.suse.com') + path.gsub!(%r{^/repo}, '') if (uri.hostname == 'updates.suse.com' || uri.hostname == 'dl.suse.com') (path == '') ? '/' : path end From caa983103853f35b895d95dd7290c8b0bbc41834 Mon Sep 17 00:00:00 2001 From: "parag.jain" Date: Thu, 12 Dec 2024 17:50:37 +0530 Subject: [PATCH 099/103] remove function remove_suse_repos_without_tokens!; --- app/models/repository.rb | 10 ++-------- lib/rmt/scc.rb | 6 ------ spec/lib/rmt/scc_spec.rb | 4 ---- 3 files changed, 2 insertions(+), 18 deletions(-) diff --git a/app/models/repository.rb b/app/models/repository.rb index 6fedd7945..ff09674e0 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -21,18 +21,12 @@ class Repository < ApplicationRecord before_destroy :ensure_destroy_possible class << self - - def remove_suse_repos_without_tokens! - where(auth_token: nil) - .where("external_url LIKE 'https://updates.suse.com%' OR external_url LIKE 'https://dl.suse.com%'") - .delete_all - end - # Mangles remote repo URL to make a nicer local path, see specs for examples def make_local_path(url) uri = URI(url) path = uri.path.to_s - path.gsub!(%r{^/repo}, '') if (uri.hostname == 'updates.suse.com' || uri.hostname == 'dl.suse.com') + # drop '/repo' from SLE11 paths, to avoid double /repo/repo in local storage path. + path.gsub!(%r{^/repo/\$RCE/}, '/$RCE/') (path == '') ? '/' : path end diff --git a/lib/rmt/scc.rb b/lib/rmt/scc.rb index 59fccdf2a..63cbe5106 100644 --- a/lib/rmt/scc.rb +++ b/lib/rmt/scc.rb @@ -24,9 +24,6 @@ def sync data.each { |item| migration_paths(item) } update_repositories(scc_api_client.list_repositories) - - Repository.remove_suse_repos_without_tokens! - update_subscriptions(scc_api_client.list_subscriptions) end @@ -68,9 +65,6 @@ def import(path) data.each { |item| migration_paths(item) } update_repositories(JSON.parse(File.read(File.join(path, 'organizations_repositories.json')), symbolize_names: true)) - - Repository.remove_suse_repos_without_tokens! - update_subscriptions(JSON.parse(File.read(File.join(path, 'organizations_subscriptions.json')), symbolize_names: true)) end diff --git a/spec/lib/rmt/scc_spec.rb b/spec/lib/rmt/scc_spec.rb index 802cf1c8a..4b62370fb 100644 --- a/spec/lib/rmt/scc_spec.rb +++ b/spec/lib/rmt/scc_spec.rb @@ -290,10 +290,6 @@ def scc expect { suse_repo_with_token.reload }.not_to raise_error end - it 'SUSE repos without auth_tokens are removed' do - expect { suse_repo_without_token.reload }.to raise_error(ActiveRecord::RecordNotFound) - end - it 'other repos without auth_tokens persist' do expect { other_repo_without_token.reload }.not_to raise_error end From d81ee4b82a8591e2f56203236418116524a6fb74 Mon Sep 17 00:00:00 2001 From: "parag.jain" Date: Mon, 16 Dec 2024 14:08:21 +0530 Subject: [PATCH 100/103] Refactor SUSE repo cleanup and local path generation logic supporting akamai url --- app/models/repository.rb | 5 +++++ lib/rmt/scc.rb | 6 ++++++ spec/lib/rmt/scc_spec.rb | 7 ++++++- 3 files changed, 17 insertions(+), 1 deletion(-) diff --git a/app/models/repository.rb b/app/models/repository.rb index ff09674e0..347ec5548 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -21,6 +21,11 @@ class Repository < ApplicationRecord before_destroy :ensure_destroy_possible class << self + + def remove_suse_repos_without_tokens! + where(auth_token: nil).where("external_url LIKE '%.suse.com%'").where(installer_updates: 0).delete_all + end + # Mangles remote repo URL to make a nicer local path, see specs for examples def make_local_path(url) uri = URI(url) diff --git a/lib/rmt/scc.rb b/lib/rmt/scc.rb index 63cbe5106..59fccdf2a 100644 --- a/lib/rmt/scc.rb +++ b/lib/rmt/scc.rb @@ -24,6 +24,9 @@ def sync data.each { |item| migration_paths(item) } update_repositories(scc_api_client.list_repositories) + + Repository.remove_suse_repos_without_tokens! + update_subscriptions(scc_api_client.list_subscriptions) end @@ -65,6 +68,9 @@ def import(path) data.each { |item| migration_paths(item) } update_repositories(JSON.parse(File.read(File.join(path, 'organizations_repositories.json')), symbolize_names: true)) + + Repository.remove_suse_repos_without_tokens! + update_subscriptions(JSON.parse(File.read(File.join(path, 'organizations_subscriptions.json')), symbolize_names: true)) end diff --git a/spec/lib/rmt/scc_spec.rb b/spec/lib/rmt/scc_spec.rb index 496730e56..a62077d00 100644 --- a/spec/lib/rmt/scc_spec.rb +++ b/spec/lib/rmt/scc_spec.rb @@ -261,7 +261,8 @@ :repository, :with_products, auth_token: nil, - external_url: 'https://example.com/repos/not/updates.suse.com/' + external_url: 'https://installer-updates.suse.com/repos/not/updates', + installer_updates: true ) end @@ -290,6 +291,10 @@ def scc expect { suse_repo_with_token.reload }.not_to raise_error end + it 'SUSE repos without auth_tokens are removed' do + expect { suse_repo_without_token.reload }.to raise_error(ActiveRecord::RecordNotFound) + end + it 'other repos without auth_tokens persist' do expect { other_repo_without_token.reload }.not_to raise_error end From 3503590a1b8e7128b707fd32e15dc1eba985d687 Mon Sep 17 00:00:00 2001 From: "parag.jain" Date: Mon, 16 Dec 2024 16:41:35 +0530 Subject: [PATCH 101/103] add check for custom repos --- app/models/repository.rb | 2 +- spec/lib/rmt/scc_spec.rb | 14 ++++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/app/models/repository.rb b/app/models/repository.rb index 347ec5548..c65c602cd 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -23,7 +23,7 @@ class Repository < ApplicationRecord class << self def remove_suse_repos_without_tokens! - where(auth_token: nil).where("external_url LIKE '%.suse.com%'").where(installer_updates: 0).delete_all + where(auth_token: nil).where("external_url LIKE '%.suse.com%'").where(installer_updates: 0).where.not(scc_id: nil).delete_all end # Mangles remote repo URL to make a nicer local path, see specs for examples diff --git a/spec/lib/rmt/scc_spec.rb b/spec/lib/rmt/scc_spec.rb index a62077d00..f1e3b9d61 100644 --- a/spec/lib/rmt/scc_spec.rb +++ b/spec/lib/rmt/scc_spec.rb @@ -265,6 +265,16 @@ installer_updates: true ) end + let!(:custom_repo) do + FactoryBot.create( + :repository, + :with_products, + auth_token: nil, + external_url: 'http://customer.com/stuff.suse.com/x86', + installer_updates: false, + scc_id: nil + ) + end before do # to prevent 'does not implement' verifying doubles error @@ -298,6 +308,10 @@ def scc it 'other repos without auth_tokens persist' do expect { other_repo_without_token.reload }.not_to raise_error end + + it 'custom repos without auth_tokens persist' do + expect { custom_repo.reload }.not_to raise_error + end end describe '#export' do From 4aa1e0e1151a1939430e781adfb82c1872ff3de2 Mon Sep 17 00:00:00 2001 From: "parag.jain" Date: Mon, 16 Dec 2024 18:48:16 +0530 Subject: [PATCH 102/103] Enable RMT to handle the new dl.suse.com CDN domain --- package/obs/rmt-server.changes | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/package/obs/rmt-server.changes b/package/obs/rmt-server.changes index f587e6685..2cc735f92 100644 --- a/package/obs/rmt-server.changes +++ b/package/obs/rmt-server.changes @@ -11,7 +11,8 @@ Wed Oct 30 09:01:32 UTC 2024 - Natnael Getahun * Fix Azure SCC connection (bsc#1233314) * Deny access of Azure Basic type images to LTSS * Allow SLE Micro system to access SLES repositories (bsc#1230419) - * Skip system token rotation in read-only APIs + * Skip system token rotation in read-only APIs + * Enable RMT to handle the new dl.suse.com CDN domain. ------------------------------------------------------------------- Wed Aug 21 15:28:43 UTC 2024 - Jesús Bermúdez Velázquez From 192969892576986b04353fffa0909ce87cc2225c Mon Sep 17 00:00:00 2001 From: Thomas Schmidt Date: Mon, 16 Dec 2024 16:24:07 +0100 Subject: [PATCH 103/103] add bugzilla reference --- package/obs/rmt-server.changes | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package/obs/rmt-server.changes b/package/obs/rmt-server.changes index 2cc735f92..c84e3dbf6 100644 --- a/package/obs/rmt-server.changes +++ b/package/obs/rmt-server.changes @@ -12,7 +12,7 @@ Wed Oct 30 09:01:32 UTC 2024 - Natnael Getahun * Deny access of Azure Basic type images to LTSS * Allow SLE Micro system to access SLES repositories (bsc#1230419) * Skip system token rotation in read-only APIs - * Enable RMT to handle the new dl.suse.com CDN domain. + * Enable RMT to handle the new dl.suse.com CDN domain (bsc#1234641) ------------------------------------------------------------------- Wed Aug 21 15:28:43 UTC 2024 - Jesús Bermúdez Velázquez