Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
9198d6a
feat(ruby): disable signature checking for dir:// repositories
imobachgs Feb 28, 2025
042d6f0
docs(ruby): update changes file
imobachgs Feb 28, 2025
72f92b3
fix(service): fix local directory URL
imobachgs Feb 28, 2025
400d4bc
feat(ruby): disable signature checking for dir:// repositories (#2098)
imobachgs Feb 28, 2025
35370c1
Adapt SLES / SLES_SAP 16 to new patterns
fcrozat Mar 4, 2025
ad2b830
add changelog entry for patterns update
fcrozat Mar 4, 2025
4088304
Update patterns for SLES / SLES_SAP 16 (#2104)
lslezak Mar 4, 2025
fff357d
storage: remove leftover in storage schema
joseivanlopez Feb 27, 2025
5d9a295
remove /storage/guided also from profile example
mvidner Mar 4, 2025
7656a16
install and package also storage.schema.json (bsc#1238367)
mvidner Mar 4, 2025
035bc7a
install and package also storage.schema.json (bsc#1238367) (#2107)
mvidner Mar 4, 2025
8b4e2ee
Revert "fix(service): fix local directory URL"
imobachgs Mar 5, 2025
a0d4bb4
Revert "feat(ruby): disable signature checking for dir:// repositories"
imobachgs Mar 5, 2025
8450886
docs(ruby): update changes file
imobachgs Mar 5, 2025
ae7bbcc
feat(ruby): enable signature checking for `dir://` repositories (#2113)
imobachgs Mar 5, 2025
de64b3a
refactor(rust): extract Transfer::get to a generic handler
imobachgs Feb 28, 2025
9ffba96
feat(rust): add support for device:, usb:, label: and hd/dvd/cd: URLs
imobachgs Feb 28, 2025
142901f
fix(rust): adapt to Transfer::get API changes
imobachgs Feb 28, 2025
68fb418
docs(rust): extend transfer API documentation
imobachgs Mar 5, 2025
61e9154
feat(rust): extract label: to a separate LabelHandler
imobachgs Mar 5, 2025
a876d6c
refactor(rust): move transfer handlers to separate modules
imobachgs Mar 5, 2025
54ed4b4
feat(rust)!: the download command expects a destination
imobachgs Mar 5, 2025
bfddec2
feat(rust): add some feedback to "agama download"
imobachgs Mar 5, 2025
353de60
refactor(rust): use lsblk --pairs to read the file systems
imobachgs Mar 6, 2025
f6a6f9e
fix(rust): FileSystemReader uses the shortest path as mount point
imobachgs Mar 6, 2025
d8f4a6e
fix(rust): FileSystem includes the whole block device
imobachgs Mar 6, 2025
4b00c20
chore(rust): updates from code review
imobachgs Mar 6, 2025
f17075c
refactor(rust): move mount logic to the FileSystem struct
imobachgs Mar 6, 2025
8e9655e
fix(rust): mount file systems in ro mode
imobachgs Mar 6, 2025
6506628
refactor(rust): better naming for FileSystemsList
imobachgs Mar 6, 2025
909c719
fix(rust): handle devices= with no /dev prefix
imobachgs Mar 6, 2025
9814e23
Merge branch 'after-release-beta2' into extend-transfer-urls
imobachgs Mar 6, 2025
1657b09
docs(rust): update changes file
imobachgs Mar 6, 2025
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
5 changes: 5 additions & 0 deletions products.d/agama-products.changes
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
-------------------------------------------------------------------
Tue Mar 4 09:06:46 UTC 2025 - Frederic Crozat <fcrozat@suse.com>

- Update patterns list for SLES / SLES_SAP 16.

-------------------------------------------------------------------
Thu Feb 20 13:13:18 UTC 2025 - Josef Reidinger <jreidinger@suse.com>

Expand Down
19 changes: 16 additions & 3 deletions products.d/sles_160.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -65,13 +65,26 @@ software:
archs: ppc

mandatory_patterns:
- base_traditional
- base
optional_patterns: null # no optional pattern shared
user_patterns:
- kvm_host
- cockpit
- sles_enhanced_base
- enhanced_base
- sles_minimal_sap
- fips
- selinux
- documentation
- sw_management
- container_runtime_podman
- dhcp_dns_server
- directory_server
- file_server
- gateway_server
- kvm_server
- kvm_tools
- lamp_server
- mail_server
- printing
mandatory_packages:
- NetworkManager
- sudo-policy-wheel-auth-self # explicit wheel group policy to conform new auth model
Expand Down
2 changes: 1 addition & 1 deletion products.d/sles_sap_160.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ software:
archs: ppc

mandatory_patterns:
- base_traditional
- base
- sles_sap_enhanced_base
- sles_sap_base_sap_server
optional_patterns: null # no optional pattern shared
Expand Down
4 changes: 4 additions & 0 deletions rust/agama-cli/src/commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
// To contact SUSE LLC about this file by physical or electronic mail, you may
// find current contact information at www.suse.com.

use std::path::PathBuf;

use crate::auth::AuthCommands;
use crate::config::ConfigCommands;
use crate::logs::LogsCommands;
Expand Down Expand Up @@ -105,6 +107,8 @@ pub enum Commands {
Download {
/// URL pointing to file for download
url: String,
/// File name
destination: PathBuf,
},
/// Finish the installation rebooting the system by default.
///
Expand Down
21 changes: 20 additions & 1 deletion rust/agama-cli/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
// find current contact information at www.suse.com.

use agama_lib::manager::FinishMethod;
use anyhow::Context;
use clap::{Args, Parser};

mod auth;
Expand All @@ -43,6 +44,9 @@ use logs::run as run_logs_cmd;
use profile::run as run_profile_cmd;
use progress::InstallerProgress;
use questions::run as run_questions_cmd;
use std::fs;
use std::os::unix::fs::OpenOptionsExt;
use std::path::PathBuf;
use std::{
collections::HashMap,
process::{ExitCode, Termination},
Expand Down Expand Up @@ -198,6 +202,21 @@ async fn allowed_insecure_api(use_insecure: bool, api_url: String) -> Result<boo
}
}

pub fn download_file(url: &str, path: &PathBuf) -> Result<(), ServiceError> {
let mut file = fs::OpenOptions::new()
.create(true)
.write(true)
.mode(0o400)
.open(path)
.context(format!("Cannot write the file '{}'", path.display()))?;

match Transfer::get(&url, &mut file) {
Ok(()) => println!("File saved to {}", path.display()),
Comment thread
imobachgs marked this conversation as resolved.
Err(e) => eprintln!("Could not retrieve the file: {e}"),
}
Ok(())
}

pub async fn run_command(cli: Cli) -> Result<(), ServiceError> {
// somehow check whether we need to ask user for self-signed certificate acceptance
let api_url = cli.opts.api.trim_end_matches('/').to_string();
Expand Down Expand Up @@ -238,7 +257,7 @@ pub async fn run_command(cli: Cli) -> Result<(), ServiceError> {
}
Commands::Questions(subcommand) => run_questions_cmd(client, subcommand).await?,
Commands::Logs(subcommand) => run_logs_cmd(client, subcommand).await?,
Commands::Download { url } => Transfer::get(&url, std::io::stdout())?,
Commands::Download { url, destination } => download_file(&url, &destination)?,
Commands::Auth(subcommand) => {
run_auth_cmd(client, subcommand).await?;
}
Expand Down
4 changes: 2 additions & 2 deletions rust/agama-cli/src/profile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -146,8 +146,8 @@ async fn import(url_string: String, dir: Option<PathBuf>) -> anyhow::Result<()>
fn pre_process_profile<P: AsRef<Path>>(url_string: &str, path: P) -> anyhow::Result<()> {
let work_dir = path.as_ref().parent().unwrap();
let tmp_profile_path = work_dir.join("profile.temp");
let tmp_file = File::create(&tmp_profile_path)?;
Transfer::get(url_string, tmp_file)?;
let mut tmp_file = File::create(&tmp_profile_path)?;
Transfer::get(url_string, &mut tmp_file)?;

match FileFormat::from_file(&tmp_profile_path)? {
FileFormat::Jsonnet => {
Expand Down
6 changes: 0 additions & 6 deletions rust/agama-lib/share/examples/profile_tw.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,6 @@
"id": "Tumbleweed"
},
"storage": {
"guided": {
"boot": {
"configure": true,
"device": "/dev/dm-1"
}
}
},
"user": {
"fullName": "Jane Doe",
Expand Down
63 changes: 0 additions & 63 deletions rust/agama-lib/share/examples/storage/guided.json

This file was deleted.

3 changes: 1 addition & 2 deletions rust/agama-lib/share/storage.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,7 @@
"description": "LVM volume groups.",
"type": "array",
"items": { "$ref": "#/$defs/volumeGroup" }
},
"guided": { "$ref": "#/$defs/guided" }
}
},
"$defs": {
"boot": {
Expand Down
2 changes: 1 addition & 1 deletion rust/agama-lib/src/scripts/model.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ impl BaseScript {

match &self.source {
ScriptSource::Text { body } => write!(file, "{}", &body)?,
ScriptSource::Remote { url } => Transfer::get(url, file)?,
ScriptSource::Remote { url } => Transfer::get(url, &mut file)?,
};

Ok(())
Expand Down
70 changes: 54 additions & 16 deletions rust/agama-lib/src/utils/transfer.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright (c) [2024] SUSE LLC
// Copyright (c) [2025] SUSE LLC
//
// All Rights Reserved.
//
Expand All @@ -20,21 +20,61 @@

//! File transfer API for Agama.
//!
//! Implement a file transfer API which, in the future, will support Agama specific URLs. Check the
//! Implement a file transfer API which, at this point, partially supports Agama specific URLs. Check the
//! YaST document about [URL handling in the
//! installer](https://github.com/yast/yast-installation/blob/master/doc/url.md) for further
//! information.
//!
//! At this point, it only supports those schemes supported by CURL.
//! This API supports the following URLs from YaST: `device:`, `usb:`, `label:`, ! `hd:`, `dvd:` and
//! `cd:`. The support for well-known URLs (e.g., `file:`, `http:`, `https:`, ! `ftp:`, `nfs:`,
//! etc.) is implemented using CURL.
//!
//! Support for `relurl:` and `repo:` are still missing.
//!
//! ## SSL
//!
//! YaST support for HTTPS used a custom certificate which was located in
//! `/etc/sssl/clientcerts/client-cert.pem`. Agama does not use such a certificate and it only
//! relies on those that are installed in the installation media.
//!
//! ## Examples
//! Requires working localectl.
//!
//! ```no_run
//! use agama_lib::utils::Transfer;
//! Transfer::get("label://OEMDRV/autoinst.xml", &mut std::io::stdout()).unwrap();
//! ````

use std::io::Write;

use curl::easy::Easy;
use thiserror::Error;
use url::Url;

mod file_finder;
mod file_systems;
mod handlers;

use handlers::{DeviceHandler, GenericHandler, HdHandler, LabelHandler};

#[derive(Error, Debug)]
#[error(transparent)]
pub struct TransferError(#[from] curl::Error);
pub enum TransferError {
#[error("Could not retrieve the file: {0}")]
CurlError(#[from] curl::Error),
#[error("Could not parse the URL: {0}")]
ParseError(#[from] url::ParseError),
#[error("File not found: {0}")]
FileNotFound(String),
#[error("IO error: {0}")]
IO(#[from] std::io::Error),
#[error("Could not mount the file system {0}")]
FileSystemMount(String),
#[error("Missing file path: {0}")]
MissingPath(Url),
#[error("Missing device: {0}")]
MissingDevice(Url),
#[error("Missing file system label: {0}")]
MissingLabel(Url),
}
pub type TransferResult<T> = Result<T, TransferError>;

/// File transfer API
Expand All @@ -45,15 +85,13 @@ impl Transfer {
///
/// * `url`: URL to get the data from.
/// * `out_fd`: where to write the data.
pub fn get(url: &str, mut out_fd: impl Write) -> TransferResult<()> {
let mut handle = Easy::new();
handle.follow_location(true)?;
handle.fail_on_error(true)?;
handle.url(url)?;

let mut transfer = handle.transfer();
transfer.write_function(|buf| Ok(out_fd.write(buf).unwrap()))?;
transfer.perform()?;
Ok(())
pub fn get(url: &str, out_fd: &mut impl Write) -> TransferResult<()> {
let url = Url::parse(url)?;
match url.scheme() {
"device" | "usb" => DeviceHandler::default().get(url, out_fd),
"label" => LabelHandler::default().get(url, out_fd),
"cd" | "dvd" | "hd" => HdHandler::default().get(url, out_fd),
_ => GenericHandler::default().get(url, out_fd),
}
}
}
Loading