infrataster 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 +18 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +133 -0
- data/Rakefile +1 -0
- data/example/.gitignore +4 -0
- data/example/.rspec +2 -0
- data/example/Berksfile +2 -0
- data/example/Gemfile +8 -0
- data/example/README.md +13 -0
- data/example/Vagrantfile +81 -0
- data/example/cookbooks/app/recipes/default.rb +36 -0
- data/example/cookbooks/db/files/default/my.cnf +127 -0
- data/example/cookbooks/db/recipes/default.rb +14 -0
- data/example/cookbooks/proxy/metadata.rb +1 -0
- data/example/cookbooks/proxy/recipes/default.rb +26 -0
- data/example/cookbooks/proxy/templates/default/app.erb +13 -0
- data/example/cookbooks/proxy/templates/default/static.erb +14 -0
- data/example/sinatra_app/.gitignore +1 -0
- data/example/sinatra_app/Gemfile +5 -0
- data/example/sinatra_app/app.rb +6 -0
- data/example/sinatra_app/config.ru +2 -0
- data/example/spec/spec_helper.rb +31 -0
- data/example/spec/web_spec.rb +64 -0
- data/infrataster.gemspec +30 -0
- data/lib/infrataster/browsermob_proxy.rb +31 -0
- data/lib/infrataster/contexts/base_context.rb +14 -0
- data/lib/infrataster/contexts/capybara_context.rb +56 -0
- data/lib/infrataster/contexts/http_context.rb +20 -0
- data/lib/infrataster/contexts/mysql_query_context.rb +26 -0
- data/lib/infrataster/contexts.rb +36 -0
- data/lib/infrataster/helpers/rspec_helper.rb +15 -0
- data/lib/infrataster/helpers/type_helper.rb +24 -0
- data/lib/infrataster/helpers.rb +4 -0
- data/lib/infrataster/rspec.rb +17 -0
- data/lib/infrataster/server.rb +125 -0
- data/lib/infrataster/types/base_type.rb +15 -0
- data/lib/infrataster/types/capybara_type.rb +21 -0
- data/lib/infrataster/types/http_type.rb +24 -0
- data/lib/infrataster/types/mysql_query_type.rb +21 -0
- data/lib/infrataster/types/server_type.rb +25 -0
- data/lib/infrataster/types.rb +5 -0
- data/lib/infrataster/version.rb +3 -0
- data/lib/infrataster.rb +12 -0
- metadata +214 -0
data/infrataster.gemspec
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'infrataster/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "infrataster"
|
8
|
+
spec.version = Infrataster::VERSION
|
9
|
+
spec.authors = ["Ryota Arai"]
|
10
|
+
spec.email = ["ryota.arai@gmail.com"]
|
11
|
+
spec.summary = %q{Infrastructure Behavior Testing Framework}
|
12
|
+
spec.homepage = "https://github.com/ryotarai/infrataster"
|
13
|
+
spec.license = "MIT"
|
14
|
+
|
15
|
+
spec.files = `git ls-files -z`.split("\x0")
|
16
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
17
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
18
|
+
spec.require_paths = ["lib"]
|
19
|
+
|
20
|
+
spec.add_runtime_dependency "rspec"
|
21
|
+
spec.add_runtime_dependency "net-ssh"
|
22
|
+
spec.add_runtime_dependency "net-ssh-gateway"
|
23
|
+
spec.add_runtime_dependency "mysql2"
|
24
|
+
spec.add_runtime_dependency "capybara"
|
25
|
+
spec.add_runtime_dependency "selenium-webdriver"
|
26
|
+
spec.add_runtime_dependency "browsermob-proxy"
|
27
|
+
|
28
|
+
spec.add_development_dependency "bundler", "~> 1.5"
|
29
|
+
spec.add_development_dependency "rake"
|
30
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'browsermob/proxy'
|
2
|
+
|
3
|
+
module Infrataster
|
4
|
+
module BrowsermobProxy
|
5
|
+
class << self
|
6
|
+
def server
|
7
|
+
@server ||= start_server
|
8
|
+
end
|
9
|
+
|
10
|
+
def bin_path
|
11
|
+
@bin_path
|
12
|
+
end
|
13
|
+
|
14
|
+
def bin_path=(path)
|
15
|
+
@bin_path = path
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
def start_server
|
20
|
+
BrowserMob::Proxy::Server.new(find_bin).tap do |server|
|
21
|
+
server.start
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def find_bin
|
26
|
+
return @bin_path if @bin_path
|
27
|
+
`which browsermob-proxy`
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
require 'capybara'
|
2
|
+
require 'capybara/rspec/matchers'
|
3
|
+
require 'selenium-webdriver'
|
4
|
+
|
5
|
+
module Infrataster
|
6
|
+
module Contexts
|
7
|
+
class CapybaraContext < BaseContext
|
8
|
+
def initialize(*args)
|
9
|
+
super(*args)
|
10
|
+
|
11
|
+
register_capybara_driver
|
12
|
+
end
|
13
|
+
|
14
|
+
def session
|
15
|
+
return @session if @session
|
16
|
+
|
17
|
+
address, port, @gateway_finalize_proc = server.from_gateway(type.uri.port)
|
18
|
+
Capybara.app_host = "http://#{address}:#{port}"
|
19
|
+
@session = Capybara::Session.new(capybara_driver_name)
|
20
|
+
end
|
21
|
+
|
22
|
+
def page
|
23
|
+
session
|
24
|
+
end
|
25
|
+
|
26
|
+
def before_each(example)
|
27
|
+
example.example_group_instance.extend(Capybara::RSpecMatchers)
|
28
|
+
end
|
29
|
+
|
30
|
+
def after_each(example)
|
31
|
+
@gateway_finalize_proc.call if @gateway_finalize_proc
|
32
|
+
end
|
33
|
+
|
34
|
+
def method_missing(method, *args)
|
35
|
+
session.public_send(method, *args)
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
def register_capybara_driver
|
40
|
+
proxy = BrowsermobProxy.server.create_proxy
|
41
|
+
proxy.header({"Host" => type.uri.host})
|
42
|
+
|
43
|
+
profile = Selenium::WebDriver::Firefox::Profile.new
|
44
|
+
profile.proxy = proxy.selenium_proxy
|
45
|
+
|
46
|
+
Capybara.register_driver capybara_driver_name do |app|
|
47
|
+
Capybara::Selenium::Driver.new(app, profile: profile)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def capybara_driver_name
|
52
|
+
:"selenium_via_proxy_#{hash}"
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'net/http'
|
2
|
+
require 'net/https'
|
3
|
+
|
4
|
+
module Infrataster
|
5
|
+
module Contexts
|
6
|
+
class HttpContext < BaseContext
|
7
|
+
def response
|
8
|
+
req = Net::HTTP::Get.new('/', {'Host' => type.uri.host})
|
9
|
+
|
10
|
+
server.from_gateway(type.uri.port) do |address, port|
|
11
|
+
Net::HTTP.start(address, port) do |http|
|
12
|
+
http.request(req)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'mysql2'
|
2
|
+
|
3
|
+
module Infrataster
|
4
|
+
module Contexts
|
5
|
+
class MysqlQueryContext < BaseContext
|
6
|
+
def results
|
7
|
+
options = {port: 3306, user: 'root', password: ''}
|
8
|
+
if server.options[:mysql]
|
9
|
+
options = options.merge(server.options[:mysql])
|
10
|
+
end
|
11
|
+
|
12
|
+
server.from_gateway(options[:port]) do |address, new_port|
|
13
|
+
client = Mysql2::Client.new(
|
14
|
+
host: address,
|
15
|
+
port: new_port,
|
16
|
+
username: options[:user],
|
17
|
+
password: options[:password],
|
18
|
+
)
|
19
|
+
client.query(type.query)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'infrataster/types'
|
2
|
+
require 'infrataster/contexts/base_context'
|
3
|
+
require 'infrataster/contexts/http_context'
|
4
|
+
require 'infrataster/contexts/mysql_query_context'
|
5
|
+
require 'infrataster/contexts/capybara_context'
|
6
|
+
|
7
|
+
module Infrataster
|
8
|
+
module Contexts
|
9
|
+
class << self
|
10
|
+
def from_example(example)
|
11
|
+
example_group = example.metadata[:example_group]
|
12
|
+
|
13
|
+
server = find_described(Types::ServerType, example_group).server
|
14
|
+
type = find_described(Types::BaseType, example_group)
|
15
|
+
|
16
|
+
type.context_class.new(server, type)
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
def find_described(type_class, example_group)
|
21
|
+
arg = example_group[:description_args].first
|
22
|
+
if arg.is_a?(type_class)
|
23
|
+
arg
|
24
|
+
else
|
25
|
+
parent_example_group = example_group[:example_group]
|
26
|
+
if parent_example_group
|
27
|
+
find_described(type_class, parent_example_group)
|
28
|
+
else
|
29
|
+
raise Error
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'infrataster/types'
|
2
|
+
|
3
|
+
module Infrataster
|
4
|
+
module Helpers
|
5
|
+
module TypeHelper
|
6
|
+
def server(*args)
|
7
|
+
Types::ServerType.new(*args)
|
8
|
+
end
|
9
|
+
|
10
|
+
def http(*args)
|
11
|
+
Types::HttpType.new(*args)
|
12
|
+
end
|
13
|
+
|
14
|
+
def mysql_query(*args)
|
15
|
+
Types::MysqlQueryType.new(*args)
|
16
|
+
end
|
17
|
+
|
18
|
+
def capybara(*args)
|
19
|
+
Types::CapybaraType.new(*args)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'infrataster'
|
2
|
+
require 'rspec'
|
3
|
+
|
4
|
+
include Infrataster::Helpers::TypeHelper
|
5
|
+
|
6
|
+
RSpec.configure do |config|
|
7
|
+
config.include Infrataster::Helpers::RSpecHelper
|
8
|
+
|
9
|
+
config.before(:each) do
|
10
|
+
@infrataster_context = Infrataster::Contexts.from_example(example)
|
11
|
+
@infrataster_context.before_each(example) if @infrataster_context.respond_to?(:before_each)
|
12
|
+
end
|
13
|
+
config.after(:each) do
|
14
|
+
@infrataster_context.after_each(example) if @infrataster_context.respond_to?(:after_each)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
@@ -0,0 +1,125 @@
|
|
1
|
+
require 'tmpdir'
|
2
|
+
require 'net/ssh'
|
3
|
+
require 'net/ssh/gateway'
|
4
|
+
require 'ipaddr'
|
5
|
+
|
6
|
+
module Infrataster
|
7
|
+
class Server
|
8
|
+
Error = Class.new(StandardError)
|
9
|
+
|
10
|
+
class << self
|
11
|
+
@@servers = []
|
12
|
+
|
13
|
+
def define(*args)
|
14
|
+
@@servers << Server.new(*args)
|
15
|
+
end
|
16
|
+
|
17
|
+
def find_by_name(name)
|
18
|
+
server = @@servers.find {|s| s.name == name }
|
19
|
+
unless server
|
20
|
+
raise Error, "Server definition for '#{name}' is not found."
|
21
|
+
end
|
22
|
+
server
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
|
27
|
+
attr_reader :name, :address, :options
|
28
|
+
|
29
|
+
def initialize(name, address, options = {})
|
30
|
+
@name, @address, @options = name, address, options
|
31
|
+
end
|
32
|
+
|
33
|
+
def from
|
34
|
+
if @options[:from]
|
35
|
+
Server.find_by_name(@options[:from])
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def ssh_gateway
|
40
|
+
gateway = Net::SSH::Gateway.new(*ssh_start_args)
|
41
|
+
finalize_proc = Proc.new do
|
42
|
+
gateway.shutdown!
|
43
|
+
end
|
44
|
+
|
45
|
+
[gateway, finalize_proc]
|
46
|
+
end
|
47
|
+
|
48
|
+
def with_ssh_gateway
|
49
|
+
gateway, finalize_proc = ssh_gateway
|
50
|
+
yield gateway
|
51
|
+
ensure
|
52
|
+
finalize_proc.call
|
53
|
+
end
|
54
|
+
|
55
|
+
def gateway_open(*args)
|
56
|
+
if block_given?
|
57
|
+
with_ssh_gateway do |gateway|
|
58
|
+
gateway.open(*args) do |port|
|
59
|
+
yield port
|
60
|
+
end
|
61
|
+
end
|
62
|
+
else
|
63
|
+
gateway, gateway_finalize_proc = ssh_gateway
|
64
|
+
port = gateway.open(*args)
|
65
|
+
finalize_proc = Proc.new do
|
66
|
+
gateway.close(port)
|
67
|
+
gateway_finalize_proc.call
|
68
|
+
end
|
69
|
+
[port, finalize_proc]
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def from_gateway(port)
|
74
|
+
if from
|
75
|
+
if block_given?
|
76
|
+
from.gateway_open(@address, port) do |new_port|
|
77
|
+
Logger.debug("tunnel: localhost:#{new_port} -> #{from.address} -> #{@address}:#{port}")
|
78
|
+
yield '127.0.0.1', new_port
|
79
|
+
end
|
80
|
+
else
|
81
|
+
new_port, finalize_proc = from.gateway_open(@address, port)
|
82
|
+
Logger.debug("tunnel: localhost:#{new_port} -> #{from.address} -> #{@address}:#{port}")
|
83
|
+
['127.0.0.1', new_port, finalize_proc]
|
84
|
+
end
|
85
|
+
else
|
86
|
+
if block_given?
|
87
|
+
yield @address, port
|
88
|
+
else
|
89
|
+
[@address, port, nil]
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
def ssh_start_args
|
95
|
+
@ssh_start_args ||= _ssh_start_args
|
96
|
+
end
|
97
|
+
|
98
|
+
private
|
99
|
+
|
100
|
+
|
101
|
+
def _ssh_start_args
|
102
|
+
config = {}
|
103
|
+
if @options[:ssh]
|
104
|
+
config = @options[:ssh]
|
105
|
+
elsif @options[:vagrant]
|
106
|
+
if @options[:vagrant] != true
|
107
|
+
vagrant_name = @options[:vagrant]
|
108
|
+
else
|
109
|
+
vagrant_name = @name
|
110
|
+
end
|
111
|
+
|
112
|
+
Dir.mktmpdir do |dir|
|
113
|
+
output = File.join(dir, 'ssh-config')
|
114
|
+
system("/usr/bin/vagrant ssh-config #{vagrant_name} > #{output}")
|
115
|
+
config = Net::SSH::Config.for(@name.to_s, [output])
|
116
|
+
end
|
117
|
+
else
|
118
|
+
raise Error, "Can't get configuration to connect to the server via SSH. Please set ssh option or vagrant option."
|
119
|
+
end
|
120
|
+
|
121
|
+
[config[:host], config[:user], config]
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'infrataster/types/base_type'
|
2
|
+
require 'uri'
|
3
|
+
|
4
|
+
module Infrataster
|
5
|
+
module Types
|
6
|
+
class CapybaraType < BaseType
|
7
|
+
Error = Class.new(StandardError)
|
8
|
+
|
9
|
+
attr_reader :uri
|
10
|
+
|
11
|
+
def initialize(url)
|
12
|
+
@uri = URI.parse(url)
|
13
|
+
end
|
14
|
+
|
15
|
+
def to_s
|
16
|
+
"capybara '#{@uri}'"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'infrataster/types/base_type'
|
2
|
+
require 'uri'
|
3
|
+
|
4
|
+
module Infrataster
|
5
|
+
module Types
|
6
|
+
class HttpType < BaseType
|
7
|
+
Error = Class.new(StandardError)
|
8
|
+
|
9
|
+
attr_reader :uri
|
10
|
+
|
11
|
+
def initialize(url_str)
|
12
|
+
@uri = URI.parse(url_str)
|
13
|
+
unless %w!http https!.include?(@uri.scheme)
|
14
|
+
raise Error, "The provided url, '#{@uri}', is not http or https."
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def to_s
|
19
|
+
"http '#{@uri}'"
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'infrataster/types/base_type'
|
2
|
+
|
3
|
+
module Infrataster
|
4
|
+
module Types
|
5
|
+
class MysqlQueryType < BaseType
|
6
|
+
Error = Class.new(StandardError)
|
7
|
+
|
8
|
+
attr_reader :query
|
9
|
+
|
10
|
+
def initialize(query, options = {})
|
11
|
+
@query = query
|
12
|
+
@options = options
|
13
|
+
end
|
14
|
+
|
15
|
+
def to_s
|
16
|
+
"mysql '#{@query}'"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Infrataster
|
2
|
+
module Types
|
3
|
+
class ServerType
|
4
|
+
Error = Class.new(StandardError)
|
5
|
+
|
6
|
+
attr_reader :name
|
7
|
+
|
8
|
+
def initialize(name)
|
9
|
+
@name = name
|
10
|
+
end
|
11
|
+
|
12
|
+
def to_s
|
13
|
+
desc = "server '#{server.name}'"
|
14
|
+
desc += " from '#{server.from.name}'" if server.from
|
15
|
+
desc
|
16
|
+
end
|
17
|
+
|
18
|
+
def server
|
19
|
+
Server.find_by_name(@name)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
|
data/lib/infrataster.rb
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
require "infrataster/version"
|
2
|
+
require "infrataster/types"
|
3
|
+
require "infrataster/server"
|
4
|
+
require "infrataster/helpers"
|
5
|
+
require "infrataster/browsermob_proxy"
|
6
|
+
require "infrataster/contexts"
|
7
|
+
require 'logger'
|
8
|
+
|
9
|
+
module Infrataster
|
10
|
+
Logger = ::Logger.new($stdout)
|
11
|
+
Logger.level = ::Logger::ERROR
|
12
|
+
end
|