Skip to content

Commit

Permalink
Merge branch 'master' into cancel-link
Browse files Browse the repository at this point in the history
  • Loading branch information
imobachgs authored Oct 11, 2024
2 parents 9e78a37 + 359dda0 commit e621b27
Show file tree
Hide file tree
Showing 56 changed files with 4,434 additions and 2,024 deletions.
75 changes: 75 additions & 0 deletions doc/auto_storage.md
Original file line number Diff line number Diff line change
Expand Up @@ -944,6 +944,81 @@ The *mandatory* keyword can be used for only generating the mandatory partitions
}
```

### Generating Physical Volumes

Volume groups can be configured to explicitly use a set of devices as physical volumes. The aliases
of the devices to use are added to the list of physical volumes:

```json
"storage": {
"drives": [
{
"search": "/dev/vda",
"partitions": [
{ "alias": "pv2" },
{ "alias": "pv1" }
]
}
],
"volumeGroups": [
{
"physicalVolumes": ["pv1", "pv2"]
}
]
}
```

The physical volumes can be automatically generated too, by simply indicating the target devices in
which to create the partitions. For that, a *generate* section is added to the list of physical
volumes:

```json
"storage": {
"drives": [
{
"search": "/dev/vda",
"alias": "pvs-disk"
}
],
"volumeGroups": [
{
"physicalVolumes": [
{ "generate": ["pvs-disk"] }
]
}
]
}
```

If the auto-generated physical volumes have to be encrypted, then the encryption config is added to
the *generate* section:


```json
"storage": {
"drives": [
{
"search": "/dev/vda",
"alias": "pvs-disk"
}
],
"volumeGroups": [
{
"physicalVolumes": [
{
"generate": {
"targetDevices": ["pvs-disk"],
"encryption": {
"luks2": { "password": "12345" }
}
}
}
]
}
]
}
```

### Using the Automatic Proposal

On the first implementations, Agama can rely on the process known as Guided Proposal to calculate
Expand Down
8 changes: 4 additions & 4 deletions rust/agama-cli/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,9 @@ mod progress;
mod questions;

use crate::error::CliError;
use agama_lib::error::ServiceError;
use agama_lib::manager::ManagerClient;
use agama_lib::progress::ProgressMonitor;
use agama_lib::{
error::ServiceError, manager::ManagerClient, progress::ProgressMonitor, transfer::Transfer,
};
use auth::run as run_auth_cmd;
use commands::Commands;
use config::run as run_config_cmd;
Expand Down Expand Up @@ -158,7 +158,7 @@ pub async fn run_command(cli: Cli) -> Result<(), ServiceError> {
Commands::Questions(subcommand) => run_questions_cmd(subcommand).await?,
Commands::Logs(subcommand) => run_logs_cmd(subcommand).await?,
Commands::Auth(subcommand) => run_auth_cmd(subcommand).await?,
Commands::Download { url } => crate::profile::download(&url, std::io::stdout())?,
Commands::Download { url } => Transfer::get(&url, std::io::stdout())?,
};

Ok(())
Expand Down
19 changes: 3 additions & 16 deletions rust/agama-cli/src/profile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,15 @@ use agama_lib::{
auth::AuthToken,
install_settings::InstallSettings,
profile::{AutoyastProfile, ProfileEvaluator, ProfileValidator, ValidationResult},
transfer::Transfer,
Store as SettingsStore,
};
use anyhow::Context;
use clap::Subcommand;
use curl::easy::Easy;
use std::os::unix::process::CommandExt;
use std::{
fs::File,
io::{stdout, Write},
io::stdout,
path::{Path, PathBuf},
process::Command,
};
Expand Down Expand Up @@ -78,19 +78,6 @@ pub enum ProfileCommands {
},
}

pub fn download(url: &str, mut out_fd: impl Write) -> anyhow::Result<()> {
let mut handle = Easy::new();
handle.url(url)?;

let mut transfer = handle.transfer();
transfer.write_function(|buf|
// unwrap here is ok, as we want to kill download if we failed to write content
Ok(out_fd.write(buf).unwrap()))?;
transfer.perform()?;

Ok(())
}

fn validate(path: &PathBuf) -> anyhow::Result<()> {
let validator = ProfileValidator::default_schema()?;
// let path = Path::new(&path);
Expand Down Expand Up @@ -138,7 +125,7 @@ async fn import(url_string: String, dir: Option<PathBuf>) -> anyhow::Result<()>
AutoyastProfile::new(&url)?.read_into(output_fd)?;
} else {
// just download profile
download(&url_string, output_fd)?;
Transfer::get(&url_string, output_fd)?;
}

// exec shell scripts
Expand Down
48 changes: 48 additions & 0 deletions rust/agama-lib/share/examples/storage/generate_pvs.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
{
"storage": {
"drives": [
{
"alias": "first-disk"
},
{
"partitions": [
{
"alias": "pv1",
"id": "lvm",
"size": { "min": "10 GiB" }
}
]
}
],
"volumeGroups": [
{
"name": "system",
"physicalVolumes": ["pv1"],
"logicalVolumes": [
{
"filesystem": { "path": "/" }
}
]
},
{
"name": "logs",
"physicalVolumes": [
{ "generate": ["first-disk"] }
]
},
{
"name": "data",
"physicalVolumes": [
{
"generate": {
"targetDevices": ["first-disk"],
"encryption": {
"luks2": { "password": "12345" }
}
}
}
]
}
]
}
}
55 changes: 50 additions & 5 deletions rust/agama-lib/share/profile.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -529,11 +529,56 @@
"physicalVolumes": {
"title": "Physical volumes",
"description": "Devices to use as physical volumes.",
"type": "array",
"items": {
"title": "Device alias",
"type": "string"
}
"$comment": "In the future it would be possible to indicate both aliases and 'generate' items together.",
"anyOf": [
{
"type": "array",
"items": {
"title": "Device alias",
"type": "string"
}
},
{
"type": "array",
"items": {
"title": "Generate physical volumes",
"description": "Automatically creates the needed physical volumes in the indicated devices.",
"type": "object",
"additionalProperties": false,
"required": ["generate"],
"properties": {
"generate": {
"anyOf": [
{
"type": "array",
"items": {
"title": "Device alias",
"type": "string"
}
},
{
"type": "object",
"additionalProperties": false,
"required": ["targetDevices"],
"properties": {
"targetDevices": {
"type": "array",
"items": {
"title": "Device alias",
"type": "string"
}
},
"encryption": {
"$ref": "#/$defs/encryption"
}
}
}
]
}
}
}
}
]
},
"logicalVolumes": {
"title": "Logical volumes",
Expand Down
7 changes: 5 additions & 2 deletions rust/agama-lib/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,13 @@
// To contact SUSE LLC about this file by physical or electronic mail, you may
// find current contact information at www.suse.com.

use curl;
use serde_json;
use std::io;
use thiserror::Error;
use zbus::{self, zvariant};

use crate::transfer::TransferError;

#[derive(Error, Debug)]
pub enum ServiceError {
#[error("Cannot generate Agama logs: {0}")]
Expand Down Expand Up @@ -66,12 +67,14 @@ pub enum ServiceError {
// Specific error when something does not work as expected, but it is not user fault
#[error("Internal error. Please report a bug and attach logs. Details: {0}")]
InternalError(String),
#[error("Could not read the file: '{0}'")]
CouldNotTransferFile(#[from] TransferError),
}

#[derive(Error, Debug)]
pub enum ProfileError {
#[error("Could not read the profile")]
Unreachable(#[from] curl::Error),
Unreachable(#[from] TransferError),
#[error("Jsonnet evaluation failed:\n{0}")]
EvaluationError(String),
#[error("I/O error")]
Expand Down
1 change: 1 addition & 0 deletions rust/agama-lib/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ pub mod proxies;
mod store;
pub use store::Store;
pub mod questions;
pub mod transfer;
use crate::error::ServiceError;
use reqwest::{header, Client};

Expand Down
37 changes: 37 additions & 0 deletions rust/agama-lib/src/transfer.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
//! File transfer API for Agama.
//!
//! Implement a file transfer API which, in the future, will support 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.

use std::io::Write;

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

#[derive(Error, Debug)]
#[error(transparent)]
pub struct TransferError(#[from] curl::Error);
pub type TransferResult<T> = Result<T, TransferError>;

/// File transfer API
pub struct Transfer {}

impl Transfer {
/// Retrieves and writes the data from an URL
///
/// * `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.url(url)?;

let mut transfer = handle.transfer();
transfer.write_function(|buf| Ok(out_fd.write(buf).unwrap()))?;
transfer.perform()?;
Ok(())
}
}
23 changes: 23 additions & 0 deletions service/lib/agama/config.rb
Original file line number Diff line number Diff line change
Expand Up @@ -186,8 +186,31 @@ def arch_elements_from(*keys, property: nil, default: [])
end.compact
end

# Default paths to be created for the product.
#
# @return [Array<String>]
def default_paths
data.dig("storage", "volumes") || []
end

# Mandatory paths to be created for the product.
#
# @return [Array<String>]
def mandatory_paths
default_paths.select { |p| mandatory_path?(p) }
end

private

def mandatory_path?(path)
templates = data.dig("storage", "volume_templates") || []
template = templates.find { |t| t["mount_path"] == path }

return false unless template

template.dig("outline", "required") || false
end

# Simple deep merge
#
# @param a_hash [Hash] Default values
Expand Down
Loading

0 comments on commit e621b27

Please sign in to comment.