1 - Serverspec
Serverspec是可以测试基础设施配置的工具,能够验证配置管理工具(Ansible、Puppet、Chef等)的配置结果,可以实现基础设施测试代码化自动化。
- 测试代码即测试设计文档
- 测试代码可以复用
- 可以通过代码对测试用例进行评审
- 使用类似YAML的固定格式编写测试用例
- 可以通过安装gem软件包coderay将测试结果输出为HTML格式
- 可以在安装了Serverspec的本地主机上进行测试,也可以通过ssh远程进行测试
- 提供了配置文件和交互式两种使用方式
官网信息
- HomePage:https://serverspec.org/
- Tutorial:https://serverspec.org/tutorial.html
- GitHub:https://github.com/mizzy/serverspec
- Version:https://rubygems.org/gems/serverspec/versions/
2 - 安装
注意:必须事先安装有ruby,并且版本不能过低,负责安装会失败。
[root@localhost ~]# gem install serverspec
Fetching rspec-support-3.9.0.gem
Fetching diff-lcs-1.3.gem
Fetching sfl-2.3.gem
Fetching multi_json-1.14.1.gem
Fetching rspec-expectations-3.9.0.gem
Fetching specinfra-2.82.4.gem
Fetching net-ssh-5.2.0.gem
Fetching net-scp-2.0.0.gem
Fetching rspec-core-3.9.0.gem
Fetching rspec-its-1.3.0.gem
Fetching rspec-mocks-3.9.0.gem
Fetching rspec-3.9.0.gem
Fetching serverspec-2.41.5.gem
Successfully installed sfl-2.3
Successfully installed net-ssh-5.2.0
Successfully installed net-scp-2.0.0
Successfully installed specinfra-2.82.4
Successfully installed multi_json-1.14.1
Successfully installed diff-lcs-1.3
Successfully installed rspec-support-3.9.0
Successfully installed rspec-expectations-3.9.0
Successfully installed rspec-core-3.9.0
Successfully installed rspec-its-1.3.0
Successfully installed rspec-mocks-3.9.0
Successfully installed rspec-3.9.0
Successfully installed serverspec-2.41.5
Parsing documentation for sfl-2.3
Installing ri documentation for sfl-2.3
Parsing documentation for net-ssh-5.2.0
Installing ri documentation for net-ssh-5.2.0
Parsing documentation for net-scp-2.0.0
Installing ri documentation for net-scp-2.0.0
Parsing documentation for specinfra-2.82.4
Installing ri documentation for specinfra-2.82.4
Parsing documentation for multi_json-1.14.1
Installing ri documentation for multi_json-1.14.1
Parsing documentation for diff-lcs-1.3
Couldn't find file to include 'Contributing.rdoc' from README.rdoc
Couldn't find file to include 'License.rdoc' from README.rdoc
Installing ri documentation for diff-lcs-1.3
Parsing documentation for rspec-support-3.9.0
Installing ri documentation for rspec-support-3.9.0
Parsing documentation for rspec-expectations-3.9.0
Installing ri documentation for rspec-expectations-3.9.0
Parsing documentation for rspec-core-3.9.0
Installing ri documentation for rspec-core-3.9.0
Parsing documentation for rspec-its-1.3.0
Installing ri documentation for rspec-its-1.3.0
Parsing documentation for rspec-mocks-3.9.0
Installing ri documentation for rspec-mocks-3.9.0
Parsing documentation for rspec-3.9.0
Installing ri documentation for rspec-3.9.0
Parsing documentation for serverspec-2.41.5
Installing ri documentation for serverspec-2.41.5
Done installing documentation for sfl, net-ssh, net-scp, specinfra, multi_json, diff-lcs, rspec-support, rspec-expectations, rspec-core, rspec-its, rspec-mocks, rspec, serverspec after 15 seconds
13 gems installed
[root@localhost ~]#
3 - Serverspec设置
在CentOS系统下以本地主机模式运行,测试内容保存在spec/localhost/*_spec.rb
文件中。
参考示例文件sample_spec.rb可以编写指定的测试内容。
[root@localhost ~]# pwd
/root
[root@localhost ~]# mkdir serverspec
[root@localhost ~]# cd serverspec
[root@localhost serverspec]# pwd
/root/serverspec
[root@localhost serverspec]#
[root@localhost serverspec]# serverspec-init
Select OS type:
1) UN*X
2) Windows
Select number: 1
Select a backend type:
1) SSH
2) Exec (local)
Select number: 2
+ spec/
+ spec/localhost/
+ spec/localhost/sample_spec.rb
+ spec/spec_helper.rb
+ Rakefile
+ .rspec
[root@localhost serverspec]# ll
total 4
-rw-rw-r-- 1 root root 685 Nov 20 09:48 Rakefile
drwxrwxr-x 3 root root 45 Nov 20 09:48 spec
[root@localhost serverspec]# tree
.
├── Rakefile
└── spec
├── localhost
│ └── sample_spec.rb
└── spec_helper.rb
2 directories, 3 files
[root@localhost serverspec]#
[root@localhost serverspec]# cat spec/localhost/sample_spec.rb
require 'spec_helper'
describe package('httpd'), :if => os[:family] == 'redhat' do
it { should be_installed }
end
describe package('apache2'), :if => os[:family] == 'ubuntu' do
it { should be_installed }
end
describe service('httpd'), :if => os[:family] == 'redhat' do
it { should be_enabled }
it { should be_running }
end
describe service('apache2'), :if => os[:family] == 'ubuntu' do
it { should be_enabled }
it { should be_running }
end
describe service('org.apache.httpd'), :if => os[:family] == 'darwin' do
it { should be_enabled }
it { should be_running }
end
describe port(80) do
it { should be_listening }
end
[root@localhost serverspec]#
4 - 使用示例
4.1 示例1 - 使用rake命令执行测试
注意:必须在Rakefile文件所在目录执行rake命令
[root@localhost serverspec]# pwd
/root/serverspec
[root@localhost serverspec]# ll
total 4
-rw-rw-r-- 1 root root 685 Nov 20 09:48 Rakefile
drwxrwxr-x 3 root root 45 Nov 20 09:48 spec
[root@localhost serverspec]#
[root@localhost serverspec]# rake spec
/usr/local/rvm/rubies/ruby-2.5.5/bin/ruby -I/usr/local/rvm/gems/ruby-2.5.5/gems/rspec-support-3.9.0/lib:/usr/local/rvm/gems/ruby-2.5.5/gems/rspec-core-3.9.0/lib /usr/local/rvm/gems/ruby-2.5.5/gems/rspec-core-3.9.0/exe/rspec --pattern spec/localhost/\*_spec.rb
Package "httpd"
is expected to be installed
Service "httpd"
is expected to be enabled (FAILED - 1)
is expected to be running (FAILED - 2)
Port "80"
is expected to be listening
Failures:
1) Service "httpd" is expected to be enabled
On host `localhost'
Failure/Error: it { should be_enabled }
expected Service "httpd" to be enabled
/bin/sh -c systemctl\ --quiet\ is-enabled\ httpd
# ./spec/localhost/sample_spec.rb:12:in `block (2 levels) in '
2) Service "httpd" is expected to be running
On host `localhost'
Failure/Error: it { should be_running }
expected Service "httpd" to be running
/bin/sh -c systemctl\ is-active\ httpd
unknown
# ./spec/localhost/sample_spec.rb:13:in `block (2 levels) in '
Finished in 0.14409 seconds (files took 0.65376 seconds to load)
4 examples, 2 failures
Failed examples:
rspec ./spec/localhost/sample_spec.rb:12 # Service "httpd" is expected to be enabled
rspec ./spec/localhost/sample_spec.rb:13 # Service "httpd" is expected to be running
/usr/local/rvm/rubies/ruby-2.5.5/bin/ruby -I/usr/local/rvm/gems/ruby-2.5.5/gems/rspec-support-3.9.0/lib:/usr/local/rvm/gems/ruby-2.5.5/gems/rspec-core-3.9.0/lib /usr/local/rvm/gems/ruby-2.5.5/gems/rspec-core-3.9.0/exe/rspec --pattern spec/localhost/\*_spec.rb failed
[root@localhost serverspec]#
4.2 示例2 - 为基础设施编写测试代码
以Nginx为例。
[root@localhost ansible-playbook-sample]# pwd
/root/zzz/ansible-playbook-sample
[root@localhost ansible-playbook-sample]#
[root@localhost ansible-playbook-sample]# cat site.yml
---
- hosts: webservers
become: yes
connection: local
roles:
- common
- nginx
- serverspec
- serverspec_sample
# - jenkins
[root@localhost ansible-playbook-sample]#
[root@localhost ansible-playbook-sample]# tree roles/serverspec_sample/
roles/serverspec_sample/
├── files
│ └── serverspec_sample
│ ├── Rakefile
│ └── spec
│ ├── localhost
│ └── spec_helper.rb
├── meta
│ └── main.yml
├── tasks
│ └── main.yml
├── templates
│ ├── nginx_spec.rb.j2
│ └── web_spec.rb.j2
└── vars
└── main.yml
8 directories, 7 files
[root@localhost ansible-playbook-sample]#
[root@localhost ansible-playbook-sample]# ansible-playbook -i development site.yml
[DEPRECATION WARNING]: The TRANSFORM_INVALID_GROUP_CHARS settings is set to allow bad characters in group names by default, this will change, but still be user configurable on deprecation. This feature will be removed in version 2.10.
Deprecation warnings can be disabled by setting deprecation_warnings=False in ansible.cfg.
[WARNING]: Invalid characters were found in group names but not replaced, use -vvvv to see details
PLAY [webservers] **************************************************************************************************************************************************************************************************************************
TASK [Gathering Facts] *********************************************************************************************************************************************************************************************************************
ok: [localhost]
TASK [common : install epel] ***************************************************************************************************************************************************************************************************************
ok: [localhost]
TASK [nginx : install nginx] ***************************************************************************************************************************************************************************************************************
ok: [localhost]
TASK [nginx : replace index.html] **********************************************************************************************************************************************************************************************************
ok: [localhost]
TASK [nginx : nginx start] *****************************************************************************************************************************************************************************************************************
ok: [localhost]
TASK [serverspec : install ruby] ***********************************************************************************************************************************************************************************************************
ok: [localhost]
TASK [serverspec : install serverspec] *****************************************************************************************************************************************************************************************************
ok: [localhost] => (item=rake)
ok: [localhost] => (item=serverspec)
TASK [serverspec_sample : distribute serverspec suite] *************************************************************************************************************************************************************************************
changed: [localhost]
TASK [serverspec_sample : distribute spec file] ********************************************************************************************************************************************************************************************
changed: [localhost]
PLAY RECAP *********************************************************************************************************************************************************************************************************************************
localhost : ok=9 changed=2 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
[root@localhost ansible-playbook-sample]#
[root@localhost ansible-playbook-sample]# ll /tmp/serverspec_sample/
total 4
-rw-rw-r-- 1 root root 685 Nov 20 13:54 Rakefile
drwxrwxr-x 3 root root 45 Nov 20 13:54 spec
[root@localhost ansible-playbook-sample]#
[root@localhost ansible-playbook-sample]# tree /tmp/serverspec_sample/
/tmp/serverspec_sample/
├── Rakefile
└── spec
├── localhost
│ └── web_spec.rb
└── spec_helper.rb
2 directories, 3 files
[root@localhost ansible-playbook-sample]# cd /tmp/serverspec_sample/
[root@localhost serverspec_sample]# cat spec/localhost/web_spec.rb
require 'spec_helper'
describe package('nginx') do
it { should be_installed }
end
describe service('nginx') do
it { should be_enabled }
it { should be_running }
end
describe port(80) do
it { should be_listening }
end
describe file('/usr/share/nginx/html/index.html') do
it { should be_file }
it { should exist }
its(:content) { should match /^Hello, development ansible!!$/ }
end
[root@localhost serverspec_sample]#
[root@localhost serverspec_sample]# rake spec
/usr/local/rvm/rubies/ruby-2.5.5/bin/ruby -I/usr/local/rvm/gems/ruby-2.5.5/gems/rspec-support-3.9.0/lib:/usr/local/rvm/gems/ruby-2.5.5/gems/rspec-core-3.9.0/lib /usr/local/rvm/gems/ruby-2.5.5/gems/rspec-core-3.9.0/exe/rspec --pattern spec/localhost/\*_spec.rb
Package "nginx"
is expected to be installed
Service "nginx"
is expected to be enabled
is expected to be running
Port "80"
is expected to be listening
File "/usr/share/nginx/html/index.html"
is expected to be file
is expected to exist
content
is expected to match /^Hello, development ansible!!$/ (FAILED - 1)
Failures:
1) File "/usr/share/nginx/html/index.html" content is expected to match /^Hello, development ansible!!$/
On host `localhost'
Failure/Error: its(:content) { should match /^Hello, development ansible!!$/ }
expected "hello, development ansible\n" to match /^Hello, development ansible!!$/
Diff:
@@ -1,2 +1,2 @@
-/^Hello, development ansible!!$/
+hello, development ansible
/bin/sh -c cat\ /usr/share/nginx/html/index.html\ 2\>\ /dev/null\ \|\|\ echo\ -n
hello, development ansible
# ./spec/localhost/web_spec.rb:19:in `block (2 levels) in '
Finished in 0.23396 seconds (files took 0.64631 seconds to load)
7 examples, 1 failure
Failed examples:
rspec ./spec/localhost/web_spec.rb:19 # File "/usr/share/nginx/html/index.html" content is expected to match /^Hello, development ansible!!$/
/usr/local/rvm/rubies/ruby-2.5.5/bin/ruby -I/usr/local/rvm/gems/ruby-2.5.5/gems/rspec-support-3.9.0/lib:/usr/local/rvm/gems/ruby-2.5.5/gems/rspec-core-3.9.0/lib /usr/local/rvm/gems/ruby-2.5.5/gems/rspec-core-3.9.0/exe/rspec --pattern spec/localhost/\*_spec.rb failed
[root@localhost serverspec_sample]#
发现有一个失败的测试用例,根据报错信息分析并更改,然后重新执行。
[root@localhost templates]# pwd
/root/zzz/ansible-playbook-sample/roles/nginx/templates
[root@localhost templates]#
[root@localhost templates]# ll
total 4
-rw-r--r-- 1 root root 25 Nov 19 17:25 index.html.j2
[root@localhost templates]# cat index.html.j2
hello, {{ env }} ansible
[root@localhost templates]#
[root@localhost templates]# vim index.html.j2
[root@localhost templates]# cat index.html.j2
Hello, {{ env }} ansible!!
[root@localhost templates]#
[root@localhost serverspec_sample]# cd ~/zzz/ansible-playbook-sample/
[root@localhost ansible-playbook-sample]# ansible-playbook -i development site.yml
[DEPRECATION WARNING]: The TRANSFORM_INVALID_GROUP_CHARS settings is set to allow bad characters in group names by default, this will change, but still be user configurable on deprecation. This feature will be removed in version 2.10.
Deprecation warnings can be disabled by setting deprecation_warnings=False in ansible.cfg.
[WARNING]: Invalid characters were found in group names but not replaced, use -vvvv to see details
PLAY [webservers] **************************************************************************************************************************************************************************************************************************
TASK [Gathering Facts] *********************************************************************************************************************************************************************************************************************
ok: [localhost]
TASK [common : install epel] ***************************************************************************************************************************************************************************************************************
ok: [localhost]
TASK [nginx : install nginx] ***************************************************************************************************************************************************************************************************************
ok: [localhost]
TASK [nginx : replace index.html] **********************************************************************************************************************************************************************************************************
changed: [localhost]
TASK [nginx : nginx start] *****************************************************************************************************************************************************************************************************************
ok: [localhost]
TASK [serverspec : install ruby] ***********************************************************************************************************************************************************************************************************
ok: [localhost]
TASK [serverspec : install serverspec] *****************************************************************************************************************************************************************************************************
ok: [localhost] => (item=rake)
ok: [localhost] => (item=serverspec)
TASK [serverspec_sample : distribute serverspec suite] *************************************************************************************************************************************************************************************
ok: [localhost]
TASK [serverspec_sample : distribute spec file] ********************************************************************************************************************************************************************************************
ok: [localhost]
PLAY RECAP *********************************************************************************************************************************************************************************************************************************
localhost : ok=9 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
[root@localhost ansible-playbook-sample]#
[root@localhost ansible-playbook-sample]# cd /tmp/serverspec_sample/
[root@localhost serverspec_sample]# rake spec
/usr/local/rvm/rubies/ruby-2.5.5/bin/ruby -I/usr/local/rvm/gems/ruby-2.5.5/gems/rspec-support-3.9.0/lib:/usr/local/rvm/gems/ruby-2.5.5/gems/rspec-core-3.9.0/lib /usr/local/rvm/gems/ruby-2.5.5/gems/rspec-core-3.9.0/exe/rspec --pattern spec/localhost/\*_spec.rb
Package "nginx"
is expected to be installed
Service "nginx"
is expected to be enabled
is expected to be running
Port "80"
is expected to be listening
File "/usr/share/nginx/html/index.html"
is expected to be file
is expected to exist
content
is expected to match /^Hello, development ansible!!$/
Finished in 0.19861 seconds (files took 0.61345 seconds to load)
7 examples, 0 failures
[root@localhost serverspec_sample]#
5 - 问题处理
通过gem安装serverspec时,ruby版本过低导致安装失败,版本必须大于等于2.2.6。
# gem install serverspec
Fetching: rspec-support-3.9.0.gem (100%)
Successfully installed rspec-support-3.9.0
......
......
......
ERROR: Error installing serverspec:
net-ssh requires Ruby version >= 2.2.6.
#
# ruby -v
ruby 2.0.0p648 (2015-12-16) [x86_64-linux]
#
处理方法
通过yum方式在线安装ruby,其版本仍然为2.0.0。
只能通过更换ruby仓库和使用RAM的方式来完成版本升级。
参考信息:https://www.jianshu.com/p/7a625eb8cde0
[root@localhost ~]# gem sources -a http://mirrors.aliyun.com/rubygems/
http://mirrors.aliyun.com/rubygems/ added to sources
[root@localhost ~]#
[root@localhost ~]# gpg --keyserver hkp://keys.gnupg.net --recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3 7D2BAF1CF37B13E2069D6956105BD0E739499BDB
gpg: requesting key D39DC0E3 from hkp server keys.gnupg.net
gpg: requesting key 39499BDB from hkp server keys.gnupg.net
gpg: /root/.gnupg/trustdb.gpg: trustdb created
gpg: key D39DC0E3: public key "Michal Papis (RVM signing) " imported
gpg: key 39499BDB: public key "Piotr Kuczynski " imported
gpg: no ultimately trusted keys found
gpg: Total number processed: 2
gpg: imported: 2 (RSA: 2)
[root@localhost ~]#
[root@localhost ~]# curl -sSL https://get.rvm.io | bash -s stable
Downloading https://github.com/rvm/rvm/archive/1.29.9.tar.gz
Downloading https://github.com/rvm/rvm/releases/download/1.29.9/1.29.9.tar.gz.asc
gpg: Signature made Wed 10 Jul 2019 04:31:02 PM CST using RSA key ID 39499BDB
gpg: Good signature from "Piotr Kuczynski "
gpg: WARNING: This key is not certified with a trusted signature!
gpg: There is no indication that the signature belongs to the owner.
Primary key fingerprint: 7D2B AF1C F37B 13E2 069D 6956 105B D0E7 3949 9BDB
GPG verified '/usr/local/rvm/archives/rvm-1.29.9.tgz'
Creating group 'rvm'
Installing RVM to /usr/local/rvm/
Installation of RVM in /usr/local/rvm/ is almost complete:
* First you need to add all users that will be using rvm to 'rvm' group,
and logout - login again, anyone using rvm will be operating with `umask u=rwx,g=rwx,o=rx`.
* To start using RVM you need to run `source /etc/profile.d/rvm.sh`
in all your open shell windows, in rare cases you need to reopen all shell windows.
* Please do NOT forget to add your users to the rvm group.
The installer no longer auto-adds root or users to the rvm group. Admins must do this.
Also, please note that group memberships are ONLY evaluated at login time.
This means that users must log out then back in before group membership takes effect!
Thanks for installing RVM
Please consider donating to our open collective to help us maintain RVM.
Donate: https://opencollective.com/rvm/donate
[root@localhost ~]#
[root@localhost ~]# source /etc/profile.d/rvm.sh
[root@localhost ~]#
[root@localhost ~]# rvm -v
rvm 1.29.9 (latest) by Michal Papis, Piotr Kuczynski, Wayne E. Seguin [https://rvm.io]
[root@localhost ~]#
[root@localhost ~]# rvm install 2.5
Searching for binary rubies, this might take some time.
No binary rubies available for: centos/7/x86_64/ruby-2.5.5.
Continuing with compilation. Please read 'rvm help mount' to get more information on binary rubies.
Checking requirements for centos.
Installing requirements for centos.
Installing required packages: patch, autoconf, automake, bison, gcc-c++, libffi-devel, libtool, patch, readline-devel, sqlite-devel, zlib-devel, openssl-devel...............................
Requirements installation successful.
Installing Ruby from source to: /usr/local/rvm/rubies/ruby-2.5.5, this may take a while depending on your cpu(s)...
ruby-2.5.5 - #downloading ruby-2.5.5, this may take a while depending on your connection...
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 13.5M 100 13.5M 0 0 383k 0 0:00:36 0:00:36 --:--:-- 356k
ruby-2.5.5 - #extracting ruby-2.5.5 to /usr/local/rvm/src/ruby-2.5.5.....
ruby-2.5.5 - #configuring...................................................................
ruby-2.5.5 - #post-configuration..
ruby-2.5.5 - #compiling....................................................................................
ruby-2.5.5 - #installing..............................
ruby-2.5.5 - #making binaries executable..
ruby-2.5.5 - #downloading rubygems-3.0.6
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 866k 100 866k 0 0 21612 0 0:00:41 0:00:41 --:--:-- 57928
No checksum for downloaded archive, recording checksum in user configuration.
ruby-2.5.5 - #extracting rubygems-3.0.6.....
ruby-2.5.5 - #removing old rubygems........
ruby-2.5.5 - #installing rubygems-3.0.6...............................................
ruby-2.5.5 - #gemset created /usr/local/rvm/gems/ruby-2.5.5@global
ruby-2.5.5 - #importing gemset /usr/local/rvm/gemsets/global.gems................................................................
ruby-2.5.5 - #generating global wrappers.......
ruby-2.5.5 - #gemset created /usr/local/rvm/gems/ruby-2.5.5
ruby-2.5.5 - #importing gemsetfile /usr/local/rvm/gemsets/default.gems evaluated to empty gem list
ruby-2.5.5 - #generating default wrappers.......
ruby-2.5.5 - #adjusting #shebangs for (gem irb erb ri rdoc testrb rake).
Install of ruby-2.5.5 - #complete
Ruby was built without documentation, to build it run: rvm docs generate-ri
[root@localhost ~]#
[root@localhost ~]# ruby -v
ruby 2.5.5p157 (2019-03-15 revision 67260) [x86_64-linux]
[root@localhost ~]#
[root@localhost ~]# gem sources --remove https://rubygems.org/
https://rubygems.org/ removed from sources
[root@localhost ~]#