Skip to content

Commit

Permalink
Merge pull request #1220 from SUSE/proxy-upgrade-migration-scc
Browse files Browse the repository at this point in the history
Propagate the upgrate call to SCC
  • Loading branch information
digitaltom authored Aug 30, 2024
2 parents 638f922 + 36cae05 commit 818b51c
Show file tree
Hide file tree
Showing 2 changed files with 202 additions and 54 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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: 201, 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
Expand Down
61 changes: 49 additions & 12 deletions engines/scc_proxy/lib/scc_proxy/engine.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -117,6 +117,17 @@ def prepare_scc_request(uri_path, product, auth, params, mode)
scc_request
end

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,
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)
Expand All @@ -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)
Expand Down Expand Up @@ -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_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}"
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
Expand Down Expand Up @@ -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
Expand All @@ -337,12 +362,7 @@ 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
announce_auth = "Token token=#{params[:token]}"

response = SccProxy.announce_system_scc(announce_auth, params)
Expand Down Expand Up @@ -375,6 +395,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)
Expand All @@ -386,10 +408,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]
Expand Down

0 comments on commit 818b51c

Please sign in to comment.