specinfra 0.0.1 → 0.0.2

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.
Files changed (31) hide show
  1. checksums.yaml +4 -4
  2. data/Rakefile +10 -0
  3. data/lib/specinfra.rb +24 -2
  4. data/lib/specinfra/backend.rb +7 -0
  5. data/lib/specinfra/backend/base.rb +31 -0
  6. data/lib/specinfra/backend/cmd.rb +39 -0
  7. data/lib/specinfra/backend/exec.rb +218 -0
  8. data/lib/specinfra/backend/powershell/command.rb +40 -0
  9. data/lib/specinfra/backend/powershell/script_helper.rb +85 -0
  10. data/lib/specinfra/backend/powershell/support/check_file_access_rules.ps1 +8 -0
  11. data/lib/specinfra/backend/powershell/support/crop_text.ps1 +11 -0
  12. data/lib/specinfra/backend/powershell/support/find_group.ps1 +8 -0
  13. data/lib/specinfra/backend/powershell/support/find_installed_application.ps1 +7 -0
  14. data/lib/specinfra/backend/powershell/support/find_service.ps1 +5 -0
  15. data/lib/specinfra/backend/powershell/support/find_user.ps1 +8 -0
  16. data/lib/specinfra/backend/powershell/support/find_usergroup.ps1 +9 -0
  17. data/lib/specinfra/backend/powershell/support/is_port_listening.ps1 +13 -0
  18. data/lib/specinfra/backend/ssh.rb +93 -0
  19. data/lib/specinfra/backend/winrm.rb +26 -0
  20. data/lib/specinfra/configuration.rb +12 -0
  21. data/lib/specinfra/helper.rb +7 -0
  22. data/lib/specinfra/helper/backend.rb +18 -0
  23. data/lib/specinfra/helper/configuration.rb +37 -0
  24. data/lib/specinfra/helper/detect_os.rb +18 -0
  25. data/lib/specinfra/helper/os.rb +27 -0
  26. data/lib/specinfra/helper/properties.rb +14 -0
  27. data/lib/specinfra/properties.rb +17 -0
  28. data/lib/specinfra/version.rb +1 -1
  29. data/spec/helpers/properties_spec.rb +11 -0
  30. data/spec/spec_helper.rb +4 -0
  31. metadata +31 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 858dd119eb2a4777139bf69b7fa5b8f79b2e33a1
4
- data.tar.gz: f46f73e18a687301dfcc5e483477fa810a0c6bbd
3
+ metadata.gz: 5e123d78d06d4075c043995799a8c0e9f2c3593d
4
+ data.tar.gz: 9a4ce0fdd1c2ff46c42d8323d3a2d31ffc4185e3
5
5
  SHA512:
6
- metadata.gz: 3d2a716331eb898b298fe517cd70b8538dd506267c8c13a590507c7aa351f1bb12e56de8fa09c6cf6f0944056581389104870adea1cbebb7cdffeaffabb816d8
7
- data.tar.gz: 7000c61a15d2051f3d51b700fded57049c3b6af480c89bdac5ca873fc6434bb966316a314688c448fcdabc6e905539cdb55b785b78052776ff677c8e6bdc766e
6
+ metadata.gz: 7440fae579eef9fa415ade153a00dbd556ef529fd4f8bd4f3cc0404ec8d2486e403e4dfcdffe0779e425d73f9e885f3eb14f64008e57445663dc32f6c151ddfa
7
+ data.tar.gz: 7320a36b630ee5ed5317a12546f94b54b79c0697e4b42e04dd5663ea34a8b815ec1847d53572133c4f5a7f5d4d94ff7531e27e81a15b7c02356235ccd607df44
data/Rakefile CHANGED
@@ -1 +1,11 @@
1
1
  require "bundler/gem_tasks"
2
+ require 'rspec/core/rake_task'
3
+
4
+ task :spec => 'spec:all'
5
+
6
+ namespace :spec do
7
+ RSpec::Core::RakeTask.new(:all) do |t|
8
+ t.pattern = "spec/**/*_spec.rb"
9
+ end
10
+ end
11
+
data/lib/specinfra.rb CHANGED
@@ -1,5 +1,27 @@
1
1
  require "specinfra/version"
2
+ require "specinfra/helper"
3
+ require "specinfra/backend"
4
+ require "specinfra/configuration"
2
5
 
3
- module Specinfra
4
- # Your code goes here...
6
+ include SpecInfra
7
+
8
+ module SpecInfra
9
+ class << self
10
+ def configuration
11
+ SpecInfra::Configuration
12
+ end
13
+ end
14
+ end
15
+
16
+ RSpec.configure do |c|
17
+ c.include(SpecInfra::Helper::Configuration)
18
+ c.add_setting :os, :default => nil
19
+ c.add_setting :host, :default => nil
20
+ c.add_setting :ssh, :default => nil
21
+ c.add_setting :sudo_password, :default => nil
22
+ c.add_setting :winrm, :default => nil
23
+ SpecInfra.configuration.defaults.each { |k, v| c.add_setting k, :default => v }
24
+ c.before :each do
25
+ backend.set_example(example) if defined?(SPEC_TYPE)
26
+ end
5
27
  end
@@ -0,0 +1,7 @@
1
+ require 'specinfra/backend/base'
2
+ require 'specinfra/backend/exec'
3
+ require 'specinfra/backend/ssh'
4
+ require 'specinfra/backend/powershell/script_helper'
5
+ require 'specinfra/backend/powershell/command'
6
+ require 'specinfra/backend/cmd'
7
+ require 'specinfra/backend/winrm'
@@ -0,0 +1,31 @@
1
+ require 'singleton'
2
+
3
+ module SpecInfra
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,39 @@
1
+ require 'open3'
2
+
3
+ module SpecInfra
4
+ module Backend
5
+ class Cmd < Base
6
+ include PowerShell::ScriptHelper
7
+
8
+ def run_command(cmd, opts={})
9
+ script = create_script(cmd)
10
+ result = execute_script %Q{powershell -encodedCommand #{encode_script(script)}}
11
+
12
+ if @example
13
+ @example.metadata[:command] = script
14
+ @example.metadata[:stdout] = result[:stdout] + result[:stderr]
15
+ end
16
+ { :stdout => result[:stdout], :stderr => result[:stderr],
17
+ :exit_status => result[:status], :exit_signal => nil }
18
+ end
19
+
20
+ def execute_script script
21
+ if Open3.respond_to? :capture3
22
+ stdout, stderr, status = Open3.capture3(script)
23
+ # powershell still exits with 0 even if there are syntax errors, although it spits the error out into stderr
24
+ # so we have to resort to return an error exit code if there is anything in the standard error
25
+ status = 1 if status == 0 and !stderr.empty?
26
+ { :stdout => stdout, :stderr => stderr, :status => status }
27
+ else
28
+ stdout = `#{script} 2>&1`
29
+ { :stdout => stdout, :stderr => nil, :status => $? }
30
+ end
31
+ end
32
+
33
+ def check_os
34
+ # Dirty hack for specs
35
+ 'Windows'
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,218 @@
1
+ require 'singleton'
2
+
3
+ module SpecInfra
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 = run_with_no_ruby_environment do
11
+ `#{build_command(cmd)} 2>&1`
12
+ end
13
+ # In ruby 1.9, it is possible to use Open3.capture3, but not in 1.8
14
+ #stdout, stderr, status = Open3.capture3(cmd)
15
+
16
+ if @example
17
+ @example.metadata[:command] = cmd
18
+ @example.metadata[:stdout] = stdout
19
+ end
20
+
21
+ { :stdout => stdout, :stderr => nil,
22
+ :exit_status => $?.exitstatus, :exit_signal => nil }
23
+ end
24
+
25
+ def run_with_no_ruby_environment
26
+ keys = %w[BUNDLER_EDITOR BUNDLE_BIN_PATH BUNDLE_GEMFILE
27
+ RUBYOPT GEM_HOME GEM_PATH GEM_CACHE]
28
+
29
+ keys.each { |key| ENV["_SPECINFRA_#{key}"] = ENV[key] ; ENV.delete(key) }
30
+ yield
31
+ ensure
32
+ keys.each { |key| ENV[key] = ENV.delete("_SPECINFRA_#{key}") }
33
+ end
34
+
35
+ def build_command(cmd)
36
+ path = SpecInfra.configuration.path || RSpec.configuration.path
37
+ if path
38
+ cmd = "env PATH=#{path}:$PATH #{cmd}"
39
+ cmd.gsub!(/(\&\&\s*!?\(?\s*)/, "\\1env PATH=#{path}:$PATH ")
40
+ cmd.gsub!(/(\|\|\s*!?\(?\s*)/, "\\1env PATH=#{path}:$PATH ")
41
+ end
42
+ cmd
43
+ end
44
+
45
+ def add_pre_command(cmd)
46
+ path = SpecInfra.configuration.path || RSpec.configuration.path
47
+ if SpecInfra.configuration.pre_command
48
+ cmd = "#{SpecInfra.configuration.pre_command} && #{cmd}"
49
+ cmd = "env PATH=#{path}:$PATH #{cmd}" if path
50
+ end
51
+ cmd
52
+ end
53
+
54
+ def check_running(process)
55
+ ret = run_command(commands.check_running(process))
56
+
57
+ # In Ubuntu, some services are under upstart and "service foo status" returns
58
+ # exit status 0 even though they are stopped.
59
+ # So return false if stdout contains "stopped/waiting".
60
+ return false if ret[:stdout] =~ /stopped\/waiting/
61
+
62
+ # If the service is not registered, check by ps command
63
+ if ret[:exit_status] == 1
64
+ ret = run_command(commands.check_process(process))
65
+ end
66
+
67
+ ret[:exit_status] == 0
68
+ end
69
+
70
+ def check_monitored_by_monit(process)
71
+ ret = run_command(commands.check_monitored_by_monit(process))
72
+ return false unless ret[:stdout] != nil && ret[:exit_status] == 0
73
+
74
+ retlines = ret[:stdout].split(/[\r\n]+/).map(&:strip)
75
+ proc_index = retlines.index("Process '#{process}'")
76
+ return false unless proc_index
77
+
78
+ retlines[proc_index+2].match(/\Amonitoring status\s+monitored\Z/i) != nil
79
+ end
80
+
81
+ def check_readable(file, by_whom)
82
+ mode = sprintf('%04s',run_command(commands.get_mode(file))[:stdout].strip)
83
+ mode = mode.split('')
84
+ mode_octal = mode[0].to_i * 512 + mode[1].to_i * 64 + mode[2].to_i * 8 + mode[3].to_i * 1
85
+ case by_whom
86
+ when nil
87
+ mode_octal & 0444 != 0
88
+ when 'owner'
89
+ mode_octal & 0400 != 0
90
+ when 'group'
91
+ mode_octal & 0040 != 0
92
+ when 'others'
93
+ mode_octal & 0004 != 0
94
+ end
95
+ end
96
+
97
+ def check_writable(file, by_whom)
98
+ mode = sprintf('%04s',run_command(commands.get_mode(file))[:stdout].strip)
99
+ mode = mode.split('')
100
+ mode_octal = mode[0].to_i * 512 + mode[1].to_i * 64 + mode[2].to_i * 8 + mode[3].to_i * 1
101
+ case by_whom
102
+ when nil
103
+ mode_octal & 0222 != 0
104
+ when 'owner'
105
+ mode_octal & 0200 != 0
106
+ when 'group'
107
+ mode_octal & 0020 != 0
108
+ when 'others'
109
+ mode_octal & 0002 != 0
110
+ end
111
+ end
112
+
113
+ def check_executable(file, by_whom)
114
+ mode = sprintf('%04s',run_command(commands.get_mode(file))[:stdout].strip)
115
+ mode = mode.split('')
116
+ mode_octal = mode[0].to_i * 512 + mode[1].to_i * 64 + mode[2].to_i * 8 + mode[3].to_i * 1
117
+ case by_whom
118
+ when nil
119
+ mode_octal & 0111 != 0
120
+ when 'owner'
121
+ mode_octal & 0100 != 0
122
+ when 'group'
123
+ mode_octal & 0010 != 0
124
+ when 'others'
125
+ mode_octal & 0001 != 0
126
+ end
127
+ end
128
+
129
+ def check_mounted(path, expected_attr, only_with)
130
+ ret = run_command(commands.check_mounted(path))
131
+ if expected_attr.nil? || ret[:exit_status] != 0
132
+ return ret[:exit_status] == 0
133
+ end
134
+
135
+ mount = ret[:stdout].scan(/\S+/)
136
+ actual_attr = { :device => mount[0], :type => mount[4] }
137
+ mount[5].gsub(/\(|\)/, '').split(',').each do |option|
138
+ name, val = option.split('=')
139
+ if val.nil?
140
+ actual_attr[name.to_sym] = true
141
+ else
142
+ val = val.to_i if val.match(/^\d+$/)
143
+ actual_attr[name.to_sym] = val
144
+ end
145
+ end
146
+
147
+ if ! expected_attr[:options].nil?
148
+ expected_attr.merge!(expected_attr[:options])
149
+ expected_attr.delete(:options)
150
+ end
151
+
152
+ if only_with
153
+ actual_attr == expected_attr
154
+ else
155
+ expected_attr.each do |key, val|
156
+ return false if actual_attr[key] != val
157
+ end
158
+ true
159
+ end
160
+ end
161
+
162
+ def check_routing_table(expected_attr)
163
+ return false if ! expected_attr[:destination]
164
+ ret = run_command(commands.check_routing_table(expected_attr[:destination]))
165
+ return false if ret[:exit_status] != 0
166
+
167
+ ret[:stdout] =~ /^(\S+)(?: via (\S+))? dev (\S+).+\r\n(?:default via (\S+))?/
168
+ actual_attr = {
169
+ :destination => $1,
170
+ :gateway => $2 ? $2 : $4,
171
+ :interface => expected_attr[:interface] ? $3 : nil
172
+ }
173
+
174
+ expected_attr.each do |key, val|
175
+ return false if actual_attr[key] != val
176
+ end
177
+ true
178
+ end
179
+
180
+ def check_os
181
+ return RSpec.configuration.os if RSpec.configuration.os
182
+ if run_command('ls /etc/redhat-release')[:exit_status] == 0
183
+ line = run_command('cat /etc/redhat-release')[:stdout]
184
+ if line =~ /release (\d[\d.]*)/
185
+ release = $1
186
+ end
187
+ { :family => 'RedHat', :release => release }
188
+ elsif run_command('ls /etc/system-release')[:exit_status] == 0
189
+ { :family => 'RedHat', :release => nil } # Amazon Linux
190
+ elsif run_command('ls /etc/debian_version')[:exit_status] == 0
191
+ { :family => 'Debian', :release => nil }
192
+ elsif run_command('ls /etc/gentoo-release')[:exit_status] == 0
193
+ { :family => 'Gentoo', :release => nil }
194
+ elsif run_command('ls /usr/lib/setup/Plamo-*')[:exit_status] == 0
195
+ { :family => 'Plamo', :release => nil }
196
+ elsif run_command('uname -s')[:stdout] =~ /AIX/i
197
+ { :family => 'AIX', :release => nil }
198
+ elsif (os = run_command('uname -sr')[:stdout]) && os =~ /SunOS/i
199
+ if os =~ /5.10/
200
+ { :family => 'Solaris10', :release => nil }
201
+ elsif run_command('grep -q "Oracle Solaris 11" /etc/release')[:exit_status] == 0
202
+ { :family => 'Solaris11', :release => nil }
203
+ elsif run_command('grep -q SmartOS /etc/release')[:exit_status] == 0
204
+ { :family => 'SmartOS', :release => nil }
205
+ else
206
+ { :family => 'Solaris', :release => nil }
207
+ end
208
+ elsif run_command('uname -s')[:stdout] =~ /Darwin/i
209
+ { :family => 'Darwin', :release => nil }
210
+ elsif run_command('uname -s')[:stdout] =~ /FreeBSD/i
211
+ { :family => 'FreeBSD', :release => nil }
212
+ else
213
+ { :family => 'Base', :release => nil }
214
+ end
215
+ end
216
+ end
217
+ end
218
+ end
@@ -0,0 +1,40 @@
1
+ module SpecInfra
2
+ module Backend
3
+ module PowerShell
4
+ class Command
5
+ attr_reader :import_functions, :script
6
+ def initialize &block
7
+ @import_functions = []
8
+ @script = ""
9
+ instance_eval &block if block_given?
10
+ end
11
+
12
+ def using *functions
13
+ functions.each { |f| import_functions << f }
14
+ end
15
+
16
+ def exec code
17
+ @script = code
18
+ end
19
+
20
+ def convert_regexp(target)
21
+ case target
22
+ when Regexp
23
+ target.source
24
+ else
25
+ target.to_s.gsub '/', ''
26
+ end
27
+ end
28
+
29
+ def get_identity id
30
+ raise "You must provide a specific Windows user/group" if id =~ /(owner|group|others)/
31
+ identity = id || 'Everyone'
32
+ end
33
+
34
+ def to_s
35
+ @script
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,85 @@
1
+ require 'base64'
2
+
3
+ module SpecInfra
4
+ module Backend
5
+ module PowerShell
6
+ module ScriptHelper
7
+ def build_command(cmd)
8
+ path = SpecInfra.configuration.path || RSpec.configuration.path
9
+ if path
10
+ cmd.strip!
11
+ cmd =
12
+ <<-EOF
13
+ $env:path = "#{path};$env:path"
14
+ #{cmd}
15
+ EOF
16
+ end
17
+ cmd
18
+ end
19
+
20
+ def add_pre_command(cmd)
21
+ path = SpecInfra.configuration.path || RSpec.configuration.path
22
+ if SpecInfra.configuration.pre_command
23
+ cmd.strip!
24
+ cmd =
25
+ <<-EOF
26
+ if (#{SpecInfra.configuration.pre_command})
27
+ {
28
+ #{cmd}
29
+ }
30
+ EOF
31
+ cmd = "$env:path = \"#{path};$env:path\"\n#{cmd}" if path
32
+ end
33
+ cmd
34
+ end
35
+
36
+ def encode_script script
37
+ script_text = script.chars.to_a.join("\x00").chomp
38
+ script_text << "\x00" unless script_text[-1].eql? "\x00"
39
+ if script_text.respond_to?(:encode)
40
+ script_text = script_text.encode('ASCII-8BIT')
41
+ end
42
+ if Base64.respond_to?(:strict_encode64)
43
+ Base64.strict_encode64(script_text)
44
+ else
45
+ [ script_text ].pack("m").strip
46
+ end
47
+ end
48
+
49
+ def create_script command
50
+ if command.is_a? Command
51
+ ps_functions = command.import_functions.map { |f| File.read(File.join(File.dirname(__FILE__), 'support', f)) }
52
+ script = build_command(command.script)
53
+ script = add_pre_command(script)
54
+ <<-EOF
55
+ $exitCode = 1
56
+ try {
57
+ #{ps_functions.join("\n")}
58
+ $success = (#{script})
59
+ if ($success -is [Boolean] -and $success) { $exitCode = 0 }
60
+ } catch {
61
+ Write-Output $_.Exception.Message
62
+ }
63
+ Write-Output "Exiting with code: $exitCode"
64
+ exit $exitCode
65
+ EOF
66
+ else
67
+ script = build_command(command.to_s)
68
+ add_pre_command(script)
69
+ end
70
+ end
71
+
72
+ def check_running(process)
73
+ ret = run_command(commands.check_running(process))
74
+
75
+ # If the service is not registered, check the process
76
+ if ret[:exit_status] == 1
77
+ ret = run_command(commands.check_process(process))
78
+ end
79
+
80
+ ret[:exit_status] == 0
81
+ end
82
+ end
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,8 @@
1
+ function CheckFileAccessRules
2
+ {
3
+ param($path, $identity, $rules)
4
+
5
+ $accessRules = @((Get-Acl $path).access | Where-Object {$_.AccessControlType -eq 'Allow' -and $_.IdentityReference -eq $identity })
6
+ $match = @($accessRules | Where-Object {($_.FileSystemRights.ToString().Split(',') | % {$_.trim()} | ? {$rules -contains $_}) -ne $null})
7
+ $match.count -gt 0
8
+ }
@@ -0,0 +1,11 @@
1
+ function CropText
2
+ {
3
+ param($text, $fromPattern, $toPattern)
4
+
5
+ $from, $to = ([regex]::matches($text, $fromPattern)), ([regex]::matches($text, $toPattern))
6
+ if ($from.count -gt 0 -and $to.count -gt 0) {
7
+ $text.substring($from[0].index, $to[0].index + $to[0].length - $from[0].index)
8
+ } else {
9
+ ""
10
+ }
11
+ }
@@ -0,0 +1,8 @@
1
+ function FindGroup
2
+ {
3
+ param($groupName, $domain)
4
+ if ($domain -eq $null) {$selectionCriteria = " and LocalAccount = true"}
5
+ else {$selectionCriteria = " and Domain = '$domain'"}
6
+
7
+ Get-WmiObject Win32_Group -filter "Name = '$groupName' $selectionCriteria"
8
+ }
@@ -0,0 +1,7 @@
1
+ function FindInstalledApplication
2
+ {
3
+ param($appName, $appVersion)
4
+ $selectionCriteria = "(Name like '$appName' or PackageName like '$appName') and InstallState = 5"
5
+ if ($appVersion -ne $null) { $selectionCriteria += " and version = '$appVersion'"}
6
+ Get-WmiObject Win32_Product -filter $selectionCriteria
7
+ }
@@ -0,0 +1,5 @@
1
+ function FindService
2
+ {
3
+ param($name)
4
+ Get-WmiObject Win32_Service | Where-Object {$_.Name -eq $name -or $_.DisplayName -eq $name}
5
+ }
@@ -0,0 +1,8 @@
1
+ function FindUser
2
+ {
3
+ param($userName, $domain)
4
+ if ($domain -eq $null) {$selectionCriteria = " and LocalAccount = true"}
5
+ else {$selectionCriteria = " and Domain = '$domain'"}
6
+
7
+ Get-WmiObject Win32_UserAccount -filter "Name = '$userName' $selectionCriteria"
8
+ }
@@ -0,0 +1,9 @@
1
+ function FindUserGroup
2
+ {
3
+ param($userName, $groupName, $userDomain, $groupDomain)
4
+ $user = FindUser -userName $userName -domain $userDomain
5
+ $group = FindGroup -groupName $groupName -domain $groupDomain
6
+ if ($user -and $group) {
7
+ Get-WmiObject Win32_GroupUser -filter ("GroupComponent = 'Win32_Group.Domain=`"" + $group.domain + "`",Name=`"" + $group.name + "`"' and PartComponent = 'Win32_UserAccount.Domain=`"" + $user.domain + "`",Name=`"" + $user.name + "`"'")
8
+ }
9
+ }
@@ -0,0 +1,13 @@
1
+ function IsPortListening
2
+ {
3
+ param($portNumber, $protocol)
4
+ $netstatOutput = netstat -an | Out-String
5
+ $networkIPs = (Get-WmiObject Win32_NetworkAdapterConfiguration | ? {$_.IPEnabled}) | %{ $_.IPAddress[0] }
6
+ foreach ($ipaddress in $networkIPs)
7
+ {
8
+ $matchExpression = ("$ipaddress" + ":" + $portNumber)
9
+ if ($protocol) { $matchExpression = ($protocol.toUpper() + "\s+$matchExpression") }
10
+ if ($netstatOutput -match $matchExpression) { return $true }
11
+ }
12
+ $false
13
+ }
@@ -0,0 +1,93 @@
1
+ require 'specinfra/backend/exec'
2
+
3
+ module SpecInfra
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 = SpecInfra.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 = SpecInfra.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,26 @@
1
+ module SpecInfra
2
+ module Backend
3
+ class WinRM < Base
4
+ include PowerShell::ScriptHelper
5
+
6
+ def run_command(cmd, opts={})
7
+ script = create_script(cmd)
8
+ winrm = RSpec.configuration.winrm
9
+
10
+ result = winrm.powershell(script)
11
+ stdout, stderr = [:stdout, :stderr].map do |s|
12
+ result[:data].select {|item| item.key? s}.map {|item| item[s]}.join
13
+ end
14
+ result[:exitcode] = 1 if result[:exitcode] == 0 and !stderr.empty?
15
+
16
+ if @example
17
+ @example.metadata[:command] = script
18
+ @example.metadata[:stdout] = stdout + stderr
19
+ end
20
+
21
+ { :stdout => stdout, :stderr => stderr,
22
+ :exit_status => result[:exitcode], :exit_signal => nil }
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,12 @@
1
+ module SpecInfra
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,7 @@
1
+ require 'specinfra/helper/os'
2
+ require 'specinfra/helper/detect_os'
3
+ require 'specinfra/helper/backend'
4
+ require 'specinfra/helper/configuration'
5
+
6
+ require 'specinfra/helper/properties'
7
+ include SpecInfra::Helper::Properties
@@ -0,0 +1,18 @@
1
+ module SpecInfra
2
+ module Helper
3
+ ['Exec', 'Ssh', 'Cmd', 'WinRM'].each do |backend|
4
+ eval <<-EOF
5
+ module #{backend}
6
+ def backend(commands_object=nil)
7
+ if ! respond_to?(:commands)
8
+ commands_object = self.class.const_get(SPEC_TYPE).const_get('Commands').const_get('Base').new
9
+ end
10
+ instance = self.class.const_get('SpecInfra').const_get('Backend').const_get('#{backend}').instance
11
+ instance.set_commands(commands_object || commands)
12
+ instance
13
+ end
14
+ end
15
+ EOF
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,37 @@
1
+ module SpecInfra
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
+ SpecInfra::Configuration.defaults.keys.each do |c|
30
+ value = self.respond_to?(c.to_sym) ?
31
+ self.send(c) : RSpec.configuration.send(c)
32
+ SpecInfra::Configuration.send(:"#{c}=", value)
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,18 @@
1
+ module SpecInfra
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(self.class.const_get(SPEC_TYPE).const_get('Commands').const_get('Base')).check_os
12
+ property[:os_by_host][host] = os
13
+ end
14
+ self.class.const_get(SPEC_TYPE).const_get('Commands').const_get(os[:family]).new
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,27 @@
1
+ module SpecInfra
2
+ module Helper
3
+ [
4
+ 'Base',
5
+ 'AIX',
6
+ 'Darwin',
7
+ 'Debian',
8
+ 'FreeBSD',
9
+ 'Gentoo',
10
+ 'Plamo',
11
+ 'RedHat',
12
+ 'SmartOS',
13
+ 'Solaris',
14
+ 'Solaris10',
15
+ 'Solaris11',
16
+ 'Windows',
17
+ ].each do |os|
18
+ eval <<-EOF
19
+ module #{os}
20
+ def commands
21
+ self.class.const_get(SPEC_TYPE).const_get('Commands').const_get('#{os}').new
22
+ end
23
+ end
24
+ EOF
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,14 @@
1
+ require 'specinfra/properties'
2
+
3
+ module SpecInfra
4
+ module Helper
5
+ module Properties
6
+ def property
7
+ SpecInfra::Properties.instance.properties
8
+ end
9
+ def set_property(prop)
10
+ SpecInfra::Properties.instance.properties(prop)
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,17 @@
1
+ require 'singleton'
2
+
3
+ module SpecInfra
4
+ class Properties
5
+ include Singleton
6
+ def initialize
7
+ @prop = {}
8
+ end
9
+ def properties(prop=nil)
10
+ if ! prop.nil?
11
+ @prop = prop
12
+ end
13
+ @prop
14
+ end
15
+ end
16
+ end
17
+
@@ -1,3 +1,3 @@
1
1
  module Specinfra
2
- VERSION = "0.0.1"
2
+ VERSION = "0.0.2"
3
3
  end
@@ -0,0 +1,11 @@
1
+ require 'spec_helper'
2
+
3
+ include SpecInfra::Helper::Properties
4
+
5
+ describe 'Properties Helper' do
6
+ before :all do
7
+ set_property :role => 'proxy'
8
+ end
9
+ subject { property }
10
+ it { should include :role => 'proxy' }
11
+ end
@@ -0,0 +1,4 @@
1
+ require 'specinfra'
2
+
3
+ include SpecInfra::Helper::Exec
4
+
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: specinfra
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Gosuke Miyashita
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2013-11-27 00:00:00.000000000 Z
11
+ date: 2013-11-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -51,7 +51,33 @@ files:
51
51
  - README.md
52
52
  - Rakefile
53
53
  - lib/specinfra.rb
54
+ - lib/specinfra/backend.rb
55
+ - lib/specinfra/backend/base.rb
56
+ - lib/specinfra/backend/cmd.rb
57
+ - lib/specinfra/backend/exec.rb
58
+ - lib/specinfra/backend/powershell/command.rb
59
+ - lib/specinfra/backend/powershell/script_helper.rb
60
+ - lib/specinfra/backend/powershell/support/check_file_access_rules.ps1
61
+ - lib/specinfra/backend/powershell/support/crop_text.ps1
62
+ - lib/specinfra/backend/powershell/support/find_group.ps1
63
+ - lib/specinfra/backend/powershell/support/find_installed_application.ps1
64
+ - lib/specinfra/backend/powershell/support/find_service.ps1
65
+ - lib/specinfra/backend/powershell/support/find_user.ps1
66
+ - lib/specinfra/backend/powershell/support/find_usergroup.ps1
67
+ - lib/specinfra/backend/powershell/support/is_port_listening.ps1
68
+ - lib/specinfra/backend/ssh.rb
69
+ - lib/specinfra/backend/winrm.rb
70
+ - lib/specinfra/configuration.rb
71
+ - lib/specinfra/helper.rb
72
+ - lib/specinfra/helper/backend.rb
73
+ - lib/specinfra/helper/configuration.rb
74
+ - lib/specinfra/helper/detect_os.rb
75
+ - lib/specinfra/helper/os.rb
76
+ - lib/specinfra/helper/properties.rb
77
+ - lib/specinfra/properties.rb
54
78
  - lib/specinfra/version.rb
79
+ - spec/helpers/properties_spec.rb
80
+ - spec/spec_helper.rb
55
81
  - specinfra.gemspec
56
82
  homepage: ''
57
83
  licenses:
@@ -77,4 +103,6 @@ rubygems_version: 2.0.3
77
103
  signing_key:
78
104
  specification_version: 4
79
105
  summary: Common layer for serverspec and configspec
80
- test_files: []
106
+ test_files:
107
+ - spec/helpers/properties_spec.rb
108
+ - spec/spec_helper.rb