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
Path to rules file. If not set rules.pl
in cwd is taken
Ssh password. Optional
Ssh user. Optional
Ssh host. Optional, see also hosts.raku
Disable color output. Optional
Debug mode (produce a lot of useful, but low level info). Optional
Examples
See:
rules.raku
file
rules/
folder
Author
Alexey Melezhik
See Also
Thanks to
God Who inspires me in my life!