Dockerコンテナー内でansible_specを実行したらエラー
はじめに
Docker使ってAnsible開発しつつテストコード書いてたら、
以下エラーが出てハマったので備忘録的に残しておきます。
$ bundle exec rake all
rake aborted!
NoMethodError: private method `select' called for nil:NilClass
/workspace/rakefile:6:in `<top (required)>'
/usr/local/rbenv/versions/2.6.1/bin/bundle:23:in `load'
/usr/local/rbenv/versions/2.6.1/bin/bundle:23:in `<main>'
(See full trace by running task with --trace)
環境
前提としてWindows 10 + Docker for Windowsを使い、コンテナー内でAnsibleを開発。
Ansibleの定義ファイルはWindowsディスク上において、Volume mountでコンテナーにマウント。
Ansibleのディレクトリ構成
$ tree
.
|____.ansiblespec
|____.rspec
|____Gemfile
|____Gemfile.lock
|____group_vars
| |____webserver.yml
|____hosts
|____Rakefile
|____README.md
|____roles
| |____configure_nginx
| | |____spec
| | | |____configure_nginx_spec.rb
| | |____tasks
| | | |____main.yml
| | |____templates
| | | |____nginx.conf.j2
| |____install_nginx
| | |____spec
| | | |____install_nginx_spec.rb
| | |____tasks
| | | |____main.yml
|____site.yml
|____spec
| |____spec_helper.rb
原因
インベントリーファイルである hosts
に実行権限があったのでエラーとなっていた。
WindowsディレクトリをVolume mountすると、権限が 777
になり、
ansible_spec
が動的インベントリーと勘違いしてエラーになる。
$ # 今回のインベントリーファイルは静的インベントリーです。
$ ls -l hosts
-rwxrwxrwx 1 root root 17 Jun 22 10:37 hosts
$ cat hosts
[webserver]
127.0.0.1
対処
Ansibleのファイルを全体的にコンテナー内にコピーし、 hosts
の権限を 644
にする。
全部コピーだと大変なのである程度はシンボリックにした。
mkdir -p /tmp/ansible
cp -p /workspace/hosts /tmp/ansible
chmod 644 /tmp/ansible//hosts
ln -s /workspace/site.yml /tmp/ansible/site.yml
ln -s /workspace/Gemfile /tmp/ansible/Gemfile
ln -s /workspace/Gemfile.lock /tmp/ansible/Gemfile.lock
ln -s /workspace/Rakefile /tmp/ansible/Rakefile
ln -s /workspace/group_vars /tmp/ansible/group_vars
ln -s /workspace/roles /tmp/ansible/roles
ln -s /workspace/spec /tmp/ansible/spec
ln -s /workspace/.ansiblespec /tmp/ansible/.ansiblespec
ln -s /workspace/.rspec /tmp/ansible/.rspec
cd /tmp/ansible
原因の詳細
スタックトレースを覗いてみる。
$ bundle exec rake all --trace 2>&1 | head
rake aborted!
NoMethodError: private method `select' called for nil:NilClass
/usr/local/rbenv/versions/2.6.1/lib/ruby/gems/2.6.0/gems/ansible_spec-0.3.1/lib/ansible_spec/load_ansible.rb:133:in `get_dynamic_inventory'
/usr/local/rbenv/versions/2.6.1/lib/ruby/gems/2.6.0/gems/ansible_spec-0.3.1/lib/ansible_spec/load_ansible.rb:24:in `load_targets'
/usr/local/rbenv/versions/2.6.1/lib/ruby/gems/2.6.0/gems/ansible_spec-0.3.1/lib/ansible_spec/load_ansible.rb:425:in `get_properties'
/workspace/rakefile:6:in `<top (required)>'
/usr/local/rbenv/versions/2.6.1/lib/ruby/gems/2.6.0/gems/rake-13.0.1/lib/rake/rake_module.rb:29:in `load'
/usr/local/rbenv/versions/2.6.1/lib/ruby/gems/2.6.0/gems/rake-13.0.1/lib/rake/rake_module.rb:29:in `load_rakefile'
/usr/local/rbenv/versions/2.6.1/lib/ruby/gems/2.6.0/gems/rake-13.0.1/lib/rake/application.rb:703:in `raw_load_rakefile'
/usr/local/rbenv/versions/2.6.1/lib/ruby/gems/2.6.0/gems/rake-13.0.1/lib/rake/application.rb:104:in `block in load_rakefile'
上から順番に見ていったのですが、今回関係あったのはこの部分。
/usr/local/rbenv/versions/2.6.1/lib/ruby/gems/2.6.0/gems/ansible_spec-0.3.1/lib/ansible_spec/load_ansible.rb:24:in `load_targets'
該当のソースを覗いてみると23行目でinventoryファイルに実行権限があるかどうかチェックしている。
$ cat -n /usr/local/rbenv/versions/2.6.1/lib/ruby/gems/2.6.0/gems/ansible_spec-0.3.1/lib/ansible_spec/load_ansible.rb | head -n 30
1 # -*- coding: utf-8 -*-
2 require 'hostlist_expression'
3 require 'oj'
4 require 'open3'
5 require 'yaml'
6 require 'inifile'
7 require 'ansible_spec/vendor/hash'
8 if Gem::Version.new(RUBY_VERSION.dup) >= Gem::Version.new('2.1')
9 require 'ansible/vault'
10 end
11
12 module AnsibleSpec
13 # param: inventory file of Ansible
14 # param: return_type 'groups' or 'groups_parent_child_relationships'
15 # return: Hash {"group" => ["192.168.0.1","192.168.0.2"]}
16 # return: Hash {"group" => [{"name" => "192.168.0.1","uri" => "192.168.0.1", "port" => 22},...]}
17 # return: Hash {"pg" => ["server", "databases"]}
18 def self.load_targets(file, return_type = 'groups')
19 if not ['groups', 'groups_parent_child_relationships'].include?(return_type)
20 raise ArgumentError, "Variable return_type must be value 'groups' or 'groups_parent_child_relationships'"
21 end
22
23 if File.executable?(file)
24 return get_dynamic_inventory(file)
25 end
26 f = File.open(file).read
27 groups = Hash.new
28 group = ''
29 hosts = Hash.new
30 hosts.default = Hash.new
これは、動的インベントリーを判定するために行っており、
今回のhosts
ファイルは静的インベントリーなので実行権限を外してあげればいい。
余談
今だったらWSL2上で開発すればいいのですが、仕事で使うWindows端末がまだWSL2に対応したバージョンでなかったのでこんなことしました。
調べてもあんまり出てこなかったので同じような人が助かれば幸いです。
rubyとか全然触ってないのでスキル持ってればハマることはなかったかも。。。