Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion service/agama-yast.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -64,5 +64,5 @@ Gem::Specification.new do |spec|
spec.add_dependency "logger", "~> 1.5"
# here we have problem as ruby3.2 on SLFO does not provide rubygem-ostruct, but newer ruby will separate it after 3.4
# but dynamic dependencies are not possible in gemspec
# spec.add_dependency "ostruct", "~> 0.6.1"
spec.add_dependency "ostruct", "~> 0.6.1" if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new("3.5")
end
60 changes: 47 additions & 13 deletions service/lib/agama/http/clients/base.rb
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,11 @@ module HTTP
module Clients
# Base for HTTP clients.
class Base
# Default timeout for HTTP requests (in seconds).
# Installation tasks like writing files or running scripts can take a long time,
# especially after Snapper configuration which might trigger a Btrfs quota scan.
DEFAULT_TIMEOUT = 300

def initialize(logger)
@base_url = "http://localhost/api/"
@logger = logger
Expand All @@ -36,43 +41,72 @@ def initialize(logger)
# send POST request with given data and path.
# @param path[String] path relatived to `api`` endpoint.
# @param data[#to_json] data to send in request
# @return [String, nil] response body or nil if it is not a success
def post(path, data)
response = Net::HTTP.post(uri(path), data.to_json, headers)
return unless response.is_a?(Net::HTTPClientError)
uri = uri(path)
request = Net::HTTP::Post.new(uri.request_uri, headers)
request.body = data.to_json
response = call_server(uri, request)
return response.body if response.is_a?(Net::HTTPSuccess)

@logger.warn "server returned #{response.code} with body: #{response.body}"
log_error(response)
nil
end

# send GET request with given path.
# @param path[String] path relatived to `api`` endpoint.
# @return [Net::HTTPResponse, nil] Net::HTTPResponse if it is not an Net::HTTPClientError
# @return [String, nil] response body or nil if it is not a success
def get(path)
response = Net::HTTP.get(uri(path), headers)
return response unless response.is_a?(Net::HTTPClientError)
uri = uri(path)
request = Net::HTTP::Get.new(uri.request_uri, headers)
response = call_server(uri, request)
return response.body if response.is_a?(Net::HTTPSuccess)

@logger.warn "server returned #{response.code} with body: #{response.body}"
log_error(response)
nil
end

# send PUT request with given data and path.
# @param path[String] path relatived to `api`` endpoint.
# @param data[#to_json] data to send in request
# @return [String, nil] response body or nil if it is not a success
def put(path, data)
response = Net::HTTP.put(uri(path), data.to_json, headers)
return unless response.is_a?(Net::HTTPClientError)
uri = uri(path)
request = Net::HTTP::Put.new(uri.request_uri, headers)
request.body = data.to_json
response = call_server(uri, request)
return response.body if response.is_a?(Net::HTTPSuccess)

@logger.warn "server returned #{response.code} with body: #{response.body}"
log_error(response)
nil
end

protected

# Log an error response.
# @param response [Net::HTTPResponse]
def log_error(response)
@logger.warn "server returned #{response.code} with body: #{response.body}"
end

# Calls the server with the given request.
# @param uri [URI]
# @param request [Net::HTTPRequest]
# @return [Net::HTTPResponse]
def call_server(uri, request)
Net::HTTP.start(uri.host, uri.port, read_timeout: DEFAULT_TIMEOUT) do |http|
http.request(request)
end
end

def uri(path)
URI.join(@base_url, path)
end

def headers
@headers = {
"Content-Type": "application/json",
Authorization: "Bearer #{auth_token}"
{
"Content-Type" => "application/json",
"Authorization" => "Bearer #{auth_token}"
}
end

Expand Down
9 changes: 9 additions & 0 deletions service/package/rubygem-agama-yast.changes
Original file line number Diff line number Diff line change
@@ -1,3 +1,12 @@
-------------------------------------------------------------------
Tue Mar 24 07:13:47 UTC 2026 - Knut Anderssen <kanderssen@suse.com>

- Increase the Net::HTTP read_timeout to 300 seconds to mitigates
timeouts during installation tasks (e.g., running scripts or
writing files) (bsc#1259745) (gh#agama-project/agama#3296).
- Cherry pick test fix for the AutoYaST legacy proposal
(original PR gh#agama-project/agama#2640).

-------------------------------------------------------------------
Wed Mar 11 14:38:44 UTC 2026 - Imobach Gonzalez Sosa <igonzalezsosa@suse.com>

Expand Down
22 changes: 17 additions & 5 deletions service/test/agama/http/clients/scripts_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,23 @@

describe "#run" do
it "calls the end-point to run the scripts" do
url = URI("http://localhost/api/scripts/run")
expect(Net::HTTP).to receive(:post).with(url, "post".to_json, {
"Content-Type": "application/json",
Authorization: "Bearer 123456"
})
http_double = instance_double(Net::HTTP)
response_double = instance_double(Net::HTTPSuccess, body: "ok")
allow(response_double).to receive(:is_a?).with(Net::HTTPSuccess).and_return(true)

expect(Net::HTTP).to receive(:start)
.with("localhost", 80, read_timeout: 300)
.and_yield(http_double)

expect(http_double).to receive(:request) do |request|
expect(request).to be_an_instance_of(Net::HTTP::Post)
expect(request.path).to eq("/api/scripts/run")
expect(request.body).to eq("post".to_json)
expect(request["Content-Type"]).to eq("application/json")
expect(request["Authorization"]).to eq("Bearer 123456")
response_double
end

scripts.run("post")
end
end
Expand Down
2 changes: 2 additions & 0 deletions service/test/agama/storage/autoyast_proposal_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@
before do
mock_storage(devicegraph: scenario)
allow(Y2Storage::Arch).to receive(:new).and_return(arch)
# This environment is enforced by Agama
allow(Y2Storage::StorageEnv.instance).to receive(:no_bls_bootloader).and_return true
end

let(:scenario) { "windows-linux-pc.yml" }
Expand Down
Loading