configspec 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 +17 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +29 -0
- data/Rakefile +14 -0
- data/bin/configspec-init +7 -0
- data/configspec.gemspec +25 -0
- data/lib/configspec/backend/base.rb +31 -0
- data/lib/configspec/backend/exec.rb +206 -0
- data/lib/configspec/backend/ssh.rb +93 -0
- data/lib/configspec/backend.rb +3 -0
- data/lib/configspec/commands/base.rb +24 -0
- data/lib/configspec/commands/linux.rb +8 -0
- data/lib/configspec/commands/redhat.rb +9 -0
- data/lib/configspec/configuration.rb +12 -0
- data/lib/configspec/helper/configuration.rb +37 -0
- data/lib/configspec/helper/detect_os.rb +19 -0
- data/lib/configspec/helper/exec.rb +14 -0
- data/lib/configspec/helper/properties.rb +14 -0
- data/lib/configspec/helper/redhat.rb +9 -0
- data/lib/configspec/helper/ssh.rb +15 -0
- data/lib/configspec/helper/type.rb +20 -0
- data/lib/configspec/helper.rb +13 -0
- data/lib/configspec/properties.rb +17 -0
- data/lib/configspec/setup.rb +294 -0
- data/lib/configspec/type/base.rb +18 -0
- data/lib/configspec/type/package.rb +9 -0
- data/lib/configspec/version.rb +3 -0
- data/lib/configspec.rb +35 -0
- data/spec/redhat/package_spec.rb +8 -0
- data/spec/spec_helper.rb +59 -0
- metadata +134 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA1:
|
|
3
|
+
metadata.gz: 44c4f6e749e74381cd17819c7f6f058794c40ee5
|
|
4
|
+
data.tar.gz: bddf65aa2cb75473a800d71e814b4d315db62ac9
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 5968fa460ac868ff9a13bde3fa4c70bb3bb1df4167006534e8be1fe904ea1bb3d393a6c3b719785327a593b0cac6acebaddfbc7f98f6b05ad485c1c66d51a137
|
|
7
|
+
data.tar.gz: c211b27045aa4a1070dbc8edd9fa12dc1f4670573ab0ecb1772010fefaf4b45c836aa25f9908382842683c701c016cb6c64b4cb0a8b52f7d2fdb9a4e2703cffb
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
Copyright (c) 2013 Gosuke Miyashita
|
|
2
|
+
|
|
3
|
+
MIT License
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
|
6
|
+
a copy of this software and associated documentation files (the
|
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
|
11
|
+
the following conditions:
|
|
12
|
+
|
|
13
|
+
The above copyright notice and this permission notice shall be
|
|
14
|
+
included in all copies or substantial portions of the Software.
|
|
15
|
+
|
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# Configspec
|
|
2
|
+
|
|
3
|
+
TODO: Write a gem description
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
Add this line to your application's Gemfile:
|
|
8
|
+
|
|
9
|
+
gem 'configspec'
|
|
10
|
+
|
|
11
|
+
And then execute:
|
|
12
|
+
|
|
13
|
+
$ bundle
|
|
14
|
+
|
|
15
|
+
Or install it yourself as:
|
|
16
|
+
|
|
17
|
+
$ gem install configspec
|
|
18
|
+
|
|
19
|
+
## Usage
|
|
20
|
+
|
|
21
|
+
TODO: Write usage instructions here
|
|
22
|
+
|
|
23
|
+
## Contributing
|
|
24
|
+
|
|
25
|
+
1. Fork it
|
|
26
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
|
27
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
|
28
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
|
29
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
require "bundler/gem_tasks"
|
|
2
|
+
require 'rspec/core/rake_task'
|
|
3
|
+
|
|
4
|
+
task :spec => 'spec:redhat'
|
|
5
|
+
|
|
6
|
+
namespace :spec do
|
|
7
|
+
oses = %w( redhat )
|
|
8
|
+
|
|
9
|
+
oses.each do |os|
|
|
10
|
+
RSpec::Core::RakeTask.new(os.to_sym) do |t|
|
|
11
|
+
t.pattern = "spec/#{os}/*_spec.rb"
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
data/bin/configspec-init
ADDED
data/configspec.gemspec
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# coding: utf-8
|
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
4
|
+
require 'configspec/version'
|
|
5
|
+
|
|
6
|
+
Gem::Specification.new do |spec|
|
|
7
|
+
spec.name = "configspec"
|
|
8
|
+
spec.version = Configspec::VERSION
|
|
9
|
+
spec.authors = ["Gosuke Miyashita"]
|
|
10
|
+
spec.email = ["gosukenator@gmail.com"]
|
|
11
|
+
spec.description = %q{A simple configuration management tool powered by RSpec}
|
|
12
|
+
spec.summary = %q{A simple configuration management tool powered by RSpec}
|
|
13
|
+
spec.homepage = "https://github.com/mizzy/configspec"
|
|
14
|
+
spec.license = "MIT"
|
|
15
|
+
|
|
16
|
+
spec.files = `git ls-files`.split($/)
|
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
|
19
|
+
spec.require_paths = ["lib"]
|
|
20
|
+
|
|
21
|
+
spec.add_runtime_dependency "net-ssh"
|
|
22
|
+
spec.add_runtime_dependency "rspec", ">= 2.13.0"
|
|
23
|
+
spec.add_development_dependency "bundler", "~> 1.3"
|
|
24
|
+
spec.add_development_dependency "rake"
|
|
25
|
+
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
require 'singleton'
|
|
2
|
+
|
|
3
|
+
module Configspec
|
|
4
|
+
module Backend
|
|
5
|
+
class Base
|
|
6
|
+
include Singleton
|
|
7
|
+
|
|
8
|
+
def set_commands(c)
|
|
9
|
+
@commands = c
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def set_example(e)
|
|
13
|
+
@example = e
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def commands
|
|
17
|
+
@commands
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def check_zero(cmd, *args)
|
|
21
|
+
ret = run_command(commands.send(cmd, *args))
|
|
22
|
+
ret[:exit_status] == 0
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# Default action is to call check_zero with args
|
|
26
|
+
def method_missing(meth, *args, &block)
|
|
27
|
+
check_zero(meth, *args)
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
require 'singleton'
|
|
2
|
+
|
|
3
|
+
module Configspec
|
|
4
|
+
module Backend
|
|
5
|
+
class Exec < Base
|
|
6
|
+
|
|
7
|
+
def run_command(cmd, opts={})
|
|
8
|
+
cmd = build_command(cmd)
|
|
9
|
+
cmd = add_pre_command(cmd)
|
|
10
|
+
stdout = `#{build_command(cmd)} 2>&1`
|
|
11
|
+
# In ruby 1.9, it is possible to use Open3.capture3, but not in 1.8
|
|
12
|
+
#stdout, stderr, status = Open3.capture3(cmd)
|
|
13
|
+
|
|
14
|
+
if @example
|
|
15
|
+
@example.metadata[:command] = cmd
|
|
16
|
+
@example.metadata[:stdout] = stdout
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
{ :stdout => stdout, :stderr => nil,
|
|
20
|
+
:exit_status => $?.exitstatus, :exit_signal => nil }
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def build_command(cmd)
|
|
24
|
+
path = Configspec.configuration.path || RSpec.configuration.path
|
|
25
|
+
if path
|
|
26
|
+
cmd = "env PATH=#{path}:$PATH #{cmd}"
|
|
27
|
+
cmd.gsub!(/(\&\&\s*!?\(?\s*)/, "\\1env PATH=#{path}:$PATH ")
|
|
28
|
+
cmd.gsub!(/(\|\|\s*!?\(?\s*)/, "\\1env PATH=#{path}:$PATH ")
|
|
29
|
+
end
|
|
30
|
+
cmd
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def add_pre_command(cmd)
|
|
34
|
+
path = Configspec.configuration.path || RSpec.configuration.path
|
|
35
|
+
if Configspec.configuration.pre_command
|
|
36
|
+
cmd = "#{Configspec.configuration.pre_command} && #{cmd}"
|
|
37
|
+
cmd = "env PATH=#{path}:$PATH #{cmd}" if path
|
|
38
|
+
end
|
|
39
|
+
cmd
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def check_running(process)
|
|
43
|
+
ret = run_command(commands.check_running(process))
|
|
44
|
+
|
|
45
|
+
# In Ubuntu, some services are under upstart and "service foo status" returns
|
|
46
|
+
# exit status 0 even though they are stopped.
|
|
47
|
+
# So return false if stdout contains "stopped/waiting".
|
|
48
|
+
return false if ret[:stdout] =~ /stopped\/waiting/
|
|
49
|
+
|
|
50
|
+
# If the service is not registered, check by ps command
|
|
51
|
+
if ret[:exit_status] == 1
|
|
52
|
+
ret = run_command(commands.check_process(process))
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
ret[:exit_status] == 0
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def check_monitored_by_monit(process)
|
|
59
|
+
ret = run_command(commands.check_monitored_by_monit(process))
|
|
60
|
+
return false unless ret[:stdout] != nil && ret[:exit_status] == 0
|
|
61
|
+
|
|
62
|
+
retlines = ret[:stdout].split(/[\r\n]+/).map(&:strip)
|
|
63
|
+
proc_index = retlines.index("Process '#{process}'")
|
|
64
|
+
return false unless proc_index
|
|
65
|
+
|
|
66
|
+
retlines[proc_index+2].match(/\Amonitoring status\s+monitored\Z/i) != nil
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def check_readable(file, by_whom)
|
|
70
|
+
mode = sprintf('%04s',run_command(commands.get_mode(file))[:stdout].strip)
|
|
71
|
+
mode = mode.split('')
|
|
72
|
+
mode_octal = mode[0].to_i * 512 + mode[1].to_i * 64 + mode[2].to_i * 8 + mode[3].to_i * 1
|
|
73
|
+
case by_whom
|
|
74
|
+
when nil
|
|
75
|
+
mode_octal & 0444 != 0
|
|
76
|
+
when 'owner'
|
|
77
|
+
mode_octal & 0400 != 0
|
|
78
|
+
when 'group'
|
|
79
|
+
mode_octal & 0040 != 0
|
|
80
|
+
when 'others'
|
|
81
|
+
mode_octal & 0004 != 0
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def check_writable(file, by_whom)
|
|
86
|
+
mode = sprintf('%04s',run_command(commands.get_mode(file))[:stdout].strip)
|
|
87
|
+
mode = mode.split('')
|
|
88
|
+
mode_octal = mode[0].to_i * 512 + mode[1].to_i * 64 + mode[2].to_i * 8 + mode[3].to_i * 1
|
|
89
|
+
case by_whom
|
|
90
|
+
when nil
|
|
91
|
+
mode_octal & 0222 != 0
|
|
92
|
+
when 'owner'
|
|
93
|
+
mode_octal & 0200 != 0
|
|
94
|
+
when 'group'
|
|
95
|
+
mode_octal & 0020 != 0
|
|
96
|
+
when 'others'
|
|
97
|
+
mode_octal & 0002 != 0
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def check_executable(file, by_whom)
|
|
102
|
+
mode = sprintf('%04s',run_command(commands.get_mode(file))[:stdout].strip)
|
|
103
|
+
mode = mode.split('')
|
|
104
|
+
mode_octal = mode[0].to_i * 512 + mode[1].to_i * 64 + mode[2].to_i * 8 + mode[3].to_i * 1
|
|
105
|
+
case by_whom
|
|
106
|
+
when nil
|
|
107
|
+
mode_octal & 0111 != 0
|
|
108
|
+
when 'owner'
|
|
109
|
+
mode_octal & 0100 != 0
|
|
110
|
+
when 'group'
|
|
111
|
+
mode_octal & 0010 != 0
|
|
112
|
+
when 'others'
|
|
113
|
+
mode_octal & 0001 != 0
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def check_mounted(path, expected_attr, only_with)
|
|
118
|
+
ret = run_command(commands.check_mounted(path))
|
|
119
|
+
if expected_attr.nil? || ret[:exit_status] != 0
|
|
120
|
+
return ret[:exit_status] == 0
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
mount = ret[:stdout].scan(/\S+/)
|
|
124
|
+
actual_attr = { :device => mount[0], :type => mount[4] }
|
|
125
|
+
mount[5].gsub(/\(|\)/, '').split(',').each do |option|
|
|
126
|
+
name, val = option.split('=')
|
|
127
|
+
if val.nil?
|
|
128
|
+
actual_attr[name.to_sym] = true
|
|
129
|
+
else
|
|
130
|
+
val = val.to_i if val.match(/^\d+$/)
|
|
131
|
+
actual_attr[name.to_sym] = val
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
if ! expected_attr[:options].nil?
|
|
136
|
+
expected_attr.merge!(expected_attr[:options])
|
|
137
|
+
expected_attr.delete(:options)
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
if only_with
|
|
141
|
+
actual_attr == expected_attr
|
|
142
|
+
else
|
|
143
|
+
expected_attr.each do |key, val|
|
|
144
|
+
return false if actual_attr[key] != val
|
|
145
|
+
end
|
|
146
|
+
true
|
|
147
|
+
end
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
def check_routing_table(expected_attr)
|
|
151
|
+
return false if ! expected_attr[:destination]
|
|
152
|
+
ret = run_command(commands.check_routing_table(expected_attr[:destination]))
|
|
153
|
+
return false if ret[:exit_status] != 0
|
|
154
|
+
|
|
155
|
+
ret[:stdout] =~ /^(\S+)(?: via (\S+))? dev (\S+).+\r\n(?:default via (\S+))?/
|
|
156
|
+
actual_attr = {
|
|
157
|
+
:destination => $1,
|
|
158
|
+
:gateway => $2 ? $2 : $4,
|
|
159
|
+
:interface => expected_attr[:interface] ? $3 : nil
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
expected_attr.each do |key, val|
|
|
163
|
+
return false if actual_attr[key] != val
|
|
164
|
+
end
|
|
165
|
+
true
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
def check_os
|
|
169
|
+
return RSpec.configuration.os if RSpec.configuration.os
|
|
170
|
+
if run_command('ls /etc/redhat-release')[:exit_status] == 0
|
|
171
|
+
line = run_command('cat /etc/redhat-release')[:stdout]
|
|
172
|
+
if line =~ /release (\d[\d.]*)/
|
|
173
|
+
release = $1
|
|
174
|
+
end
|
|
175
|
+
{ :family => 'RedHat', :release => release }
|
|
176
|
+
elsif run_command('ls /etc/system-release')[:exit_status] == 0
|
|
177
|
+
{ :family => 'RedHat', :release => nil } # Amazon Linux
|
|
178
|
+
elsif run_command('ls /etc/debian_version')[:exit_status] == 0
|
|
179
|
+
{ :family => 'Debian', :release => nil }
|
|
180
|
+
elsif run_command('ls /etc/gentoo-release')[:exit_status] == 0
|
|
181
|
+
{ :family => 'Gentoo', :release => nil }
|
|
182
|
+
elsif run_command('ls /usr/lib/setup/Plamo-*')[:exit_status] == 0
|
|
183
|
+
{ :family => 'Plamo', :release => nil }
|
|
184
|
+
elsif run_command('uname -s')[:stdout] =~ /AIX/i
|
|
185
|
+
{ :family => 'AIX', :release => nil }
|
|
186
|
+
elsif (os = run_command('uname -sr')[:stdout]) && os =~ /SunOS/i
|
|
187
|
+
if os =~ /5.10/
|
|
188
|
+
{ :family => 'Solaris10', :release => nil }
|
|
189
|
+
elsif run_command('grep -q "Oracle Solaris 11" /etc/release')[:exit_status] == 0
|
|
190
|
+
{ :family => 'Solaris11', :release => nil }
|
|
191
|
+
elsif run_command('grep -q SmartOS /etc/release')[:exit_status] == 0
|
|
192
|
+
{ :family => 'SmartOS', :release => nil }
|
|
193
|
+
else
|
|
194
|
+
{ :family => 'Solaris', :release => nil }
|
|
195
|
+
end
|
|
196
|
+
elsif run_command('uname -s')[:stdout] =~ /Darwin/i
|
|
197
|
+
{ :family => 'Darwin', :release => nil }
|
|
198
|
+
elsif run_command('uname -s')[:stdout] =~ /FreeBSD/i
|
|
199
|
+
{ :family => 'FreeBSD', :release => nil }
|
|
200
|
+
else
|
|
201
|
+
{ :family => 'Base', :release => nil }
|
|
202
|
+
end
|
|
203
|
+
end
|
|
204
|
+
end
|
|
205
|
+
end
|
|
206
|
+
end
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
require 'configspec/backend/exec'
|
|
2
|
+
|
|
3
|
+
module Configspec
|
|
4
|
+
module Backend
|
|
5
|
+
class Ssh < Exec
|
|
6
|
+
def run_command(cmd, opt={})
|
|
7
|
+
cmd = build_command(cmd)
|
|
8
|
+
cmd = add_pre_command(cmd)
|
|
9
|
+
ret = ssh_exec!(cmd)
|
|
10
|
+
|
|
11
|
+
ret[:stdout].gsub!(/\r\n/, "\n")
|
|
12
|
+
|
|
13
|
+
if @example
|
|
14
|
+
@example.metadata[:command] = cmd
|
|
15
|
+
@example.metadata[:stdout] = ret[:stdout]
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
ret
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def build_command(cmd)
|
|
22
|
+
cmd = super(cmd)
|
|
23
|
+
if RSpec.configuration.ssh.options[:user] != 'root'
|
|
24
|
+
cmd = "#{sudo} #{cmd}"
|
|
25
|
+
cmd.gsub!(/(\&\&\s*!?\(?\s*)/, "\\1#{sudo} ")
|
|
26
|
+
cmd.gsub!(/(\|\|\s*!?\(?\s*)/, "\\1#{sudo} ")
|
|
27
|
+
end
|
|
28
|
+
cmd
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def add_pre_command(cmd)
|
|
32
|
+
cmd = super(cmd)
|
|
33
|
+
user = RSpec.configuration.ssh.options[:user]
|
|
34
|
+
pre_command = Configspec.configuration.pre_command
|
|
35
|
+
if pre_command && user != 'root'
|
|
36
|
+
cmd = "#{sudo} #{cmd}"
|
|
37
|
+
end
|
|
38
|
+
cmd
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
private
|
|
42
|
+
def ssh_exec!(command)
|
|
43
|
+
stdout_data = ''
|
|
44
|
+
stderr_data = ''
|
|
45
|
+
exit_status = nil
|
|
46
|
+
exit_signal = nil
|
|
47
|
+
pass_prompt = RSpec.configuration.pass_prompt || /^\[sudo\] password for/
|
|
48
|
+
|
|
49
|
+
ssh = RSpec.configuration.ssh
|
|
50
|
+
ssh.open_channel do |channel|
|
|
51
|
+
channel.request_pty do |ch, success|
|
|
52
|
+
abort "Could not obtain pty " if !success
|
|
53
|
+
end
|
|
54
|
+
channel.exec("#{command}") do |ch, success|
|
|
55
|
+
abort "FAILED: couldn't execute command (ssh.channel.exec)" if !success
|
|
56
|
+
channel.on_data do |ch, data|
|
|
57
|
+
if data.match pass_prompt
|
|
58
|
+
abort "Please set sudo password by using SUDO_PASSWORD or ASK_SUDO_PASSWORD environment variable" if RSpec.configuration.sudo_password.nil?
|
|
59
|
+
channel.send_data "#{RSpec.configuration.sudo_password}\n"
|
|
60
|
+
else
|
|
61
|
+
stdout_data += data
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
channel.on_extended_data do |ch, type, data|
|
|
66
|
+
stderr_data += data
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
channel.on_request("exit-status") do |ch, data|
|
|
70
|
+
exit_status = data.read_long
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
channel.on_request("exit-signal") do |ch, data|
|
|
74
|
+
exit_signal = data.read_long
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
ssh.loop
|
|
79
|
+
{ :stdout => stdout_data, :stderr => stderr_data, :exit_status => exit_status, :exit_signal => exit_signal }
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def sudo
|
|
83
|
+
sudo_path = Configspec.configuration.sudo_path || RSpec.configuration.sudo_path
|
|
84
|
+
if sudo_path
|
|
85
|
+
"#{sudo_path}/sudo"
|
|
86
|
+
else
|
|
87
|
+
'sudo'
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
require 'shellwords'
|
|
2
|
+
|
|
3
|
+
module Configspec
|
|
4
|
+
module Commands
|
|
5
|
+
class Base
|
|
6
|
+
class NotImplementedError < Exception; end
|
|
7
|
+
|
|
8
|
+
def escape(target)
|
|
9
|
+
str = case target
|
|
10
|
+
when Regexp
|
|
11
|
+
target.source
|
|
12
|
+
else
|
|
13
|
+
target.to_s
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
Shellwords.shellescape(str)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def check_installed(package, version=nil)
|
|
20
|
+
raise NotImplementedError.new
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
module Configspec
|
|
2
|
+
module Configuration
|
|
3
|
+
class << self
|
|
4
|
+
VALID_OPTIONS_KEYS = [:path, :pre_command, :stdout, :stderr, :sudo_path, :pass_prompt].freeze
|
|
5
|
+
attr_accessor(*VALID_OPTIONS_KEYS)
|
|
6
|
+
|
|
7
|
+
def defaults
|
|
8
|
+
VALID_OPTIONS_KEYS.inject({}) { |o, k| o.merge!(k => send(k)) }
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
end
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
module Configspec
|
|
2
|
+
module Helper
|
|
3
|
+
module Configuration
|
|
4
|
+
def subject
|
|
5
|
+
example.metadata[:subject] = described_class
|
|
6
|
+
build_configurations
|
|
7
|
+
super
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
# You can create a set of configurations provided to all specs in your spec_helper:
|
|
11
|
+
#
|
|
12
|
+
# RSpec.configure { |c| c.pre_command = "source ~/.zshrc" }
|
|
13
|
+
#
|
|
14
|
+
# Any configurations you provide with `let(:option_name)` in a spec will
|
|
15
|
+
# automatically be merged on top of the configurations.
|
|
16
|
+
#
|
|
17
|
+
# @example
|
|
18
|
+
#
|
|
19
|
+
# describe 'Gem' do
|
|
20
|
+
# let(:pre_command) { "source ~/.zshrc" }
|
|
21
|
+
#
|
|
22
|
+
# %w(pry awesome_print bundler).each do |p|
|
|
23
|
+
# describe package(p) do
|
|
24
|
+
# it { should be_installed.by('gem') }
|
|
25
|
+
# end
|
|
26
|
+
# end
|
|
27
|
+
# end
|
|
28
|
+
def build_configurations
|
|
29
|
+
Configspec::Configuration.defaults.keys.each do |c|
|
|
30
|
+
value = self.respond_to?(c.to_sym) ?
|
|
31
|
+
self.send(c) : RSpec.configuration.send(c)
|
|
32
|
+
Configspec::Configuration.send(:"#{c}=", value)
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
module Configspec
|
|
2
|
+
module Helper
|
|
3
|
+
module DetectOS
|
|
4
|
+
def commands
|
|
5
|
+
property[:os_by_host] = {} if ! property[:os_by_host]
|
|
6
|
+
host = RSpec.configuration.ssh ? RSpec.configuration.ssh.host : 'localhost'
|
|
7
|
+
|
|
8
|
+
if property[:os_by_host][host]
|
|
9
|
+
os = property[:os_by_host][host]
|
|
10
|
+
else
|
|
11
|
+
os = backend(Configspec::Commands::Base).check_os
|
|
12
|
+
property[:os_by_host][host] = os
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
self.class.const_get('Configspec').const_get('Commands').const_get(os[:family]).new
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
module Configspec
|
|
2
|
+
module Helper
|
|
3
|
+
module Exec
|
|
4
|
+
def backend(commands_object=nil)
|
|
5
|
+
if commands_object.nil? && ! respond_to?(:commands)
|
|
6
|
+
commands_object = Configspec::Commands::Base.new
|
|
7
|
+
end
|
|
8
|
+
instance = Configspec::Backend::Exec.instance
|
|
9
|
+
instance.set_commands(commands_object || commands)
|
|
10
|
+
instance
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
require 'configspec/properties'
|
|
2
|
+
|
|
3
|
+
module Configspec
|
|
4
|
+
module Helper
|
|
5
|
+
module Properties
|
|
6
|
+
def property
|
|
7
|
+
Configspec::Properties.instance.properties
|
|
8
|
+
end
|
|
9
|
+
def set_property(prop)
|
|
10
|
+
Configspec::Properties.instance.properties(prop)
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
module Configspec
|
|
2
|
+
module Helper
|
|
3
|
+
module Ssh
|
|
4
|
+
def backend(commands_object=nil)
|
|
5
|
+
if ! respond_to?(:commands)
|
|
6
|
+
commands_object = Configspec::Commands::Base.new
|
|
7
|
+
end
|
|
8
|
+
instance = Configspec::Backend::Ssh.instance
|
|
9
|
+
instance.set_commands(commands_object || commands)
|
|
10
|
+
instance
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
module Configspec
|
|
2
|
+
module Helper
|
|
3
|
+
module Type
|
|
4
|
+
types = %w( base package )
|
|
5
|
+
|
|
6
|
+
types.each {|type| require "configspec/type/#{type}" }
|
|
7
|
+
|
|
8
|
+
types.each do |type|
|
|
9
|
+
define_method type do |*args|
|
|
10
|
+
name = args.first
|
|
11
|
+
self.class.const_get('Configspec').const_get('Type').const_get(camelize(type)).new(name)
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def camelize(string)
|
|
16
|
+
string.split("_").each {|s| s.capitalize! }.join("")
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
require 'configspec/helper/exec'
|
|
2
|
+
require 'configspec/helper/ssh'
|
|
3
|
+
require 'configspec/helper/detect_os'
|
|
4
|
+
require 'configspec/helper/redhat'
|
|
5
|
+
|
|
6
|
+
require 'configspec/helper/type'
|
|
7
|
+
include Configspec::Helper::Type
|
|
8
|
+
|
|
9
|
+
require 'configspec/helper/properties'
|
|
10
|
+
include Configspec::Helper::Properties
|
|
11
|
+
|
|
12
|
+
require 'configspec/helper/configuration'
|
|
13
|
+
|
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
require 'fileutils'
|
|
2
|
+
require 'erb'
|
|
3
|
+
|
|
4
|
+
module Configspec
|
|
5
|
+
class Setup
|
|
6
|
+
def self.run
|
|
7
|
+
|
|
8
|
+
# ask_os_type
|
|
9
|
+
@os_type = 'UN*X'
|
|
10
|
+
|
|
11
|
+
if @os_type == 'UN*X'
|
|
12
|
+
ask_unix_backend
|
|
13
|
+
else
|
|
14
|
+
ask_windows_backend
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
if @backend_type == 'Ssh'
|
|
18
|
+
print "Vagrant instance y/n: "
|
|
19
|
+
@vagrant = $stdin.gets.chomp
|
|
20
|
+
if @vagrant =~ (/(true|t|yes|y|1)$/i)
|
|
21
|
+
@vagrant = true
|
|
22
|
+
print "Auto-configure Vagrant from Vagrantfile? y/n: "
|
|
23
|
+
auto_config = $stdin.gets.chomp
|
|
24
|
+
if auto_config =~ (/(true|t|yes|y|1)$/i)
|
|
25
|
+
auto_vagrant_configuration
|
|
26
|
+
else
|
|
27
|
+
print("Input vagrant instance name: ")
|
|
28
|
+
@hostname = $stdin.gets.chomp
|
|
29
|
+
end
|
|
30
|
+
else
|
|
31
|
+
@vagrant = false
|
|
32
|
+
print("Input target host name: ")
|
|
33
|
+
@hostname = $stdin.gets.chomp
|
|
34
|
+
end
|
|
35
|
+
else
|
|
36
|
+
@hostname = 'localhost'
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
[ 'spec', "spec/#{@hostname}" ].each { |dir| safe_mkdir(dir) }
|
|
40
|
+
safe_create_spec
|
|
41
|
+
safe_create_spec_helper
|
|
42
|
+
safe_create_rakefile
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def self.ask_os_type
|
|
46
|
+
prompt = <<-EOF
|
|
47
|
+
Select OS type:
|
|
48
|
+
|
|
49
|
+
1) UN*X
|
|
50
|
+
2) Windows
|
|
51
|
+
|
|
52
|
+
Select number:
|
|
53
|
+
EOF
|
|
54
|
+
|
|
55
|
+
print prompt.chop
|
|
56
|
+
num = $stdin.gets.to_i - 1
|
|
57
|
+
puts
|
|
58
|
+
|
|
59
|
+
@os_type = [ 'UN*X', 'Windows' ][num] || 'UN*X'
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def self.ask_unix_backend
|
|
63
|
+
prompt = <<-EOF
|
|
64
|
+
Select a backend type:
|
|
65
|
+
|
|
66
|
+
1) SSH
|
|
67
|
+
2) Exec (local)
|
|
68
|
+
|
|
69
|
+
Select number:
|
|
70
|
+
EOF
|
|
71
|
+
print prompt.chop
|
|
72
|
+
num = $stdin.gets.to_i - 1
|
|
73
|
+
puts
|
|
74
|
+
|
|
75
|
+
@backend_type = [ 'Ssh', 'Exec' ][num] || 'Exec'
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def self.ask_windows_backend
|
|
79
|
+
prompt = <<-EOF
|
|
80
|
+
Select a backend type:
|
|
81
|
+
|
|
82
|
+
1) WinRM
|
|
83
|
+
2) Cmd (local)
|
|
84
|
+
|
|
85
|
+
Select number:
|
|
86
|
+
EOF
|
|
87
|
+
print prompt.chop
|
|
88
|
+
num = $stdin.gets.to_i - 1
|
|
89
|
+
puts
|
|
90
|
+
|
|
91
|
+
@backend_type = [ 'WinRM', 'Cmd' ][num] || 'Exec'
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def self.safe_create_spec
|
|
95
|
+
content = <<-EOF
|
|
96
|
+
require 'spec_helper'
|
|
97
|
+
|
|
98
|
+
describe package('httpd') do
|
|
99
|
+
it { should be_installed }
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
describe service('httpd') do
|
|
103
|
+
it { should be_enabled }
|
|
104
|
+
it { should be_running }
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
describe port(80) do
|
|
108
|
+
it { should be_listening }
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
describe file('/etc/httpd/conf/httpd.conf') do
|
|
112
|
+
it { should be_file }
|
|
113
|
+
it { should contain "ServerName #{@hostname}" }
|
|
114
|
+
end
|
|
115
|
+
EOF
|
|
116
|
+
|
|
117
|
+
if File.exists? "spec/#{@hostname}/001_httpd_spec.rb"
|
|
118
|
+
old_content = File.read("spec/#{@hostname}/001_httpd_spec.rb")
|
|
119
|
+
if old_content != content
|
|
120
|
+
$stderr.puts "!! spec/#{@hostname}/httpd_spec.rb already exists and differs from template"
|
|
121
|
+
end
|
|
122
|
+
else
|
|
123
|
+
File.open("spec/#{@hostname}/001_httpd_spec.rb", 'w') do |f|
|
|
124
|
+
f.puts content
|
|
125
|
+
end
|
|
126
|
+
puts " + spec/#{@hostname}/001_httpd_spec.rb"
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
def self.safe_mkdir(dir)
|
|
131
|
+
if File.exists? dir
|
|
132
|
+
unless File.directory? dir
|
|
133
|
+
$stderr.puts "!! #{dir} already exists and is not a directory"
|
|
134
|
+
end
|
|
135
|
+
else
|
|
136
|
+
FileUtils.mkdir dir
|
|
137
|
+
puts " + #{dir}/"
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
def self.safe_create_spec_helper
|
|
142
|
+
requirements = []
|
|
143
|
+
content = ERB.new(spec_helper_template, nil, '-').result(binding)
|
|
144
|
+
if File.exists? 'spec/spec_helper.rb'
|
|
145
|
+
old_content = File.read('spec/spec_helper.rb')
|
|
146
|
+
if old_content != content
|
|
147
|
+
$stderr.puts "!! spec/spec_helper.rb already exists and differs from template"
|
|
148
|
+
end
|
|
149
|
+
else
|
|
150
|
+
File.open('spec/spec_helper.rb', 'w') do |f|
|
|
151
|
+
f.puts content
|
|
152
|
+
end
|
|
153
|
+
puts ' + spec/spec_helper.rb'
|
|
154
|
+
end
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
def self.safe_create_rakefile
|
|
158
|
+
content = <<-'EOF'
|
|
159
|
+
require 'rake'
|
|
160
|
+
require 'rspec/core/rake_task'
|
|
161
|
+
|
|
162
|
+
RSpec::Core::RakeTask.new(:spec) do |t|
|
|
163
|
+
t.pattern = 'spec/*/*_spec.rb'
|
|
164
|
+
end
|
|
165
|
+
EOF
|
|
166
|
+
if File.exists? 'Rakefile'
|
|
167
|
+
old_content = File.read('Rakefile')
|
|
168
|
+
if old_content != content
|
|
169
|
+
$stderr.puts "!! Rakefile already exists and differs from template"
|
|
170
|
+
end
|
|
171
|
+
else
|
|
172
|
+
File.open('Rakefile', 'w') do |f|
|
|
173
|
+
f.puts content
|
|
174
|
+
end
|
|
175
|
+
puts ' + Rakefile'
|
|
176
|
+
end
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
def self.find_vagrantfile
|
|
180
|
+
Pathname.new(Dir.pwd).ascend do |dir|
|
|
181
|
+
path = File.expand_path("Vagrantfile", dir)
|
|
182
|
+
return path if File.exists?(path)
|
|
183
|
+
end
|
|
184
|
+
nil
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
def self.auto_vagrant_configuration
|
|
188
|
+
if find_vagrantfile
|
|
189
|
+
vagrant_list = `vagrant status`
|
|
190
|
+
list_of_vms = []
|
|
191
|
+
if vagrant_list != ''
|
|
192
|
+
vagrant_list.each_line do |line|
|
|
193
|
+
if match = /([a-z_-]+[\s]+)(created|not created|poweroff|running|saved)[\s](\(virtualbox\)|\(vmware\))/.match(line)
|
|
194
|
+
list_of_vms << match[1].strip!
|
|
195
|
+
end
|
|
196
|
+
end
|
|
197
|
+
if list_of_vms.length == 1
|
|
198
|
+
@hostname = list_of_vms[0]
|
|
199
|
+
else
|
|
200
|
+
list_of_vms.each_with_index { |vm, index | puts "#{index}) #{vm}\n" }
|
|
201
|
+
print "Choose a VM from the Vagrantfile: "
|
|
202
|
+
chosen_vm = $stdin.gets.chomp
|
|
203
|
+
@hostname = list_of_vms[chosen_vm.to_i]
|
|
204
|
+
end
|
|
205
|
+
else
|
|
206
|
+
$stderr.puts "Vagrant status error - Check your Vagrantfile or .vagrant"
|
|
207
|
+
exit 1
|
|
208
|
+
end
|
|
209
|
+
else
|
|
210
|
+
$stderr.puts "Vagrantfile not found in directory!"
|
|
211
|
+
exit 1
|
|
212
|
+
end
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
def self.spec_helper_template
|
|
216
|
+
template = <<-EOF
|
|
217
|
+
require 'configspec'
|
|
218
|
+
<% if @os_type == 'UN*X' -%>
|
|
219
|
+
require 'pathname'
|
|
220
|
+
<% end -%>
|
|
221
|
+
<% if @backend_type == 'Ssh' -%>
|
|
222
|
+
require 'net/ssh'
|
|
223
|
+
<% end -%>
|
|
224
|
+
<% if @backend_type == 'WinRM' -%>
|
|
225
|
+
require 'winrm'
|
|
226
|
+
<% end -%>
|
|
227
|
+
|
|
228
|
+
include Configspec::Helper::<%= @backend_type %>
|
|
229
|
+
<% if @os_type == 'UN*X' -%>
|
|
230
|
+
include Configspec::Helper::DetectOS
|
|
231
|
+
<% else -%>
|
|
232
|
+
include Configspec::Helper::Windows
|
|
233
|
+
<% end -%>
|
|
234
|
+
|
|
235
|
+
<% if @os_type == 'UN*X' -%>
|
|
236
|
+
RSpec.configure do |c|
|
|
237
|
+
if ENV['ASK_SUDO_PASSWORD']
|
|
238
|
+
require 'highline/import'
|
|
239
|
+
c.sudo_password = ask("Enter sudo password: ") { |q| q.echo = false }
|
|
240
|
+
else
|
|
241
|
+
c.sudo_password = ENV['SUDO_PASSWORD']
|
|
242
|
+
end
|
|
243
|
+
<%- if @backend_type == 'Ssh' -%>
|
|
244
|
+
c.before :all do
|
|
245
|
+
block = self.class.metadata[:example_group_block]
|
|
246
|
+
if RUBY_VERSION.start_with?('1.8')
|
|
247
|
+
file = block.to_s.match(/.*@(.*):[0-9]+>/)[1]
|
|
248
|
+
else
|
|
249
|
+
file = block.source_location.first
|
|
250
|
+
end
|
|
251
|
+
host = File.basename(Pathname.new(file).dirname)
|
|
252
|
+
if c.host != host
|
|
253
|
+
c.ssh.close if c.ssh
|
|
254
|
+
c.host = host
|
|
255
|
+
options = Net::SSH::Config.for(c.host)
|
|
256
|
+
user = options[:user] || Etc.getlogin
|
|
257
|
+
<%- if @vagrant -%>
|
|
258
|
+
vagrant_up = `vagrant up #{@hostname}`
|
|
259
|
+
config = `vagrant ssh-config #{@hostname}`
|
|
260
|
+
if config != ''
|
|
261
|
+
config.each_line do |line|
|
|
262
|
+
if match = /HostName (.*)/.match(line)
|
|
263
|
+
host = match[1]
|
|
264
|
+
elsif match = /User (.*)/.match(line)
|
|
265
|
+
user = match[1]
|
|
266
|
+
elsif match = /IdentityFile (.*)/.match(line)
|
|
267
|
+
options[:keys] = [match[1].gsub(/\"/,'')]
|
|
268
|
+
elsif match = /Port (.*)/.match(line)
|
|
269
|
+
options[:port] = match[1]
|
|
270
|
+
end
|
|
271
|
+
end
|
|
272
|
+
end
|
|
273
|
+
<%- end -%>
|
|
274
|
+
c.ssh = Net::SSH.start(host, user, options)
|
|
275
|
+
end
|
|
276
|
+
end
|
|
277
|
+
<%- end -%>
|
|
278
|
+
end
|
|
279
|
+
<% end -%>
|
|
280
|
+
<% if @backend_type == 'WinRM'-%>
|
|
281
|
+
RSpec.configure do |c|
|
|
282
|
+
user = <username>
|
|
283
|
+
pass = <password>
|
|
284
|
+
endpoint = "http://<hostname>:5985/wsman"
|
|
285
|
+
|
|
286
|
+
c.winrm = ::WinRM::WinRMWebService.new(endpoint, :ssl, :user => user, :pass => pass, :basic_auth_only => true)
|
|
287
|
+
c.winrm.set_timeout 300 # 5 minutes max timeout for any operation
|
|
288
|
+
end
|
|
289
|
+
<% end -%>
|
|
290
|
+
EOF
|
|
291
|
+
template
|
|
292
|
+
end
|
|
293
|
+
end
|
|
294
|
+
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
module Configspec
|
|
2
|
+
module Type
|
|
3
|
+
class Base
|
|
4
|
+
def initialize(name=nil)
|
|
5
|
+
@name = name
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def to_s
|
|
9
|
+
type = self.class.name.split(':')[-1]
|
|
10
|
+
type.gsub!(/([a-z\d])([A-Z])/, '\1 \2')
|
|
11
|
+
type.capitalize!
|
|
12
|
+
%Q!#{type} "#{@name}"!
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
alias_method :inspect, :to_s
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
data/lib/configspec.rb
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
require "configspec/version"
|
|
2
|
+
require "configspec/setup"
|
|
3
|
+
require "configspec/helper"
|
|
4
|
+
require "configspec/backend"
|
|
5
|
+
|
|
6
|
+
require "configspec/commands/base"
|
|
7
|
+
require "configspec/commands/linux"
|
|
8
|
+
require "configspec/commands/redhat"
|
|
9
|
+
|
|
10
|
+
require "configspec/configuration"
|
|
11
|
+
|
|
12
|
+
include Configspec
|
|
13
|
+
|
|
14
|
+
module Configspec
|
|
15
|
+
class << self
|
|
16
|
+
def configuration
|
|
17
|
+
Configspec::Configuration
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
RSpec.configure do |c|
|
|
23
|
+
c.include(Configspec::Helper::Configuration)
|
|
24
|
+
|
|
25
|
+
c.add_setting :ssh, :default => nil
|
|
26
|
+
c.add_setting :host, :default => nil
|
|
27
|
+
c.add_setting :os, :default => nil
|
|
28
|
+
c.add_setting :sudo_password, :default => nil
|
|
29
|
+
|
|
30
|
+
Configspec.configuration.defaults.each { |k, v| c.add_setting k, :default => v }
|
|
31
|
+
|
|
32
|
+
c.before :each do
|
|
33
|
+
backend.set_example(example)
|
|
34
|
+
end
|
|
35
|
+
end
|
data/spec/spec_helper.rb
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
require 'configspec'
|
|
2
|
+
require 'pathname'
|
|
3
|
+
require 'rspec/mocks/standalone'
|
|
4
|
+
|
|
5
|
+
include Configspec::Helper::Exec
|
|
6
|
+
|
|
7
|
+
PROJECT_ROOT = (Pathname.new(File.dirname(__FILE__)) + '..').expand_path
|
|
8
|
+
|
|
9
|
+
Dir[PROJECT_ROOT.join("spec/support/**/*.rb")].each { |file| require(file) }
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
module Configspec
|
|
13
|
+
module Backend
|
|
14
|
+
module TestCommandRunner
|
|
15
|
+
def do_run cmd
|
|
16
|
+
if @example
|
|
17
|
+
@example.metadata[:subject].set_command(cmd)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
if cmd =~ /invalid/
|
|
21
|
+
{
|
|
22
|
+
:stdout => ::Configspec.configuration.stdout,
|
|
23
|
+
:stderr => ::Configspec.configuration.stderr,
|
|
24
|
+
:exit_status => 1,
|
|
25
|
+
:exit_signal => nil
|
|
26
|
+
}
|
|
27
|
+
else
|
|
28
|
+
{
|
|
29
|
+
:stdout => ::Configspec.configuration.stdout,
|
|
30
|
+
:stderr => ::Configspec.configuration.stderr,
|
|
31
|
+
:exit_status => 0,
|
|
32
|
+
:exit_signal => nil
|
|
33
|
+
}
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
[Exec, Ssh].each do |clz|
|
|
38
|
+
clz.class_eval do
|
|
39
|
+
include TestCommandRunner
|
|
40
|
+
def run_command(cmd)
|
|
41
|
+
cmd = build_command(cmd.to_s)
|
|
42
|
+
cmd = add_pre_command(cmd)
|
|
43
|
+
do_run cmd
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
module Type
|
|
50
|
+
class Base
|
|
51
|
+
def set_command(command)
|
|
52
|
+
@command = command
|
|
53
|
+
end
|
|
54
|
+
def command
|
|
55
|
+
@command
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: configspec
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.0.1
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Gosuke Miyashita
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2013-11-25 00:00:00.000000000 Z
|
|
12
|
+
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: net-ssh
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - '>='
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: '0'
|
|
20
|
+
type: :runtime
|
|
21
|
+
prerelease: false
|
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
+
requirements:
|
|
24
|
+
- - '>='
|
|
25
|
+
- !ruby/object:Gem::Version
|
|
26
|
+
version: '0'
|
|
27
|
+
- !ruby/object:Gem::Dependency
|
|
28
|
+
name: rspec
|
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
|
30
|
+
requirements:
|
|
31
|
+
- - '>='
|
|
32
|
+
- !ruby/object:Gem::Version
|
|
33
|
+
version: 2.13.0
|
|
34
|
+
type: :runtime
|
|
35
|
+
prerelease: false
|
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
37
|
+
requirements:
|
|
38
|
+
- - '>='
|
|
39
|
+
- !ruby/object:Gem::Version
|
|
40
|
+
version: 2.13.0
|
|
41
|
+
- !ruby/object:Gem::Dependency
|
|
42
|
+
name: bundler
|
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
|
44
|
+
requirements:
|
|
45
|
+
- - ~>
|
|
46
|
+
- !ruby/object:Gem::Version
|
|
47
|
+
version: '1.3'
|
|
48
|
+
type: :development
|
|
49
|
+
prerelease: false
|
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
51
|
+
requirements:
|
|
52
|
+
- - ~>
|
|
53
|
+
- !ruby/object:Gem::Version
|
|
54
|
+
version: '1.3'
|
|
55
|
+
- !ruby/object:Gem::Dependency
|
|
56
|
+
name: rake
|
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
|
58
|
+
requirements:
|
|
59
|
+
- - '>='
|
|
60
|
+
- !ruby/object:Gem::Version
|
|
61
|
+
version: '0'
|
|
62
|
+
type: :development
|
|
63
|
+
prerelease: false
|
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
65
|
+
requirements:
|
|
66
|
+
- - '>='
|
|
67
|
+
- !ruby/object:Gem::Version
|
|
68
|
+
version: '0'
|
|
69
|
+
description: A simple configuration management tool powered by RSpec
|
|
70
|
+
email:
|
|
71
|
+
- gosukenator@gmail.com
|
|
72
|
+
executables:
|
|
73
|
+
- configspec-init
|
|
74
|
+
extensions: []
|
|
75
|
+
extra_rdoc_files: []
|
|
76
|
+
files:
|
|
77
|
+
- .gitignore
|
|
78
|
+
- Gemfile
|
|
79
|
+
- LICENSE.txt
|
|
80
|
+
- README.md
|
|
81
|
+
- Rakefile
|
|
82
|
+
- bin/configspec-init
|
|
83
|
+
- configspec.gemspec
|
|
84
|
+
- lib/configspec.rb
|
|
85
|
+
- lib/configspec/backend.rb
|
|
86
|
+
- lib/configspec/backend/base.rb
|
|
87
|
+
- lib/configspec/backend/exec.rb
|
|
88
|
+
- lib/configspec/backend/ssh.rb
|
|
89
|
+
- lib/configspec/commands/base.rb
|
|
90
|
+
- lib/configspec/commands/linux.rb
|
|
91
|
+
- lib/configspec/commands/redhat.rb
|
|
92
|
+
- lib/configspec/configuration.rb
|
|
93
|
+
- lib/configspec/helper.rb
|
|
94
|
+
- lib/configspec/helper/configuration.rb
|
|
95
|
+
- lib/configspec/helper/detect_os.rb
|
|
96
|
+
- lib/configspec/helper/exec.rb
|
|
97
|
+
- lib/configspec/helper/properties.rb
|
|
98
|
+
- lib/configspec/helper/redhat.rb
|
|
99
|
+
- lib/configspec/helper/ssh.rb
|
|
100
|
+
- lib/configspec/helper/type.rb
|
|
101
|
+
- lib/configspec/properties.rb
|
|
102
|
+
- lib/configspec/setup.rb
|
|
103
|
+
- lib/configspec/type/base.rb
|
|
104
|
+
- lib/configspec/type/package.rb
|
|
105
|
+
- lib/configspec/version.rb
|
|
106
|
+
- spec/redhat/package_spec.rb
|
|
107
|
+
- spec/spec_helper.rb
|
|
108
|
+
homepage: https://github.com/mizzy/configspec
|
|
109
|
+
licenses:
|
|
110
|
+
- MIT
|
|
111
|
+
metadata: {}
|
|
112
|
+
post_install_message:
|
|
113
|
+
rdoc_options: []
|
|
114
|
+
require_paths:
|
|
115
|
+
- lib
|
|
116
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
117
|
+
requirements:
|
|
118
|
+
- - '>='
|
|
119
|
+
- !ruby/object:Gem::Version
|
|
120
|
+
version: '0'
|
|
121
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
122
|
+
requirements:
|
|
123
|
+
- - '>='
|
|
124
|
+
- !ruby/object:Gem::Version
|
|
125
|
+
version: '0'
|
|
126
|
+
requirements: []
|
|
127
|
+
rubyforge_project:
|
|
128
|
+
rubygems_version: 2.0.3
|
|
129
|
+
signing_key:
|
|
130
|
+
specification_version: 4
|
|
131
|
+
summary: A simple configuration management tool powered by RSpec
|
|
132
|
+
test_files:
|
|
133
|
+
- spec/redhat/package_spec.rb
|
|
134
|
+
- spec/spec_helper.rb
|