configspec 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in configspec.gemspec
4
+ gemspec
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
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $LOAD_PATH.unshift File.join(File.dirname(__FILE__), *%w[.. lib])
4
+
5
+ require 'configspec'
6
+
7
+ Configspec::Setup.run
@@ -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,3 @@
1
+ require 'configspec/backend/base'
2
+ require 'configspec/backend/ssh'
3
+ require 'configspec/backend/exec'
@@ -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,8 @@
1
+ require 'shellwords'
2
+
3
+ module Configspec
4
+ module Commands
5
+ class Linux < Base
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,9 @@
1
+ module Configspec
2
+ module Commands
3
+ class RedHat < Linux
4
+ def install(package)
5
+ cmd = "yum -y install #{package}"
6
+ end
7
+ end
8
+ end
9
+ 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,9 @@
1
+ module Configspec
2
+ module Helper
3
+ module RedHat
4
+ def commands
5
+ Configspec::Commands::RedHat.new
6
+ end
7
+ end
8
+ end
9
+ 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,17 @@
1
+ require 'singleton'
2
+
3
+ module Configspec
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
+
@@ -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
@@ -0,0 +1,9 @@
1
+ module Configspec
2
+ module Type
3
+ class Package < Base
4
+ def installed?
5
+ backend.install(@name)
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,3 @@
1
+ module Configspec
2
+ VERSION = "0.0.1"
3
+ 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
@@ -0,0 +1,8 @@
1
+ require 'spec_helper'
2
+
3
+ include Configspec::Helper::RedHat
4
+
5
+ describe package('httpd') do
6
+ it { should be_installed }
7
+ its(:command) { should eq "yum -y install httpd" }
8
+ end
@@ -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