forked from Proxmox/proxmox
apt: use api types from apt-api-types crate
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
This commit is contained in:
parent
e56e39185a
commit
f536a91b2f
@ -8,7 +8,7 @@ license.workspace = true
|
||||
repository.workspace = true
|
||||
homepage.workspace = true
|
||||
|
||||
exclude = [ "debian" ]
|
||||
exclude = ["debian"]
|
||||
|
||||
[dependencies]
|
||||
anyhow.workspace = true
|
||||
@ -20,4 +20,5 @@ serde_json.workspace = true
|
||||
|
||||
rfc822-like = "0.2.1"
|
||||
|
||||
proxmox-schema = { workspace = true, features = [ "api-macro" ] }
|
||||
proxmox-apt-api-types.workspace = true
|
||||
proxmox-config-digest = { workspace = true, features = ["openssl"] }
|
||||
|
@ -10,8 +10,9 @@ Build-Depends: debhelper (>= 12),
|
||||
librust-hex-0.4+default-dev <!nocheck>,
|
||||
librust-once-cell-1+default-dev (>= 1.3.1-~~) <!nocheck>,
|
||||
librust-openssl-0.10+default-dev <!nocheck>,
|
||||
librust-proxmox-schema-3+api-macro-dev (>= 3.1.1-~~) <!nocheck>,
|
||||
librust-proxmox-schema-3+default-dev (>= 3.1.1-~~) <!nocheck>,
|
||||
librust-proxmox-apt-api-types-1+default-dev <!nocheck>,
|
||||
librust-proxmox-config-digest-0.1+default-dev <!nocheck>,
|
||||
librust-proxmox-config-digest-0.1+openssl-dev <!nocheck>,
|
||||
librust-rfc822-like-0.2+default-dev (>= 0.2.1-~~) <!nocheck>,
|
||||
librust-serde-1+default-dev <!nocheck>,
|
||||
librust-serde-1+derive-dev <!nocheck>,
|
||||
@ -33,8 +34,9 @@ Depends:
|
||||
librust-hex-0.4+default-dev,
|
||||
librust-once-cell-1+default-dev (>= 1.3.1-~~),
|
||||
librust-openssl-0.10+default-dev,
|
||||
librust-proxmox-schema-3+api-macro-dev (>= 3.1.1-~~),
|
||||
librust-proxmox-schema-3+default-dev (>= 3.1.1-~~),
|
||||
librust-proxmox-apt-api-types-1+default-dev,
|
||||
librust-proxmox-config-digest-0.1+default-dev,
|
||||
librust-proxmox-config-digest-0.1+openssl-dev,
|
||||
librust-rfc822-like-0.2+default-dev (>= 0.2.1-~~),
|
||||
librust-serde-1+default-dev,
|
||||
librust-serde-1+derive-dev,
|
||||
|
@ -1,123 +1,29 @@
|
||||
use std::fmt::Display;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use anyhow::{format_err, Error};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::repositories::release::DebianCodename;
|
||||
use crate::repositories::repository::{
|
||||
APTRepository, APTRepositoryFileType, APTRepositoryPackageType,
|
||||
use proxmox_apt_api_types::{
|
||||
APTRepository, APTRepositoryFile, APTRepositoryFileError, APTRepositoryFileType,
|
||||
APTRepositoryInfo, APTRepositoryPackageType,
|
||||
};
|
||||
|
||||
use crate::repositories::repository::APTRepositoryImpl;
|
||||
|
||||
use proxmox_schema::api;
|
||||
|
||||
mod list_parser;
|
||||
use list_parser::APTListFileParser;
|
||||
|
||||
mod sources_parser;
|
||||
use sources_parser::APTSourcesFileParser;
|
||||
|
||||
use proxmox_config_digest::ConfigDigest;
|
||||
|
||||
trait APTRepositoryParser {
|
||||
/// Parse all repositories including the disabled ones and push them onto
|
||||
/// the provided vector.
|
||||
fn parse_repositories(&mut self) -> Result<Vec<APTRepository>, Error>;
|
||||
}
|
||||
|
||||
#[api(
|
||||
properties: {
|
||||
"file-type": {
|
||||
type: APTRepositoryFileType,
|
||||
},
|
||||
repositories: {
|
||||
description: "List of APT repositories.",
|
||||
type: Array,
|
||||
items: {
|
||||
type: APTRepository,
|
||||
},
|
||||
},
|
||||
digest: {
|
||||
description: "Digest for the content of the file.",
|
||||
optional: true,
|
||||
type: Array,
|
||||
items: {
|
||||
description: "Digest byte.",
|
||||
type: u8,
|
||||
},
|
||||
},
|
||||
},
|
||||
)]
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
/// Represents an abstract APT repository file.
|
||||
pub struct APTRepositoryFile {
|
||||
/// The path to the file. If None, `contents` must be set directly.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub path: Option<String>,
|
||||
|
||||
/// The type of the file.
|
||||
pub file_type: APTRepositoryFileType,
|
||||
|
||||
/// List of repositories in the file.
|
||||
pub repositories: Vec<APTRepository>,
|
||||
|
||||
/// The file content, if already parsed.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub content: Option<String>,
|
||||
|
||||
/// Digest of the original contents.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub digest: Option<[u8; 32]>,
|
||||
}
|
||||
|
||||
#[api]
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
/// Error type for problems with APT repository files.
|
||||
pub struct APTRepositoryFileError {
|
||||
/// The path to the problematic file.
|
||||
pub path: String,
|
||||
|
||||
/// The error message.
|
||||
pub error: String,
|
||||
}
|
||||
|
||||
impl Display for APTRepositoryFileError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "proxmox-apt error for '{}' - {}", self.path, self.error)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for APTRepositoryFileError {
|
||||
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
#[api]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
/// Additional information for a repository.
|
||||
pub struct APTRepositoryInfo {
|
||||
/// Path to the defining file.
|
||||
#[serde(default, skip_serializing_if = "String::is_empty")]
|
||||
pub path: String,
|
||||
|
||||
/// Index of the associated respository within the file (starting from 0).
|
||||
pub index: usize,
|
||||
|
||||
/// The property from which the info originates (e.g. "Suites")
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub property: Option<String>,
|
||||
|
||||
/// Info kind (e.g. "warning")
|
||||
pub kind: String,
|
||||
|
||||
/// Info message
|
||||
pub message: String,
|
||||
}
|
||||
|
||||
pub trait APTRepositoryFileImpl {
|
||||
/// Creates a new `APTRepositoryFile` without parsing.
|
||||
///
|
||||
@ -131,7 +37,7 @@ pub trait APTRepositoryFileImpl {
|
||||
/// Check if the file exists.
|
||||
fn exists(&self) -> bool;
|
||||
|
||||
fn read_with_digest(&self) -> Result<(Vec<u8>, [u8; 32]), APTRepositoryFileError>;
|
||||
fn read_with_digest(&self) -> Result<(Vec<u8>, ConfigDigest), APTRepositoryFileError>;
|
||||
|
||||
/// Create an `APTRepositoryFileError`.
|
||||
fn err(&self, error: Error) -> APTRepositoryFileError;
|
||||
@ -213,7 +119,8 @@ impl APTRepositoryFileImpl for APTRepositoryFile {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
let file_type = APTRepositoryFileType::try_from(&extension[..])
|
||||
let file_type = extension[..]
|
||||
.parse()
|
||||
.map_err(|_| new_err("invalid extension"))?;
|
||||
|
||||
if !file_name
|
||||
@ -250,15 +157,15 @@ impl APTRepositoryFileImpl for APTRepositoryFile {
|
||||
}
|
||||
}
|
||||
|
||||
fn read_with_digest(&self) -> Result<(Vec<u8>, [u8; 32]), APTRepositoryFileError> {
|
||||
fn read_with_digest(&self) -> Result<(Vec<u8>, ConfigDigest), APTRepositoryFileError> {
|
||||
if let Some(path) = &self.path {
|
||||
let content = std::fs::read(path).map_err(|err| self.err(format_err!("{}", err)))?;
|
||||
let digest = openssl::sha::sha256(&content);
|
||||
let digest = ConfigDigest::from_slice(&content);
|
||||
|
||||
Ok((content, digest))
|
||||
} else if let Some(ref content) = self.content {
|
||||
let content = content.as_bytes();
|
||||
let digest = openssl::sha::sha256(content);
|
||||
let digest = ConfigDigest::from_slice(content);
|
||||
Ok((content.to_vec(), digest))
|
||||
} else {
|
||||
Err(self.err(format_err!(
|
||||
@ -308,13 +215,13 @@ impl APTRepositoryFileImpl for APTRepositoryFile {
|
||||
}
|
||||
};
|
||||
|
||||
if let Some(digest) = self.digest {
|
||||
if let Some(digest) = &self.digest {
|
||||
if !self.exists() {
|
||||
return Err(self.err(format_err!("digest specified, but file does not exist")));
|
||||
}
|
||||
|
||||
let (_, current_digest) = self.read_with_digest()?;
|
||||
if digest != current_digest {
|
||||
if digest != ¤t_digest {
|
||||
return Err(self.err(format_err!("digest mismatch")));
|
||||
}
|
||||
}
|
||||
|
@ -184,7 +184,7 @@ impl<R: BufRead> APTListFileParser<R> {
|
||||
// e.g. quoted "deb" is not accepted by APT, so no need for quote word parsing here
|
||||
line = match line.split_once(|c| char::is_ascii_whitespace(&c)) {
|
||||
Some((package_type, rest)) => {
|
||||
repo.types.push(package_type.try_into()?);
|
||||
repo.types.push(package_type.parse()?);
|
||||
rest
|
||||
}
|
||||
None => return Ok(None), // empty line
|
||||
|
@ -108,7 +108,7 @@ impl<R: BufRead> APTSourcesFileParser<R> {
|
||||
}
|
||||
let mut types = Vec::<APTRepositoryPackageType>::new();
|
||||
for package_type in values {
|
||||
types.push((&package_type[..]).try_into()?);
|
||||
types.push((&package_type[..]).parse()?);
|
||||
}
|
||||
repo.types = types;
|
||||
}
|
||||
|
@ -4,21 +4,22 @@ use std::path::PathBuf;
|
||||
use anyhow::{bail, Error};
|
||||
|
||||
mod repository;
|
||||
pub use repository::APTRepositoryImpl;
|
||||
pub use repository::{
|
||||
APTRepository, APTRepositoryFileType, APTRepositoryOption, APTRepositoryPackageType,
|
||||
use proxmox_apt_api_types::{
|
||||
APTRepository, APTRepositoryFile, APTRepositoryFileError, APTRepositoryFileType,
|
||||
APTRepositoryHandle, APTRepositoryInfo, APTRepositoryOption, APTRepositoryPackageType,
|
||||
APTStandardRepository,
|
||||
};
|
||||
use proxmox_config_digest::ConfigDigest;
|
||||
pub use repository::APTRepositoryImpl;
|
||||
|
||||
mod file;
|
||||
pub use file::APTRepositoryFileImpl;
|
||||
pub use file::{APTRepositoryFile, APTRepositoryFileError, APTRepositoryInfo};
|
||||
|
||||
mod release;
|
||||
pub use release::{get_current_release_codename, DebianCodename};
|
||||
|
||||
mod standard;
|
||||
pub use standard::APTRepositoryHandleImpl;
|
||||
pub use standard::{APTRepositoryHandle, APTStandardRepository};
|
||||
pub use standard::{APTRepositoryHandleImpl, APTStandardRepositoryImpl};
|
||||
|
||||
const APT_SOURCES_LIST_FILENAME: &str = "/etc/apt/sources.list";
|
||||
const APT_SOURCES_LIST_DIRECTORY: &str = "/etc/apt/sources.list.d/";
|
||||
@ -28,7 +29,7 @@ const APT_SOURCES_LIST_DIRECTORY: &str = "/etc/apt/sources.list.d/";
|
||||
/// The digest is invariant with respect to file order.
|
||||
///
|
||||
/// Files without a digest are ignored.
|
||||
fn common_digest(files: &[APTRepositoryFile]) -> [u8; 32] {
|
||||
fn common_digest(files: &[APTRepositoryFile]) -> ConfigDigest {
|
||||
let mut digests = BTreeMap::new();
|
||||
|
||||
for file in files.iter() {
|
||||
@ -43,7 +44,7 @@ fn common_digest(files: &[APTRepositoryFile]) -> [u8; 32] {
|
||||
}
|
||||
}
|
||||
|
||||
openssl::sha::sha256(&common_raw[..])
|
||||
ConfigDigest::from_slice(&common_raw[..])
|
||||
}
|
||||
|
||||
/// Provides additional information about the repositories.
|
||||
@ -86,22 +87,22 @@ pub fn standard_repositories(
|
||||
suite: DebianCodename,
|
||||
) -> Vec<APTStandardRepository> {
|
||||
let mut result = vec![
|
||||
APTStandardRepository::from(APTRepositoryHandle::Enterprise),
|
||||
APTStandardRepository::from(APTRepositoryHandle::NoSubscription),
|
||||
APTStandardRepository::from(APTRepositoryHandle::Test),
|
||||
APTStandardRepository::from_handle(APTRepositoryHandle::Enterprise),
|
||||
APTStandardRepository::from_handle(APTRepositoryHandle::NoSubscription),
|
||||
APTStandardRepository::from_handle(APTRepositoryHandle::Test),
|
||||
];
|
||||
|
||||
if product == "pve" {
|
||||
result.append(&mut vec![
|
||||
APTStandardRepository::from(APTRepositoryHandle::CephQuincyEnterprise),
|
||||
APTStandardRepository::from(APTRepositoryHandle::CephQuincyNoSubscription),
|
||||
APTStandardRepository::from(APTRepositoryHandle::CephQuincyTest),
|
||||
APTStandardRepository::from_handle(APTRepositoryHandle::CephQuincyEnterprise),
|
||||
APTStandardRepository::from_handle(APTRepositoryHandle::CephQuincyNoSubscription),
|
||||
APTStandardRepository::from_handle(APTRepositoryHandle::CephQuincyTest),
|
||||
]);
|
||||
if suite == DebianCodename::Bookworm {
|
||||
result.append(&mut vec![
|
||||
APTStandardRepository::from(APTRepositoryHandle::CephReefEnterprise),
|
||||
APTStandardRepository::from(APTRepositoryHandle::CephReefNoSubscription),
|
||||
APTStandardRepository::from(APTRepositoryHandle::CephReefTest),
|
||||
APTStandardRepository::from_handle(APTRepositoryHandle::CephReefEnterprise),
|
||||
APTStandardRepository::from_handle(APTRepositoryHandle::CephReefNoSubscription),
|
||||
APTStandardRepository::from_handle(APTRepositoryHandle::CephReefTest),
|
||||
]);
|
||||
}
|
||||
}
|
||||
@ -128,7 +129,7 @@ pub fn standard_repositories(
|
||||
pub type Repositories = (
|
||||
Vec<APTRepositoryFile>,
|
||||
Vec<APTRepositoryFileError>,
|
||||
[u8; 32],
|
||||
ConfigDigest,
|
||||
);
|
||||
|
||||
/// Returns all APT repositories configured in `/etc/apt/sources.list` and
|
||||
|
@ -1,193 +1,12 @@
|
||||
use std::fmt::Display;
|
||||
use std::io::{BufRead, BufReader, Write};
|
||||
use std::path::PathBuf;
|
||||
|
||||
use anyhow::{bail, format_err, Error};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use proxmox_schema::api;
|
||||
|
||||
use crate::repositories::standard::APTRepositoryHandle;
|
||||
use crate::repositories::standard::APTRepositoryHandleImpl;
|
||||
|
||||
#[api]
|
||||
#[derive(Debug, Copy, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
pub enum APTRepositoryFileType {
|
||||
/// One-line-style format
|
||||
List,
|
||||
/// DEB822-style format
|
||||
Sources,
|
||||
}
|
||||
|
||||
impl TryFrom<&str> for APTRepositoryFileType {
|
||||
type Error = Error;
|
||||
|
||||
fn try_from(file_type: &str) -> Result<Self, Error> {
|
||||
match file_type {
|
||||
"list" => Ok(APTRepositoryFileType::List),
|
||||
"sources" => Ok(APTRepositoryFileType::Sources),
|
||||
_ => bail!("invalid file type '{file_type}'"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for APTRepositoryFileType {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
APTRepositoryFileType::List => write!(f, "list"),
|
||||
APTRepositoryFileType::Sources => write!(f, "sources"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[api]
|
||||
#[derive(Debug, Copy, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
pub enum APTRepositoryPackageType {
|
||||
/// Debian package
|
||||
Deb,
|
||||
/// Debian source package
|
||||
DebSrc,
|
||||
}
|
||||
|
||||
impl TryFrom<&str> for APTRepositoryPackageType {
|
||||
type Error = Error;
|
||||
|
||||
fn try_from(package_type: &str) -> Result<Self, Error> {
|
||||
match package_type {
|
||||
"deb" => Ok(APTRepositoryPackageType::Deb),
|
||||
"deb-src" => Ok(APTRepositoryPackageType::DebSrc),
|
||||
_ => bail!("invalid package type '{package_type}'"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for APTRepositoryPackageType {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
APTRepositoryPackageType::Deb => write!(f, "deb"),
|
||||
APTRepositoryPackageType::DebSrc => write!(f, "deb-src"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[api(
|
||||
properties: {
|
||||
Key: {
|
||||
description: "Option key.",
|
||||
type: String,
|
||||
},
|
||||
Values: {
|
||||
description: "Option values.",
|
||||
type: Array,
|
||||
items: {
|
||||
description: "Value.",
|
||||
type: String,
|
||||
},
|
||||
},
|
||||
},
|
||||
)]
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "PascalCase")] // for consistency
|
||||
/// Additional options for an APT repository.
|
||||
/// Used for both single- and mutli-value options.
|
||||
pub struct APTRepositoryOption {
|
||||
/// Option key.
|
||||
pub key: String,
|
||||
/// Option value(s).
|
||||
pub values: Vec<String>,
|
||||
}
|
||||
|
||||
#[api(
|
||||
properties: {
|
||||
Types: {
|
||||
description: "List of package types.",
|
||||
type: Array,
|
||||
items: {
|
||||
type: APTRepositoryPackageType,
|
||||
},
|
||||
},
|
||||
URIs: {
|
||||
description: "List of repository URIs.",
|
||||
type: Array,
|
||||
items: {
|
||||
description: "Repository URI.",
|
||||
type: String,
|
||||
},
|
||||
},
|
||||
Suites: {
|
||||
description: "List of distributions.",
|
||||
type: Array,
|
||||
items: {
|
||||
description: "Package distribution.",
|
||||
type: String,
|
||||
},
|
||||
},
|
||||
Components: {
|
||||
description: "List of repository components.",
|
||||
type: Array,
|
||||
items: {
|
||||
description: "Repository component.",
|
||||
type: String,
|
||||
},
|
||||
},
|
||||
Options: {
|
||||
type: Array,
|
||||
optional: true,
|
||||
items: {
|
||||
type: APTRepositoryOption,
|
||||
},
|
||||
},
|
||||
Comment: {
|
||||
description: "Associated comment.",
|
||||
type: String,
|
||||
optional: true,
|
||||
},
|
||||
FileType: {
|
||||
type: APTRepositoryFileType,
|
||||
},
|
||||
Enabled: {
|
||||
description: "Whether the repository is enabled or not.",
|
||||
type: Boolean,
|
||||
},
|
||||
},
|
||||
)]
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "PascalCase")]
|
||||
/// Describes an APT repository.
|
||||
pub struct APTRepository {
|
||||
/// List of package types.
|
||||
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||
pub types: Vec<APTRepositoryPackageType>,
|
||||
|
||||
/// List of repository URIs.
|
||||
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||
#[serde(rename = "URIs")]
|
||||
pub uris: Vec<String>,
|
||||
|
||||
/// List of package distributions.
|
||||
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||
pub suites: Vec<String>,
|
||||
|
||||
/// List of repository components.
|
||||
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||
pub components: Vec<String>,
|
||||
|
||||
/// Additional options.
|
||||
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||
pub options: Vec<APTRepositoryOption>,
|
||||
|
||||
/// Associated comment.
|
||||
#[serde(default, skip_serializing_if = "String::is_empty")]
|
||||
pub comment: String,
|
||||
|
||||
/// Format of the defining file.
|
||||
pub file_type: APTRepositoryFileType,
|
||||
|
||||
/// Whether the repository is enabled or not.
|
||||
pub enabled: bool,
|
||||
}
|
||||
use proxmox_apt_api_types::{
|
||||
APTRepository, APTRepositoryFileType, APTRepositoryHandle, APTRepositoryOption,
|
||||
};
|
||||
|
||||
pub trait APTRepositoryImpl {
|
||||
/// Crates an empty repository.
|
||||
|
@ -1,70 +1,14 @@
|
||||
use std::fmt::Display;
|
||||
|
||||
use anyhow::{bail, Error};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::repositories::repository::{
|
||||
APTRepository, APTRepositoryFileType, APTRepositoryPackageType,
|
||||
use proxmox_apt_api_types::{
|
||||
APTRepository, APTRepositoryFileType, APTRepositoryHandle, APTRepositoryPackageType,
|
||||
APTStandardRepository,
|
||||
};
|
||||
|
||||
use proxmox_schema::api;
|
||||
|
||||
#[api(
|
||||
properties: {
|
||||
handle: {
|
||||
description: "Handle referencing a standard repository.",
|
||||
type: String,
|
||||
},
|
||||
},
|
||||
)]
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
/// Reference to a standard repository and configuration status.
|
||||
pub struct APTStandardRepository {
|
||||
/// Handle referencing a standard repository.
|
||||
pub handle: APTRepositoryHandle,
|
||||
|
||||
/// Configuration status of the associated repository, where `None` means
|
||||
/// not configured, and `Some(bool)` indicates enabled or disabled.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub status: Option<bool>,
|
||||
|
||||
/// Display name of the repository.
|
||||
pub name: String,
|
||||
|
||||
/// Description of the repository.
|
||||
pub description: String,
|
||||
pub trait APTStandardRepositoryImpl {
|
||||
fn from_handle(handle: APTRepositoryHandle) -> APTStandardRepository;
|
||||
}
|
||||
|
||||
#[api]
|
||||
#[derive(Debug, Copy, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
/// Handles for Proxmox repositories.
|
||||
pub enum APTRepositoryHandle {
|
||||
/// The enterprise repository for production use.
|
||||
Enterprise,
|
||||
/// The repository that can be used without subscription.
|
||||
NoSubscription,
|
||||
/// The test repository.
|
||||
Test,
|
||||
/// Ceph Quincy enterprise repository.
|
||||
CephQuincyEnterprise,
|
||||
/// Ceph Quincy no-subscription repository.
|
||||
CephQuincyNoSubscription,
|
||||
/// Ceph Quincy test repository.
|
||||
CephQuincyTest,
|
||||
// TODO: Add separate enum for ceph releases and use something like
|
||||
// `CephTest(CephReleaseCodename),` once the API macro supports it.
|
||||
/// Ceph Reef enterprise repository.
|
||||
CephReefEnterprise,
|
||||
/// Ceph Reef no-subscription repository.
|
||||
CephReefNoSubscription,
|
||||
/// Ceph Reef test repository.
|
||||
CephReefTest,
|
||||
}
|
||||
|
||||
impl From<APTRepositoryHandle> for APTStandardRepository {
|
||||
fn from(handle: APTRepositoryHandle) -> Self {
|
||||
impl APTStandardRepositoryImpl for APTStandardRepository {
|
||||
fn from_handle(handle: APTRepositoryHandle) -> APTStandardRepository {
|
||||
APTStandardRepository {
|
||||
handle,
|
||||
status: None,
|
||||
@ -74,43 +18,6 @@ impl From<APTRepositoryHandle> for APTStandardRepository {
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<&str> for APTRepositoryHandle {
|
||||
type Error = Error;
|
||||
|
||||
fn try_from(string: &str) -> Result<Self, Error> {
|
||||
match string {
|
||||
"enterprise" => Ok(APTRepositoryHandle::Enterprise),
|
||||
"no-subscription" => Ok(APTRepositoryHandle::NoSubscription),
|
||||
"test" => Ok(APTRepositoryHandle::Test),
|
||||
"ceph-quincy-enterprise" => Ok(APTRepositoryHandle::CephQuincyEnterprise),
|
||||
"ceph-quincy-no-subscription" => Ok(APTRepositoryHandle::CephQuincyNoSubscription),
|
||||
"ceph-quincy-test" => Ok(APTRepositoryHandle::CephQuincyTest),
|
||||
"ceph-reef-enterprise" => Ok(APTRepositoryHandle::CephReefEnterprise),
|
||||
"ceph-reef-no-subscription" => Ok(APTRepositoryHandle::CephReefNoSubscription),
|
||||
"ceph-reef-test" => Ok(APTRepositoryHandle::CephReefTest),
|
||||
_ => bail!("unknown repository handle '{}'", string),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for APTRepositoryHandle {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
APTRepositoryHandle::Enterprise => write!(f, "enterprise"),
|
||||
APTRepositoryHandle::NoSubscription => write!(f, "no-subscription"),
|
||||
APTRepositoryHandle::Test => write!(f, "test"),
|
||||
APTRepositoryHandle::CephQuincyEnterprise => write!(f, "ceph-quincy-enterprise"),
|
||||
APTRepositoryHandle::CephQuincyNoSubscription => {
|
||||
write!(f, "ceph-quincy-no-subscription")
|
||||
}
|
||||
APTRepositoryHandle::CephQuincyTest => write!(f, "ceph-quincy-test"),
|
||||
APTRepositoryHandle::CephReefEnterprise => write!(f, "ceph-reef-enterprise"),
|
||||
APTRepositoryHandle::CephReefNoSubscription => write!(f, "ceph-reef-no-subscription"),
|
||||
APTRepositoryHandle::CephReefTest => write!(f, "ceph-reef-test"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait APTRepositoryHandleImpl {
|
||||
/// Get the description for the repository.
|
||||
fn description(self) -> String;
|
||||
|
@ -9,7 +9,7 @@ use proxmox_apt::repositories::{
|
||||
APTRepositoryHandle, APTRepositoryInfo, APTStandardRepository, DebianCodename,
|
||||
};
|
||||
use proxmox_apt::repositories::{
|
||||
APTRepositoryFileImpl, APTRepositoryHandleImpl, APTRepositoryImpl,
|
||||
APTRepositoryFileImpl, APTRepositoryHandleImpl, APTRepositoryImpl, APTStandardRepositoryImpl,
|
||||
};
|
||||
|
||||
fn create_clean_directory(path: &PathBuf) -> Result<(), Error> {
|
||||
@ -114,7 +114,7 @@ fn test_digest() -> Result<(), Error> {
|
||||
let new_path = write_dir.join(path.file_name().unwrap());
|
||||
file.path = Some(new_path.clone().into_os_string().into_string().unwrap());
|
||||
|
||||
let old_digest = file.digest.unwrap();
|
||||
let old_digest = file.digest.clone().unwrap();
|
||||
|
||||
// file does not exist yet...
|
||||
assert!(file.read_with_digest().is_err());
|
||||
@ -132,7 +132,7 @@ fn test_digest() -> Result<(), Error> {
|
||||
repo.enabled = !repo.enabled;
|
||||
|
||||
// ...then it should work
|
||||
file.digest = Some(old_digest);
|
||||
file.digest = Some(old_digest.clone());
|
||||
file.write()?;
|
||||
|
||||
// expect a different digest, because the repo was modified
|
||||
@ -361,15 +361,15 @@ fn test_standard_repositories() -> Result<(), Error> {
|
||||
let read_dir = test_dir.join("sources.list.d");
|
||||
|
||||
let mut expected = vec![
|
||||
APTStandardRepository::from(APTRepositoryHandle::Enterprise),
|
||||
APTStandardRepository::from(APTRepositoryHandle::NoSubscription),
|
||||
APTStandardRepository::from(APTRepositoryHandle::Test),
|
||||
APTStandardRepository::from(APTRepositoryHandle::CephQuincyEnterprise),
|
||||
APTStandardRepository::from(APTRepositoryHandle::CephQuincyNoSubscription),
|
||||
APTStandardRepository::from(APTRepositoryHandle::CephQuincyTest),
|
||||
APTStandardRepository::from(APTRepositoryHandle::CephReefEnterprise),
|
||||
APTStandardRepository::from(APTRepositoryHandle::CephReefNoSubscription),
|
||||
APTStandardRepository::from(APTRepositoryHandle::CephReefTest),
|
||||
APTStandardRepository::from_handle(APTRepositoryHandle::Enterprise),
|
||||
APTStandardRepository::from_handle(APTRepositoryHandle::NoSubscription),
|
||||
APTStandardRepository::from_handle(APTRepositoryHandle::Test),
|
||||
APTStandardRepository::from_handle(APTRepositoryHandle::CephQuincyEnterprise),
|
||||
APTStandardRepository::from_handle(APTRepositoryHandle::CephQuincyNoSubscription),
|
||||
APTStandardRepository::from_handle(APTRepositoryHandle::CephQuincyTest),
|
||||
APTStandardRepository::from_handle(APTRepositoryHandle::CephReefEnterprise),
|
||||
APTStandardRepository::from_handle(APTRepositoryHandle::CephReefNoSubscription),
|
||||
APTStandardRepository::from_handle(APTRepositoryHandle::CephReefTest),
|
||||
];
|
||||
|
||||
let absolute_suite_list = read_dir.join("absolute_suite.list");
|
||||
|
Loading…
Reference in New Issue
Block a user