etna 0.1.45 → 0.1.47

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 217bba41b5188f5d0e2349ba31dcb6f84477a3dcb36d04fd598977e814190ae8
4
- data.tar.gz: 758215abeb4ce074eb9dc6dd0313a3d271f2875f4071314cc7db30e9c4f19ad6
3
+ metadata.gz: eb695213d2c20c23465eb992a31fa8b0d30c80d78f0c83f672fbb45317ff33d4
4
+ data.tar.gz: efd86410cf85007a8bd1c957882a44ab5a0334fd0b54b87a8a140ff4203b74c5
5
5
  SHA512:
6
- metadata.gz: 52f1098ebc84e4fef32e8230246c09415adaf010bc748231fbcb3062f66aa7b43d2a34e78c99f43d3186f4d856d2f80447da72f15ccd47b0445936c5658f4d77
7
- data.tar.gz: 9c3feccc37d002e3493052aee8eccf3c6fedcfbf79a7251eda1a68d1324f4e05908e4dfc5d7c20996be34c448c739706e0e6a38fbc5a582918ebf0e4ea7859fc
6
+ metadata.gz: c288d8ecc5d518523a9ae7591c1edd8d85030ea84fe0b9f0f468d1f8610b27b7b3d4f4ca3fe28b0cf0f49c77baeedf168707193b95d2489729a6ab6bab966044
7
+ data.tar.gz: e987626f648fee08acd56c5615d63200b5d3813f511a8043d6afa9987f0bdad4fd72e485d25982702ebb748a00f0a7703372fdc45cf962550e171472b17e7ab8
data/lib/commands.rb CHANGED
@@ -206,7 +206,7 @@ class EtnaApp
206
206
  end
207
207
  end
208
208
 
209
- class Administrate
209
+ class Janus
210
210
  include Etna::CommandExecutor
211
211
 
212
212
  class Token
@@ -238,6 +238,59 @@ class EtnaApp
238
238
  end
239
239
  end
240
240
  end
241
+ end
242
+
243
+ class Magma
244
+ include Etna::CommandExecutor
245
+
246
+ class Materialize < Etna::Command
247
+ include WithEtnaClients
248
+
249
+ string_flags << "--project-name"
250
+ string_flags << "--log-file"
251
+ string_flags << "--log-level"
252
+ string_flags << "--concurrency"
253
+
254
+
255
+ def execute(project_name:, log_file:'/dev/stdout', log_level: ::Logger::INFO, concurrency: 1)
256
+ logger = Etna::Logger.new(log_file, 0, 1048576)
257
+
258
+ logger.level = log_level
259
+
260
+ workflow = Etna::Clients::Magma::MaterializeDataWorkflow.new(
261
+ model_attributes_mask: model_attribute_pairs(project_name),
262
+ record_names: 'all',
263
+ model_filters: {},
264
+ metis_client: metis_client,
265
+ magma_client: magma_client,
266
+ logger: logger,
267
+ project_name: project_name,
268
+ model_name: 'project', filesystem: filesystem,
269
+ concurrency: concurrency.to_i)
270
+
271
+ workflow.materialize_all(project_name)
272
+ logger.info("Done")
273
+ end
274
+
275
+ def model_attribute_pairs(project_name)
276
+ models = magma_client.retrieve(Etna::Clients::Magma::RetrievalRequest.new(
277
+ project_name: project_name,
278
+ model_name: 'all',
279
+ attribute_names: 'all',
280
+ record_names: []
281
+ )).models
282
+
283
+ result = models.model_keys.map do |model_name|
284
+ [ model_name, models.model(model_name).template.attributes.attribute_keys ]
285
+ end.to_h
286
+
287
+ result
288
+ end
289
+
290
+ def filesystem
291
+ @filesystem ||= Etna::Filesystem.new
292
+ end
293
+ end
241
294
 
242
295
  class Project
243
296
  include Etna::CommandExecutor
@@ -68,38 +68,43 @@ module Etna::Application
68
68
  [path.map(&:to_sym), YAML.load(File.read(value))]
69
69
  end
70
70
 
71
+ def controller_tags
72
+ [:controller, :action, :project_name, :user_hash]
73
+ end
74
+
71
75
  def setup_yabeda
72
76
  application = self.id
77
+ ctags = self.controller_tags
73
78
  Yabeda.configure do
74
79
  default_tag :application, application
80
+ default_tag :project_name, 'unknown'
81
+ default_tag :controller, 'none'
82
+ default_tag :action, 'none'
83
+ default_tag :user_hash, 'unknown'
75
84
 
76
85
  group :etna do
77
86
  histogram :response_time do
78
87
  comment "Time spent by a controller returning a response"
79
88
  unit :seconds
80
- tags [:controller, :action, :user_hash, :project_name]
89
+ tags ctags
90
+ buckets [0.01, 0.1, 0.3, 0.5, 1, 5]
91
+ end
92
+
93
+ histogram :perf do
94
+ comment "Time spent inside code path"
95
+ unit :seconds
96
+ tags ctags + [:class_name, :method_name]
81
97
  buckets [0.01, 0.1, 0.3, 0.5, 1, 5]
82
98
  end
83
99
 
84
100
  counter :visits do
85
101
  comment "Counts visits to the controller"
86
- tags [:controller, :action, :user_hash, :project_name]
102
+ tags ctags
87
103
  end
88
104
 
89
105
  counter :rollbar_errors do
90
106
  comment "Counts errors detected by and sent to rollbar"
91
- end
92
-
93
- gauge :last_command_completion do
94
- comment "Unix time of last time command was completed"
95
- tags [:command, :status, :application]
96
- end
97
-
98
- histogram :command_runtime do
99
- comment "Time spent processing a given command"
100
- tags [:command, :status, :application]
101
- unit :seconds
102
- buckets [0.1, 1, 5, 60, 300, 1500]
107
+ tags ctags
103
108
  end
104
109
  end
105
110
  end
@@ -176,9 +181,6 @@ module Etna::Application
176
181
  end
177
182
 
178
183
  def run_command(config, *args, &block)
179
- application = self.id
180
- status = 'success'
181
- start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
182
184
  cmd, cmd_args, cmd_kwds = find_command(*args)
183
185
 
184
186
  begin
@@ -190,18 +192,7 @@ module Etna::Application
190
192
  cmd.execute(*cmd.fill_in_missing_params(cmd_args), **cmd_kwds)
191
193
  rescue => e
192
194
  Rollbar.error(e)
193
- status = 'failed'
194
195
  raise
195
- ensure
196
- if defined?(Yabeda) && Yabeda.configured?
197
- tags = { command: cmd.class.name, status: status, application: application }
198
- dur = Process.clock_gettime(Process::CLOCK_MONOTONIC) - start
199
-
200
- Yabeda.etna.last_command_completion.set(tags, Time.now.to_i)
201
- Yabeda.etna.command_runtime.measure(tags, dur)
202
-
203
- write_job_metrics("run_command.#{cmd.class.name}")
204
- end
205
196
  end
206
197
  end
207
198
  end
data/lib/etna/auth.rb CHANGED
@@ -18,7 +18,7 @@ module Etna
18
18
  if [ approve_noauth(request), approve_hmac(request), approve_user(request) ].all?{|approved| !approved}
19
19
  return fail_or_redirect(request)
20
20
  end
21
-
21
+
22
22
  @app.call(request.env)
23
23
  end
24
24
 
@@ -97,7 +97,7 @@ module Etna
97
97
 
98
98
  return payload unless route
99
99
 
100
- begin
100
+ begin
101
101
  permissions = permissions(payload)
102
102
 
103
103
  janus.resource_projects(token).each do |resource_project|
@@ -22,6 +22,7 @@ module Etna
22
22
  :new_attribute_name,
23
23
  :attribute_type,
24
24
  :link_model_name,
25
+ :link_attribute_name,
25
26
  :description,
26
27
  :display_name,
27
28
  :format_hint,
@@ -114,7 +115,10 @@ module Etna
114
115
  if attribute.attribute_type == Etna::Clients::Magma::AttributeType::IDENTIFIER
115
116
  # Identifiers for models whose parent link type ends up being a table are non configurable, so we don't
116
117
  # want to include them in the CSV.
117
- if models.find_reciprocal(model: model, link_attribute_name: model.template.parent)&.attribute_type == Etna::Clients::Magma::AttributeType::TABLE
118
+ if models.find_reciprocal(
119
+ model: model,
120
+ link_attribute_name: model.name,
121
+ link_model: models.model(model.template.parent))&.attribute_type == Etna::Clients::Magma::AttributeType::TABLE
118
122
  return
119
123
  end
120
124
  else
@@ -137,6 +141,7 @@ module Etna
137
141
  attribute_name: attribute.name,
138
142
  attribute_type: attribute.attribute_type,
139
143
  link_model_name: attribute.link_model_name,
144
+ link_attribute_name: attribute.link_attribute_name,
140
145
  reciprocal_link_type: models.find_reciprocal(model: model, attribute: attribute)&.attribute_type,
141
146
  description: attribute.description,
142
147
  display_name: attribute.display_name,
@@ -175,7 +180,7 @@ module Etna
175
180
  # This should line up with the attribute names _on the model itself_.
176
181
  ATTRIBUTE_ROW_ENTRIES = [
177
182
  :attribute_type,
178
- :link_model_name, :description,
183
+ :link_model_name, :link_attribute_name, :description,
179
184
  :display_name, :format_hint,
180
185
  :restricted, :read_only,
181
186
  :validation, :attribute_group,
@@ -223,6 +228,7 @@ module Etna
223
228
  end
224
229
  rescue ImportError => e
225
230
  validation_err_block.call(e.message)
231
+ raise e
226
232
  end
227
233
 
228
234
 
@@ -255,7 +261,11 @@ module Etna
255
261
  end
256
262
 
257
263
  row_processor.process(:parent_link_type, :model_name, :parent_model_name) do |parent_link_type, template|
258
- reciprocal = models.find_reciprocal(model_name: template.name, link_attribute_name: template.parent)
264
+ reciprocal = models.find_reciprocal(
265
+ model_name: template.name,
266
+ link_attribute_name: template.name,
267
+ link_model: models.model(template.parent)
268
+ )
259
269
  if reciprocal && reciprocal.attribute_type.to_s != parent_link_type
260
270
  raise ImportError.new("Model #{template.name} was provided multiple parent_link_types: #{reciprocal.attribute_type} and #{parent_link_type}")
261
271
  end
@@ -333,7 +343,7 @@ module Etna
333
343
  att.send(:"#{prop_name}=", value)
334
344
 
335
345
  if att.attribute_type && att.link_model_name
336
- if att.attribute_type == Etna::Clients::Magma::AttributeType::LINK && models.find_reciprocal(model_name: template.name, attribute: att).nil?
346
+ if att.attribute_type == Etna::Clients::Magma::AttributeType::LINK && models.find_reciprocal(model_name: template.name, attribute: att, link_attribute_name: att.link_attribute_name).nil?
337
347
  models.build_model(att.link_model_name).build_template.build_attributes.build_attribute(template.name).tap do |rec_att|
338
348
  rec_att.attribute_name = rec_att.name = template.name
339
349
  rec_att.display_name = prettify(template.name)
@@ -239,6 +239,9 @@ module Etna
239
239
  link_model: self.model(attribute&.link_model_name)
240
240
  )
241
241
  return nil if model.nil? || model.name.nil?
242
+
243
+ return link_model&.template&.attributes&.all&.find { |a| a.name == link_attribute_name } if link_attribute_name
244
+
242
245
  link_model&.template&.attributes&.all&.find { |a| a.link_model_name == model.name }
243
246
  end
244
247
 
@@ -457,7 +460,7 @@ module Etna
457
460
  def is_edited?(other)
458
461
  # Don't just override == in case need to do a full comparison.
459
462
  editable_attribute_names = Attribute::EDITABLE_ATTRIBUTE_ATTRIBUTES.map(&:to_s)
460
-
463
+
461
464
  self_editable = raw.slice(*editable_attribute_names)
462
465
  other_editable = other.raw.slice(*editable_attribute_names)
463
466
 
@@ -505,6 +508,14 @@ module Etna
505
508
  @raw['attribute_type'] = val
506
509
  end
507
510
 
511
+ def link_attribute_name
512
+ @raw['link_attribute_name']
513
+ end
514
+
515
+ def link_attribute_name=(val)
516
+ @raw['link_attribute_name'] = val
517
+ end
518
+
508
519
  def link_model_name
509
520
  @raw['link_model_name']
510
521
  end
@@ -113,7 +113,11 @@ module Etna
113
113
  end
114
114
 
115
115
  def parent_reciprocal_attribute
116
- @models.find_reciprocal(model: @model, link_attribute_name: @model.template.parent)
116
+ @models.find_reciprocal(
117
+ model: @model,
118
+ link_attribute_name: @model.name,
119
+ link_model: @models.model(@model.template.parent)
120
+ )
117
121
  end
118
122
 
119
123
  def name
@@ -41,6 +41,7 @@ module Etna
41
41
 
42
42
  begin
43
43
  if (error = errors.pop(true))
44
+ logger&.error(error)
44
45
  raise error
45
46
  end
46
47
  rescue ThreadError
@@ -51,6 +52,7 @@ module Etna
51
52
  begin
52
53
  materialize_record(dest, template, document)
53
54
  rescue => e
55
+ logger&.error(e)
54
56
  errors << e
55
57
  ensure
56
58
  semaphore.release
@@ -62,6 +64,7 @@ module Etna
62
64
 
63
65
  begin
64
66
  if (error = errors.pop(true))
67
+ logger&.error(error)
65
68
  raise error
66
69
  end
67
70
  rescue ThreadError
@@ -105,6 +108,7 @@ module Etna
105
108
  end
106
109
 
107
110
  dest_file = File.join(dest_dir, metadata_file_name(record_name: record[template.identifier], record_model_name: template.name, ext: "_#{attr_name}_#{idx}#{File.extname(filename)}"))
111
+ filesystem.mkdir_p(File.dirname(dest_file))
108
112
  sync_metis_data_workflow.copy_file(dest: dest_file, url: url, stub: stub_files)
109
113
  record_to_serialize[attr_name] << {file: dest_file, original_filename: filename}
110
114
  end
@@ -131,7 +131,7 @@ module Etna
131
131
  unless attribute.attribute_type == AttributeType::PARENT
132
132
  if attribute.link_model_name
133
133
  ensure_model(attribute.link_model_name)
134
- ensure_model_link(model_name, attribute.link_model_name, attribute.attribute_name)
134
+ ensure_model_link(model_name, attribute.link_model_name, attribute.attribute_name, attribute.link_attribute_name)
135
135
  else
136
136
  ensure_model_attribute(model_name, attribute.attribute_name)
137
137
  end
@@ -144,26 +144,26 @@ module Etna
144
144
  end
145
145
  end
146
146
 
147
- def ensure_model_link(model_name, link_model_name, attribute_name)
147
+ def ensure_model_link(model_name, link_model_name, attribute_name, link_attribute_name)
148
148
  return unless (model = source_models.model(model_name))
149
149
  return unless (source_attribute = model.template.attributes.attribute(attribute_name))
150
150
 
151
151
  return unless (link_model = source_models.model(link_model_name))
152
- link_model_attributes = link_model.template.attributes
153
- reciprocal = link_model_attributes.all.find do |attr|
154
- attr.link_model_name == model_name
155
- end
152
+ return unless (reciprocal = link_model.template.attributes.attribute(link_attribute_name))
156
153
 
157
154
  target_model_name = target_of_source(model_name)
158
155
  target_link_model_name = target_of_source(link_model_name)
159
156
 
160
157
  target_attributes = target_models.model(target_model_name).template.attributes
161
158
  return if target_attributes.attribute_keys.include?(target_link_model_name)
159
+ return if target_attributes.attribute_keys.include?(reciprocal.attribute_name)
160
+
161
+ # skip non-links for circular references so they don't get added twice
162
+ return if link_model_name == model_name && reciprocal.attribute_type != 'link'
162
163
 
163
164
  add_link = AddLinkAction.new
164
165
  add_link.links << AddLinkDefinition.new(model_name: target_model_name, attribute_name: attribute_name, type: source_attribute.attribute_type)
165
166
  add_link.links << AddLinkDefinition.new(model_name: target_link_model_name, attribute_name: reciprocal.attribute_name, type: reciprocal.attribute_type)
166
-
167
167
  queue_update(add_link)
168
168
  end
169
169
 
@@ -6,6 +6,8 @@ module Etna
6
6
  module Clients
7
7
  class Metis
8
8
  class SyncMetisDataWorkflow < Struct.new(:metis_client, :filesystem, :project_name, :bucket_name, :logger, keyword_init: true)
9
+ DOWNLOAD_REGEX = /^https:\/\/[^\/]*\/(?<project_name>.*)\/download\/(?<bucket_name>.*)\/(?<file_path>[^\?]*).*$/
10
+
9
11
  def copy_directory(src, dest, root = dest)
10
12
  response = metis_client.list_folder(ListFolderRequest.new(project_name: project_name, bucket_name: bucket_name, folder_path: src))
11
13
 
@@ -20,15 +22,56 @@ module Etna
20
22
  end
21
23
 
22
24
  def copy_file(dest:, url:, stub: false)
25
+ url_match = DOWNLOAD_REGEX.match(url)
26
+
27
+ if filesystem.instance_of?(Etna::Filesystem::Metis) && !url_match.nil?
28
+ bucket_name = url_match[:bucket_name]
29
+ project_name = url_match[:project_name]
30
+ file_path = url_match[:file_path]
31
+
32
+ # ensure target parent directory exists
33
+ metis_client.ensure_parent_folder_exists(
34
+ project_name: filesystem.project_name,
35
+ bucket_name: filesystem.bucket_name,
36
+ path: dest
37
+ )
38
+
39
+ metis_client.copy_files(
40
+ Etna::Clients::Metis::CopyFilesRequest.new(
41
+ project_name: project_name,
42
+ revisions: [
43
+ Etna::Clients::Metis::CopyRevision.new(
44
+ source: "metis://#{project_name}/#{bucket_name}/#{file_path}",
45
+ dest: "metis://#{filesystem.project_name}/#{filesystem.bucket_name}/#{dest}",
46
+ )
47
+ ]
48
+ )
49
+ )
50
+
51
+ return
52
+ end
53
+
23
54
  metadata = metis_client.file_metadata(url)
24
55
  size = metadata[:size]
25
56
 
57
+ begin
58
+ if filesystem.exist?(dest) && filesystem.stat(dest).size == size
59
+ logger&.info "Already downloaded #{dest}"
60
+ return
61
+ end
62
+ rescue Etna::Filesystem::Error => e
63
+ unless e.message =~ /stat not supported/
64
+ raise e
65
+ end
66
+ end
67
+
26
68
  tmp_file = dest
27
69
  upload_timings = []
28
70
  upload_amount = 0
29
71
  last_rate = 0.00001
30
72
  remaining = size
31
73
 
74
+ logger&.info "Downloading #{dest} - #{Etna::Formatting.as_size(size)}"
32
75
  filesystem.with_writeable(tmp_file, "w", size_hint: size) do |io|
33
76
  if stub
34
77
  io.write("(stub) #{size} bytes")
@@ -55,7 +98,7 @@ module Etna
55
98
  rate = upload_amount / (end_time - start_time)
56
99
 
57
100
  if rate / last_rate > 1.3 || rate / last_rate < 0.7
58
- logger&.info("Uploading #{Etna::Formatting.as_size(rate)} per second, #{Etna::Formatting.as_size(remaining)} remaining")
101
+ logger&.debug("Uploading #{Etna::Formatting.as_size(rate)} per second, #{Etna::Formatting.as_size(remaining)} remaining")
59
102
 
60
103
  if rate == 0
61
104
  last_rate = 0.0001
@@ -64,7 +107,6 @@ module Etna
64
107
  end
65
108
  end
66
109
  end
67
-
68
110
  end
69
111
  end
70
112
  end
data/lib/etna/command.rb CHANGED
@@ -136,7 +136,7 @@ module Etna
136
136
  "<#{name}>..."
137
137
  when :keyrest
138
138
  "[flags...]"
139
- when :key
139
+ when :key, :keyreq
140
140
  flag = "--#{name.to_s.gsub('_', '-')}"
141
141
  if self.class.boolean_flags.include?(flag)
142
142
  "[#{flag}]"
@@ -133,7 +133,7 @@ MESSAGE_END
133
133
  end
134
134
 
135
135
  def config_hosts
136
- [:janus, :magma, :timur, :metis, :vulcan, :polyphemus].map do |host|
136
+ [:janus, :magma, :timur, :metis, :vulcan, :polyphemus, :gnomon].map do |host|
137
137
  [ :"#{host}_host", @server.send(:application).config(host)&.dig(:host) ]
138
138
  end.to_h.compact
139
139
  end
@@ -14,45 +14,48 @@ module Etna
14
14
  ::File.open(dest, opts, &block)
15
15
  end
16
16
 
17
+ class Error < StandardError
18
+ end
19
+
17
20
  def ls(dir)
18
- raise "ls not supported by #{self.class.name}" unless self.class == Filesystem
21
+ raise Etna::Filesystem::Error, "ls not supported by #{self.class.name}" unless self.class == Filesystem
19
22
  ::Dir.entries(dir).select { |p| !p.start_with?('.') }.map do |path|
20
23
  ::File.file?(::File.join(dir, path)) ? [:file, path] : [:dir, path]
21
24
  end
22
25
  end
23
26
 
24
27
  def with_readable(src, opts = 'r', &block)
25
- raise "with_readable not supported by #{self.class.name}" unless self.class == Filesystem
28
+ raise Etna::Filesystem::Error, "with_readable not supported by #{self.class.name}" unless self.class == Filesystem
26
29
  ::File.open(src, opts, &block)
27
30
  end
28
31
 
29
32
  def mkdir_p(dir)
30
- raise "mkdir_p not supported by #{self.class.name}" unless self.class == Filesystem
33
+ raise Etna::Filesystem::Error, "mkdir_p not supported by #{self.class.name}" unless self.class == Filesystem
31
34
  ::FileUtils.mkdir_p(dir)
32
35
  end
33
36
 
34
37
  def rm_rf(dir)
35
- raise "rm_rf not supported by #{self.class.name}" unless self.class == Filesystem
38
+ raise Etna::Filesystem::Error, "rm_rf not supported by #{self.class.name}" unless self.class == Filesystem
36
39
  ::FileUtils.rm_rf(dir)
37
40
  end
38
41
 
39
42
  def tmpdir
40
- raise "tmpdir not supported by #{self.class.name}" unless self.class == Filesystem
43
+ raise Etna::Filesystem::Error, "tmpdir not supported by #{self.class.name}" unless self.class == Filesystem
41
44
  ::Dir.mktmpdir
42
45
  end
43
46
 
44
47
  def exist?(src)
45
- raise "exist? not supported by #{self.class.name}" unless self.class == Filesystem
48
+ raise Etna::Filesystem::Error, "exist? not supported by #{self.class.name}" unless self.class == Filesystem
46
49
  ::File.exist?(src)
47
50
  end
48
51
 
49
52
  def mv(src, dest)
50
- raise "mv not supported by #{self.class.name}" unless self.class == Filesystem
53
+ raise Etna::Filesystem::Error, "mv not supported by #{self.class.name}" unless self.class == Filesystem
51
54
  ::FileUtils.mv(src, dest)
52
55
  end
53
56
 
54
57
  def stat(src)
55
- raise "stat not supported by #{self.class.name}" unless self.class == Filesystem
58
+ raise Etna::Filesystem::Error, "stat not supported by #{self.class.name}" unless self.class == Filesystem
56
59
  ::File.stat(src)
57
60
  end
58
61
 
@@ -66,7 +69,9 @@ module Etna
66
69
  def mkio(file, opts, size_hint: nil, &block)
67
70
  rd, wd = IO.pipe
68
71
 
69
- pid = spawn(*mkcommand(rd, wd, file, opts, size_hint: size_hint))
72
+ cmd = mkcommand(rd, wd, file, opts, size_hint: size_hint)
73
+ puts "in mkio: #{cmd}"
74
+ pid = spawn(*cmd)
70
75
  q = Queue.new
71
76
 
72
77
  closer = Thread.new do
@@ -184,9 +189,22 @@ module Etna
184
189
  cmd << @key_file
185
190
  end
186
191
 
192
+ cmd << "-O"
193
+ cmd << @port.to_s
194
+
187
195
  cmd << "-P"
188
196
  cmd << @port.to_s
189
197
 
198
+ cmd << "-v"
199
+ cmd << "-k"
200
+ cmd << "0"
201
+ cmd << "--file-manifest=text"
202
+ cmd << "--file-manifest-path=/var/tmp"
203
+ cmd << "--file-checksum=md5"
204
+ cmd << "-l"
205
+ cmd << "100m"
206
+ cmd << "--overwrite=always"
207
+
190
208
  remote_path = file
191
209
  # https://download.asperasoft.com/download/docs/entsrv/3.9.1/es_admin_linux/webhelp/index.html#dita/stdio_2.html
192
210
  local_path = "stdio://"
@@ -212,6 +230,7 @@ module Etna
212
230
  cmd << {in: rd}
213
231
  end
214
232
 
233
+ puts "end of mkcmd: #{cmd}"
215
234
  cmd
216
235
  end
217
236
  end
@@ -228,7 +247,8 @@ module Etna
228
247
  if e.instance_of?(String) && e.start_with?("stdio://")
229
248
  "stdio-tar://"
230
249
  elsif e == file
231
- ::File.dirname(file)
250
+ dir = ::File.dirname(file)
251
+ dir[0] == '/' ? dir : "/#{dir}"
232
252
  else
233
253
  e
234
254
  end
@@ -251,6 +271,8 @@ module Etna
251
271
  end
252
272
 
253
273
  class Metis < Filesystem
274
+ attr_reader :project_name, :bucket_name
275
+
254
276
  def initialize(metis_client:, project_name:, bucket_name:, root: '/', uuid: SecureRandom.uuid)
255
277
  @metis_client = metis_client
256
278
  @project_name = project_name
@@ -563,4 +585,4 @@ module Etna
563
585
  end
564
586
  end
565
587
  end
566
- end
588
+ end
@@ -0,0 +1,56 @@
1
+ module Etna
2
+ module Instrumentation
3
+ def self.included(cls)
4
+ cls.instance_eval do
5
+ def self.time_it(method_name, &metric_block)
6
+ orig_method_name = :"#{method_name}_without_time_it"
7
+ self.alias_method orig_method_name, method_name
8
+
9
+ self.define_method method_name do |*args|
10
+ time_it(orig_method_name, args, &metric_block)
11
+ end
12
+ end
13
+ end
14
+ end
15
+
16
+ def increment_it(&block)
17
+ if has_yabeda?
18
+ metric = yield
19
+ metric.increment({})
20
+ end
21
+ end
22
+
23
+ def time_it(method_name, args, &metric_block)
24
+ if has_yabeda?
25
+ start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
26
+ begin
27
+ return send(method_name, *args)
28
+ ensure
29
+ dur = Process.clock_gettime(Process::CLOCK_MONOTONIC) - start
30
+ if block_given?
31
+ metric = yield
32
+ tags = {}
33
+ else
34
+ tags = {class_name: self.class.name, method_name: method_name.to_s}
35
+ metric = Yabeda.etna.perf
36
+ end
37
+ metric.measure(tags, dur)
38
+ end
39
+ else
40
+ return send(method_name, *args, **kwds)
41
+ end
42
+ end
43
+
44
+ def with_yabeda_tags(tags, &block)
45
+ if has_yabeda?
46
+ Yabeda.with_tags(tags, &block)
47
+ else
48
+ yield
49
+ end
50
+ end
51
+
52
+ def has_yabeda?
53
+ defined?(Yabeda) && Yabeda.configured?
54
+ end
55
+ end
56
+ end
data/lib/etna/route.rb CHANGED
@@ -1,9 +1,13 @@
1
1
  require 'digest'
2
2
  require 'date'
3
+ require "addressable/uri"
3
4
  require_relative "./censor"
5
+ require_relative './instrumentation'
4
6
 
5
7
  module Etna
6
8
  class Route
9
+ include Etna::Instrumentation
10
+
7
11
  attr_reader :name
8
12
 
9
13
  def initialize(method, route, options, &block)
@@ -38,11 +42,11 @@ module Etna
38
42
 
39
43
  UNSAFE=/[^\-_.!~*'()a-zA-Z\d;\/?:@&=+$,]/
40
44
 
41
- def self.path(route, params=nil)
45
+ def self.path(route, params=nil, &block)
42
46
  if params
43
47
  PARAM_TYPES.reduce(route) do |path,pat|
44
48
  path.gsub(pat) do
45
- params[$1.to_sym].split('/').map { |c| URI.encode_www_form_component(c) }.join('/')
49
+ params[$1.to_sym].split('/').map { |c| block_given? ? yield(c) : Addressable::URI.normalized_encode(c) }.join('/')
46
50
  end
47
51
  end
48
52
  else
@@ -50,8 +54,8 @@ module Etna
50
54
  end
51
55
  end
52
56
 
53
- def path(params=nil)
54
- self.class.path(@route, params)
57
+ def path(params=nil, &block)
58
+ self.class.path(@route, params, &block)
55
59
  end
56
60
 
57
61
  def parts
@@ -64,19 +68,27 @@ module Etna
64
68
  end
65
69
 
66
70
  def call(app, request)
67
- start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
71
+ update_params(request)
68
72
 
69
- try_yabeda(request) do |tags|
70
- Yabeda.etna.visits.increment(tags)
73
+ tags = {}
74
+ if @action
75
+ tags[:controller], tags[:action] = @action.split('#')
76
+ elsif @name
77
+ tags[:action] = @name
78
+ else
79
+ tags[:action] = @route
71
80
  end
72
81
 
73
- begin
82
+ params = request.env['rack.request.params']
83
+ user = request.env['etna.user']
84
+ tags[:user_hash] = user ? hash_user_email(user.email) : 'unknown'
85
+ if params && (params.include?(:project_name) || params.include?('project_name'))
86
+ tags[:project_name] = params[:project_name] || params['project_name']
87
+ end
88
+
89
+ with_yabeda_tags(tags) do
90
+ increment_it { Yabeda.etna.visits }
74
91
  process_call(app, request)
75
- ensure
76
- try_yabeda(request) do |tags|
77
- dur = Process.clock_gettime(Process::CLOCK_MONOTONIC) - start
78
- Yabeda.etna.response_time.measure(tags, dur)
79
- end
80
92
  end
81
93
  end
82
94
 
@@ -93,36 +105,7 @@ module Etna
93
105
  Digest::MD5.hexdigest(digest)
94
106
  end
95
107
 
96
- def try_yabeda(request, &block)
97
- if @action
98
- controller, action = @action.split('#')
99
- elsif @name
100
- controller = "none"
101
- action = @name
102
- else
103
- controller = "none"
104
- action = @route
105
- end
106
-
107
- params = request.env['rack.request.params']
108
- user = request.env['etna.user']
109
- user_hash = user ? hash_user_email(user.email) : 'unknown'
110
- project_name = "unknown"
111
-
112
- if params && (params.include?(:project_name) || params.include?('project_name'))
113
- project_name = params[:project_name] || params['project_name']
114
- end
115
-
116
- begin
117
- block.call({ controller: controller, action: action, user_hash: user_hash, project_name: project_name })
118
- rescue => e
119
- raise e unless Etna::Application.instance.environment == :production
120
- end
121
- end
122
-
123
108
  def process_call(app, request)
124
- update_params(request)
125
-
126
109
  unless authorized?(request)
127
110
  if cc_available?(request)
128
111
  if request.content_type == 'application/json'
@@ -155,6 +138,8 @@ module Etna
155
138
  end
156
139
  end
157
140
 
141
+ time_it(:process_call) { Yabeda.etna.response_time }
142
+
158
143
  # the route does not require authorization
159
144
  def noauth?
160
145
  @auth && @auth[:noauth]
@@ -256,7 +241,7 @@ module Etna
256
241
  Hash[
257
242
  match.names.map(&:to_sym).zip(
258
243
  match.captures.map do |capture|
259
- capture.split('/').map {|c| URI.decode_www_form_component(c) }.join('/')
244
+ capture.split('/').map {|c| Addressable::URI.unencode_component(c) }.join('/')
260
245
  end
261
246
  )
262
247
  ]
data/lib/etna/server.rb CHANGED
@@ -3,7 +3,19 @@ module Etna
3
3
  class Server
4
4
  class << self
5
5
  def route(method, path, options={}, &block)
6
- @routes ||= []
6
+ # For healthchecks, set up servers
7
+ # with an OPTIONS route on /, with noauth
8
+ @routes ||= [
9
+ Etna::Route.new(
10
+ 'OPTIONS',
11
+ '/',
12
+ {
13
+ auth: {
14
+ noauth: true
15
+ }
16
+ }
17
+ )
18
+ ]
7
19
 
8
20
  @routes << Etna::Route.new(
9
21
  method,
@@ -54,6 +54,9 @@ module Etna
54
54
 
55
55
  return false unless token
56
56
 
57
+ # Useful for testing certain behavior
58
+ params = request.env["rack.request.params"]
59
+
57
60
  # Here we simply base64-encode our user hash and pass it through
58
61
  # In order to behave more like "real" tokens, we expect the user hash to be
59
62
  # in index 1 after splitting by ".".
@@ -63,7 +66,7 @@ module Etna
63
66
  request.env['etna.user'] = Etna::User.new(
64
67
  update_payload(payload, token, request),
65
68
  token
66
- )
69
+ ) unless !!params[:do_not_set_user]
67
70
  end
68
71
 
69
72
  def approve_hmac(request)
data/lib/etna/user.rb CHANGED
@@ -71,5 +71,9 @@ module Etna
71
71
  def active? project=nil
72
72
  permissions.keys.length > 0
73
73
  end
74
+
75
+ def display_name
76
+ [ email, name ].join('|')
77
+ end
74
78
  end
75
79
  end
data/lib/etna.rb CHANGED
@@ -28,6 +28,7 @@ require_relative './etna/metrics'
28
28
  require_relative './etna/remote'
29
29
  require_relative './etna/synchronize_db'
30
30
  require_relative './etna/janus_utils'
31
+ require_relative './etna/instrumentation'
31
32
 
32
33
  class EtnaApp
33
34
  include Etna::Application
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: etna
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.45
4
+ version: 0.1.47
5
5
  platform: ruby
6
6
  authors:
7
7
  - Saurabh Asthana
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-06-29 00:00:00.000000000 Z
11
+ date: 2023-01-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rack
@@ -200,6 +200,7 @@ files:
200
200
  - lib/etna/generate_autocompletion_script.rb
201
201
  - lib/etna/hmac.rb
202
202
  - lib/etna/injection.rb
203
+ - lib/etna/instrumentation.rb
203
204
  - lib/etna/janus_utils.rb
204
205
  - lib/etna/json_serializable_struct.rb
205
206
  - lib/etna/logger.rb
@@ -240,8 +241,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
240
241
  - !ruby/object:Gem::Version
241
242
  version: '0'
242
243
  requirements: []
243
- rubyforge_project:
244
- rubygems_version: 2.7.6.3
244
+ rubygems_version: 3.1.6
245
245
  signing_key:
246
246
  specification_version: 4
247
247
  summary: Base classes for Mount Etna applications