Skip to content

Commit

Permalink
feat: add support to run pre/post scripts (#1673)
Browse files Browse the repository at this point in the history
This is the first implementation of pre/post-installation scripts. They
are not as capable as their AutoYaST counterparts, but the idea is to
evolve them in the future.

## Specifying a script

> [!WARNING]
> JSON does not support multiline values (a.k.a. blocks) but Jsonnet
does it. Perhaps we should consider using Jsonnet in the `agama config
edit` command.

```jsonnet
{
  scripts: {
    pre: [
      {
         name: "say-hi",
         body: |||
           #!/bin/bash
           echo "before starting the installation"
         |||
      }
    ]
  }
}
```

> [!NOTE]
> What about having a plain list where each script has a `type` (or
`group`) like `pre`, `post`, etc.?

## When scripts get executed

* Pre-scripts: when loading a profile. We plan to do [some
improvements](https://trello.com/c/PiOGzXSn/584-agama-running-pre-scripts-in-the-right-place)
to support the single-product case properly.
* Post-scripts: after the installation.

## Implementation details

The scripting support is written in Rust and does not use the YaST
scripts code anymore. We will rewrite the Manager in Rust (at least the
Agama-specific part) in the future, and the scripting support will be
part of that.

## Future

Agama scripts will gain additional features soon:

* Running in a `chroot`.
* Better error handling if the shebang line is not present.
* Running after the first boot.

## Pending tasks

- [x] Extend the JSON schema
- [x] Copy the resulting files to the installed system
- [x] Add some logging
- [x] Clean-up the old scripts when reloading a profile
  • Loading branch information
imobachgs authored Oct 18, 2024
2 parents 1b45405 + 1322ca9 commit 78edd27
Show file tree
Hide file tree
Showing 24 changed files with 997 additions and 58 deletions.
25 changes: 24 additions & 1 deletion rust/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions rust/agama-lib/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ curl = { version = "0.4.47", features = ["protocol-ftp"] }
jsonwebtoken = "9.3.0"
chrono = { version = "0.4.38", default-features = false, features = ["now", "std", "alloc", "clock"] }
home = "0.5.9"
strum = { version = "0.26.3", features = ["derive"] }

[dev-dependencies]
httpmock = "0.7.0"
Expand Down
143 changes: 89 additions & 54 deletions rust/agama-lib/share/profile.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -54,9 +54,7 @@
"items": {
"type": "object",
"additionalProperties": false,
"required": [
"id"
],
"required": ["id"],
"properties": {
"id": {
"title": "Connection ID",
Expand All @@ -79,22 +77,12 @@
"method4": {
"title": "IPv4 configuration method",
"type": "string",
"enum": [
"auto",
"manual",
"link-local",
"disabled"
]
"enum": ["auto", "manual", "link-local", "disabled"]
},
"method6": {
"title": "IPv6 configuration method",
"type": "string",
"enum": [
"auto",
"manual",
"link-local",
"disabled"
]
"enum": ["auto", "manual", "link-local", "disabled"]
},
"gateway4": {
"title": "Connection gateway address",
Expand Down Expand Up @@ -162,12 +150,7 @@
"mode": {
"title": "Wireless network mode",
"type": "string",
"enum": [
"infrastructure",
"adhoc",
"mesh",
"ap"
]
"enum": ["infrastructure", "adhoc", "mesh", "ap"]
},
"hidden": {
"title": "Indicates that the wireless network is not broadcasting its SSID",
Expand All @@ -176,15 +159,12 @@
"band": {
"title": "Frequency band of the wireless network",
"type": "string",
"enum": [
"a",
"bg"
]
"enum": ["a", "bg"]
},
"channel": {
"title": "Wireless channel of the wireless network",
"type": "integer",
"minimum" : 0
"minimum": 0
},
"bssid": {
"title": "Only allow connection to this mac address",
Expand All @@ -195,34 +175,23 @@
"items": {
"title": "A list of group/broadcast encryption algorithms",
"type": "string",
"enum": [
"wep40",
"wep104",
"tkip",
"ccmp"
]
"enum": ["wep40", "wep104", "tkip", "ccmp"]
}
},
"pairwiseAlgorithms": {
"type": "array",
"items": {
"title": "A list of pairwise encryption algorithms",
"type": "string",
"enum": [
"tkip",
"ccmp"
]
"enum": ["tkip", "ccmp"]
}
},
"wpaProtocolVersions": {
"type": "array",
"items": {
"title": "A list of allowed WPA protocol versions",
"type": "string",
"enum": [
"wpa",
"rsn"
]
"enum": ["wpa", "rsn"]
}
},
"pmf": {
Expand Down Expand Up @@ -369,10 +338,7 @@
"peapVersion": {
"title": "Which PEAP version is used when PEAP is set as the EAP method in the 'eap' property",
"type": "string",
"enum": [
"0",
"1"
]
"enum": ["0", "1"]
},
"peapLabel": {
"title": "Force the use of the new PEAP label during key derivation",
Expand Down Expand Up @@ -406,11 +372,7 @@
"examples": ["nots3cr3t"]
}
},
"required": [
"fullName",
"userName",
"password"
]
"required": ["fullName", "userName", "password"]
},
"root": {
"title": "Root authentication settings",
Expand Down Expand Up @@ -906,7 +868,7 @@
"description": "Name of a disk device.",
"type": "string",
"examples": ["/dev/vda"]
}
}
}
},
{
Expand Down Expand Up @@ -967,6 +929,30 @@
"items": {
"type": "object"
}
},
"scripts": {
"title": "User-defined installation scripts",
"description": "User-defined scripts to run at different points of the installation",
"type": "object",
"additionalProperties": false,
"properties": {
"pre": {
"title": "Pre-installation scripts",
"description": "User-defined scripts to run before the installation starts",
"type": "array",
"items": {
"$ref": "#/$defs/script"
}
},
"post": {
"title": "Post-installation scripts",
"description": "User-defined scripts to run after the installation finishes",
"type": "array",
"items": {
"$ref": "#/$defs/script"
}
}
}
}
},
"$defs": {
Expand Down Expand Up @@ -1013,7 +999,12 @@
},
"minItems": 1,
"maxItems": 2,
"examples": [[1024, "current"], ["1 GiB", "5 GiB"], [1024, "2 GiB"], ["2 GiB"]]
"examples": [
[1024, "current"],
["1 GiB", "5 GiB"],
[1024, "2 GiB"],
["2 GiB"]
]
},
{
"title": "Size range",
Expand Down Expand Up @@ -1206,8 +1197,22 @@
{
"title": "File system type",
"enum": [
"bcachefs", "btrfs", "exfat", "ext2", "ext3", "ext4", "f2fs", "jfs",
"nfs", "nilfs2", "ntfs", "reiserfs", "swap", "tmpfs", "vfat", "xfs"
"bcachefs",
"btrfs",
"exfat",
"ext2",
"ext3",
"ext4",
"f2fs",
"jfs",
"nfs",
"nilfs2",
"ntfs",
"reiserfs",
"swap",
"tmpfs",
"vfat",
"xfs"
]
},
{
Expand Down Expand Up @@ -1330,7 +1335,15 @@
},
"id": {
"title": "Partition ID",
"enum": ["linux", "swap", "lvm", "raid", "esp", "prep", "bios_boot"]
"enum": [
"linux",
"swap",
"lvm",
"raid",
"esp",
"prep",
"bios_boot"
]
},
"size": {
"title": "Partition size",
Expand Down Expand Up @@ -1394,6 +1407,28 @@
"lvStripeSize": {
"title": "Stripe size",
"$ref": "#/$defs/sizeValue"
},
"script": {
"title": "User-defined installation script",
"type": "object",
"additionalProperties": false,
"properties": {
"name": {
"description": "Script name, to be used as file name",
"type": "string"
},
"body": {
"title": "Script content",
"description": "Script content, starting with the shebang",
"type": "string"
},
"url": {
"title": "Script URL",
"description": "URL to fetch the script from"
}
},
"required": ["name"],
"oneOf": [{ "required": ["body"] }, { "required": ["url"] }]
}
}
}
4 changes: 3 additions & 1 deletion rust/agama-lib/src/install_settings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
//! This module implements the mechanisms to load and store the installation settings.
use crate::{
localization::LocalizationSettings, network::NetworkSettings, product::ProductSettings,
software::SoftwareSettings, users::UserSettings,
scripts::ScriptsConfig, software::SoftwareSettings, users::UserSettings,
};
use serde::{Deserialize, Serialize};
use serde_json::value::RawValue;
Expand Down Expand Up @@ -55,6 +55,8 @@ pub struct InstallSettings {
pub network: Option<NetworkSettings>,
#[serde(default)]
pub localization: Option<LocalizationSettings>,
#[serde(default)]
pub scripts: Option<ScriptsConfig>,
}

impl InstallSettings {
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 scripts;
pub mod transfer;
use crate::error::ServiceError;
use reqwest::{header, Client};
Expand Down
32 changes: 32 additions & 0 deletions rust/agama-lib/src/scripts.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// Copyright (c) [2024] SUSE LLC
//
// All Rights Reserved.
//
// This program is free software; you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by the Free
// Software Foundation; either version 2 of the License, or (at your option)
// any later version.
//
// This program is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
// more details.
//
// You should have received a copy of the GNU General Public License along
// with this program; if not, contact SUSE LLC.
//
// To contact SUSE LLC about this file by physical or electronic mail, you may
// find current contact information at www.suse.com.

//! Implements support for handling the user-defined scripts.

mod client;
mod error;
mod model;
mod settings;
mod store;

pub use error::ScriptError;
pub use model::*;
pub use settings::*;
pub use store::ScriptsStore;
Loading

0 comments on commit 78edd27

Please sign in to comment.