-
Notifications
You must be signed in to change notification settings - Fork 11
/
crd.rs
150 lines (132 loc) · 4.58 KB
/
crd.rs
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
use std::marker::PhantomData;
use derivative::Derivative;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use crate::error::{Error, OperatorResult};
use crate::yaml;
use std::fs::File;
use std::io::Write;
use std::path::Path;
/// A reference to a product cluster (for example, a `ZookeeperCluster`)
///
/// `namespace`'s defaulting only applies when retrieved via [`ClusterRef::namespace_relative_from`]
#[derive(Deserialize, Serialize, JsonSchema, Derivative)]
#[derivative(
Default(bound = ""),
Clone(bound = ""),
Debug(bound = ""),
PartialEq(bound = "")
)]
pub struct ClusterRef<K> {
/// The name of the cluster
pub name: Option<String>,
/// The namespace of the cluster
///
/// This field is optional, and will default to the namespace of the referring object.
#[serde(default)]
pub namespace: Option<String>,
#[serde(skip)]
_kind: PhantomData<K>,
}
impl<K: kube::Resource> ClusterRef<K> {
pub fn to_named(name: &str, namespace: Option<&str>) -> Self {
Self {
name: Some(name.into()),
namespace: namespace.map(|ns| ns.into()),
_kind: PhantomData,
}
}
pub fn to_object(obj: &K) -> Self {
Self {
name: obj.meta().name.clone(),
namespace: obj.meta().namespace.clone(),
_kind: PhantomData,
}
}
pub fn namespace_relative_from<'a, K2: kube::Resource>(
&'a self,
container: &'a K2,
) -> Option<&'a str> {
self.namespace
.as_deref()
.or_else(|| container.meta().namespace.as_deref())
}
}
/// Retrieve the custom resource name (e.g. simple-test-cluster).
pub trait HasInstance {
fn get_instance_name(&self) -> &str;
}
/// Retrieve the application name (e.g. spark, zookeeper).
pub trait HasApplication {
fn get_application_name() -> &'static str;
}
/// This trait can be implemented to allow automatic handling
/// (e.g. creation) of `CustomResourceDefinition`s in Kubernetes.
pub trait CustomResourceExt: kube::CustomResourceExt {
/// Generates a YAML CustomResourceDefinition and writes it to a `Write`.
///
/// The generated YAML string is an explicit document with leading dashes (`---`).
fn generate_yaml_schema<W>(mut writer: W) -> OperatorResult<()>
where
W: Write,
{
let schema = yaml::to_explicit_document_string(&Self::crd())?;
writer.write_all(schema.as_bytes())?;
Ok(())
}
/// Generates a YAML CustomResourceDefinition and writes it to the specified file.
///
/// The written YAML string is an explicit document with leading dashes (`---`).
fn write_yaml_schema<P: AsRef<Path>>(path: P) -> OperatorResult<()> {
let writer = File::create(path)?;
Self::generate_yaml_schema(writer)
}
/// Generates a YAML CustomResourceDefinition and prints it to stdout.
///
/// The printed YAML string is an explicit document with leading dashes (`---`).
fn print_yaml_schema() -> OperatorResult<()> {
let writer = std::io::stdout();
Self::generate_yaml_schema(writer)
}
// Returns the YAML schema of this CustomResourceDefinition as a string.
///
/// The written YAML string is an explicit document with leading dashes (`---`).
fn yaml_schema() -> OperatorResult<String> {
let mut writer = Vec::new();
Self::generate_yaml_schema(&mut writer)?;
String::from_utf8(writer).map_err(Error::CrdFromUtf8Error)
}
}
impl<T> CustomResourceExt for T where T: kube::CustomResourceExt {}
#[cfg(test)]
mod tests {
use k8s_openapi::api::core::v1::ConfigMap;
use kube::core::ObjectMeta;
use super::ClusterRef;
#[test]
fn cluster_ref_should_default_namespace() {
let relative_ref = ClusterRef::<ConfigMap>::to_named("foo", None);
let absolute_ref = ClusterRef::<ConfigMap>::to_named("foo", Some("bar"));
let nsless_obj = ConfigMap::default();
let namespaced_obj = ConfigMap {
metadata: ObjectMeta {
namespace: Some("baz".to_string()),
..ObjectMeta::default()
},
..ConfigMap::default()
};
assert_eq!(relative_ref.namespace_relative_from(&nsless_obj), None);
assert_eq!(
absolute_ref.namespace_relative_from(&nsless_obj),
Some("bar")
);
assert_eq!(
relative_ref.namespace_relative_from(&namespaced_obj),
Some("baz")
);
assert_eq!(
absolute_ref.namespace_relative_from(&namespaced_obj),
Some("bar")
);
}
}