Skip to content

Step 13 deploy to heroku

opensas edited this page Nov 3, 2011 · 21 revisions

Step 13 - deploy to heroku

Purpose: in this step we will see how to deploy you app to heroku's cloud computing platform.

cd ~/devel/apps
git clone git://github.com/opensas/play-demo.git
cd play-demo
git checkout origin/13-deploy_to_heroku

Creating an account at heroku

Creating an account at heroku is really easy. Go to Heroku homepage and click on signup to create a new account, enter a valid email address and just follow the instructions you'll receive by mail.

Setup heroku command line tools

Follow these instructions to add heroku tools on linux.

On ubuntu, execute the following commands to add heroku's repository

sudo apt-add-repository 'deb http://toolbelt.herokuapp.com/ubuntu ./'
curl http://toolbelt.herokuapp.com/apt/release.key | sudo apt-key add -
sudo apt-get update
sudo apt-get install heroku-toolbelt

You might have to install curl first

sudo apt-get install curl

Now check that the client was successfully installed

heroku version
heroku-gem/2.8.0

heroku update
-----> Updating to latest client... done

Authenticate with heroku

At the command line, issue the following commands

heroku login
Enter your Heroku credentials.
Email: open@gmail.com
Password:

Here you have to enter your heroku password, do not confuse it with you private key password.

If you don't have any private/public key in ~/.ssh, it will create a new pair of keys

Could not find an existing public key.
Would you like to generate one? [Yn] Y
Generating new SSH public key.
Uploading ssh public key /home/sas/.ssh/id_rsa.pub

To manually setup you public/private keys, see the Setup Up ssh keys in this article.

Prepare your app for heroku - initialize the git repo

In order to work with heroku, first you have to create a git repository.

You will have to install git on you development station. In ubuntu is as easy as sudo apt-get install git. Have a look at github documentation for linux and windows instructions.

First of all, make sure you are at the root of your application. Then create a .gitignore file. Here you have a handy play .gitignore template file.

.gitignore

# Extracted from https://github.com/ulrich/macaron-factory/blob/master/.gitignore
# Ignore all dotfiles...
.*
# except for .gitignore
!.gitignore

# Ignore Play! working directory #
db
eclipse
log
logs
precompiled
tmp
test-result
eclipse
server.pid

Now run the app in production mode to test it.

play run --%production

And then create the git repo and issue an initial commit.

git init
git add .
git commit -m init
[master (root-commit) 93e6f9d] init
 16 files changed, 391 insertions(+), 0 deletions(-)
 create mode 100644 .gitignore
 create mode 100644 app/controllers/Application.java
 create mode 100644 app/views/Application/index.html
 create mode 100644 app/views/errors/404.html
 create mode 100644 app/views/errors/500.html
 create mode 100644 app/views/main.html
 create mode 100644 conf/application.conf
 create mode 100644 conf/dependencies.yml
 create mode 100644 conf/messages
 create mode 100644 conf/routes
 create mode 100644 public/images/favicon.png
 create mode 100644 public/javascripts/jquery-1.5.2.min.js
 create mode 100644 public/stylesheets/main.css
 create mode 100644 test/Application.test.html
 create mode 100644 test/ApplicationTest.java
 create mode 100644 test/BasicTest.java
 create mode 100644 test/data.yml

Create a new app on Heroku

Now we'll create the new app on heroku

heroku create -s cedar
Creating afternoon-dusk-4670... done, stack is cedar
http://afternoon-dusk-4670.herokuapp.com/ | git@heroku.com:afternoon-dusk-4670.git
Git remote heroku added

Heroku will automatically pick a name for you application in this case it was afternoon-dusk-4670.

And we will issue our initial deploy:

git push heroku master

That's it! A deploy to heroku it's just a git push...

When pushing to git, you'll have to enter the password of the private key created in ~/.ssh, do not confuse with your heroku password.

Always before deploying you have to commit your changes on the master branch

Now you can point your browser to http://afternoon-dusk-4670.herokuapp.com or just issue heroku open to let heroku do that for you.

Changing the application name

It's highly probable that you won't like afternoon-dusk-4670, so navigate to https://api.heroku.com/myapps, click on afternoon-dusk-4670, and change it's name to jugar-demo. The following message will appear:

afternoon-dusk-4670 renamed to jugar-demo.
Existing git checkouts must be updated.

Follow these instructions:

git remote rm heroku
git remote add heroku git@heroku.com:jugar-demo.git

Now you can check your app at http://jugar-demo.herokuapp.com

Trouble shooting heroku

We'll have to make a couple of changes so that our app con work flawlessly on heroku. For that purpose we'll first see a few commands that will let us deal with heroku.

heroku info
=== play-demo-test
Web URL:        http://jugar-demo.herokuapp.com/
Git Repo:       git@heroku.com:play-demo-test.git
Dynos:          0
Workers:        0
Repo size:      42M
Slug size:      26M
Stack:          cedar
Data size:      (empty)
Addons:         Basic Logging, Basic Release Management, Shared Database 5MB
Owner:          opensas@gmail.com

heroku config
DATABASE_URL        => postgres://xxx:yyyy@zzz/wwww
JAVA_OPTS           => -Xmx384m
PATH                => .play:.tools:/usr/local/bin:/usr/bin:/bin
PLAY_OPTS           => --%prod -Dprecompiled=true
SHARED_DATABASE_URL => postgres://xxx:yyyy@zzz/wwww

heroku releases
Rel   Change                          By                    When
----  ----------------------          ----------            ----------
v5    Deploy a4776ec                  opensas@gmail.com     8 minutes ago            
v4    Config add PLAY_OPTS, PATH, ..  opensas@gmail.com     8 minutes ago

And the most important command heroku logs. This command will let us inspect heroku's execution logs. We will have to study them carefully to see what's going on on the server. For example, here's the output when we try to execute BootstrapJob.

heroku logs
...
2011-09-20T14:05:40+00:00 app[web.1]: 14:05:40,511 ERROR ~ ERROR: current transaction is aborted, commands ignored until end of transaction block
2011-09-20T14:05:40+00:00 app[web.1]: 14:05:40,511 ERROR ~ While deleting class models.Event instances
2011-09-20T14:05:40+00:00 app[web.1]: javax.persistence.PersistenceException: org.hibernate.exception.GenericJDBCException: could not execute update query
2011-09-20T14:05:40+00:00 app[web.1]: 	at 
...
p[web.1]: 	at play.test.Fixtures.delete(Fixtures.java:72)

Locally installing postgresql

Most modifications you'll have to make to your app comes from the fact that Heroku uses postgresql database. So you should install postgresql on your workstation to be able to spot problem when developing the app.

Follow this guide to install postgresql on ubuntu, or just issue

sudo apt-get install postgresql pgadmin3

Run pgadmin and create a play-demo database and a play-demo-account account, with full permissions to that db. These are the sql commands generated by pgadmin.

-- create database
CREATE DATABASE "jugar-demo"
  WITH ENCODING='UTF8'
       CONNECTION LIMIT=-1;

-- create a new group role jugar-demo-role
CREATE ROLE "jugar-demo-role"
   VALID UNTIL 'infinity';

-- create a new login role jugar-demo-login with password: play-demo-pass
-- and in role membership add jugar-demo-role
CREATE ROLE "jugar-demo-login" LOGIN ENCRYPTED PASSWORD 'md577e4b34d0ac90d3047950899a76fb04f'
   VALID UNTIL 'infinity';
GRANT "jugar-demo-role" TO "jugar-demo-login";

-- grant permissions to play-demo-role on play-demo database
GRANT ALL ON DATABASE "jugar-demo" TO GROUP "jugar-demo-role";

Now you'll have to modify your application.conf to change the database we are using on development and production mode. So comment the db=mem line, add our local database configuration, and set the prod configuration to point to the environment variable SHARED_DATABASE_URL.

Your application.conf file should be like this:

#   - fs  : for a simple file written database (H2 file stored)
# db=mem
[...]

# heroku configuration
%prod.db=${SHARED_DATABASE_URL}

# local development configuration
# If you need a full JDBC configuration use the following :
db.url=jdbc:postgresql:jugar-demo
db.driver=org.postgresql.Driver
db.user=jugar-demo-login
db.pass=play-demo-pass

See these two articles for more information on configurating your database for heroku

http://www.touilleur-express.fr/2011/09/19/deployer-play-framework-heroku/

http://devcenter.heroku.com/articles/database-driven-play-apps

You'll also have to enable sql statements debug on play. Edit application.conf file an uncomment the following line

conf/application.con

# Debug SQL statements (logged using DEBUG level):
jpa.debugSQL=true

Now start your app and check the console output, you should see the following line:

11:40:12,742 INFO  ~ Connected to jdbc:postgresql:play-demo

First problem: User is a reserved word in postresql

11:40:18,506 ERROR ~ Unsuccessful: create table User (id int8 not null, accessToken varchar(255), avatarUrl varchar(255), name varchar(255), secret varchar(255), token varchar(255), primary key (id))
11:40:18,507 ERROR ~ ERROR: syntax error at or near "User"

Just put the table name between quotation marks "" so that postgresql can refer to the table. Add the @Table annotation to User model like this:

models.User

@Entity()
@Table(name="\"User\"")
public class User extends Model {

Then restart the app.

Second Problem: Foreign Key constraint violation when deleting EventType

When executing the BootstrapJob for the first time everything works fine, but the second time you'll see the following error on the console:

11:44:32,433 WARN  ~ SQL Error: 0, SQLState: 23503
11:44:32,435 ERROR ~ ERROR: update or delete on table "eventtype" violates foreign key constraint "fk403827aa2ebda90" on table "event"
  Detail: Key (id)=(1) is still referenced from table "event".
11:44:32,444 ERROR ~ While deleting class models.EventType instances
javax.persistence.PersistenceException: org.hibernate.exception.ConstraintViolationException: could not execute update query

Postgresql can't delete EventTypes because there are Events associated with them.

Just modify BootstrapJob to delete the Entities in an order that prevents foreign key violation from happening. And at the same time will add some logging.

	@Override
	public void doJob() {
		//forces orm to delete first the events
		Fixtures.delete(Event.class);
		Fixtures.delete(EventType.class);
		Fixtures.delete(User.class);

		// just to be sure, now delete all models
		Fixtures.deleteAllModels();
		Fixtures.loadModels("data.yml");

		Logger.info("ran BootstrapJob, %s events loaded, %s types loaded", Event.count(), EventType.count());
	}

Now start the app again and reload the events from the yaml file a couple of times.

Flash info at regular intervals

Our app will be on running to the cloud, for everyone to create and update events. We don't want to run out of events, so will schedule a job to recreate data every 30 minutes. Play supports scheduled jobs, so we'll just have to add the @Every annotation to the job.

lib.utils.BootstrapJob

[...]
@OnApplicationStart
@Every("30min")
public class BootstrapJob extends Job {

	@Override
	public void doJob() {
[...]

And we will also add a legend telling the user that the info will be reloaded.

views/Application/list.html

[...]
<div class="page-header">
    <h1>
    &{'event.title'}
    <small>&{'event.flashData'}</small>
    </h1>
</div>
[...]

And of course, we'll have define event.flashData in messages.en and any other locale you support.

conf/messages.en

event.flashData=(demo application, data will be flashed every 30 minutes)

Commiting your changes and deploying to heroku

Tip: with the command git status you can see the changed files

git status
# On branch master
# Your branch is ahead of 'origin/master' by 11 commits.
#
# Changes not staged for commit:
#   (use "git add <file>..." to update what will be committed)
#   (use "git checkout -- <file>..." to discard changes in working directory)
#
#	modified:   app/lib/jobs/BootstrapJob.java
#	modified:   app/views/Application/list.html
#	modified:   app/views/partials/topbar.html
#	modified:   conf/application.conf
#	modified:   conf/messages
#	modified:   conf/messages.en
#	modified:   conf/messages.es

Stage every changed file and commit commit.

git add .
git commit -m "troubleshooted heroku, flash data every 30 min"

And now deploy.

git push heroku master

And that's it, now your application is up and running in heroku.

More info at

http://devcenter.heroku.com/articles/play

http://toolbelt.herokuapp.com/linux/readme

http://www.jamesward.com/2011/08/29/getting-started-with-play-framework-on-heroku


Now we are going to Step 14 - deploy to gae.