ansible_spec_plus 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +3 -0
- data/Gemfile +5 -0
- data/Gemfile.lock +73 -0
- data/Guardfile +14 -0
- data/Jenkinsfile +71 -0
- data/README.md +291 -0
- data/ansible_spec_plus.gemspec +26 -0
- data/bin/asp +72 -0
- data/bin/asp-init +78 -0
- data/lib/ansible_spec_plus.rb +653 -0
- data/lib/ansiblespec_helper.rb +34 -0
- data/lib/helpers/buffered_logger.rb +110 -0
- data/lib/helpers/color_formatter.rb +18 -0
- data/lib/helpers/colorize.rb +52 -0
- data/lib/helpers/log.rb +80 -0
- data/lib/helpers/ring_buffer.rb +65 -0
- data/lib/src/.ansiblespec +5 -0
- data/lib/src/.rspec +3 -0
- data/lib/src/spec/spec_helper.rb +92 -0
- data/spec/ansible_spec_plus_spec.rb +553 -0
- data/spec_helper.rb +16 -0
- metadata +145 -0
@@ -0,0 +1,34 @@
|
|
1
|
+
module AnsibleSpecHelper
|
2
|
+
def self.get_properties
|
3
|
+
playbook, inventoryfile = AnsibleSpec.load_ansiblespec
|
4
|
+
|
5
|
+
hosts = AnsibleSpec.load_targets(inventoryfile)
|
6
|
+
properties = AnsibleSpec.load_playbook(playbook)
|
7
|
+
|
8
|
+
properties.each do |var|
|
9
|
+
var["hosts_childrens"] = hosts["hosts_childrens"]
|
10
|
+
var["group"] = var["hosts"]
|
11
|
+
if var["hosts"].to_s == "all"
|
12
|
+
var["hosts"] = hosts.values.flatten
|
13
|
+
elsif hosts.has_key?("#{var["hosts"]}")
|
14
|
+
var["hosts"] = hosts["#{var["hosts"]}"]
|
15
|
+
elsif var["hosts"].instance_of?(Array)
|
16
|
+
tmp_host = var["hosts"]
|
17
|
+
var["hosts"] = []
|
18
|
+
tmp_host.each do |v|
|
19
|
+
if hosts.has_key?("#{v}")
|
20
|
+
hosts["#{v}"].map {|target_server| target_server["hosts"] = v}
|
21
|
+
var["hosts"].concat hosts["#{v}"]
|
22
|
+
end
|
23
|
+
end
|
24
|
+
if var["hosts"].size == 0
|
25
|
+
properties = properties.compact.reject{|e| e["hosts"].length == 0}
|
26
|
+
end
|
27
|
+
else
|
28
|
+
var["hosts"] = []
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
return properties
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,110 @@
|
|
1
|
+
require 'logger'
|
2
|
+
require_relative 'ring_buffer'
|
3
|
+
|
4
|
+
module Helpers
|
5
|
+
|
6
|
+
class BufferedLogger < Logger
|
7
|
+
|
8
|
+
#
|
9
|
+
# :call-seq:
|
10
|
+
# Logger.new(name, shift_age = 7, shift_size = 1048576)
|
11
|
+
# Logger.new(name, shift_age = 'weekly')
|
12
|
+
#
|
13
|
+
# === Args
|
14
|
+
#
|
15
|
+
# +logdev+::
|
16
|
+
# The log device. This is a filename (String) or IO object (typically
|
17
|
+
# +STDOUT+, +STDERR+, or an open file).
|
18
|
+
# +shift_age+::
|
19
|
+
# Number of old log files to keep, *or* frequency of rotation (+daily+,
|
20
|
+
# +weekly+ or +monthly+).
|
21
|
+
# +shift_size+::
|
22
|
+
# Maximum logfile size (only applies when +shift_age+ is a number).
|
23
|
+
#
|
24
|
+
# === Description
|
25
|
+
#
|
26
|
+
# Create an instance.
|
27
|
+
#
|
28
|
+
def initialize(logdev, shift_age = 0, shift_size = 1048576)
|
29
|
+
super
|
30
|
+
if logdev == STDOUT
|
31
|
+
@stdout = STDOUT
|
32
|
+
end
|
33
|
+
|
34
|
+
@last_messages = RingBuffer.new(200)
|
35
|
+
end
|
36
|
+
|
37
|
+
#
|
38
|
+
# :call-seq:
|
39
|
+
# Logger#add(severity, message = nil, progname = nil) { ... }
|
40
|
+
#
|
41
|
+
# === Args
|
42
|
+
#
|
43
|
+
# +severity+::
|
44
|
+
# Severity. Constants are defined in Logger namespace: +DEBUG+, +INFO+,
|
45
|
+
# +WARN+, +ERROR+, +FATAL+, or +UNKNOWN+.
|
46
|
+
# +message+::
|
47
|
+
# The log message. A String or Exception.
|
48
|
+
# +progname+::
|
49
|
+
# Program name string. Can be omitted. Treated as a message if no
|
50
|
+
# +message+ and +block+ are given.
|
51
|
+
# +block+::
|
52
|
+
# Can be omitted. Called to get a message string if +message+ is nil.
|
53
|
+
#
|
54
|
+
# === Return
|
55
|
+
#
|
56
|
+
# When the given severity is not high enough (for this particular logger),
|
57
|
+
# log no message, and return +true+. Even if the severity is not high enough
|
58
|
+
# we save each message in a ring buffer.
|
59
|
+
#
|
60
|
+
# === Description
|
61
|
+
#
|
62
|
+
# Log a message if the given severity is high enough. This is the generic
|
63
|
+
# logging method. Users will be more inclined to use #debug, #info, #warn,
|
64
|
+
# #error, and #fatal.
|
65
|
+
#
|
66
|
+
# <b>Message format</b>: +message+ can be any object, but it has to be
|
67
|
+
# converted to a String in order to log it. Generally, +inspect+ is used
|
68
|
+
# if the given object is not a String.
|
69
|
+
# A special case is an +Exception+ object, which will be printed in detail,
|
70
|
+
# including message, class, and backtrace. See #msg2str for the
|
71
|
+
# implementation if required.
|
72
|
+
#
|
73
|
+
# === Bugs
|
74
|
+
#
|
75
|
+
# * Logfile is not locked.
|
76
|
+
# * Append open does not need to lock file.
|
77
|
+
# * If the OS supports multi I/O, records possibly may be mixed.
|
78
|
+
#
|
79
|
+
def add(severity, message = nil, progname = nil, &block)
|
80
|
+
super
|
81
|
+
severity ||= UNKNOWN
|
82
|
+
progname ||= @progname
|
83
|
+
if message.nil?
|
84
|
+
if block_given?
|
85
|
+
message = yield
|
86
|
+
else
|
87
|
+
message = progname
|
88
|
+
progname = @progname
|
89
|
+
end
|
90
|
+
end
|
91
|
+
@last_messages.push(format_message(format_severity(severity), Time.now, progname, message))
|
92
|
+
@stdout.flush if @stdout
|
93
|
+
true
|
94
|
+
end
|
95
|
+
|
96
|
+
#
|
97
|
+
# === Return
|
98
|
+
#
|
99
|
+
# Get array of last log messages. Even log messages with low severity are included.
|
100
|
+
# The number of returned messages is limited by the size of the underlying ring buffer.
|
101
|
+
#
|
102
|
+
# === Description
|
103
|
+
#
|
104
|
+
# After calling this method the underling ring buffer is empty.
|
105
|
+
#
|
106
|
+
def last_messages
|
107
|
+
@last_messages.flush
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require_relative 'colorize'
|
2
|
+
require 'logger'
|
3
|
+
|
4
|
+
module Helpers
|
5
|
+
# colorful log formatter, also logs thread object id instead of process id
|
6
|
+
class ColorFormatter < Logger::Formatter
|
7
|
+
|
8
|
+
SCHEMA = %w(nothing green yellow red purple cyan)
|
9
|
+
|
10
|
+
def call(severity, time, progname, msg)
|
11
|
+
level = ::Logger::Severity.const_get(severity)
|
12
|
+
color = SCHEMA[level]
|
13
|
+
text = Format % [severity[0..0], format_datetime(time), Thread.current.object_id, severity, progname,
|
14
|
+
msg2str(msg)]
|
15
|
+
color ? Colorize.colorize(color, text) : text
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
module Helpers
|
2
|
+
module Colorize
|
3
|
+
module Colors
|
4
|
+
NOTHING = '0;0'
|
5
|
+
BLACK = '0;30'
|
6
|
+
DARK_RED = '0;31'
|
7
|
+
DARK_GREEN = '0;32'
|
8
|
+
BROWN = '0;33'
|
9
|
+
BLUE = '0;34'
|
10
|
+
PURPLE = '0;35'
|
11
|
+
CYAN = '0;36'
|
12
|
+
GRAY = '0;37'
|
13
|
+
LIGHT_GRAY = '0;37'
|
14
|
+
DARK_GRAY = '1;30'
|
15
|
+
RED = '1;31'
|
16
|
+
GREEN = '1;32'
|
17
|
+
YELLOW = '1;33'
|
18
|
+
LIGHT_BLUE = '1;34'
|
19
|
+
LIGHT_PURPLE = '1;35'
|
20
|
+
LIGHT_CYAN = '1;36'
|
21
|
+
WHITE = '1;37'
|
22
|
+
end
|
23
|
+
|
24
|
+
class << self
|
25
|
+
def colorize(color, text)
|
26
|
+
color = Colorize::Colors.const_get(color.to_s.upcase)
|
27
|
+
color ? colorful_text(color, text) : text
|
28
|
+
rescue
|
29
|
+
# puts color
|
30
|
+
# puts $?
|
31
|
+
text
|
32
|
+
end
|
33
|
+
|
34
|
+
def method_missing(symbol, *args)
|
35
|
+
color = Colorize::Colors.const_get(color.to_s.upcase)
|
36
|
+
return colorful_text(color, *args) if color
|
37
|
+
super
|
38
|
+
rescue
|
39
|
+
super
|
40
|
+
# puts symbol
|
41
|
+
# puts $?
|
42
|
+
# args
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
def colorful_text(color, text)
|
48
|
+
"\e[#{color}m#{text}\e[0;0m"
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
data/lib/helpers/log.rb
ADDED
@@ -0,0 +1,80 @@
|
|
1
|
+
require 'logger'
|
2
|
+
require_relative 'color_formatter'
|
3
|
+
require_relative 'buffered_logger'
|
4
|
+
|
5
|
+
module Helpers
|
6
|
+
module Log
|
7
|
+
class << self
|
8
|
+
def log
|
9
|
+
unless @logger
|
10
|
+
@logger = BufferedLogger.new($stdout)
|
11
|
+
@logger.formatter = ColorFormatter.new if $stdout.tty?
|
12
|
+
end
|
13
|
+
@logger
|
14
|
+
end
|
15
|
+
|
16
|
+
def log=(logger)
|
17
|
+
@logger = logger
|
18
|
+
log.info('changed logger')
|
19
|
+
end
|
20
|
+
|
21
|
+
def debug?
|
22
|
+
!@no_debug
|
23
|
+
end
|
24
|
+
|
25
|
+
def set_debug(debug, level = ::Logger::INFO)
|
26
|
+
@no_debug = !debug
|
27
|
+
log.level = level
|
28
|
+
end
|
29
|
+
|
30
|
+
def last_messages
|
31
|
+
return [] unless @logger
|
32
|
+
@logger.last_messages
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.included(base)
|
37
|
+
class << base
|
38
|
+
def log
|
39
|
+
Log.log
|
40
|
+
end
|
41
|
+
|
42
|
+
def log=(logger)
|
43
|
+
Log.log=(logger)
|
44
|
+
end
|
45
|
+
|
46
|
+
def debug?
|
47
|
+
Log.debug?
|
48
|
+
end
|
49
|
+
|
50
|
+
def set_debug(debug, level = Logger::INFO)
|
51
|
+
Log.set_debug(debug, level)
|
52
|
+
end
|
53
|
+
|
54
|
+
def last_messages
|
55
|
+
Log.last_messages
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def log
|
61
|
+
Log.log
|
62
|
+
end
|
63
|
+
|
64
|
+
def log=(logger)
|
65
|
+
Log.log=(logger)
|
66
|
+
end
|
67
|
+
|
68
|
+
def debug?
|
69
|
+
Log.debug?
|
70
|
+
end
|
71
|
+
|
72
|
+
def set_debug(debug, level = ::Logger::INFO)
|
73
|
+
Log.set_debug(debug, level)
|
74
|
+
end
|
75
|
+
|
76
|
+
def last_messages
|
77
|
+
Log.last_messages
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
module Helpers
|
2
|
+
class RingBuffer
|
3
|
+
def initialize(size)
|
4
|
+
@size = size
|
5
|
+
@start = 0
|
6
|
+
@count = 0
|
7
|
+
@buffer = Array.new(size)
|
8
|
+
@mutex = Mutex.new
|
9
|
+
end
|
10
|
+
|
11
|
+
def full?
|
12
|
+
@count == @size
|
13
|
+
end
|
14
|
+
|
15
|
+
def empty?
|
16
|
+
@count == 0
|
17
|
+
end
|
18
|
+
|
19
|
+
def push(value)
|
20
|
+
@mutex.synchronize do
|
21
|
+
stop = (@start + @count) % @size
|
22
|
+
@buffer[stop] = value
|
23
|
+
if full?
|
24
|
+
@start = (@start + 1) % @size
|
25
|
+
else
|
26
|
+
@count += 1
|
27
|
+
end
|
28
|
+
value
|
29
|
+
end
|
30
|
+
end
|
31
|
+
alias :<< :push
|
32
|
+
|
33
|
+
def shift
|
34
|
+
@mutex.synchronize do
|
35
|
+
remove_element
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def flush
|
40
|
+
values = []
|
41
|
+
@mutex.synchronize do
|
42
|
+
until empty?
|
43
|
+
values << remove_element
|
44
|
+
end
|
45
|
+
end
|
46
|
+
values
|
47
|
+
end
|
48
|
+
|
49
|
+
def clear
|
50
|
+
@buffer = Array.new(@size)
|
51
|
+
@start = 0
|
52
|
+
@count = 0
|
53
|
+
end
|
54
|
+
|
55
|
+
private
|
56
|
+
|
57
|
+
def remove_element
|
58
|
+
return nil if empty?
|
59
|
+
value, @buffer[@start] = @buffer[@start], nil
|
60
|
+
@start = (@start + 1) % @size
|
61
|
+
@count -= 1
|
62
|
+
value
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
data/lib/src/.rspec
ADDED
@@ -0,0 +1,92 @@
|
|
1
|
+
require 'serverspec'
|
2
|
+
require 'net/ssh'
|
3
|
+
require 'ansible_spec'
|
4
|
+
require 'winrm'
|
5
|
+
|
6
|
+
ENV['BACKEND'] = 'ssh' if ! ENV['BACKEND'] || ENV['BACKEND'] == ''
|
7
|
+
|
8
|
+
#
|
9
|
+
# Set ansible variables to serverspec property
|
10
|
+
#
|
11
|
+
|
12
|
+
host = ENV['TARGET_HOST']
|
13
|
+
hosts = ENV["TARGET_HOSTS"]
|
14
|
+
|
15
|
+
group_idx = ENV['TARGET_GROUP_INDEX'].to_i
|
16
|
+
ssh_config_file = AnsibleSpec.get_ssh_config_file
|
17
|
+
|
18
|
+
connection = ENV['TARGET_CONNECTION']
|
19
|
+
|
20
|
+
if connection != 'winrm'
|
21
|
+
#
|
22
|
+
# OS type: UN*X
|
23
|
+
#
|
24
|
+
set :backend, :"#{ENV['BACKEND']}"
|
25
|
+
|
26
|
+
if ENV['ASK_SUDO_PASSWORD']
|
27
|
+
begin
|
28
|
+
require 'highline/import'
|
29
|
+
rescue LoadError
|
30
|
+
fail "highline is not available. Try installing it."
|
31
|
+
end
|
32
|
+
set :sudo_password, ask("Enter sudo password: ") { |q| q.echo = false }
|
33
|
+
else
|
34
|
+
set :sudo_password, ENV['SUDO_PASSWORD']
|
35
|
+
end
|
36
|
+
|
37
|
+
unless ssh_config_file
|
38
|
+
options = Net::SSH::Config.for(host)
|
39
|
+
else
|
40
|
+
options = Net::SSH::Config.for(host,files=[ssh_config_file])
|
41
|
+
end
|
42
|
+
|
43
|
+
options[:user] ||= ENV['TARGET_USER']
|
44
|
+
options[:port] ||= ENV['TARGET_PORT']
|
45
|
+
options[:keys] ||= ENV['TARGET_PRIVATE_KEY']
|
46
|
+
|
47
|
+
set :host, options[:host_name] || host
|
48
|
+
set :ssh_options, options
|
49
|
+
|
50
|
+
# Disable sudo
|
51
|
+
# set :disable_sudo, true
|
52
|
+
|
53
|
+
# Set environment variables
|
54
|
+
# set :env, :LANG => 'C', :LC_MESSAGES => 'C'
|
55
|
+
|
56
|
+
# Set PATH
|
57
|
+
# set :path, '/sbin:/usr/local/sbin:$PATH'
|
58
|
+
else
|
59
|
+
#
|
60
|
+
# OS type: Windows
|
61
|
+
#
|
62
|
+
set :backend, :winrm
|
63
|
+
set :os, :family => 'windows'
|
64
|
+
|
65
|
+
user = ENV['TARGET_USER']
|
66
|
+
port = ENV['TARGET_PORT']
|
67
|
+
pass = ENV['TARGET_PASSWORD']
|
68
|
+
|
69
|
+
if user.nil?
|
70
|
+
begin
|
71
|
+
require 'highline/import'
|
72
|
+
rescue LoadError
|
73
|
+
fail "highline is not available. Try installing it."
|
74
|
+
end
|
75
|
+
user = ask("\nEnter #{host}'s login user: ") { |q| q.echo = true }
|
76
|
+
end
|
77
|
+
if pass.nil?
|
78
|
+
begin
|
79
|
+
require 'highline/import'
|
80
|
+
rescue LoadError
|
81
|
+
fail "highline is not available. Try installing it."
|
82
|
+
end
|
83
|
+
pass = ask("\nEnter #{user}@#{host}'s login password: ") { |q| q.echo = false }
|
84
|
+
end
|
85
|
+
|
86
|
+
endpoint = "http://#{host}:#{port}/wsman"
|
87
|
+
|
88
|
+
winrm = ::WinRM::WinRMWebService.new(endpoint, :ssl, :user => user, :pass => pass, :basic_auth_only => true)
|
89
|
+
winrm.set_timeout 300 # 5 minutes max timeout for any operation
|
90
|
+
Specinfra.configuration.winrm = winrm
|
91
|
+
|
92
|
+
end
|