Ruby Config Management
Navigate into a configuration dir and call rbcm.
rbcm # cwd
rbcm project/ # specific directory
rbcm project.rb # single file
rbcm --node localhost # single node
applying
Applying actually passes three steps: checking, approving and execution.
1/3 check
Rbcm compares the affected files and executes the action-checks. Unneccessary actions are marked and will be skipped in the approvemnt process.
2/3 approve
The user approves each action interactively.
- identical actions on multiple nodes can be approved at once by choosing "o"
- multiple changes to a file can be approved individually by choosing "i"
3/3 execute
All approved actions are being executed.
documentation
Rbcm expects to be called from within a project directory.
capabilities
Nodes are defined by calling capabilities. Capabilities are user defined methods residing in project directory. Each capability may have a second incarnation with a bang suffix ("cap!"). The bang version is called once on each node if the node called the non-bang-version before.
def ip v4: nil, mac: nil
# may call further capabilities
end
def ip!
# called once at the end
end
User defined capabilities extend the set of base capabilities by further mechanisms. They are meant to use the base capabilities to actually generate actions to be executed on the server.
base capabilities
The base capabilities file
and run
are neccessary to actually generate
actions to be executed on the server.
file
file "/etc/dhcp/dhcpd.conf", content: "i am in"
run
run "apt-get install -y #{install}",
check: "dpkg-query -l #{install}"
needs
Jobs following a call of needs :capability
will get a dependency on
:capability
.
trigger
Actions may trigger or may be triggered by other actions. Actions with
triggered_by
-attributes will only be approved and applied, if the
corresponding trigger has been activated.
trigger :reload_dhcp do
dhcp_server conf: "dns-servers: 8.8.8.8;"
end
triggered_by :reload_dhcp do
systemctl reload: :dhcpd
end
Inline-Version:
dhcp_server conf: "dns-servers: 8.8.8.8;", trigger :reload_dhcp
systemctl reload: :dhcpd, triggered_by :reload_dhcp
Every job automatically activiates a trigger with the name of the actions capability, if present.
dhcp_server conf: "dns-servers: 8.8.8.8;"
triggered_by :dhcp_server do
systemctl reload: :dhcpd
end
reading state
Every capability has an automatically generated questionmark suffix version to access jobs called so far.
node "example.com" do
user "alice"
user "bob", :no_home
user? # [["alice", "bob"], [nil, :no_home]]
user?[0] # ["alice", "bob"] <- grouped by ordered param number
ip v4: "10.0.0.1", v6: "2000::f0f0:1212"
ip v4: "192.168.1.55"
ip?(:v4) # ["10.0.0.1", "192.168.1.55"]
ip?(:v4) # ["2000::f0f0:1212"]
ip?(with: :v6) # [{v4: "10.0.0.1", v6: "2000::f0f0:1212"}]
end
nodes
Nodes represent a real server.
node "example.com" do
# defintion
end
Further actions:
- call capabilities
- include groups
- add dependencies
groups
Groups can be used to apply definition to multiple nodes.
group :dhcp_clients do
# definition
end
Nodes need to include a groups definition.
node "example.com" do
group :dhcp_clients
end
expand groups
Groups can be expanded from within other groups or nodes. This way, you can add definition from one to another node, which is member of the expanded group. Local variables can be used to pass local state.
group :dhcp_clients do
host = @name
ip = ip?(:v4).first
ip = ip?(:mac).first
group :dhcp_servers do
dhcp host: host, ip: ip, mac: mac
end
end
provider
A node can act as an information provider for neighbouring nodes, by means of being in the same group. The scope of both, the offering and the request, may be limited to certain groups or a single group.
The 'provide'-statements checks for providers in specified group or in all groups the node is part of.
Nodes acting as provider will be parsed before their consuming nodes.
def :easy_rsa
# surrounding actions are implicitly beeing made dependecies for the provider
# rbcm runs a second time, if the dependencies aren't fulllfilled on first run
group :site_frankfurt
installl :openvpn
provides :vpn_certificate,
for: :site_frankfurt,
required_tags: :easy_rsa_installed # add more dependencies
do
"easy-rsa genarate cert; cat cert"
end
end
def :openvpn_client
group :site_frankfurt
file "/etc/openvpn/client.conf",
content: provide(:vpn_certificate, from: :site_frankfurt)
end
addons
Projects can be combined via addons, even recursively. This can be used to centralizise and share capability sets. An addon is an usal rbcm project, although nodes make no sense within an addon.
capabilities/localhost.rb
:
addon github: "CroneKorkN/rbcm-capabilities"
node 'localhost' do
systemctl restart: "apache2"
end
Examples
dhcp server und clients
capabilities/ip.rb
:
def ip address, mac: nil
end
capabilities/dhcpd.rb
:
def dhcpd!
hosts = dhcpd?(with: :host).collect{ |host|
"host #{host[:host]} {
hardware ethernet #{host[:mac]};
fixed-address #{host[:ip]};
}"
}
file '/etc/dhcp/dhcpd.conf', content: hosts.join("\n")
end
definitions/dhcp_clients.rb
:
group :dhcp_clients do
host = @name
ip = ip?.first.first
mac = ip?(:mac).first
group :dhcp_servers do
dhcpd host: host,
mac: mac,
ip: ip
end
end
definitions/dhcp_servers.rb
:
group :dhcp_servers do
end
definitions/router.rb
:
node 'router.example.com' do
group :dhcp_serves
end
definitions/pc.rb
:
node 'pc.example.com' do
ip '10.0.0.2', mac: "22:22:22:22:22:22"
group :dhcp_clients
end
definitions/notebook.rb
:
node 'notebook.example.com' do
ip '10.0.0.3', mac: "33:33:33:33:33:33"
group :dhcp_clients
end
TODO
- interactive shell from within a virtual localhost-node to run actions
- display not applied triggered actions if fail, that would otherwise get lost
- auto apply via git integration
- display "used infos from" when approving
- https://github.com/jamis/net-ssh-multi, https://github.com/delano/rye
- warn if apt? hasnt been called yet
- dont
- https://shopify.github.io/liquid/basics/introduction/
rm ./rbcm-0.0.0.gem; gem build ./rbcm.gemspec; gem install ./rbcm-0.0.0.gem; rbcm ../config/