magellan-gcs-proxy 0.1.1 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.gitignore +2 -0
- data/.rubocop.yml +20 -0
- data/Gemfile +2 -0
- data/README.md +32 -12
- data/Rakefile +7 -3
- data/bin/console +3 -3
- data/example/Dockerfile +19 -0
- data/example/Gemfile +11 -0
- data/example/Gemfile.lock +129 -0
- data/example/README.md +73 -0
- data/example/VERSION +1 -0
- data/example/app.sh +19 -0
- data/example/config.yml +25 -0
- data/exe/magellan-gcs-proxy +1 -1
- data/exe/magellan-gcs-proxy-dev-progress-listener +14 -0
- data/exe/magellan-gcs-proxy-dev-setup +53 -0
- data/lib/magellan/gcs/proxy/cli.rb +24 -39
- data/lib/magellan/gcs/proxy/composite_logger.rb +37 -0
- data/lib/magellan/gcs/proxy/config.rb +44 -0
- data/lib/magellan/gcs/proxy/context.rb +33 -32
- data/lib/magellan/gcs/proxy/expand_variable.rb +20 -29
- data/lib/magellan/gcs/proxy/gcp.rb +10 -4
- data/lib/magellan/gcs/proxy/log.rb +67 -1
- data/lib/magellan/gcs/proxy/message_wrapper.rb +34 -5
- data/lib/magellan/gcs/proxy/progress_notification.rb +73 -0
- data/lib/magellan/gcs/proxy/progress_notifier_adapter.rb +24 -0
- data/lib/magellan/gcs/proxy/pubsub_progress_notifier.rb +25 -0
- data/lib/magellan/gcs/proxy/pubsub_sustainer.rb +54 -0
- data/lib/magellan/gcs/proxy/version.rb +1 -1
- data/lib/magellan/gcs/proxy.rb +20 -4
- data/magellan-gcs-proxy.gemspec +17 -15
- metadata +48 -2
@@ -0,0 +1,44 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
require 'magellan/gcs/proxy'
|
3
|
+
|
4
|
+
require 'yaml'
|
5
|
+
require 'erb'
|
6
|
+
|
7
|
+
module Magellan
|
8
|
+
module Gcs
|
9
|
+
module Proxy
|
10
|
+
class Config
|
11
|
+
attr_reader :path
|
12
|
+
def initialize(path = './config.yml')
|
13
|
+
@path = path
|
14
|
+
end
|
15
|
+
|
16
|
+
def data
|
17
|
+
@data ||= load_file
|
18
|
+
end
|
19
|
+
|
20
|
+
def reset
|
21
|
+
@data = nil
|
22
|
+
end
|
23
|
+
|
24
|
+
def load_file
|
25
|
+
erb = ERB.new(File.read(path), nil, '-')
|
26
|
+
erb.filename = path
|
27
|
+
t = erb.result
|
28
|
+
puts '=' * 100
|
29
|
+
puts t
|
30
|
+
puts '-' * 100
|
31
|
+
YAML.load(t)
|
32
|
+
end
|
33
|
+
|
34
|
+
def [](key)
|
35
|
+
data[key.to_s]
|
36
|
+
end
|
37
|
+
|
38
|
+
def verbose?
|
39
|
+
ENV['VERBOSE'] =~ /true|yes|on|1/i
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
# coding: utf-8
|
2
|
-
require
|
3
|
-
require
|
2
|
+
require 'magellan/gcs/proxy'
|
3
|
+
require 'magellan/gcs/proxy/log'
|
4
4
|
|
5
5
|
require 'fileutils'
|
6
6
|
require 'uri'
|
@@ -10,30 +10,27 @@ module Magellan
|
|
10
10
|
module Proxy
|
11
11
|
class Context
|
12
12
|
include Log
|
13
|
+
include Proxy::ProgressNotification
|
13
14
|
|
14
|
-
attr_reader :workspace, :remote_download_files
|
15
|
-
def initialize(
|
16
|
-
@
|
17
|
-
@remote_download_files =
|
15
|
+
attr_reader :message, :workspace, :remote_download_files
|
16
|
+
def initialize(message)
|
17
|
+
@message = message
|
18
|
+
@remote_download_files = parse_json(message.attributes['download_files'])
|
19
|
+
@workspace = nil
|
18
20
|
end
|
19
21
|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
def [](key)
|
29
|
-
case key.to_sym
|
30
|
-
when *KEYS then send(key)
|
31
|
-
else nil
|
22
|
+
def setup
|
23
|
+
Dir.mktmpdir 'workspace' do |dir|
|
24
|
+
@workspace = dir
|
25
|
+
setup_dirs
|
26
|
+
PubsubSustainer.run(message) do
|
27
|
+
yield
|
28
|
+
end
|
32
29
|
end
|
33
30
|
end
|
34
31
|
|
35
|
-
def
|
36
|
-
|
32
|
+
def ltsv(hash)
|
33
|
+
hash.map { |k, v| "#{k}:#{v}" }.join("\t")
|
37
34
|
end
|
38
35
|
|
39
36
|
def downloads_dir
|
@@ -47,16 +44,12 @@ module Magellan
|
|
47
44
|
def local_download_files
|
48
45
|
@local_download_files ||= build_local_files_obj(remote_download_files, download_mapping)
|
49
46
|
end
|
50
|
-
|
47
|
+
alias download_files local_download_files
|
51
48
|
|
52
49
|
def uploads_dir
|
53
50
|
File.join(workspace, 'uploads')
|
54
51
|
end
|
55
52
|
|
56
|
-
def setup
|
57
|
-
setup_dirs
|
58
|
-
end
|
59
|
-
|
60
53
|
def download
|
61
54
|
download_mapping.each do |url, path|
|
62
55
|
FileUtils.mkdir_p File.dirname(path)
|
@@ -64,7 +57,7 @@ module Magellan
|
|
64
57
|
uri = parse_uri(url)
|
65
58
|
@last_bucket_name = uri.host
|
66
59
|
bucket = GCP.storage.bucket(@last_bucket_name)
|
67
|
-
file = bucket.file uri.path.sub(
|
60
|
+
file = bucket.file uri.path.sub(%r{\A/}, '')
|
68
61
|
file.download(path)
|
69
62
|
logger.info("Download OK: #{url} to #{path}")
|
70
63
|
end
|
@@ -73,7 +66,7 @@ module Magellan
|
|
73
66
|
def upload
|
74
67
|
Dir.chdir(uploads_dir) do
|
75
68
|
Dir.glob('**/*') do |path|
|
76
|
-
next if
|
69
|
+
next if directory?(path)
|
77
70
|
url = "gs://#{@last_bucket_name}/#{path}"
|
78
71
|
logger.info("Uploading: #{path} to #{url}")
|
79
72
|
bucket = GCP.storage.bucket(@last_bucket_name)
|
@@ -83,8 +76,12 @@ module Magellan
|
|
83
76
|
end
|
84
77
|
end
|
85
78
|
|
79
|
+
def directory?(path)
|
80
|
+
File.directory?(path)
|
81
|
+
end
|
82
|
+
|
86
83
|
def setup_dirs
|
87
|
-
[:downloads_dir, :uploads_dir].each{|k|
|
84
|
+
[:downloads_dir, :uploads_dir].each { |k| FileUtils.mkdir_p(send(k)) }
|
88
85
|
end
|
89
86
|
|
90
87
|
def build_mapping(base_dir, obj)
|
@@ -98,11 +95,16 @@ module Magellan
|
|
98
95
|
case obj
|
99
96
|
when nil then []
|
100
97
|
when Hash then flatten_values(obj.values)
|
101
|
-
when Array then obj.map{|i| flatten_values(i) }
|
98
|
+
when Array then obj.map { |i| flatten_values(i) }
|
102
99
|
else obj
|
103
100
|
end
|
104
101
|
end
|
105
102
|
|
103
|
+
def parse_json(str)
|
104
|
+
return nil if str.nil? || str.empty?
|
105
|
+
JSON.parse(str)
|
106
|
+
end
|
107
|
+
|
106
108
|
def parse_uri(str)
|
107
109
|
uri = URI.parse(str)
|
108
110
|
raise "Unsupported scheme #{uri.scheme.inspect} of #{str}" unless uri.scheme == 'gs'
|
@@ -111,13 +113,12 @@ module Magellan
|
|
111
113
|
|
112
114
|
def build_local_files_obj(obj, mapping)
|
113
115
|
case obj
|
114
|
-
when Hash then obj.each_with_object({}){|(k,v), d| d[k] = build_local_files_obj(v, mapping)}
|
115
|
-
when Array then obj.map{|i| build_local_files_obj(i, mapping)}
|
116
|
+
when Hash then obj.each_with_object({}) { |(k, v), d| d[k] = build_local_files_obj(v, mapping) }
|
117
|
+
when Array then obj.map { |i| build_local_files_obj(i, mapping) }
|
116
118
|
when String then mapping[obj]
|
117
119
|
else obj
|
118
120
|
end
|
119
121
|
end
|
120
|
-
|
121
122
|
end
|
122
123
|
end
|
123
124
|
end
|
@@ -1,53 +1,45 @@
|
|
1
|
-
require
|
1
|
+
require 'magellan/gcs/proxy'
|
2
2
|
|
3
3
|
module Magellan
|
4
4
|
module Gcs
|
5
5
|
module Proxy
|
6
6
|
module ExpandVariable
|
7
|
-
|
8
7
|
class InvalidReferenceError < StandardError
|
9
8
|
end
|
10
9
|
|
11
10
|
module_function
|
12
11
|
|
13
12
|
def dig_variables(variable_ref, data)
|
14
|
-
vars = variable_ref.split(
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
if tmp.size > v
|
31
|
-
tmp[v]
|
32
|
-
else
|
33
|
-
raise InvalidReferenceError, variable_ref
|
34
|
-
end
|
35
|
-
else
|
36
|
-
raise InvalidReferenceError, variable_ref
|
37
|
-
end
|
13
|
+
vars = variable_ref.split('.').map { |i| /\A\d+\z/ =~ i ? i.to_i : i }
|
14
|
+
vars.inject(data) do |tmp, v|
|
15
|
+
dig_variable(tmp, v, variable_ref)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def dig_variable(tmp, v, variable_ref)
|
20
|
+
case v
|
21
|
+
when String
|
22
|
+
if tmp.respond_to?(:[]) && tmp.respond_to?(:include?)
|
23
|
+
return tmp[v] if tmp.include?(v)
|
24
|
+
end
|
25
|
+
when Integer
|
26
|
+
case tmp
|
27
|
+
when Array
|
28
|
+
return tmp[v] if tmp.size > v
|
38
29
|
end
|
39
30
|
end
|
31
|
+
raise InvalidReferenceError, variable_ref
|
40
32
|
end
|
41
33
|
|
42
34
|
def expand_variables(str, data, quote_string: false)
|
43
35
|
data ||= {}
|
44
|
-
str.gsub(/\%\{\s*([\w.]+)\s*\}/) do |
|
36
|
+
str.gsub(/\%\{\s*([\w.]+)\s*\}/) do |_m|
|
45
37
|
var = Regexp.last_match(1)
|
46
38
|
value =
|
47
39
|
begin
|
48
40
|
dig_variables(var, data)
|
49
41
|
rescue InvalidReferenceError
|
50
|
-
|
42
|
+
''
|
51
43
|
end
|
52
44
|
|
53
45
|
case value
|
@@ -58,7 +50,6 @@ module Magellan
|
|
58
50
|
end
|
59
51
|
end
|
60
52
|
end
|
61
|
-
|
62
53
|
end
|
63
54
|
end
|
64
55
|
end
|
@@ -1,8 +1,10 @@
|
|
1
1
|
# coding: utf-8
|
2
2
|
require 'magellan/gcs/proxy'
|
3
3
|
|
4
|
-
require
|
5
|
-
require
|
4
|
+
require 'google/cloud/logging'
|
5
|
+
require 'google/cloud/logging/version'
|
6
|
+
require 'google/cloud/pubsub'
|
7
|
+
require 'google/cloud/storage'
|
6
8
|
require 'net/http'
|
7
9
|
|
8
10
|
module Magellan
|
@@ -24,7 +26,7 @@ module Magellan
|
|
24
26
|
|
25
27
|
METADATA_HOST = 'metadata.google.internal'.freeze
|
26
28
|
METADATA_PATH_BASE = '/computeMetadata/v1/'.freeze
|
27
|
-
METADATA_HEADER = {
|
29
|
+
METADATA_HEADER = { 'Metadata-Flavor' => 'Google' }.freeze
|
28
30
|
|
29
31
|
def retrieve_metadata(key)
|
30
32
|
http = Net::HTTP.new(METADATA_HOST)
|
@@ -51,8 +53,12 @@ module Magellan
|
|
51
53
|
@subscription
|
52
54
|
end
|
53
55
|
|
56
|
+
def logging
|
57
|
+
@logging ||= Google::Cloud::Logging.new(project: project_id)
|
58
|
+
end
|
59
|
+
|
54
60
|
def reset
|
55
|
-
instance_variables.each {|ivar| instance_variable_set(ivar, nil)}
|
61
|
+
instance_variables.each { |ivar| instance_variable_set(ivar, nil) }
|
56
62
|
end
|
57
63
|
end
|
58
64
|
end
|
@@ -1,10 +1,76 @@
|
|
1
|
+
# coding: utf-8
|
1
2
|
require 'logger'
|
3
|
+
|
2
4
|
module Magellan
|
3
5
|
module Gcs
|
4
6
|
module Proxy
|
5
7
|
module Log
|
8
|
+
module_function
|
9
|
+
|
10
|
+
def verbose(msg)
|
11
|
+
logger.debug(msg) if Proxy.config.verbose?
|
12
|
+
end
|
13
|
+
|
6
14
|
def logger
|
7
|
-
@logger ||=
|
15
|
+
@logger ||= build_logger(loggers)
|
16
|
+
end
|
17
|
+
|
18
|
+
def build_logger(loggers)
|
19
|
+
case loggers.length
|
20
|
+
when 0 then Logger.new('/dev/null')
|
21
|
+
when 1 then loggers.first
|
22
|
+
else CompositeLogger.new(loggers)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def loggers
|
27
|
+
@loggers ||= build_loggers
|
28
|
+
end
|
29
|
+
|
30
|
+
def build_loggers
|
31
|
+
(Proxy.config[:loggers] || []).map do |logger_def|
|
32
|
+
config = logger_def.dup
|
33
|
+
type = config.delete('type')
|
34
|
+
case type
|
35
|
+
when 'stdout' then Logger.new($stdout)
|
36
|
+
when 'stderr' then Logger.new($stderr)
|
37
|
+
when 'cloud_logging' then build_cloud_logging_logger(config)
|
38
|
+
else raise "Unsupported logger type: #{type} with #{config.inspect}"
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
CLOUD_LOGGING_RESOURCE_KEYS = [
|
44
|
+
:project_id,
|
45
|
+
:cluster_name,
|
46
|
+
:namespace_id,
|
47
|
+
:instance_id,
|
48
|
+
:pod_id,
|
49
|
+
:container_name,
|
50
|
+
:zone,
|
51
|
+
].freeze
|
52
|
+
|
53
|
+
def build_cloud_logging_logger(config)
|
54
|
+
log_name = config['log_name']
|
55
|
+
return nil unless log_name
|
56
|
+
# container
|
57
|
+
# GKE Container A Google Container Engine (GKE) container instance.
|
58
|
+
# project_id: The identifier of the GCP project associated with this resource (e.g., my-project).
|
59
|
+
# cluster_name: An immutable name for the cluster the container is running in.
|
60
|
+
# namespace_id: Immutable ID of the cluster namespace the container is running in.
|
61
|
+
# instance_id: Immutable ID of the GCE instance the container is running in.
|
62
|
+
# pod_id: Immutable ID of the pod the container is running in.
|
63
|
+
# container_name: Immutable name of the container.
|
64
|
+
# zone: The GCE zone in which the instance is running.
|
65
|
+
# See https://cloud.google.com/logging/docs/api/v2/resource-list
|
66
|
+
options = CLOUD_LOGGING_RESOURCE_KEYS.each_with_object({}) do |key, d|
|
67
|
+
if v = ENV["BLOCKS_BATCH_CLOUD_LOGGING_#{key.to_s.upcase}"]
|
68
|
+
d[key] = v
|
69
|
+
end
|
70
|
+
end
|
71
|
+
resource = GCP.logging.resource 'container', options
|
72
|
+
Google::Cloud::Logging::Logger.new GCP.logging, log_name, resource,
|
73
|
+
magellan_gcs_proxy: Magellan::Gcs::Proxy::VERSION
|
8
74
|
end
|
9
75
|
end
|
10
76
|
end
|
@@ -1,12 +1,13 @@
|
|
1
|
-
require
|
1
|
+
require 'magellan/gcs/proxy'
|
2
2
|
|
3
3
|
module Magellan
|
4
4
|
module Gcs
|
5
5
|
module Proxy
|
6
6
|
class MessageWrapper
|
7
7
|
attr_reader :msg, :context
|
8
|
-
def initialize(
|
9
|
-
@msg
|
8
|
+
def initialize(context)
|
9
|
+
@msg = context.message
|
10
|
+
@context = ContextAccessor.new(context)
|
10
11
|
end
|
11
12
|
|
12
13
|
def [](key)
|
@@ -26,6 +27,31 @@ module Magellan
|
|
26
27
|
Attrs.new(msg.attributes)
|
27
28
|
end
|
28
29
|
|
30
|
+
class ContextAccessor
|
31
|
+
attr_accessor :context
|
32
|
+
def initialize(context)
|
33
|
+
@context = context
|
34
|
+
end
|
35
|
+
|
36
|
+
KEYS = [
|
37
|
+
:workspace,
|
38
|
+
:downloads_dir, :uploads_dir,
|
39
|
+
:download_files,
|
40
|
+
:local_download_files,
|
41
|
+
:remote_download_files
|
42
|
+
].freeze
|
43
|
+
|
44
|
+
def [](key)
|
45
|
+
case key.to_sym
|
46
|
+
when *KEYS then context.send(key)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def include?(key)
|
51
|
+
KEYS.include?(key)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
29
55
|
class Attrs
|
30
56
|
attr_reader :data
|
31
57
|
def initialize(data)
|
@@ -35,7 +61,11 @@ module Magellan
|
|
35
61
|
def [](key)
|
36
62
|
value = data[key]
|
37
63
|
if value.is_a?(String) && value =~ /\A\[.*\]\z|\A\{.*\}\z/
|
38
|
-
|
64
|
+
begin
|
65
|
+
JSON.parse(value)
|
66
|
+
rescue
|
67
|
+
value
|
68
|
+
end
|
39
69
|
else
|
40
70
|
value
|
41
71
|
end
|
@@ -44,7 +74,6 @@ module Magellan
|
|
44
74
|
def include?(key)
|
45
75
|
data.include?(key) || data.include?(key.to_sym)
|
46
76
|
end
|
47
|
-
|
48
77
|
end
|
49
78
|
end
|
50
79
|
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
require 'magellan/gcs/proxy'
|
2
|
+
|
3
|
+
module Magellan
|
4
|
+
module Gcs
|
5
|
+
module Proxy
|
6
|
+
module ProgressNotification
|
7
|
+
include Log
|
8
|
+
|
9
|
+
def process_with_notification(numbers, total, base_message, main = nil)
|
10
|
+
start_no, complete_no, error_no = *numbers
|
11
|
+
notify(start_no, total, "#{base_message} starting")
|
12
|
+
begin
|
13
|
+
main ? main.call(self) : yield(self)
|
14
|
+
rescue => e
|
15
|
+
notify(error_no, total, "#{base_message} error: [#{e.class}] #{e.message}", severity: :error)
|
16
|
+
raise e unless main
|
17
|
+
else
|
18
|
+
notify(complete_no, total, "#{base_message} completed")
|
19
|
+
yield(self) if main
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def notify(progress, total, data, severity: :info)
|
24
|
+
notifier.notify(severity, message, data, progress: progress, total: total)
|
25
|
+
end
|
26
|
+
|
27
|
+
def notifier
|
28
|
+
@notifier ||= build_notifier
|
29
|
+
end
|
30
|
+
|
31
|
+
# Build the Notifier object like these...
|
32
|
+
#
|
33
|
+
# CompositeNotifier
|
34
|
+
# @notifiers:
|
35
|
+
# PubsubProgressNotifier
|
36
|
+
# ProgressNotifierAdapter
|
37
|
+
# @logger:
|
38
|
+
# CompositeLogger
|
39
|
+
# @loggers:
|
40
|
+
# Logger
|
41
|
+
# Google::Cloud::Logging::Logger
|
42
|
+
def build_notifier
|
43
|
+
notifiers = []
|
44
|
+
if c = Proxy.config[:progress_notification]
|
45
|
+
notifiers << PubsubProgressNotifier.new(c['topic'])
|
46
|
+
end
|
47
|
+
notifiers << ProgressNotifierAdapter.new(logger)
|
48
|
+
case notifiers.length
|
49
|
+
when 1 then notifiers.first
|
50
|
+
else CompositeNotifier.new(notifiers)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
class CompositeNotifier
|
55
|
+
attr_reader :notifiers
|
56
|
+
def initialize(notifiers)
|
57
|
+
@notifiers = notifiers
|
58
|
+
end
|
59
|
+
|
60
|
+
def notify(*args, &block)
|
61
|
+
notifiers.each do |notifier|
|
62
|
+
begin
|
63
|
+
notifier.notify(*args, &block)
|
64
|
+
rescue => e
|
65
|
+
$stderr.puts("[#{e.class}] #{e.message}")
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'magellan/gcs/proxy'
|
2
|
+
|
3
|
+
module Magellan
|
4
|
+
module Gcs
|
5
|
+
module Proxy
|
6
|
+
class ProgressNotifierAdapter
|
7
|
+
attr_reader :logger
|
8
|
+
def initialize(logger)
|
9
|
+
@logger = logger
|
10
|
+
end
|
11
|
+
|
12
|
+
def ltsv(hash)
|
13
|
+
hash.map { |k, v| "#{k}:#{v}" }.join("\t")
|
14
|
+
end
|
15
|
+
|
16
|
+
def notify(severity, job_message, data, attrs)
|
17
|
+
d = { job_message_id: job_message.message_id }.merge(attrs)
|
18
|
+
d[:data] = data # Show data at the end of string
|
19
|
+
logger.send(severity, ltsv(d))
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'magellan/gcs/proxy'
|
2
|
+
|
3
|
+
require 'logger'
|
4
|
+
require 'json'
|
5
|
+
|
6
|
+
module Magellan
|
7
|
+
module Gcs
|
8
|
+
module Proxy
|
9
|
+
class PubsubProgressNotifier
|
10
|
+
attr_reader :topic_name
|
11
|
+
def initialize(topic_name)
|
12
|
+
@topic_name = topic_name
|
13
|
+
end
|
14
|
+
|
15
|
+
def topic
|
16
|
+
@topic ||= GCP.pubsub.topic(topic_name)
|
17
|
+
end
|
18
|
+
|
19
|
+
def notify(severity, job_message, data, attrs)
|
20
|
+
topic.publish data, { level: severity, job_message_id: job_message.message_id }.merge(attrs)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'magellan/gcs/proxy'
|
2
|
+
|
3
|
+
require 'logger'
|
4
|
+
require 'json'
|
5
|
+
|
6
|
+
module Magellan
|
7
|
+
module Gcs
|
8
|
+
module Proxy
|
9
|
+
class PubsubSustainer
|
10
|
+
include Log
|
11
|
+
|
12
|
+
class << self
|
13
|
+
def run(message)
|
14
|
+
raise "#{name}.run requires block" unless block_given?
|
15
|
+
if c = Proxy.config[:sustainer]
|
16
|
+
t = Thread.new(message, c['delay'], c['interval']) do |msg, delay, interval|
|
17
|
+
Thread.current[:processing_message] = true
|
18
|
+
new(msg, delay: delay, interval: interval).run
|
19
|
+
end
|
20
|
+
begin
|
21
|
+
yield
|
22
|
+
ensure
|
23
|
+
t[:processing_message] = false
|
24
|
+
t.join
|
25
|
+
end
|
26
|
+
else
|
27
|
+
yield
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
attr_reader :message, :delay, :interval
|
33
|
+
def initialize(message, delay: 10, interval: nil)
|
34
|
+
@message = message
|
35
|
+
@delay = delay.to_i
|
36
|
+
@interval = (interval || @delay * 0.9).to_f
|
37
|
+
end
|
38
|
+
|
39
|
+
def run
|
40
|
+
loop do
|
41
|
+
sleep(interval)
|
42
|
+
break unless Thread.current[:processing_message]
|
43
|
+
begin
|
44
|
+
message.delay! delay
|
45
|
+
rescue => e
|
46
|
+
logger.error(e)
|
47
|
+
break
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
data/lib/magellan/gcs/proxy.rb
CHANGED
@@ -1,14 +1,30 @@
|
|
1
|
-
require
|
2
|
-
|
3
|
-
|
1
|
+
require 'dotenv'
|
2
|
+
Dotenv.load
|
3
|
+
|
4
|
+
require 'magellan/gcs/proxy/version'
|
4
5
|
require 'magellan/gcs/proxy/expand_variable'
|
5
|
-
require 'magellan/gcs/proxy/
|
6
|
+
require 'magellan/gcs/proxy/config'
|
6
7
|
require 'magellan/gcs/proxy/log'
|
8
|
+
require 'magellan/gcs/proxy/gcp'
|
9
|
+
|
10
|
+
require 'magellan/gcs/proxy/composite_logger'
|
11
|
+
require 'magellan/gcs/proxy/progress_notifier_adapter'
|
12
|
+
require 'magellan/gcs/proxy/pubsub_progress_notifier'
|
13
|
+
require 'magellan/gcs/proxy/pubsub_sustainer'
|
14
|
+
require 'magellan/gcs/proxy/progress_notification'
|
15
|
+
|
7
16
|
require 'magellan/gcs/proxy/message_wrapper'
|
17
|
+
require 'magellan/gcs/proxy/context'
|
18
|
+
require 'magellan/gcs/proxy/cli'
|
8
19
|
|
9
20
|
module Magellan
|
10
21
|
module Gcs
|
11
22
|
module Proxy
|
23
|
+
class << self
|
24
|
+
def config
|
25
|
+
@config ||= Config.new
|
26
|
+
end
|
27
|
+
end
|
12
28
|
end
|
13
29
|
end
|
14
30
|
end
|