persistence-providers 0.0.2
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 +7 -0
- data/Gemfile +2 -0
- data/lib/crd_client.rb +49 -0
- data/lib/persistence_providers.rb +9 -0
- data/lib/state/component.rb +73 -0
- data/lib/state/component/attribute.rb +88 -0
- data/lib/state/component/providers/influxdb.rb +42 -0
- data/lib/state/component/providers/influxdb/client.rb +59 -0
- data/lib/state/component/providers/influxdb/measurement.rb +94 -0
- data/lib/state/component/providers/influxdb/measurement/attribute_measurement.rb +21 -0
- data/lib/state/component/providers/kube_crd.rb +11 -0
- data/lib/state/component_def.rb +21 -0
- data/lib/state/component_def/attribute_type_info.rb +23 -0
- data/lib/state/crd_component.rb +64 -0
- data/lib/state/executable_action.rb +30 -0
- data/persistence-providers.gemspec +19 -0
- data/test-destroy-influxdb.rb +9 -0
- data/test-influxdb.rb +8 -0
- data/test.rb +5 -0
- metadata +102 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: a953df0805cd56776854b2ecc2536bd65168a09134cac2a9e19370800e2d08bf
|
|
4
|
+
data.tar.gz: cd8693b064bcc866ebdbbe870b258da63c4cf26ead0a844d7c6481b63f64dd37
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 6a13cae6f9c26a52e2408e5c7f630ea85a2aab48da505b8ff35a89966a3004103ecf815bd37e618f61532e758f5576359e1a8074fe29be0010ed7e0c0a1a307b
|
|
7
|
+
data.tar.gz: 9059eeec1a431c6460e820afa1be83ee5a365b8df82980c528dd0397ca716d96571b4d92d0d0dd486ba4a39d3d12eda5763546929cd7341e73c5fa10ca0aee9f
|
data/Gemfile
ADDED
data/lib/crd_client.rb
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
require 'kubeclient'
|
|
2
|
+
require 'celluloid/io'
|
|
3
|
+
require 'singleton'
|
|
4
|
+
|
|
5
|
+
module DTK
|
|
6
|
+
class CrdClient
|
|
7
|
+
include Singleton
|
|
8
|
+
|
|
9
|
+
attr_accessor :kubeclient
|
|
10
|
+
|
|
11
|
+
def self.get_kubeclient(opts)
|
|
12
|
+
if @kubeclient = opts[:kubeclient]
|
|
13
|
+
@kubeclient
|
|
14
|
+
else
|
|
15
|
+
::DTK::CrdClient.instance.kubeclient
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# opts can have keys
|
|
20
|
+
# kubernetes_client - already instantiated kubernetes client
|
|
21
|
+
def initialize(opts = {})
|
|
22
|
+
if @kubeclient = opts[:kubernetes_client]
|
|
23
|
+
return @kubeclient
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
ssl_options = {}
|
|
27
|
+
auth_options = { bearer_token_file: '/var/run/secrets/kubernetes.io/serviceaccount/token' }
|
|
28
|
+
|
|
29
|
+
if File.exist?("/var/run/secrets/kubernetes.io/serviceaccount/ca.crt")
|
|
30
|
+
ssl_options[:ca_file] = "/var/run/secrets/kubernetes.io/serviceaccount/ca.crt"
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
socket_options = {
|
|
34
|
+
socket_class: Celluloid::IO::TCPSocket,
|
|
35
|
+
ssl_socket_class: Celluloid::IO::SSLSocket
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
@kubeclient = Kubeclient::Client.new(
|
|
39
|
+
'https://kubernetes.default.svc/apis/',
|
|
40
|
+
'dtk.io/v1alpha1',
|
|
41
|
+
auth_options: auth_options,
|
|
42
|
+
ssl_options: ssl_options,
|
|
43
|
+
socket_options: socket_options
|
|
44
|
+
)
|
|
45
|
+
@kubeclient.discover unless @kubeclient.discovered
|
|
46
|
+
@kubeclient
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
module DTK::State
|
|
2
|
+
class Component
|
|
3
|
+
require_relative 'component/attribute'
|
|
4
|
+
|
|
5
|
+
attr_reader :name, :parent, :module_refs, :component_def
|
|
6
|
+
|
|
7
|
+
def initialize(name, component_content, parent, opts = {})
|
|
8
|
+
@name = name
|
|
9
|
+
# @parent = parent
|
|
10
|
+
@module_refs = parent.module_refs
|
|
11
|
+
@component_def = get_component_def(opts)
|
|
12
|
+
|
|
13
|
+
@attribute_objs = Attribute.create_from_kube_hash(component_content[:attributes])# convert_to_attribute_objects(component_content[:attributes])
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# opts can have keys
|
|
17
|
+
# task_id
|
|
18
|
+
def self.get(namespace, crd_component_name, component_name, opts = {})
|
|
19
|
+
crd_component = CrdComponent.get(namespace, crd_component_name, opts)
|
|
20
|
+
if matching_component = crd_component.components.find{ |cmp| cmp.to_hash.keys.first.to_s == component_name }
|
|
21
|
+
Component.new(component_name, matching_component[component_name], crd_component, opts)
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# opts can have keys
|
|
26
|
+
# task_id
|
|
27
|
+
# format:
|
|
28
|
+
# hash
|
|
29
|
+
# kubernetes - return attributes in raw format from kubeclient
|
|
30
|
+
def self.get_attributes(namespace, crd_component_name, component_name, opts = {})
|
|
31
|
+
component = get(namespace, crd_component_name, component_name, opts)
|
|
32
|
+
component.attributes(opts)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def attributes(opts = {})
|
|
36
|
+
format = opts[:format]
|
|
37
|
+
|
|
38
|
+
return @attribute_objs unless format
|
|
39
|
+
|
|
40
|
+
if format.to_s == 'hash'
|
|
41
|
+
@attribute_objs.map{ |attr| attr.to_hash }
|
|
42
|
+
else
|
|
43
|
+
fail Error.new "Unsupported format '#{format}'"
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def attribute_values
|
|
48
|
+
attribute_with_values = []
|
|
49
|
+
@content[:attributes].each do |name, content|
|
|
50
|
+
attribute_with_values << { name => content[:value] }
|
|
51
|
+
end
|
|
52
|
+
attribute_with_values
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
private
|
|
56
|
+
|
|
57
|
+
def get_component_def(opts = {})
|
|
58
|
+
destructured_component = destructure_component_full_name
|
|
59
|
+
module_ref = @module_refs.find { |module_ref| module_ref[:name] == destructured_component[:module_name] }
|
|
60
|
+
ComponentDef.get(module_ref[:namespace], destructured_component[:component_def_name], opts)
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def destructure_component_full_name
|
|
64
|
+
regex = /(.*)::(.*)\[(.*)\]/
|
|
65
|
+
module_name, component_def_name, component_name = @name.match(regex).captures
|
|
66
|
+
{
|
|
67
|
+
module_name: module_name,
|
|
68
|
+
component_def_name: "#{module_name}-#{component_def_name}",
|
|
69
|
+
component_name: component_name
|
|
70
|
+
}
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
module DTK::State
|
|
2
|
+
class Component
|
|
3
|
+
class Attribute
|
|
4
|
+
require_relative 'providers/influxdb'
|
|
5
|
+
require_relative 'providers/kube_crd'
|
|
6
|
+
|
|
7
|
+
attr_reader :name, :parent, :type, :function, :value, :required, :dynamic, :encrypted
|
|
8
|
+
|
|
9
|
+
def initialize(name, attribute_content, parent)
|
|
10
|
+
@name = name
|
|
11
|
+
# @attribute_content = attribute_content
|
|
12
|
+
# @parent = parent
|
|
13
|
+
|
|
14
|
+
# TODO for backward compatibility with remote executors all attribute content is considered as value
|
|
15
|
+
# we should change this to use proper parts of attribute as specified below
|
|
16
|
+
@value = attribute_content
|
|
17
|
+
|
|
18
|
+
# workaround for cases where we have attribute set as attribut_name: value
|
|
19
|
+
# if attribute_content.is_a?(String)
|
|
20
|
+
# attribute_content = {
|
|
21
|
+
# value: attribute_content
|
|
22
|
+
# }
|
|
23
|
+
# end
|
|
24
|
+
|
|
25
|
+
# @type = attribute_content[:type]
|
|
26
|
+
# @function = attribute_content[:function]
|
|
27
|
+
# @value = attribute_content[:value]
|
|
28
|
+
# @temporal = attribute_content[:temporal]
|
|
29
|
+
# @required = attribute_content[:required] || false
|
|
30
|
+
# @dynamic = attribute_content[:dynamic] || false
|
|
31
|
+
# @encrypted = attribute_content[:encrypted] || false
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def self.get(namespace, crd_component_name, component_instance_name, attribute_name, opts = {})
|
|
35
|
+
# getting attribute here because we can reuse it if it's stored in crd
|
|
36
|
+
# when we decide to not store attribute value as part of component_instance crd we can overwrite this
|
|
37
|
+
component_instance = Component.get(namespace, crd_component_name, component_instance_name, opts)
|
|
38
|
+
attribute_content = component_instance.attributes.find{ |attribute| attribute.name == attribute_name }
|
|
39
|
+
|
|
40
|
+
fail Error.new("Unable to find attribute '#{attribute_name}' in provided component instance") if attribute_content.nil? || attribute_content.empty?
|
|
41
|
+
|
|
42
|
+
attribute = self.new(attribute_name, attribute_content, component_instance)
|
|
43
|
+
provider_class = provider_class_name(attribute, opts)
|
|
44
|
+
# return already fetched attribute value if provider is KubeCrd
|
|
45
|
+
return attribute if provider_class == 'KubeCrd'
|
|
46
|
+
|
|
47
|
+
# for KubeCrd this will do the same work we did in provider_class_name method to find temporal param
|
|
48
|
+
self.class.const_get(provider_class).get(component_instance_name, attribute_name, opts)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def to_hash
|
|
52
|
+
{
|
|
53
|
+
@name => {
|
|
54
|
+
# type: @type,
|
|
55
|
+
# function: @function,
|
|
56
|
+
value: @value,
|
|
57
|
+
# temporal: @temporal,
|
|
58
|
+
# required: @required,
|
|
59
|
+
# dynamic: @dynamic,
|
|
60
|
+
# encrypted: @encrypted
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def self.create_from_kube_hash(kube_attributes)
|
|
66
|
+
kube_attributes.to_hash.map do |attribute_name, attribute_content|
|
|
67
|
+
Attribute.new(attribute_name, attribute_content, nil)
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def self.create_from_kube_array(kube_attributes)
|
|
72
|
+
kube_attributes.map do |attribute_content|
|
|
73
|
+
Attribute.new(attribute_content[:name], attribute_content, nil)
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
private
|
|
78
|
+
|
|
79
|
+
def provider_class_name(attribute, opts = {})
|
|
80
|
+
if attribute.temporal
|
|
81
|
+
'Influxdb'
|
|
82
|
+
else
|
|
83
|
+
'KubeCrd'
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
end
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
module DTK::State
|
|
2
|
+
class Component::Attribute
|
|
3
|
+
class Influxdb < self
|
|
4
|
+
require_relative('influxdb/client')
|
|
5
|
+
require_relative('influxdb/measurement')
|
|
6
|
+
|
|
7
|
+
attr_reader :client, :measurement
|
|
8
|
+
|
|
9
|
+
def initialize
|
|
10
|
+
@client = Influxdb::Client.new('dtk')
|
|
11
|
+
@measurement = @client.measurement_helper(:attributes)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def get(namespace, crd_component_name, component_instance_name, attribute_name, opts = {})
|
|
15
|
+
required_tags = get_required_tags(namespace, crd_component_name, component_instance_name, attribute_name)
|
|
16
|
+
last_point = measurement.get_last_point(required_tags)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def write(namespace, crd_component_name, component_instance_name, attribute_name, value, opts = {}, timestamp = nil)
|
|
20
|
+
begin
|
|
21
|
+
fail "Bad timestamp input, write operation wont be completed" if timestamp > Time.new
|
|
22
|
+
required_tags = get_required_tags(namespace, crd_component_name, component_instance_name, attribute_name)
|
|
23
|
+
measurement.write(value, required_tags, timestamp)
|
|
24
|
+
rescue => error
|
|
25
|
+
puts error
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
private
|
|
30
|
+
|
|
31
|
+
def get_required_tags(namespace, crd_component_name, component_instance_name, attribute_name)
|
|
32
|
+
required_tags = {
|
|
33
|
+
namespace: namespace,
|
|
34
|
+
crd_component_name: crd_component_name,
|
|
35
|
+
component_instance_name: component_instance_name,
|
|
36
|
+
attribute_name: attribute_name,
|
|
37
|
+
task_id: 1
|
|
38
|
+
}
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
require 'influxdb'
|
|
2
|
+
require 'resolv-replace'
|
|
3
|
+
module DTK::State
|
|
4
|
+
class Component::Attribute::Influxdb
|
|
5
|
+
class Client
|
|
6
|
+
INFLUXDB_SECRET_DIR = '/app/influxdb/'
|
|
7
|
+
def initialize(database)
|
|
8
|
+
@database = database
|
|
9
|
+
@connection_parameters = return_connection_parameters
|
|
10
|
+
@connection = return_connection(@database, @connection_parameters)
|
|
11
|
+
create_database?
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def query(query_expression)
|
|
15
|
+
self.connection.query(query_expression)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def write_point(name, data)
|
|
19
|
+
self.connection.write_point(name.to_s, data)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def measurement_helper(measurement_name)
|
|
23
|
+
begin
|
|
24
|
+
klass = Measurement.const_get(measurement_name.to_sym.capitalize)
|
|
25
|
+
klass.new(measurement_name, self)
|
|
26
|
+
rescue => error
|
|
27
|
+
puts error
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
attr_reader :database, :connection_parameters, :connection
|
|
32
|
+
|
|
33
|
+
private
|
|
34
|
+
|
|
35
|
+
def return_connection_parameters
|
|
36
|
+
params = {}
|
|
37
|
+
Dir.children(INFLUXDB_SECRET_DIR).each do |file|
|
|
38
|
+
if (file == 'username' || file == 'password' || file == 'url')
|
|
39
|
+
params[file.to_sym] = File.read("#{INFLUXDB_SECRET_DIR}#{file}")
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
{
|
|
43
|
+
url: params[:url],
|
|
44
|
+
username: params[:username],
|
|
45
|
+
password: params[:password],
|
|
46
|
+
time_precision: 'ms'
|
|
47
|
+
}
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def return_connection(database, connection_parameters)
|
|
51
|
+
::InfluxDB::Client.new(database, connection_parameters)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def create_database?
|
|
55
|
+
self.connection.query("CREATE DATABASE #{database}")
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
module DTK::State
|
|
2
|
+
class Component::Attribute::Influxdb
|
|
3
|
+
class Measurement
|
|
4
|
+
require_relative('measurement/attribute_measurement')
|
|
5
|
+
|
|
6
|
+
def initialize(name, client)
|
|
7
|
+
@name = name
|
|
8
|
+
@client = client
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def get_last_point(params_hash = {})
|
|
12
|
+
|
|
13
|
+
begin
|
|
14
|
+
check_params_hash(params_hash)
|
|
15
|
+
influxdb_encoding = influxdb_encoding(params_hash, Time.new)
|
|
16
|
+
last_point = self.client.query("select last(*) from #{influxdb_encoding.measurement_name} WHERE #{where_clause(influxdb_encoding)}")
|
|
17
|
+
fail "No data points in measurement #{name} for parameters:\n#{JSON.pretty_generate(params_hash)}" if last_point.empty?
|
|
18
|
+
last_point[0]["values"][0]
|
|
19
|
+
rescue => error
|
|
20
|
+
puts error
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
protected
|
|
25
|
+
|
|
26
|
+
attr_reader :name, :client
|
|
27
|
+
|
|
28
|
+
private
|
|
29
|
+
|
|
30
|
+
InfluxdbEncoding = ::Struct.new(:measurement_name, :tags, :timestamp)
|
|
31
|
+
|
|
32
|
+
def write_point(value, params_hash = {}, timestamp)
|
|
33
|
+
# The encode function we allow us to use different mappings of name, params_hash
|
|
34
|
+
# to the influxdb actual measurement name and tags.
|
|
35
|
+
influxdb_encoding = influxdb_encoding(params_hash, timestamp)
|
|
36
|
+
timestamp = timestamp.nil? ? Time.now : timestamp
|
|
37
|
+
|
|
38
|
+
data = {
|
|
39
|
+
values: { value: value },
|
|
40
|
+
tags: influxdb_encoding.tags,
|
|
41
|
+
timestamp: (timestamp.to_f * 1000).to_i
|
|
42
|
+
}
|
|
43
|
+
self.client.write_point(influxdb_encoding.measurement_name, data)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def check_params_hash(params_hash = {})
|
|
47
|
+
checked_params_hash = {}
|
|
48
|
+
self.required_params.each do |name|
|
|
49
|
+
unless value = params_hash[name]
|
|
50
|
+
fail "Missing parameter '#{name}'"
|
|
51
|
+
end
|
|
52
|
+
fail_if_illegal_tag_value(name, value)
|
|
53
|
+
checked_params_hash[name] = value
|
|
54
|
+
end
|
|
55
|
+
checked_params_hash
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# This default encoding can be overwritten by its children, for example
|
|
59
|
+
# attribute might make teh emasurement name be "#{self.name}_#{params[:component_name]" if that turns
|
|
60
|
+
# to be more efficient indexing
|
|
61
|
+
def influxdb_encoding(params_hash, timestamp)
|
|
62
|
+
InfluxdbEncoding.new(self.name, params_hash, timestamp)
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def where_clause(influxdb_encoding)
|
|
66
|
+
ret = ''
|
|
67
|
+
influxdb_encoding.tags.each_pair do |name, value|
|
|
68
|
+
unless value.nil?
|
|
69
|
+
if ret.empty?
|
|
70
|
+
ret += where_clause_term(name, value)
|
|
71
|
+
else
|
|
72
|
+
ret += " AND #{where_clause_term(name, value)}"
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
ret
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def where_clause_term(name, value)
|
|
80
|
+
fail_if_illegal_tag_value(name, value)
|
|
81
|
+
"#{name} ='#{value}'"
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
LEGAL_TAG_CLASSES = [::String, ::Symbol, ::Integer]
|
|
85
|
+
|
|
86
|
+
def fail_if_illegal_tag_value(name, value)
|
|
87
|
+
unless LEGAL_TAG_CLASSES.include?(value.class)
|
|
88
|
+
fail "Parameter '#{name}' has an illegal type, legal types are #{LEGAL_TAG_CLASSES.join(', ')}"
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
module DTK::State
|
|
2
|
+
class Component::Attribute::Influxdb
|
|
3
|
+
class Measurement
|
|
4
|
+
# DTK Attributes measurement
|
|
5
|
+
class Attributes < self
|
|
6
|
+
|
|
7
|
+
def write(value, params_hash = {}, timestamp)
|
|
8
|
+
checked_params_hash = check_params_hash(params_hash)
|
|
9
|
+
write_point(value, checked_params_hash, timestamp)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
protected
|
|
13
|
+
|
|
14
|
+
def required_params
|
|
15
|
+
[:namespace, :crd_component_name, :component_instance_name, :attribute_name, :task_id]
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
module DTK::State
|
|
2
|
+
class Component::Attribute
|
|
3
|
+
class KubeCrd < self
|
|
4
|
+
def get(namespace, crd_component_name, component_instance_name, attribute_name, opts = {})
|
|
5
|
+
component_instance = Component.get(namespace, crd_component_name, component_instance_name, opts)
|
|
6
|
+
attribute = component_instance.attributes.find{ |attribute| attribute.name == attribute_name }
|
|
7
|
+
Attribute.new(attribute_name, attribute, component_instance)
|
|
8
|
+
end
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
module DTK::State
|
|
2
|
+
class ComponentDef
|
|
3
|
+
require_relative 'component_def/attribute_type_info'
|
|
4
|
+
|
|
5
|
+
attr_reader :name, :namespace, :executable_actions, :attribute_type_info
|
|
6
|
+
|
|
7
|
+
def initialize(namespace, name, content)
|
|
8
|
+
@name = name
|
|
9
|
+
@namespace = namespace
|
|
10
|
+
@executable_actions = content[:spec][:actions]
|
|
11
|
+
# @attributes = AttributeTypeInfo.create_from_kube_array(content[:spec][:attributes])
|
|
12
|
+
@attribute_type_info = AttributeTypeInfo.create_from_kube_array(content[:spec][:attributes])
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def self.get(namespace, name, opts = {})
|
|
16
|
+
# crd_component_def = ::DTK::CrdClient.instance(opts).kubeclient.get_componentdef(name, namespace)
|
|
17
|
+
crd_component_def = ::DTK::CrdClient.get_kubeclient(opts).get_componentdef(name, namespace)
|
|
18
|
+
ComponentDef.new(namespace, name, crd_component_def)
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
module DTK::State
|
|
2
|
+
class ComponentDef
|
|
3
|
+
class AttributeTypeInfo
|
|
4
|
+
|
|
5
|
+
attr_reader :name, :type, :required, :dynamic, :encrypted
|
|
6
|
+
|
|
7
|
+
def initialize(params)
|
|
8
|
+
@name = params[:name]
|
|
9
|
+
@type = params[:type]
|
|
10
|
+
@required = params[:required] || false
|
|
11
|
+
@dynamic = params[:dynamic] || false
|
|
12
|
+
@encrypted = params[:encrypted] || false
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def self.create_from_kube_array(kube_attributes)
|
|
16
|
+
kube_attributes.map do |attribute_content|
|
|
17
|
+
AttributeTypeInfo.new(attribute_content)
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
module DTK::State
|
|
2
|
+
class CrdComponent
|
|
3
|
+
attr_reader :name, :namespace, :crd_content, :components, :module_refs
|
|
4
|
+
|
|
5
|
+
def initialize(namespace, name, crd_content)
|
|
6
|
+
@name = name
|
|
7
|
+
@namespace = namespace
|
|
8
|
+
@crd_content = crd_content
|
|
9
|
+
@components = crd_content[:spec][:components]
|
|
10
|
+
@module_refs = crd_content[:references][:module_refs]
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def self.get(namespace, name, opts = {})
|
|
14
|
+
# crd_component = ::DTK::CrdClient.instance.kubeclient.get_component(name, namespace)
|
|
15
|
+
crd_component = ::DTK::CrdClient.get_kubeclient(opts).get_component(name, namespace)
|
|
16
|
+
CrdComponent.new(namespace, name, crd_component)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# components can be:
|
|
20
|
+
# 'all' - print attributes for all components
|
|
21
|
+
# ['cmp1', 'cmp2', ...] - print attributes for set of components
|
|
22
|
+
# '<cmp_name>' - print attributes for single component
|
|
23
|
+
# opts can have keys:
|
|
24
|
+
# task_id
|
|
25
|
+
# format - format to print in (default is yaml)
|
|
26
|
+
def self.get_attributes_for(namespace, crd_component_name, components, opts = {})
|
|
27
|
+
output = {}
|
|
28
|
+
|
|
29
|
+
crd_component = get(namespace, crd_component_name, opts)
|
|
30
|
+
crd_components = crd_component.components
|
|
31
|
+
|
|
32
|
+
if components == 'all'
|
|
33
|
+
crd_components.each do |kube_component|
|
|
34
|
+
component_hash = kube_component.to_hash
|
|
35
|
+
component_name = component_hash.keys.first
|
|
36
|
+
component = Component.new(component_name, component_hash[component_name], crd_component, opts)
|
|
37
|
+
output.merge!(component_name => component.attributes(opts))
|
|
38
|
+
end
|
|
39
|
+
else
|
|
40
|
+
components = [components] unless components.is_a?(Array)
|
|
41
|
+
|
|
42
|
+
check_for_missing_components(crd_components, components)
|
|
43
|
+
components.each do |component_name|
|
|
44
|
+
matching_component = crd_components.find{ |cmp| cmp.to_hash.keys.first == component_name }
|
|
45
|
+
component = Component.new(component_name, matching_component[component_name], crd_component, opts)
|
|
46
|
+
output.merge!(component_name => component.attributes(opts))
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
output
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
private
|
|
54
|
+
|
|
55
|
+
def self.check_for_missing_components(crd_components, requested_components)
|
|
56
|
+
crd_component_names = crd_components.map{ |cmp| cmp.to_hash.keys.first }
|
|
57
|
+
missing_components = requested_components - crd_component_names
|
|
58
|
+
|
|
59
|
+
return if missing_components.empty?
|
|
60
|
+
|
|
61
|
+
fail Error.new("You have requested following component(s) but they do not exist in crd instance: #{missing_components.join(', ')}")
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
module DTK::State
|
|
2
|
+
class ExecutableAction
|
|
3
|
+
attr_reader :entrypoint, :bash_script, :name, :type
|
|
4
|
+
|
|
5
|
+
def initialize(params)
|
|
6
|
+
@name = params[:name]
|
|
7
|
+
@entrypoint = params[:entrypoint]
|
|
8
|
+
@type = params[:type]
|
|
9
|
+
@bash_script = params[:bash_script]
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def self.get(namespace, crd_component_name, component_name, action_name, opts = {})
|
|
13
|
+
component_obj = Component.get(namespace, crd_component_name, component_name, opts)
|
|
14
|
+
actions = component_obj.component_def.executable_actions
|
|
15
|
+
action = actions[action_name]
|
|
16
|
+
|
|
17
|
+
raise Error.new("Unable to find action '#{action_name}'") unless action
|
|
18
|
+
|
|
19
|
+
ExecutableAction.new(
|
|
20
|
+
{
|
|
21
|
+
name: action_name,
|
|
22
|
+
entrypoint: action[:entrypoint] || '',
|
|
23
|
+
type: action[:type] || '',
|
|
24
|
+
bash_script: action[:bash_script] || ''
|
|
25
|
+
}
|
|
26
|
+
)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
Gem::Specification.new do |spec|
|
|
2
|
+
spec.name = 'persistence-providers'
|
|
3
|
+
spec.version = '0.0.2'
|
|
4
|
+
spec.author = 'Reactor8'
|
|
5
|
+
spec.email = 'support@reactor8.com'
|
|
6
|
+
spec.description = %q{Persistence providers plugin}
|
|
7
|
+
spec.summary = %q{Persistence providers plugin}
|
|
8
|
+
spec.license = 'Apache-2.0'
|
|
9
|
+
spec.platform = Gem::Platform::RUBY
|
|
10
|
+
spec.required_ruby_version = Gem::Requirement.new('>= 2.4.0')
|
|
11
|
+
|
|
12
|
+
spec.require_paths = ['lib']
|
|
13
|
+
spec.files = Dir.glob("**/*").reject { |el| el =~ /persistence-providers-.*.gem/ }
|
|
14
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
|
15
|
+
|
|
16
|
+
spec.add_dependency 'kubeclient'
|
|
17
|
+
spec.add_dependency 'celluloid-io'
|
|
18
|
+
spec.add_dependency 'influxdb', '0.8.0'
|
|
19
|
+
end
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
require_relative 'lib/persistence_providers'
|
|
2
|
+
|
|
3
|
+
DATABASE = 'dtk'
|
|
4
|
+
client = DTK::State::Component::Attribute::Influxdb::Client.new(DATABASE)
|
|
5
|
+
MEASUREMENT_NAME = :attributes
|
|
6
|
+
measurement = client.measurement_helper(MEASUREMENT_NAME)
|
|
7
|
+
|
|
8
|
+
# clear the measurement
|
|
9
|
+
puts "Cleared the measurement with name '#{MEASUREMENT_NAME}'" if client.query("DELETE FROM #{MEASUREMENT_NAME}")
|
data/test-influxdb.rb
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
require_relative 'lib/persistence_providers'
|
|
2
|
+
|
|
3
|
+
influxdb = DTK::State::Component::Attribute::Influxdb.new
|
|
4
|
+
|
|
5
|
+
something = influxdb.get('default', 'ccn', 'cin', 'an')
|
|
6
|
+
influxdb.write('default', 'ccn', 'cin', 'an', 'some', {}, Time.new('2045', '12'))
|
|
7
|
+
something = influxdb.get('default', 'ccn', 'cin', 'an')
|
|
8
|
+
puts something
|
data/test.rb
ADDED
metadata
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: persistence-providers
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.0.2
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Reactor8
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2020-02-27 00:00:00.000000000 Z
|
|
12
|
+
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: kubeclient
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - ">="
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: '0'
|
|
20
|
+
type: :runtime
|
|
21
|
+
prerelease: false
|
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
+
requirements:
|
|
24
|
+
- - ">="
|
|
25
|
+
- !ruby/object:Gem::Version
|
|
26
|
+
version: '0'
|
|
27
|
+
- !ruby/object:Gem::Dependency
|
|
28
|
+
name: celluloid-io
|
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
|
30
|
+
requirements:
|
|
31
|
+
- - ">="
|
|
32
|
+
- !ruby/object:Gem::Version
|
|
33
|
+
version: '0'
|
|
34
|
+
type: :runtime
|
|
35
|
+
prerelease: false
|
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
37
|
+
requirements:
|
|
38
|
+
- - ">="
|
|
39
|
+
- !ruby/object:Gem::Version
|
|
40
|
+
version: '0'
|
|
41
|
+
- !ruby/object:Gem::Dependency
|
|
42
|
+
name: influxdb
|
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
|
44
|
+
requirements:
|
|
45
|
+
- - '='
|
|
46
|
+
- !ruby/object:Gem::Version
|
|
47
|
+
version: 0.8.0
|
|
48
|
+
type: :runtime
|
|
49
|
+
prerelease: false
|
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
51
|
+
requirements:
|
|
52
|
+
- - '='
|
|
53
|
+
- !ruby/object:Gem::Version
|
|
54
|
+
version: 0.8.0
|
|
55
|
+
description: Persistence providers plugin
|
|
56
|
+
email: support@reactor8.com
|
|
57
|
+
executables: []
|
|
58
|
+
extensions: []
|
|
59
|
+
extra_rdoc_files: []
|
|
60
|
+
files:
|
|
61
|
+
- Gemfile
|
|
62
|
+
- lib/crd_client.rb
|
|
63
|
+
- lib/persistence_providers.rb
|
|
64
|
+
- lib/state/component.rb
|
|
65
|
+
- lib/state/component/attribute.rb
|
|
66
|
+
- lib/state/component/providers/influxdb.rb
|
|
67
|
+
- lib/state/component/providers/influxdb/client.rb
|
|
68
|
+
- lib/state/component/providers/influxdb/measurement.rb
|
|
69
|
+
- lib/state/component/providers/influxdb/measurement/attribute_measurement.rb
|
|
70
|
+
- lib/state/component/providers/kube_crd.rb
|
|
71
|
+
- lib/state/component_def.rb
|
|
72
|
+
- lib/state/component_def/attribute_type_info.rb
|
|
73
|
+
- lib/state/crd_component.rb
|
|
74
|
+
- lib/state/executable_action.rb
|
|
75
|
+
- persistence-providers.gemspec
|
|
76
|
+
- test-destroy-influxdb.rb
|
|
77
|
+
- test-influxdb.rb
|
|
78
|
+
- test.rb
|
|
79
|
+
homepage:
|
|
80
|
+
licenses:
|
|
81
|
+
- Apache-2.0
|
|
82
|
+
metadata: {}
|
|
83
|
+
post_install_message:
|
|
84
|
+
rdoc_options: []
|
|
85
|
+
require_paths:
|
|
86
|
+
- lib
|
|
87
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
88
|
+
requirements:
|
|
89
|
+
- - ">="
|
|
90
|
+
- !ruby/object:Gem::Version
|
|
91
|
+
version: 2.4.0
|
|
92
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
93
|
+
requirements:
|
|
94
|
+
- - ">="
|
|
95
|
+
- !ruby/object:Gem::Version
|
|
96
|
+
version: '0'
|
|
97
|
+
requirements: []
|
|
98
|
+
rubygems_version: 3.0.8
|
|
99
|
+
signing_key:
|
|
100
|
+
specification_version: 4
|
|
101
|
+
summary: Persistence providers plugin
|
|
102
|
+
test_files: []
|