kubernetes-operator 0.0.5 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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