@@ -3,13 +3,13 @@ use std::string::FromUtf8Error;
3
3
use k8s_openapi:: {
4
4
api:: {
5
5
apps:: v1:: { Deployment , DeploymentCondition , StatefulSet , StatefulSetCondition } ,
6
- core:: v1:: { Secret , Service } ,
6
+ core:: v1:: { Namespace , Secret , Service } ,
7
7
} ,
8
8
apimachinery:: pkg:: apis:: meta:: v1:: Condition ,
9
9
} ;
10
10
use kube:: {
11
- api:: { ListParams , Patch , PatchParams } ,
12
- core:: { DynamicObject , GroupVersionKind , ObjectList , TypeMeta } ,
11
+ api:: { ListParams , Patch , PatchParams , PostParams } ,
12
+ core:: { DynamicObject , GroupVersionKind , ObjectList , ObjectMeta , TypeMeta } ,
13
13
discovery:: Scope ,
14
14
Api , Client , Discovery , ResourceExt ,
15
15
} ;
@@ -22,6 +22,9 @@ use utoipa::ToSchema;
22
22
23
23
use crate :: constants:: REDACTED_PASSWORD ;
24
24
25
+ pub type ListResult < T , E = KubeClientError > = Result < ObjectList < T > , E > ;
26
+ pub type Result < T , E = KubeClientError > = std:: result:: Result < T , E > ;
27
+
25
28
#[ derive( Debug , Snafu ) ]
26
29
pub enum KubeClientError {
27
30
#[ snafu( display( "kube error: {source}" ) ) ]
@@ -60,7 +63,7 @@ pub struct KubeClient {
60
63
impl KubeClient {
61
64
/// Tries to create a new default Kubernetes client and immediately runs
62
65
/// a discovery.
63
- pub async fn new ( ) -> Result < Self , KubeClientError > {
66
+ pub async fn new ( ) -> Result < Self > {
64
67
let client = Client :: try_default ( ) . await . context ( KubeSnafu ) ?;
65
68
let discovery = Discovery :: new ( client. clone ( ) )
66
69
. run ( )
@@ -73,11 +76,7 @@ impl KubeClient {
73
76
/// Deploys manifests defined the in raw `manifests` YAML string. This
74
77
/// method will fail if it is unable to parse the manifests, unable to
75
78
/// resolve GVKs or unable to patch the dynamic objects.
76
- pub async fn deploy_manifests (
77
- & self ,
78
- manifests : & str ,
79
- namespace : & str ,
80
- ) -> Result < ( ) , KubeClientError > {
79
+ pub async fn deploy_manifests ( & self , manifests : & str , namespace : & str ) -> Result < ( ) > {
81
80
for manifest in serde_yaml:: Deserializer :: from_str ( manifests) {
82
81
let mut object = DynamicObject :: deserialize ( manifest) . context ( YamlSnafu ) ?;
83
82
let object_type = object. types . as_ref ( ) . ok_or (
@@ -115,7 +114,7 @@ impl KubeClient {
115
114
Ok ( ( ) )
116
115
}
117
116
118
- /// List objects by looking up a GVK via the discovery. It returns an
117
+ /// Lists objects by looking up a GVK via the discovery. It returns an
119
118
/// optional list of dynamic objects. The method returns [`Ok(None)`]
120
119
/// if the client was unable to resolve the GVK. An error is returned
121
120
/// when the client failed to list the objects.
@@ -146,15 +145,15 @@ impl KubeClient {
146
145
Ok ( Some ( objects) )
147
146
}
148
147
149
- /// List services by matching labels. The services can me matched by the
150
- /// product labels. [`ListParamsExt`] provides a utility function to
148
+ /// Lists [`Service`]s by matching labels. The services can be matched by
149
+ /// the product labels. [`ListParamsExt`] provides a utility function to
151
150
/// create [`ListParams`] based on a product name and optional instance
152
151
/// name.
153
152
pub async fn list_services (
154
153
& self ,
155
154
namespace : Option < & str > ,
156
155
list_params : & ListParams ,
157
- ) -> Result < ObjectList < Service > , KubeClientError > {
156
+ ) -> ListResult < Service > {
158
157
let service_api: Api < Service > = match namespace {
159
158
Some ( namespace) => Api :: namespaced ( self . client . clone ( ) , namespace) ,
160
159
None => Api :: all ( self . client . clone ( ) ) ,
@@ -174,7 +173,7 @@ impl KubeClient {
174
173
secret_namespace : & str ,
175
174
username_key : & str ,
176
175
password_key : Option < & str > ,
177
- ) -> Result < Option < ( String , String ) > , KubeClientError > {
176
+ ) -> Result < Option < ( String , String ) > > {
178
177
let secret_api: Api < Secret > = Api :: namespaced ( self . client . clone ( ) , secret_namespace) ;
179
178
180
179
let secret = secret_api. get ( secret_name) . await . context ( KubeSnafu ) ?;
@@ -200,11 +199,14 @@ impl KubeClient {
200
199
Ok ( Some ( ( username, password) ) )
201
200
}
202
201
202
+ /// Lists [`Deployment`]s by matching labels. The services can be matched
203
+ /// by the app labels. [`ListParamsExt`] provides a utility function to
204
+ /// create [`ListParams`] based on a app name and other labels.
203
205
pub async fn list_deployments (
204
206
& self ,
205
207
namespace : Option < & str > ,
206
208
list_params : & ListParams ,
207
- ) -> Result < ObjectList < Deployment > , KubeClientError > {
209
+ ) -> ListResult < Deployment > {
208
210
let deployment_api: Api < Deployment > = match namespace {
209
211
Some ( namespace) => Api :: namespaced ( self . client . clone ( ) , namespace) ,
210
212
None => Api :: all ( self . client . clone ( ) ) ,
@@ -215,11 +217,14 @@ impl KubeClient {
215
217
Ok ( deployments)
216
218
}
217
219
220
+ /// Lists [`StatefulSet`]s by matching labels. The services can be matched
221
+ /// by the app labels. [`ListParamsExt`] provides a utility function to
222
+ /// create [`ListParams`] based on a app name and other labels.
218
223
pub async fn list_stateful_sets (
219
224
& self ,
220
225
namespace : Option < & str > ,
221
226
list_params : & ListParams ,
222
- ) -> Result < ObjectList < StatefulSet > , KubeClientError > {
227
+ ) -> ListResult < StatefulSet > {
223
228
let stateful_set_api: Api < StatefulSet > = match namespace {
224
229
Some ( namespace) => Api :: namespaced ( self . client . clone ( ) , namespace) ,
225
230
None => Api :: all ( self . client . clone ( ) ) ,
@@ -233,7 +238,48 @@ impl KubeClient {
233
238
Ok ( stateful_sets)
234
239
}
235
240
236
- /// Extracts the GVK from [`TypeMeta`].
241
+ /// Returns a [`Namespace`] identified by name. If this namespace doesn't
242
+ /// exist, this method returns [`None`].
243
+ pub async fn get_namespace ( & self , name : & str ) -> Result < Option < Namespace > > {
244
+ let namespace_api: Api < Namespace > = Api :: all ( self . client . clone ( ) ) ;
245
+ namespace_api. get_opt ( name) . await . context ( KubeSnafu )
246
+ }
247
+
248
+ /// Creates a [`Namespace`] with `name` in the cluster. This method will
249
+ /// return an error if the namespace already exists. Instead of using this
250
+ /// method directly, it is advised to use [`namespace::create_if_needed`][1]
251
+ /// instead.
252
+ ///
253
+ /// [1]: crate::platform::namespace
254
+ pub async fn create_namespace ( & self , name : String ) -> Result < ( ) > {
255
+ let namespace_api: Api < Namespace > = Api :: all ( self . client . clone ( ) ) ;
256
+ namespace_api
257
+ . create (
258
+ & PostParams :: default ( ) ,
259
+ & Namespace {
260
+ metadata : ObjectMeta {
261
+ name : Some ( name) ,
262
+ ..Default :: default ( )
263
+ } ,
264
+ ..Default :: default ( )
265
+ } ,
266
+ )
267
+ . await
268
+ . context ( KubeSnafu ) ?;
269
+
270
+ Ok ( ( ) )
271
+ }
272
+
273
+ /// Creates a [`Namespace`] only if not already present in the current cluster.
274
+ pub async fn create_namespace_if_needed ( & self , name : String ) -> Result < ( ) > {
275
+ if self . get_namespace ( & name) . await ?. is_none ( ) {
276
+ self . create_namespace ( name) . await ?
277
+ }
278
+
279
+ Ok ( ( ) )
280
+ }
281
+
282
+ /// Extracts the [`GroupVersionKind`] from [`TypeMeta`].
237
283
fn gvk_of_typemeta ( type_meta : & TypeMeta ) -> GroupVersionKind {
238
284
match type_meta. api_version . split_once ( '/' ) {
239
285
Some ( ( group, version) ) => GroupVersionKind :: gvk ( group, version, & type_meta. kind ) ,
0 commit comments