persistence-providers 0.0.3.5 → 0.0.4.1
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/crd_client.rb +24 -9
- data/lib/persistence_providers.rb +1 -0
- data/lib/state/component.rb +16 -2
- data/lib/state/component/providers/influxdb.rb +31 -67
- data/lib/state/component/providers/influxdb/client.rb +22 -21
- data/lib/state/component/providers/influxdb/measurement.rb +17 -9
- data/lib/state/component/providers/influxdb/measurement/errors.rb +6 -0
- data/lib/state/component/providers/influxdb/measurement/events.rb +12 -1
- data/lib/state/component/providers/influxdb/measurement/states.rb +13 -1
- data/lib/state/component/providers/influxdb/semantictype.rb +221 -0
- data/lib/state/component_def.rb +3 -0
- data/lib/state/crd_assembly.rb +3 -1
- data/lib/state/workflow_instance.rb +50 -2
- data/lib/utils.rb +3 -0
- data/lib/utils/log.rb +22 -0
- data/persistence-providers.gemspec +1 -1
- metadata +5 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9bc555b9367087540ccddc49bc1ce3fa937b0a36be7b3d6ff832373a9beeadd0
|
4
|
+
data.tar.gz: 5ae84500a46b183544d53b1d7d2f90cf6b49bcf52d34d2b95eba8474f7653c93
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 52cc878735fdf04cb19b7a202ff2622872b4514f40c1b849a0ff73a821f4e8e333c1aa9a3ac6ba6b31de29b199f4c49fb5fbd3df2c2bdf64fd4d421f6ff67e7d
|
7
|
+
data.tar.gz: a9b7fbf25e02a5b1dc15220760e91ca1bc703e7f1c2f388f6f2b083c48170c1162ffc483464cbde99f31e2465dd687c78bc47006a5ad9ee6157c5c40538083e9
|
data/lib/crd_client.rb
CHANGED
@@ -1,10 +1,16 @@
|
|
1
1
|
require 'kubeclient'
|
2
2
|
require 'celluloid/io'
|
3
|
-
require 'singleton'
|
3
|
+
# require 'singleton'
|
4
4
|
|
5
5
|
module DTK
|
6
6
|
class CrdClient
|
7
|
-
include Singleton
|
7
|
+
# include Singleton
|
8
|
+
DEFAULT_API_VERSION = 'v1alpha1'
|
9
|
+
|
10
|
+
# COMPONENT_DEF_CRD_VERSION = ENV["COMPONENT_DEF_CRD_VERSION"]
|
11
|
+
# ASSEMBLY_CRD_VERSION = ENV["ASSEMBLY_CRD_VERSION"]
|
12
|
+
# WORKFLOW_CRD_VERSION = ENV["WORKFLOW_CRD_VERSION"]
|
13
|
+
# WORKFLOW_INSTANCE_CRD_VERSION = ENV["WORKFLOW_INSTANCE_CRD_VERSION"]
|
8
14
|
|
9
15
|
attr_accessor :kubeclient
|
10
16
|
|
@@ -12,17 +18,26 @@ module DTK
|
|
12
18
|
if @kubeclient = opts[:kubeclient]
|
13
19
|
@kubeclient
|
14
20
|
else
|
15
|
-
|
21
|
+
kubeclient_version(opts)
|
16
22
|
end
|
17
23
|
end
|
18
24
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
if
|
23
|
-
return
|
25
|
+
def self.kubeclient_version(opts = {})
|
26
|
+
version = opts[:apiVersion] || DEFAULT_API_VERSION
|
27
|
+
|
28
|
+
if existing_version = KubeclientVersions[version]
|
29
|
+
return existing_version
|
30
|
+
else
|
31
|
+
new_instance = new(version).kubeclient
|
32
|
+
KubeclientVersions[version] = new_instance
|
33
|
+
new_instance
|
24
34
|
end
|
35
|
+
end
|
36
|
+
KubeclientVersions = {}
|
25
37
|
|
38
|
+
# opts can have keys
|
39
|
+
# kubernetes_client - already instantiated kubernetes client
|
40
|
+
def initialize(apiVersion)
|
26
41
|
ssl_options = {}
|
27
42
|
auth_options = { bearer_token_file: '/var/run/secrets/kubernetes.io/serviceaccount/token' }
|
28
43
|
|
@@ -37,7 +52,7 @@ module DTK
|
|
37
52
|
|
38
53
|
@kubeclient = Kubeclient::Client.new(
|
39
54
|
'https://kubernetes.default.svc/apis/',
|
40
|
-
|
55
|
+
"dtk.io/#{apiVersion}",
|
41
56
|
auth_options: auth_options,
|
42
57
|
ssl_options: ssl_options,
|
43
58
|
socket_options: socket_options
|
data/lib/state/component.rb
CHANGED
@@ -17,8 +17,8 @@ module DTK::State
|
|
17
17
|
# task_id
|
18
18
|
def self.get(crd_assembly_namespace, crd_assembly_name, component_name, opts = {})
|
19
19
|
crd_assembly = CrdAssembly.get(crd_assembly_namespace, crd_assembly_name, opts)
|
20
|
-
if matching_component = crd_assembly
|
21
|
-
Component.new(component_name, matching_component
|
20
|
+
if matching_component = Component.find_matching_component(crd_assembly, component_name)
|
21
|
+
Component.new(component_name, Component.get_component_content(matching_component, component_name), crd_assembly, opts)
|
22
22
|
end
|
23
23
|
end
|
24
24
|
|
@@ -54,6 +54,20 @@ module DTK::State
|
|
54
54
|
|
55
55
|
private
|
56
56
|
|
57
|
+
def self.get_component_content(matching_component, component_name)
|
58
|
+
return matching_component.is_a?(String) ? {} : matching_component[component_name]
|
59
|
+
end
|
60
|
+
|
61
|
+
def self.find_matching_component(assembly, component_name)
|
62
|
+
assembly.components.find do |cmp|
|
63
|
+
if cmp.is_a? String
|
64
|
+
cmp == component_name
|
65
|
+
else
|
66
|
+
cmp.to_hash.keys.first.to_s == component_name
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
57
71
|
def get_component_def(opts = {})
|
58
72
|
destructured_component = destructure_component_full_name
|
59
73
|
component_def = @component_defs.find { |component_def| component_def[:name] == destructured_component[:component_def_name] }
|
@@ -3,6 +3,7 @@ module DTK::State
|
|
3
3
|
class Influxdb < self
|
4
4
|
require_relative('influxdb/client')
|
5
5
|
require_relative('influxdb/measurement')
|
6
|
+
require_relative('influxdb/semantictype')
|
6
7
|
|
7
8
|
attr_reader :client, :measurement
|
8
9
|
|
@@ -12,88 +13,51 @@ module DTK::State
|
|
12
13
|
end
|
13
14
|
|
14
15
|
def get(namespace, component_name, assembly_name, attribute_name, opts = {})
|
15
|
-
required_tags = get_required_tags(namespace, component_name, assembly_name, attribute_name)
|
16
|
-
if opts[:provider] == "correlation"
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
elsif
|
21
|
-
last_value = measurement.get_last_point(required_tags)
|
22
|
-
last_value
|
23
|
-
end
|
16
|
+
required_tags = measurement.get_required_tags(namespace, component_name, assembly_name, attribute_name)
|
17
|
+
required_tags.merge! measurement.get_correlator_type(opts[:entrypoint]) if opts[:provider] == "correlation"
|
18
|
+
measurement.get_last_point(required_tags)
|
19
|
+
rescue => e
|
20
|
+
raise "Error happened while getting attribute from InfluxDB.\nError: #{e}"
|
24
21
|
end
|
25
22
|
|
26
23
|
def write(namespace, component_name, assembly_name, attribute_name, value, opts = {}, timestamp = nil)
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
elsif
|
33
|
-
required_tags = get_required_tags(namespace, component_name, assembly_name, attribute_name)
|
34
|
-
measurement.write(value, required_tags, timestamp)
|
35
|
-
end
|
24
|
+
required_tags = measurement.get_required_tags(namespace, component_name, assembly_name, attribute_name)
|
25
|
+
required_tags.merge! measurement.get_correlator_type(opts[:entrypoint]) if opts[:provider] == "correlation"
|
26
|
+
measurement.write(value.to_s, required_tags, timestamp)
|
27
|
+
rescue => e
|
28
|
+
raise "Error happened while writing attribute into InfluxDB.\Error: #{e}"
|
36
29
|
end
|
37
30
|
|
38
31
|
def write_event(event_id, pod_name, pod_namespace, event_source, event_message, component_name, attribute_name, task_id, timestamp)
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
pod_namespace: pod_namespace,
|
46
|
-
component_name: component_name,
|
47
|
-
attribute_name: attribute_name,
|
48
|
-
task_id: task_id
|
49
|
-
}
|
50
|
-
measurement.write(value_to_write.to_s, required_tags, timestamp)
|
51
|
-
rescue => error
|
52
|
-
puts error
|
53
|
-
end
|
32
|
+
fail "Bad timestamp input, write operation wont be completed" if timestamp > Time.new
|
33
|
+
value_to_write = { event_source: event_source, event_message: event_message }
|
34
|
+
required_tags = measurement.get_required_tags(event_id, pod_name, pod_namespace, component_name, attribute_name, task_id)
|
35
|
+
measurement.write(value_to_write.to_s, required_tags, timestamp)
|
36
|
+
rescue => error
|
37
|
+
raise "Error happened while writing event into InfluxDB.\nError: #{e}"
|
54
38
|
end
|
55
39
|
|
56
40
|
def get_event(event_id, pod_name, pod_namespace, component_name, attribute_name, task_id)
|
57
|
-
required_tags =
|
58
|
-
event_id: event_id,
|
59
|
-
pod_name: pod_name,
|
60
|
-
pod_namespace: pod_namespace,
|
61
|
-
component_name: component_name,
|
62
|
-
attribute_name: attribute_name,
|
63
|
-
task_id: task_id
|
64
|
-
}
|
41
|
+
required_tags = measurement.get_required_tags(event_id, pod_name, pod_namespace, component_name, attribute_name, task_id)
|
65
42
|
last_point = measurement.get_last_point(required_tags)
|
43
|
+
rescue => e
|
44
|
+
raise "Error happened while getting event from InfluxDB.\nError: #{e}"
|
66
45
|
end
|
67
46
|
|
68
47
|
def write_state(type, name, namespace, object_state, spec, status, component_name, attribute_name, task_id, timestamp)
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
namespace: namespace,
|
76
|
-
object_state: object_state,
|
77
|
-
component_name: component_name,
|
78
|
-
attribute_name: attribute_name,
|
79
|
-
task_id: task_id
|
80
|
-
}
|
81
|
-
measurement.write(value_to_write.to_s, required_tags, timestamp)
|
82
|
-
rescue => error
|
83
|
-
puts error
|
84
|
-
end
|
48
|
+
raise "Bad timestamp input, write operation to InfluxDB wont be completed" if timestamp > Time.new
|
49
|
+
value_to_write = { spec: spec, status: status }
|
50
|
+
required_tags = measurement.get_required_tags(type, name, namespace, object_state, component_name, attribute_name, task_id)
|
51
|
+
measurement.write(value_to_write.to_s, required_tags, timestamp)
|
52
|
+
rescue => e
|
53
|
+
raise "Error happened while writing state into InfluxDB.\nError: #{e}"
|
85
54
|
end
|
86
|
-
|
87
|
-
private
|
88
55
|
|
89
|
-
def
|
90
|
-
required_tags =
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
attribute_name: attribute_name,
|
95
|
-
task_id: "1"
|
96
|
-
}
|
56
|
+
def get_state(type, name, namespace, object_state, component_name, attribute_name, task_id)
|
57
|
+
required_tags = measurement.get_required_tags(type, name, namespace, object_state, component_name, attribute_name, task_id)
|
58
|
+
measurement.get_last_point(required_tags)
|
59
|
+
rescue => e
|
60
|
+
raise "Error happened while getting state from InfluxDB.\nError: #{e}"
|
97
61
|
end
|
98
62
|
end
|
99
63
|
end
|
@@ -12,24 +12,22 @@ module DTK::State
|
|
12
12
|
def query(query_expression)
|
13
13
|
query_api = self.connection.create_query_api
|
14
14
|
query_api.query(query_expression)
|
15
|
+
rescue => e
|
16
|
+
raise "Failed while processing flux query!. Error: #{e}"
|
15
17
|
end
|
16
18
|
|
17
19
|
def write_point(data)
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
fail error
|
23
|
-
end
|
20
|
+
write_api = self.connection.create_write_api
|
21
|
+
write_api.write(data: data)
|
22
|
+
rescue => e
|
23
|
+
raise e
|
24
24
|
end
|
25
25
|
|
26
26
|
def measurement_helper(measurement_name)
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
puts error
|
32
|
-
end
|
27
|
+
klass = Measurement.const_get(measurement_name.to_sym.capitalize)
|
28
|
+
klass.new(measurement_name, self)
|
29
|
+
rescue => e
|
30
|
+
raise e
|
33
31
|
end
|
34
32
|
|
35
33
|
attr_reader :connection_parameters, :connection
|
@@ -49,18 +47,21 @@ module DTK::State
|
|
49
47
|
org: params[:org],
|
50
48
|
bucket: params[:bucket]
|
51
49
|
}
|
50
|
+
rescue => e
|
51
|
+
raise "Problem happened while processing InfluxDB connection parameters. Error: #{e}"
|
52
52
|
end
|
53
53
|
|
54
54
|
def return_connection(connection_parameters)
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
55
|
+
client = InfluxDB2::Client.new(connection_parameters[:url], connection_parameters[:token],
|
56
|
+
bucket: connection_parameters[:bucket],
|
57
|
+
org: connection_parameters[:org],
|
58
|
+
precision: InfluxDB2::WritePrecision::MILLISECOND,
|
59
|
+
use_ssl: false)
|
60
|
+
query_api = client.create_query_api
|
61
|
+
query_api.query(query: 'from(bucket:"' + connection_parameters[:bucket] + '") |> range(start: -5)')
|
62
|
+
client
|
63
|
+
rescue => e
|
64
|
+
raise "Connection with InfluxDB could not be established. #{e}"
|
64
65
|
end
|
65
66
|
end
|
66
67
|
end
|
@@ -20,14 +20,22 @@ module DTK::State
|
|
20
20
|
end
|
21
21
|
|
22
22
|
def get_last_point(params_hash = {})
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
23
|
+
check_params_hash(params_hash)
|
24
|
+
flux_query = 'from(bucket:"' + client.connection_parameters[:bucket] + '") |> range(start:-5) |> filter(fn: (r) => r._measurement == "' + name.to_s + '")' + flux_filter(params_hash) + ' |> last()' + '|> drop(columns: ["_start", "_stop", "_field", "_measurement", "attribute_name", "assembly_name", "task_id", "component_name", "namespace"])'
|
25
|
+
result = self.client.query(query: flux_query)
|
26
|
+
result.values.map(&:records).flatten.map(&:values)
|
27
|
+
rescue => e
|
28
|
+
raise "Failed while getting last attribute point. Error: #{e}"
|
29
|
+
end
|
30
|
+
|
31
|
+
def get_required_tags(namespace, component_name, assembly_name, attribute_name)
|
32
|
+
required_tags = {
|
33
|
+
namespace: namespace,
|
34
|
+
component_name: component_name,
|
35
|
+
assembly_name: assembly_name,
|
36
|
+
attribute_name: attribute_name,
|
37
|
+
task_id: "1"
|
38
|
+
}
|
31
39
|
end
|
32
40
|
|
33
41
|
protected
|
@@ -90,7 +98,7 @@ module DTK::State
|
|
90
98
|
fail "Parameter '#{name}' has an illegal type, legal types are #{LEGAL_TAG_CLASSES.join(', ')}"
|
91
99
|
end
|
92
100
|
end
|
93
|
-
|
101
|
+
|
94
102
|
end
|
95
103
|
end
|
96
104
|
end
|
@@ -8,12 +8,23 @@ module DTK::State
|
|
8
8
|
write_point(value, checked_params_hash, timestamp)
|
9
9
|
end
|
10
10
|
|
11
|
+
def get_required_tags(event_id, pod_name, pod_namespace, component_name, attribute_name, task_id)
|
12
|
+
{
|
13
|
+
event_id: event_id,
|
14
|
+
pod_name: pod_name,
|
15
|
+
pod_namespace: pod_namespace,
|
16
|
+
component_name: component_name,
|
17
|
+
attribute_name: attribute_name,
|
18
|
+
task_id: task_id
|
19
|
+
}
|
20
|
+
end
|
21
|
+
|
11
22
|
protected
|
12
23
|
|
13
24
|
def required_params
|
14
25
|
[:event_id, :pod_name, :pod_namespace, :component_name, :attribute_name, :task_id]
|
15
26
|
end
|
16
|
-
|
27
|
+
|
17
28
|
end
|
18
29
|
end
|
19
30
|
end
|
@@ -8,12 +8,24 @@ module DTK::State
|
|
8
8
|
write_point(value, checked_params_hash, timestamp)
|
9
9
|
end
|
10
10
|
|
11
|
+
def get_required_tags(type, name, namespace, object_state, component_name, attribute_name, task_id)
|
12
|
+
{
|
13
|
+
type: type,
|
14
|
+
name: name,
|
15
|
+
namespace: namespace,
|
16
|
+
object_state: object_state,
|
17
|
+
component_name: component_name,
|
18
|
+
attribute_name: attribute_name,
|
19
|
+
task_id: task_id
|
20
|
+
}
|
21
|
+
end
|
22
|
+
|
11
23
|
protected
|
12
24
|
|
13
25
|
def required_params
|
14
26
|
[:type, :name, :namespace, :object_state, :component_name, :attribute_name, :task_id]
|
15
27
|
end
|
16
|
-
|
28
|
+
|
17
29
|
end
|
18
30
|
end
|
19
31
|
end
|
@@ -0,0 +1,221 @@
|
|
1
|
+
module DTK::State
|
2
|
+
class Component::Attribute::Influxdb
|
3
|
+
class SemanticType
|
4
|
+
require_relative './client'
|
5
|
+
attr_reader :name, :crd_content, :namespace, :client
|
6
|
+
attr_accessor :content_to_write
|
7
|
+
|
8
|
+
def initialize(name, namespace)
|
9
|
+
@name = name
|
10
|
+
@namespace = namespace
|
11
|
+
@client = Client.new
|
12
|
+
@crd_content = get(name, namespace)
|
13
|
+
@content_to_write = []
|
14
|
+
end
|
15
|
+
|
16
|
+
# no namespace because semantictype instances are going to be unique in cluster
|
17
|
+
def get(name, namespace, opts = {})
|
18
|
+
begin
|
19
|
+
semantictype = ::DTK::CrdClient.get_kubeclient(opts).get_semantictype(name, namespace)
|
20
|
+
semantictype.spec[:openAPIV3Schema]
|
21
|
+
rescue => error
|
22
|
+
fail "SemanticType attribute with name '#{name}' not found on the cluster!. Error: #{error}"
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def write_semantictype_inventory(inventory, component_id)
|
27
|
+
begin
|
28
|
+
get_influxdb_properties(inventory)
|
29
|
+
content_to_write.each do |point|
|
30
|
+
point[:tags].merge!({ component_id: component_id, attribute_name: @name })
|
31
|
+
@client.write_point({
|
32
|
+
name: point[:name],
|
33
|
+
tags: point[:tags],
|
34
|
+
fields: point[:fields],
|
35
|
+
time: (Time.new.to_f * 1000).to_i
|
36
|
+
})
|
37
|
+
end
|
38
|
+
"Inventory for attribute #{name} written to InfluxDB"
|
39
|
+
rescue => error
|
40
|
+
fail "#{name} inventory write failed. Error: #{error}"
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def partial_write_update(component_and_attribute, path, field_name, field_value)
|
45
|
+
parent, child = validate_parameter(path)
|
46
|
+
component_id, attribute_name = component_and_attribute.split('/')
|
47
|
+
# getting previous value for given parameters
|
48
|
+
previous_value = { }
|
49
|
+
begin
|
50
|
+
flux_query = 'from(bucket:"' + @client.connection_parameters[:bucket] + '") |> range(start:-5) |> filter(fn:(r) => r._measurement == "' + attribute_name + "_" + child[:type] + '") |> filter(fn: (r) => r.parent == "' + parent[:name] + '") |> filter(fn: (r) => r.name == "' + child[:name] + '")|> last()'
|
51
|
+
result = @client.query(query: flux_query)
|
52
|
+
previous_value = result.values.map(&:records).flatten.map(&:values)
|
53
|
+
rescue => error
|
54
|
+
fail "Partial write could not be completed. Previous point for given parameters not found!"
|
55
|
+
end
|
56
|
+
update_current(previous_value[0], get_path_to_object(path), field_name, field_value)
|
57
|
+
end
|
58
|
+
|
59
|
+
private
|
60
|
+
|
61
|
+
def update_current(previous_value, path, field_name, field_value)
|
62
|
+
tags = { }
|
63
|
+
previous_value.each_pair do |key, value|
|
64
|
+
tags[key] = value if key[0..0] != "_" && key != "result" && key != "table"
|
65
|
+
end
|
66
|
+
fields = Hash.new
|
67
|
+
fields[field_name.to_sym] = field_value
|
68
|
+
validate_fields(get_partial_definition(path), fields)
|
69
|
+
@client.write_point({
|
70
|
+
name: previous_value["_measurement"],
|
71
|
+
tags: tags,
|
72
|
+
fields: fields,
|
73
|
+
time: (Time.new.to_f * 1000).to_i
|
74
|
+
})
|
75
|
+
"Partial write update successful"
|
76
|
+
end
|
77
|
+
|
78
|
+
def get_influxdb_properties(inventory, parent_type = [:top], parent_name = nil)
|
79
|
+
content_to_write = []
|
80
|
+
properties = { }
|
81
|
+
inventory.each_pair do |key, value|
|
82
|
+
if value.class.to_s == "Array"
|
83
|
+
inventory[key].each do |element|
|
84
|
+
get_influxdb_properties(element, parent_type.push(key), inventory[:name])
|
85
|
+
end
|
86
|
+
else
|
87
|
+
properties[key] = value
|
88
|
+
end
|
89
|
+
end
|
90
|
+
resolve_property(parent_type, parent_name, properties)
|
91
|
+
parent_type.pop
|
92
|
+
"Attribute successfully validated!"
|
93
|
+
end
|
94
|
+
|
95
|
+
def resolve_property(parent_type, parent_name, properties)
|
96
|
+
definition = get_partial_definition(parent_type)
|
97
|
+
request = get_tags_and_fields(definition, properties)
|
98
|
+
validate_request(definition, request)
|
99
|
+
request[:name] = name + "_" + parent_type.last.to_s
|
100
|
+
request[:tags][:parent] = parent_name unless parent_name.nil?
|
101
|
+
content_to_write.push(request)
|
102
|
+
end
|
103
|
+
|
104
|
+
def validate_request(partial_definition, request)
|
105
|
+
begin
|
106
|
+
validate_tags(partial_definition, request[:tags])
|
107
|
+
validate_fields(partial_definition, request[:fields])
|
108
|
+
rescue => error
|
109
|
+
fail error
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
def get_tags_and_fields(partial_definition, properties)
|
114
|
+
tags = { }
|
115
|
+
fields = { }
|
116
|
+
properties.each_pair do |key, value|
|
117
|
+
if partial_definition[key].nil?
|
118
|
+
fail "Property '#{key}' not found in the definition of attribute"
|
119
|
+
else
|
120
|
+
if partial_definition[key][:metric].nil? || partial_definition[key][:metric] == false
|
121
|
+
tags[key] = value
|
122
|
+
else
|
123
|
+
fields[key] = value
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
{
|
128
|
+
tags: tags,
|
129
|
+
fields: fields
|
130
|
+
}
|
131
|
+
end
|
132
|
+
|
133
|
+
def validate_fields(partial_definition, fields)
|
134
|
+
|
135
|
+
partial_definition.each_pair do |key, value|
|
136
|
+
next if key == :required || value[:metric] == (false || nil)
|
137
|
+
|
138
|
+
if fields[key].nil?
|
139
|
+
fail "Field #{key} is missing. Validation of request failed!"
|
140
|
+
elsif value[:type].capitalize != fields[key].class.to_s
|
141
|
+
fail "Defined type for SemanticType attribute property '#{key}' is #{value[:type].capitalize}, #{fields[key].class} provided"
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
def validate_tags(partial_definition, tags)
|
147
|
+
partial_definition.each_pair do |key, value|
|
148
|
+
next if key == :required || value[:metric] == true
|
149
|
+
|
150
|
+
if tags[key].nil?
|
151
|
+
if value[:default].nil?
|
152
|
+
fail "Property #{key} is missing. Validation of request failed!"
|
153
|
+
else
|
154
|
+
tags[key] = value[:default]
|
155
|
+
end
|
156
|
+
else
|
157
|
+
type = tags[key].class
|
158
|
+
type = "Boolean" if type == TrueClass || type == FalseClass
|
159
|
+
if value[:type].capitalize == type.to_s
|
160
|
+
next
|
161
|
+
else
|
162
|
+
fail "Defined type for SemanticType attribute property '#{key}' is #{value[:type].capitalize}, #{type} provided"
|
163
|
+
end
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
def get_partial_definition(path)
|
169
|
+
i = 0
|
170
|
+
definition = { }
|
171
|
+
semantictype_crd = crd_content[:properties]
|
172
|
+
while i < path.length
|
173
|
+
if path[i].to_sym == :top
|
174
|
+
semantictype_crd.each_pair do |key, value|
|
175
|
+
if key == :required
|
176
|
+
definition[key] = value
|
177
|
+
else
|
178
|
+
definition[key] = value if value[:type] != "array"
|
179
|
+
end
|
180
|
+
end
|
181
|
+
else
|
182
|
+
definition = {}
|
183
|
+
definition[:required] = semantictype_crd[path[i].to_sym][:items][:required]
|
184
|
+
semantictype_crd[path[i].to_sym][:items][:properties].each_pair do |key, value|
|
185
|
+
definition[key] = value if value[:type] != "array"
|
186
|
+
end
|
187
|
+
semantictype_crd = semantictype_crd[path[i].to_sym][:items][:properties]
|
188
|
+
end
|
189
|
+
i+=1
|
190
|
+
end
|
191
|
+
definition
|
192
|
+
end
|
193
|
+
|
194
|
+
def get_path_to_object(parameter)
|
195
|
+
path = ["top"]
|
196
|
+
array = parameter.split('/')
|
197
|
+
array.each do |element|
|
198
|
+
path.push(element.split(':')[1])
|
199
|
+
end
|
200
|
+
path
|
201
|
+
end
|
202
|
+
|
203
|
+
def validate_parameter(parameter)
|
204
|
+
array_of_parameters = []
|
205
|
+
begin
|
206
|
+
parameter.split('/').each_with_index do |param, index|
|
207
|
+
name, type = param.split(':')
|
208
|
+
fail unless name && type
|
209
|
+
array_of_parameters.push({
|
210
|
+
name: name,
|
211
|
+
type: type
|
212
|
+
})
|
213
|
+
end
|
214
|
+
array_of_parameters
|
215
|
+
rescue => error
|
216
|
+
fail "Could not resolve parameter '#{parameter}'. It should be in format: 'parent:type/child:type'"
|
217
|
+
end
|
218
|
+
end
|
219
|
+
end
|
220
|
+
end
|
221
|
+
end
|
data/lib/state/component_def.rb
CHANGED
@@ -2,6 +2,8 @@ module DTK::State
|
|
2
2
|
class ComponentDef
|
3
3
|
require_relative 'component_def/attribute_type_info'
|
4
4
|
|
5
|
+
COMPONENT_DEF_CRD_VERSION = ENV["COMPONENT_DEF_CRD_VERSION"]
|
6
|
+
|
5
7
|
attr_reader :name, :namespace, :executable_actions, :attribute_type_info
|
6
8
|
|
7
9
|
def initialize(namespace, name, content)
|
@@ -12,6 +14,7 @@ module DTK::State
|
|
12
14
|
end
|
13
15
|
|
14
16
|
def self.get(namespace, name, opts = {})
|
17
|
+
opts[:apiVersion] = COMPONENT_DEF_CRD_VERSION
|
15
18
|
crd_component_def = ::DTK::CrdClient.get_kubeclient(opts).get_componentdef(name, namespace)
|
16
19
|
ComponentDef.new(namespace, name, crd_component_def)
|
17
20
|
end
|
data/lib/state/crd_assembly.rb
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
module DTK::State
|
2
2
|
class CrdAssembly
|
3
|
+
ASSEMBLY_CRD_VERSION = ENV["ASSEMBLY_CRD_VERSION"]
|
4
|
+
|
3
5
|
attr_reader :name, :namespace, :crd_content, :components, :references
|
4
6
|
|
5
7
|
def initialize(namespace, name, crd_content)
|
@@ -11,7 +13,7 @@ module DTK::State
|
|
11
13
|
end
|
12
14
|
|
13
15
|
def self.get(namespace, name, opts = {})
|
14
|
-
|
16
|
+
opts[:apiVersion] = ASSEMBLY_CRD_VERSION
|
15
17
|
crd_assembly = ::DTK::CrdClient.get_kubeclient(opts).get_assembly(name, namespace)
|
16
18
|
CrdAssembly.new(namespace, name, crd_assembly)
|
17
19
|
end
|
@@ -2,6 +2,8 @@ module DTK::State
|
|
2
2
|
class WorkflowInstance
|
3
3
|
require_relative 'workflow_instance/attribute_type_info'
|
4
4
|
|
5
|
+
WORKFLOW_INSTANCE_CRD_VERSION = ENV["WORKFLOW_INSTANCE_CRD_VERSION"]
|
6
|
+
|
5
7
|
attr_reader :name, :namespace, :assembly, :workflow_template, :attributes, :workflow, :attribute_type_info
|
6
8
|
|
7
9
|
def initialize(namespace, name, crd_content)
|
@@ -15,6 +17,7 @@ module DTK::State
|
|
15
17
|
end
|
16
18
|
|
17
19
|
def self.get(namespace, name, opts = {})
|
20
|
+
opts[:apiVersion] = WORKFLOW_INSTANCE_CRD_VERSION
|
18
21
|
workflow_instance = ::DTK::CrdClient.get_kubeclient(opts).get_workflow_instance(name, namespace)
|
19
22
|
WorkflowInstance.new(namespace, name, workflow_instance)
|
20
23
|
end
|
@@ -26,13 +29,58 @@ module DTK::State
|
|
26
29
|
|
27
30
|
def self.get_action_attributes(namespace, name, action_id, opts = {})
|
28
31
|
workflow_instance = get(namespace, name, opts)
|
29
|
-
action =
|
32
|
+
action = WorkflowInstance.find_action(action_id, workflow_instance.workflow)
|
30
33
|
return nil unless action
|
31
34
|
attributes = action[:attributes] || {}
|
32
35
|
attributes.to_h
|
33
36
|
end
|
34
37
|
|
35
|
-
def
|
38
|
+
def self.update_action_level_result_attributes(namespace, name, attributes, action_id, opts = {})
|
39
|
+
return "Dynamic attributes do not exist for action with id #{@action_id}, nothing to update" if attributes.nil? || attributes.empty?
|
40
|
+
attributes.delete_if { |key, value| value.nil? || value.to_s.strip == '' }
|
41
|
+
opts[:apiVersion] = WORKFLOW_INSTANCE_CRD_VERSION
|
42
|
+
workflow_instance = ::DTK::CrdClient.get_kubeclient(opts).get_workflow_instance(name, namespace)
|
43
|
+
workflow = workflow_instance[:spec][:workflow]
|
44
|
+
|
45
|
+
action = WorkflowInstance.find_action(action_id, workflow)
|
46
|
+
action[:attributes] = {} if !action[:attributes]
|
47
|
+
attributes.each do |attr_name, attr_val|
|
48
|
+
action[:attributes][attr_name.to_sym] = {} unless action[:attributes][attr_name.to_sym]
|
49
|
+
unless action[:attributes][attr_name.to_sym][:hidden]
|
50
|
+
if attr_val.is_a? Hash
|
51
|
+
action[:attributes][attr_name.to_sym][:value] = attr_val[:value] || attr_val
|
52
|
+
else
|
53
|
+
action[:attributes][attr_name.to_sym][:value] = attr_val
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
::DTK::CrdClient.get_kubeclient(opts).update_workflow_instance(workflow_instance)
|
58
|
+
end
|
59
|
+
|
60
|
+
def self.patchError!(patches, message, action_index_steps)
|
61
|
+
errorPatch = {
|
62
|
+
"op" => "add",
|
63
|
+
"path" => "/spec/status/steps/#{action_index_steps}/errorMsg",
|
64
|
+
"value" => message
|
65
|
+
}
|
66
|
+
patches << errorPatch
|
67
|
+
end
|
68
|
+
|
69
|
+
def self.update_action_status(namespace, name, parent_id, action_id, status, error_message = "", opts = {})
|
70
|
+
opts[:apiVersion] = WORKFLOW_INSTANCE_CRD_VERSION
|
71
|
+
workflow_instance = ::DTK::CrdClient.get_kubeclient(opts).get_workflow_instance(name, namespace)
|
72
|
+
steps = workflow_instance[:spec][:status][:steps]
|
73
|
+
action_index_steps = steps.find_index { |action| action[:id].eql? action_id }
|
74
|
+
patch = [{
|
75
|
+
"op" => "replace",
|
76
|
+
"path" => "/spec/status/steps/#{action_index_steps}/state",
|
77
|
+
"value" => status
|
78
|
+
}]
|
79
|
+
patchError!(patch, error_message, action_index_steps) unless error_message.empty? || error_message.nil?
|
80
|
+
::DTK::CrdClient.get_kubeclient(opts).json_patch_workflow_instance(name, patch, namespace)
|
81
|
+
end
|
82
|
+
|
83
|
+
def self.find_action(id, workflow = @workflow)
|
36
84
|
action = nil
|
37
85
|
subtasks = workflow[:subtasks]
|
38
86
|
subtasks.each do |subtask|
|
data/lib/utils.rb
ADDED
data/lib/utils/log.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
module Utils
|
2
|
+
module Log
|
3
|
+
require 'logger'
|
4
|
+
|
5
|
+
def self.instance
|
6
|
+
@instance ||= Logger.new('/proc/1/fd/1', formatter: proc { |severity, datetime, progname, msg|
|
7
|
+
orange_color = "\x1b[33m"
|
8
|
+
white_color = "\x1b[37m"
|
9
|
+
red_color = "\x1b[31m"
|
10
|
+
|
11
|
+
date_format = datetime.strftime("%Y-%m-%d %H:%M:%S:%L")
|
12
|
+
if severity == "INFO"
|
13
|
+
"#{orange_color}[#{date_format}] - #{white_color}#{msg}\n"
|
14
|
+
elsif severity == "WARN"
|
15
|
+
"#{orange_color}[#{date_format}] [WARNING] - #{msg}\n"
|
16
|
+
elsif severity == "ERROR"
|
17
|
+
"#{red_color}[#{date_format}] [ERROR] - #{msg}\n"
|
18
|
+
end
|
19
|
+
})
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: persistence-providers
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.4.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Reactor8
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-05-
|
11
|
+
date: 2020-05-27 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: kubeclient
|
@@ -70,6 +70,7 @@ files:
|
|
70
70
|
- lib/state/component/providers/influxdb/measurement/errors.rb
|
71
71
|
- lib/state/component/providers/influxdb/measurement/events.rb
|
72
72
|
- lib/state/component/providers/influxdb/measurement/states.rb
|
73
|
+
- lib/state/component/providers/influxdb/semantictype.rb
|
73
74
|
- lib/state/component/providers/kube_crd.rb
|
74
75
|
- lib/state/component_def.rb
|
75
76
|
- lib/state/component_def/attribute_type_info.rb
|
@@ -78,6 +79,8 @@ files:
|
|
78
79
|
- lib/state/executable_action/attribute_type_info.rb
|
79
80
|
- lib/state/workflow_instance.rb
|
80
81
|
- lib/state/workflow_instance/attribute_type_info.rb
|
82
|
+
- lib/utils.rb
|
83
|
+
- lib/utils/log.rb
|
81
84
|
- persistence-providers.gemspec
|
82
85
|
- test-destroy-influxdb.rb
|
83
86
|
- test-influxdb.rb
|