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
17 changes: 16 additions & 1 deletion rust/agama-lib/src/http/base_http_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,25 @@ pub enum BaseHTTPClientError {
InvalidURL(#[from] url::ParseError),
#[error(transparent)]
InvalidJSON(#[from] serde_json::Error),
#[error("Backend call failed with status {0} and text '{1}'")]
#[error("Backend responded with code {} and the following message:\n\n{}", .0, format_backend_error(.1))]
BackendError(u16, String),
}

fn format_backend_error(error: &String) -> String {
let message: Result<serde_json::Value, _> = serde_json::from_str(error);

match message {
Ok(message) => {
if let Some(error) = message.get("error") {
error.to_string().replace("\\n", "\n")
} else {
format!("{:?}", error)
}
}
Err(_) => format!("{:?}", error),
}
}

/// Base that all HTTP clients should use.
///
/// It provides several features including automatic base URL switching,
Expand Down
34 changes: 24 additions & 10 deletions rust/agama-lib/src/profile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,35 +37,49 @@ pub use http_client::ProfileHTTPClient;
pub const DEFAULT_SCHEMA_DIR: &str = "/usr/share/agama/schema";
pub const DEFAULT_JSONNET_DIR: &str = "/usr/share/agama/jsonnet";

#[derive(thiserror::Error, Debug)]
pub enum AutoyastError {
#[error("I/O error: {0}")]
IO(#[from] std::io::Error),
#[error("Failed to run the agama-autoyast script: {0}")]
Execute(#[source] std::io::Error),
#[error("Failed to convert the AutoYaST profile: {0}")]
Evaluation(String),
#[error("Unsupported AutoYaST format at {0}")]
UnsupportedFormat(Url),
}

/// Downloads and converts autoyast profile.
pub struct AutoyastProfileImporter {
pub content: String,
}

impl AutoyastProfileImporter {
pub async fn read(url: &Url) -> anyhow::Result<Self> {
pub async fn read(url: &Url) -> Result<Self, AutoyastError> {
let path = url.path();
if !path.ends_with(".xml") && !path.ends_with(".erb") && !path.ends_with('/') {
let msg = format!("Unsupported AutoYaST format at {}", url);
return Err(anyhow::Error::msg(msg));
return Err(AutoyastError::UnsupportedFormat(url.clone()));
}

const TMP_DIR_PREFIX: &str = "autoyast";
const AUTOINST_JSON: &str = "autoinst.json";

let tmp_dir = TempDir::with_prefix(TMP_DIR_PREFIX)?;
tokio::process::Command::new("agama-autoyast")
let result = tokio::process::Command::new("agama-autoyast")
.env("YAST_SKIP_PROFILE_FETCH_ERROR", "1")
.args([url.as_str(), &tmp_dir.path().to_string_lossy()])
.status()
.output()
.await
.context("Failed to run agama-autoyast")?;
.map_err(AutoyastError::Execute)?;

if !result.status.success() {
return Err(AutoyastError::Evaluation(
String::from_utf8_lossy(&result.stderr).to_string(),
));
}

let autoinst_json = tmp_dir.path().join(AUTOINST_JSON);
let content = fs::read_to_string(&autoinst_json).context(format!(
"agama-autoyast did not produce {:?}",
autoinst_json
))?;
let content = fs::read_to_string(&autoinst_json)?;
Ok(Self { content })
}
}
Expand Down
4 changes: 2 additions & 2 deletions rust/agama-lib/src/profile/http_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
// To contact SUSE LLC about this file by physical or electronic mail, you may
// find current contact information at www.suse.com.

use crate::http::BaseHTTPClient;
use crate::http::{BaseHTTPClient, BaseHTTPClientError};
use crate::profile::ValidationOutcome;
use fluent_uri::Uri;
use serde::Serialize;
Expand Down Expand Up @@ -51,7 +51,7 @@ impl ProfileHTTPClient {
/// Note that this client does not act on this *url*, it passes it as a parameter
/// to our web backend.
/// Return well-formed Agama JSON on success.
pub async fn from_autoyast(&self, url: &Uri<String>) -> anyhow::Result<String> {
pub async fn from_autoyast(&self, url: &Uri<String>) -> Result<String, BaseHTTPClientError> {
let mut map = HashMap::new();

map.insert(String::from("url"), url.to_string());
Expand Down
8 changes: 3 additions & 5 deletions rust/agama-manager/src/service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,20 +29,18 @@ use agama_utils::{
self, event,
manager::{self, LicenseContent},
status::Stage,
Action, Config, Event, FinishMethod, Issue, IssueMap, Proposal, Scope, Status, SystemInfo,
Action, Config, Event, Issue, IssueMap, Proposal, Scope, Status, SystemInfo,
},
arch::Arch,
issue,
kernel_cmdline::KernelCmdline,
licenses,
issue, licenses,
products::{self, ProductSpec},
progress, question,
};
use async_trait::async_trait;
use merge::Merge;
use network::NetworkSystemClient;
use serde_json::Value;
use std::{collections::HashMap, str::FromStr, sync::Arc};
use std::{collections::HashMap, sync::Arc};
use tokio::sync::{broadcast, RwLock};

#[derive(Debug, thiserror::Error)]
Expand Down
6 changes: 1 addition & 5 deletions rust/agama-manager/src/tasks/runner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,6 @@
// To contact SUSE LLC about this file by physical or electronic mail, you may
// find current contact information at www.suse.com.

use std::str::FromStr;

use crate::{
actions::{FinishAction, InstallAction, SetConfigAction},
bootloader, files, hostname, iscsi, l10n, proxy, s390, security, service, software, storage,
Expand All @@ -30,9 +28,7 @@ use agama_network::NetworkSystemClient;
use agama_utils::{
actor::{Actor, Handler, MessageHandler},
api::{FinishMethod, Scope},
issue,
kernel_cmdline::KernelCmdline,
progress, question,
issue, progress, question,
};
use async_trait::async_trait;

Expand Down
27 changes: 16 additions & 11 deletions rust/agama-server/src/profile/web.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
// To contact SUSE LLC about this file by physical or electronic mail, you may
// find current contact information at www.suse.com.

use agama_lib::profile::AutoyastError;
use agama_transfer::Transfer;
use anyhow::Context;

Expand Down Expand Up @@ -50,6 +51,19 @@ impl std::fmt::Display for ProfileServiceError {
}
}

impl From<AutoyastError> for ProfileServiceError {
fn from(e: AutoyastError) -> Self {
let http_status = match e {
AutoyastError::Execute(..) => StatusCode::INTERNAL_SERVER_ERROR,
_ => StatusCode::BAD_REQUEST,
};
Self {
source: e.into(),
http_status,
}
}
}

// Make a 400 response
// ```
// let r: Result<T, anyhow::Error> = foo();
Expand Down Expand Up @@ -208,15 +222,6 @@ async fn autoyast(body: String) -> Result<String, ProfileServiceError> {
}

let url = Url::parse(profile.url.as_ref().unwrap()).map_err(anyhow::Error::new)?;
let importer_res = AutoyastProfileImporter::read(&url).await;
match importer_res {
Ok(importer) => Ok(importer.content),
Err(error) => {
// anyhow can be only displayed, not so nice
if format!("{}", error).contains("Failed to run") {
return Err(make_internal(error));
}
Err(error.into())
}
}
let importer = AutoyastProfileImporter::read(&url).await?;
Ok(importer.content)
}
6 changes: 6 additions & 0 deletions rust/package/agama.changes
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
-------------------------------------------------------------------
Thu Mar 12 13:20:48 UTC 2026 - Imobach Gonzalez Sosa <igonzalezsosa@suse.com>

- Add error reporting when working with AutoYaST profiles (related
to bsc#1259434).

-------------------------------------------------------------------
Thu Mar 12 13:05:51 UTC 2026 - Ladislav Slezák <lslezak@suse.com>

Expand Down
10 changes: 6 additions & 4 deletions service/bin/agama-autoyast
Original file line number Diff line number Diff line change
Expand Up @@ -51,10 +51,12 @@ begin
warn "Did not convert the profile (canceled by the user)."
exit 2
end
rescue Agama::Commands::CouldNotFetchProfile
warn "Could not fetch the AutoYaST profile."
rescue Agama::Commands::CouldNotFetchProfile => e
warn "Could not fetch the AutoYaST profile:\n\n"
warn e.full_message
exit 3
rescue Agama::Commands::CouldNotWriteAgamaConfig
warn "Could not write the Agama configuration."
rescue Agama::Commands::CouldNotWriteAgamaConfig => e
warn "Could not write the Agama configuration:\n\n"
warn e.full_message
exit 4
end
2 changes: 1 addition & 1 deletion service/lib/agama/autoyast/profile_reporter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ def report(elements)
)

questions_client.ask(question) do |answer|
answer == :continue
answer.action == :continue
end
end

Expand Down
12 changes: 10 additions & 2 deletions service/lib/agama/autoyast/root_reader.rb
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,19 @@ def read
root_user = config.users.find { |u| u.name == "root" }
return {} unless root_user

hsh = { "password" => root_user.password.value.to_s }
hsh["hashedPassword"] = true if root_user.password.value.encrypted?
hsh = {}
password = root_user.password

if password
hsh["password"] = password.value.to_s
hsh["hashedPassword"] = true if password.value.encrypted?
end

public_key = root_user.authorized_keys.first
hsh["sshPublicKey"] = public_key if public_key

return {} if hsh.empty?

{ "root" => hsh }
end

Expand Down
9 changes: 6 additions & 3 deletions service/lib/agama/autoyast/user_reader.rb
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,14 @@ def read

hsh = {
"userName" => user.name,
"fullName" => user.gecos.first.to_s,
"password" => user.password.value.to_s
"fullName" => user.gecos.first.to_s
}

hsh["hashedPassword"] = true if user.password.value.encrypted?
password = user.password
if password
hsh["password"] = password.value.to_s
hsh["hashedPassword"] = true if password.value.encrypted?
end

{ "user" => hsh }
end
Expand Down
5 changes: 5 additions & 0 deletions service/package/rubygem-agama-yast.changes
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
-------------------------------------------------------------------
Thu Mar 12 13:03:36 UTC 2026 - Imobach Gonzalez Sosa <igonzalezsosa@suse.com>

- Add errors to the output of agama-autoyast (related to bsc#1259434).

-------------------------------------------------------------------
Thu Mar 12 08:35:23 UTC 2026 - Ancor Gonzalez Sosa <ancor@suse.com>

Expand Down
14 changes: 14 additions & 0 deletions service/test/agama/autoyast/root_reader_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,20 @@
"password" => "123456", "sshPublicKey" => "ssh-key 1"
)
end

context "but does not contain password or SSH public key" do
let(:root) do
{
"username" => "root",
"user_password" => nil,
"encrypted" => nil
}
end

it "returns an empty hash" do
expect(subject.read).to be_empty
end
end
end
end
end
19 changes: 19 additions & 0 deletions service/test/agama/autoyast/user_reader_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -77,5 +77,24 @@
)
end
end

context "when the password is nil" do
let(:user) do
{
"username" => "suse",
"fullname" => "SUSE",
"user_password" => nil,
"encrypted" => nil
}
end

it "does not include password information" do
user = subject.read["user"]
expect(user).to eq(
"userName" => "suse",
"fullName" => "SUSE"
)
end
end
end
end
Loading