Migrate from Itamae¶
Sadame's recipe language comes from the same family as Itamae's and
mitamae's: Ruby files that declare resources, evaluated top to bottom, with
templates in ERB. Porting is mostly mechanical: resource names stay, block
attributes become keyword options, and node becomes vars. This guide
lists what is identical, what changed, and walks through
examples/migrate-from-itamae, an Itamae recipe and its translation, which
the test suite applies on Debian and Ubuntu.
Side by side¶
# The Itamae recipe that ../recipe.rb translates, kept for comparison.
# Run with Itamae: itamae local itamae/recipe.rb -j itamae/node.json
package 'nginx'
package 'openssl'
template '/etc/nginx/conf.d/app.conf' do
source 'templates/app.conf.erb'
mode '644'
variables(server_name: node[:server_name], workers: node[:workers])
notifies :reload, 'service[nginx]'
end
directory '/srv/app' do
owner 'www-data'
mode '755'
end
remote_file '/usr/local/bin/backup' do
source 'files/backup.sh'
mode '755'
end
execute 'self-signed certificate' do
command 'openssl req -x509 -newkey rsa:2048 -nodes -days 365 -subj /CN=app.example.com ' \
'-keyout /etc/ssl/private/app.key -out /etc/ssl/certs/app.crt'
not_if 'test -f /etc/ssl/private/app.key'
end
user 'deploy' do
shell '/bin/bash'
end
service 'nginx' do
action [:enable, :start]
end
# itamae/recipe.rb, translated. Resource names mostly stay; blocks become
# keyword options; node becomes vars; template variables become locals.
package "nginx"
package "openssl"
file "/etc/nginx/conf.d/app.conf",
template: "app.conf.erb",
locals: { server_name: vars[:server_name], workers: vars[:workers] },
mode: "644",
notifies: [:reload, "service[nginx]"]
directory "/srv/app", owner: "www-data", mode: "755"
file "/usr/local/bin/backup", source: "backup.sh", mode: "755"
exec "self-signed certificate",
command: "openssl req -x509 -newkey rsa:2048 -nodes -days 365 -subj /CN=app.example.com " \
"-keyout /etc/ssl/private/app.key -out /etc/ssl/certs/app.crt",
not_if: "test -f /etc/ssl/private/app.key"
user "deploy", shell: "/bin/bash"
service "nginx", enable: true, state: :running
The template changes in one way: Itamae's variables(...) arrive as
instance variables, sadame's locals: as a Hash.
# Managed by Itamae.
server {
listen 8080;
server_name <%= @server_name %>;
root /srv/app;
# workers: <%= @workers %>
}
# Managed by sadame.
server {
listen 8080;
server_name <%= locals[:server_name] %>;
root /srv/app;
# workers: <%= locals[:workers] %>
}
What is identical¶
- Recipes are Ruby files evaluated in order; resources run in the order they are declared.
package,directory,file,user,service,group,linkandgitkeep their names.include_recipe "path", relative to the including file, each file once.notifiesandsubscribes, with delayed notifications by default and:immediately.only_ifandnot_ifwith shell commands, onexec.- Templates are ERB with
<%= %>,<% %>and-%>.
What changed¶
| Itamae | sadame |
|---|---|
template "/path" do source "x.erb"; variables(a: 1) end |
file "/path", template: "x.erb", locals: { a: 1 } |
remote_file "/path" do source "files/x" end |
file "/path", source: "x" (looked up next to the recipe, then in files/) |
execute "name" do command "..." end |
exec "name", command: "..." |
service "x" do action [:enable, :start] end |
service "x", enable: true, state: :running |
link "/srv/app/current" do to "/srv/app/v2" end |
link "/srv/app/current", to: "/srv/app/v2" |
git "/srv/app" do repository "URL"; revision "v2" end |
git "/srv/app", repository: "URL", revision: "v2" |
action :nothing |
action: :nothing, on exec only; service without enable:/state: manages nothing |
node[:attr], -j node.json, -y node.yml |
vars[:attr], vars.rb, --var, vars in hosts.rb |
host inventory in node: node[:cpu][:total], node[:memory][:total], node[:hostname] |
facts[:cpus], facts[:memory_mb], facts[:hostname] |
@name in templates |
locals[:name]; vars[:name] is also available |
notifies :reload, "service[x]" |
notifies: [:reload, "service[x]"] |
only_if / not_if on any resource |
on exec only |
define |
plain Ruby methods in the recipe |
run_command in recipes |
not available; recipes do not run commands while loading |
mode "644" |
mode: "644" or "0644"; numbers are rejected |
itamae local recipe.rb |
sadame apply recipe.rb |
itamae ssh -h host recipe.rb |
sadame apply recipe.rb -h host (copies itself; nothing to install on the host) |
--dry-run |
sadame plan |
| no drift command | sadame drift |
mitamae users: sadame is also a single binary built with mruby, so the
mruby limits on recipe code are similar (no gems, no standard library); see
Architecture.
The differences from mitamae are the same as above, plus push over SSH,
inventories and drift.
Resources Itamae has and sadame does not¶
http_request, local_ruby_block, gem_package and plugins. Use exec
with a guard in their place:
| Itamae | sadame |
|---|---|
http_request "/srv/app/example.html" do url "https://..." end |
exec "curl -fsSL -o /srv/app/example.html https://...", creates: "/srv/app/example.html" |
gem_package "bundler" |
exec "gem install bundler", not_if: "gem list -i bundler" |
link, group and git came in sadame 0.2.0; before that, they needed a
guarded exec too.
How to port¶
- Move
node.jsonornode.ymlvalues intovars.rb(a Ruby Hash). - Rewrite each resource block as a call with keyword options, using the
table above.
sadame planrejects misspelled options with the list of valid ones. - Change
@namein templates tolocals[:name]orvars[:name]. - Move
only_ifandnot_iffrom non-execresources into Rubyifaround the resource, or into anexec. - Run
sadame planon a machine Itamae already manages. A complete translation shows no changes, or only the differences you expect.
Common mistakes¶
- Expecting
@variablesin templates. They arenilin sadame's templates; uselocals[:name]. sourcepaths. Itamae paths are relative to the recipe; so are sadame's, but sadame also looks intemplates/andfiles/, so"templates/x.erb"and"x.erb"both work.- Guards on files and packages. Only
exectakesonly_ifandnot_if; wrap other resources in Rubyif.