ansible-wrapper 0.2.1 → 0.2.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.
- checksums.yaml +4 -4
- data/README.md +4 -3
- data/ansible-wrapper.gemspec +2 -0
- data/lib/ansible-wrapper.rb +4 -0
- data/lib/ansible/ad_hoc.rb +33 -6
- data/lib/ansible/config.rb +22 -5
- data/lib/ansible/output.rb +80 -38
- data/lib/ansible/playbook.rb +26 -11
- data/lib/ansible/safe_pty.rb +4 -0
- data/lib/ansible/shortcuts.rb +6 -0
- data/lib/ansible/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2e5caf4e2e2b9582a3d4440288160b30e51895cab153c2cb6194fcc38f682144
|
4
|
+
data.tar.gz: fc87385c8a099a7fb357a9d89a2b21c024dffa0534861b4afa9165def73d30cb
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ed22098776f0eb16adcf65707310e85c982d04a050505d0ffcfb8fb701ccce106f665d1bf6710bf25f738c41e650e32ca404f958304bd0c97bcacf858030db6f
|
7
|
+
data.tar.gz: 1d1ae31bbfd9cc8c4f1453bebc24d47a9a2e29496cf27efd924e6c3ec567e97becc489319aee5fe9bfe3752c180199b22574cb737d2c668eb728734bda7ff4c1
|
data/README.md
CHANGED
@@ -4,13 +4,14 @@
|
|
4
4
|
[](https://travis-ci.com/pgeraghty/ansible-wrapper-ruby)
|
5
5
|
[](https://coveralls.io/github/pgeraghty/ansible-wrapper-ruby?branch=master)
|
6
6
|
[](https://codeclimate.com/github/pgeraghty/ansible-wrapper-ruby)
|
7
|
+
[](http://inch-ci.org/github/pgeraghty/ansible-wrapper-ruby)
|
7
8
|
|
8
9
|
#### A lightweight Ruby wrapper around Ansible that allows for ad-hoc commands and playbook execution. The primary purpose is to support easy streaming output.
|
9
10
|
|
10
11
|
## Requirements
|
11
12
|
|
12
13
|
Ensure [Ansible](http://docs.ansible.com/intro_getting_started.html) is installed and available to shell commands i.e. in PATH.
|
13
|
-
[Tested](https://travis-ci.org/pgeraghty/ansible-wrapper-ruby) with Ansible versions
|
14
|
+
[Tested](https://travis-ci.org/pgeraghty/ansible-wrapper-ruby) with Ansible versions 2.0.2 thru 2.8.5 and Ruby 2.1+, but please create an issue if you use a version that fails.
|
14
15
|
|
15
16
|
## Installation
|
16
17
|
|
@@ -72,9 +73,9 @@ A['all -i localhost, --list-hosts'] # alias for Ansible::AdHoc.run
|
|
72
73
|
A << '-i localhost, spec/fixtures/mock_playbook.yml' # alias for Ansible::Playbook.stream
|
73
74
|
```
|
74
75
|
|
75
|
-
##
|
76
|
+
## Examples
|
76
77
|
|
77
|
-
*
|
78
|
+
* For a streaming output example using Sinatra, see the [examples/streaming](examples/streaming) folder.
|
78
79
|
|
79
80
|
## Development
|
80
81
|
|
data/ansible-wrapper.gemspec
CHANGED
@@ -3,6 +3,8 @@ lib = File.expand_path('../lib', __FILE__)
|
|
3
3
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
4
|
require 'ansible/version'
|
5
5
|
|
6
|
+
# TODO set required ruby version??
|
7
|
+
|
6
8
|
Gem::Specification.new do |spec|
|
7
9
|
spec.name = 'ansible-wrapper'
|
8
10
|
spec.version = Ansible::VERSION
|
data/lib/ansible-wrapper.rb
CHANGED
@@ -3,6 +3,8 @@ require 'ansible/ad_hoc'
|
|
3
3
|
require 'ansible/playbook'
|
4
4
|
require 'ansible/output'
|
5
5
|
|
6
|
+
# A lightweight Ruby wrapper around Ansible that allows for ad-hoc commands and playbook execution.
|
7
|
+
# The primary purpose is to support easy streaming output.
|
6
8
|
module Ansible
|
7
9
|
include Ansible::Config
|
8
10
|
include Ansible::Methods
|
@@ -10,6 +12,8 @@ module Ansible
|
|
10
12
|
|
11
13
|
extend self
|
12
14
|
|
15
|
+
# Enables shortcuts
|
16
|
+
# @see ansible/shortcuts.rb
|
13
17
|
def enable_shortcuts!
|
14
18
|
require 'ansible/shortcuts'
|
15
19
|
end
|
data/lib/ansible/ad_hoc.rb
CHANGED
@@ -2,21 +2,41 @@ require 'ansible/config'
|
|
2
2
|
require 'json'
|
3
3
|
|
4
4
|
module Ansible
|
5
|
+
# Ansible Ad-Hoc methods
|
5
6
|
module Methods
|
7
|
+
# executable that runs Ansible Ad-Hoc commands
|
6
8
|
BIN = 'ansible'
|
7
9
|
|
8
|
-
|
10
|
+
# Run an Ad-Hoc Ansible command
|
11
|
+
# @param cmd [String] the Ansible command to execute
|
12
|
+
# @return [String] the output
|
13
|
+
# @example Run a simple shell command with an inline inventory that only contains localhost
|
14
|
+
# one_off 'all -c local -a "echo hello"'
|
15
|
+
def one_off(cmd)
|
16
|
+
# TODO if debug then puts w/ colour
|
9
17
|
`#{config.to_s "#{BIN} #{cmd}"}`
|
10
18
|
end
|
11
19
|
alias :[] :one_off
|
12
20
|
|
13
|
-
|
21
|
+
# Ask Ansible to list hosts
|
22
|
+
# @param cmd [String] the Ansible command to execute
|
23
|
+
# @return [String] the output
|
24
|
+
# @example List hosts with an inline inventory that only contains localhost
|
25
|
+
# list_hosts 'all -i localhost,'
|
26
|
+
def list_hosts(cmd)
|
14
27
|
output = one_off("#{cmd} --list-hosts").gsub!(/\s+hosts.*:\n/, '').strip
|
15
28
|
output.split("\n").map(&:strip)
|
16
29
|
end
|
17
30
|
|
18
|
-
|
19
|
-
|
31
|
+
# Fetches host variables via Ansible's debug module
|
32
|
+
# @param host [String] the +<host-pattern>+ for target host(s)
|
33
|
+
# @param inv [String] the inventory host path or comma-separated host list
|
34
|
+
# @param filter [String] the variable filter
|
35
|
+
# @return [Hash] the variables pertaining to the host
|
36
|
+
# @example List variables for localhost
|
37
|
+
# parse_host_vars 'localhost', 'localhost,'
|
38
|
+
def parse_host_vars(host, inv, filter = 'hostvars[inventory_hostname]')
|
39
|
+
cmd = "all -m debug -a 'var=#{filter}' -i #{inv} -l #{host}"
|
20
40
|
json = self[cmd].split(/>>|=>/).last
|
21
41
|
|
22
42
|
# remove any colour added to console output
|
@@ -31,11 +51,18 @@ module Ansible
|
|
31
51
|
end
|
32
52
|
end
|
33
53
|
|
54
|
+
# Provides static access to Ad-Hoc methods
|
34
55
|
module AdHoc
|
35
|
-
include
|
36
|
-
include
|
56
|
+
include Config
|
57
|
+
include Methods
|
37
58
|
|
38
59
|
extend self
|
60
|
+
|
61
|
+
# Run an Ad-Hoc Ansible command
|
62
|
+
# @see Methods#one_off
|
63
|
+
# @param cmd [String] the Ansible command to execute
|
64
|
+
# @return [String] the output
|
65
|
+
# @since 0.2.1
|
39
66
|
alias :run :one_off
|
40
67
|
end
|
41
68
|
end
|
data/lib/ansible/config.rb
CHANGED
@@ -1,31 +1,41 @@
|
|
1
1
|
module Ansible
|
2
|
+
# Ansible configuration
|
2
3
|
module Config
|
3
4
|
PATH = 'lib/ansible/'
|
4
5
|
# IP_OR_HOSTNAME = /((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3})$|^((([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9]))\n/
|
5
6
|
SKIP_HOSTVARS = %w(ansible_version inventory_dir inventory_file inventory_hostname inventory_hostname_short group_names groups omit playbook_dir)
|
6
7
|
VERSION = `ansible --version`.split("\n").first.split.last rescue nil # nil when Ansible not installed
|
7
8
|
|
9
|
+
# Default configuration options
|
8
10
|
DefaultConfig = Struct.new(:env, :extra_vars, :params) do
|
11
|
+
# @!attribute env
|
12
|
+
# @return [Hash] environment variables
|
13
|
+
# @!attribute params
|
14
|
+
# @return [Hash] parameters
|
15
|
+
# @!attribute extra_vars
|
16
|
+
# @return [Hash] extra variables to pass to Ansible
|
17
|
+
|
9
18
|
def initialize
|
10
19
|
self.env = {
|
11
20
|
'ANSIBLE_FORCE_COLOR' => 'True',
|
12
21
|
'ANSIBLE_HOST_KEY_CHECKING' => 'False'
|
13
22
|
}
|
14
23
|
|
15
|
-
self.params = {
|
16
|
-
debug: false
|
17
|
-
}
|
18
|
-
|
19
|
-
# options
|
20
24
|
self.extra_vars = {
|
21
25
|
# skip creation of .retry files
|
22
26
|
'retry_files_enabled' => 'False'
|
23
27
|
}
|
24
28
|
# TODO support --ssh-common-args, --ssh-extra-args
|
25
29
|
# e.g. ansible-playbook --ssh-common-args="-o ServerAliveInterval=60" -i inventory install.yml
|
30
|
+
|
31
|
+
self.params = {
|
32
|
+
debug: false
|
33
|
+
}
|
26
34
|
end
|
27
35
|
|
36
|
+
# Pass additional options to Ansible
|
28
37
|
# NB: --extra-vars can also accept JSON string, see http://stackoverflow.com/questions/25617273/pass-array-in-extra-vars-ansible
|
38
|
+
# @return [String] command-line options
|
29
39
|
def options
|
30
40
|
x = extra_vars.each_with_object('--extra-vars=\'') { |kv, a| a << "#{kv.first}=\"#{kv.last}\" " }.strip+'\'' if extra_vars unless extra_vars.empty?
|
31
41
|
# can test with configure { |config| config.extra_vars.clear }
|
@@ -33,6 +43,9 @@ module Ansible
|
|
33
43
|
[x, '--ssh-extra-args=\'-o UserKnownHostsFile=/dev/null\'']*' '
|
34
44
|
end
|
35
45
|
|
46
|
+
# Output configuration as a string for the command-line
|
47
|
+
# @param cmd [String] command to be appended to the command-line produced
|
48
|
+
# @return [Config, DefaultConfig] the configuration
|
36
49
|
def to_s(cmd)
|
37
50
|
entire_cmd = [env.each_with_object([]) { |kv, a| a << kv*'=' } * ' ', cmd, options]*' '
|
38
51
|
puts entire_cmd if params[:debug]
|
@@ -40,6 +53,8 @@ module Ansible
|
|
40
53
|
end
|
41
54
|
end
|
42
55
|
|
56
|
+
# Create and yield configuration
|
57
|
+
# @return [Config, DefaultConfig] the configuration
|
43
58
|
def configure
|
44
59
|
@config ||= DefaultConfig.new
|
45
60
|
yield(@config) if block_given?
|
@@ -48,6 +63,8 @@ module Ansible
|
|
48
63
|
block_given? ? self : @config
|
49
64
|
end
|
50
65
|
|
66
|
+
# accessor for config
|
67
|
+
# @return [DefaultConfig] the configuration
|
51
68
|
def config
|
52
69
|
@config || configure
|
53
70
|
end
|
data/lib/ansible/output.rb
CHANGED
@@ -2,49 +2,91 @@ require 'strscan'
|
|
2
2
|
require 'erb'
|
3
3
|
|
4
4
|
module Ansible
|
5
|
+
# Output module provides formatting of Ansible output
|
5
6
|
module Output
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
7
|
+
# Generate HTML for an output string formatted with ANSI escape sequences representing colours and styling
|
8
|
+
# @param ansi [String] an output string formatted with escape sequences to represent formatting
|
9
|
+
# @param stream [String] a stream or string (that supports +<<+) to which generated HTML will be appended
|
10
|
+
# @return the stream provided or a new String
|
11
|
+
# @example List hosts with an inline inventory that only contains localhost
|
12
|
+
# to_html "\e[90mGrey\e[0m" => '<span style="color: grey;">Grey</span>'
|
13
|
+
def self.to_html(ansi, stream='')
|
14
|
+
Ansi2Html.new(ansi).to_html stream
|
15
|
+
end
|
16
|
+
|
17
|
+
# Converter for strings containing with ANSI escape sequences
|
18
|
+
class Ansi2Html
|
19
|
+
# Hash of colors to convert shell colours to CSS
|
20
|
+
COLOR = {
|
21
|
+
'1' => 'font-weight: bold',
|
22
|
+
'30' => 'color: black',
|
23
|
+
'31' => 'color: red',
|
24
|
+
'32' => 'color: green',
|
25
|
+
'33' => 'color: yellow',
|
26
|
+
'34' => 'color: blue',
|
27
|
+
'35' => 'color: magenta',
|
28
|
+
'36' => 'color: cyan',
|
29
|
+
'37' => 'color: white',
|
30
|
+
'90' => 'color: grey'
|
31
|
+
}
|
32
|
+
|
33
|
+
SUPPORTED_STYLE_PATTERN = /\e\[([0-1])?[;]?(3[0-7]|90|1)m/
|
34
|
+
END_ESCAPE_SEQUENCE_PATTERN = /\e\[0m/
|
35
|
+
UNSUPPORTED_STYLE_PATTERN = /\e\[[^0]*m/
|
36
|
+
IGNORED_OUTPUT = /./m
|
37
|
+
|
38
|
+
OPEN_SPAN_TAG = %{<span>}
|
39
|
+
CLOSE_SPAN_TAG = %{</span>}
|
40
|
+
|
41
|
+
# Create StringScanner for string
|
42
|
+
# @param line [String] a stream or string (that supports +<<+) to which generated HTML will be appended
|
43
|
+
def initialize(line)
|
44
|
+
# ensure any HTML tag characters are escaped
|
45
|
+
@strscan = StringScanner.new(ERB::Util.h line)
|
46
|
+
end
|
47
|
+
|
48
|
+
# Generate HTML from string formatted with ANSI escape sequences
|
49
|
+
# @return [String, IO] the HTML
|
50
|
+
def to_html(stream)
|
51
|
+
until @strscan.eos?
|
52
|
+
stream << generate_html
|
53
|
+
end
|
54
|
+
|
55
|
+
stream
|
56
|
+
end
|
57
|
+
|
58
|
+
|
59
|
+
private
|
60
|
+
|
61
|
+
# Scan string and generate HTML
|
62
|
+
def generate_html
|
63
|
+
if @strscan.scan SUPPORTED_STYLE_PATTERN
|
64
|
+
open_tag
|
65
|
+
elsif @strscan.scan END_ESCAPE_SEQUENCE_PATTERN
|
66
|
+
CLOSE_SPAN_TAG
|
67
|
+
elsif @strscan.scan UNSUPPORTED_STYLE_PATTERN
|
68
|
+
OPEN_SPAN_TAG
|
42
69
|
else
|
43
|
-
|
70
|
+
@strscan.scan IGNORED_OUTPUT
|
44
71
|
end
|
45
72
|
end
|
46
73
|
|
47
|
-
|
74
|
+
# Generate opening HTML tag, which may contain a style attribute
|
75
|
+
# @return [String] opening tag
|
76
|
+
def open_tag
|
77
|
+
bold, colour = @strscan[1], @strscan[2]
|
78
|
+
styles = []
|
79
|
+
|
80
|
+
styles << COLOR[bold] if bold.to_i == 1
|
81
|
+
styles << COLOR[colour]
|
82
|
+
|
83
|
+
# in case of invalid colours, although this may be impossible
|
84
|
+
if styles.compact.empty?
|
85
|
+
OPEN_SPAN_TAG
|
86
|
+
else
|
87
|
+
%{<span style="#{styles*'; '};">}
|
88
|
+
end
|
89
|
+
end
|
48
90
|
end
|
49
91
|
end
|
50
92
|
end
|
data/lib/ansible/playbook.rb
CHANGED
@@ -2,38 +2,53 @@ require 'ansible/config'
|
|
2
2
|
require 'ansible/safe_pty'
|
3
3
|
|
4
4
|
module Ansible
|
5
|
+
# Ansible Playbook methods
|
5
6
|
module PlaybookMethods
|
7
|
+
# executable that runs Ansible Playbooks
|
6
8
|
BIN = 'ansible-playbook'
|
7
9
|
|
8
|
-
|
10
|
+
# Run playbook, returning output
|
11
|
+
# @param pb [String] path to playbook
|
12
|
+
# @return [String] output
|
13
|
+
def playbook(pb)
|
14
|
+
# TODO if debug then puts w/ colour
|
9
15
|
`#{config.to_s "#{BIN} #{pb}"}`
|
10
16
|
end
|
11
17
|
alias :<< :playbook
|
12
18
|
|
13
|
-
|
14
|
-
|
15
|
-
|
19
|
+
# Stream execution of a playbook using PTY because otherwise output is buffered
|
20
|
+
# @param pb [String] path to playbook
|
21
|
+
# @return [Integer] exit status
|
22
|
+
def stream(pb)
|
23
|
+
cmd = config.to_s("#{BIN} #{pb}")
|
24
|
+
|
25
|
+
SafePty.spawn cmd do |r,_,_| # add -vvvv here for verbose
|
16
26
|
until r.eof? do
|
17
27
|
line = r.gets
|
18
28
|
block_given? ? yield(line) : puts(line)
|
19
29
|
|
20
|
-
|
21
|
-
|
22
|
-
|
30
|
+
case line
|
31
|
+
when /fatal: \[/ then raise Playbook::Exception.new("FAILED: #{line}")
|
32
|
+
when /ERROR!/,/FAILED!/ then raise Playbook::Exception.new("ERROR: #{line}")
|
33
|
+
end
|
23
34
|
end
|
24
35
|
end
|
25
36
|
end
|
26
37
|
end
|
27
|
-
end
|
28
38
|
|
29
|
-
|
39
|
+
# Provides static access to Playbook methods
|
30
40
|
module Playbook
|
31
|
-
include
|
32
|
-
include
|
41
|
+
include Config
|
42
|
+
include PlaybookMethods
|
33
43
|
|
34
44
|
extend self
|
45
|
+
|
46
|
+
# Run playbook, returning output
|
47
|
+
# @param pb [String] path to playbook
|
48
|
+
# @return [String] output
|
35
49
|
alias :run :playbook
|
36
50
|
|
51
|
+
# Exception to represent Playbook failures
|
37
52
|
class Exception < RuntimeError; end
|
38
53
|
end
|
39
54
|
end
|
data/lib/ansible/safe_pty.rb
CHANGED
data/lib/ansible/shortcuts.rb
CHANGED
@@ -1,10 +1,16 @@
|
|
1
1
|
module Ansible
|
2
2
|
extend self
|
3
3
|
|
4
|
+
# shortcut for executing an Ad-Hoc command
|
5
|
+
# @param cmd [String] the command-line to pass
|
6
|
+
# @see AdHoc#run
|
4
7
|
def [](cmd)
|
5
8
|
AdHoc.run cmd
|
6
9
|
end
|
7
10
|
|
11
|
+
# shortcut to run a Playbook, streaming the output
|
12
|
+
# @param cmd [String] the command-line to pass
|
13
|
+
# @see Playbook#stream
|
8
14
|
def <<(cmd)
|
9
15
|
Playbook.stream cmd
|
10
16
|
end
|
data/lib/ansible/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ansible-wrapper
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Paul Geraghty
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-09-
|
11
|
+
date: 2019-09-26 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: json
|