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.
- checksums.yaml +4 -4
- data/lib/kubernetes-operator.rb +138 -77
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c3f00225b351939f42585587df92d9bd3e5425ded2a8c260f58171a39216bb77
|
4
|
+
data.tar.gz: 6eb6bf84877a2aabe98f6df1ebbe7a549822cf17cf1dfcc65d8813187d29202b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: af548e2e4d2873aa1841cabf4c9c3021341f81a7e5fdb6f63b77fedd6a8dd5f917740dbf416346a31b722ee521084c0e1e8405cd9fee91c81f91a674004d27aa
|
7
|
+
data.tar.gz: 692bf36c849fc4b839376d1d9a03b80059e49441de9f4f9a81890950aab0d1d286d19a29b132df075ca4617c2a8566e557044b99c7a4fa0240b44799359ee524
|
data/lib/kubernetes-operator.rb
CHANGED
@@ -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
|
-
#
|
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
|
-
#
|
96
|
-
|
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
|
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
|
-
|
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
|
-
#
|
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
|
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-
|
11
|
+
date: 2020-03-31 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: kubeclient
|