Compare commits
2 Commits
feat/certi
...
feat/cert_
| Author | SHA1 | Date | |
|---|---|---|---|
| dc421fa099 | |||
| 2153edc68c |
@@ -12,7 +12,13 @@ use harmony::{
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
let cert_manager = CertificateManagementScore {
|
||||
config: CertificateManagementConfig {},
|
||||
config: CertificateManagementConfig {
|
||||
name: todo!(),
|
||||
namespace: todo!(),
|
||||
acme_issuer: todo!(),
|
||||
ca_issuer: todo!(),
|
||||
self_signed: todo!(),
|
||||
},
|
||||
};
|
||||
|
||||
harmony_cli::run(
|
||||
|
||||
@@ -18,7 +18,8 @@ use crate::{
|
||||
inventory::Inventory,
|
||||
modules::{
|
||||
cert_manager::{
|
||||
capability::{CertificateManagement, CertificateManagementConfig},
|
||||
capability::{CertificateManagement, CertificateManagementConfig, Issuer},
|
||||
crd::{score_certificate::CertificateScore, score_issuer::IssuerScore},
|
||||
operator::CertManagerOperatorScore,
|
||||
},
|
||||
k3d::K3DInstallationScore,
|
||||
@@ -382,6 +383,38 @@ impl CertificateManagement for K8sAnywhereTopology {
|
||||
),
|
||||
})
|
||||
}
|
||||
|
||||
async fn ensure_ready(
|
||||
&self,
|
||||
config: &CertificateManagementConfig,
|
||||
) -> Result<PreparationOutcome, PreparationError> {
|
||||
self.certificate_issuer_ready(Issuer::Issuer, config)
|
||||
.await?;
|
||||
Ok(PreparationOutcome::Success {
|
||||
details: "issuer ready".to_string(),
|
||||
})
|
||||
}
|
||||
|
||||
async fn create_certificate(
|
||||
&self,
|
||||
cert_name: String,
|
||||
config: &CertificateManagementConfig,
|
||||
) -> Result<PreparationOutcome, PreparationError> {
|
||||
let cert = CertificateScore {
|
||||
name: cert_name,
|
||||
config: config.clone(),
|
||||
};
|
||||
cert.interpret(&Inventory::empty(), self)
|
||||
.await
|
||||
.map_err(|e| PreparationError { msg: e.to_string() })?;
|
||||
|
||||
Ok(PreparationOutcome::Success {
|
||||
details: format!(
|
||||
"Created cert into ns: {:#?}",
|
||||
config.namespace.clone()
|
||||
),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl K8sAnywhereTopology {
|
||||
@@ -935,6 +968,29 @@ impl K8sAnywhereTopology {
|
||||
),
|
||||
})
|
||||
}
|
||||
|
||||
async fn certificate_issuer_ready(
|
||||
&self,
|
||||
issuer: Issuer,
|
||||
config: &CertificateManagementConfig,
|
||||
) -> Result<PreparationOutcome, PreparationError> {
|
||||
match issuer {
|
||||
Issuer::ClusterIssuer => todo!(),
|
||||
|
||||
Issuer::Issuer => {
|
||||
let issuer_score = IssuerScore {
|
||||
config: config.clone(),
|
||||
};
|
||||
issuer_score
|
||||
.interpret(&Inventory::empty(), self)
|
||||
.await
|
||||
.map_err(|e| PreparationError { msg: e.to_string() })?;
|
||||
Ok(PreparationOutcome::Success {
|
||||
details: format!("issuer of kind {} is ready", issuer.to_string()),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
use async_trait::async_trait;
|
||||
use serde::Serialize;
|
||||
|
||||
use crate::{
|
||||
interpret::Outcome,
|
||||
topology::{PreparationError, PreparationOutcome},
|
||||
};
|
||||
use crate::{modules::cert_manager::crd::{AcmeIssuer, CaIssuer}, topology::{PreparationError, PreparationOutcome}};
|
||||
|
||||
#[async_trait]
|
||||
pub trait CertificateManagement: Send + Sync {
|
||||
@@ -12,7 +9,40 @@ pub trait CertificateManagement: Send + Sync {
|
||||
&self,
|
||||
config: &CertificateManagementConfig,
|
||||
) -> Result<PreparationOutcome, PreparationError>;
|
||||
|
||||
async fn ensure_ready(
|
||||
&self,
|
||||
config: &CertificateManagementConfig,
|
||||
) -> Result<PreparationOutcome, PreparationError>;
|
||||
|
||||
async fn create_certificate(
|
||||
&self,
|
||||
cert_name: String,
|
||||
config: &CertificateManagementConfig,
|
||||
) -> Result<PreparationOutcome, PreparationError>;
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
pub struct CertificateManagementConfig {}
|
||||
pub struct CertificateManagementConfig {
|
||||
pub name: String,
|
||||
pub namespace: Option<String>,
|
||||
pub acme_issuer: Option<AcmeIssuer>,
|
||||
pub ca_issuer: Option<CaIssuer>,
|
||||
pub self_signed: bool,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub enum Issuer {
|
||||
ClusterIssuer,
|
||||
Issuer,
|
||||
}
|
||||
|
||||
impl std::fmt::Display for Issuer {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Issuer::Issuer => f.write_str("Issuer"),
|
||||
Issuer::ClusterIssuer => f.write_str("ClusterIssuer"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
113
harmony/src/modules/cert_manager/crd/certificate.rs
Normal file
113
harmony/src/modules/cert_manager/crd/certificate.rs
Normal file
@@ -0,0 +1,113 @@
|
||||
use kube::{CustomResource, api::ObjectMeta};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(CustomResource, Deserialize, Serialize, Clone, Debug)]
|
||||
#[kube(
|
||||
group = "cert-manager.io",
|
||||
version = "v1",
|
||||
kind = "Certificate",
|
||||
plural = "certificates",
|
||||
namespaced = true,
|
||||
schema = "disabled"
|
||||
)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct CertificateSpec {
|
||||
/// Name of the Secret where the certificate will be stored
|
||||
pub secret_name: String,
|
||||
|
||||
/// Common Name (optional but often discouraged in favor of SANs)
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub common_name: Option<String>,
|
||||
|
||||
/// DNS Subject Alternative Names
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub dns_names: Option<Vec<String>>,
|
||||
|
||||
/// IP Subject Alternative Names
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub ip_addresses: Option<Vec<String>>,
|
||||
|
||||
/// Certificate duration (e.g. "2160h")
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub duration: Option<String>,
|
||||
|
||||
/// How long before expiry cert-manager should renew
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub renew_before: Option<String>,
|
||||
|
||||
/// Reference to the Issuer or ClusterIssuer
|
||||
pub issuer_ref: IssuerRef,
|
||||
|
||||
/// Is this a CA certificate
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub is_ca: Option<bool>,
|
||||
|
||||
/// Private key configuration
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub private_key: Option<PrivateKey>,
|
||||
}
|
||||
|
||||
impl Default for Certificate {
|
||||
fn default() -> Self {
|
||||
Certificate {
|
||||
metadata: ObjectMeta::default(),
|
||||
spec: CertificateSpec::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for CertificateSpec {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
secret_name: String::new(),
|
||||
common_name: None,
|
||||
dns_names: None,
|
||||
ip_addresses: None,
|
||||
duration: None,
|
||||
renew_before: None,
|
||||
issuer_ref: IssuerRef::default(),
|
||||
is_ca: None,
|
||||
private_key: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize, Clone, Debug)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct IssuerRef {
|
||||
pub name: String,
|
||||
|
||||
/// Either "Issuer" or "ClusterIssuer"
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub kind: Option<String>,
|
||||
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub group: Option<String>,
|
||||
}
|
||||
|
||||
impl Default for IssuerRef {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
name: String::new(),
|
||||
kind: None,
|
||||
group: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize, Clone, Debug)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct PrivateKey {
|
||||
/// RSA or ECDSA
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub algorithm: Option<String>,
|
||||
|
||||
/// Key size (e.g. 2048, 4096)
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub size: Option<u32>,
|
||||
|
||||
/// Rotation policy: "Never" or "Always"
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub rotation_policy: Option<String>,
|
||||
}
|
||||
|
||||
45
harmony/src/modules/cert_manager/crd/cluster_issuer.rs
Normal file
45
harmony/src/modules/cert_manager/crd/cluster_issuer.rs
Normal file
@@ -0,0 +1,45 @@
|
||||
use kube::{CustomResource, api::ObjectMeta};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::modules::cert_manager::crd::{AcmeIssuer, CaIssuer, SelfSignedIssuer};
|
||||
|
||||
#[derive(CustomResource, Deserialize, Serialize, Clone, Debug)]
|
||||
#[kube(
|
||||
group = "cert-manager.io",
|
||||
version = "v1",
|
||||
kind = "ClusterIssuer",
|
||||
plural = "clusterissuers",
|
||||
namespaced = false,
|
||||
schema = "disabled"
|
||||
)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ClusterIssuerSpec {
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub self_signed: Option<SelfSignedIssuer>,
|
||||
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub ca: Option<CaIssuer>,
|
||||
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub acme: Option<AcmeIssuer>,
|
||||
}
|
||||
|
||||
impl Default for ClusterIssuer {
|
||||
fn default() -> Self {
|
||||
ClusterIssuer {
|
||||
metadata: ObjectMeta::default(),
|
||||
spec: ClusterIssuerSpec::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for ClusterIssuerSpec {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
self_signed: None,
|
||||
ca: None,
|
||||
acme: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
44
harmony/src/modules/cert_manager/crd/issuer.rs
Normal file
44
harmony/src/modules/cert_manager/crd/issuer.rs
Normal file
@@ -0,0 +1,44 @@
|
||||
use kube::{CustomResource, api::ObjectMeta};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::modules::cert_manager::crd::{AcmeIssuer, CaIssuer, SelfSignedIssuer};
|
||||
|
||||
#[derive(CustomResource, Deserialize, Serialize, Clone, Debug)]
|
||||
#[kube(
|
||||
group = "cert-manager.io",
|
||||
version = "v1",
|
||||
kind = "Issuer",
|
||||
plural = "issuers",
|
||||
namespaced = true,
|
||||
schema = "disabled"
|
||||
)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct IssuerSpec {
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub self_signed: Option<SelfSignedIssuer>,
|
||||
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub ca: Option<CaIssuer>,
|
||||
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub acme: Option<AcmeIssuer>,
|
||||
}
|
||||
|
||||
impl Default for Issuer {
|
||||
fn default() -> Self {
|
||||
Issuer {
|
||||
metadata: ObjectMeta::default(),
|
||||
spec: IssuerSpec::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for IssuerSpec {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
self_signed: None,
|
||||
ca: None,
|
||||
acme: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
66
harmony/src/modules/cert_manager/crd/mod.rs
Normal file
66
harmony/src/modules/cert_manager/crd/mod.rs
Normal file
@@ -0,0 +1,66 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
|
||||
pub mod certificate;
|
||||
pub mod issuer;
|
||||
pub mod cluster_issuer;
|
||||
//pub mod score_cluster_issuer;
|
||||
pub mod score_issuer;
|
||||
pub mod score_certificate;
|
||||
|
||||
#[derive(Deserialize, Serialize, Clone, Debug)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct CaIssuer {
|
||||
/// Secret containing `tls.crt` and `tls.key`
|
||||
pub secret_name: String,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize, Clone, Debug, Default)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct SelfSignedIssuer {}
|
||||
|
||||
#[derive(Deserialize, Serialize, Clone, Debug)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct AcmeIssuer {
|
||||
pub server: String,
|
||||
pub email: String,
|
||||
|
||||
/// Secret used to store the ACME account private key
|
||||
pub private_key_secret_ref: SecretKeySelector,
|
||||
|
||||
pub solvers: Vec<AcmeSolver>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize, Clone, Debug)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct SecretKeySelector {
|
||||
pub name: String,
|
||||
pub key: String,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize, Clone, Debug)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct AcmeSolver {
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub http01: Option<Http01Solver>,
|
||||
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub dns01: Option<Dns01Solver>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize, Clone, Debug)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct Dns01Solver {}
|
||||
|
||||
#[derive(Deserialize, Serialize, Clone, Debug)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct Http01Solver {
|
||||
pub ingress: IngressSolver,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize, Clone, Debug)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct IngressSolver {
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub class: Option<String>,
|
||||
}
|
||||
47
harmony/src/modules/cert_manager/crd/score_certificate.rs
Normal file
47
harmony/src/modules/cert_manager/crd/score_certificate.rs
Normal file
@@ -0,0 +1,47 @@
|
||||
use kube::api::ObjectMeta;
|
||||
use serde::Serialize;
|
||||
|
||||
use crate::{
|
||||
interpret::Interpret,
|
||||
modules::{
|
||||
cert_manager::{
|
||||
capability::CertificateManagementConfig,
|
||||
crd::certificate::{Certificate, CertificateSpec, IssuerRef},
|
||||
},
|
||||
k8s::resource::K8sResourceScore,
|
||||
},
|
||||
score::Score,
|
||||
topology::{K8sclient, Topology},
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
pub struct CertificateScore {
|
||||
pub name: String,
|
||||
pub config: CertificateManagementConfig,
|
||||
}
|
||||
|
||||
impl<T: Topology + K8sclient> Score<T> for CertificateScore {
|
||||
fn name(&self) -> String {
|
||||
"CertificateScore".to_string()
|
||||
}
|
||||
|
||||
fn create_interpret(&self) -> Box<dyn Interpret<T>> {
|
||||
let cert = Certificate {
|
||||
metadata: ObjectMeta {
|
||||
name: Some(self.name.clone()),
|
||||
namespace: self.config.namespace.clone(),
|
||||
..Default::default()
|
||||
},
|
||||
spec: CertificateSpec {
|
||||
secret_name: format!("{}-tls", self.name.clone()),
|
||||
issuer_ref: IssuerRef {
|
||||
name: self.config.name.clone(),
|
||||
kind: Some("Issuer".into()),
|
||||
group: Some("cert-manager.io".into()),
|
||||
},
|
||||
..Default::default()
|
||||
},
|
||||
};
|
||||
K8sResourceScore::single(cert, self.config.namespace.clone()).create_interpret()
|
||||
}
|
||||
}
|
||||
51
harmony/src/modules/cert_manager/crd/score_cluster_issuer.rs
Normal file
51
harmony/src/modules/cert_manager/crd/score_cluster_issuer.rs
Normal file
@@ -0,0 +1,51 @@
|
||||
use kube::api::ObjectMeta;
|
||||
use serde::Serialize;
|
||||
|
||||
use crate::{
|
||||
interpret::Interpret,
|
||||
modules::{
|
||||
cert_manager::crd::{
|
||||
AcmeIssuer, CaIssuer, SelfSignedIssuer,
|
||||
cluster_issuer::{ClusterIssuer, ClusterIssuerSpec},
|
||||
},
|
||||
k8s::resource::K8sResourceScore,
|
||||
},
|
||||
score::Score,
|
||||
topology::{K8sclient, Topology},
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
pub struct ClusterIssuerScore {
|
||||
name: String,
|
||||
acme_issuer: Option<AcmeIssuer>,
|
||||
ca_issuer: Option<CaIssuer>,
|
||||
self_signed: bool,
|
||||
}
|
||||
|
||||
impl<T: Topology + K8sclient> Score<T> for ClusterIssuerScore {
|
||||
fn name(&self) -> String {
|
||||
"ClusterIssuerScore".to_string()
|
||||
}
|
||||
|
||||
fn create_interpret(&self) -> Box<dyn Interpret<T>> {
|
||||
let metadata = ObjectMeta {
|
||||
name: Some(self.name.clone()),
|
||||
namespace: None,
|
||||
..ObjectMeta::default()
|
||||
};
|
||||
|
||||
let spec = ClusterIssuerSpec {
|
||||
acme: self.acme_issuer.clone(),
|
||||
ca: self.ca_issuer.clone(),
|
||||
self_signed: if self.self_signed {
|
||||
Some(SelfSignedIssuer::default())
|
||||
} else {
|
||||
None
|
||||
},
|
||||
};
|
||||
|
||||
let cluster_issuer = ClusterIssuer { metadata, spec };
|
||||
|
||||
K8sResourceScore::single(cluster_issuer, None).create_interpret()
|
||||
}
|
||||
}
|
||||
48
harmony/src/modules/cert_manager/crd/score_issuer.rs
Normal file
48
harmony/src/modules/cert_manager/crd/score_issuer.rs
Normal file
@@ -0,0 +1,48 @@
|
||||
use kube::api::ObjectMeta;
|
||||
use serde::Serialize;
|
||||
|
||||
use crate::{
|
||||
interpret::Interpret,
|
||||
modules::{
|
||||
cert_manager::{capability::CertificateManagementConfig, crd::{
|
||||
AcmeIssuer, CaIssuer, SelfSignedIssuer,
|
||||
issuer::{Issuer, IssuerSpec},
|
||||
}},
|
||||
k8s::resource::K8sResourceScore,
|
||||
},
|
||||
score::Score,
|
||||
topology::{K8sclient, Topology},
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
pub struct IssuerScore {
|
||||
pub config: CertificateManagementConfig,
|
||||
}
|
||||
|
||||
impl<T: Topology + K8sclient> Score<T> for IssuerScore {
|
||||
fn name(&self) -> String {
|
||||
"IssuerScore".to_string()
|
||||
}
|
||||
|
||||
fn create_interpret(&self) -> Box<dyn Interpret<T>> {
|
||||
let metadata = ObjectMeta {
|
||||
name: Some(self.config.name.clone()),
|
||||
namespace: self.config.namespace.clone(),
|
||||
..ObjectMeta::default()
|
||||
};
|
||||
|
||||
let spec = IssuerSpec {
|
||||
acme: self.config.acme_issuer.clone(),
|
||||
ca: self.config.ca_issuer.clone(),
|
||||
self_signed: if self.config.self_signed {
|
||||
Some(SelfSignedIssuer::default())
|
||||
} else {
|
||||
None
|
||||
},
|
||||
};
|
||||
|
||||
let issuer = Issuer { metadata, spec };
|
||||
|
||||
K8sResourceScore::single(issuer, self.config.namespace.clone()).create_interpret()
|
||||
}
|
||||
}
|
||||
@@ -3,4 +3,5 @@ pub mod cluster_issuer;
|
||||
mod helm;
|
||||
pub mod operator;
|
||||
pub mod score_k8s;
|
||||
pub mod crd;
|
||||
pub use helm::*;
|
||||
|
||||
Reference in New Issue
Block a user