herdst_worker 0.1.0
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 +7 -0
- data/Rakefile +7 -0
- data/bin/herdst_worker +16 -0
- data/lib/herdst_worker.rb +8 -0
- data/lib/herdst_worker/adapters/database.rb +31 -0
- data/lib/herdst_worker/adapters/facade.rb +20 -0
- data/lib/herdst_worker/adapters/sentry.rb +26 -0
- data/lib/herdst_worker/application/facade.rb +88 -0
- data/lib/herdst_worker/autoload/facade.rb +67 -0
- data/lib/herdst_worker/configuration/facade.rb +51 -0
- data/lib/herdst_worker/configuration/metadata.rb +183 -0
- data/lib/herdst_worker/configuration/paths.rb +41 -0
- data/lib/herdst_worker/log/facade.rb +87 -0
- data/lib/herdst_worker/queue/facade.rb +162 -0
- data/lib/herdst_worker/queue/processor.rb +223 -0
- data/lib/herdst_worker/queue/runner.rb +137 -0
- data/lib/herdst_worker/signals/facade.rb +35 -0
- data/lib/herdst_worker/version.rb +3 -0
- metadata +272 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: c5b5decf991f20026eb7fe48327f09a7c385c6fad0fdff66e683a4d4d142c8d8
|
4
|
+
data.tar.gz: f3e8a159f0ceb54681f120a69c688ed95a86e78a1f7c008b960f2bd41c3558eb
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 768569b090a702a755a589d353af9c9f67d478c7a8e81bc7a6a92f04d5b3c826a11e546fa237347ed5e8c83284b630803673b749c830a32a2cfb87ff3e4fe5a3
|
7
|
+
data.tar.gz: 84044ceab2c26c95c5338b16d3ef4c97596781b04a5d4a341550aef02b7cd166d2b4eed1013986868ceaeacfe6066719b01e9c9f3089e856ac01aaee4d35fb08
|
data/Rakefile
ADDED
data/bin/herdst_worker
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
args = Hash[ ARGV.flat_map { |s| s.scan(/--?([^=\s]+)(?:=(\S+))?/) } ]
|
4
|
+
|
5
|
+
application_file = args["application"] || "application.rb"
|
6
|
+
application_path = "#{Dir.pwd}/#{application_file}"
|
7
|
+
application_env = args["env"] || "production"
|
8
|
+
queue_enabled = args["queue_enabled"] == "false" ? false : true
|
9
|
+
|
10
|
+
require_relative "../lib/herdst_worker"
|
11
|
+
|
12
|
+
application_instance = HerdstWorker::Application.new(application_env, queue_enabled)
|
13
|
+
|
14
|
+
require_relative application_path
|
15
|
+
|
16
|
+
application_instance.run
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module HerdstWorker
|
2
|
+
module Adapters
|
3
|
+
class Database
|
4
|
+
|
5
|
+
|
6
|
+
def self.setup(app)
|
7
|
+
db_config = app.config_for(:database)
|
8
|
+
|
9
|
+
if app.config.is_dev?
|
10
|
+
ActiveRecord::Base.logger = app.logger.activerecord
|
11
|
+
end
|
12
|
+
|
13
|
+
ActiveRecord::Base.default_timezone = :utc
|
14
|
+
ActiveRecord::Base.establish_connection(
|
15
|
+
adapter: db_config[:adapter],
|
16
|
+
encoding: db_config[:encoding],
|
17
|
+
charset: db_config[:charset],
|
18
|
+
collation: db_config[:collation],
|
19
|
+
pool: db_config[:pool],
|
20
|
+
host: db_config[:host],
|
21
|
+
username: db_config[:username],
|
22
|
+
password: db_config[:password],
|
23
|
+
database: db_config[:database]
|
24
|
+
)
|
25
|
+
ActiveRecord::Base.connection.enable_query_cache!
|
26
|
+
end
|
27
|
+
|
28
|
+
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require "sentry-raven"
|
2
|
+
|
3
|
+
require_relative "database"
|
4
|
+
require_relative "sentry"
|
5
|
+
|
6
|
+
|
7
|
+
module HerdstWorker
|
8
|
+
module Adapters
|
9
|
+
class Facade
|
10
|
+
|
11
|
+
|
12
|
+
def self.bootstrap(app)
|
13
|
+
HerdstWorker::Adapters::Database.setup(app)
|
14
|
+
HerdstWorker::Adapters::Sentry.setup(app)
|
15
|
+
end
|
16
|
+
|
17
|
+
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require "sentry-raven"
|
2
|
+
|
3
|
+
|
4
|
+
module HerdstWorker
|
5
|
+
module Adapters
|
6
|
+
class Sentry
|
7
|
+
|
8
|
+
|
9
|
+
def self.setup(app)
|
10
|
+
environment = app.config.metadata[:DEPLOYMENT_ENV]
|
11
|
+
release = app.config.metadata[:RELEASE_VERSION]
|
12
|
+
sentry_key = app.config.metadata[:SENTRY_KEY]
|
13
|
+
|
14
|
+
Raven.configure do |config|
|
15
|
+
config.current_environment = environment
|
16
|
+
config.release = release
|
17
|
+
config.dsn = sentry_key if sentry_key
|
18
|
+
config.environments = ["preview", "production"]
|
19
|
+
config.async = lambda { |event| Thread.new { Raven.send_event(event) } }
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
require_relative '../configuration/facade'
|
2
|
+
require_relative '../log/facade'
|
3
|
+
require_relative '../autoload/facade'
|
4
|
+
require_relative '../signals/facade'
|
5
|
+
require_relative '../queue/facade'
|
6
|
+
require_relative '../adapters/facade'
|
7
|
+
|
8
|
+
|
9
|
+
module HerdstWorker
|
10
|
+
class Application
|
11
|
+
|
12
|
+
|
13
|
+
attr_accessor :name
|
14
|
+
attr_accessor :logger, :config, :autoload
|
15
|
+
attr_accessor :poller_enabled, :queues, :poller_url, :queue
|
16
|
+
|
17
|
+
|
18
|
+
def initialize(env, poller_enabled)
|
19
|
+
self.poller_enabled = poller_enabled
|
20
|
+
self.queues = ActiveSupport::HashWithIndifferentAccess.new
|
21
|
+
|
22
|
+
HerdstWorker.set_application(self)
|
23
|
+
|
24
|
+
self.logger = HerdstWorker::Log::Facade.new(get_logger_level(env))
|
25
|
+
self.config = HerdstWorker::Configuration::Facade.new(env)
|
26
|
+
self.autoload = HerdstWorker::Autoload::Facade.new(self)
|
27
|
+
self.set_inflections
|
28
|
+
|
29
|
+
HerdstWorker::Adapters::Facade.bootstrap(self)
|
30
|
+
end
|
31
|
+
|
32
|
+
|
33
|
+
def configure(&block)
|
34
|
+
yield self
|
35
|
+
|
36
|
+
self.autoload.reload
|
37
|
+
end
|
38
|
+
|
39
|
+
|
40
|
+
def add_queue(name, url, poller = false)
|
41
|
+
self.queues[name] = url
|
42
|
+
self.poller_url = url if poller
|
43
|
+
end
|
44
|
+
|
45
|
+
|
46
|
+
def run
|
47
|
+
if self.queue == nil
|
48
|
+
self.logger.info "Starting Application (#{$$})"
|
49
|
+
|
50
|
+
HerdstWorker::Signals::Facade.listen(self.config.paths.temp)
|
51
|
+
|
52
|
+
self.queue = HerdstWorker::Queue::Facade.new(self, self.poller_enabled, self.poller_url, 20)
|
53
|
+
self.queue.start
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
|
58
|
+
def config_for(name)
|
59
|
+
self.config.config_for(name)
|
60
|
+
end
|
61
|
+
|
62
|
+
|
63
|
+
private
|
64
|
+
def set_inflections
|
65
|
+
ActiveSupport::Inflector.inflections do |inflect|
|
66
|
+
inflect.irregular "meta", "meta"
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def get_logger_level(env)
|
71
|
+
return "WARN" if env == "production"
|
72
|
+
return "FATAL" if env == "test"
|
73
|
+
return "DEBUG"
|
74
|
+
end
|
75
|
+
|
76
|
+
|
77
|
+
end
|
78
|
+
|
79
|
+
|
80
|
+
def self.set_application(application)
|
81
|
+
@@application = application
|
82
|
+
end
|
83
|
+
|
84
|
+
|
85
|
+
def self.application
|
86
|
+
@@application
|
87
|
+
end
|
88
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
module HerdstWorker
|
2
|
+
module Autoload
|
3
|
+
class Facade
|
4
|
+
|
5
|
+
|
6
|
+
attr_accessor :root_path, :loaded, :files, :paths
|
7
|
+
|
8
|
+
|
9
|
+
def initialize(app)
|
10
|
+
self.root_path = app.config.paths.root
|
11
|
+
|
12
|
+
self.loaded = []
|
13
|
+
self.files = []
|
14
|
+
self.paths = [ "lib" ]
|
15
|
+
|
16
|
+
self.reload
|
17
|
+
end
|
18
|
+
|
19
|
+
|
20
|
+
def <<(path)
|
21
|
+
full_path = self.root_path + "/" + path
|
22
|
+
|
23
|
+
if File.directory?(full_path)
|
24
|
+
self.paths << path
|
25
|
+
else
|
26
|
+
self.files << path
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
|
31
|
+
def reload
|
32
|
+
self.files.each do |file|
|
33
|
+
self.load_file(self.root_path + "/" + file)
|
34
|
+
end
|
35
|
+
|
36
|
+
self.paths.each do |folder|
|
37
|
+
concern_files = Dir.glob("#{root_path}/#{folder}/concerns/*.rb").sort
|
38
|
+
folder_files = Dir.glob("#{root_path}/#{folder}/*.rb").sort
|
39
|
+
|
40
|
+
concern_sub_files = Dir.glob("#{self.root_path}/#{folder}/**/concerns/*.rb").sort
|
41
|
+
folder_sub_files = Dir.glob("#{self.root_path}/#{folder}/**/*.rb").sort
|
42
|
+
|
43
|
+
files = (concern_files + folder_files + concern_sub_files + folder_sub_files).uniq
|
44
|
+
|
45
|
+
files.each do |file|
|
46
|
+
self.load_file(file)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
|
52
|
+
def has_loaded?(file)
|
53
|
+
self.loaded.include?(file)
|
54
|
+
end
|
55
|
+
|
56
|
+
|
57
|
+
def load_file(file)
|
58
|
+
unless has_loaded?(file)
|
59
|
+
load file
|
60
|
+
self.loaded << file
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
require 'erb'
|
3
|
+
|
4
|
+
require_relative 'metadata'
|
5
|
+
require_relative 'paths'
|
6
|
+
|
7
|
+
|
8
|
+
module HerdstWorker
|
9
|
+
module Configuration
|
10
|
+
class Facade
|
11
|
+
|
12
|
+
|
13
|
+
attr_accessor :env, :metadata, :paths, :actions
|
14
|
+
|
15
|
+
|
16
|
+
def initialize(env)
|
17
|
+
self.env = env.downcase
|
18
|
+
|
19
|
+
self.paths = Paths.new
|
20
|
+
self.metadata = Metadata.new(env, self)
|
21
|
+
self.actions = self.config_for(:actions)
|
22
|
+
end
|
23
|
+
|
24
|
+
|
25
|
+
def config_for(name)
|
26
|
+
file = "#{self.paths.config}/#{name}.yml"
|
27
|
+
file_contents = ERB.new(File.new(file).read)
|
28
|
+
yaml = YAML.load(file_contents.result(binding)).with_indifferent_access
|
29
|
+
|
30
|
+
yaml.include?(self.env) ? yaml[self.env] : yaml
|
31
|
+
end
|
32
|
+
|
33
|
+
|
34
|
+
def is_prod?
|
35
|
+
self.env == "production"
|
36
|
+
end
|
37
|
+
|
38
|
+
|
39
|
+
def is_test?
|
40
|
+
self.env == "test"
|
41
|
+
end
|
42
|
+
|
43
|
+
|
44
|
+
def is_dev?
|
45
|
+
self.env == "development"
|
46
|
+
end
|
47
|
+
|
48
|
+
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,183 @@
|
|
1
|
+
require "aws-sdk-core"
|
2
|
+
require "aws-sdk-secretsmanager"
|
3
|
+
require "aws-sdk-ecs"
|
4
|
+
require "net/http"
|
5
|
+
|
6
|
+
|
7
|
+
module HerdstWorker
|
8
|
+
module Configuration
|
9
|
+
class Metadata
|
10
|
+
|
11
|
+
|
12
|
+
attr_accessor :config, :aws_credentials, :secrets
|
13
|
+
attr_accessor :config_suffix, :secrets_suffix
|
14
|
+
|
15
|
+
|
16
|
+
def initialize(env, config)
|
17
|
+
self.config = config
|
18
|
+
self.config_suffix = "-service-config"
|
19
|
+
self.secrets_suffix = "-service-secrets"
|
20
|
+
|
21
|
+
self.secrets = {}.with_indifferent_access
|
22
|
+
self.secrets["ENV"] = env
|
23
|
+
|
24
|
+
begin
|
25
|
+
self.reload!
|
26
|
+
rescue Exception => ex
|
27
|
+
raise ex unless self.is_prod?
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
|
32
|
+
def reload!
|
33
|
+
self.get_aws_credentials!
|
34
|
+
self.set_task_info!
|
35
|
+
self.get_config_and_secrets!
|
36
|
+
|
37
|
+
self
|
38
|
+
end
|
39
|
+
|
40
|
+
|
41
|
+
def get_secrets
|
42
|
+
self.secrets
|
43
|
+
end
|
44
|
+
|
45
|
+
|
46
|
+
def [](key)
|
47
|
+
self.secrets[key.to_s.upcase]
|
48
|
+
end
|
49
|
+
|
50
|
+
|
51
|
+
def []=(key, value)
|
52
|
+
self.secrets[key.to_s.upcase] = value
|
53
|
+
end
|
54
|
+
|
55
|
+
|
56
|
+
def get_aws_credentials
|
57
|
+
self.get_aws_credentials! rescue nil
|
58
|
+
end
|
59
|
+
|
60
|
+
|
61
|
+
def get_aws_credentials!
|
62
|
+
credentials = self.is_prod? ?
|
63
|
+
make_request!(get_credentials_uri) :
|
64
|
+
self.config.config_for(:aws)
|
65
|
+
|
66
|
+
self.aws_credentials = Aws::Credentials.new(
|
67
|
+
credentials["AccessKeyId"],
|
68
|
+
credentials["SecretAccessKey"],
|
69
|
+
credentials["Token"]
|
70
|
+
)
|
71
|
+
|
72
|
+
Aws.config.update(
|
73
|
+
region: self.get_aws_region,
|
74
|
+
credentials: self.aws_credentials
|
75
|
+
)
|
76
|
+
|
77
|
+
self.aws_credentials
|
78
|
+
end
|
79
|
+
|
80
|
+
|
81
|
+
def get_aws_region
|
82
|
+
# "ap-southeast-2"
|
83
|
+
|
84
|
+
self["AWS_REGION"] = (ENV["AWS_REGION"] || "us-east-1")
|
85
|
+
end
|
86
|
+
|
87
|
+
|
88
|
+
protected
|
89
|
+
def is_prod?
|
90
|
+
self.secrets["ENV"] == "production"
|
91
|
+
end
|
92
|
+
|
93
|
+
|
94
|
+
def set_task_info!
|
95
|
+
return self.secrets["DEPLOYMENT_ENV"] if self.secrets["DEPLOYMENT_ENV"]
|
96
|
+
|
97
|
+
if self.is_prod?
|
98
|
+
metadata = make_request!(get_metadata_uri)
|
99
|
+
|
100
|
+
ecs_client = Aws::ECS::Client.new(
|
101
|
+
:region => self.secrets["AWS_REGION"],
|
102
|
+
:credentials => self.aws_credentials
|
103
|
+
)
|
104
|
+
|
105
|
+
ecs_response = ecs_client.describe_tasks(
|
106
|
+
:cluster => metadata["Cluster"],
|
107
|
+
:tasks => [
|
108
|
+
metadata["TaskARN"]
|
109
|
+
],
|
110
|
+
:include => ["TAGS"]
|
111
|
+
)
|
112
|
+
|
113
|
+
ecs_response["tasks"][0]["tags"].each do |i|
|
114
|
+
self[i["key"]] = i["value"]
|
115
|
+
end
|
116
|
+
|
117
|
+
report_error("Deployment env not found") unless self.secrets["DEPLOYMENT_ENV"]
|
118
|
+
|
119
|
+
self["RELEASE_VERSION"] = ecs_response["tasks"][0]["containers"][0]["containerArn"] rescue nil
|
120
|
+
else
|
121
|
+
self["DEPLOYMENT_ENV"] = "development"
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
|
126
|
+
def get_config_and_secrets!
|
127
|
+
values = nil
|
128
|
+
|
129
|
+
if self.is_prod?
|
130
|
+
secrets_client = Aws::SecretsManager::Client.new(
|
131
|
+
:region => self.secrets["AWS_REGION"],
|
132
|
+
:credentials => self.aws_credentials
|
133
|
+
)
|
134
|
+
|
135
|
+
config = secrets_client.get_secret_value({
|
136
|
+
:secret_id => (self.secrets["DEPLOYMENT_ENV"].to_s + self.config_suffix)
|
137
|
+
})
|
138
|
+
|
139
|
+
secrets = secrets_client.get_secret_value({
|
140
|
+
:secret_id => (self.secrets["DEPLOYMENT_ENV"].to_s + self.secrets_suffix)
|
141
|
+
})
|
142
|
+
|
143
|
+
values = Hash.new
|
144
|
+
values.merge!(JSON.parse(config[:secret_string]))
|
145
|
+
values.merge!(JSON.parse(secrets[:secret_string]))
|
146
|
+
else
|
147
|
+
values = self.config.config_for(:metadata)
|
148
|
+
end
|
149
|
+
|
150
|
+
values.each { |k, v| self[k] = v } if values
|
151
|
+
end
|
152
|
+
|
153
|
+
|
154
|
+
private
|
155
|
+
def get_metadata_uri
|
156
|
+
ENV["ECS_CONTAINER_METADATA_URI"].to_s + "/task"
|
157
|
+
end
|
158
|
+
|
159
|
+
def get_credentials_uri
|
160
|
+
"http://169.254.170.2" + ENV["AWS_CONTAINER_CREDENTIALS_RELATIVE_URI"].to_s
|
161
|
+
end
|
162
|
+
|
163
|
+
def make_request!(request_url)
|
164
|
+
uri = URI.parse(request_url)
|
165
|
+
http = Net::HTTP.start(uri.host, uri.port, :use_ssl => uri.scheme == "https")
|
166
|
+
request = Net::HTTP::Get.new(uri.to_s)
|
167
|
+
response = http.request(request) rescue nil
|
168
|
+
|
169
|
+
if response && response.code.to_i === 200
|
170
|
+
return JSON.parse(response.body).with_indifferent_access
|
171
|
+
else
|
172
|
+
report_error("Failed to ger request: #{request_url}")
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
def report_error(message)
|
177
|
+
raise message
|
178
|
+
end
|
179
|
+
|
180
|
+
|
181
|
+
end
|
182
|
+
end
|
183
|
+
end
|