use crate::api::machine::{
apply_configuration_request::Mode as ProtoMode, ApplyConfiguration as ProtoApplyConfiguration,
ApplyConfigurationRequest as ProtoRequest, ApplyConfigurationResponse as ProtoResponse,
};
use std::time::Duration;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum ApplyMode {
Reboot,
#[default]
Auto,
NoReboot,
Staged,
Try,
}
impl From<ApplyMode> for i32 {
fn from(mode: ApplyMode) -> Self {
match mode {
ApplyMode::Reboot => ProtoMode::Reboot as i32,
ApplyMode::Auto => ProtoMode::Auto as i32,
ApplyMode::NoReboot => ProtoMode::NoReboot as i32,
ApplyMode::Staged => ProtoMode::Staged as i32,
ApplyMode::Try => ProtoMode::Try as i32,
}
}
}
impl From<i32> for ApplyMode {
fn from(value: i32) -> Self {
match value {
0 => ApplyMode::Reboot,
1 => ApplyMode::Auto,
2 => ApplyMode::NoReboot,
3 => ApplyMode::Staged,
4 => ApplyMode::Try,
_ => ApplyMode::Auto, }
}
}
impl std::fmt::Display for ApplyMode {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
ApplyMode::Reboot => write!(f, "reboot"),
ApplyMode::Auto => write!(f, "auto"),
ApplyMode::NoReboot => write!(f, "no-reboot"),
ApplyMode::Staged => write!(f, "staged"),
ApplyMode::Try => write!(f, "try"),
}
}
}
#[derive(Debug, Clone, Default)]
pub struct ApplyConfigurationRequest {
pub data: Vec<u8>,
pub mode: ApplyMode,
pub dry_run: bool,
pub try_mode_timeout: Option<Duration>,
}
impl ApplyConfigurationRequest {
#[must_use]
pub fn builder() -> ApplyConfigurationRequestBuilder {
ApplyConfigurationRequestBuilder::default()
}
#[must_use]
pub fn from_yaml(yaml: impl AsRef<str>) -> Self {
Self {
data: yaml.as_ref().as_bytes().to_vec(),
mode: ApplyMode::Auto,
dry_run: false,
try_mode_timeout: None,
}
}
#[must_use]
pub fn from_bytes(data: Vec<u8>) -> Self {
Self {
data,
mode: ApplyMode::Auto,
dry_run: false,
try_mode_timeout: None,
}
}
}
impl From<ApplyConfigurationRequest> for ProtoRequest {
fn from(req: ApplyConfigurationRequest) -> Self {
ProtoRequest {
data: req.data,
mode: req.mode.into(),
dry_run: req.dry_run,
try_mode_timeout: req.try_mode_timeout.map(|d| prost_types::Duration {
seconds: d.as_secs() as i64,
nanos: d.subsec_nanos() as i32,
}),
}
}
}
#[derive(Debug, Clone, Default)]
pub struct ApplyConfigurationRequestBuilder {
data: Vec<u8>,
mode: ApplyMode,
dry_run: bool,
try_mode_timeout: Option<Duration>,
}
impl ApplyConfigurationRequestBuilder {
#[must_use]
pub fn config_yaml(mut self, yaml: impl AsRef<str>) -> Self {
self.data = yaml.as_ref().as_bytes().to_vec();
self
}
#[must_use]
pub fn config_bytes(mut self, data: Vec<u8>) -> Self {
self.data = data;
self
}
pub fn config_file(mut self, path: impl AsRef<std::path::Path>) -> std::io::Result<Self> {
self.data = std::fs::read(path)?;
Ok(self)
}
#[must_use]
pub fn mode(mut self, mode: ApplyMode) -> Self {
self.mode = mode;
self
}
#[must_use]
pub fn dry_run(mut self, dry_run: bool) -> Self {
self.dry_run = dry_run;
self
}
#[must_use]
pub fn try_mode_timeout(mut self, timeout: Duration) -> Self {
self.try_mode_timeout = Some(timeout);
self
}
#[must_use]
pub fn build(self) -> ApplyConfigurationRequest {
ApplyConfigurationRequest {
data: self.data,
mode: self.mode,
dry_run: self.dry_run,
try_mode_timeout: self.try_mode_timeout,
}
}
}
#[derive(Debug, Clone)]
pub struct ApplyConfigurationResult {
pub node: Option<String>,
pub warnings: Vec<String>,
pub mode: ApplyMode,
pub mode_details: String,
}
impl From<ProtoApplyConfiguration> for ApplyConfigurationResult {
fn from(proto: ProtoApplyConfiguration) -> Self {
Self {
node: proto.metadata.map(|m| m.hostname),
warnings: proto.warnings,
mode: proto.mode.into(),
mode_details: proto.mode_details,
}
}
}
#[derive(Debug, Clone)]
pub struct ApplyConfigurationResponse {
pub results: Vec<ApplyConfigurationResult>,
}
impl From<ProtoResponse> for ApplyConfigurationResponse {
fn from(proto: ProtoResponse) -> Self {
Self {
results: proto.messages.into_iter().map(Into::into).collect(),
}
}
}
impl ApplyConfigurationResponse {
#[must_use]
pub fn is_success(&self) -> bool {
self.results.iter().all(|r| r.warnings.is_empty())
}
#[must_use]
pub fn all_warnings(&self) -> Vec<&str> {
self.results
.iter()
.flat_map(|r| r.warnings.iter().map(String::as_str))
.collect()
}
#[must_use]
pub fn first(&self) -> Option<&ApplyConfigurationResult> {
self.results.first()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_apply_mode_conversion() {
assert_eq!(i32::from(ApplyMode::Reboot), 0);
assert_eq!(i32::from(ApplyMode::Auto), 1);
assert_eq!(i32::from(ApplyMode::NoReboot), 2);
assert_eq!(i32::from(ApplyMode::Staged), 3);
assert_eq!(i32::from(ApplyMode::Try), 4);
assert_eq!(ApplyMode::from(0), ApplyMode::Reboot);
assert_eq!(ApplyMode::from(1), ApplyMode::Auto);
assert_eq!(ApplyMode::from(2), ApplyMode::NoReboot);
assert_eq!(ApplyMode::from(3), ApplyMode::Staged);
assert_eq!(ApplyMode::from(4), ApplyMode::Try);
}
#[test]
fn test_builder_pattern() {
let request = ApplyConfigurationRequest::builder()
.config_yaml("machine:\n type: worker")
.mode(ApplyMode::NoReboot)
.dry_run(true)
.try_mode_timeout(Duration::from_secs(60))
.build();
assert_eq!(request.data, b"machine:\n type: worker");
assert_eq!(request.mode, ApplyMode::NoReboot);
assert!(request.dry_run);
assert_eq!(request.try_mode_timeout, Some(Duration::from_secs(60)));
}
#[test]
fn test_from_yaml() {
let request = ApplyConfigurationRequest::from_yaml("test: config");
assert_eq!(request.data, b"test: config");
assert_eq!(request.mode, ApplyMode::Auto);
assert!(!request.dry_run);
}
#[test]
fn test_proto_conversion() {
let request = ApplyConfigurationRequest::builder()
.config_yaml("test")
.mode(ApplyMode::Staged)
.dry_run(true)
.try_mode_timeout(Duration::from_secs(120))
.build();
let proto: ProtoRequest = request.into();
assert_eq!(proto.data, b"test");
assert_eq!(proto.mode, ProtoMode::Staged as i32);
assert!(proto.dry_run);
assert!(proto.try_mode_timeout.is_some());
}
#[test]
fn test_apply_mode_display() {
assert_eq!(ApplyMode::Reboot.to_string(), "reboot");
assert_eq!(ApplyMode::Auto.to_string(), "auto");
assert_eq!(ApplyMode::NoReboot.to_string(), "no-reboot");
assert_eq!(ApplyMode::Staged.to_string(), "staged");
assert_eq!(ApplyMode::Try.to_string(), "try");
}
}