Sinatra Starter for Predix: Ruby App with PostgreSQL and Redis

by Dmitry SavitskiApril 19, 2016
With a source code of a sample Sinatra app, learn how to streamline the process of preparing it with Hazel before configuring the databases.

sinatra-starter-for-predix-platform-v1

The technical requirements for the Internet of Things (IoT) server applications can substantially differ from the ones applied to the Internet of Humans. First of all, the need for any visual interfaces is completely eliminated.

These step-by-step instructions are intended to guide you through the process of starting a basic Ruby/Sinatra application server on Predix, GE’s IoT platform for the Industrial Internet. From the post, you can learn how to deploy the application to Predix, as well as configure the PostgreSQL database and Redis.

 

Preparing Sinatra

Sinatra is a simple Ruby framework that has the flexibility to focus on the main task of exchanging information between the server and clients.

Since we are going to concentrate on Predix deployment rather than on preparing a Sinatra application, we’ll use Hazel to streamline the process.

gem install hazel
hazel predix_sinatra_starter -d postgres --redis --rvm --git

Executing these commands generates a proper Sinatra application skeleton prepared to run PostgreSQL through Sequel and with Redis bindings.

sinatra-starter-for-predix-ruby-app-with-postgresql-and-redis

You might need to temporarily comment out the Sequel.connect attempts in config/initializers/database.rb until you configure the connection. The command below will start the application for you locally to check that it works.

rackup config.ru

To follow along, you can use our sample application.

 

Deploying the application to Predix

To get started, follow the installation instructions for the Cloud Foundry CLI binaries.

curl -L "https://cli.run.pivotal.io/stable?release=linux64-binary&source=github" | tar -zx
./cf --version
./cf --help

Run the cf login -a https://api.system.aws-usw02-pr.ice.predix.io command to enter your authentication data.

Cloud Foundry needs the manifest.yml file for your application, which we’re going to create now in the application root.

applications:
 - name: sinatra-app
   buildpack: https://github.com/cloudfoundry/ruby-buildpack
   memory: 64M
   stack: cflinuxfs2

As we work with the Ruby Cloud Foundry buildpack, there are a couple of things we need to do:

  1. Declare a Ruby version in your Gemfile by adding the following line.
  2. ruby '2.1.8'

  3. Add a Procfile starting your application to the root. Ensure it is a valid YAML hash.
    web: rackup config.ru -p $PORT

    Reading from the global $PORT variable is crucial here, because Cloud Foundry–based Predix assigns you the port to run on and expects you to use it.

  4. Be sure to add the .cfignore file to the root of the application. It should list all files and folders in your project directory tree you don’t want to be uploaded to production.

Calling the cf push command will upload, bundle, and run our simple Sinatra application.

 

Configuring the PostgreSQL database

Create a local Postgres DB for development.

createdb predix_sinatra_starter_development

Update your Rakefile with the db:environment task.

namespace :db do
  task :environment do
    require 'sequel'
    ENV['RACK_ENV'] ||= 'development'
  end
end

If you used Hazel to generate your application, rename its default database configuration file config/db.yml into a standard config/database.yml file. This is the file the Ruby buildpack will replace with an auto-generated one in production. Make sure the file contains your local database’s address.

development: "postgres://username@localhost/predix_sinatra_starter_development"

Note that the simplest database setup might result in Postgres refusing the connection due to no password being supplied. My usual local development databases run in a not-so-safe mode of allowing any local connections, but you can tweak this step of database setup to use password protection.

Let’s create a migration that will demonstrate we have a proper connection between the application and the database.

db/migrate/001_create_items_table.rb:

Add an indicator that we have data access from Sinatra to views/welcome.erb:

<%= DB[:items].map(:name)  %>

Create a Postgres service as described in Predix Documentation and associate it with your application.

cf create-service postgres shared-nr sinatra-postgres-test
cf bind-service sinatra-app sinatra-postgres-test
cf restage sinatra-app

As we’ve already mentioned, the Ruby buildpack writes a new database.yml file. However, the file is ERB-enhanced, so it will fail our current application’s database initialization. To avoid that, Sinatra can take a note from Rails, which runs this file through ERB when loading it, to allow various dynamic options.

In the config/initializers/database.rb file, replace settings = YAML::load_file("config/database.yml") with the input provided below.

require ‘erb’
settings = YAML.load(ERB.new(File.read('config/database.yml')).result)

Finally, update Procfile for it to try running the migrations each time we deploy.

To limit the number of times the migration gets triggered, you can add a classic Cloud Foundry Rake task, limiting the Rake execution to only the first instance (when in the batch).

Rakefile:

namespace :cf do
  desc 'Only run on the first application instance'
    task :on_first_instance do
      instance_index = JSON.parse(ENV["VCAP_APPLICATION"])["instance_index"] rescue nil
      exit(0) unless instance_index == 0
    end
end

Procfile:

web: bundle exec rake cf:on_first_instance db:migrate && rackup config.ru -p $PORT

Push the new application version:

cf push

 

Configuring Redis

Setting up Redis requires following pretty much the same steps as configuring PostgreSQL.

cf create-service redis-1 shared-vm sinatra-redis-test         
cf bind-service sinatra-app sinatra-redis-test

Modify the Redis initializer to read the configuration from an environment variable.

config/initializers/redis.rb:

if ENV['VCAP_SERVICES']
   require 'json'
   credentials = JSON.parse(ENV['VCAP_SERVICES'])["redis-1"].first["credentials"]
   REDIS = Redis.new(credentials)
end

You can also see this and other production variables by running the following command.

cf env sinatra-app

Deploy the new version.

cf push

 

Further reading


This blog post was written by Dmitry Savitski, edited by Victoria Fedzkovich and Alex Khizhniak.