kubernetes-operator 0.0.5 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (3) hide show
  1. checksums.yaml +4 -4
  2. data/lib/kubernetes-operator.rb +138 -77
  3. metadata +2 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f594436b606a9874d099009d5428952569792547c090199b08553b5bac9828bf
4
- data.tar.gz: cf8f0c651177e18aa07b682cb05c4b0c716908151e72dcadbae2edb9c4ee4139
3
+ metadata.gz: c3f00225b351939f42585587df92d9bd3e5425ded2a8c260f58171a39216bb77
4
+ data.tar.gz: 6eb6bf84877a2aabe98f6df1ebbe7a549822cf17cf1dfcc65d8813187d29202b
5
5
  SHA512:
6
- metadata.gz: 04221fa717adb6c04562554236980e56a01f8ea90c491f33cabe6df62b99935739cc63ecbc03cd1c7db509cadb857bf5ed96dc8ddc4654d407d15e1f8a774385
7
- data.tar.gz: a6564a4cbf18e78ee6a7dee2589c1db1862763b61133c3ba7f14f63f636a18847425acf09767f33459313b29ea731a98d32c5d49ce9b9b9ed07223f049e05717
6
+ metadata.gz: af548e2e4d2873aa1841cabf4c9c3021341f81a7e5fdb6f63b77fedd6a8dd5f917740dbf416346a31b722ee521084c0e1e8405cd9fee91c81f91a674004d27aa
7
+ data.tar.gz: 692bf36c849fc4b839376d1d9a03b80059e49441de9f4f9a81890950aab0d1d286d19a29b132df075ca4617c2a8566e557044b99c7a4fa0240b44799359ee524
@@ -13,8 +13,111 @@ require 'log_formatter'
13
13
  require 'log_formatter/log4r_json_formatter'
14
14
  require 'json'
15
15
 
16
+ class EventHelper
17
+
18
+ # EventHelper initialize provides an helper class to add and delete events from ressources
19
+ # @see https://www.bluematador.com/blog/kubernetes-events-explained Documentation
20
+ # @param logger [Log4r::Logger] Logger for this class
21
+ def initialize(logger)
22
+ # logging
23
+ @logger = logger
24
+
25
+ # kubeconfig
26
+ # we need our own client because its an different api path
27
+ # (for local development it's nice to use .kube/config)
28
+ if File.exist?("#{Dir.home}/.kube/config")
29
+ config = Kubeclient::Config.read(ENV['KUBECONFIG'] || "#{ENV['HOME']}/.kube/config")
30
+ context = config.context
31
+ @k8sclient = Kubeclient::Client.new(
32
+ context.api_endpoint,
33
+ 'v1',
34
+ ssl_options: context.ssl_options,
35
+ auth_options: context.auth_options
36
+ )
37
+ else
38
+ auth_options = {
39
+ bearer_token_file: '/var/run/secrets/kubernetes.io/serviceaccount/token'
40
+ }
41
+ ssl_options = {}
42
+ if File.exist?("/var/run/secrets/kubernetes.io/serviceaccount/ca.crt")
43
+ ssl_options[:ca_file] = "/var/run/secrets/kubernetes.io/serviceaccount/ca.crt"
44
+ end
45
+ @k8sclient = Kubeclient::Client.new(
46
+ 'https://kubernetes.default.svc',
47
+ 'v1',
48
+ auth_options: auth_options,
49
+ ssl_options: ssl_options
50
+ )
51
+ end
52
+ end
53
+
54
+ # Add an event
55
+ # @param obj [Hash] Kubernetes custom resource object as hash (required)
56
+ # @param message [string] Event message (required)
57
+ # @param reason [string] Event reason (default: "Upsert")
58
+ # @param type [string] Event type (default: "Normal")
59
+ # @param component [string] Event component (default: "KubernetesOperator")
60
+ def add(obj,message,reason = "Upsert",type = "Normal", component = "KubernetesOperator")
61
+ begin
62
+ event = Kubeclient::Resource.new
63
+ time = Time.new.utc
64
+
65
+ _tmpNS = obj[:metadata][:namespace]
66
+
67
+ event.firstTimestamp = time.strftime("%Y-%m-%dT%H:%M:%SZ")
68
+ event.lastTimestamp = time.strftime("%Y-%m-%dT%H:%M:%SZ")
69
+ event.involvedObject = {}
70
+ event.involvedObject.apiVersion = obj[:apiVersion]
71
+ event.involvedObject.kind = obj[:kind]
72
+ event.involvedObject.name = obj[:metadata][:name]
73
+ event.involvedObject.namespace = obj[:metadata][:namespace]
74
+ event.involvedObject.resourceVersion = obj[:metadata][:resourceVersion]
75
+ event.involvedObject.uid = obj[:metadata][:uid]
76
+ event.kind = "Event"
77
+ event.message = message
78
+ event.metadata = {}
79
+ event.metadata.name = "#{obj[:metadata][:name]}.#{time.to_i}"
80
+ event.metadata.namespace = obj[:metadata][:namespace] ||= "default"
81
+ event.reason = reason
82
+ event.source = {}
83
+ event.source.component = component
84
+ event.type = type
85
+
86
+ @k8sclient.create_event(event)
87
+
88
+ @logger.info("add event #{message}(#{type}) to #{obj[:metadata][:name]}(#{obj[:metadata][:uid]})")
89
+ rescue => exception
90
+ @logger.error(exception.inspect)
91
+ end
92
+
93
+ end
94
+
95
+ # Delete all events from an cr
96
+ # @param obj [Hash] Kubernetes custom resource object as hash (required)
97
+ def deleteAll(obj)
98
+ begin
99
+ events = @k8sclient.get_events(namespace: obj[:metadata][:namespace],field_selector: "involvedObject.uid=#{obj[:metadata][:uid]}")
100
+ events.each do |event|
101
+ @logger.info("delete event #{event[:metadata][:name]}(#{event[:metadata][:namespace]}) from #{obj[:metadata][:name]}(#{obj[:metadata][:uid]})")
102
+ @k8sclient.delete_event(event[:metadata][:name],event[:metadata][:namespace])
103
+ end
104
+ rescue => exception
105
+ @logger.error(exception.inspect)
106
+ end
107
+ end
108
+ end
109
+
16
110
  class KubernetesOperator
17
111
 
112
+ # Operator Class to run the core operator functions for your crd
113
+ # @see https://kubernetes.io/docs/tasks/access-kubernetes-api/custom-resources/custom-resource-definition-versioning/ Documentation
114
+ # @param group [string] Group from crd
115
+ # @param version [string] Api Version from crd
116
+ # @param plural [string] Name (plural) from crd
117
+ # @param options [Hash] Additional options
118
+ # @option options [Hash] sleepTimer Time to wait for retry if the watch event stops
119
+ # @option options [Hash] namespace Watch only an namespace, default watch all namespaces
120
+ # @option options [Hash] persistence_location Location for the yaml store, default is /tmp/persistence
18
121
  def initialize(crdGroup, crdVersion, crdPlural, options = {} )
19
122
  # parameter
20
123
  @crdGroup = crdGroup
@@ -67,34 +170,55 @@ class KubernetesOperator
67
170
  ssl_options: ssl_options
68
171
  )
69
172
  end
173
+
174
+ # event helper
175
+ @eventHelper = EventHelper.new(@logger)
70
176
  end
71
177
 
72
178
  # Action Methods
179
+
180
+ # Set the method that was triggert then an add cr event occurred
181
+ # @param callback [methode] Ruby methode
73
182
  def setAddMethod(callback)
74
183
  @addMethod = callback
75
184
  end
76
185
 
186
+ # Set the method that was triggert then an update cr event occurred
187
+ # @param callback [methode] Ruby methode
77
188
  def setUpdateMethod(callback)
78
189
  @updateMethod = callback
79
190
  end
80
191
 
192
+ # Set the method that was triggert then an add or update cr event occurred
193
+ # This function overwrite setAddMethod and setUpdateMethod
194
+ # @param callback [methode] Ruby methode
81
195
  def setUpsertMethod(callback)
82
196
  @updateMethod = callback
83
197
  @addMethod = callback
84
198
  end
85
199
 
200
+ # Set the method that was triggert then an delete cr event occurred
201
+ # @param callback [methode] Ruby methode
86
202
  def setDeleteMethod(callback)
87
203
  @deleteMethod = callback
88
204
  end
89
205
 
90
- # Logger Methods
206
+ # Helper Methods
207
+
208
+ # get the logger from the operatore framework
209
+ # @return [Log4r::Logger] logger
91
210
  def getLogger()
92
211
  return @logger
93
212
  end
94
213
 
95
- # Controller
96
- def run
214
+ # get the event helper from the operatore framework
215
+ # @return [EventHelper] event helper
216
+ def getEventHelper()
217
+ return @eventHelper
218
+ end
97
219
 
220
+ # start the operator to watch your cr
221
+ def run
98
222
  @logger.info("start the operator")
99
223
  # load methods
100
224
  @addMethod = method(:defaultActionMethod) unless @addMethod
@@ -102,7 +226,6 @@ class KubernetesOperator
102
226
  @deleteMethod = method(:defaultActionMethod) unless @deleteMethod
103
227
 
104
228
  while true
105
-
106
229
  begin
107
230
  if @options[:namespace]
108
231
  watcher = @k8sclient.watch_entities(@crdPlural,@options[:namespace])
@@ -117,9 +240,6 @@ class KubernetesOperator
117
240
  when "ADDED"
118
241
  # check if version is already processed
119
242
  unless isCached
120
- # add finalizer
121
- @logger.info("add finalizer to #{notice[:object][:metadata][:name]} (#{notice[:object][:metadata][:uid]})")
122
- patched = @k8sclient.patch_entity(@crdPlural,notice[:object][:metadata][:name], {metadata: {finalizers: ["#{@crdPlural}.#{@crdVersion}.#{@crdGroup}"]}},'merge-patch',@options[:namespace])
123
243
  # trigger action
124
244
  @logger.info("trigger add action for #{notice[:object][:metadata][:name]} (#{notice[:object][:metadata][:uid]})")
125
245
  resp = @addMethod.call(notice[:object],@k8sclient)
@@ -127,6 +247,9 @@ class KubernetesOperator
127
247
  if resp[:status]
128
248
  @k8sclient.patch_entity(@crdPlural,notice[:object][:metadata][:name]+"/status", {status: resp[:status]},'merge-patch',@options[:namespace])
129
249
  end
250
+ # add finalizer
251
+ @logger.info("add finalizer to #{notice[:object][:metadata][:name]} (#{notice[:object][:metadata][:uid]})")
252
+ patched = @k8sclient.patch_entity(@crdPlural,notice[:object][:metadata][:name], {metadata: {finalizers: ["#{@crdPlural}.#{@crdVersion}.#{@crdGroup}"]}},'merge-patch',@options[:namespace])
130
253
  # save version
131
254
  @store.transaction do
132
255
  @store[patched[:metadata][:uid]] = patched[:metadata][:resourceVersion]
@@ -138,7 +261,7 @@ class KubernetesOperator
138
261
  # cr was change or deleted (if finalizer is set, it an modified call, not an delete)
139
262
  when "MODIFIED"
140
263
  # check if version is already processed
141
- if isCached != notice[:object][:metadata][:resourceVersion]
264
+ if isCached.to_i < notice[:object][:metadata][:resourceVersion].to_i
142
265
  # check if it's an delete event
143
266
  unless notice[:object][:metadata][:deletionTimestamp]
144
267
  # trigger action
@@ -165,94 +288,32 @@ class KubernetesOperator
165
288
  @logger.info("skip update action for #{notice[:object][:metadata][:name]} (#{notice[:object][:metadata][:uid]}), found version in cache")
166
289
  end
167
290
  when "DELETED"
168
- @logger.info("#{notice[:object][:metadata][:name]} (#{notice[:object][:metadata][:uid]}) is done")
291
+ # clear events, if delete was successfuly
292
+ @logger.info("#{notice[:object][:metadata][:name]} (#{notice[:object][:metadata][:uid]}) is done, clean up events")
293
+ @eventHelper.deleteAll(notice[:object])
169
294
  else
295
+ # upsi, something wrong
170
296
  @logger.info("strange things are going on here, I found the type "+notice[:type])
171
297
  end
172
298
  rescue => exception
173
299
  @logger.error(exception.inspect)
174
300
  end
175
301
  end
302
+ # watcher is done, lost connection ...
176
303
  watcher.finish
177
-
178
- ## Search for ressources
179
- #_ressources = @k8sclient.get(@crdGroup+"/"+@crdVersion).resource(@crdPlural).list()
180
-
181
- #_ressources.each do |_i|
182
- # _uid = _i["metadata"]["uid"]
183
- # _v = _i["metadata"]["resourceVersion"]
184
-
185
- # if @options[:namespace] == nil || @options[:namespace].contains(_i["metadata"]["namespace"])
186
- # _from_cache = @store.transaction{@store[_uid]}
187
-
188
- # unless _from_cache
189
- # # Add finalizer and refresh version number
190
- # _i[:metadata][:finalizers] = ["#{@crdPlural}.#{@crdVersion}.#{@crdGroup}"]
191
- # _i = @k8sclient.api(@crdGroup+"/"+@crdVersion).resource(@crdPlural).update_resource(_i)
192
-
193
- # # call the action method
194
- # _i["metadata"]["crd_status"] = "add"
195
- # @logger.info("add custom resource #{_i["metadata"]["name"]}@#{_i["metadata"]["namespace"]}") if _i["metadata"]["namespace"]
196
- # @logger.info("add custom resource #{_i["metadata"]["name"]}@cluster") unless _i["metadata"]["namespace"]
197
- # @addMethod.call(_i,@k8sclient)
198
-
199
- # # update status
200
- # _i[:status] = {message: "Test"}
201
- # _i = @k8sclient.api(@crdGroup+"/"+@crdVersion).resource(@crdPlural).update_resource(_i)
202
-
203
- # # Cache last version of ressources
204
- # @store.transaction do
205
- # @store[_uid] = _i["metadata"]["resourceVersion"]
206
- # @store.commit
207
- # end
208
-
209
- # else
210
- # # only trigger action on change or delete event
211
- # unless _from_cache == _v
212
- # if _i["metadata"]["deletionTimestamp"]
213
- # # remove finalizers
214
- # _i[:metadata][:finalizers] = []
215
- # @k8sclient.api(@crdGroup+"/"+@crdVersion).resource(@crdPlural).update_resource(_i)
216
-
217
- # # call the action method
218
- # _i["metadata"]["crd_status"] = "delete"
219
- # @logger.info("delete custom resource #{_i["metadata"]["name"]}@#{_i["metadata"]["namespace"]}") if _i["metadata"]["namespace"]
220
- # @logger.info("delete custom resource #{_i["metadata"]["name"]}@cluster") unless _i["metadata"]["namespace"]
221
- # @deleteMethod.call(_i,@k8sclient)
222
- # else
223
- # # store new version in cache
224
- # @store.transaction do
225
- # @store[_uid] = _v
226
- # @store.commit
227
- # end
228
-
229
- # # call the action method
230
- # _i["metadata"]["crd_status"] = "update"
231
- # @logger.info("update custom resource #{_i["metadata"]["name"]}@#{_i["metadata"]["namespace"]}") if _i["metadata"]["namespace"]
232
- # @logger.info("update custom resource #{_i["metadata"]["name"]}@cluster") unless _i["metadata"]["namespace"]
233
- # @updateMethod.call(_i,@k8sclient)
234
- # end
235
- # end
236
- # end
237
- # end
238
- #end
239
-
240
304
  rescue => exception
241
305
  @logger.error(exception.inspect)
242
306
  end
243
307
 
244
- # Done
308
+ # done
245
309
  sleep @options[:sleepTimer]
246
310
 
247
311
  end
248
312
  end
249
313
 
314
+ # default methode, only an esay print command
250
315
  private
251
316
  def defaultActionMethod(obj,k8sclient)
252
317
  puts "{leve: \"info\", action: \"#{obj["metadata"]["crd_status"]}\", ressource: \"#{obj["metadata"]["namespace"]}/#{obj["metadata"]["name"]}\"}"
253
318
  end
254
319
  end
255
-
256
-
257
-
258
-
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: kubernetes-operator
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.5
4
+ version: 0.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tobias Kuntzsch
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-03-29 00:00:00.000000000 Z
11
+ date: 2020-03-31 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: kubeclient