From: Fabian Ebner <f.ebner@proxmox.com>
To: pbs-devel@lists.proxmox.com,
"Fabian Grünbichler" <f.gruenbichler@proxmox.com>,
"PVE development discussion" <pve-devel@pve.proxmox.com>
Subject: Re: [pbs-devel] [PATCH v6 proxmox-apt 06/11] add release_upgrade function and constants for the current and upgrade suite
Date: Fri, 18 Jun 2021 08:50:55 +0200 [thread overview]
Message-ID: <d20877c0-6eec-e147-622a-d9ed58e8499e@proxmox.com> (raw)
In-Reply-To: <1623937266.48p9vpkidm.astroid@nora.none>
Am 17.06.21 um 16:16 schrieb Fabian Grünbichler:
> On June 11, 2021 1:43 pm, Fabian Ebner wrote:
>> useful for major upgrades. The stable branch can enable the upgrade, and bump
>> the minor version, while the master branch will adapt to the new release and
>> bump the major version. Each product can depend on the the new major version
>> after branching off the stable branch, and once the release is out, its stable
>> branch can depend on the new minor version.
>>
>> Signed-off-by: Fabian Ebner <f.ebner@proxmox.com>
>> ---
>>
>> Changes from v5:
>> * Make function less general (only care about the current release upgrade)
>> and handle special case for security repository.
>> * Make list of suite available as constants.
>> * Get the current release from /etc/os-release and abort if it is not the
>> same as STABLE_SUITE.
>> * Add a constant UPGRADE_SUITE which can be set for the library's last
>> release in the stable-X branch to enable the release_upgrade() function.
>>
>> .gitignore | 1 +
>> src/repositories/check.rs | 57 +++++++++++++-----------
>> src/repositories/mod.rs | 92 +++++++++++++++++++++++++++++++++++++++
>> tests/repositories.rs | 79 ++++++++++++++++++++++++++++++++-
>> 4 files changed, 202 insertions(+), 27 deletions(-)
>>
>> diff --git a/.gitignore b/.gitignore
>> index db6f13e..de68da9 100644
>> --- a/.gitignore
>> +++ b/.gitignore
>> @@ -1,6 +1,7 @@
>> Cargo.lock
>> target/
>> tests/sources.list.d.actual
>> +tests/sources.list.d.upgraded.actual
>> tests/sources.list.d.digest
>> proxmox-apt-*/
>> *proxmox-apt*.buildinfo
>> diff --git a/src/repositories/check.rs b/src/repositories/check.rs
>> index 585c28d..e0ec93e 100644
>> --- a/src/repositories/check.rs
>> +++ b/src/repositories/check.rs
>> @@ -5,8 +5,34 @@ use crate::types::{
>> APTRepositoryPackageType,
>> };
>>
>> +/// The (code)names of old Debian releases.
>> +pub const OLD_SUITES: [&str; 7] = [
>> + "lenny",
>> + "squeeze",
>> + "wheezy",
>> + "jessie",
>> + "stretch",
>> + "oldoldstable",
>> + "oldstable",
>> +];
>> +
>> +/// The codename of the current stable Debian release.
>> +pub const STABLE_SUITE: &str = "buster";
>> +/// The codename of the next stable Debian release.
>> +pub const NEXT_STABLE_SUITE: &str = "bullseye";
>> +
>> +/// The (code)names of new/testing Debian releases.
>> +pub const NEW_SUITES: [&str; 6] = [
>> + "bookworm",
>> + "trixie",
>> + "testing",
>> + "unstable",
>> + "sid",
>> + "experimental",
>> +];
>> +
>> /// Splits the suite into its base part and variant.
>> -fn suite_variant(suite: &str) -> (&str, &str) {
>> +pub fn suite_variant(suite: &str) -> (&str, &str) {
>> let variants = ["-backports-sloppy", "-backports", "-updates", "/updates"];
>>
>> for variant in variants.iter() {
>> @@ -19,7 +45,7 @@ fn suite_variant(suite: &str) -> (&str, &str) {
>> }
>>
>> /// Get the host part from a given URI.
>> -fn host_from_uri(uri: &str) -> Option<&str> {
>> +pub fn host_from_uri(uri: &str) -> Option<&str> {
>> if let Some(begin) = uri.find("://") {
>> let mut host = uri.split_at(begin + 3).1;
>>
>> @@ -145,34 +171,13 @@ impl APTRepository {
>> /// Checks if old or unstable suites are configured and also that the
>> /// `stable` keyword is not used.
>> fn check_suites(&self, add_info: &mut dyn FnMut(String, String)) {
>> - let old_suites = [
>> - "lenny",
>> - "squeeze",
>> - "wheezy",
>> - "jessie",
>> - "stretch",
>> - "oldoldstable",
>> - "oldstable",
>> - ];
>> -
>> - let next_suite = "bullseye";
>> -
>> - let new_suites = [
>> - "bookworm",
>> - "trixie",
>> - "testing",
>> - "unstable",
>> - "sid",
>> - "experimental",
>> - ];
>> -
>> if self
>> .types
>> .iter()
>> .any(|package_type| *package_type == APTRepositoryPackageType::Deb)
>> {
>> for suite in self.suites.iter() {
>> - if old_suites
>> + if OLD_SUITES
>> .iter()
>> .any(|base_suite| suite_variant(suite).0 == *base_suite)
>> {
>> @@ -182,14 +187,14 @@ impl APTRepository {
>> );
>> }
>>
>> - if suite_variant(suite).0 == next_suite {
>> + if suite_variant(suite).0 == NEXT_STABLE_SUITE {
>> add_info(
>> "ignore-pre-upgrade-warning".to_string(),
>> format!("suite '{}' should not be used in production!", suite),
>> );
>> }
>>
>> - if new_suites
>> + if NEW_SUITES
>> .iter()
>> .any(|base_suite| suite_variant(suite).0 == *base_suite)
>> {
>> diff --git a/src/repositories/mod.rs b/src/repositories/mod.rs
>> index 2c01011..eceede3 100644
>> --- a/src/repositories/mod.rs
>> +++ b/src/repositories/mod.rs
>> @@ -1,4 +1,5 @@
>> use std::collections::BTreeMap;
>> +use std::io::{BufRead, BufReader};
>> use std::path::PathBuf;
>>
>> use anyhow::{bail, format_err, Error};
>> @@ -21,6 +22,11 @@ mod writer;
>> const APT_SOURCES_LIST_FILENAME: &str = "/etc/apt/sources.list";
>> const APT_SOURCES_LIST_DIRECTORY: &str = "/etc/apt/sources.list.d/";
>>
>> +/// The codename of the current stable Debian release.
>> +pub const STABLE_SUITE: &str = check::STABLE_SUITE;
>> +/// The codename of the next stable Debian release or `None` if an upgrade is not yet possible.
>> +pub const UPGRADE_SUITE: Option<&str> = None;
>> +
>> impl APTRepository {
>> /// Crates an empty repository.
>> fn new(file_type: APTRepositoryFileType) -> Self {
>> @@ -265,6 +271,92 @@ pub fn repositories() -> Result<(Vec<APTRepositoryFile>, Vec<APTRepositoryFileEr
>> Ok((files, errors))
>> }
>>
>> +/// Read the `VERSION_CODENAME` from `/etc/os-release`.
>> +fn get_release_codename() -> Result<String, Error> {
>> + let raw = std::fs::read("/etc/os-release")
>> + .map_err(|err| format_err!("unable to read '/etc/os-release' - {}", err))?;
>> +
>> + let reader = BufReader::new(&*raw);
>> +
>> + for line in reader.lines() {
>> + let line = line.map_err(|err| format_err!("unable to read '/etc/os-release' - {}", err))?;
>> +
>> + if let Some(codename) = line.strip_prefix("VERSION_CODENAME=") {
>> + let codename = codename.trim_matches(&['"', '\''][..]);
>> + return Ok(codename.to_string());
>> + }
>> + }
>> +
>> + bail!("unable to parse codename from '/etc/os-release'");
>> +}
>> +
>> +/// For enabled repositories, replaces each occurence of the `STABLE_SUITE` with the
>> +/// `UPGRADE_SUITE` suite, including variants (e.g. `-updates`).
>> +///
>> +/// Returns an error if the `UPGRADE_SUITE` is currently `None`, i.e. upgrade not yet possible.
>> +///
>> +/// Returns an error if the `VERSION_CODENAME` from `/etc/os-release` is not `STABLE_SUITE`.
>> +///
>> +/// Also handles the special case `buster/updates` -> `bullseye-security` when the URI is
>> +/// security.debian.org, but fails if there's additional URIs.
>> +pub fn release_upgrade(files: &mut [APTRepositoryFile]) -> Result<(), Error> {
>> + let upgrade_suite = match UPGRADE_SUITE {
>> + Some(suite) => suite,
>> + None => bail!("release upgrade is not yet possible"),
>> + };
>> +
>> + let current = get_release_codename()?;
>> +
>> + if current == upgrade_suite {
>> + bail!("already installed '{}'", current);
>> + }
>> +
>> + if current != STABLE_SUITE {
>> + bail!(
>> + "unexpected release '{}' - cannot prepare repositories for upgrade",
>> + current
>> + );
>> + }
>> +
>> + for file in files.iter_mut() {
>> + for repo in file.repositories.iter_mut() {
>> + if !repo.enabled {
>> + continue;
>> + }
>> +
>> + for i in 0..repo.suites.len() {
>> + let suite = &repo.suites[i];
>> +
>> + // FIXME special case for security repository can be removed for Debian Bookworm
>> +
>> + let is_security_uri = |uri| {
>> + check::host_from_uri(uri).map_or(false, |host| host == "security.debian.org")
>
> this should probably also check for the not uncommon case of
>
> https://deb.debian.org/debian-security (or http)
>
Thanks for the info. The Debian wiki[0] only mentions
security.debian.org, so I wasn't aware of that. And somebody might have
a FQDN here too...
[0]: https://wiki.debian.org/NewInBullseye
>> + };
>> +
>> + let has_security_uri = repo.uris.iter().any(|uri| is_security_uri(uri));
>> + let has_only_security_uri = repo.uris.iter().all(|uri| is_security_uri(uri));
>> +
>> + if suite == "buster/updates" && has_security_uri {
>> + if !has_only_security_uri {
>> + bail!("cannot replace 'buster/updates' suite - multiple URIs");
>> + }
>> +
>> + repo.suites[i] = "bullseye-security".to_string();
>> +
>> + continue;
>> + }
>> +
>> + let (base, variant) = check::suite_variant(suite);
>> + if base == STABLE_SUITE {
>> + repo.suites[i] = format!("{}{}", upgrade_suite, variant);
>> + }
>> + }
>> + }
>> + }
>> +
>> + Ok(())
>> +}
>> +
>> /// Write the repositories for each file.
>> ///
>> /// Returns an error for each file that could not be written successfully.
>> diff --git a/tests/repositories.rs b/tests/repositories.rs
>> index 3919077..ee7f1a8 100644
>> --- a/tests/repositories.rs
>> +++ b/tests/repositories.rs
>> @@ -4,7 +4,7 @@ use anyhow::{bail, format_err, Error};
>>
>> use proxmox_apt::repositories::{
>> check_repositories, common_digest, enterprise_repository_enabled,
>> - no_subscription_repository_enabled, write_repositories,
>> + no_subscription_repository_enabled, release_upgrade, write_repositories,
>> };
>> use proxmox_apt::types::{APTRepositoryFile, APTRepositoryInfo};
>>
>> @@ -292,3 +292,80 @@ fn test_common_digest() -> Result<(), Error> {
>>
>> Ok(())
>> }
>> +
>> +#[test]
>> +fn test_release_upgrade() -> Result<(), Error> {
>> + let test_dir = std::env::current_dir()?.join("tests");
>> + let read_dir = test_dir.join("sources.list.d");
>> + let write_dir = test_dir.join("sources.list.d.upgraded.actual");
>> + let expected_dir = test_dir.join("sources.list.d.upgraded.expected");
>> +
>> + if write_dir.is_dir() {
>> + std::fs::remove_dir_all(&write_dir)
>> + .map_err(|err| format_err!("unable to remove dir {:?} - {}", write_dir, err))?;
>> + }
>> +
>> + std::fs::create_dir_all(&write_dir)
>> + .map_err(|err| format_err!("unable to create dir {:?} - {}", write_dir, err))?;
>> +
>> + let mut files = vec![];
>> + let mut errors = vec![];
>> +
>> + for entry in std::fs::read_dir(read_dir)? {
>> + let path = entry?.path();
>> +
>> + match APTRepositoryFile::new(&path)? {
>> + Some(mut file) => match file.parse() {
>> + Ok(()) => files.push(file),
>> + Err(err) => errors.push(err),
>> + },
>> + None => bail!("unexpected None for '{:?}'", path),
>> + }
>> + }
>> +
>> + assert!(errors.is_empty());
>> +
>> + for file in files.iter_mut() {
>> + let path = PathBuf::from(&file.path);
>> + let new_path = write_dir.join(path.file_name().unwrap());
>> + file.path = new_path.into_os_string().into_string().unwrap();
>> + file.digest = None;
>> + }
>> +
>> + let res = release_upgrade(&mut files);
>> +
>> + // FIXME adapt test after branching off the stable-X branch!
>> + assert!(res.is_err());
>> + if res.is_err() {
>> + return Ok(());
>> + }
>> +
>> + write_repositories(&files).map_err(|err| format_err!("{:?}", err))?;
>> +
>> + let mut expected_count = 0;
>> +
>> + for entry in std::fs::read_dir(expected_dir)? {
>> + expected_count += 1;
>> +
>> + let expected_path = entry?.path();
>> + let actual_path = write_dir.join(expected_path.file_name().unwrap());
>> +
>> + let expected_contents = std::fs::read(&expected_path)
>> + .map_err(|err| format_err!("unable to read {:?} - {}", expected_path, err))?;
>> +
>> + let actual_contents = std::fs::read(&actual_path)
>> + .map_err(|err| format_err!("unable to read {:?} - {}", actual_path, err))?;
>> +
>> + assert_eq!(
>> + expected_contents, actual_contents,
>> + "Use\n\ndiff {:?} {:?}\n\nif you're not fluent in byte decimals",
>> + expected_path, actual_path
>> + );
>> + }
>> +
>> + let actual_count = std::fs::read_dir(write_dir)?.count();
>> +
>> + assert_eq!(expected_count, actual_count);
>> +
>> + Ok(())
>> +}
>> --
>> 2.20.1
>>
>>
>>
>> _______________________________________________
>> pbs-devel mailing list
>> pbs-devel@lists.proxmox.com
>> https://lists.proxmox.com/cgi-bin/mailman/listinfo/pbs-devel
>>
>>
>>
>
>
> _______________________________________________
> pbs-devel mailing list
> pbs-devel@lists.proxmox.com
> https://lists.proxmox.com/cgi-bin/mailman/listinfo/pbs-devel
>
>
next prev parent reply other threads:[~2021-06-18 6:51 UTC|newest]
Thread overview: 37+ messages / expand[flat|nested] mbox.gz Atom feed top
2021-06-11 11:43 [pbs-devel] [PATCH-SERIES v6] APT repositories API/UI Fabian Ebner
2021-06-11 11:43 ` [pbs-devel] [PATCH v6 proxmox-apt 01/11] initial commit Fabian Ebner
2021-06-18 8:14 ` [pbs-devel] [pve-devel] " Fabian Grünbichler
2021-06-11 11:43 ` [pbs-devel] [PATCH v6 proxmox-apt 02/11] add files for Debian packaging Fabian Ebner
2021-06-11 11:43 ` [pbs-devel] [PATCH v6 proxmox-apt 03/11] add functions to check for Proxmox repositories Fabian Ebner
2021-06-11 11:43 ` [pbs-devel] [PATCH v6 proxmox-apt 04/11] add check_repositories function Fabian Ebner
2021-06-17 8:39 ` [pbs-devel] [pve-devel] " Wolfgang Bumiller
2021-06-18 6:42 ` Fabian Ebner
2021-06-17 14:16 ` Fabian Grünbichler
2021-06-11 11:43 ` [pbs-devel] [PATCH v6 proxmox-apt 05/11] add common_digest helper Fabian Ebner
2021-06-11 11:43 ` [pbs-devel] [PATCH v6 proxmox-apt 06/11] add release_upgrade function and constants for the current and upgrade suite Fabian Ebner
2021-06-17 14:16 ` Fabian Grünbichler
2021-06-18 6:50 ` Fabian Ebner [this message]
2021-06-11 11:43 ` [pbs-devel] [PATCH v6 proxmox-apt 07/11] bump version to 0.1.1-1 Fabian Ebner
2021-06-11 11:43 ` [pbs-devel] [PATCH v6 proxmox-apt 08/11] update for bullseye Fabian Ebner
2021-06-11 11:43 ` [pbs-devel] [PATCH v6 proxmox-apt 09/11] bump version to 1.0.0-1 Fabian Ebner
2021-06-11 11:43 ` [pbs-devel] [PATCH v6 proxmox-apt 10/11] allow upgrade to bullseye Fabian Ebner
2021-06-11 11:44 ` [pbs-devel] [PATCH v6 proxmox-apt 11/11] bump version to 0.2.0-1 Fabian Ebner
2021-06-11 11:44 ` [pbs-devel] [PATCH v6 proxmox-widget-toolkit 1/3] add UI for APT repositories Fabian Ebner
2021-06-11 11:44 ` [pbs-devel] [PATCH v6 proxmox-widget-toolkit 2/3] APT repositories: add warnings Fabian Ebner
2021-06-11 11:44 ` [pbs-devel] [PATCH v6 proxmox-widget-toolkit 3/3] add upgrade button Fabian Ebner
2021-06-11 11:44 ` [pbs-devel] [PATCH v6 proxmox-backup 1/6] depend on new proxmox-apt crate Fabian Ebner
2021-06-11 11:44 ` [pbs-devel] [PATCH v6 proxmox-backup 2/6] api: apt: add repositories call Fabian Ebner
2021-06-11 11:44 ` [pbs-devel] [PATCH v6 proxmox-backup 3/6] ui: add APT repositories Fabian Ebner
2021-06-11 11:44 ` [pbs-devel] [PATCH v6 proxmox-backup 4/6] api: apt: add check_repositories_call Fabian Ebner
2021-06-11 11:44 ` [pbs-devel] [PATCH v6 proxmox-backup 5/6] add upgrade_repositories call Fabian Ebner
2021-06-18 8:21 ` [pbs-devel] [pve-devel] " Fabian Grünbichler
2021-06-11 11:44 ` [pbs-devel] [RFC v6 proxmox-backup 6/6] enable release upgrade for package repositories Fabian Ebner
2021-06-11 11:44 ` [pbs-devel] [PATCH v6 pve-rs 1/4] initial commit Fabian Ebner
2021-06-11 11:44 ` [pbs-devel] [PATCH v6 pve-rs 2/4] add files for Debian packaging Fabian Ebner
2021-06-11 11:44 ` [pbs-devel] [PATCH v6 pve-rs 3/4] apt: add upgrade_repositories call Fabian Ebner
2021-06-11 11:44 ` [pbs-devel] [PATCH v6 pve-rs 4/4] depend on proxmox-apt 0.2.0 Fabian Ebner
2021-06-11 11:44 ` [pbs-devel] [PATCH v6 pve-manager 1/5] api: apt: add call to list repositories Fabian Ebner
2021-06-11 11:44 ` [pbs-devel] [PATCH v6 pve-manager 2/5] ui: add panel for listing APT repositories Fabian Ebner
2021-06-11 11:44 ` [pbs-devel] [PATCH v6 pve-manager 3/5] api: apt: add call for repository check Fabian Ebner
2021-06-11 11:44 ` [pbs-devel] [PATCH v6 pve-manager 4/5] api: apt: add upgrade repos call Fabian Ebner
2021-06-11 11:44 ` [pbs-devel] [PATCH v6 pve-manager 5/5] ui: node config: enable release upgrade button for package repositories Fabian Ebner
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=d20877c0-6eec-e147-622a-d9ed58e8499e@proxmox.com \
--to=f.ebner@proxmox.com \
--cc=f.gruenbichler@proxmox.com \
--cc=pbs-devel@lists.proxmox.com \
--cc=pve-devel@pve.proxmox.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox