-
-
Notifications
You must be signed in to change notification settings - Fork 17
Expand file tree
/
Copy pathsecret_class.rs
More file actions
165 lines (145 loc) · 5.66 KB
/
secret_class.rs
File metadata and controls
165 lines (145 loc) · 5.66 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
use k8s_openapi::api::core::v1::{EphemeralVolumeSource, Volume};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use snafu::{ResultExt, Snafu};
use crate::builder::pod::volume::{
SecretOperatorVolumeSourceBuilder, SecretOperatorVolumeSourceBuilderError, VolumeBuilder,
};
#[derive(Debug, PartialEq, Snafu)]
pub enum SecretClassVolumeError {
#[snafu(display("failed to build secret operator volume"))]
SecretOperatorVolume {
source: SecretOperatorVolumeSourceBuilderError,
},
}
#[derive(
Clone, Debug, Deserialize, Eq, Hash, JsonSchema, Ord, PartialEq, PartialOrd, Serialize,
)]
#[serde(rename_all = "camelCase")]
pub struct SecretClassVolume {
/// [SecretClass](DOCS_BASE_URL_PLACEHOLDER/secret-operator/secretclass) containing the LDAP bind credentials.
pub secret_class: String,
/// [Scope](DOCS_BASE_URL_PLACEHOLDER/secret-operator/scope) of the
/// [SecretClass](DOCS_BASE_URL_PLACEHOLDER/secret-operator/secretclass).
pub scope: Option<SecretClassVolumeScope>,
}
impl SecretClassVolume {
pub fn new(secret_class: String, scope: Option<SecretClassVolumeScope>) -> Self {
Self {
secret_class,
scope,
}
}
pub fn to_ephemeral_volume_source(
&self,
provision_parts: SecretClassVolumeProvisionParts,
) -> Result<EphemeralVolumeSource, SecretClassVolumeError> {
let mut secret_operator_volume_builder =
SecretOperatorVolumeSourceBuilder::new(&self.secret_class, provision_parts);
if let Some(scope) = &self.scope {
if scope.pod {
secret_operator_volume_builder.with_pod_scope();
}
if scope.node {
secret_operator_volume_builder.with_node_scope();
}
for service in &scope.services {
secret_operator_volume_builder.with_service_scope(service);
}
for listener_volume in &scope.listener_volumes {
secret_operator_volume_builder.with_listener_volume_scope(listener_volume);
}
}
secret_operator_volume_builder
.build()
.context(SecretOperatorVolumeSnafu)
}
pub fn to_volume(
&self,
volume_name: &str,
provision_parts: SecretClassVolumeProvisionParts,
) -> Result<Volume, SecretClassVolumeError> {
let ephemeral = self.to_ephemeral_volume_source(provision_parts)?;
Ok(VolumeBuilder::new(volume_name).ephemeral(ephemeral).build())
}
}
#[derive(
Clone, Debug, Deserialize, Eq, Hash, JsonSchema, Ord, PartialEq, PartialOrd, Serialize,
)]
#[serde(rename_all = "camelCase")]
pub struct SecretClassVolumeScope {
/// The pod scope is resolved to the name of the Kubernetes Pod.
/// This allows the secret to differentiate between StatefulSet replicas.
#[serde(default)]
pub pod: bool,
/// The node scope is resolved to the name of the Kubernetes Node object that the Pod is running on.
/// This will typically be the DNS name of the node.
#[serde(default)]
pub node: bool,
/// The service scope allows Pod objects to specify custom scopes.
/// This should typically correspond to Service objects that the Pod participates in.
#[serde(default)]
pub services: Vec<String>,
/// The listener volume scope allows Node and Service scopes to be inferred from the applicable listeners.
/// This must correspond to Volume names in the Pod that mount Listeners.
#[serde(default)]
pub listener_volumes: Vec<String>,
}
/// What parts secret-operator should provision into the requested volume.
//
// There intentionally isn't a global [`Default`] impl, as it's secret-ops concern what it chooses
// as a default.
#[derive(Copy, Clone, Debug, PartialEq, Eq, strum::AsRefStr)]
#[strum(serialize_all = "kebab-case")]
pub enum SecretClassVolumeProvisionParts {
/// Only provision public parts, such as the CA certificate (either as PEM or truststore) or
/// `krb5.conf`.
Public,
/// Provision all parts, which includes all [`Public`](Self::Public) ones as well as additional
/// private parts, such as a TLS cert + private key, a keystore or a keytab.
PublicPrivate,
}
#[cfg(test)]
mod tests {
use std::collections::BTreeMap;
use super::*;
#[test]
fn volume_to_csi_volume_source() {
let secret_class_volume_source = SecretClassVolume {
secret_class: "myclass".to_string(), // pragma: allowlist secret
scope: Some(SecretClassVolumeScope {
pod: true,
node: false,
services: vec!["myservice".to_string()],
listener_volumes: vec!["mylistener".to_string()],
}),
}
// Let's assume we need some form of private data (e.g. a certificate or S3 credentials)
.to_ephemeral_volume_source(SecretClassVolumeProvisionParts::PublicPrivate)
.unwrap();
let expected_volume_attributes = BTreeMap::from([
(
"secrets.stackable.tech/class".to_string(),
"myclass".to_string(),
),
(
"secrets.stackable.tech/scope".to_string(),
"pod,service=myservice,listener-volume=mylistener".to_string(),
),
(
"secrets.stackable.tech/provision-parts".to_string(),
"public-private".to_string(),
),
]);
assert_eq!(
expected_volume_attributes,
secret_class_volume_source
.volume_claim_template
.unwrap()
.metadata
.unwrap()
.annotations
.unwrap()
);
}
}