Bird
Bird - Alternative to Chef Inspec and Goss, written in Raku.
Install / Use
/learn @melezhik/BirdREADME
Bird 🐦
Bird - Raku DSL for Linux Servers Verification.
It's written in Raku and exposes pure Raku API.
WARNING ⚠️
Bird is under active development now, things might change.
Build Status
INSTALL
zef install Bird
Bird uses Sparrow6 plugin ssh-bulk-check to do it's work, so one needs
to setup Sparrow6 repository:
export SP6_REPO=http://sparrowhub.io/repo
To run check remotely install ssh-pass:
sudo yum install sshpass
USAGE
Create rules:
cat rules.raku
directory-exists "{%*ENV<HOME>}";
file-exists "{%*ENV<HOME>}/.bashrc";
bash "echo hello > /tmp/bird.tmp";
bash "echo bird >> /tmp/bird.tmp";
file-has-line "/tmp/bird.tmp", "hello", "bird";
file-has-permission "documentation/dsl.md", %( read-all => True );
file-has-permission "bin/bird", %( execute-all => True, read-all => True );
bash "chmod a+r /tmp/bird.tmp";
bash "chmod a+w /tmp/bird.tmp";
file-has-permission "/tmp/bird.tmp", %( write-all => True, read-all => True );
command-has-stdout "echo hello; echo bird", "hello", "bird";
command-exit-code "raku --version", 0;
package-installed "nano";
pip3-package-installed "PyYAML";
file-has-permission "rules.raku", %( read-all => True );
package-installed "nano";
bash "echo username=admin > /tmp/creds.txt; echo password=123 >> /tmp/creds.txt;";
file-data-not-empty "/tmp/creds.txt",
"username=", "password=";
Run checks against hosts:
bird
bird:: [read hosts from file] [hosts.raku]
bird:: [cmd file] [/root/.bird/274302/cmd.sh]
bird:: [check file] [/root/.bird/274302/state.check]
bird:: [init cmd file]
[bash: echo hello > /tmp/bird.tmp] :: <empty stdout>
[bash: echo bird >> /tmp/bird.tmp] :: <empty stdout>
[bash: chmod a+r /tmp/bird.tmp] :: <empty stdout>
[bash: chmod a+w /tmp/bird.tmp] :: <empty stdout>
[bash: echo username=admin > /tmp/creds.txt; echo passwor ...] :: <empty stdout>
[repository] :: index updated from file:///root/repo/api/v1/index
[check my hosts] :: check host [localhost]
[check my hosts] :: ==========================================================
[check my hosts] :: <<< test_01: directory /root exists
[check my hosts] :: directory /root exists
[check my hosts] :: >>>
[check my hosts] :: <<< test_02: file /root/.bashrc exists
[check my hosts] :: file /root/.bashrc exists
[check my hosts] :: >>>
[check my hosts] :: <<< test_03: file /tmp/bird.tmp has lines
[check my hosts] :: hello
[check my hosts] :: bird
[check my hosts] :: >>>
[check my hosts] :: <<< test_04: file documentation/dsl.md is readable by all
[check my hosts] :: -rw-r--r--
[check my hosts] :: >>>
[check my hosts] :: <<< test_05: file bin/bird is readable by all
[check my hosts] :: -rwxr-xr-x
[check my hosts] :: >>>
[check my hosts] :: <<< test_06: file bin/bird is executable by all
[check my hosts] :: -rwxr-xr-x
[check my hosts] :: >>>
[check my hosts] :: <<< test_07: file /tmp/bird.tmp is readable by all
[check my hosts] :: -rw-rw-rw-
[check my hosts] :: >>>
[check my hosts] :: <<< test_08: file /tmp/bird.tmp is writable by all
[check my hosts] :: -rw-rw-rw-
[check my hosts] :: >>>
[check my hosts] :: <<< test_09: command [echo hello; echo bird] has stdout
[check my hosts] :: hello
[check my hosts] :: bird
[check my hosts] :: >>>
[check my hosts] :: <<< test_10: command [raku --version] has exit code
[check my hosts] :: command [raku --version] exit code [0]
[check my hosts] :: >>>
[check my hosts] :: <<< test_11: package(s) installed
[check my hosts] :: Installed Packages
[check my hosts] :: nano.x86_64 2.9.8-1.el8 @baseos
[check my hosts] :: package nano is installed
[check my hosts] :: >>>
[check my hosts] :: <<< test_12: pip3 package(s) installed
[check my hosts] :: PyYAML 3.12
[check my hosts] :: >>>
[check my hosts] :: <<< test_13: file rules.raku is readable by all
[check my hosts] :: -rw-r--r--
[check my hosts] :: >>>
[check my hosts] :: <<< test_14: package(s) installed
[check my hosts] :: Installed Packages
[check my hosts] :: nano.x86_64 2.9.8-1.el8 @baseos
[check my hosts] :: package nano is installed
[check my hosts] :: >>>
[check my hosts] :: <<< test_15: file /tmp/creds.txt data not empty
[check my hosts] :: >>> username=[censored]
[check my hosts] :: >>> password=[censored]
[check my hosts] :: >>>
[check my hosts] :: end check host [localhost]
[check my hosts] :: ==========================================================
[task check] verify host [localhost] start
[task check] test_01: directory /root exists
[task check] stdout match (r) <directory /root exists> True
[task check] test_02: file /root/.bashrc exists
[task check] stdout match (r) <file /root/.bashrc exists> True
[task check] test_03: file /tmp/bird.tmp has lines ["hello", "bird"]
[task check] stdout match (r) <hello> True
[task check] stdout match (r) <bird> True
[task check] test_04: file documentation/dsl.md is readable by all
[task check] stdout match (r) <^^^ \S "r"\S\S "r"\S\S "r"\S\S $$> True
[task check] test_05: file bin/bird is readable by all
[task check] stdout match (r) <^^^ \S "r"\S\S "r"\S\S "r"\S\S $$> True
[task check] test_06: file bin/bird is executable by all
[task check] stdout match (r) <^^^ \S \S\S"x" \S\S"x" \S\S"x" $$> True
[task check] test_07: file /tmp/bird.tmp is readable by all
[task check] stdout match (r) <^^^ \S "r"\S\S "r"\S\S "r"\S\S $$> True
[task check] test_08: file /tmp/bird.tmp is writable by all
[task check] stdout match (r) <^^^ \S \S"w"\S \S"w"\S \S"w"\S $$> True
[task check] test_09: command [echo hello; echo bird] has stdout
[task check] ["hello", "bird"]
[task check] stdout match (r) <hello> True
[task check] stdout match (r) <bird> True
[task check] test_10: command [raku --version] has exit code [0]
[task check] stdout match (r) <command [raku --version] exit code [0]> True
[task check] test_11: package(s) installed "nano"
[task check] stdout match (r) <package nano is installed> True
[task check] test_12: pip3 package(s) installed "PyYAML"
[task check] stdout match (r) <^^ 'PyYAML' \s+> True
[task check] test_13: file rules.raku is readable by all
[task check] stdout match (r) <^^^ \S "r"\S\S "r"\S\S "r"\S\S $$> True
[task check] test_14: package(s) installed "nano"
[task check] stdout match (r) <package nano is installed> True
[task check] test_15: file /tmp/creds.txt data not empty ["username=", "password="]
[task check] stdout match (r) <^^ '>>>'> True
[task check] <>>> username=[censored] is not empty> True
[task check] <>>> password=[censored] is not empty> True
[task check] verify host [localhost] end
Rules DSL
Rules DSL is special language to validate states of Linux host.
This is just plain Raku functions:
file-exists "{%*ENV<HOME>}/.bashrc";
The full list of DSL functions available here.
Ssh hosts
There are two ways to set ssh hosts:
Command line
By using --host, --user, --password options:
bird --host=192.168.0.1 --user=alex --password=PasSword
Hosts.raku
To define dynamic hosts configuration, use hosts.raku, this should
be Raku script that returns @Array of hosts:
[
'192.168.0.1',
'foo.bar.baz',
# so on
]
For example:
use Sparrow6::DSL;
my %state = task-run "worker nodes", "ambari-hosts", %(
ambari_host => 'cluster.fqdn.host',
admin => 'super-jack',
password => 'PaSsWord123',
cluster => "cluster01",
node_type => "worker",
);
%state<hosts><>.map: { %i<ip> }
cli options
--rules
Path to rules file. If not set rules.pl in cwd is taken
--password
Ssh password. Optional
--user
Ssh user. Optional
--host
Ssh host. Optional, see also hosts.raku
--nocolor
Disable color output. Optional
--debug
Debug mode (produce a lot of useful, but low level info). Optional
Examples
See:
-
rules.rakufile -
rules/folder
Author
Alexey Melezhik
See Also
Thanks to
God Who inspires me in my life!
