Skip to content

Commit

Permalink
Tweak new functionality, augment tests
Browse files Browse the repository at this point in the history
  • Loading branch information
pgeraghty committed Sep 24, 2019
1 parent 7b1dce0 commit ec4d4ae
Show file tree
Hide file tree
Showing 15 changed files with 240 additions and 118 deletions.
6 changes: 3 additions & 3 deletions .github/workflows/test_via_docker.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,10 @@ jobs:
env:
RUBY_VERSION: 2.6.4
ANSIBLE_VERSION: ${{ matrix.ansible }}
COVERALLS_REPO_TOKEN: ${{ secrets.COVERALLS_REPO_TOKEN }}
run: >
docker run -e COVERALLS_REPO_TOKEN --rm -v $PWD:/app pgeraghty/ansible-ruby:$RUBY_VERSION-$ANSIBLE_VERSION
/bin/sh -c "cd /app && bundle install --jobs=3 --retry=3 && COVERALLS_RUN_LOCALLY=true bundle exec rake"
docker run --rm -v $PWD:/app pgeraghty/ansible-ruby:$RUBY_VERSION-$ANSIBLE_VERSION
/bin/sh -c "cd /app && bundle install --jobs=3 --retry=3 &&
COVERALLS_REPO_TOKEN=${{ secrets.COVERALLS_REPO_TOKEN }} COVERALLS_RUN_LOCALLY=true bundle exec rake"
- name: Test against Ruby 2.5.6
env:
Expand Down
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,15 +43,15 @@ Ansible::AdHoc.run 'all -m shell -a "echo Test" -i localhost,'
### Playbooks

```ruby
Ansible::Playbook.run '-i localhost, spec/mock_playbook.yml'
Ansible::Playbook.run '-i localhost, spec/fixtures/mock_playbook.yml'
```

```ruby
Ansible::Playbook.stream('-i localhost, spec/mock_playbook.yml') # defaults to standard output
Ansible::Playbook.stream('-i localhost, spec/fixtures/mock_playbook.yml') # defaults to standard output
```

```ruby
Ansible::Playbook.stream('-i localhost, spec/mock_playbook.yml') { |line_of_output| puts line_of_output }
Ansible::Playbook.stream('-i localhost, spec/fixtures/mock_playbook.yml') { |line_of_output| puts line_of_output }
```

### Shortcuts
Expand All @@ -69,7 +69,7 @@ A['all -i localhost, --list-hosts'] # alias for Ansible::AdHoc.run
```

```ruby
A << '-i localhost, spec/mock_playbook.yml' # alias for Ansible::Playbook.stream
A << '-i localhost, spec/fixtures/mock_playbook.yml' # alias for Ansible::Playbook.stream
```

## Coming Soon
Expand Down
2 changes: 2 additions & 0 deletions ansible-wrapper.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ Gem::Specification.new do |spec|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
spec.require_paths = ['lib']

spec.add_dependency 'json'

spec.add_development_dependency 'bundler','~> 1.10'
spec.add_development_dependency 'rake', '~> 10.0'
spec.add_development_dependency 'rspec'
Expand Down
2 changes: 1 addition & 1 deletion bin/console
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#!/usr/bin/env ruby

require 'bundler/setup'
require 'ansible'
require 'ansible-wrapper'

# You can add fixtures and/or initialization code here to make experimenting
# with your gem easier. You can also use a different console, if you like.
Expand Down
2 changes: 1 addition & 1 deletion examples/streaming/run.rb
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@
#content_type 'text/plain'
stream do |out|
out << CONSOLE_OUTPUT_START
Ansible.stream ['-i', 'localhost,', File.expand_path('../../../spec/mock_playbook.yml', __FILE__)]*' ' do |line|
Ansible.stream ['-i', 'localhost,', File.expand_path('../../../spec/fixtures/mock_playbook.yml', __FILE__)]*' ' do |line|
Ansible::Output.to_html line, out
end
out << CONSOLE_OUTPUT_END
Expand Down
1 change: 0 additions & 1 deletion lib/ansible-wrapper.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
require 'ansible/version'
require 'ansible/config'
require 'ansible/ad_hoc'
require 'ansible/playbook'
require 'ansible/output'
Expand Down
46 changes: 26 additions & 20 deletions lib/ansible/ad_hoc.rb
Original file line number Diff line number Diff line change
@@ -1,30 +1,36 @@
module Ansible::Methods
BIN = 'ansible'
require 'ansible/config'
require 'json'

def one_off cmd
`#{config.to_s "#{BIN} #{cmd}"}`
end
alias :[] :one_off
module Ansible
module Methods
BIN = 'ansible'

# this solution should work for Ansible pre-2.0 as well as 2.0+
def list_hosts cmd
one_off("#{cmd} --list-hosts").scan(IP_OR_HOSTNAME).map { |ip| ip.first.strip }
end
def one_off cmd
`#{config.to_s "#{BIN} #{cmd}"}`
end
alias :[] :one_off

def parse_host_vars(host, inv_file, filter = 'hostvars[inventory_hostname]')
hostvars_filter = filter
cmd = "all -m debug -a 'var=#{hostvars_filter}' -i #{inv_file} -l #{host} --one-line"
hostvars = JSON.parse(self[cmd].split(/>>|=>/).last)
def list_hosts cmd
output = one_off("#{cmd} --list-hosts").gsub!(/\s+hosts.*:\n/, '').strip
output.split("\n").map(&:strip)
end

if Gem::Version.new(Ansible::Config::VERSION) >= Gem::Version.new('2.0')
hostvars[hostvars_filter]
else
hostvars['var'][hostvars_filter]
def parse_host_vars(host, inv_file, filter = 'hostvars[inventory_hostname]')
cmd = "all -m debug -a 'var=#{filter}' -i #{inv_file} -l #{host}"
json = self[cmd].split(/>>|=>/).last

# remove any colour added to console output
# TODO move to Output module as #bleach, perhaps use term-ansicolor
# possibly replace regexp with /\e\[(?:(?:[349]|10)[0-7]|[0-9]|[34]8;5;\d{1,3})?m/
# possibly use ANSIBLE_NOCOLOR? or --nocolor
json = json.strip.chomp.gsub(/\e\[[0-1][;]?(3[0-7]|90|1)?m/, '')

hostvars = JSON.parse(json)

hostvars[filter]
end
end
end

module Ansible
module AdHoc
include Ansible::Config
include Ansible::Methods
Expand Down
92 changes: 47 additions & 45 deletions lib/ansible/config.rb
Original file line number Diff line number Diff line change
@@ -1,53 +1,55 @@
module Ansible::Config
PATH = 'lib/ansible/'
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]))/
SKIP_HOSTVARS = %w(ansible_version inventory_dir inventory_file inventory_hostname inventory_hostname_short group_names groups omit playbook_dir)
VERSION = `ansible --version`.split("\n").first.split.last rescue nil # nil when Ansible not installed

DefaultConfig = Struct.new(:env, :extra_vars, :params) do
def initialize
self.env = {
'ANSIBLE_FORCE_COLOR' => 'True',
'ANSIBLE_HOST_KEY_CHECKING' => 'False'
}

self.params = {
debug: false
}

# options
self.extra_vars = {
# skip creation of .retry files
'retry_files_enabled' => 'False'
}
# TODO support --ssh-common-args, --ssh-extra-args
# e.g. ansible-playbook --ssh-common-args="-o ServerAliveInterval=60" -i inventory install.yml
module Ansible
module Config
PATH = 'lib/ansible/'
# 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/
SKIP_HOSTVARS = %w(ansible_version inventory_dir inventory_file inventory_hostname inventory_hostname_short group_names groups omit playbook_dir)
VERSION = `ansible --version`.split("\n").first.split.last rescue nil # nil when Ansible not installed

DefaultConfig = Struct.new(:env, :extra_vars, :params) do
def initialize
self.env = {
'ANSIBLE_FORCE_COLOR' => 'True',
'ANSIBLE_HOST_KEY_CHECKING' => 'False'
}

self.params = {
debug: false
}

# options
self.extra_vars = {
# skip creation of .retry files
'retry_files_enabled' => 'False'
}
# TODO support --ssh-common-args, --ssh-extra-args
# e.g. ansible-playbook --ssh-common-args="-o ServerAliveInterval=60" -i inventory install.yml
end

# NB: --extra-vars can also accept JSON string, see http://stackoverflow.com/questions/25617273/pass-array-in-extra-vars-ansible
def options
x = extra_vars.each_with_object('--extra-vars=\'') { |kv, a| a << "#{kv.first}=\"#{kv.last}\" " }.strip+'\'' if extra_vars unless extra_vars.empty?
# can test with configure { |config| config.extra_vars.clear }

[x, '--ssh-extra-args=\'-o UserKnownHostsFile=/dev/null\'']*' '
end

def to_s(cmd)
entire_cmd = [env.each_with_object([]) { |kv, a| a << kv*'=' } * ' ', cmd, options]*' '
puts entire_cmd if params[:debug]
entire_cmd
end
end

# NB: --extra-vars can also accept JSON string, see http://stackoverflow.com/questions/25617273/pass-array-in-extra-vars-ansible
def options
x = extra_vars.each_with_object('--extra-vars=\'') { |kv, a| a << "#{kv.first}=\"#{kv.last}\" " }.strip+'\'' if extra_vars unless extra_vars.empty?
# can test with configure { |config| config.extra_vars.clear }
def configure
@config ||= DefaultConfig.new
yield(@config) if block_given?

[x, '--ssh-extra-args=\'-o UserKnownHostsFile=/dev/null\'']*' '
# allow chaining if block given
block_given? ? self : @config
end

def to_s(cmd)
entire_cmd = [env.each_with_object([]) { |kv, a| a << kv*'=' } * ' ', cmd, options]*' '
puts entire_cmd if params[:debug]
entire_cmd
def config
@config || configure
end
end

def configure
@config ||= DefaultConfig.new
yield(@config) if block_given?

# allow chaining if block given
block_given? ? self : @config
end

def config
@config || configure
end
end
67 changes: 35 additions & 32 deletions lib/ansible/output.rb
Original file line number Diff line number Diff line change
@@ -1,47 +1,50 @@
require 'strscan'
require 'erb'

module Ansible::Output
COLOR = {
'1' => 'font-weight: bold',
'30' => 'color: black',
'31' => 'color: red',
'32' => 'color: green',
'33' => 'color: yellow',
'34' => 'color: blue',
'35' => 'color: magenta',
'36' => 'color: cyan',
'37' => 'color: white',
'90' => 'color: grey'
}
module Ansible
module Output
COLOR = {
'1' => 'font-weight: bold',
'30' => 'color: black',
'31' => 'color: red',
'32' => 'color: green',
'33' => 'color: yellow',
'34' => 'color: blue',
'35' => 'color: magenta',
'36' => 'color: cyan',
'37' => 'color: white',
'90' => 'color: grey'
}

def self.to_html(line, stream='')
s = StringScanner.new(ERB::Util.h line)
while(!s.eos?)
if s.scan(/\e\[([0-1]);(3[0-7]|90|1)m/)
bold, colour = s.captures
styles = []
def self.to_html(line, stream='')
s = StringScanner.new(ERB::Util.h line)
while(!s.eos?)
if s.scan(/\e\[([0-1])?[;]?(3[0-7]|90|1)m/)
bold, colour = s.captures
styles = []

styles << COLOR[bold] if bold.to_i == 1
styles << COLOR[colour]
styles << COLOR[bold] if bold.to_i == 1
styles << COLOR[colour]

span =
if styles.empty?
%{<span>}
else
%{<span style="#{styles*'; '};">}
end
span =
# in case of invalid colours, although this may be impossible
if styles.compact.empty?
%{<span>}
else
%{<span style="#{styles*'; '};">}
end

stream << span
else
if s.scan(/\e\[0m/)
stream << span
elsif s.scan(/\e\[0m/)
stream << %{</span>}
elsif s.scan(/\e\[[^0]*m/)
stream << '<span>'
else
stream << s.scan(/./m)
end
end
end

stream
stream
end
end
end
1 change: 1 addition & 0 deletions lib/ansible/playbook.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
require 'ansible/config'
require 'ansible/safe_pty'

module Ansible
Expand Down
16 changes: 16 additions & 0 deletions spec/ansible/ad_hoc_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,21 @@ module Ansible
expect(AdHoc.run 'all -i localhost, --list-hosts').to match /localhost/
end
end

describe '.list_hosts' do
it 'can list hosts for an inline inventory' do
inline_inv = %w(localhost 99.99.99.99)
cmd = "all -i #{inline_inv*','}, --list-hosts"

expect(AdHoc.list_hosts cmd).to match inline_inv
end
end

describe '.parse_host_vars' do
it 'can parse and return default host vars' do
host_vars = AdHoc.parse_host_vars 'all', 'localhost,'
expect(host_vars['inventory_hostname']).to match 'localhost'
end
end
end
end
4 changes: 2 additions & 2 deletions spec/ansible/ansible_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
Ansible.enable_shortcuts!
disable_host_key_checking

cmd = '-i localhost, spec/mock_playbook.yml'
cmd = '-i localhost, spec/fixtures/mock_playbook.yml'
expect(A << cmd).to be_a Integer
expect(A['all -i localhost, --list-hosts']).to match /localhost/
end
Expand All @@ -33,7 +33,7 @@ module Nodes
extend self

def install!(ip)
stream ['-i', ip, 'spec/mock_playbook.yml']*' '
stream ['-i', ip, 'spec/fixtures/mock_playbook.yml']*' '
end
end

Expand Down

0 comments on commit ec4d4ae

Please sign in to comment.