magellan-rails 0.0.1
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/.gitignore +37 -0
- data/Gemfile +7 -0
- data/Gemfile.lock +76 -0
- data/LICENSE.txt +22 -0
- data/README.md +29 -0
- data/Rakefile +7 -0
- data/bin/magellan-rails +7 -0
- data/gems/magellan-publisher/.gitignore +37 -0
- data/gems/magellan-publisher/Gemfile +11 -0
- data/gems/magellan-publisher/Gemfile.lock +51 -0
- data/gems/magellan-publisher/LICENSE.txt +22 -0
- data/gems/magellan-publisher/README.md +29 -0
- data/gems/magellan-publisher/Rakefile +14 -0
- data/gems/magellan-publisher/lib/magellan/publisher/version.rb +5 -0
- data/gems/magellan-publisher/lib/magellan/publisher.rb +37 -0
- data/gems/magellan-publisher/magellan-publisher.gemspec +27 -0
- data/gems/magellan-publisher/spec/magellan/publisher_spec.rb +39 -0
- data/gems/magellan-publisher/spec/spec_helper.rb +8 -0
- data/lib/generators/install_generator.rb +20 -0
- data/lib/generators/templates/Magellan.yml +115 -0
- data/lib/magellan/extentions/rails/engine.rb +28 -0
- data/lib/magellan/extentions/rails.rb +2 -0
- data/lib/magellan/extentions.rb +2 -0
- data/lib/magellan/rails/executor.rb +35 -0
- data/lib/magellan/rails/railtie.rb +12 -0
- data/lib/magellan/rails/request.rb +111 -0
- data/lib/magellan/rails/response.rb +35 -0
- data/lib/magellan/rails/version.rb +5 -0
- data/lib/magellan/rails.rb +10 -0
- data/lib/magellan/subscriber/base.rb +26 -0
- data/lib/magellan/subscriber/executor.rb +21 -0
- data/lib/magellan/subscriber/mapper.rb +105 -0
- data/lib/magellan/subscriber/request.rb +25 -0
- data/lib/magellan/subscriber/rspec.rb +7 -0
- data/lib/magellan/subscriber/testing/integration.rb +22 -0
- data/lib/magellan/subscriber.rb +10 -0
- data/lib/magellan/worker/config.rb +33 -0
- data/lib/magellan/worker/core.rb +70 -0
- data/lib/magellan/worker/executor.rb +38 -0
- data/lib/magellan/worker.rb +14 -0
- data/lib/magellan.rb +28 -0
- data/magellan-rails.gemspec +28 -0
- data/spec/magellan/rails/request_spec.rb +103 -0
- data/spec/magellan/rails/response_spec.rb +58 -0
- data/spec/magellan/subscriber/mapper_spec.rb +264 -0
- data/spec/magellan/worker/core_spec.rb +70 -0
- data/spec/spec_helper.rb +4 -0
- metadata +167 -0
@@ -0,0 +1,39 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
require "spec_helper"
|
4
|
+
require 'bunny'
|
5
|
+
require 'bunny_mock'
|
6
|
+
require 'json'
|
7
|
+
|
8
|
+
describe Magellan::Publisher do
|
9
|
+
|
10
|
+
describe "#publish" do
|
11
|
+
|
12
|
+
before do
|
13
|
+
@topic = "topic/aaa"
|
14
|
+
@rabbitmq_topic = "topic.aaa"
|
15
|
+
@payload = "Hello World"
|
16
|
+
@bunny = BunnyMock.new
|
17
|
+
channel = @bunny.create_channel
|
18
|
+
@exchange = @bunny.exchange('test_exchange', type: :direct, durable: false, auto_delete: false)
|
19
|
+
expect(@exchange).to receive(:publish).with(@payload, routing_key: @rabbitmq_topic)
|
20
|
+
|
21
|
+
allow(Bunny ).to receive(:new).and_return(@bunny)
|
22
|
+
allow(@bunny ).to receive(:create_channel).and_return(channel)
|
23
|
+
allow(channel).to receive(:exchange).with('test_exchange', no_declare: true).and_return(@exchange)
|
24
|
+
|
25
|
+
ENV["MAGELLAN_PUBLISH_EXCHANGE"] = "test_exchange"
|
26
|
+
@publisher = Magellan::Publisher.new(host: "127.0.0.1",
|
27
|
+
port: 5672,
|
28
|
+
vhost: "customer1.sample_project",
|
29
|
+
user: "customer1.sample_project",
|
30
|
+
pass: "pasw1")
|
31
|
+
end
|
32
|
+
|
33
|
+
it do
|
34
|
+
@publisher.publish(@topic, @payload)
|
35
|
+
end
|
36
|
+
|
37
|
+
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'rails/generators/base'
|
2
|
+
require 'securerandom'
|
3
|
+
|
4
|
+
module Magellan
|
5
|
+
module Generators
|
6
|
+
class InstallGenerator < ::Rails::Generators::Base
|
7
|
+
source_root File.expand_path("../templates", __FILE__)
|
8
|
+
|
9
|
+
desc "Creates config files for Magellan"
|
10
|
+
|
11
|
+
def copy_magellan_yaml
|
12
|
+
template "Magellan.yml", "Magellan.yml"
|
13
|
+
end
|
14
|
+
|
15
|
+
def rails_4?
|
16
|
+
::Rails::VERSION::MAJOR == 4
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,115 @@
|
|
1
|
+
trdb:
|
2
|
+
image: redis:2.8.13
|
3
|
+
# Dockerfile: https://raw.githubusercontent.com/docker-library/redis/docker-2.8.12/Dockerfile
|
4
|
+
after_tasks:
|
5
|
+
- name: "setup TD data"
|
6
|
+
docker:
|
7
|
+
detach: False
|
8
|
+
name: magellan-tr-data
|
9
|
+
image: groovenauts/magellan-tr-data
|
10
|
+
links:
|
11
|
+
- trdb:redis
|
12
|
+
|
13
|
+
|
14
|
+
rabbitmq:
|
15
|
+
before_tasks:
|
16
|
+
- name: "stop RabbitMQ container"
|
17
|
+
command: docker stop rabbitmq
|
18
|
+
ignore_errors: yes
|
19
|
+
- name: "remove RabbitMQ container"
|
20
|
+
command: docker rm rabbitmq
|
21
|
+
ignore_errors: yes
|
22
|
+
|
23
|
+
- name: "make RabbitMQ log directory"
|
24
|
+
file:
|
25
|
+
path: /var/log/rabbitmq
|
26
|
+
force: yes
|
27
|
+
state: directory
|
28
|
+
|
29
|
+
- name: "change owner /var/log/rabbitmq to rabbitmq"
|
30
|
+
docker:
|
31
|
+
name: rabbitmq_dir_owner
|
32
|
+
image: tutum/rabbitmq
|
33
|
+
volumes:
|
34
|
+
- /var/log/rabbitmq:/var/log/rabbitmq
|
35
|
+
command: "chown -R rabbitmq:rabbitmq /var/log/rabbitmq"
|
36
|
+
|
37
|
+
image: tutum/rabbitmq
|
38
|
+
# Dockerfile: https://raw.githubusercontent.com/tutumcloud/tutum-docker-rabbitmq/master/Dockerfile
|
39
|
+
ports:
|
40
|
+
- 5672:5672
|
41
|
+
- 15672:15672
|
42
|
+
env:
|
43
|
+
- RABBITMQ_USER=magellan
|
44
|
+
- RABBITMQ_PASS=mypass
|
45
|
+
volumes:
|
46
|
+
- /var/log/rabbitmq:/var/log/rabbitmq
|
47
|
+
|
48
|
+
after_tasks:
|
49
|
+
- name: "wait to ready rabbitmq"
|
50
|
+
wait_for: delay=3 port=5672
|
51
|
+
- name: "wait to ready rabbitmq admin"
|
52
|
+
wait_for: port=15672
|
53
|
+
- name: "setup Queues and Exchanges"
|
54
|
+
docker:
|
55
|
+
detach: False
|
56
|
+
name: magellan-setup_tools
|
57
|
+
image: groovenauts/magellan-setup_tools:0.1.1
|
58
|
+
links:
|
59
|
+
- trdb:redis
|
60
|
+
- rabbitmq:rabbitmq
|
61
|
+
command: ruby ./rabbitmq customer1.magellan-rails-example 0.0.1 rails transaction-router pswd1 pswd2
|
62
|
+
|
63
|
+
|
64
|
+
tr:
|
65
|
+
before_tasks:
|
66
|
+
- name: "stop tr container"
|
67
|
+
command: docker stop tr
|
68
|
+
ignore_errors: yes
|
69
|
+
- name: "remove tr container"
|
70
|
+
command: docker rm tr
|
71
|
+
ignore_errors: yes
|
72
|
+
|
73
|
+
image: groovenauts/magellan-transaction-router:0.0.1-with_signature
|
74
|
+
# Dockerfile: https://raw.githubusercontent.com/tengine/magellan-transaction-router-broadleaf/feature/integration01/Dockerfile?token=18912__eyJzY29wZSI6IlJhd0Jsb2I6dGVuZ2luZS9tYWdlbGxhbi10cmFuc2FjdGlvbi1yb3V0ZXItYnJvYWRsZWFmL2ZlYXR1cmUvaW50ZWdyYXRpb24wMS9Eb2NrZXJmaWxlIiwiZXhwaXJlcyI6MTQwNzIzMDg4MX0%3D--2ca5fb4de69eca8cbc5cda747b8716ed85ce89a2
|
75
|
+
ports:
|
76
|
+
- 3000:3000
|
77
|
+
links:
|
78
|
+
- trdb:redis
|
79
|
+
- rabbitmq:rabbitmq
|
80
|
+
tty: True
|
81
|
+
stdin_open: True
|
82
|
+
command: /bin/bash ./boot.sh
|
83
|
+
|
84
|
+
|
85
|
+
|
86
|
+
|
87
|
+
app:
|
88
|
+
before_tasks:
|
89
|
+
- name: "stop app container"
|
90
|
+
command: docker stop app
|
91
|
+
ignore_errors: yes
|
92
|
+
- name: "remove app container"
|
93
|
+
command: docker rm app
|
94
|
+
ignore_errors: yes
|
95
|
+
|
96
|
+
- name: "make application log directory"
|
97
|
+
file:
|
98
|
+
path: /var/log/application
|
99
|
+
force: yes
|
100
|
+
state: directory
|
101
|
+
|
102
|
+
image: groovenauts/magellan-rails-example
|
103
|
+
# Dockerfile: "./Dockerfile"
|
104
|
+
links:
|
105
|
+
- rabbitmq:rabbitmq
|
106
|
+
env:
|
107
|
+
- VHOST=/customer1.magellan-rails-example
|
108
|
+
- REQUEST_QUEUE=customer1.magellan-rails-example.0.0.1.rails
|
109
|
+
- RESPONSE_EXCHANGE=customer1.magellan-rails-example.reply
|
110
|
+
- RABBITMQ_USER=customer1.magellan-rails-example
|
111
|
+
- RABBITMQ_PASS=pswd2
|
112
|
+
- SECRET_KEY_BASE={{ app_secret_key_base }}
|
113
|
+
volumes:
|
114
|
+
- /var/log/application:/usr/src/app/log
|
115
|
+
command: bundle exec magellan-rails
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
require "rails/engine"
|
4
|
+
require "magellan/worker/config"
|
5
|
+
|
6
|
+
module Magellan
|
7
|
+
module Extentions
|
8
|
+
module Rails
|
9
|
+
module Engine
|
10
|
+
def eager_load!
|
11
|
+
config = Magellan::Worker.worker.config
|
12
|
+
unless config[:http_worker]
|
13
|
+
self.config.eager_load_paths -= [::Rails.root.join("app/controllers").to_s]
|
14
|
+
end
|
15
|
+
unless config[:subscriber_worker]
|
16
|
+
self.config.eager_load_paths -= [::Rails.root.join("app/subscribers").to_s]
|
17
|
+
end
|
18
|
+
super
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
class ::Rails::Engine
|
26
|
+
prepend Magellan::Extentions::Rails::Engine
|
27
|
+
end
|
28
|
+
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
require 'magellan/rails'
|
3
|
+
require "bunny"
|
4
|
+
|
5
|
+
class Magellan::Rails::Executor
|
6
|
+
def initialize(exchange)
|
7
|
+
@exchange = exchange
|
8
|
+
@app = ::Rails.application
|
9
|
+
end
|
10
|
+
|
11
|
+
def execute(reply_to, correlation_id, delivery_tag, request_message)
|
12
|
+
response = Magellan::Rails::Response.new()
|
13
|
+
|
14
|
+
request = Magellan::Rails::Request.new()
|
15
|
+
request.parse_message(request_message)
|
16
|
+
|
17
|
+
begin
|
18
|
+
rack_response = @app.call(request.to_rack_env)
|
19
|
+
response.parse_rack_response(rack_response)
|
20
|
+
rescue Exception => e
|
21
|
+
response.status = '500'
|
22
|
+
response.headers = {'Content-Type' => 'text/plain', 'charset' => 'utf-8'}
|
23
|
+
response.body = 'Internal Server Error'
|
24
|
+
Magellan.logger.error(e)
|
25
|
+
ensure
|
26
|
+
@exchange.publish(
|
27
|
+
response.to_message,
|
28
|
+
{
|
29
|
+
expiration: request.reply_ttl,
|
30
|
+
correlation_id: correlation_id,
|
31
|
+
routing_key: reply_to
|
32
|
+
})
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,111 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
require 'magellan/rails'
|
3
|
+
require 'set'
|
4
|
+
require 'base64'
|
5
|
+
|
6
|
+
class Magellan::Rails::Request
|
7
|
+
attr_reader :headers, :options, :body, :body_encoding
|
8
|
+
|
9
|
+
RACK_HEADER = Set.new ['CONTENT_LENGTH', 'CONTENT_TYPE', 'GATEWAY_INTERFACE',
|
10
|
+
'Path-Info', 'Query-String', "Method", "Server-Name", "Server-Port"]
|
11
|
+
|
12
|
+
VALID_ENCODING = %w(plain base64)
|
13
|
+
|
14
|
+
def option(key)
|
15
|
+
options[key]
|
16
|
+
end
|
17
|
+
|
18
|
+
def reply_ttl
|
19
|
+
# TRからのメッセージにワーカー処理結果の送信時に指定するttlが含まれていない場合, 1秒を設定します
|
20
|
+
ttl = options['reply_ttl'] || 1000
|
21
|
+
ttl.to_i
|
22
|
+
end
|
23
|
+
|
24
|
+
def parse_message(request_message)
|
25
|
+
# @options = request_message['options']
|
26
|
+
# TRから送られるメッセージのoptionsの型が本来はHash型なのですが、
|
27
|
+
# 配列型になってしまっているため、暫定的に空のHashをセットします
|
28
|
+
@options = {}
|
29
|
+
@headers = request_message['headers']
|
30
|
+
@body_encoding = request_message['body_encoding']
|
31
|
+
@body_encoding = (@body_encoding.present? and VALID_ENCODING.include?(@body_encoding)) ? @body_encoding.to_sym : :plain
|
32
|
+
@body = decode_body(request_message['body'], @body_encoding)
|
33
|
+
end
|
34
|
+
|
35
|
+
def to_rack_env
|
36
|
+
env = Hash.new
|
37
|
+
|
38
|
+
scripe_name = @headers['Path-Info'][@headers['Path-Info'].rindex('/') + 1..-1]
|
39
|
+
query_string = @headers['Query-String']
|
40
|
+
|
41
|
+
# Rackにクエリーストリングを渡す時は
|
42
|
+
query_string.slice!(0) if (query_string[0] == '?')
|
43
|
+
|
44
|
+
ct = @headers["Content-Type"]
|
45
|
+
|
46
|
+
env["CONTENT_LENGTH"] = @body.bytesize
|
47
|
+
env["CONTENT_TYPE"] = ct.dup if ct
|
48
|
+
env["GATEWAY_INTERFACE"] = "CGI/1.1"
|
49
|
+
env["PATH_INFO"] = @headers['Path-Info'] # "/hello/index",
|
50
|
+
env["QUERY_STRING"] = @headers['Query-String'] #
|
51
|
+
|
52
|
+
# TRから送られてきていないので一旦コメントアウト
|
53
|
+
# env["REMOTE_ADDR"] = @peeraddr[3]
|
54
|
+
# env["REMOTE_HOST"] = @peeraddr[2]
|
55
|
+
# env["REMOTE_USER"] = @user
|
56
|
+
|
57
|
+
env["REQUEST_METHOD"] = @headers['Method'] # GET, PUT, DELETE...
|
58
|
+
env["REQUEST_URI"] = "#{env["PATH_INFO"]}?#{env["QUERY_STRING"]}"
|
59
|
+
env["SCRIPT_NAME"] = '' # /scripts/sample?a=c の場合 ルートなので ''
|
60
|
+
env["SERVER_NAME"] = @headers['Server-Name'] # "localhost",
|
61
|
+
env["SERVER_PORT"] = @headers['Server-Port'].to_s # "3000",
|
62
|
+
|
63
|
+
# 一旦決め打ち
|
64
|
+
env["SERVER_PROTOCOL"] = "HTTP/1.1"
|
65
|
+
|
66
|
+
env["SERVER_SOFTWARE"] = 'magellan'
|
67
|
+
|
68
|
+
@headers.each{|key, val|
|
69
|
+
next if RACK_HEADER.include? key
|
70
|
+
name = "HTTP_" + key
|
71
|
+
name.gsub!(/-/o, "_")
|
72
|
+
name.upcase!
|
73
|
+
env[name] = val
|
74
|
+
}
|
75
|
+
|
76
|
+
env["rack.version"] = [1, 5, 2]
|
77
|
+
env["rack.url_scheme"] = "http"
|
78
|
+
env["rack.multithread"] = true
|
79
|
+
env["rack.multiprocess"] = false
|
80
|
+
env["rack.run_once"] = false
|
81
|
+
env["rack.hijack?"] = false
|
82
|
+
|
83
|
+
env['rack.input'] = StringIO.new(@body.to_s)
|
84
|
+
|
85
|
+
# 本来はロガーに渡すべきだが、設計するまで一旦標準エラー出力に出します
|
86
|
+
env["rack.errors"] = $stderr
|
87
|
+
|
88
|
+
env["HTTP_VERSION"] = env["SERVER_PROTOCOL"]
|
89
|
+
env["REQUEST_PATH"] = env["PATH_INFO"]
|
90
|
+
env['rack.logger'] = Magellan.logger
|
91
|
+
|
92
|
+
env
|
93
|
+
end
|
94
|
+
|
95
|
+
|
96
|
+
# @doc レクエストボディをencodingに応じてデコードする。
|
97
|
+
# 現在対応しているencodingは以下。
|
98
|
+
# - base64: Base64デコードする
|
99
|
+
# - plain: デコードせずそのままの値を返す
|
100
|
+
#
|
101
|
+
# @param [String] body リクエストボディ
|
102
|
+
# @param [String] encoding エンコード形式
|
103
|
+
def decode_body(body, encoding)
|
104
|
+
case encoding.to_sym
|
105
|
+
when :base64 then
|
106
|
+
Base64.decode64(body)
|
107
|
+
else
|
108
|
+
body
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
require 'magellan/rails'
|
3
|
+
|
4
|
+
require 'json'
|
5
|
+
require 'base64'
|
6
|
+
|
7
|
+
class Magellan::Rails::Response
|
8
|
+
attr_accessor :headers, :body, :status, :body_encoding
|
9
|
+
|
10
|
+
def parse_rack_response(response)
|
11
|
+
@status = response[0]
|
12
|
+
@headers = response[1]
|
13
|
+
@body = ''
|
14
|
+
@body_encoding = :plain
|
15
|
+
body_proxy = response[2]
|
16
|
+
body_proxy.each{|b| @body << b}
|
17
|
+
body_proxy.close
|
18
|
+
end
|
19
|
+
|
20
|
+
def to_message
|
21
|
+
json_generate_errors = [JSON::GeneratorError, Encoding::UndefinedConversionError]
|
22
|
+
begin
|
23
|
+
{
|
24
|
+
headers: @headers,
|
25
|
+
status: @status,
|
26
|
+
body: @body,
|
27
|
+
body_encoding: @body_encoding.to_s,
|
28
|
+
}.to_json
|
29
|
+
rescue *json_generate_errors
|
30
|
+
@body = Base64.strict_encode64(@body)
|
31
|
+
@body_encoding = :base64
|
32
|
+
to_message
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
require "magellan"
|
3
|
+
require 'magellan/rails/railtie'
|
4
|
+
|
5
|
+
module Magellan::Rails
|
6
|
+
autoload :Executor, 'magellan/rails/executor'
|
7
|
+
autoload :Request, 'magellan/rails/request'
|
8
|
+
autoload :Response, 'magellan/rails/response'
|
9
|
+
autoload :VERSION, 'magellan/rails/version'
|
10
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
require 'magellan/rails'
|
3
|
+
|
4
|
+
class Magellan::Subscriber::Base < AbstractController::Base
|
5
|
+
|
6
|
+
include AbstractController::Callbacks # for (before,after,around)_filter
|
7
|
+
|
8
|
+
def self.logger=(logger)
|
9
|
+
@logger = logger
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.logger
|
13
|
+
@logger || ::Rails.logger
|
14
|
+
end
|
15
|
+
|
16
|
+
attr_reader :request, :topic, :body
|
17
|
+
def initialize(request)
|
18
|
+
@request = request
|
19
|
+
@topic = request.topic
|
20
|
+
@body = request.body
|
21
|
+
end
|
22
|
+
|
23
|
+
def logger
|
24
|
+
Magellan::Subscriber::Base.logger
|
25
|
+
end
|
26
|
+
end
|