1
- use futures:: { future, StreamExt } ;
1
+ use futures:: { future, stream , StreamExt } ;
2
2
use k8s_openapi:: api:: {
3
3
apps:: v1:: Deployment ,
4
4
core:: v1:: { ConfigMap , Secret } ,
5
5
} ;
6
6
use kube:: {
7
+ api:: { ApiResource , DynamicObject , GroupVersionKind } ,
8
+ core:: TypedResource ,
7
9
runtime:: {
8
- reflector,
9
- reflector:: { ObjectRef , Store } ,
10
- watcher, WatchStreamExt ,
10
+ reflector:: {
11
+ store:: { CacheWriter , Writer } ,
12
+ ObjectRef , Store ,
13
+ } ,
14
+ watcher:: { self , dynamic_watcher} ,
15
+ WatchStreamExt ,
11
16
} ,
12
- Api , Client ,
17
+ Api , Client , Resource ,
13
18
} ;
19
+ use parking_lot:: RwLock ;
20
+ use serde:: de:: DeserializeOwned ;
14
21
use std:: sync:: Arc ;
15
22
use tracing:: * ;
16
23
17
- // This does not work because Resource trait is not dyn safe.
18
- /*
19
- use std::any::TypeId;
20
24
use std:: collections:: HashMap ;
21
- use k8s_openapi::NamespaceResourceScope;
22
- use kube::api::{Resource, ResourceExt};
23
- struct MultiStore {
24
- stores: HashMap<TypeId, Store<dyn Resource<DynamicType = (), Scope = NamespaceResourceScope>>>,
25
- }
26
- impl MultiStore {
27
- fn get<K: Resource<DynamicType = ()>>(&self, name: &str, ns: &str) -> Option<Arc<K>> {
28
- let oref = ObjectRef::<K>::new(name).within(ns);
29
- if let Some(store) = self.stores.get(&TypeId::of::<K>()) {
30
- store.get(oref)
31
- } else {
32
- None
33
- }
34
- }
35
- }*/
36
25
37
- // explicit store can work
26
+ type Cache = Arc < RwLock < HashMap < GroupVersionKind , Writer < DynamicObject > > > > ;
27
+
28
+ #[ derive( Default , Clone , Copy ) ]
29
+ struct MultiWriter {
30
+ store : Cache ,
31
+ buffer : Cache ,
32
+ }
33
+
34
+ #[ derive( Default , Clone ) ]
38
35
struct MultiStore {
39
- deploys : Store < Deployment > ,
40
- cms : Store < ConfigMap > ,
41
- secs : Store < Secret > ,
36
+ store : Cache ,
42
37
}
43
- // but using generics to help out won't because the K needs to be concretised
44
- /*
45
- impl MultiStore {
46
- fn get<K: Resource<DynamicType = ()>>(&self, name: &str, ns: &str) -> Option<Arc<Option<K>>> {
47
- let oref = ObjectRef::<K>::new(name).within(ns);
48
- let kind = K::kind(&()).to_owned();
49
- match kind.as_ref() {
50
- "Deployment" => self.deploys.get(&ObjectRef::new(name).within(ns)),
51
- "ConfigMap" => self.cms.get(&ObjectRef::new(name).within(ns)),
52
- "Secret" => self.secs.get(&ObjectRef::new(name).within(ns)),
53
- _ => None,
54
- }
55
- None
38
+
39
+ impl MultiWriter {
40
+ fn as_reader ( & self ) -> MultiStore {
41
+ MultiStore { store : self . store }
56
42
}
57
43
}
58
- */
59
- // so left with this
60
44
61
45
impl MultiStore {
62
- fn get_deploy ( & self , name : & str , ns : & str ) -> Option < Arc < Deployment > > {
63
- self . deploys . get ( & ObjectRef :: < Deployment > :: new ( name) . within ( ns) )
46
+ fn get < K : Resource < DynamicType = impl Default > + DeserializeOwned + Clone > (
47
+ & self ,
48
+ name : & str ,
49
+ ns : & str ,
50
+ ) -> Option < Arc < K > > {
51
+ let oref = ObjectRef :: < K > :: new ( name) . within ( ns) . erase ( ) ;
52
+ let store = self . get_store :: < K > ( ) ?;
53
+ let obj = store. get ( & oref) ?. as_ref ( ) . clone ( ) ;
54
+ obj. try_parse ( ) . ok ( ) . map ( Arc :: new)
64
55
}
65
56
66
- fn get_secret ( & self , name : & str , ns : & str ) -> Option < Arc < Secret > > {
67
- self . secs . get ( & ObjectRef :: < Secret > :: new ( name) . within ( ns) )
57
+ fn get_store < K : Resource < DynamicType = impl Default > + DeserializeOwned + Clone > (
58
+ & self ,
59
+ ) -> Option < Store < DynamicObject > > {
60
+ Some ( self . store . read ( ) . get ( & K :: gvk ( & Default :: default ( ) ) ) ?. as_reader ( ) )
68
61
}
62
+ }
69
63
70
- fn get_cm ( & self , name : & str , ns : & str ) -> Option < Arc < ConfigMap > > {
71
- self . cms . get ( & ObjectRef :: < ConfigMap > :: new ( name) . within ( ns) )
64
+ impl CacheWriter < DynamicObject > for MultiWriter {
65
+ /// Applies a single watcher event to the store
66
+ fn apply_watcher_event ( & mut self , event : & watcher:: Event < DynamicObject > ) {
67
+ match event {
68
+ watcher:: Event :: Init | watcher:: Event :: InitDone ( None ) => { }
69
+ watcher:: Event :: Apply ( obj) | watcher:: Event :: Delete ( obj) => {
70
+ let mut stores = self . store . write ( ) ;
71
+ if stores. get ( & obj. gvk ( ) ) . is_none ( ) {
72
+ let store = Writer :: new ( ApiResource :: from_gvk ( & obj. gvk ( ) ) ) ;
73
+ stores. insert ( obj. gvk ( ) , store) ;
74
+ } ;
75
+ if let Some ( store) = stores. get_mut ( & obj. gvk ( ) ) {
76
+ store. apply_watcher_event ( event) ;
77
+ } ;
78
+ }
79
+ watcher:: Event :: InitApply ( obj) => {
80
+ let mut buffer = self . buffer . write ( ) ;
81
+ if buffer. get ( & obj. gvk ( ) ) . is_none ( ) {
82
+ let store = Writer :: new ( ApiResource :: from_gvk ( & obj. gvk ( ) ) ) ;
83
+ buffer. insert ( obj. gvk ( ) , store) ;
84
+ } ;
85
+ if let Some ( store) = buffer. get_mut ( & obj. gvk ( ) ) {
86
+ store. apply_watcher_event ( event) ;
87
+ } ;
88
+ }
89
+ watcher:: Event :: InitDone ( Some ( obj) ) => {
90
+ let mut buffer = self . buffer . write ( ) ;
91
+ if let Some ( mut store) = buffer. remove ( & obj. gvk ( ) ) {
92
+ store. apply_watcher_event ( event) ;
93
+ self . store . write ( ) . insert ( obj. gvk ( ) , store) ;
94
+ }
95
+ }
96
+ }
72
97
}
73
98
}
74
99
@@ -77,60 +102,43 @@ async fn main() -> anyhow::Result<()> {
77
102
tracing_subscriber:: fmt:: init ( ) ;
78
103
let client = Client :: try_default ( ) . await ?;
79
104
80
- let deploys: Api < Deployment > = Api :: default_namespaced ( client. clone ( ) ) ;
81
- let cms: Api < ConfigMap > = Api :: default_namespaced ( client. clone ( ) ) ;
82
- let secret: Api < Secret > = Api :: default_namespaced ( client. clone ( ) ) ;
83
-
84
- let ( dep_reader, dep_writer) = reflector:: store :: < Deployment > ( ) ;
85
- let ( cm_reader, cm_writer) = reflector:: store :: < ConfigMap > ( ) ;
86
- let ( sec_reader, sec_writer) = reflector:: store :: < Secret > ( ) ;
105
+ // multistore
106
+ let combo_stream = stream:: select_all ( vec ! [
107
+ dynamic_watcher( Api :: <Deployment >:: all( client. clone( ) ) , Default :: default ( ) ) . boxed( ) ,
108
+ dynamic_watcher( Api :: <ConfigMap >:: all( client. clone( ) ) , Default :: default ( ) ) . boxed( ) ,
109
+ dynamic_watcher( Api :: <Secret >:: all( client. clone( ) ) , Default :: default ( ) ) . boxed( ) ,
110
+ ] ) ;
87
111
88
- let cfg = watcher:: Config :: default ( ) ;
89
- let dep_watcher = watcher ( deploys, cfg. clone ( ) )
90
- . reflect ( dep_writer)
91
- . applied_objects ( )
92
- . for_each ( |_| future:: ready ( ( ) ) ) ;
93
- let cm_watcher = watcher ( cms, cfg. clone ( ) )
94
- . reflect ( cm_writer)
95
- . applied_objects ( )
96
- . for_each ( |_| future:: ready ( ( ) ) ) ;
97
- let sec_watcher = watcher ( secret, cfg)
98
- . reflect ( sec_writer)
112
+ let multi_writer = MultiWriter :: default ( ) ;
113
+ let watcher = combo_stream
114
+ . reflect ( multi_writer)
99
115
. applied_objects ( )
100
116
. for_each ( |_| future:: ready ( ( ) ) ) ;
101
- // poll these forever
102
-
103
- // multistore
104
- let stores = MultiStore {
105
- deploys : dep_reader,
106
- cms : cm_reader,
107
- secs : sec_reader,
108
- } ;
109
117
110
118
// simulate doing stuff with the stores from some other thread
111
119
tokio:: spawn ( async move {
112
- // Show state every 5 seconds of watching
113
- info ! ( "waiting for them to be ready" ) ;
114
- stores. deploys . wait_until_ready ( ) . await . unwrap ( ) ;
115
- stores. cms . wait_until_ready ( ) . await . unwrap ( ) ;
116
- stores. secs . wait_until_ready ( ) . await . unwrap ( ) ;
117
- info ! ( "stores initialised" ) ;
118
120
// can use helper accessors
119
- info ! (
120
- "common cm: {:?}" ,
121
- stores. get_cm( "kube-root-ca.crt" , "kube-system" ) . unwrap( )
122
- ) ;
123
121
loop {
124
122
tokio:: time:: sleep ( std:: time:: Duration :: from_secs ( 5 ) ) . await ;
123
+ info ! (
124
+ "cache content: {:?}" ,
125
+ multi_writer. as_reader( ) . store. read( ) . keys( )
126
+ ) ;
127
+ info ! (
128
+ "common cm: {:?}" ,
129
+ multi_writer
130
+ . as_reader( )
131
+ . get:: <ConfigMap >( "kube-root-ca.crt" , "kube-system" )
132
+ ) ;
125
133
// access individual sub stores
126
- info ! ( "Current deploys count: {}" , stores. deploys. state( ) . len( ) ) ;
134
+ if let Some ( deploys) = multi_writer. as_reader ( ) . get_store :: < Deployment > ( ) {
135
+ info ! ( "Current deploys count: {}" , deploys. state( ) . len( ) ) ;
136
+ }
127
137
}
128
138
} ) ;
129
- // info!("long watches starting");
139
+ info ! ( "long watches starting" ) ;
130
140
tokio:: select! {
131
- r = dep_watcher => println!( "dep watcher exit: {r:?}" ) ,
132
- r = cm_watcher => println!( "cm watcher exit: {r:?}" ) ,
133
- r = sec_watcher => println!( "sec watcher exit: {r:?}" ) ,
141
+ r = watcher => println!( "watcher exit: {r:?}" ) ,
134
142
}
135
143
136
144
Ok ( ( ) )
0 commit comments