Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Replace time with chrono #23

Merged
merged 1 commit into from
Jul 18, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ serde_derive = "1"
serde_json = "1"
openssl = "0"
base64 = "0"
time = "=0.1.45" # same version as acme-lib uses
acme-lib = { git = 'https://github.com/DBCDK/acme-lib', branch = 'add-sans-individually' }
regex = "1"
lazy_static = "1"
Expand Down
55 changes: 34 additions & 21 deletions src/common.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
extern crate openssl;
extern crate regex;

use self::openssl::asn1::Asn1TimeRef;
use self::openssl::asn1::{Asn1Time, Asn1TimeRef};
use self::openssl::nid::Nid;
use self::openssl::x509::{X509NameEntryRef, X509};

Expand All @@ -20,6 +20,7 @@ use std::collections::HashSet;
use std::convert::TryFrom;
use std::fmt::Formatter;
use std::path::PathBuf;
use chrono::{TimeZone, Utc};
pub type CertName = String;

#[derive(Debug, Clone, Serialize)]
Expand Down Expand Up @@ -199,12 +200,17 @@ impl std::convert::From<KubeError> for PersistError {
}
}

pub enum TimeError {
Diff(openssl::error::ErrorStack),
UnixTimestampOutOfBounds,
}

#[derive(Debug, Clone)]
pub struct Cert {
pub cn: String,
pub sans: HashSet<String>,
pub valid_from: time::Tm, // always utc
pub valid_to: time::Tm // always utc
pub valid_from: chrono::DateTime<Utc>,
pub valid_to: chrono::DateTime<Utc>,
}

impl Cert {
Expand Down Expand Up @@ -256,18 +262,24 @@ impl Cert {
}
}

// #cryhard rust openssl lib doesn't allow for a plain convertion from ASN-time to time::Tm or any
// other rustlang time type. :( So... We to_string the ASNTime and re-parse it and hope for the best
fn get_timestamp(time_ref: &Asn1TimeRef) -> Result<time::Tm, time::ParseError> {
const IN_FORMAT: &str = "%b %e %H:%M:%S %Y %Z"; // May 31 15:21:16 2020 GMT
time::strptime(&format!("{}", &time_ref), IN_FORMAT)
fn get_timestamp(time_ref: &Asn1TimeRef) -> Result<chrono::DateTime<Utc>, TimeError> {
let epoch = Asn1Time::from_unix(0).expect("Failed to create Asn1Time at unix epoch");
let diff = epoch.diff(&time_ref).map_err(TimeError::Diff)?;
//let diff = time_ref.diff(&epoch).map_err(TimeError::Diff)?;
let unix_ts = diff.days as i64 * (24 * 60 * 60) + diff.secs as i64;
use chrono::offset::LocalResult;
match chrono::Utc.timestamp_opt(unix_ts.into(), 0) {
LocalResult::None => Err(TimeError::UnixTimestampOutOfBounds),
LocalResult::Single(datetime) => Ok(datetime),
LocalResult::Ambiguous(_, _) => unreachable!("timestamp_opt never returns LocalResult::Ambigious"),
}
}

pub fn state(&self, config: &FaytheConfig, spec: &CertSpec) -> CertState {
let now = time::now_utc();
let now = Utc::now();
let state = match self.valid_to {
to if now > to => CertState::Expired,
to if now + time::Duration::days(config.renewal_threshold as i64) > to => CertState::ExpiresSoon,
to if now + chrono::Duration::days(config.renewal_threshold as i64) > to => CertState::ExpiresSoon,
_ if now < self.valid_from => CertState::NotYetValid,
to if now >= self.valid_from && now <= to => CertState::Valid,
_ => CertState::Unknown,
Expand Down Expand Up @@ -369,8 +381,8 @@ impl std::convert::From<SpecError> for TouchError {
}
}

impl std::convert::From<time::ParseError> for CertState {
fn from(_: time::ParseError) -> Self {
impl std::convert::From<TimeError> for CertState {
fn from(_: TimeError) -> Self {
CertState::ParseError
}
}
Expand All @@ -385,6 +397,7 @@ pub mod tests {
use crate::set;
use super::DNSName;
use crate::config::{KubeMonitorConfig, FileMonitorConfig, MonitorConfig};
use chrono::DateTime;

const TIME_FORMAT: &str = "%Y-%m-%dT%H:%M:%S%z"; // 2019-10-09T11:50:22+0200

Expand Down Expand Up @@ -487,7 +500,7 @@ pub mod tests {
Ingress{
name: "test".to_string(),
namespace: "test".to_string(),
touched: time::empty_tm(),
touched: DateTime::<Utc>::MIN_UTC,
hosts: [host.to_string()].to_vec(),
}
}
Expand Down Expand Up @@ -518,8 +531,8 @@ pub mod tests {
Not Before: Dec 1 11:42:07 2020 GMT
Not After : Nov 24 11:42:07 2050 GMT
*/
assert_eq!(cert.valid_from, time::strptime("2020-12-01T11:42:07+0000", TIME_FORMAT).unwrap());
assert_eq!(cert.valid_to, time::strptime("2050-11-24T11:42:07+0000", TIME_FORMAT).unwrap());
assert_eq!(cert.valid_from, DateTime::parse_from_str("2020-12-01T11:42:07+0000", TIME_FORMAT).unwrap());
assert_eq!(cert.valid_to, DateTime::parse_from_str("2050-11-24T11:42:07+0000", TIME_FORMAT).unwrap());

let config = create_test_kubernetes_config(false).faythe_config;
let spec = create_test_certspec(cn, sans);
Expand All @@ -540,8 +553,8 @@ pub mod tests {
Not Before: Dec 1 11:42:07 2020 GMT
Not After : Nov 24 11:42:07 2050 GMT
*/
assert_eq!(cert.valid_from, time::strptime("2020-12-01T11:42:07+0000", TIME_FORMAT).unwrap());
assert_eq!(cert.valid_to, time::strptime("2050-11-24T11:42:07+0000", TIME_FORMAT).unwrap());
assert_eq!(cert.valid_from, DateTime::parse_from_str("2020-12-01T11:42:07+0000", TIME_FORMAT).unwrap());
assert_eq!(cert.valid_to, DateTime::parse_from_str("2050-11-24T11:42:07+0000", TIME_FORMAT).unwrap());

let config = create_test_kubernetes_config(false).faythe_config;
let spec = create_test_certspec(cn, sans);
Expand All @@ -559,8 +572,8 @@ pub mod tests {
Not Before: Dec 1 11:42:07 2020 GMT
Not After : Nov 24 11:42:07 2050 GMT
*/
assert_eq!(cert.valid_from, time::strptime("2020-12-01T11:42:07+0000", TIME_FORMAT).unwrap());
assert_eq!(cert.valid_to, time::strptime("2050-11-24T11:42:07+0000", TIME_FORMAT).unwrap());
assert_eq!(cert.valid_from, DateTime::parse_from_str("2020-12-01T11:42:07+0000", TIME_FORMAT).unwrap());
assert_eq!(cert.valid_to, DateTime::parse_from_str("2050-11-24T11:42:07+0000", TIME_FORMAT).unwrap());

let cn = "cn.longlived";
let sans = set![cn, "san1.longlived", "san2.shortlived"];
Expand Down Expand Up @@ -610,8 +623,8 @@ pub mod tests {
Not Before: Dec 1 11:41:19 2020 GMT
Not After : Dec 2 11:41:19 2020 GMT
*/
assert_eq!(cert.valid_from, time::strptime("2020-12-01T11:41:19+0000", TIME_FORMAT).unwrap());
assert_eq!(cert.valid_to, time::strptime("2020-12-02T11:41:19+0000", TIME_FORMAT).unwrap());
assert_eq!(cert.valid_from, DateTime::parse_from_str("2020-12-01T11:41:19+0000", TIME_FORMAT).unwrap());
assert_eq!(cert.valid_to, DateTime::parse_from_str("2020-12-02T11:41:19+0000", TIME_FORMAT).unwrap());

let config = create_test_kubernetes_config(false).faythe_config;
let spec = create_test_certspec(cn, sans);
Expand Down
1 change: 0 additions & 1 deletion src/file.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
extern crate time;
extern crate walkdir;


Expand Down
8 changes: 5 additions & 3 deletions src/issuer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ use crate::dns::DNSError;
use crate::metrics;
use crate::metrics::MetricsType;

use chrono::Utc;

pub fn process(faythe_config: FaytheConfig, rx: Receiver<CertSpec>) {

let mut queue: VecDeque<IssueOrder> = VecDeque::new();
Expand Down Expand Up @@ -82,7 +84,7 @@ fn check_queue(queue: &mut VecDeque<IssueOrder>) -> Result<(), IssuerError> {
IssuerError::DNS(dns::DNSError::WrongAnswer(domain)) => {
log::data("Wrong DNS answer", &domain);
// Retry for two hours. Propagation on gratisdns is pretty slow.
if time::now_utc() < order.challenge_time + time::Duration::minutes(120) {
if Utc::now() < order.challenge_time + chrono::Duration::minutes(120) {
queue.push_back(order);
} else {
log::data("giving up validating dns challenge for spec", &order.spec);
Expand Down Expand Up @@ -169,7 +171,7 @@ fn setup_challenge(config: &FaytheConfig, spec: &CertSpec) -> Result<IssueOrder,
spec: spec.clone(),
authorizations,
inner: ord_new,
challenge_time: time::now_utc(),
challenge_time: Utc::now(),
auth_dns_servers,
val_dns_servers,
})
Expand All @@ -179,7 +181,7 @@ struct IssueOrder {
spec: CertSpec,
inner: NewOrder<MemoryPersist>,
authorizations: Vec<Auth<MemoryPersist>>,
challenge_time: time::Tm,
challenge_time: chrono::DateTime<Utc>,
auth_dns_servers: HashSet<String>,
val_dns_servers: HashSet<String>,
}
Expand Down
26 changes: 13 additions & 13 deletions src/kube.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
extern crate serde;
extern crate serde_json;
extern crate base64;
extern crate time;

use serde_json::{Value};
use serde_json::json;
Expand All @@ -22,12 +21,14 @@ use acme_lib::Certificate;

use base64::Engine;

use chrono::Utc;

#[derive(Debug, Clone)]
pub struct Ingress {
pub name: String,
pub namespace: String,
pub hosts: Vec<String>,
pub touched: time::Tm,
pub touched: chrono::DateTime<Utc>,
}

#[derive(Debug, Clone)]
Expand Down Expand Up @@ -77,7 +78,7 @@ pub fn get_ingresses(config: &KubeMonitorConfig) -> Result<Vec<Ingress>, KubeErr
let rules = vec(&i["spec"]["rules"])?;
let touched = match &config.touch_annotation {
Some(a) => tm(i["metadata"]["annotations"].get(&a)),
None => time::empty_tm()
None => chrono::DateTime::<Utc>::MIN_UTC,
};
ingresses.push(Ingress{
name: sr(&i["metadata"]["name"])?,
Expand Down Expand Up @@ -135,12 +136,12 @@ fn sr(subject: &Value) -> Result<String, KubeError> {
}
}

fn tm(subject: Option<&Value>) -> time::Tm {
fn tm(subject: Option<&Value>) -> chrono::DateTime<Utc> {
let a = subject.and_then(|s| s.as_str());
match a {
// assume "now" if there is something non-parsable in there
Some(a) => time::strptime(&a, self::TIME_FORMAT).unwrap_or(time::now_utc()),
_ => time::empty_tm()
Some(a) => chrono::DateTime::parse_from_str(&a, self::TIME_FORMAT).map(std::convert::Into::into).unwrap_or(Utc::now()),
_ => chrono::DateTime::<Utc>::MIN_UTC,
}
}

Expand Down Expand Up @@ -261,14 +262,13 @@ impl CertSpecable for Ingress {
fn touch(&self, config: &ConfigContainer) -> Result<(), TouchError> {
let monitor_config = config.get_kube_monitor_config()?;
match &monitor_config.touch_annotation {
Some(a) => Ok(self.annotate(&a,
&time::strftime(TIME_FORMAT, &time::now_utc())?)?),
Some(a) => Ok(self.annotate(&a, &Utc::now().format(TIME_FORMAT).to_string())?),
None => Ok(())
}
}

fn should_retry(&self, config: &ConfigContainer) -> bool {
time::now_utc() > self.touched + time::Duration::milliseconds(config.faythe_config.issue_grace as i64)
Utc::now() > self.touched + chrono::Duration::milliseconds(config.faythe_config.issue_grace as i64)
}
}

Expand Down Expand Up @@ -303,15 +303,15 @@ impl std::convert::From<base64::DecodeError> for KubeError {
KubeError::Format
}
}
impl std::convert::From<time::ParseError> for KubeError {
fn from(err: time::ParseError) -> Self {
impl std::convert::From<chrono::ParseError> for KubeError {
fn from(err: chrono::ParseError) -> Self {
log::error("Failed to parse timestamp", &err);
KubeError::Format
}
}

impl std::convert::From<time::ParseError> for TouchError {
fn from(_err: time::ParseError) -> Self {
impl std::convert::From<chrono::ParseError> for TouchError {
fn from(_err: chrono::ParseError) -> Self {
TouchError::Failed
}
}
Expand Down
11 changes: 5 additions & 6 deletions src/monitor.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
extern crate time;

use std::thread;
use std::time::Duration;

Expand All @@ -24,6 +22,8 @@ use std::prelude::v1::Vec;
use crate::metrics;
use crate::metrics::MetricsType;

use chrono::Utc;

pub fn monitor_k8s(config: ConfigContainer, tx: Sender<CertSpec>) {
log::info("k8s monitoring-started");
let monitor_config = config.get_kube_monitor_config().unwrap();
Expand Down Expand Up @@ -131,7 +131,6 @@ mod tests {
use crate::mpsc;
use crate::mpsc::{Receiver, Sender};
use std::collections::HashSet;
use std::ops::Add;

fn create_channel() -> (Sender<CertSpec>, Receiver<CertSpec>) {
mpsc::channel()
Expand All @@ -141,7 +140,7 @@ mod tests {
[Ingress {
name: "test".to_string(),
namespace: "test".to_string(),
touched: time::empty_tm(),
touched: chrono::DateTime::<Utc>::MIN_UTC,
hosts: [host.clone()].to_vec(),
}]
.to_vec()
Expand All @@ -156,8 +155,8 @@ mod tests {
cert: Cert {
cn: host.clone(),
sans,
valid_from: time::now_utc(),
valid_to: time::now_utc().add(time::Duration::days(valid_days)),
valid_from: Utc::now(),
valid_to: Utc::now() + chrono::Duration::days(valid_days),
},
key: vec![],
}
Expand Down
Loading