cosmos 5.0.3 → 5.0.4
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/bin/cosmos +1 -1
- data/lib/cosmos/api/api.rb +1 -25
- data/lib/cosmos/api/cmd_api.rb +6 -6
- data/lib/cosmos/api/config_api.rb +10 -4
- data/lib/cosmos/api/limits_api.rb +1 -1
- data/lib/cosmos/api/settings_api.rb +19 -7
- data/lib/cosmos/api/target_api.rb +2 -2
- data/lib/cosmos/api/tlm_api.rb +8 -8
- data/lib/cosmos/config/config_parser.rb +2 -2
- data/lib/cosmos/config/meta_config_parser.rb +1 -1
- data/lib/cosmos/logs/log_writer.rb +2 -2
- data/lib/cosmos/microservices/decom_microservice.rb +1 -1
- data/lib/cosmos/microservices/interface_microservice.rb +0 -1
- data/lib/cosmos/microservices/microservice.rb +2 -2
- data/lib/cosmos/microservices/reducer_microservice.rb +12 -10
- data/lib/cosmos/models/cvt_model.rb +6 -6
- data/lib/cosmos/models/gem_model.rb +2 -2
- data/lib/cosmos/models/info_model.rb +1 -1
- data/lib/cosmos/models/interface_status_model.rb +1 -1
- data/lib/cosmos/models/metadata_model.rb +42 -216
- data/lib/cosmos/models/metric_model.rb +2 -2
- data/lib/cosmos/models/microservice_model.rb +1 -1
- data/lib/cosmos/models/microservice_status_model.rb +1 -1
- data/lib/cosmos/models/model.rb +16 -16
- data/lib/cosmos/models/note_model.rb +124 -0
- data/lib/cosmos/models/ping_model.rb +2 -1
- data/lib/cosmos/models/plugin_model.rb +1 -1
- data/lib/cosmos/models/process_status_model.rb +1 -1
- data/lib/cosmos/models/scope_model.rb +9 -6
- data/lib/cosmos/models/settings_model.rb +55 -0
- data/lib/cosmos/models/sorted_model.rb +165 -0
- data/lib/cosmos/models/target_model.rb +20 -20
- data/lib/cosmos/models/tool_config_model.rb +38 -0
- data/lib/cosmos/models/tool_model.rb +1 -1
- data/lib/cosmos/models/widget_model.rb +1 -1
- data/lib/cosmos/operators/microservice_operator.rb +2 -1
- data/lib/cosmos/script/calendar.rb +26 -15
- data/lib/cosmos/system/system.rb +2 -1
- data/lib/cosmos/top_level.rb +5 -1
- data/lib/cosmos/topics/autonomic_topic.rb +2 -2
- data/lib/cosmos/topics/calendar_topic.rb +1 -1
- data/lib/cosmos/topics/command_decom_topic.rb +31 -1
- data/lib/cosmos/topics/command_topic.rb +6 -4
- data/lib/cosmos/topics/interface_topic.rb +8 -8
- data/lib/cosmos/topics/limits_event_topic.rb +5 -3
- data/lib/cosmos/topics/notifications_topic.rb +1 -1
- data/lib/cosmos/topics/router_topic.rb +9 -9
- data/lib/cosmos/topics/telemetry_decom_topic.rb +1 -1
- data/lib/cosmos/topics/telemetry_topic.rb +1 -1
- data/lib/cosmos/topics/timeline_topic.rb +1 -1
- data/lib/cosmos/topics/topic.rb +21 -16
- data/lib/cosmos/utilities/logger.rb +3 -3
- data/lib/cosmos/utilities/metric.rb +32 -26
- data/lib/cosmos/utilities/s3.rb +1 -1
- data/lib/cosmos/utilities/s3_file_cache.rb +12 -6
- data/lib/cosmos/utilities/store.rb +1 -0
- data/lib/cosmos/utilities/store_autoload.rb +27 -126
- data/lib/cosmos/version.rb +5 -5
- metadata +7 -4
- data/lib/cosmos/models/narrative_model.rb +0 -280
@@ -19,271 +19,97 @@
|
|
19
19
|
|
20
20
|
# https://www.rubydoc.info/gems/redis/Redis/Commands/SortedSets
|
21
21
|
|
22
|
-
require 'cosmos/
|
22
|
+
require 'cosmos/models/sorted_model'
|
23
23
|
|
24
24
|
module Cosmos
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
class MetadataInputError < MetadataError; end
|
29
|
-
|
30
|
-
class MetadataOverlapError < MetadataError; end
|
31
|
-
|
32
|
-
class MetadataModel < Model
|
33
|
-
|
34
|
-
CHRONICLE_TYPE = 'metadata'.freeze
|
35
|
-
CURRENT_VALUE = '__current.metadata.value'.freeze
|
25
|
+
class MetadataModel < SortedModel
|
26
|
+
METADATA_TYPE = 'metadata'.freeze
|
36
27
|
PRIMARY_KEY = '__METADATA'.freeze
|
37
28
|
|
38
29
|
def self.pk(scope)
|
39
|
-
|
40
|
-
end
|
41
|
-
|
42
|
-
# @return [String|nil] String of the saved json or nil if score not found under current value
|
43
|
-
def self.get_current_value(target:, scope:)
|
44
|
-
json = Store.hget("#{scope}#{CURRENT_VALUE}", target)
|
45
|
-
return nil unless json
|
46
|
-
return self.from_json(JSON.parse(json), scope: scope)
|
47
|
-
end
|
48
|
-
|
49
|
-
# @return [Array|nil] Array up to 100 of this model or empty array
|
50
|
-
def self.get(start:, stop:, scope:, limit: 100)
|
51
|
-
if start > stop
|
52
|
-
raise MetadataInputError.new "start: #{start} must be before stop: #{stop}"
|
53
|
-
end
|
54
|
-
pk = self.pk(scope)
|
55
|
-
array = Store.zrangebyscore(pk, start, stop, :limit => [0, limit])
|
56
|
-
ret_array = Array.new
|
57
|
-
array.each do |value|
|
58
|
-
ret_array << JSON.parse(value)
|
59
|
-
end
|
60
|
-
return ret_array
|
61
|
-
end
|
62
|
-
|
63
|
-
# @return [Array<Hash>] Array up to the limit of the models (as Hash objects) stored under the primary key
|
64
|
-
def self.all(scope:, limit: 100)
|
65
|
-
pk = self.pk(scope)
|
66
|
-
array = Store.zrange(pk, 0, -1, :limit => [0, limit])
|
67
|
-
ret_array = Array.new
|
68
|
-
array.each do |value|
|
69
|
-
ret_array << JSON.parse(value)
|
70
|
-
end
|
71
|
-
return ret_array
|
72
|
-
end
|
73
|
-
|
74
|
-
# @return [Integer] count of the members stored under the primary key
|
75
|
-
def self.count(scope:)
|
76
|
-
return Store.zcard(self.pk(scope))
|
30
|
+
"#{scope}#{PRIMARY_KEY}"
|
77
31
|
end
|
78
32
|
|
79
|
-
|
80
|
-
def self.score(score:, scope:)
|
81
|
-
pk = self.pk(scope)
|
82
|
-
array = Store.zrangebyscore(pk, score, score, :limit => [0, 1])
|
83
|
-
array.each do |value|
|
84
|
-
return JSON.parse(value)
|
85
|
-
end
|
86
|
-
return nil
|
87
|
-
end
|
88
|
-
|
89
|
-
# Remove member from a sorted set based on the score.
|
90
|
-
# @return [Integer] count of the members removed
|
91
|
-
def self.destroy(scope:, score:)
|
92
|
-
pk = self.pk(scope)
|
93
|
-
Store.zremrangebyscore(pk, score, score)
|
94
|
-
end
|
95
|
-
|
96
|
-
# Remove members from min to max of the sorted set.
|
97
|
-
# @return [Integer] count of the members removed
|
98
|
-
def self.range_destroy(scope:, min:, max:)
|
99
|
-
pk = self.pk(scope)
|
100
|
-
Store.zremrangebyscore(pk, min, max)
|
101
|
-
end
|
102
|
-
|
103
|
-
# @return [MetadataModel] Model generated from the passed JSON
|
104
|
-
def self.from_json(json, scope:)
|
105
|
-
json = JSON.parse(json) if String === json
|
106
|
-
raise "json data is nil" if json.nil?
|
107
|
-
|
108
|
-
json.transform_keys!(&:to_sym)
|
109
|
-
self.new(**json, scope: scope)
|
110
|
-
end
|
111
|
-
|
112
|
-
attr_reader :target, :start, :color, :metadata, :type
|
33
|
+
attr_reader :color, :metadata, :type
|
113
34
|
|
114
|
-
# @param [String] target - should be the target but can be anything
|
115
35
|
# @param [Integer] start - time metadata is active in seconds from Epoch
|
116
36
|
# @param [String] color - The event color
|
117
37
|
# @param [String] metadata - Key value pair object to link to name
|
118
38
|
# @param [String] scope - Cosmos scope to track event to
|
119
39
|
def initialize(
|
120
|
-
|
40
|
+
scope:,
|
121
41
|
start:,
|
122
42
|
color: nil,
|
123
43
|
metadata:,
|
124
|
-
|
125
|
-
type: CHRONICLE_TYPE,
|
44
|
+
type: METADATA_TYPE,
|
126
45
|
updated_at: 0
|
127
46
|
)
|
128
|
-
super(
|
129
|
-
set_input(start: start, color: color, metadata: metadata)
|
130
|
-
@target = target
|
131
|
-
@type = type
|
132
|
-
@updated_at = updated_at
|
133
|
-
end
|
134
|
-
|
135
|
-
# validate color
|
136
|
-
def validate_color(color)
|
137
|
-
if color.nil?
|
138
|
-
color = '#%06x' % (rand * 0xffffff)
|
139
|
-
end
|
140
|
-
valid_color = color =~ /(#*)([0-9,a-f,A-f]{6})/
|
141
|
-
if valid_color.nil?
|
142
|
-
raise MetadataInputError.new "invalid color, must be in hex format, e.g. #FF0000"
|
143
|
-
end
|
144
|
-
|
145
|
-
color = "##{color}" unless color.start_with?('#')
|
146
|
-
return color
|
147
|
-
end
|
148
|
-
|
149
|
-
# validate the input to the rules we have created for timelines.
|
150
|
-
# - An entry's start MUST be valid.
|
151
|
-
# - An entry's start MUST NOT be in the future.
|
152
|
-
# - An entry's metadata MUST a hash/object.
|
153
|
-
def validate_input(start:, color:, metadata:)
|
154
|
-
if start.is_a?(Integer) == false
|
155
|
-
raise MetadataInputError.new "failed validation input must be integer: #{start}"
|
156
|
-
end
|
157
|
-
now = Time.now.strftime('%s%3N').to_i
|
158
|
-
if start > now
|
159
|
-
raise MetadataInputError.new "start can not be in the future: #{start} > #{now}"
|
160
|
-
end
|
161
|
-
validate_color(color)
|
162
|
-
if metadata.is_a?(Hash) == false
|
163
|
-
raise MetadataInputError.new "Metadata must be a hash/object: #{metadata}"
|
164
|
-
end
|
165
|
-
end
|
166
|
-
|
167
|
-
# Set the values of the instance, @start, @stop, @metadata...
|
168
|
-
def set_input(start:, color:, metadata:)
|
169
|
-
if start.is_a?(Integer) == false
|
170
|
-
raise MetadataInputError.new "start input must be integer: #{start}"
|
171
|
-
end
|
47
|
+
super(start: start, scope: scope, updated_at: updated_at)
|
172
48
|
@start = start
|
173
49
|
@color = color
|
174
50
|
@metadata = metadata
|
51
|
+
@type = type # For the as_json, from_json round trip
|
175
52
|
end
|
176
53
|
|
177
|
-
#
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
# a time when doing an update
|
183
|
-
def validate_time(ignore_score: nil)
|
184
|
-
array = Store.zrangebyscore(@primary_key, @start, @start, :limit => [0, 1])
|
185
|
-
array.each do |value|
|
186
|
-
entry = JSON.parse(value)
|
187
|
-
if ignore_score == entry['start']
|
188
|
-
next
|
189
|
-
else
|
190
|
-
return entry
|
191
|
-
end
|
192
|
-
end
|
193
|
-
return nil
|
54
|
+
# Validates the instance variables: @start, @color, @metadata
|
55
|
+
def validate(update: false)
|
56
|
+
validate_start(update: update)
|
57
|
+
validate_color()
|
58
|
+
validate_metadata()
|
194
59
|
end
|
195
60
|
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
validate_input(start: @start, color: @color, metadata: @metadata)
|
200
|
-
collision = validate_time()
|
201
|
-
unless collision.nil?
|
202
|
-
raise MetadataOverlapError.new "no chronicle can overlap, collision: #{collision}"
|
61
|
+
def validate_color()
|
62
|
+
if @color.nil?
|
63
|
+
@color = '#%06x' % (rand * 0xffffff)
|
203
64
|
end
|
204
|
-
|
205
|
-
|
206
|
-
Store.zadd(@primary_key, @start, JSON.generate(as_json()))
|
207
|
-
update_current_value()
|
208
|
-
notify(kind: 'created')
|
209
|
-
end
|
210
|
-
|
211
|
-
# Update the Redis hash at primary_key and remove the current activity at the current score
|
212
|
-
# and update the score to the new score equal to the start Epoch time this uses a multi
|
213
|
-
# to execute both the remove and create. The member via the JSON generated via calling as_json
|
214
|
-
def update(start:, color:, metadata:)
|
215
|
-
validate_input(start: start, color: color, metadata: metadata)
|
216
|
-
old_start = @start
|
217
|
-
@updated_at = Time.now.to_nsec_from_epoch
|
218
|
-
set_input(start: start, color: color, metadata: metadata)
|
219
|
-
# copy of create
|
220
|
-
collision = validate_time(ignore_score: old_start)
|
221
|
-
unless collision.nil?
|
222
|
-
raise MetadataOverlapError.new "failed to update #{old_start}, no chronicles can overlap, collision: #{collision}"
|
65
|
+
unless @color =~ /(#*)([0-9,a-f,A-f]{6})/
|
66
|
+
raise SortedInputError.new "invalid color, must be in hex format, e.g. #FF0000"
|
223
67
|
end
|
68
|
+
@color = "##{@color}" unless @color.start_with?('#')
|
69
|
+
end
|
224
70
|
|
225
|
-
|
226
|
-
|
227
|
-
|
71
|
+
def validate_metadata()
|
72
|
+
unless @metadata.is_a?(Hash)
|
73
|
+
raise SortedInputError.new "Metadata must be a hash/object: #{@metadata}"
|
228
74
|
end
|
229
|
-
update_current_value(old_start: old_start)
|
230
|
-
notify(kind: 'updated', extra: old_start)
|
231
|
-
return @start
|
232
75
|
end
|
233
76
|
|
234
|
-
# Update the Redis hash at primary_key
|
235
|
-
#
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
json = Store.hget("#{@scope}#{CURRENT_VALUE}", @target)
|
241
|
-
unless json.nil?
|
242
|
-
model = MetadataModel.from_json(JSON.parse(json), scope: @scope)
|
243
|
-
update = model.start <= @start || model.start == old_start
|
244
|
-
end
|
77
|
+
# Update the Redis hash at primary_key based on the initial passed start
|
78
|
+
# The member is set to the JSON generated via calling as_json
|
79
|
+
def create(update: false)
|
80
|
+
validate(update: update)
|
81
|
+
@updated_at = Time.now.to_nsec_from_epoch
|
82
|
+
Store.zadd(@primary_key, @start, JSON.generate(as_json()))
|
245
83
|
if update
|
246
|
-
|
84
|
+
notify(kind: 'updated')
|
85
|
+
else
|
86
|
+
notify(kind: 'created')
|
247
87
|
end
|
248
88
|
end
|
249
89
|
|
250
|
-
#
|
251
|
-
def
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
# @return [] update the redis stream / timeline topic that something has changed
|
257
|
-
def notify(kind:, extra: nil)
|
258
|
-
notification = {
|
259
|
-
'data' => JSON.generate(as_json()),
|
260
|
-
'kind' => kind,
|
261
|
-
'type' => 'calendar',
|
262
|
-
}
|
263
|
-
notification['extra'] = extra unless extra.nil?
|
264
|
-
begin
|
265
|
-
CalendarTopic.write_entry(notification, scope: @scope)
|
266
|
-
rescue StandardError => e
|
267
|
-
raise MetadataError.new "Failed to write to stream: #{notification}, #{e}"
|
268
|
-
end
|
90
|
+
# Update the Redis hash at primary_key
|
91
|
+
def update(start:, color:, metadata:)
|
92
|
+
@start = start
|
93
|
+
@color = color
|
94
|
+
@metadata = metadata
|
95
|
+
create(update: true)
|
269
96
|
end
|
270
97
|
|
271
98
|
# @return [Hash] generated from the MetadataModel
|
272
99
|
def as_json
|
273
100
|
return {
|
274
|
-
'target' => @target,
|
275
101
|
'scope' => @scope,
|
276
|
-
'updated_at' => @updated_at,
|
277
102
|
'start' => @start,
|
278
103
|
'color' => @color,
|
279
104
|
'metadata' => @metadata,
|
280
|
-
'type' =>
|
105
|
+
'type' => METADATA_TYPE,
|
106
|
+
'updated_at' => @updated_at,
|
281
107
|
}
|
282
108
|
end
|
283
109
|
|
284
110
|
# @return [String] string view of metadata
|
285
111
|
def to_s
|
286
|
-
return "<MetadataModel
|
112
|
+
return "<MetadataModel s: #{@start}, c: #{@color}, m: #{@metadata}>"
|
287
113
|
end
|
288
114
|
end
|
289
115
|
end
|
@@ -20,7 +20,7 @@
|
|
20
20
|
require 'cosmos/models/model'
|
21
21
|
|
22
22
|
module Cosmos
|
23
|
-
class MetricModel <
|
23
|
+
class MetricModel < EphemeralModel
|
24
24
|
PRIMARY_KEY = '__cosmos__metric'.freeze
|
25
25
|
|
26
26
|
# NOTE: The following three class methods are used by the ModelController
|
@@ -38,7 +38,7 @@ module Cosmos
|
|
38
38
|
end
|
39
39
|
|
40
40
|
def self.destroy(scope:, name:)
|
41
|
-
|
41
|
+
EphemeralStore.hdel("#{scope}#{PRIMARY_KEY}", name)
|
42
42
|
end
|
43
43
|
|
44
44
|
def initialize(name:, scope:, metric_name:, label_list:)
|
@@ -186,7 +186,7 @@ module Cosmos
|
|
186
186
|
# Load microservice files
|
187
187
|
data = File.read(filename, mode: "rb")
|
188
188
|
Cosmos.set_working_dir(File.dirname(filename)) do
|
189
|
-
data = ERB.new(data).result(binding.set_variables(variables)) if data.is_printable?
|
189
|
+
data = ERB.new(data, trim_mode: "-").result(binding.set_variables(variables)) if data.is_printable?
|
190
190
|
end
|
191
191
|
rubys3_client.put_object(bucket: 'config', key: key, body: data)
|
192
192
|
end
|
data/lib/cosmos/models/model.rb
CHANGED
@@ -27,12 +27,16 @@ module Cosmos
|
|
27
27
|
attr_accessor :plugin
|
28
28
|
attr_accessor :scope
|
29
29
|
|
30
|
+
def self.store
|
31
|
+
Store
|
32
|
+
end
|
33
|
+
|
30
34
|
# NOTE: The following three methods must be reimplemented by Model subclasses
|
31
35
|
# without primary_key to support other class methods.
|
32
36
|
|
33
37
|
# @return [Hash|nil] Hash of this model or nil if name not found under primary_key
|
34
38
|
def self.get(primary_key, name:)
|
35
|
-
json =
|
39
|
+
json = store.hget(primary_key, name)
|
36
40
|
if json
|
37
41
|
return JSON.parse(json)
|
38
42
|
else
|
@@ -42,12 +46,12 @@ module Cosmos
|
|
42
46
|
|
43
47
|
# @return [Array<String>] All the names stored under the primary key
|
44
48
|
def self.names(primary_key)
|
45
|
-
|
49
|
+
store.hkeys(primary_key).sort
|
46
50
|
end
|
47
51
|
|
48
52
|
# @return [Array<Hash>] All the models (as Hash objects) stored under the primary key
|
49
53
|
def self.all(primary_key)
|
50
|
-
hash =
|
54
|
+
hash = store.hgetall(primary_key)
|
51
55
|
hash.each do |key, value|
|
52
56
|
hash[key] = JSON.parse(value)
|
53
57
|
end
|
@@ -117,16 +121,6 @@ module Cosmos
|
|
117
121
|
raise "must be implemented by subclass"
|
118
122
|
end
|
119
123
|
|
120
|
-
# TODO: Not used
|
121
|
-
# def self.from_config(primary_key, filename)
|
122
|
-
# model = nil
|
123
|
-
# parser = ConfigParser.new
|
124
|
-
# parser.parse_file(filename) do |keyword, parameters|
|
125
|
-
# model = self.handle_config(primary_key, parser, model, keyword, parameters)
|
126
|
-
# end
|
127
|
-
# model
|
128
|
-
# end
|
129
|
-
|
130
124
|
# Store the primary key and keyword arguments
|
131
125
|
def initialize(primary_key, **kw_args)
|
132
126
|
@primary_key = primary_key
|
@@ -140,7 +134,7 @@ module Cosmos
|
|
140
134
|
# to the JSON generated via calling as_json
|
141
135
|
def create(update: false, force: false)
|
142
136
|
unless force
|
143
|
-
existing =
|
137
|
+
existing = self.class.store.hget(@primary_key, @name)
|
144
138
|
if existing
|
145
139
|
raise "#{@primary_key}:#{@name} already exists at create" unless update
|
146
140
|
else
|
@@ -148,7 +142,7 @@ module Cosmos
|
|
148
142
|
end
|
149
143
|
end
|
150
144
|
@updated_at = Time.now.to_nsec_from_epoch
|
151
|
-
|
145
|
+
self.class.store.hset(@primary_key, @name, JSON.generate(self.as_json))
|
152
146
|
end
|
153
147
|
|
154
148
|
# Alias for create(update: true)
|
@@ -170,7 +164,7 @@ module Cosmos
|
|
170
164
|
# Delete the model from the Store
|
171
165
|
def destroy
|
172
166
|
undeploy()
|
173
|
-
|
167
|
+
self.class.store.hdel(@primary_key, @name)
|
174
168
|
end
|
175
169
|
|
176
170
|
# @return [Hash] JSON encoding of this model
|
@@ -186,4 +180,10 @@ module Cosmos
|
|
186
180
|
""
|
187
181
|
end
|
188
182
|
end
|
183
|
+
|
184
|
+
class EphemeralModel < Model
|
185
|
+
def self.store
|
186
|
+
EphemeralStore
|
187
|
+
end
|
188
|
+
end
|
189
189
|
end
|
@@ -0,0 +1,124 @@
|
|
1
|
+
# encoding: ascii-8bit
|
2
|
+
|
3
|
+
# Copyright 2022 Ball Aerospace & Technologies Corp.
|
4
|
+
# All Rights Reserved.
|
5
|
+
#
|
6
|
+
# This program is free software; you can modify and/or redistribute it
|
7
|
+
# under the terms of the GNU Affero General Public License
|
8
|
+
# as published by the Free Software Foundation; version 3 with
|
9
|
+
# attribution addendums as found in the LICENSE.txt
|
10
|
+
#
|
11
|
+
# This program is distributed in the hope that it will be useful,
|
12
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
13
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
14
|
+
# GNU Affero General Public License for more details.
|
15
|
+
#
|
16
|
+
# This program may also be used under the terms of a commercial or
|
17
|
+
# enterprise edition license of COSMOS if purchased from the
|
18
|
+
# copyright holder
|
19
|
+
|
20
|
+
# https://www.rubydoc.info/gems/redis/Redis/Commands/SortedSets
|
21
|
+
|
22
|
+
require 'cosmos/models/sorted_model'
|
23
|
+
|
24
|
+
module Cosmos
|
25
|
+
class NoteModel < SortedModel
|
26
|
+
NOTE_TYPE = 'note'.freeze
|
27
|
+
PRIMARY_KEY = '__NOTE'.freeze
|
28
|
+
|
29
|
+
def self.pk(scope)
|
30
|
+
"#{scope}#{PRIMARY_KEY}"
|
31
|
+
end
|
32
|
+
|
33
|
+
attr_reader :stop, :color, :description, :type
|
34
|
+
|
35
|
+
# @param [String] scope - Cosmos scope to track event to
|
36
|
+
# @param [Integer] start - start of the event in seconds from Epoch
|
37
|
+
# @param [Integer] stop - stop of the event in seconds from Epoch
|
38
|
+
# @param [String] color - The event color
|
39
|
+
# @param [String] description - What the event is about
|
40
|
+
def initialize(
|
41
|
+
scope:,
|
42
|
+
start:,
|
43
|
+
stop:,
|
44
|
+
color: nil,
|
45
|
+
description:,
|
46
|
+
type: NOTE_TYPE,
|
47
|
+
updated_at: 0
|
48
|
+
)
|
49
|
+
super(start: start, scope: scope, updated_at: updated_at)
|
50
|
+
@start = start
|
51
|
+
@stop = stop
|
52
|
+
@color = color
|
53
|
+
@description = description
|
54
|
+
@type = type # For the as_json, from_json round trip
|
55
|
+
end
|
56
|
+
|
57
|
+
# Validates the instance variables: @start, @stop, @color, @description
|
58
|
+
def validate(update: false)
|
59
|
+
validate_start(update: update)
|
60
|
+
validate_stop()
|
61
|
+
validate_color()
|
62
|
+
end
|
63
|
+
|
64
|
+
def validate_stop()
|
65
|
+
unless @stop.is_a?(Integer)
|
66
|
+
raise SortedInputError.new "stop must be integer: #{@stop}"
|
67
|
+
end
|
68
|
+
if @stop.to_i < @start
|
69
|
+
raise SortedInputError.new "stop: #{@stop} must be >= start: #{@start}"
|
70
|
+
end
|
71
|
+
@stop = @stop.to_i
|
72
|
+
end
|
73
|
+
|
74
|
+
def validate_color()
|
75
|
+
if @color.nil?
|
76
|
+
@color = '#%06x' % (rand * 0xffffff)
|
77
|
+
end
|
78
|
+
unless @color =~ /(#*)([0-9,a-f,A-f]{6})/
|
79
|
+
raise SortedInputError.new "invalid color, must be in hex format, e.g. #FF0000"
|
80
|
+
end
|
81
|
+
@color = "##{@color}" unless @color.start_with?('#')
|
82
|
+
end
|
83
|
+
|
84
|
+
# Update the Redis hash at primary_key based on the initial passed start
|
85
|
+
# The member is set to the JSON generated via calling as_json
|
86
|
+
def create(update: false)
|
87
|
+
validate(update: update)
|
88
|
+
@updated_at = Time.now.to_nsec_from_epoch
|
89
|
+
Store.zadd(@primary_key, @start, JSON.generate(as_json()))
|
90
|
+
if update
|
91
|
+
notify(kind: 'updated')
|
92
|
+
else
|
93
|
+
notify(kind: 'created')
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
# Update the Redis hash at primary_key
|
98
|
+
def update(start:, stop:, color:, description:)
|
99
|
+
@start = start
|
100
|
+
@stop = stop
|
101
|
+
@color = color
|
102
|
+
@description = description
|
103
|
+
create(update: true)
|
104
|
+
end
|
105
|
+
|
106
|
+
# @return [Hash] generated from the NoteModel
|
107
|
+
def as_json
|
108
|
+
return {
|
109
|
+
'scope' => @scope,
|
110
|
+
'start' => @start,
|
111
|
+
'stop' => @stop,
|
112
|
+
'color' => @color,
|
113
|
+
'description' => @description,
|
114
|
+
'type' => NOTE_TYPE,
|
115
|
+
'updated_at' => @updated_at,
|
116
|
+
}
|
117
|
+
end
|
118
|
+
|
119
|
+
# @return [String] string view of NoteModel
|
120
|
+
def to_s
|
121
|
+
return "<NoteModel s: #{@start}, x: #{@stop}, c: #{@color}, d: #{@description}>"
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
@@ -125,7 +125,7 @@ module Cosmos
|
|
125
125
|
gem_file_path = Cosmos::GemModel.get(temp_dir, name)
|
126
126
|
|
127
127
|
# Actually install the gem now (slow)
|
128
|
-
Cosmos::GemModel.install(gem_file_path)
|
128
|
+
Cosmos::GemModel.install(gem_file_path, scope: scope)
|
129
129
|
|
130
130
|
# Extract gem contents
|
131
131
|
gem_path = File.join(temp_dir, "gem")
|
@@ -20,6 +20,7 @@
|
|
20
20
|
require 'cosmos/version'
|
21
21
|
require 'cosmos/models/model'
|
22
22
|
require 'cosmos/models/microservice_model'
|
23
|
+
require 'cosmos/models/settings_model'
|
23
24
|
|
24
25
|
module Cosmos
|
25
26
|
class ScopeModel < Model
|
@@ -87,7 +88,7 @@ module Cosmos
|
|
87
88
|
Logger.info "Configured microservice #{microservice_name}"
|
88
89
|
|
89
90
|
# UNKNOWN CommandLog Microservice
|
90
|
-
|
91
|
+
Topic.initialize_streams(["#{@scope}__COMMAND__{UNKNOWN}__UNKNOWN"])
|
91
92
|
microservice_name = "#{@scope}__COMMANDLOG__UNKNOWN"
|
92
93
|
microservice = MicroserviceModel.new(
|
93
94
|
name: microservice_name,
|
@@ -107,7 +108,7 @@ module Cosmos
|
|
107
108
|
Logger.info "Configured microservice #{microservice_name}"
|
108
109
|
|
109
110
|
# UNKNOWN PacketLog Microservice
|
110
|
-
|
111
|
+
Topic.initialize_streams(["#{@scope}__TELEMETRY__{UNKNOWN}__UNKNOWN"])
|
111
112
|
microservice_name = "#{@scope}__PACKETLOG__UNKNOWN"
|
112
113
|
microservice = MicroserviceModel.new(
|
113
114
|
name: microservice_name,
|
@@ -139,10 +140,12 @@ module Cosmos
|
|
139
140
|
end
|
140
141
|
|
141
142
|
def seed_database
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
143
|
+
setting = SettingsModel.get(name: 'source_url')
|
144
|
+
SettingsModel.set({ name: 'source_url', data: 'https://github.com/BallAerospace/COSMOS' }, scope: @scope) unless setting
|
145
|
+
end
|
146
|
+
|
147
|
+
def self.limits_set(scope:)
|
148
|
+
Store.hget("#{scope}__cosmos_system", 'limits_set').intern
|
146
149
|
end
|
147
150
|
end
|
148
151
|
end
|