-
-
Notifications
You must be signed in to change notification settings - Fork 18
Expand file tree
/
Copy pathsecret_class.rs
More file actions
168 lines (148 loc) · 5.96 KB
/
Copy pathsecret_class.rs
File metadata and controls
168 lines (148 loc) · 5.96 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
166
167
168
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 of secret material should be provisioned into the requested volume.
//
// There intentionally isn't a global [`Default`] impl, as it is secret-operator's concern what it
// chooses as a default.
// TODO (@Techassi): This to me is a HUGE indicator this lives in the wrong place. All these secret
// volume builders/helpers should be defined as part of a secret-operator library to be as close as
// possible to secret-operator, which is the authoritative source of truth for all of this.
#[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_ephemeral_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()
);
}
}