vagrant-test-subject 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.
- data/Changelog +2 -0
- data/LICENSE +29 -0
- data/README.md +33 -0
- data/lib/vagrant-test-subject.rb +148 -0
- data/lib/vagrant-test-subject/monkey-patches/rspec-http.rb +27 -0
- data/lib/vagrant-test-subject/os/omnios.rb +12 -0
- data/lib/vagrant-test-subject/os/redhat.rb +56 -0
- data/lib/vagrant-test-subject/ssh.rb +41 -0
- metadata +85 -0
data/Changelog
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
Copyright (c) 2013, OmniTI Computer Consulting, Inc.
|
2
|
+
All rights reserved.
|
3
|
+
|
4
|
+
Redistribution and use in source and binary forms, with or without
|
5
|
+
modification, are permitted provided that the following conditions are
|
6
|
+
met:
|
7
|
+
|
8
|
+
* Redistributions of source code must retain the above copyright
|
9
|
+
notice, this list of conditions and the following disclaimer.
|
10
|
+
* Redistributions in binary form must reproduce the above
|
11
|
+
copyright notice, this list of conditions and the following
|
12
|
+
disclaimer in the documentation and/or other materials provided
|
13
|
+
with the distribution.
|
14
|
+
* Neither the name OmniTI Computer Consulting, Inc. nor the names
|
15
|
+
of its contributors may be used to endorse or promote products
|
16
|
+
derived from this software without specific prior written
|
17
|
+
permission.
|
18
|
+
|
19
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
20
|
+
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
21
|
+
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
22
|
+
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
23
|
+
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
24
|
+
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
25
|
+
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
26
|
+
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
27
|
+
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
28
|
+
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
29
|
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
data/README.md
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
vagrant-test-subject
|
2
|
+
====================
|
3
|
+
|
4
|
+
A wrapper around a Vagrant VM, to ease using it as a test subject from rspec and other ruby testing tools.
|
5
|
+
|
6
|
+
# Synopsis
|
7
|
+
|
8
|
+
describe "TrafficServer Service" do
|
9
|
+
before(:all) do
|
10
|
+
@vm = VagrantTestSubject::VM.attach()
|
11
|
+
end
|
12
|
+
it "should appear as a healthy service" do
|
13
|
+
@vm.should have_running_service("trafficserver")
|
14
|
+
end
|
15
|
+
it "should be listening on localhost:80" do
|
16
|
+
@vm.should be_listening_on_localhost(80)
|
17
|
+
end
|
18
|
+
it "should be listening on external_ip:80" do
|
19
|
+
@vm.should be_listening_on_external_ip(80)
|
20
|
+
end
|
21
|
+
it "should be the right process name on port 80" do
|
22
|
+
process = @vm.process_name_listening('127.0.0.1', 80)
|
23
|
+
process.should_not be_nil
|
24
|
+
process.should match(/\/opt\/ts\/bin\/traffic_manager/)
|
25
|
+
end
|
26
|
+
it "should respond with HTTP 200 to / on port 80" do
|
27
|
+
@vm.http_get('/').should be_http_ok
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
# Maturity
|
32
|
+
|
33
|
+
Alpha.
|
@@ -0,0 +1,148 @@
|
|
1
|
+
|
2
|
+
# Std lib
|
3
|
+
require 'tempfile'
|
4
|
+
require 'json'
|
5
|
+
require 'net/http'
|
6
|
+
require 'socket'
|
7
|
+
require 'timeout'
|
8
|
+
|
9
|
+
# Gemfiles
|
10
|
+
require 'net/ssh'
|
11
|
+
require 'rspec/http'
|
12
|
+
|
13
|
+
# Our precious self
|
14
|
+
require 'vagrant-test-subject/monkey-patches/rspec-http'
|
15
|
+
require 'vagrant-test-subject/ssh'
|
16
|
+
require 'vagrant-test-subject/os/redhat'
|
17
|
+
require 'vagrant-test-subject/os/omnios'
|
18
|
+
|
19
|
+
module VagrantTestSubject
|
20
|
+
|
21
|
+
class VM
|
22
|
+
attr_reader :ssh, :vbox_guid, :external_ip
|
23
|
+
|
24
|
+
# Factory method, reads VM type and then instantiates
|
25
|
+
def self.attach(vm_name = 'default')
|
26
|
+
os_type = VM.read_vbox_os_type(vm_name)
|
27
|
+
|
28
|
+
# Yecch
|
29
|
+
vbox_os_to_class = {
|
30
|
+
'RedHat_64' => VagrantTestSubject::VM::RedHat,
|
31
|
+
'Red Hat (64 bit)' => VagrantTestSubject::VM::RedHat,
|
32
|
+
'OpenSolaris_64' => VagrantTestSubject::VM::OmniOS,
|
33
|
+
}
|
34
|
+
|
35
|
+
klass = vbox_os_to_class[os_type]
|
36
|
+
klass.new(vm_name)
|
37
|
+
end
|
38
|
+
|
39
|
+
def initialize(vm_name = 'default')
|
40
|
+
@vm_name = vm_name
|
41
|
+
@vbox_guid = VM.read_vbox_guid(vm_name)
|
42
|
+
@vm_info = `VBoxManage showvminfo #{@vbox_guid} --machinereadable`.split("\n")
|
43
|
+
init_port_map()
|
44
|
+
@ssh = VagrantTestSubject::SSH.new(vm_name)
|
45
|
+
end
|
46
|
+
|
47
|
+
|
48
|
+
#================================================#
|
49
|
+
# VM Info Methods
|
50
|
+
#================================================#
|
51
|
+
def list_internal_ports
|
52
|
+
return @port_map.keys.sort
|
53
|
+
end
|
54
|
+
|
55
|
+
def map_port(internal)
|
56
|
+
return @port_map[internal.to_i]
|
57
|
+
end
|
58
|
+
|
59
|
+
#================================================#
|
60
|
+
# Testing Helpers
|
61
|
+
#================================================#
|
62
|
+
|
63
|
+
# Returns a Net::HTTP::Response
|
64
|
+
def http_get (local_absolute_url)
|
65
|
+
uri = URI('http://localhost:' + self.map_port(80).to_s + local_absolute_url)
|
66
|
+
response = Net::HTTP.get_response(uri)
|
67
|
+
end
|
68
|
+
def https_get (local_absolute_url, opts={})
|
69
|
+
# TODO - set OpenSSL::SSL::VERIFY_PEER based on opts ?
|
70
|
+
uri = URI('http://localhost:' + self.map_port(443).to_s + local_absolute_url)
|
71
|
+
response = Net::HTTP.get_response(uri)
|
72
|
+
end
|
73
|
+
|
74
|
+
#================================================#
|
75
|
+
# Testing Predicates
|
76
|
+
#================================================#
|
77
|
+
def has_running_service? (service_name)
|
78
|
+
raise "pure virtual"
|
79
|
+
end
|
80
|
+
|
81
|
+
def listening_on_localhost? (vm_port)
|
82
|
+
vm_port = vm_port.to_i
|
83
|
+
self.listening_ports.find{|lp| ['127.0.0.1', '0.0.0.0', '*'].include?(lp[:ip]) && lp[:port] == vm_port }
|
84
|
+
end
|
85
|
+
|
86
|
+
def listening_on_external_ip? (vm_port)
|
87
|
+
vm_port = vm_port.to_i
|
88
|
+
self.listening_ports.find{|lp| [@external_ip, '0.0.0.0', '*'].include?(lp[:ip]) && lp[:port] == vm_port }
|
89
|
+
end
|
90
|
+
|
91
|
+
def has_matching_process_listening? (ip, vm_port, process_regex)
|
92
|
+
raise "pure virtual"
|
93
|
+
end
|
94
|
+
|
95
|
+
def listening_on_portmap?(vm_port)
|
96
|
+
host_port = map_port(vm_port)
|
97
|
+
|
98
|
+
# This doesn't work - always connects, even to a port that isn't listening.
|
99
|
+
return false
|
100
|
+
|
101
|
+
begin
|
102
|
+
#Timeout::timeout(1) do
|
103
|
+
begin
|
104
|
+
puts "Trying VM host port " + host_port.to_s
|
105
|
+
binding.pry
|
106
|
+
s = TCPSocket.new('127.0.0.1', host_port )
|
107
|
+
s.close
|
108
|
+
return true
|
109
|
+
rescue Errno::ECONNREFUSED, Errno::EHOSTUNREACH
|
110
|
+
return false
|
111
|
+
end
|
112
|
+
#end
|
113
|
+
#rescue Timeout::Error
|
114
|
+
end
|
115
|
+
return false
|
116
|
+
end
|
117
|
+
|
118
|
+
#================================================#
|
119
|
+
# Guts
|
120
|
+
#================================================#
|
121
|
+
protected
|
122
|
+
def self.read_vbox_guid(vm_name)
|
123
|
+
(JSON.parse(IO.read(".vagrant")))['active'][vm_name]
|
124
|
+
end
|
125
|
+
|
126
|
+
def self.read_vbox_os_type(vm_name)
|
127
|
+
vbox_guid = read_vbox_guid(vm_name)
|
128
|
+
output = `VBoxManage showvminfo #{vbox_guid} --machinereadable | grep ostype`
|
129
|
+
output[/".+"/].gsub('"','')
|
130
|
+
end
|
131
|
+
|
132
|
+
def init_port_map
|
133
|
+
@port_map = {}
|
134
|
+
@vm_info.grep(/Forwarding/) do |rule|
|
135
|
+
# Forwarding(0)="2g-183o,tcp,,41080,,80"
|
136
|
+
if /Forwarding\(\d+\)="[\w\-]+,(?<proto>\w+),,(?<ext>\d+),,(?<int>\d+)"/ =~ rule then
|
137
|
+
@port_map[int.to_i()] = ext.to_i()
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
def init_iface_list
|
143
|
+
# TODO - populate @external_ip
|
144
|
+
@external_ip = '10.0.2.15'
|
145
|
+
end
|
146
|
+
|
147
|
+
end
|
148
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# What is wrong with you people
|
2
|
+
module RSpec
|
3
|
+
module Http
|
4
|
+
class ResponseCodeMatcher
|
5
|
+
# Override, this stupidly doesn't work with the stdlib net/http
|
6
|
+
def matches?(target)
|
7
|
+
@target = target
|
8
|
+
if @target.respond_to? :status then
|
9
|
+
return @target.status.to_s == @expected_code.to_s
|
10
|
+
elsif @target.respond_to? :code then
|
11
|
+
# Net::HTTPResponse
|
12
|
+
return @target.code.to_s == @expected_code.to_s
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def common_message
|
17
|
+
status_code = (@target.respond_to? :status) ? @target.status : @target.code
|
18
|
+
message = "have a response code of #{@expected_code}, but got #{status_code}"
|
19
|
+
if status_code == 302 || status_code == 201
|
20
|
+
message += " with a location of #{@target['Location'] || @target['location']}"
|
21
|
+
end
|
22
|
+
message
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
module VagrantTestSubject
|
2
|
+
class VM
|
3
|
+
class RedHat < VM
|
4
|
+
@@SERVICE_NAMES = {
|
5
|
+
'trafficserver' => 'trafficserver'
|
6
|
+
}
|
7
|
+
|
8
|
+
def initialize(*args)
|
9
|
+
super
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.register_service_alias(human_name, os_specific_name)
|
13
|
+
@@SERVICE_NAMES[human_name] = os_specific_name
|
14
|
+
end
|
15
|
+
|
16
|
+
def has_running_service? (human_service_name)
|
17
|
+
redhat_service_name = @@SERVICE_NAMES[human_service_name] || human_service_name
|
18
|
+
output = self.ssh.exec!("/sbin/service #{redhat_service_name} status; echo exit_code:$?")
|
19
|
+
/exit_code:(?<exit_code>\d+)/ =~ output
|
20
|
+
return exit_code.to_i == 0
|
21
|
+
end
|
22
|
+
|
23
|
+
def listening_ports
|
24
|
+
cmd = "netstat -ln --inet | tail -n +3 | awk '{print $4}'"
|
25
|
+
open_ports = []
|
26
|
+
self.ssh.exec!(cmd).split("\n").each do |line|
|
27
|
+
ip, port = line.split(':')
|
28
|
+
next unless ip
|
29
|
+
open_ports << { :ip => ip, :port => port.to_i }
|
30
|
+
end
|
31
|
+
open_ports
|
32
|
+
end
|
33
|
+
|
34
|
+
def process_name_listening (ip, vm_port)
|
35
|
+
# Note the trailing space after port - prevents 127.0.0.1:80 from matching 127.0.0.1:8084
|
36
|
+
cmd = "sudo netstat -lnp --inet | grep '#{ip}:#{vm_port} ' | awk '{print $7}'"
|
37
|
+
output = self.ssh.exec!(cmd)
|
38
|
+
if output.nil? then
|
39
|
+
cmd = "sudo netstat -lnp --inet | grep '0.0.0.0:#{vm_port} ' | awk '{print $7}'"
|
40
|
+
output = self.ssh.exec!(cmd)
|
41
|
+
end
|
42
|
+
return nil if output.nil?
|
43
|
+
output.chomp!
|
44
|
+
|
45
|
+
pid, truncated_process = output.split('/');
|
46
|
+
unless pid =~ /^\d+$/ then
|
47
|
+
raise "Not sure what this means, when running netstat -nlp:\n#{output}"
|
48
|
+
end
|
49
|
+
|
50
|
+
cmd = "ps -fp #{pid} | tail -n +2 | awk '{print $8}'"
|
51
|
+
process_string = self.ssh.exec!(cmd).chomp()
|
52
|
+
return process_string
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module VagrantTestSubject
|
2
|
+
|
3
|
+
# Encapsulates an SSH channel into a VM
|
4
|
+
class SSH
|
5
|
+
def initialize(vm_name = 'default')
|
6
|
+
# Grab ssh config
|
7
|
+
config = `vagrant ssh-config`
|
8
|
+
|
9
|
+
# Write most of it to a file, grabbing the username and host
|
10
|
+
config_file = Tempfile.new("vagrant-ssh-conf")
|
11
|
+
|
12
|
+
host, username = nil, nil
|
13
|
+
config.split(/\n/).each do |line|
|
14
|
+
case line
|
15
|
+
when /^\s+User\s+(\w+)/
|
16
|
+
username = $1
|
17
|
+
when /^\s+HostName\s+(\S+)/
|
18
|
+
host = $1
|
19
|
+
when /^\s*Host\s+#{vm_name}/
|
20
|
+
# Ignore
|
21
|
+
else
|
22
|
+
config_file << line + "\n"
|
23
|
+
end
|
24
|
+
end
|
25
|
+
config_file.flush
|
26
|
+
|
27
|
+
# make a Net::SSH Session
|
28
|
+
# and delegate everything to it
|
29
|
+
@session = Net::SSH.start(host, username, :config => config_file.path)
|
30
|
+
|
31
|
+
config_file.close
|
32
|
+
|
33
|
+
end
|
34
|
+
|
35
|
+
def method_missing(*args, &block)
|
36
|
+
method_name = args.shift
|
37
|
+
@session.send method_name, *args, &block
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
end
|
metadata
ADDED
@@ -0,0 +1,85 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: vagrant-test-subject
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Clinton Wolfe
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2013-04-17 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: net-ssh
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '0'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: rspec-http
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ! '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '0'
|
38
|
+
type: :runtime
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
46
|
+
description: Wrapper class for a Vagrant VM, providing access to testing predicates,
|
47
|
+
such as port map information, process data, ssh connections, and more.
|
48
|
+
email: clinton@NOSPAM.omniti.com
|
49
|
+
executables: []
|
50
|
+
extensions: []
|
51
|
+
extra_rdoc_files: []
|
52
|
+
files:
|
53
|
+
- Changelog
|
54
|
+
- LICENSE
|
55
|
+
- README.md
|
56
|
+
- lib/vagrant-test-subject.rb
|
57
|
+
- lib/vagrant-test-subject/ssh.rb
|
58
|
+
- lib/vagrant-test-subject/monkey-patches/rspec-http.rb
|
59
|
+
- lib/vagrant-test-subject/os/redhat.rb
|
60
|
+
- lib/vagrant-test-subject/os/omnios.rb
|
61
|
+
homepage: https://github.com/clintoncwolfe/vagrant-test-subject
|
62
|
+
licenses: []
|
63
|
+
post_install_message:
|
64
|
+
rdoc_options: []
|
65
|
+
require_paths:
|
66
|
+
- lib
|
67
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
68
|
+
none: false
|
69
|
+
requirements:
|
70
|
+
- - ! '>='
|
71
|
+
- !ruby/object:Gem::Version
|
72
|
+
version: '0'
|
73
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
74
|
+
none: false
|
75
|
+
requirements:
|
76
|
+
- - ! '>='
|
77
|
+
- !ruby/object:Gem::Version
|
78
|
+
version: '0'
|
79
|
+
requirements: []
|
80
|
+
rubyforge_project:
|
81
|
+
rubygems_version: 1.8.24
|
82
|
+
signing_key:
|
83
|
+
specification_version: 3
|
84
|
+
summary: Wrapper class for a Vagrant VM, providing access to testing predicates
|
85
|
+
test_files: []
|