etna 0.1.37 → 0.1.38
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/etna/application.rb +73 -9
- data/lib/etna/clients/metis/workflows.rb +4 -3
- data/lib/etna/clients/metis/workflows/ingest_metis_data_workflow.rb +31 -0
- data/lib/etna/clients/metis/workflows/metis_upload_workflow.rb +1 -1
- data/lib/etna/controller.rb +18 -13
- data/lib/etna/filesystem.rb +63 -0
- data/lib/etna/logger.rb +4 -0
- data/lib/etna/route.rb +60 -0
- metadata +17 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: e7c665ee736c06bf1b566467985638cd41a5b3536f848fe2971174aab26ba3a4
|
|
4
|
+
data.tar.gz: 22bd58b6dd1a4902ee91f6588f53cf56a4bec9db5b42d9236ed9cd7c4310a2f0
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 04ad878b6f9593a06b65b7652ba3b6da9b0a2aa4de57981ef91247b19eb606e5018cc01e45d033ccdaf5b755cb92149e92a1d26218c47fce6c1a77085dce300e
|
|
7
|
+
data.tar.gz: 26134c1553f33e59e13f5ec737bd11b0bd8e62e1f54c3b1c5963560d1078cb00d5ce0255c01bddae86dc19f9bb38ae5a0dfc6b9d2845e955e55df0299ad1f456
|
data/lib/etna/application.rb
CHANGED
|
@@ -60,12 +60,61 @@ module Etna::Application
|
|
|
60
60
|
end
|
|
61
61
|
end
|
|
62
62
|
|
|
63
|
+
# This will cause metrics to persist to a file.
|
|
64
|
+
# NOTE -- /tmp/metrics.bin should be a persistent mount when using this.
|
|
65
|
+
# You will still need to export metrics in the text format for the node_exporter on the host machine to
|
|
66
|
+
# export them to prometheus. Ensure that the /tmp/metrics.bin is on a named volume or a bind mount, either is fine.
|
|
67
|
+
def enable_job_metrics!
|
|
68
|
+
require 'prometheus'
|
|
69
|
+
Prometheus::Client.config.data_store = Prometheus::Client::DataStores::DirectFileStore.new({
|
|
70
|
+
dir: "/tmp/metrics.bin"
|
|
71
|
+
})
|
|
72
|
+
end
|
|
73
|
+
|
|
63
74
|
def setup_yabeda
|
|
75
|
+
application = self.id
|
|
76
|
+
Yabeda.configure do
|
|
77
|
+
default_tag :application, application
|
|
78
|
+
|
|
79
|
+
group :etna do
|
|
80
|
+
histogram :response_time do
|
|
81
|
+
comment "Time spent by a controller returning a response"
|
|
82
|
+
unit :seconds
|
|
83
|
+
tags [:controller, :action, :user_hash, :project_name]
|
|
84
|
+
buckets [0.01, 0.1, 0.3, 0.5, 1, 5]
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
counter :visits do
|
|
88
|
+
comment "Counts visits to the controller"
|
|
89
|
+
tags [:controller, :action, :user_hash, :project_name]
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
counter :rollbar_errors do
|
|
93
|
+
comment "Counts errors detected by and sent to rollbar"
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
gauge :last_command_completion do
|
|
97
|
+
comment "Unix time of last time command was completed"
|
|
98
|
+
tags [:command, :status, :application]
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
histogram :command_runtime do
|
|
102
|
+
comment "Time spent processing a given command"
|
|
103
|
+
tags [:command, :status, :application]
|
|
104
|
+
unit :seconds
|
|
105
|
+
buckets [0.1, 1, 5, 60, 300, 1500]
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
|
|
64
110
|
Yabeda.configure!
|
|
65
111
|
end
|
|
66
112
|
|
|
113
|
+
# Writes all metrics currently gathered to a text format prometheus file. If /tmp/metrics.prom is bind mounted
|
|
114
|
+
# to the host directed bound to the node_exporter's file exporter directory, these will be exported to
|
|
115
|
+
# prometheus. Combine this enable_job_metrics! for maximum effect.
|
|
67
116
|
def write_job_metrics(name)
|
|
68
|
-
node_metrics_dir =
|
|
117
|
+
node_metrics_dir = "/tmp/metrics.prom"
|
|
69
118
|
::FileUtils.mkdir_p(node_metrics_dir)
|
|
70
119
|
|
|
71
120
|
tmp_file = ::File.join(node_metrics_dir, "#{name}.prom.$$")
|
|
@@ -126,17 +175,32 @@ module Etna::Application
|
|
|
126
175
|
end
|
|
127
176
|
|
|
128
177
|
def run_command(config, *args, &block)
|
|
178
|
+
application = self.id
|
|
179
|
+
status = 'success'
|
|
180
|
+
start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
129
181
|
cmd, cmd_args, cmd_kwds = find_command(*args)
|
|
130
|
-
cmd.setup(config)
|
|
131
182
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
183
|
+
begin
|
|
184
|
+
cmd.setup(config)
|
|
185
|
+
if block_given?
|
|
186
|
+
return unless yield [cmd, cmd_args]
|
|
187
|
+
end
|
|
135
188
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
189
|
+
cmd.execute(*cmd.fill_in_missing_params(cmd_args), **cmd_kwds)
|
|
190
|
+
rescue => e
|
|
191
|
+
Rollbar.error(e)
|
|
192
|
+
status = 'failed'
|
|
193
|
+
raise
|
|
194
|
+
ensure
|
|
195
|
+
if Yabeda.configured?
|
|
196
|
+
tags = { command: cmd.class.name, status: status, application: application }
|
|
197
|
+
dur = Process.clock_gettime(Process::CLOCK_MONOTONIC) - start
|
|
198
|
+
|
|
199
|
+
Yabeda.etna.last_command_completion.set(tags, Time.now.to_i)
|
|
200
|
+
Yabeda.etna.command_runtime.measure(tags, dur)
|
|
201
|
+
write_job_metrics("run_command")
|
|
202
|
+
end
|
|
203
|
+
end
|
|
140
204
|
end
|
|
141
205
|
end
|
|
142
206
|
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
-
require_relative
|
|
2
|
-
require_relative
|
|
3
|
-
require_relative
|
|
1
|
+
require_relative "./workflows/metis_download_workflow"
|
|
2
|
+
require_relative "./workflows/metis_upload_workflow"
|
|
3
|
+
require_relative "./workflows/sync_metis_data_workflow"
|
|
4
|
+
require_relative "./workflows/ingest_metis_data_workflow"
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
require "ostruct"
|
|
2
|
+
require "fileutils"
|
|
3
|
+
require "tempfile"
|
|
4
|
+
|
|
5
|
+
module Etna
|
|
6
|
+
module Clients
|
|
7
|
+
class Metis
|
|
8
|
+
class IngestMetisDataWorkflow < Struct.new(:metis_filesystem, :ingest_filesystem, :logger, keyword_init: true)
|
|
9
|
+
# Since we are doing manual triage of files,
|
|
10
|
+
# do not automatically copy directory trees.
|
|
11
|
+
# srcs must be a list of full paths to files.
|
|
12
|
+
def copy_files(srcs)
|
|
13
|
+
srcs.each do |src|
|
|
14
|
+
next unless ingest_filesystem.exist?(src)
|
|
15
|
+
|
|
16
|
+
logger&.info("Copying file #{src} (#{Etna::Formatting.as_size(ingest_filesystem.stat(src).size)})")
|
|
17
|
+
|
|
18
|
+
# For ingestion triage, just copy over the exact path + filename.
|
|
19
|
+
copy_file(dest: src, src: src)
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def copy_file(dest:, src:)
|
|
24
|
+
ingest_filesystem.with_readable(src, "r") do |file|
|
|
25
|
+
metis_filesystem.do_streaming_upload(file, dest, file.size)
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
data/lib/etna/controller.rb
CHANGED
|
@@ -19,25 +19,30 @@ module Etna
|
|
|
19
19
|
@logger.warn(request_msg(line))
|
|
20
20
|
end
|
|
21
21
|
|
|
22
|
+
def handle_error(e)
|
|
23
|
+
case e
|
|
24
|
+
when Etna::Error
|
|
25
|
+
Rollbar.error(e)
|
|
26
|
+
@logger.error(request_msg("Exiting with #{e.status}, #{e.message}"))
|
|
27
|
+
return failure(e.status, error: e.message)
|
|
28
|
+
else
|
|
29
|
+
Rollbar.error(e)
|
|
30
|
+
@logger.error(request_msg('Caught unspecified error'))
|
|
31
|
+
@logger.error(request_msg(e.message))
|
|
32
|
+
e.backtrace.each do |trace|
|
|
33
|
+
@logger.error(request_msg(trace))
|
|
34
|
+
end
|
|
35
|
+
return failure(500, error: 'Server error.')
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
22
39
|
def response(&block)
|
|
23
40
|
return instance_eval(&block) if block_given?
|
|
24
|
-
|
|
25
41
|
return send(@action) if @action
|
|
26
42
|
|
|
27
|
-
|
|
28
43
|
[501, {}, ['This controller is not implemented.']]
|
|
29
|
-
rescue Etna::Error => e
|
|
30
|
-
Rollbar.error(e)
|
|
31
|
-
@logger.error(request_msg("Exiting with #{e.status}, #{e.message}"))
|
|
32
|
-
return failure(e.status, error: e.message)
|
|
33
44
|
rescue Exception => e
|
|
34
|
-
|
|
35
|
-
@logger.error(request_msg('Caught unspecified error'))
|
|
36
|
-
@logger.error(request_msg(e.message))
|
|
37
|
-
e.backtrace.each do |trace|
|
|
38
|
-
@logger.error(request_msg(trace))
|
|
39
|
-
end
|
|
40
|
-
return failure(500, error: 'Server error.')
|
|
45
|
+
handle_error(e)
|
|
41
46
|
end
|
|
42
47
|
|
|
43
48
|
def require_params(*params)
|
data/lib/etna/filesystem.rb
CHANGED
|
@@ -3,6 +3,8 @@ require 'fileutils'
|
|
|
3
3
|
require 'open3'
|
|
4
4
|
require 'securerandom'
|
|
5
5
|
require 'concurrent-ruby'
|
|
6
|
+
require 'net/sftp'
|
|
7
|
+
require 'net/ssh'
|
|
6
8
|
|
|
7
9
|
module Etna
|
|
8
10
|
# A class that encapsulates opening / reading file system entries that abstracts normal file access in order
|
|
@@ -50,6 +52,11 @@ module Etna
|
|
|
50
52
|
::FileUtils.mv(src, dest)
|
|
51
53
|
end
|
|
52
54
|
|
|
55
|
+
def stat(src)
|
|
56
|
+
raise "stat not supported by #{self.class.name}" unless self.class == Filesystem
|
|
57
|
+
::File.stat(src)
|
|
58
|
+
end
|
|
59
|
+
|
|
53
60
|
class EmptyIO < StringIO
|
|
54
61
|
def write(*args)
|
|
55
62
|
# Do nothing -- always leave empty
|
|
@@ -369,7 +376,59 @@ module Etna
|
|
|
369
376
|
end
|
|
370
377
|
end
|
|
371
378
|
|
|
379
|
+
class SftpFilesystem < Filesystem
|
|
380
|
+
def initialize(host:, username:, password: nil, port: 22, **args)
|
|
381
|
+
@username = username
|
|
382
|
+
@password = password
|
|
383
|
+
@host = host
|
|
384
|
+
@port = port
|
|
385
|
+
end
|
|
386
|
+
|
|
387
|
+
def ssh
|
|
388
|
+
@ssh ||= Net::SSH.start(@host, @username, password: @password)
|
|
389
|
+
end
|
|
390
|
+
|
|
391
|
+
def sftp
|
|
392
|
+
@sftp ||= begin
|
|
393
|
+
conn = Net::SFTP::Session.new(ssh)
|
|
394
|
+
conn.loop { conn.opening? }
|
|
395
|
+
|
|
396
|
+
conn
|
|
397
|
+
end
|
|
398
|
+
end
|
|
399
|
+
|
|
400
|
+
def with_readable(src, opts = 'r', &block)
|
|
401
|
+
sftp.file.open(src, opts, &block)
|
|
402
|
+
end
|
|
403
|
+
|
|
404
|
+
def ls(dir)
|
|
405
|
+
sftp.dir.entries(dir)
|
|
406
|
+
end
|
|
407
|
+
|
|
408
|
+
def exist?(src)
|
|
409
|
+
begin
|
|
410
|
+
sftp.file.open(src)
|
|
411
|
+
rescue Net::SFTP::StatusException
|
|
412
|
+
return false
|
|
413
|
+
end
|
|
414
|
+
return true
|
|
415
|
+
end
|
|
416
|
+
|
|
417
|
+
def stat(src)
|
|
418
|
+
sftp.file.open(src).stat
|
|
419
|
+
end
|
|
420
|
+
end
|
|
421
|
+
|
|
372
422
|
class Mock < Filesystem
|
|
423
|
+
class MockStat
|
|
424
|
+
def initialize
|
|
425
|
+
end
|
|
426
|
+
|
|
427
|
+
def size
|
|
428
|
+
0
|
|
429
|
+
end
|
|
430
|
+
end
|
|
431
|
+
|
|
373
432
|
def initialize(&new_io)
|
|
374
433
|
@files = {}
|
|
375
434
|
@dirs = {}
|
|
@@ -438,6 +497,10 @@ module Etna
|
|
|
438
497
|
def exist?(src)
|
|
439
498
|
@files.include?(src) || @dirs.include?(src)
|
|
440
499
|
end
|
|
500
|
+
|
|
501
|
+
def stat(src)
|
|
502
|
+
@files[src].respond_to?(:stat) ? @files[src].stat : MockStat.new
|
|
503
|
+
end
|
|
441
504
|
end
|
|
442
505
|
end
|
|
443
506
|
end
|
data/lib/etna/logger.rb
CHANGED
data/lib/etna/route.rb
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
require 'digest'
|
|
2
|
+
require 'date'
|
|
3
|
+
|
|
1
4
|
module Etna
|
|
2
5
|
class Route
|
|
3
6
|
attr_reader :name
|
|
@@ -58,6 +61,63 @@ module Etna
|
|
|
58
61
|
end
|
|
59
62
|
|
|
60
63
|
def call(app, request)
|
|
64
|
+
start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
65
|
+
|
|
66
|
+
try_yabeda(request) do |tags|
|
|
67
|
+
Yabeda.etna.visits.increment(tags)
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
begin
|
|
71
|
+
process_call(app, request)
|
|
72
|
+
ensure
|
|
73
|
+
try_yabeda(request) do |tags|
|
|
74
|
+
dur = Process.clock_gettime(Process::CLOCK_MONOTONIC) - start
|
|
75
|
+
Yabeda.etna.response_time.measure(tags, dur)
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def hash_user_email(email)
|
|
81
|
+
secret = Etna::Application.instance.config(:user_hash_secret) || 'notsosecret'
|
|
82
|
+
digest = email + secret + Date.today.to_s
|
|
83
|
+
|
|
84
|
+
if @name
|
|
85
|
+
digest += @name.to_s
|
|
86
|
+
else
|
|
87
|
+
digest += @route.to_s
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
Digest::MD5.hexdigest(digest)
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def try_yabeda(request, &block)
|
|
94
|
+
if @action
|
|
95
|
+
controller, action = @action.split('#')
|
|
96
|
+
elsif @name
|
|
97
|
+
controller = "none"
|
|
98
|
+
action = @name
|
|
99
|
+
else
|
|
100
|
+
controller = "none"
|
|
101
|
+
action = @route
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
params = request.env['rack.request.params']
|
|
105
|
+
user = request.env['etna.user']
|
|
106
|
+
user_hash = user ? hash_user_email(user.email) : 'unknown'
|
|
107
|
+
project_name = "unknown"
|
|
108
|
+
|
|
109
|
+
if params && (params.include?(:project_name) || params.include?('project_name'))
|
|
110
|
+
project_name = params[:project_name] || params['project_name']
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
begin
|
|
114
|
+
block.call({ controller: controller, action: action, user_hash: user_hash, project_name: project_name })
|
|
115
|
+
rescue => e
|
|
116
|
+
raise e unless Etna::Application.instance.environment == :production
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def process_call(app, request)
|
|
61
121
|
update_params(request)
|
|
62
122
|
|
|
63
123
|
unless authorized?(request)
|
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.
|
|
4
|
+
version: 0.1.38
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Saurabh Asthana
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2021-
|
|
11
|
+
date: 2021-07-21 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: rack
|
|
@@ -94,6 +94,20 @@ dependencies:
|
|
|
94
94
|
- - ">="
|
|
95
95
|
- !ruby/object:Gem::Version
|
|
96
96
|
version: '0'
|
|
97
|
+
- !ruby/object:Gem::Dependency
|
|
98
|
+
name: net-sftp
|
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
|
100
|
+
requirements:
|
|
101
|
+
- - ">="
|
|
102
|
+
- !ruby/object:Gem::Version
|
|
103
|
+
version: 3.0.0
|
|
104
|
+
type: :runtime
|
|
105
|
+
prerelease: false
|
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
107
|
+
requirements:
|
|
108
|
+
- - ">="
|
|
109
|
+
- !ruby/object:Gem::Version
|
|
110
|
+
version: 3.0.0
|
|
97
111
|
description: See summary
|
|
98
112
|
email: Saurabh.Asthana@ucsf.edu
|
|
99
113
|
executables:
|
|
@@ -144,6 +158,7 @@ files:
|
|
|
144
158
|
- lib/etna/clients/metis/client.rb
|
|
145
159
|
- lib/etna/clients/metis/models.rb
|
|
146
160
|
- lib/etna/clients/metis/workflows.rb
|
|
161
|
+
- lib/etna/clients/metis/workflows/ingest_metis_data_workflow.rb
|
|
147
162
|
- lib/etna/clients/metis/workflows/metis_download_workflow.rb
|
|
148
163
|
- lib/etna/clients/metis/workflows/metis_upload_workflow.rb
|
|
149
164
|
- lib/etna/clients/metis/workflows/sync_metis_data_workflow.rb
|