herdst_worker 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|