Jisota
Jisota is a simple provisioning tool meant for smaller projects.
Provisioning, in this context, is the act of turning an empty server into a working machine tailored to your needs.
Note: Jisota is still considered unstable. Breaking changes may occur in patch version updates.
Installation
Add this line to your application's Gemfile:
gem 'jisota', require: false
And then execute:
$ bundle
Or install it yourself as:
$ gem install jisota
Introduction
A simple example of a Jisota script:
# config/provision.rb (Or whatever you want to call this file)
require 'jisota'
config = Jisota.config do
role :app do
ruby version: "2.1.1"
nginx config_file: "path/to/nginx.conf"
end
server "mydomain.com", user: "john_doe", roles: :app
end
Jisota.run(config)
Run the script with the ruby executable:
$ ruby config/provision.rb
Ruby version
Jisota is cutting edge and requires Ruby 2.1.0 or later.
Defining a package
Inside a Jisota.config
block, use the package
method to define a package.
Example:
Jisota.config do
package :essentials do
description "Installs important stuff"
run do
apt :curl, :vim, :git
end
end
end
In this simple example we define a package with the name :essentials
. It has
a description and runs another package, :apt
, that it calls with the arguments
:curl, :vim, :git
The DSL you can use inside a package include:
description
: Will provide a description for the package.param
: Specify one or more named parameters for the package. See more in the params section below.run(&block)
: This block will be called when the package is executed.verify(&block)
: If this block exits with code 0, the run block will not be executed
Inside a run-block, packages can be called by their name, somewhat like a Ruby
method. See the apt
example above.
Having multiple run and verify blocks
Sometimes you need to verify only parts of the package. Just specify multiple run and verify blocks:
package :install_awesome_service do
description "Installs a service and starts it"
run { cmd "sudo apt-get install awesome-service" }
verify { cmd "command-to-tell-if-service-is-installed" }
run { cmd "sudo service awesome start" }
verify { cmd "command-to-tell-if-service-is-started" }
end
You can optionally name each of these sections to give a more precise output:
package :install_awesome_service do
description "Installs a service and starts it"
section "Install the service from apt-get"
run { cmd "sudo apt-get install awesome-service" }
verify { cmd "command-to-tell-if-service-is-installed" }
section "Start the service"
run { cmd "sudo service awesome start" }
verify { cmd "command-to-tell-if-service-is-started" }
end
Defining packages to be used in more than one project
Defining packages with Jisota.config
as above will only add the package to
the current configuration. To make packages global they need to be in the
global package manager. Simply use the Jisota.global_config
instead:
Jisota.global_config do
package :ruby do
description "Installs ruby from source"
...
end
end
This will make the package accessable from all configurations. If a local package has the same name, the local package is used.
All build-in packages are defined this way.
Defining params for a package
Params for a package are defined with the param
DSL method inside a
package
-block. Params are named:
package :my_package do
param :foo
param :bar
...
end
This package can be called either with named parameters or with sorted parameters. The following calls are all equivalent:
my_package 42, "Baz"
my_package 42, bar: "Baz"
my_package foo: 42, bar: "Baz"
my_package bar: "Baz", foo: 42
Param options
Params can have the following options:
default
: Sets a default value, if no value is passed to that parameterrequired
: Validates that the parameter will have a valuesplat
: Will assign all the following unnamed arguments to this parameter as an array
Example:
package :apt do
param :packages, splat: true
param :command, default: "sudo apt-get install :package"
run do
cmd command.gsub(/:package/, packages.join(' '))
end
end
# And then call params inside another script block:
run do
apt :vim, :git
apt :vim, :git, command: "sudo apt-get install -y :package"
end
Defining a server
A server needs at least a host, a user and a role:
server "mydomain.com", user: "john", roles: :app
Other options:
key
: File path to ssh private key
Atomic script operations
All packages will eventually boil down to these few atomic operations, which can be called inside a script block:
cmd
Will run a script on the server. Example:
cmd "apt-get install foo"
upload
Uploads the file to the server. Example:
upload from: "path/to/nginx.conf", to: "/etc/nginx.conf"
You can register script blocks to be called if the file was created or updated. If the file on the server is identical with the uploaded file, no action is done and the file will not be overwritten.
upload from: "foo", to: "bar" do
create { cmd 'echo "File was created!"' }
update { cmd 'echo "File was updated!"' }
end
You can disable creation or update of the file:
upload from: "foo", to: "bar" do
create false # Only update is allowed. File will not be created if it does not exists
end
- OR -
upload from: "foo", to: "bar", create: false
You can also use package params and call packages inside these blocks:
package :nginx_config do
param :config_file, require: true
run do
upload from: config_file, to: "/etc/nginx.conf" do
update do
cmd %q{echo "#{config_file} was updated. Restarting nginx..."}
nginx_restart
end
create do
cmd %q{echo "#{config_file} was started. Starting nginx..."}
nginx_start
end
end
end
end
Complete list of build-in packages
ruby
description "Installs ruby from source"
param :version, required: true
param :tmp_dir, default: "~/tmp"
apt
description "Installs packages with apt-get"
param :packages, required: true, splat: true
gem_install
description "Installs a gem"
param :gem_name, required: true
param :sudo, default: true
nginx_passenger
description "Install nginx with passenger module"
param :config_file, required: true
nginx
description "Install nginx from apt"
param :config_file
More packages?
Jisota is a young gem. Please contribute with any packages that you think others could benifit from.
To DSL or not to DSL
The DSL provided by Jisota is just a layer of abstraction. The entire library can easily be used without the DSL. This could be useful if you need to e.g. dynamically build a configuration.
Example:
my_package = Jisota::Package.new(:stuff)
my_package.params << Jisota::Param.new(:foo, default: 42)
config = Jisota::Configuration.new
config.packages << my_package
is equivalent to:
config = Jisota.config do
package :stuff do
param :foo, default: 42
end
end
Full example
An example using most of the features of Jisota.
Note: This was not tested and probably won't make sense in a real application. This is just meant to show some features. A good place to look for examples are in the build-in packages
require 'jisota'
config = Jisota.config do
package :essentials do
description "Install essentials"
param :extra_packages, splat: true
run do
packages = %w[git vim curl]
packages += extra_packages
cmd "sudo apt-get install #{packages.join(" ")}"
end
end
package :postgres do
description "Installs postgres"
run do
cmd "some command sequence to install postgres"
end
end
package :nginx do
description "Install nginx"
param :config_file, required: true
run do
cmd "sudo apt get install nginx"
upload from: config_file, to: /etc/nginx.conf do
update { cmd "sudo service nginx restart" }
create { cmd "sudo service nginx start" }
end
end
end
role :app do
essentials "libfoo"
ruby version: "2.1.1"
nginx
end
role :db do
essentials
postgres
end
server "myapp.com", user: "deploy", roles: :app
server "staging.myapp.com", user: "deploy", roles: :app
server "db.myapp.com", user: "deploy", roles: :db
server "standby.myapp.com", user: "deploy", roles: [:app, :db]
end
Jisota.run(config)
Testing
Unit tests
Run with rake
Acceptance tests
Acceptance tests are not meant to be run regularly. Some of them might take a long time to finish. They are a way to run Jisota and the build-in packages in a near-real environment that is easy to destroy and rebuild.
Start the vagrant:
$ rake vagrant:up
Then run acceptance specs
$ rake spec:acceptance
Or run individual specs. Some of them might take a while:
$ ACCEPTANCE=1 rspec spec/acceptance/some_spec.rb
To kill vagrant and start with a fresh machine:
$ rake vagrant:rebuild
Contributing
Any pull requests, suggestions, bug reports and feedback are most welcome :)
- Fork it ( http://github.com/lasseebert/jisota/fork )
- Create your feature branch (
git checkout -b my-new-feature
) - Commit your changes (
git commit -am 'Add some feature'
) - Push to the branch (
git push origin my-new-feature
) - Create new Pull Request