Compare commits
7 Commits
a6dc420275
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| d449fc8dcf | |||
| ddc82199fb | |||
| 61d31bc22c | |||
| d2293df011 | |||
| 723cecff1b | |||
| e8c9ee9885 | |||
| 6a6536bdfb |
541
Cargo.lock
generated
541
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@ -13,6 +13,7 @@ codegen-units = 1
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
async-compression = { version = "0.4.27", features = ["tokio", "zstd"] }
|
async-compression = { version = "0.4.27", features = ["tokio", "zstd"] }
|
||||||
base32 = "0.5.1"
|
base32 = "0.5.1"
|
||||||
|
base64 = "0.22.1"
|
||||||
bytes = "1.10.1"
|
bytes = "1.10.1"
|
||||||
chrono = { version = "0.4.41", default-features = false, features = ["clock", "now"] }
|
chrono = { version = "0.4.41", default-features = false, features = ["clock", "now"] }
|
||||||
clap = { version = "4.5.40", features = ["derive", "env"] }
|
clap = { version = "4.5.40", features = ["derive", "env"] }
|
||||||
|
|||||||
50
src/apply.rs
50
src/apply.rs
@ -3,21 +3,61 @@ use log::info;
|
|||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use tokio::fs;
|
use tokio::fs;
|
||||||
|
|
||||||
pub async fn files(files: &[crate::File], root: &str) -> Result<()> {
|
use crate::base64_decode;
|
||||||
|
|
||||||
|
pub async fn files(files: &[crate::File], root: &str, dry_run: bool) -> Result<()> {
|
||||||
for file in files {
|
for file in files {
|
||||||
let path = chroot(root, &file.path);
|
let path = chroot(root, &file.path);
|
||||||
let path = Path::new(&path);
|
let path = Path::new(&path);
|
||||||
|
|
||||||
if let Some(parent) = path.parent() {
|
if !dry_run && let Some(parent) = path.parent() {
|
||||||
fs::create_dir_all(parent).await?;
|
fs::create_dir_all(parent).await?;
|
||||||
}
|
}
|
||||||
|
|
||||||
use crate::FileKind as K;
|
use crate::FileKind as K;
|
||||||
match &file.kind {
|
match &file.kind {
|
||||||
K::Content(content) => fs::write(path, content.as_bytes()).await?,
|
K::Content(content) => {
|
||||||
K::Dir(true) => fs::create_dir(path).await?,
|
if dry_run {
|
||||||
|
info!(
|
||||||
|
"would create {} ({} bytes from content)",
|
||||||
|
file.path,
|
||||||
|
content.len()
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
fs::write(path, content.as_bytes()).await?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
K::Content64(content) => {
|
||||||
|
let content = base64_decode(content)?;
|
||||||
|
if dry_run {
|
||||||
|
info!(
|
||||||
|
"would create {} ({} bytes from content64)",
|
||||||
|
file.path,
|
||||||
|
content.len()
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
fs::write(path, content).await?
|
||||||
|
}
|
||||||
|
}
|
||||||
|
K::Dir(true) => {
|
||||||
|
if dry_run {
|
||||||
|
info!("would create {} (directory)", file.path);
|
||||||
|
} else {
|
||||||
|
fs::create_dir(path).await?;
|
||||||
|
}
|
||||||
|
}
|
||||||
K::Dir(false) => {} // shouldn't happen, but semantic is to ignore
|
K::Dir(false) => {} // shouldn't happen, but semantic is to ignore
|
||||||
K::Symlink(tgt) => fs::symlink(tgt, path).await?,
|
K::Symlink(tgt) => {
|
||||||
|
if dry_run {
|
||||||
|
info!("would create {} (symlink to {})", file.path, tgt);
|
||||||
|
} else {
|
||||||
|
fs::symlink(tgt, path).await?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if dry_run {
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
match file.kind {
|
match file.kind {
|
||||||
|
|||||||
@ -24,6 +24,9 @@ enum Command {
|
|||||||
/// path prefix (aka chroot)
|
/// path prefix (aka chroot)
|
||||||
#[arg(short = 'P', long, default_value = "/")]
|
#[arg(short = 'P', long, default_value = "/")]
|
||||||
prefix: String,
|
prefix: String,
|
||||||
|
/// don't really write files
|
||||||
|
#[arg(long)]
|
||||||
|
dry_run: bool,
|
||||||
},
|
},
|
||||||
Logger {
|
Logger {
|
||||||
/// Path where the logs are stored
|
/// Path where the logs are stored
|
||||||
@ -97,9 +100,10 @@ async fn main() -> Result<()> {
|
|||||||
config,
|
config,
|
||||||
filters,
|
filters,
|
||||||
prefix,
|
prefix,
|
||||||
|
dry_run,
|
||||||
} => {
|
} => {
|
||||||
let filters = parse_globs(&filters)?;
|
let filters = parse_globs(&filters)?;
|
||||||
apply_config(&config, &filters, &prefix).await
|
apply_config(&config, &filters, &prefix, dry_run).await
|
||||||
}
|
}
|
||||||
C::Logger {
|
C::Logger {
|
||||||
ref log_path,
|
ref log_path,
|
||||||
@ -156,7 +160,12 @@ async fn main() -> Result<()> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn apply_config(config_file: &str, filters: &[glob::Pattern], chroot: &str) -> Result<()> {
|
async fn apply_config(
|
||||||
|
config_file: &str,
|
||||||
|
filters: &[glob::Pattern],
|
||||||
|
chroot: &str,
|
||||||
|
dry_run: bool,
|
||||||
|
) -> Result<()> {
|
||||||
let config = fs::read_to_string(config_file).await?;
|
let config = fs::read_to_string(config_file).await?;
|
||||||
let config: dkl::Config = serde_yaml::from_str(&config)?;
|
let config: dkl::Config = serde_yaml::from_str(&config)?;
|
||||||
|
|
||||||
@ -168,7 +177,7 @@ async fn apply_config(config_file: &str, filters: &[glob::Pattern], chroot: &str
|
|||||||
.collect()
|
.collect()
|
||||||
};
|
};
|
||||||
|
|
||||||
dkl::apply::files(&files, chroot).await
|
dkl::apply::files(&files, chroot, dry_run).await
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Subcommand)]
|
#[derive(Subcommand)]
|
||||||
|
|||||||
@ -105,7 +105,7 @@ impl Default for SSHServer {
|
|||||||
|
|
||||||
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
|
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
|
||||||
pub struct LvmVG {
|
pub struct LvmVG {
|
||||||
#[serde(alias = "vg")]
|
#[serde(rename = "vg", alias = "name")]
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub pvs: LvmPV,
|
pub pvs: LvmPV,
|
||||||
|
|
||||||
|
|||||||
@ -170,6 +170,8 @@ pub struct Config {
|
|||||||
pub host_templates: Vec<HostConfig>,
|
pub host_templates: Vec<HostConfig>,
|
||||||
#[serde(default, rename = "SSLConfig")]
|
#[serde(default, rename = "SSLConfig")]
|
||||||
pub ssl_config: String,
|
pub ssl_config: String,
|
||||||
|
#[serde(default, deserialize_with = "deserialize_null_as_default")]
|
||||||
|
pub extra_ca_certs: Map<String, String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
// compensate for go's encoder pitfalls
|
// compensate for go's encoder pitfalls
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
use eyre::{format_err, Result};
|
use eyre::{Result, format_err};
|
||||||
use log::{debug, error, info, warn};
|
use log::{debug, error, info, warn};
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use tokio::{fs, io::AsyncWriteExt, process::Command};
|
use tokio::{fs, io::AsyncWriteExt, process::Command};
|
||||||
|
|||||||
10
src/lib.rs
10
src/lib.rs
@ -1,10 +1,10 @@
|
|||||||
pub mod apply;
|
pub mod apply;
|
||||||
pub mod proxy;
|
|
||||||
pub mod bootstrap;
|
pub mod bootstrap;
|
||||||
pub mod dls;
|
pub mod dls;
|
||||||
pub mod dynlay;
|
pub mod dynlay;
|
||||||
pub mod fs;
|
pub mod fs;
|
||||||
pub mod logger;
|
pub mod logger;
|
||||||
|
pub mod proxy;
|
||||||
|
|
||||||
#[derive(Debug, Default, serde::Deserialize, serde::Serialize)]
|
#[derive(Debug, Default, serde::Deserialize, serde::Serialize)]
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
@ -63,9 +63,10 @@ pub struct File {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, serde::Deserialize, serde::Serialize)]
|
#[derive(Debug, PartialEq, Eq, serde::Deserialize, serde::Serialize)]
|
||||||
#[serde(rename_all = "snake_case")]
|
#[serde(rename_all = "lowercase")]
|
||||||
pub enum FileKind {
|
pub enum FileKind {
|
||||||
Content(String),
|
Content(String),
|
||||||
|
Content64(String),
|
||||||
Symlink(String),
|
Symlink(String),
|
||||||
Dir(bool),
|
Dir(bool),
|
||||||
}
|
}
|
||||||
@ -80,3 +81,8 @@ impl Config {
|
|||||||
self.files.iter().find(|f| f.path == path)
|
self.files.iter().find(|f| f.path == path)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn base64_decode(s: &str) -> Result<Vec<u8>, base64::DecodeError> {
|
||||||
|
use base64::{Engine, prelude::BASE64_STANDARD_NO_PAD as B64};
|
||||||
|
B64.decode(s.trim_end_matches('='))
|
||||||
|
}
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
use async_compression::tokio::write::{ZstdDecoder, ZstdEncoder};
|
use async_compression::tokio::write::{ZstdDecoder, ZstdEncoder};
|
||||||
use chrono::{DurationRound, TimeDelta, Utc};
|
use chrono::{DurationRound, TimeDelta, Utc};
|
||||||
use eyre::{format_err, Result};
|
use eyre::{Result, format_err};
|
||||||
use log::{debug, error, warn};
|
use log::{debug, error, warn};
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::process::Stdio;
|
use std::process::Stdio;
|
||||||
@ -9,7 +9,7 @@ use tokio::{
|
|||||||
io::{self, AsyncBufReadExt, AsyncRead, AsyncWrite, AsyncWriteExt, BufReader, BufWriter},
|
io::{self, AsyncBufReadExt, AsyncRead, AsyncWrite, AsyncWriteExt, BufReader, BufWriter},
|
||||||
process,
|
process,
|
||||||
sync::mpsc,
|
sync::mpsc,
|
||||||
time::{sleep, Duration},
|
time::{Duration, sleep},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub type Timestamp = chrono::DateTime<Utc>;
|
pub type Timestamp = chrono::DateTime<Utc>;
|
||||||
|
|||||||
Reference in New Issue
Block a user