mcrain 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/.gitignore +10 -0
- data/.gitmodules +3 -0
- data/.rspec +2 -0
- data/.travis.yml +3 -0
- data/CODE_OF_CONDUCT.md +13 -0
- data/Gemfile +24 -0
- data/README.md +150 -0
- data/Rakefile +6 -0
- data/bin/console +14 -0
- data/bin/setup +7 -0
- data/exe/mcrain +48 -0
- data/lib/mcrain/base.rb +133 -0
- data/lib/mcrain/boot2docker.rb +72 -0
- data/lib/mcrain/rabbitmq.rb +57 -0
- data/lib/mcrain/redis.rb +41 -0
- data/lib/mcrain/riak.rb +155 -0
- data/lib/mcrain/version.rb +3 -0
- data/lib/mcrain.rb +88 -0
- data/mcrain.gemspec +29 -0
- metadata +154 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: c1a06ee8da82a778eea1700f2366af6a025a6456
|
4
|
+
data.tar.gz: 77b113a4873c5acd9d2f6ed093e56092c6648929
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 8af73c32ed5d7e65b217be6cd30c35c72b1e9778812344b04b03b1e0e96957b08dbc0146a23694c62c7d7c34711ee6969b6664ad66732005d438779c5069f1db
|
7
|
+
data.tar.gz: 2895f8d1a5df7e7020045e798badc8290f3d112e3191256b5e4f112ecbd751fe96feae65cda8f8bbdd0e8e32475e2d4ebef1a166ea5ee16c7e19057c41259d8b
|
data/.gitignore
ADDED
data/.gitmodules
ADDED
data/.rspec
ADDED
data/.travis.yml
ADDED
data/CODE_OF_CONDUCT.md
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
# Contributor Code of Conduct
|
2
|
+
|
3
|
+
As contributors and maintainers of this project, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities.
|
4
|
+
|
5
|
+
We are committed to making participation in this project a harassment-free experience for everyone, regardless of level of experience, gender, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, age, or religion.
|
6
|
+
|
7
|
+
Examples of unacceptable behavior by participants include the use of sexual language or imagery, derogatory comments or personal attacks, trolling, public or private harassment, insults, or other unprofessional conduct.
|
8
|
+
|
9
|
+
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct. Project maintainers who do not follow the Code of Conduct may be removed from the project team.
|
10
|
+
|
11
|
+
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by opening an issue or contacting one or more of the project maintainers.
|
12
|
+
|
13
|
+
This Code of Conduct is adapted from the [Contributor Covenant](http:contributor-covenant.org), version 1.0.0, available at [http://contributor-covenant.org/version/1/0/0/](http://contributor-covenant.org/version/1/0/0/)
|
data/Gemfile
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
source 'https://rubygems.org'
|
2
|
+
|
3
|
+
# Specify your gem's dependencies in mcrain.gemspec
|
4
|
+
gemspec
|
5
|
+
|
6
|
+
group :development do
|
7
|
+
# for redis
|
8
|
+
gem "redis"
|
9
|
+
|
10
|
+
# for rabbitmq
|
11
|
+
gem 'rabbitmq_http_api_client', '>= 1.6.0'
|
12
|
+
|
13
|
+
# for riak
|
14
|
+
gem "docker-api", "~> 1.21.1"
|
15
|
+
gem "riak-client"
|
16
|
+
end
|
17
|
+
|
18
|
+
group :development do
|
19
|
+
gem "pry"
|
20
|
+
gem "pry-byebug"
|
21
|
+
gem "pry-stack_explorer"
|
22
|
+
gem "simplecov"
|
23
|
+
gem "fuubar"
|
24
|
+
end
|
data/README.md
ADDED
@@ -0,0 +1,150 @@
|
|
1
|
+
# Mcrain
|
2
|
+
|
3
|
+
Mcrain helps you to use docker container in test cases.
|
4
|
+
It supports redis, rabbitmq and riak (stand alone node or clustering) currently.
|
5
|
+
|
6
|
+
## prerequisite
|
7
|
+
|
8
|
+
- [docker](https://docs.docker.com/installation/#installation)
|
9
|
+
|
10
|
+
## Installation
|
11
|
+
|
12
|
+
Add this line to your application's Gemfile:
|
13
|
+
|
14
|
+
```ruby
|
15
|
+
gem 'mcrain'
|
16
|
+
```
|
17
|
+
|
18
|
+
And then execute:
|
19
|
+
|
20
|
+
$ bundle
|
21
|
+
|
22
|
+
Or install it yourself as:
|
23
|
+
|
24
|
+
$ gem install mcrain
|
25
|
+
|
26
|
+
### with redis
|
27
|
+
|
28
|
+
Add this line also to your application's Gemfile
|
29
|
+
```ruby
|
30
|
+
gem 'redis'
|
31
|
+
```
|
32
|
+
|
33
|
+
### with rabbitmq
|
34
|
+
|
35
|
+
Add this line also to your application's Gemfile
|
36
|
+
```ruby
|
37
|
+
gem 'rabbitmq_http_api_client', '>= 1.6.0'
|
38
|
+
```
|
39
|
+
|
40
|
+
### with riak
|
41
|
+
|
42
|
+
Add this line also to your application's Gemfile
|
43
|
+
```ruby
|
44
|
+
gem 'docker-api', '~> 1.21.1'
|
45
|
+
gem 'riak-client'
|
46
|
+
```
|
47
|
+
|
48
|
+
## Usage
|
49
|
+
|
50
|
+
### redis in code
|
51
|
+
|
52
|
+
```ruby
|
53
|
+
Mcrain[:redis].start do |s|
|
54
|
+
c = s.client # Redis::Client object
|
55
|
+
c.ping
|
56
|
+
end
|
57
|
+
```
|
58
|
+
|
59
|
+
### rabbitmq in code
|
60
|
+
|
61
|
+
```ruby
|
62
|
+
Mcrain[:rabbitmq].start do |s|
|
63
|
+
c = s.client # RabbitMQ::HTTP::Client object
|
64
|
+
c.list_nodes
|
65
|
+
end
|
66
|
+
```
|
67
|
+
|
68
|
+
### riak in code
|
69
|
+
|
70
|
+
Mcrain::Riak uses [docker-riak](https://github.com/hectcastro/docker-riak).
|
71
|
+
So set the path to `Mcrain.docker_riak_path` .
|
72
|
+
|
73
|
+
```ruby
|
74
|
+
Mcrain.docker_riak_path = "path/to/docker-riak"
|
75
|
+
Mcrain[:riak].start do |s|
|
76
|
+
c = s.client # Riak::Client object
|
77
|
+
obj = c.bucket("bucket1").get_or_new("foo")
|
78
|
+
obj.data = data
|
79
|
+
obj.store
|
80
|
+
end
|
81
|
+
```
|
82
|
+
|
83
|
+
|
84
|
+
### redis in terminal
|
85
|
+
|
86
|
+
```
|
87
|
+
$ mcrain start redis
|
88
|
+
To connect:
|
89
|
+
require 'redis'
|
90
|
+
client = Redis.new({:host=>"192.168.59.103", :port=>50669})
|
91
|
+
OK
|
92
|
+
|
93
|
+
$ mcrain stop redis
|
94
|
+
OK
|
95
|
+
```
|
96
|
+
|
97
|
+
### rabbitmq in terminal
|
98
|
+
|
99
|
+
```
|
100
|
+
$ mcrain start rabbitmq
|
101
|
+
To connect:
|
102
|
+
require 'rabbitmq/http/client'
|
103
|
+
client = RabbitMQ::HTTP::Client.new(*["http://192.168.59.103:50684", {:username=>"guest", :password=>"guest"}])
|
104
|
+
OK
|
105
|
+
|
106
|
+
$ mcrain stop rabbitmq
|
107
|
+
OK
|
108
|
+
```
|
109
|
+
|
110
|
+
### riak in terminal
|
111
|
+
|
112
|
+
```
|
113
|
+
$ export DOCKER_RIAK_PATH=/path/to/docker-riak
|
114
|
+
$ mcrain start riak
|
115
|
+
To connect:
|
116
|
+
require 'riak'
|
117
|
+
client = Riak::Client.new({:nodes=>[{:host=>"192.168.59.103", :pb_port=>33152}]})
|
118
|
+
OK
|
119
|
+
|
120
|
+
$ mcrain stop riak
|
121
|
+
OK
|
122
|
+
```
|
123
|
+
|
124
|
+
|
125
|
+
```
|
126
|
+
$ export DOCKER_RIAK_PATH=/path/to/docker-riak
|
127
|
+
$ mcrain start riak 5
|
128
|
+
To connect:
|
129
|
+
require 'riak'
|
130
|
+
client = Riak::Client.new({:nodes=>[{:host=>"192.168.59.103", :pb_port=>33162}, {:host=>"192.168.59.103", :pb_port=>33160}, {:host=>"192.168.59.103", :pb_port=>33158}, {:host=>"192.168.59.103", :pb_port=>33157}, {:host=>"192.168.59.103", :pb_port=>33155}]})
|
131
|
+
OK
|
132
|
+
|
133
|
+
$ mcrain stop riak 5
|
134
|
+
OK
|
135
|
+
```
|
136
|
+
|
137
|
+
|
138
|
+
## Development
|
139
|
+
|
140
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `bin/console` for an interactive prompt that will allow you to experiment. Run `bundle exec mcrain` to use the code located in this directory, ignoring other installed copies of this gem.
|
141
|
+
|
142
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release` to create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
143
|
+
|
144
|
+
## Contributing
|
145
|
+
|
146
|
+
1. Fork it ( https://github.com/groovenauts/mcrain/fork )
|
147
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
148
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
149
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
150
|
+
5. Create a new Pull Request
|
data/Rakefile
ADDED
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "mcrain"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require "irb"
|
14
|
+
IRB.start
|
data/bin/setup
ADDED
data/exe/mcrain
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# coding: utf-8
|
3
|
+
|
4
|
+
require "mcrain"
|
5
|
+
|
6
|
+
action, service, *args = *ARGV
|
7
|
+
if action.nil? || service.nil?
|
8
|
+
|
9
|
+
$stderr.puts(<<MESSAGE)
|
10
|
+
#{$PROGRAM_NAME} <action> <target> [n] [-v or --verbose]
|
11
|
+
action: start or stop
|
12
|
+
service: redis, rabbitmq or riak
|
13
|
+
n: cluster size for riak
|
14
|
+
MESSAGE
|
15
|
+
|
16
|
+
exit(1)
|
17
|
+
end
|
18
|
+
|
19
|
+
unless args.include?("-v") || args.include?("--verbose")
|
20
|
+
require 'logger'
|
21
|
+
Mcrain.logger = Logger.new("/dev/null")
|
22
|
+
end
|
23
|
+
|
24
|
+
begin
|
25
|
+
options = {}
|
26
|
+
case service
|
27
|
+
when "riak" then
|
28
|
+
raise "DOCKER_RIAK_PATH is blank. Set it the path to docker-riak repository." unless ENV['DOCKER_RIAK_PATH']
|
29
|
+
Mcrain::Riak.docker_riak_path = ENV['DOCKER_RIAK_PATH']
|
30
|
+
cluster_size = args.shift.to_i
|
31
|
+
cluster_size = 1 if cluster_size == 0
|
32
|
+
options = {cluster_size: cluster_size, automatic_clustering: cluster_size > 1}
|
33
|
+
end
|
34
|
+
|
35
|
+
server = Mcrain.lookup(service.to_sym, options)
|
36
|
+
server.send(action)
|
37
|
+
|
38
|
+
case action
|
39
|
+
when "start" then
|
40
|
+
puts "To connect:\nrequire '#{server.client_require}'\nclient = #{server.client_script}"
|
41
|
+
end
|
42
|
+
rescue => e
|
43
|
+
$stderr.puts "\e[31m[#{e.class}] #{e.message}\e[0m"
|
44
|
+
exit(1)
|
45
|
+
else
|
46
|
+
$stderr.puts "\e[32mOK\e[0m"
|
47
|
+
exit(0)
|
48
|
+
end
|
data/lib/mcrain/base.rb
ADDED
@@ -0,0 +1,133 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
require 'mcrain'
|
3
|
+
|
4
|
+
require 'uri'
|
5
|
+
require 'timeout'
|
6
|
+
|
7
|
+
require 'logger_pipe'
|
8
|
+
|
9
|
+
module Mcrain
|
10
|
+
class Base
|
11
|
+
|
12
|
+
class << self
|
13
|
+
attr_writer :server_name
|
14
|
+
def server_name
|
15
|
+
@server_name ||= self.name.split(/::/).last.underscore.to_sym
|
16
|
+
end
|
17
|
+
|
18
|
+
attr_accessor :container_image, :port
|
19
|
+
end
|
20
|
+
|
21
|
+
def container_image
|
22
|
+
self.class.container_image or raise "No container_image for #{self.class.name}"
|
23
|
+
end
|
24
|
+
|
25
|
+
def container_name
|
26
|
+
"test-#{self.class.server_name}"
|
27
|
+
end
|
28
|
+
|
29
|
+
def host
|
30
|
+
@host ||= URI.parse(ENV["DOCKER_HOST"] || "tcp://localhost").host
|
31
|
+
end
|
32
|
+
|
33
|
+
def find_portno
|
34
|
+
# 未使用のポートをシステムに割り当てさせてすぐ閉じてそれを利用する
|
35
|
+
tmpserv = TCPServer.new(0)
|
36
|
+
portno = tmpserv.local_address.ip_port
|
37
|
+
tmpserv.close
|
38
|
+
portno
|
39
|
+
end
|
40
|
+
|
41
|
+
def port
|
42
|
+
@port ||= find_portno
|
43
|
+
end
|
44
|
+
|
45
|
+
def url
|
46
|
+
@url ||= "#{self.class.server_name}://#{host}:#{port}"
|
47
|
+
end
|
48
|
+
|
49
|
+
def start
|
50
|
+
clear_old_container
|
51
|
+
run_container
|
52
|
+
if block_given?
|
53
|
+
begin
|
54
|
+
wait
|
55
|
+
return yield(self)
|
56
|
+
ensure
|
57
|
+
stop
|
58
|
+
end
|
59
|
+
else
|
60
|
+
wait
|
61
|
+
return self
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def clear_old_container
|
66
|
+
LoggerPipe.run(logger, "docker rm #{container_name}", timeout: 10)
|
67
|
+
rescue => e
|
68
|
+
logger.warn("[#{e.class}] #{e.message}")
|
69
|
+
end
|
70
|
+
|
71
|
+
def run_container
|
72
|
+
s = LoggerPipe.run(logger, build_docker_command, timeout: 10)
|
73
|
+
|
74
|
+
end
|
75
|
+
|
76
|
+
def build_docker_command
|
77
|
+
"docker run #{build_docker_command_options} #{container_image}"
|
78
|
+
end
|
79
|
+
|
80
|
+
def build_docker_command_options
|
81
|
+
r = "-d -p #{port}:#{self.class.port} --name #{container_name}"
|
82
|
+
if ext = docker_extra_options
|
83
|
+
r << ext
|
84
|
+
end
|
85
|
+
r
|
86
|
+
end
|
87
|
+
|
88
|
+
def docker_extra_options
|
89
|
+
nil
|
90
|
+
end
|
91
|
+
|
92
|
+
def wait
|
93
|
+
# ポートがLISTENされるまで待つ
|
94
|
+
Mcrain.wait_port_opened(host, port, interval: 0.5, timeout: 30)
|
95
|
+
# ポートはdockerがまずLISTENしておいて、その後コンテナ内のredisが起動するので、
|
96
|
+
# 実際にAPIを叩いてみて例外が起きないことを確認します。
|
97
|
+
Timeout.timeout(30) do
|
98
|
+
begin
|
99
|
+
wait_for_ready
|
100
|
+
rescue => e
|
101
|
+
# $stderr.puts "[#{e.class}] #{e.message}"
|
102
|
+
sleep(1)
|
103
|
+
retry
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
def wait_for_ready
|
109
|
+
raise NotImplementedError
|
110
|
+
end
|
111
|
+
|
112
|
+
def client
|
113
|
+
raise NotImplementedError
|
114
|
+
end
|
115
|
+
|
116
|
+
def client_require
|
117
|
+
raise NotImplementedError
|
118
|
+
end
|
119
|
+
|
120
|
+
def client_script
|
121
|
+
raise NotImplementedError
|
122
|
+
end
|
123
|
+
|
124
|
+
def stop
|
125
|
+
LoggerPipe.run(logger, "docker kill #{container_name}", timeout: 10)
|
126
|
+
end
|
127
|
+
|
128
|
+
def logger
|
129
|
+
Mcrain.logger
|
130
|
+
end
|
131
|
+
|
132
|
+
end
|
133
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
require 'mcrain'
|
2
|
+
|
3
|
+
require 'uri'
|
4
|
+
require 'fileutils'
|
5
|
+
require 'rbconfig'
|
6
|
+
|
7
|
+
require 'net/scp'
|
8
|
+
require 'docker'
|
9
|
+
|
10
|
+
module Mcrain
|
11
|
+
module Boot2docker
|
12
|
+
|
13
|
+
class << self
|
14
|
+
attr_accessor :certs_dir
|
15
|
+
end
|
16
|
+
self.certs_dir = File.expand_path('.boot2docker/certs/boot2docker-vm', ENV["HOME"])
|
17
|
+
|
18
|
+
module_function
|
19
|
+
|
20
|
+
def used?
|
21
|
+
RbConfig::CONFIG["host_os"] =~ /darwin/
|
22
|
+
end
|
23
|
+
|
24
|
+
def preparing_command
|
25
|
+
return "" unless used?
|
26
|
+
unless `boot2docker status`.strip == "running"
|
27
|
+
raise "boot2docker is not running. Please `boot2docker start`"
|
28
|
+
end
|
29
|
+
"%s && " % `boot2docker shellinit`.strip.split(/\n/).join(" && ")
|
30
|
+
end
|
31
|
+
|
32
|
+
def setup_docker_options
|
33
|
+
if RbConfig::CONFIG["host_os"] =~ /darwin/
|
34
|
+
require 'docker'
|
35
|
+
uri = URI.parse(ENV["DOCKER_HOST"])
|
36
|
+
Excon.defaults[:ssl_verify_peer] = false
|
37
|
+
Docker.options = build_docker_options(uri)
|
38
|
+
elsif ENV["DOCKER_HOST"].nil?
|
39
|
+
ENV["DOCKER_HOST"] = "http://localhost:2375"
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def build_docker_options(uri)
|
44
|
+
d = Boot2docker.certs_dir
|
45
|
+
cert_path = File.join(d, "cert.pem")
|
46
|
+
key_path = File.join(d, "key.pem")
|
47
|
+
files = {
|
48
|
+
".docker/cert.pem" => cert_path,
|
49
|
+
".docker/key.pem" => key_path,
|
50
|
+
}
|
51
|
+
download_files_from_vm(uri.host, files)
|
52
|
+
return {
|
53
|
+
client_cert: cert_path,
|
54
|
+
client_key: key_path,
|
55
|
+
scheme: 'https',
|
56
|
+
}
|
57
|
+
end
|
58
|
+
|
59
|
+
# http://docs.docker.com/reference/api/docker_remote_api/
|
60
|
+
# https://github.com/boot2docker/boot2docker#ssh-into-vm
|
61
|
+
def download_files_from_vm(host, files)
|
62
|
+
return if files.values.all?{|f| File.readable?(f)}
|
63
|
+
files.values.each{|f| FileUtils.mkdir_p(File.dirname(f))}
|
64
|
+
Net::SCP.start(host, "docker", :password => "tcuser") do |scp|
|
65
|
+
files.each do |src, dest|
|
66
|
+
scp.download(src, dest)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
require 'mcrain'
|
2
|
+
|
3
|
+
# don't require 'rabbitmq/http/client' here in order to use mcrain without 'rabbitmq_http_api_client' gem
|
4
|
+
# require 'rabbitmq/http/client'
|
5
|
+
|
6
|
+
module Mcrain
|
7
|
+
class Rabbitmq < Base
|
8
|
+
self.server_name = :rabbitmq
|
9
|
+
|
10
|
+
self.container_image = "rabbitmq:3.4.4-management"
|
11
|
+
|
12
|
+
def build_docker_command_options
|
13
|
+
"-d -p #{runtime_port}:5672 -p #{port}:15672 --name #{container_name}"
|
14
|
+
end
|
15
|
+
|
16
|
+
def runtime_port
|
17
|
+
@runtime_port ||= find_portno
|
18
|
+
end
|
19
|
+
|
20
|
+
def url
|
21
|
+
"http://#{username}:#{password}@#{host}:#{port}"
|
22
|
+
end
|
23
|
+
|
24
|
+
def runtime_url
|
25
|
+
"rabbitmq://#{host}:#{runtime_port}"
|
26
|
+
end
|
27
|
+
|
28
|
+
def username
|
29
|
+
"guest"
|
30
|
+
end
|
31
|
+
def password
|
32
|
+
"guest"
|
33
|
+
end
|
34
|
+
|
35
|
+
def client
|
36
|
+
require client_require
|
37
|
+
@client ||= RabbitMQ::HTTP::Client.new(*build_client_args)
|
38
|
+
end
|
39
|
+
|
40
|
+
def build_client_args
|
41
|
+
["http://#{host}:#{port}", {username: username, password: password}]
|
42
|
+
end
|
43
|
+
|
44
|
+
def client_require
|
45
|
+
'rabbitmq/http/client'
|
46
|
+
end
|
47
|
+
|
48
|
+
def client_script
|
49
|
+
client
|
50
|
+
"RabbitMQ::HTTP::Client.new(*#{build_client_args.inspect})"
|
51
|
+
end
|
52
|
+
|
53
|
+
def wait_for_ready
|
54
|
+
client.list_users
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
data/lib/mcrain/redis.rb
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'mcrain'
|
2
|
+
|
3
|
+
# don't require 'redis' here in order to use mcrain without 'redis' gem
|
4
|
+
# require 'redis'
|
5
|
+
|
6
|
+
module Mcrain
|
7
|
+
class Redis < Base
|
8
|
+
self.server_name = :redis
|
9
|
+
|
10
|
+
self.container_image = "redis:2.8.19"
|
11
|
+
self.port = 6379
|
12
|
+
|
13
|
+
def client
|
14
|
+
require client_require
|
15
|
+
@client ||= ::Redis.new(build_client_options)
|
16
|
+
end
|
17
|
+
|
18
|
+
def build_client_options
|
19
|
+
{host: host, port: port}
|
20
|
+
end
|
21
|
+
|
22
|
+
def client_require
|
23
|
+
'redis'
|
24
|
+
end
|
25
|
+
|
26
|
+
def client_script
|
27
|
+
client
|
28
|
+
"Redis.new(#{build_client_options.inspect})"
|
29
|
+
end
|
30
|
+
|
31
|
+
def wait_for_ready
|
32
|
+
client.keys
|
33
|
+
end
|
34
|
+
|
35
|
+
attr_accessor :db_dir
|
36
|
+
|
37
|
+
def docker_extra_options
|
38
|
+
db_dir ? " -v #{File.expand_path(db_dir)}:/data" : nil
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
data/lib/mcrain/riak.rb
ADDED
@@ -0,0 +1,155 @@
|
|
1
|
+
require 'mcrain'
|
2
|
+
|
3
|
+
# require 'riak'
|
4
|
+
|
5
|
+
module Mcrain
|
6
|
+
class Riak < Base
|
7
|
+
|
8
|
+
class << self
|
9
|
+
# path to clone of https://github.com/hectcastro/docker-riak
|
10
|
+
attr_accessor :docker_riak_path
|
11
|
+
end
|
12
|
+
|
13
|
+
self.server_name = :riak
|
14
|
+
|
15
|
+
self.container_image = nil # not use docker directly
|
16
|
+
self.port = 8087
|
17
|
+
|
18
|
+
def wait
|
19
|
+
build_uris
|
20
|
+
super
|
21
|
+
end
|
22
|
+
|
23
|
+
def client
|
24
|
+
unless @client
|
25
|
+
require client_require
|
26
|
+
build_uris
|
27
|
+
@client = ::Riak::Client.new(build_client_options)
|
28
|
+
end
|
29
|
+
@client
|
30
|
+
end
|
31
|
+
|
32
|
+
def build_client_options
|
33
|
+
options = {
|
34
|
+
nodes: uris.map{|uri| {host: uri.host, pb_port: uri.port} }
|
35
|
+
}
|
36
|
+
if uri = uris.first
|
37
|
+
if !uri.user.blank? or !uri.password.blank?
|
38
|
+
options[:authentication] = {user: uri.user, password: uri.password}
|
39
|
+
end
|
40
|
+
end
|
41
|
+
options
|
42
|
+
end
|
43
|
+
|
44
|
+
def client_require
|
45
|
+
'riak'
|
46
|
+
end
|
47
|
+
|
48
|
+
def client_script
|
49
|
+
client
|
50
|
+
"Riak::Client.new(#{build_client_options.inspect})"
|
51
|
+
end
|
52
|
+
|
53
|
+
def build_uris
|
54
|
+
# https://github.com/hectcastro/docker-riak/blob/develop/bin/test-cluster.sh#L9
|
55
|
+
|
56
|
+
# http://docs.docker.com/reference/api/docker_remote_api/
|
57
|
+
# https://github.com/boot2docker/boot2docker#ssh-into-vm
|
58
|
+
Boot2docker.setup_docker_options
|
59
|
+
|
60
|
+
uri = URI.parse(ENV["DOCKER_HOST"])
|
61
|
+
@host = (uri.scheme == "unix") ? "localhost" : uri.host
|
62
|
+
list = Docker::Container.all
|
63
|
+
riak_containers = list.select{|r| r.info['Image'] == "hectcastro/riak:latest"}
|
64
|
+
@cids = riak_containers.map(&:id)
|
65
|
+
@pb_ports = riak_containers.map do |r|
|
66
|
+
map = r.info['Ports'].each_with_object({}){|rr,d| d[ rr['PrivatePort'] ] = rr['PublicPort']}
|
67
|
+
map[8087]
|
68
|
+
end
|
69
|
+
@port = @pb_ports.first
|
70
|
+
@admin_uris = @cids.map do |cid|
|
71
|
+
r = Docker::Container.get(cid)
|
72
|
+
host = r.info["NetworkSettings"]["IPAddress"]
|
73
|
+
# login with insecure_key
|
74
|
+
# https://github.com/phusion/baseimage-docker#using-the-insecure-key-for-one-container-only
|
75
|
+
"ssh://root@#{host}:22"
|
76
|
+
end
|
77
|
+
@uris = @pb_ports.map do |port|
|
78
|
+
URI::Generic.build(scheme: "riak", host: @host, port: port)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def wait_for_ready
|
83
|
+
c = client
|
84
|
+
logger.debug("sending a ping")
|
85
|
+
r = c.ping
|
86
|
+
raise "Ping failure with #{c.inspect}" unless r
|
87
|
+
20.times do |i|
|
88
|
+
begin
|
89
|
+
logger.debug("get and store ##{i}")
|
90
|
+
o1 = c.bucket("test").get_or_new("foo")
|
91
|
+
o1.data = {"bar" => 100}
|
92
|
+
o1.store
|
93
|
+
o2 = c.bucket("test").get_or_new("foo")
|
94
|
+
raise "Something wrong!" unless o2.data == o1.data
|
95
|
+
break
|
96
|
+
rescue => e
|
97
|
+
if e.message =~ /Expected success from Riak but received 0/
|
98
|
+
sleep(0.5)
|
99
|
+
logger.debug("retrying [#{e.class}] #{e.message}")
|
100
|
+
retry
|
101
|
+
else
|
102
|
+
logger.warn(e)
|
103
|
+
raise
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
def clear_old_container
|
110
|
+
end
|
111
|
+
|
112
|
+
attr_reader :host, :cids, :pb_ports, :uris, :admin_uris
|
113
|
+
attr_accessor :automatic_clustering, :cluster_size
|
114
|
+
|
115
|
+
def initialize
|
116
|
+
w = @work_dir = Mcrain::Riak.docker_riak_path
|
117
|
+
raise "#{self.class.name}.docker_riak_path is blank. You have to set it to use the class" if w.blank?
|
118
|
+
raise "#{w}/Makefile not found" unless File.readable?(File.join(w, "Makefile"))
|
119
|
+
@prepare_cmd = Boot2docker.preparing_command
|
120
|
+
@automatic_clustering = false
|
121
|
+
@cluster_size = 1
|
122
|
+
end
|
123
|
+
|
124
|
+
def build_command
|
125
|
+
"DOCKER_RIAK_AUTOMATIC_CLUSTERING=#{automatic_clustering ? 1 : 0} DOCKER_RIAK_CLUSTER_SIZE=#{cluster_size} make start-cluster"
|
126
|
+
end
|
127
|
+
|
128
|
+
def run_container
|
129
|
+
logger.debug("cd #{@work_dir.inspect}")
|
130
|
+
Dir.chdir(@work_dir) do
|
131
|
+
# http://basho.co.jp/riak-quick-start-with-docker/
|
132
|
+
LoggerPipe.run(logger, "#{@prepare_cmd} #{build_command}")
|
133
|
+
sleep(1)
|
134
|
+
20.times do
|
135
|
+
begin
|
136
|
+
LoggerPipe.run(logger, "#{@prepare_cmd} make test-cluster")
|
137
|
+
sleep(45) # Please wait approximately 30 seconds for the cluster to stabilize
|
138
|
+
return
|
139
|
+
rescue
|
140
|
+
sleep(0.5)
|
141
|
+
retry
|
142
|
+
end
|
143
|
+
end
|
144
|
+
raise "failed to run a riak server"
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
def stop
|
149
|
+
Dir.chdir(@work_dir) do
|
150
|
+
LoggerPipe.run(logger, "#{@prepare_cmd} make stop-cluster")
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
end
|
155
|
+
end
|
data/lib/mcrain.rb
ADDED
@@ -0,0 +1,88 @@
|
|
1
|
+
require "mcrain/version"
|
2
|
+
|
3
|
+
require 'timeout'
|
4
|
+
require 'logger'
|
5
|
+
|
6
|
+
require 'logger_pipe'
|
7
|
+
|
8
|
+
require 'active_support/inflector/inflections'
|
9
|
+
require 'active_support/core_ext/class/subclasses'
|
10
|
+
require 'active_support/core_ext/object/blank'
|
11
|
+
|
12
|
+
module Mcrain
|
13
|
+
class << self
|
14
|
+
def [](name)
|
15
|
+
lookup(name)
|
16
|
+
end
|
17
|
+
|
18
|
+
def lookup(name, options = {})
|
19
|
+
klass = class_for(name.to_sym)
|
20
|
+
r = instances[name] ||= klass.new
|
21
|
+
options.each{|k,v| r.send("#{k}=", v)}
|
22
|
+
r
|
23
|
+
end
|
24
|
+
|
25
|
+
def class_names
|
26
|
+
@class_names ||= {}
|
27
|
+
end
|
28
|
+
|
29
|
+
def class_for(name)
|
30
|
+
if class_name = class_names[name]
|
31
|
+
class_name.constantize
|
32
|
+
else
|
33
|
+
if klass = Mcrain::Base.descendants.detect{|c| c.server_name == name}
|
34
|
+
class_names[name] = klass.name
|
35
|
+
end
|
36
|
+
klass
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def register(name, class_name)
|
41
|
+
class_names[name] = class_name
|
42
|
+
end
|
43
|
+
|
44
|
+
def instances
|
45
|
+
@instances ||= {}
|
46
|
+
end
|
47
|
+
|
48
|
+
def pull_images
|
49
|
+
Mcrain::Base.descendants.each do |klass|
|
50
|
+
Timeout.timeout(10.minutes) do
|
51
|
+
LoggerPipe.run(logger, "docker pull #{klass.container_image}")
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
attr_writer :logger
|
57
|
+
def logger
|
58
|
+
@logger ||= Logger.new($stderr)
|
59
|
+
end
|
60
|
+
|
61
|
+
def wait_port_opened(host, port, options = {})
|
62
|
+
logger.debug("wait_port_opened(#{host.inspect}, #{port.inspect}, #{options.inspect})")
|
63
|
+
interval = options[:interval] || 10 # second
|
64
|
+
Timeout.timeout(options[:timeout] || 60) do
|
65
|
+
begin
|
66
|
+
s = TCPSocket.open(host, port)
|
67
|
+
s.close
|
68
|
+
return true
|
69
|
+
rescue Errno::ECONNREFUSED
|
70
|
+
sleep(interval)
|
71
|
+
retry
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
end
|
77
|
+
|
78
|
+
autoload :Base, 'mcrain/base'
|
79
|
+
autoload :Boot2docker, 'mcrain/boot2docker'
|
80
|
+
|
81
|
+
autoload :Riak, 'mcrain/riak'
|
82
|
+
autoload :Redis, 'mcrain/redis'
|
83
|
+
autoload :Rabbitmq, 'mcrain/rabbitmq'
|
84
|
+
|
85
|
+
register :riak, "Mcrain::Riak"
|
86
|
+
register :redis, "Mcrain::Redis"
|
87
|
+
register :rabbitmq, "Mcrain::Rabbitmq"
|
88
|
+
end
|
data/mcrain.gemspec
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'mcrain/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "mcrain"
|
8
|
+
spec.version = Mcrain::VERSION
|
9
|
+
spec.authors = ["akm"]
|
10
|
+
spec.email = ["t-akima@groovenauts.jp"]
|
11
|
+
|
12
|
+
spec.summary = %q{mcrain supports to run docker container for test.}
|
13
|
+
spec.description = %q{mcrain supports to run docker container for test.}
|
14
|
+
spec.homepage = "https://github.com/groovenauts/mcrain"
|
15
|
+
spec.license = "MIT"
|
16
|
+
|
17
|
+
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
18
|
+
spec.bindir = "exe"
|
19
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
20
|
+
spec.require_paths = ["lib"]
|
21
|
+
|
22
|
+
spec.add_runtime_dependency "logger_pipe", "~> 0.3.1"
|
23
|
+
spec.add_runtime_dependency "net-scp", "~> 1.2.1"
|
24
|
+
spec.add_runtime_dependency "activesupport", ">= 3.0", "< 5.0"
|
25
|
+
|
26
|
+
spec.add_development_dependency "bundler", "~> 1.9"
|
27
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
28
|
+
spec.add_development_dependency "rspec"
|
29
|
+
end
|
metadata
ADDED
@@ -0,0 +1,154 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: mcrain
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- akm
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-05-07 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: logger_pipe
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 0.3.1
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 0.3.1
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: net-scp
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 1.2.1
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 1.2.1
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: activesupport
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '3.0'
|
48
|
+
- - "<"
|
49
|
+
- !ruby/object:Gem::Version
|
50
|
+
version: '5.0'
|
51
|
+
type: :runtime
|
52
|
+
prerelease: false
|
53
|
+
version_requirements: !ruby/object:Gem::Requirement
|
54
|
+
requirements:
|
55
|
+
- - ">="
|
56
|
+
- !ruby/object:Gem::Version
|
57
|
+
version: '3.0'
|
58
|
+
- - "<"
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: '5.0'
|
61
|
+
- !ruby/object:Gem::Dependency
|
62
|
+
name: bundler
|
63
|
+
requirement: !ruby/object:Gem::Requirement
|
64
|
+
requirements:
|
65
|
+
- - "~>"
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
version: '1.9'
|
68
|
+
type: :development
|
69
|
+
prerelease: false
|
70
|
+
version_requirements: !ruby/object:Gem::Requirement
|
71
|
+
requirements:
|
72
|
+
- - "~>"
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
version: '1.9'
|
75
|
+
- !ruby/object:Gem::Dependency
|
76
|
+
name: rake
|
77
|
+
requirement: !ruby/object:Gem::Requirement
|
78
|
+
requirements:
|
79
|
+
- - "~>"
|
80
|
+
- !ruby/object:Gem::Version
|
81
|
+
version: '10.0'
|
82
|
+
type: :development
|
83
|
+
prerelease: false
|
84
|
+
version_requirements: !ruby/object:Gem::Requirement
|
85
|
+
requirements:
|
86
|
+
- - "~>"
|
87
|
+
- !ruby/object:Gem::Version
|
88
|
+
version: '10.0'
|
89
|
+
- !ruby/object:Gem::Dependency
|
90
|
+
name: rspec
|
91
|
+
requirement: !ruby/object:Gem::Requirement
|
92
|
+
requirements:
|
93
|
+
- - ">="
|
94
|
+
- !ruby/object:Gem::Version
|
95
|
+
version: '0'
|
96
|
+
type: :development
|
97
|
+
prerelease: false
|
98
|
+
version_requirements: !ruby/object:Gem::Requirement
|
99
|
+
requirements:
|
100
|
+
- - ">="
|
101
|
+
- !ruby/object:Gem::Version
|
102
|
+
version: '0'
|
103
|
+
description: mcrain supports to run docker container for test.
|
104
|
+
email:
|
105
|
+
- t-akima@groovenauts.jp
|
106
|
+
executables:
|
107
|
+
- mcrain
|
108
|
+
extensions: []
|
109
|
+
extra_rdoc_files: []
|
110
|
+
files:
|
111
|
+
- ".gitignore"
|
112
|
+
- ".gitmodules"
|
113
|
+
- ".rspec"
|
114
|
+
- ".travis.yml"
|
115
|
+
- CODE_OF_CONDUCT.md
|
116
|
+
- Gemfile
|
117
|
+
- README.md
|
118
|
+
- Rakefile
|
119
|
+
- bin/console
|
120
|
+
- bin/setup
|
121
|
+
- exe/mcrain
|
122
|
+
- lib/mcrain.rb
|
123
|
+
- lib/mcrain/base.rb
|
124
|
+
- lib/mcrain/boot2docker.rb
|
125
|
+
- lib/mcrain/rabbitmq.rb
|
126
|
+
- lib/mcrain/redis.rb
|
127
|
+
- lib/mcrain/riak.rb
|
128
|
+
- lib/mcrain/version.rb
|
129
|
+
- mcrain.gemspec
|
130
|
+
homepage: https://github.com/groovenauts/mcrain
|
131
|
+
licenses:
|
132
|
+
- MIT
|
133
|
+
metadata: {}
|
134
|
+
post_install_message:
|
135
|
+
rdoc_options: []
|
136
|
+
require_paths:
|
137
|
+
- lib
|
138
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
139
|
+
requirements:
|
140
|
+
- - ">="
|
141
|
+
- !ruby/object:Gem::Version
|
142
|
+
version: '0'
|
143
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
144
|
+
requirements:
|
145
|
+
- - ">="
|
146
|
+
- !ruby/object:Gem::Version
|
147
|
+
version: '0'
|
148
|
+
requirements: []
|
149
|
+
rubyforge_project:
|
150
|
+
rubygems_version: 2.4.5
|
151
|
+
signing_key:
|
152
|
+
specification_version: 4
|
153
|
+
summary: mcrain supports to run docker container for test.
|
154
|
+
test_files: []
|