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
@ -20,4 +20,5 @@ serde_json.workspace = true
|
|||||||
|
|
||||||
rfc822-like = "0.2.1"
|
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-hex-0.4+default-dev <!nocheck>,
|
||||||
librust-once-cell-1+default-dev (>= 1.3.1-~~) <!nocheck>,
|
librust-once-cell-1+default-dev (>= 1.3.1-~~) <!nocheck>,
|
||||||
librust-openssl-0.10+default-dev <!nocheck>,
|
librust-openssl-0.10+default-dev <!nocheck>,
|
||||||
librust-proxmox-schema-3+api-macro-dev (>= 3.1.1-~~) <!nocheck>,
|
librust-proxmox-apt-api-types-1+default-dev <!nocheck>,
|
||||||
librust-proxmox-schema-3+default-dev (>= 3.1.1-~~) <!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-rfc822-like-0.2+default-dev (>= 0.2.1-~~) <!nocheck>,
|
||||||
librust-serde-1+default-dev <!nocheck>,
|
librust-serde-1+default-dev <!nocheck>,
|
||||||
librust-serde-1+derive-dev <!nocheck>,
|
librust-serde-1+derive-dev <!nocheck>,
|
||||||
@ -33,8 +34,9 @@ Depends:
|
|||||||
librust-hex-0.4+default-dev,
|
librust-hex-0.4+default-dev,
|
||||||
librust-once-cell-1+default-dev (>= 1.3.1-~~),
|
librust-once-cell-1+default-dev (>= 1.3.1-~~),
|
||||||
librust-openssl-0.10+default-dev,
|
librust-openssl-0.10+default-dev,
|
||||||
librust-proxmox-schema-3+api-macro-dev (>= 3.1.1-~~),
|
librust-proxmox-apt-api-types-1+default-dev,
|
||||||
librust-proxmox-schema-3+default-dev (>= 3.1.1-~~),
|
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-rfc822-like-0.2+default-dev (>= 0.2.1-~~),
|
||||||
librust-serde-1+default-dev,
|
librust-serde-1+default-dev,
|
||||||
librust-serde-1+derive-dev,
|
librust-serde-1+derive-dev,
|
||||||
|
@ -1,123 +1,29 @@
|
|||||||
use std::fmt::Display;
|
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
use anyhow::{format_err, Error};
|
use anyhow::{format_err, Error};
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
|
|
||||||
use crate::repositories::release::DebianCodename;
|
use crate::repositories::release::DebianCodename;
|
||||||
use crate::repositories::repository::{
|
use proxmox_apt_api_types::{
|
||||||
APTRepository, APTRepositoryFileType, APTRepositoryPackageType,
|
APTRepository, APTRepositoryFile, APTRepositoryFileError, APTRepositoryFileType,
|
||||||
|
APTRepositoryInfo, APTRepositoryPackageType,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::repositories::repository::APTRepositoryImpl;
|
use crate::repositories::repository::APTRepositoryImpl;
|
||||||
|
|
||||||
use proxmox_schema::api;
|
|
||||||
|
|
||||||
mod list_parser;
|
mod list_parser;
|
||||||
use list_parser::APTListFileParser;
|
use list_parser::APTListFileParser;
|
||||||
|
|
||||||
mod sources_parser;
|
mod sources_parser;
|
||||||
use sources_parser::APTSourcesFileParser;
|
use sources_parser::APTSourcesFileParser;
|
||||||
|
|
||||||
|
use proxmox_config_digest::ConfigDigest;
|
||||||
|
|
||||||
trait APTRepositoryParser {
|
trait APTRepositoryParser {
|
||||||
/// Parse all repositories including the disabled ones and push them onto
|
/// Parse all repositories including the disabled ones and push them onto
|
||||||
/// the provided vector.
|
/// the provided vector.
|
||||||
fn parse_repositories(&mut self) -> Result<Vec<APTRepository>, Error>;
|
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 {
|
pub trait APTRepositoryFileImpl {
|
||||||
/// Creates a new `APTRepositoryFile` without parsing.
|
/// Creates a new `APTRepositoryFile` without parsing.
|
||||||
///
|
///
|
||||||
@ -131,7 +37,7 @@ pub trait APTRepositoryFileImpl {
|
|||||||
/// Check if the file exists.
|
/// Check if the file exists.
|
||||||
fn exists(&self) -> bool;
|
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`.
|
/// Create an `APTRepositoryFileError`.
|
||||||
fn err(&self, error: Error) -> APTRepositoryFileError;
|
fn err(&self, error: Error) -> APTRepositoryFileError;
|
||||||
@ -213,7 +119,8 @@ impl APTRepositoryFileImpl for APTRepositoryFile {
|
|||||||
return Ok(None);
|
return Ok(None);
|
||||||
}
|
}
|
||||||
|
|
||||||
let file_type = APTRepositoryFileType::try_from(&extension[..])
|
let file_type = extension[..]
|
||||||
|
.parse()
|
||||||
.map_err(|_| new_err("invalid extension"))?;
|
.map_err(|_| new_err("invalid extension"))?;
|
||||||
|
|
||||||
if !file_name
|
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 {
|
if let Some(path) = &self.path {
|
||||||
let content = std::fs::read(path).map_err(|err| self.err(format_err!("{}", err)))?;
|
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))
|
Ok((content, digest))
|
||||||
} else if let Some(ref content) = self.content {
|
} else if let Some(ref content) = self.content {
|
||||||
let content = content.as_bytes();
|
let content = content.as_bytes();
|
||||||
let digest = openssl::sha::sha256(content);
|
let digest = ConfigDigest::from_slice(content);
|
||||||
Ok((content.to_vec(), digest))
|
Ok((content.to_vec(), digest))
|
||||||
} else {
|
} else {
|
||||||
Err(self.err(format_err!(
|
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() {
|
if !self.exists() {
|
||||||
return Err(self.err(format_err!("digest specified, but file does not exist")));
|
return Err(self.err(format_err!("digest specified, but file does not exist")));
|
||||||
}
|
}
|
||||||
|
|
||||||
let (_, current_digest) = self.read_with_digest()?;
|
let (_, current_digest) = self.read_with_digest()?;
|
||||||
if digest != current_digest {
|
if digest != ¤t_digest {
|
||||||
return Err(self.err(format_err!("digest mismatch")));
|
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
|
// 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)) {
|
line = match line.split_once(|c| char::is_ascii_whitespace(&c)) {
|
||||||
Some((package_type, rest)) => {
|
Some((package_type, rest)) => {
|
||||||
repo.types.push(package_type.try_into()?);
|
repo.types.push(package_type.parse()?);
|
||||||
rest
|
rest
|
||||||
}
|
}
|
||||||
None => return Ok(None), // empty line
|
None => return Ok(None), // empty line
|
||||||
|
@ -108,7 +108,7 @@ impl<R: BufRead> APTSourcesFileParser<R> {
|
|||||||
}
|
}
|
||||||
let mut types = Vec::<APTRepositoryPackageType>::new();
|
let mut types = Vec::<APTRepositoryPackageType>::new();
|
||||||
for package_type in values {
|
for package_type in values {
|
||||||
types.push((&package_type[..]).try_into()?);
|
types.push((&package_type[..]).parse()?);
|
||||||
}
|
}
|
||||||
repo.types = types;
|
repo.types = types;
|
||||||
}
|
}
|
||||||
|
@ -4,21 +4,22 @@ use std::path::PathBuf;
|
|||||||
use anyhow::{bail, Error};
|
use anyhow::{bail, Error};
|
||||||
|
|
||||||
mod repository;
|
mod repository;
|
||||||
pub use repository::APTRepositoryImpl;
|
use proxmox_apt_api_types::{
|
||||||
pub use repository::{
|
APTRepository, APTRepositoryFile, APTRepositoryFileError, APTRepositoryFileType,
|
||||||
APTRepository, APTRepositoryFileType, APTRepositoryOption, APTRepositoryPackageType,
|
APTRepositoryHandle, APTRepositoryInfo, APTRepositoryOption, APTRepositoryPackageType,
|
||||||
|
APTStandardRepository,
|
||||||
};
|
};
|
||||||
|
use proxmox_config_digest::ConfigDigest;
|
||||||
|
pub use repository::APTRepositoryImpl;
|
||||||
|
|
||||||
mod file;
|
mod file;
|
||||||
pub use file::APTRepositoryFileImpl;
|
pub use file::APTRepositoryFileImpl;
|
||||||
pub use file::{APTRepositoryFile, APTRepositoryFileError, APTRepositoryInfo};
|
|
||||||
|
|
||||||
mod release;
|
mod release;
|
||||||
pub use release::{get_current_release_codename, DebianCodename};
|
pub use release::{get_current_release_codename, DebianCodename};
|
||||||
|
|
||||||
mod standard;
|
mod standard;
|
||||||
pub use standard::APTRepositoryHandleImpl;
|
pub use standard::{APTRepositoryHandleImpl, APTStandardRepositoryImpl};
|
||||||
pub use standard::{APTRepositoryHandle, APTStandardRepository};
|
|
||||||
|
|
||||||
const APT_SOURCES_LIST_FILENAME: &str = "/etc/apt/sources.list";
|
const APT_SOURCES_LIST_FILENAME: &str = "/etc/apt/sources.list";
|
||||||
const APT_SOURCES_LIST_DIRECTORY: &str = "/etc/apt/sources.list.d/";
|
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.
|
/// The digest is invariant with respect to file order.
|
||||||
///
|
///
|
||||||
/// Files without a digest are ignored.
|
/// Files without a digest are ignored.
|
||||||
fn common_digest(files: &[APTRepositoryFile]) -> [u8; 32] {
|
fn common_digest(files: &[APTRepositoryFile]) -> ConfigDigest {
|
||||||
let mut digests = BTreeMap::new();
|
let mut digests = BTreeMap::new();
|
||||||
|
|
||||||
for file in files.iter() {
|
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.
|
/// Provides additional information about the repositories.
|
||||||
@ -86,22 +87,22 @@ pub fn standard_repositories(
|
|||||||
suite: DebianCodename,
|
suite: DebianCodename,
|
||||||
) -> Vec<APTStandardRepository> {
|
) -> Vec<APTStandardRepository> {
|
||||||
let mut result = vec![
|
let mut result = vec![
|
||||||
APTStandardRepository::from(APTRepositoryHandle::Enterprise),
|
APTStandardRepository::from_handle(APTRepositoryHandle::Enterprise),
|
||||||
APTStandardRepository::from(APTRepositoryHandle::NoSubscription),
|
APTStandardRepository::from_handle(APTRepositoryHandle::NoSubscription),
|
||||||
APTStandardRepository::from(APTRepositoryHandle::Test),
|
APTStandardRepository::from_handle(APTRepositoryHandle::Test),
|
||||||
];
|
];
|
||||||
|
|
||||||
if product == "pve" {
|
if product == "pve" {
|
||||||
result.append(&mut vec![
|
result.append(&mut vec![
|
||||||
APTStandardRepository::from(APTRepositoryHandle::CephQuincyEnterprise),
|
APTStandardRepository::from_handle(APTRepositoryHandle::CephQuincyEnterprise),
|
||||||
APTStandardRepository::from(APTRepositoryHandle::CephQuincyNoSubscription),
|
APTStandardRepository::from_handle(APTRepositoryHandle::CephQuincyNoSubscription),
|
||||||
APTStandardRepository::from(APTRepositoryHandle::CephQuincyTest),
|
APTStandardRepository::from_handle(APTRepositoryHandle::CephQuincyTest),
|
||||||
]);
|
]);
|
||||||
if suite == DebianCodename::Bookworm {
|
if suite == DebianCodename::Bookworm {
|
||||||
result.append(&mut vec![
|
result.append(&mut vec![
|
||||||
APTStandardRepository::from(APTRepositoryHandle::CephReefEnterprise),
|
APTStandardRepository::from_handle(APTRepositoryHandle::CephReefEnterprise),
|
||||||
APTStandardRepository::from(APTRepositoryHandle::CephReefNoSubscription),
|
APTStandardRepository::from_handle(APTRepositoryHandle::CephReefNoSubscription),
|
||||||
APTStandardRepository::from(APTRepositoryHandle::CephReefTest),
|
APTStandardRepository::from_handle(APTRepositoryHandle::CephReefTest),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -128,7 +129,7 @@ pub fn standard_repositories(
|
|||||||
pub type Repositories = (
|
pub type Repositories = (
|
||||||
Vec<APTRepositoryFile>,
|
Vec<APTRepositoryFile>,
|
||||||
Vec<APTRepositoryFileError>,
|
Vec<APTRepositoryFileError>,
|
||||||
[u8; 32],
|
ConfigDigest,
|
||||||
);
|
);
|
||||||
|
|
||||||
/// Returns all APT repositories configured in `/etc/apt/sources.list` and
|
/// 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::io::{BufRead, BufReader, Write};
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
use anyhow::{bail, format_err, Error};
|
use anyhow::{bail, format_err, Error};
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
|
|
||||||
use proxmox_schema::api;
|
|
||||||
|
|
||||||
use crate::repositories::standard::APTRepositoryHandle;
|
|
||||||
use crate::repositories::standard::APTRepositoryHandleImpl;
|
use crate::repositories::standard::APTRepositoryHandleImpl;
|
||||||
|
use proxmox_apt_api_types::{
|
||||||
#[api]
|
APTRepository, APTRepositoryFileType, APTRepositoryHandle, APTRepositoryOption,
|
||||||
#[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,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait APTRepositoryImpl {
|
pub trait APTRepositoryImpl {
|
||||||
/// Crates an empty repository.
|
/// Crates an empty repository.
|
||||||
|
@ -1,70 +1,14 @@
|
|||||||
use std::fmt::Display;
|
use proxmox_apt_api_types::{
|
||||||
|
APTRepository, APTRepositoryFileType, APTRepositoryHandle, APTRepositoryPackageType,
|
||||||
use anyhow::{bail, Error};
|
APTStandardRepository,
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
|
|
||||||
use crate::repositories::repository::{
|
|
||||||
APTRepository, APTRepositoryFileType, APTRepositoryPackageType,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
use proxmox_schema::api;
|
pub trait APTStandardRepositoryImpl {
|
||||||
|
fn from_handle(handle: APTRepositoryHandle) -> APTStandardRepository;
|
||||||
#[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,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[api]
|
impl APTStandardRepositoryImpl for APTStandardRepository {
|
||||||
#[derive(Debug, Copy, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
fn from_handle(handle: APTRepositoryHandle) -> APTStandardRepository {
|
||||||
#[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 {
|
|
||||||
APTStandardRepository {
|
APTStandardRepository {
|
||||||
handle,
|
handle,
|
||||||
status: None,
|
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 {
|
pub trait APTRepositoryHandleImpl {
|
||||||
/// Get the description for the repository.
|
/// Get the description for the repository.
|
||||||
fn description(self) -> String;
|
fn description(self) -> String;
|
||||||
|
@ -9,7 +9,7 @@ use proxmox_apt::repositories::{
|
|||||||
APTRepositoryHandle, APTRepositoryInfo, APTStandardRepository, DebianCodename,
|
APTRepositoryHandle, APTRepositoryInfo, APTStandardRepository, DebianCodename,
|
||||||
};
|
};
|
||||||
use proxmox_apt::repositories::{
|
use proxmox_apt::repositories::{
|
||||||
APTRepositoryFileImpl, APTRepositoryHandleImpl, APTRepositoryImpl,
|
APTRepositoryFileImpl, APTRepositoryHandleImpl, APTRepositoryImpl, APTStandardRepositoryImpl,
|
||||||
};
|
};
|
||||||
|
|
||||||
fn create_clean_directory(path: &PathBuf) -> Result<(), Error> {
|
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());
|
let new_path = write_dir.join(path.file_name().unwrap());
|
||||||
file.path = Some(new_path.clone().into_os_string().into_string().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...
|
// file does not exist yet...
|
||||||
assert!(file.read_with_digest().is_err());
|
assert!(file.read_with_digest().is_err());
|
||||||
@ -132,7 +132,7 @@ fn test_digest() -> Result<(), Error> {
|
|||||||
repo.enabled = !repo.enabled;
|
repo.enabled = !repo.enabled;
|
||||||
|
|
||||||
// ...then it should work
|
// ...then it should work
|
||||||
file.digest = Some(old_digest);
|
file.digest = Some(old_digest.clone());
|
||||||
file.write()?;
|
file.write()?;
|
||||||
|
|
||||||
// expect a different digest, because the repo was modified
|
// 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 read_dir = test_dir.join("sources.list.d");
|
||||||
|
|
||||||
let mut expected = vec![
|
let mut expected = vec![
|
||||||
APTStandardRepository::from(APTRepositoryHandle::Enterprise),
|
APTStandardRepository::from_handle(APTRepositoryHandle::Enterprise),
|
||||||
APTStandardRepository::from(APTRepositoryHandle::NoSubscription),
|
APTStandardRepository::from_handle(APTRepositoryHandle::NoSubscription),
|
||||||
APTStandardRepository::from(APTRepositoryHandle::Test),
|
APTStandardRepository::from_handle(APTRepositoryHandle::Test),
|
||||||
APTStandardRepository::from(APTRepositoryHandle::CephQuincyEnterprise),
|
APTStandardRepository::from_handle(APTRepositoryHandle::CephQuincyEnterprise),
|
||||||
APTStandardRepository::from(APTRepositoryHandle::CephQuincyNoSubscription),
|
APTStandardRepository::from_handle(APTRepositoryHandle::CephQuincyNoSubscription),
|
||||||
APTStandardRepository::from(APTRepositoryHandle::CephQuincyTest),
|
APTStandardRepository::from_handle(APTRepositoryHandle::CephQuincyTest),
|
||||||
APTStandardRepository::from(APTRepositoryHandle::CephReefEnterprise),
|
APTStandardRepository::from_handle(APTRepositoryHandle::CephReefEnterprise),
|
||||||
APTStandardRepository::from(APTRepositoryHandle::CephReefNoSubscription),
|
APTStandardRepository::from_handle(APTRepositoryHandle::CephReefNoSubscription),
|
||||||
APTStandardRepository::from(APTRepositoryHandle::CephReefTest),
|
APTStandardRepository::from_handle(APTRepositoryHandle::CephReefTest),
|
||||||
];
|
];
|
||||||
|
|
||||||
let absolute_suite_list = read_dir.join("absolute_suite.list");
|
let absolute_suite_list = read_dir.join("absolute_suite.list");
|
||||||
|
Loading…
Reference in New Issue
Block a user