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.

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

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.

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 parameter
  • required: Validates that the parameter will have a value
  • splat: 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

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"

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

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:

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
    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)

Contributing

Any pull requests, suggestions, bug reports and feedback are most welcome :)

  1. Fork it ( http://github.com/lasseebert/jisota/fork )
  2. Create your feature branch (git checkout -b my-new-feature)
  3. Commit your changes (git commit -am 'Add some feature')
  4. Push to the branch (git push origin my-new-feature)
  5. Create new Pull Request