The Cloud Foundry Blog

Using Cloud Foundry to Create the #ContributingCode Competition App

A situational application generally has a short lifespan and sidesteps formal requirements so that it can be developed quickly with a narrow focus. Cloud-based application development and deployment platforms lend themselves very well for such applications. This blog post will demonstrate a situational application that I developed and guide you to use cloud platform services to aid in your web development.

This app was built to register participants for a VMware Service Foundation pilot project called #ContributingCode, a program that involves an intense coding competition for students that is set to begin July 13, 2012. While there are several existing web applications available to do basic registration functionality, my custom application had additional requirements to provide:

  • Seamless user experience without having to navigate across sites
  • Flexibility in the registration set up
  • A highly customizable user interface

I developed this app to be very generic so it can be used for other events by just changing the content and CSS as required.

Requirements

A web application for handling registrations where users can create teams or join existing ones. Specifically, the owner of the team has the ability to:

  • Add or delete members on the team
  • Provide the ability for members to leave a team
  • Send email notifications for registration, add requests and any modification to the team
  • Send team announcements and view team summary using an admin panel

Application Architecture

Application Design

My application architecture called for an array of data services to be accessible by a core and standalone worker app. I considered using an IaaS like Amazon EC2, but realized I would have to take a raw Ubuntu Amazon Machine Image (AMI) and install Ruby, Rails, MySQL, Redis, MongoDB and then configure all of them to communicate with both the core and standalone worker application. Noting the amount of time, effort and possibility of errors involved in that approach, I chose to use Cloud Foundry PaaS. All the above steps could either be avoided or performed within seconds. Having such a platform with an array of data services and frameworks, supporting auto reconfiguration, and an NGNIX web server made my job far faster and easier.

The application is a Rails 3.2.6 app using MySQL for storing data, Mongo GridFS to store images, SendGrid for email delivery and Redis for Resque operations. There is a main app and a standalone worker app to perform the Resque operations in the background. The applications run on CloudFoundry.com and are bound to MySQL and Redis service instances provided by CloudFoundry.com. Using the services provided by CloudFoundry.com means that we can leverage Cloud Foundry’s ability to auto-reconfigure service bindings automatically.The reasoning behind this and the broader system environment is explained below.

Choice of Application Framework

The Rails framework includes everything I needed to create a database-backed web application using the Model-View-Controller (MVC) pattern. Also the ActiveRecord, the base for the models in a Rails application provides database independence, basic CRUD functionality, advanced query capabilities, and the ability to relate models to one another. ActionMailer is a component of Rails which provides a framework for email services. I also leverage a number of existing Ruby gems for this web application to perform various tasks.

Background Processing

A standalone app executes background tasks using Redis and Resque to email the users. This was primarily done to improve the user experience by sending the emails lazily in the background and responding to post requests as soon as the database entries have been made. For example, while registering as a new user, we need to send in a confirmation email which delays the response that the server has to send to the client as it takes a while to send the email. So when the job of sending an email is queued to Redis and later processed by the worker app the response is instantaneous. To learn how to run Resque workers on Cloud Foundry refer to this blog and there is more on Resque and Redis here.

Communications

We use the SendGrid email delivery system to deliver the email. There is clear documentation on how to use SendGrid with Rails here. In order to make the emails richer for our customization needs, we used HTML layouts. The notifier.rb file shows how we can include the layout in the email.

 
def register_email(name, email) 
  @name = name mail( :to => email, :subject => "Thank you for registering for contributingcode", :template_path => 'mailer', :template_name => 'register' ) 
end

Data Storage

Relational persistance made the most sense for most of the data on this application. Each logical model, and operations on the model, are well defined. The models for this app are quite straightforward; User, Team, Member (which associates the users to the teams they belong to), JoinRequest, and AddRequest. The MySQL database was picked for its popularity. Additionally, we thought that a team avatar option would make the app more user friendly, so we extended the storage services to include MongoDB to use GridFS. The carrierwave gem for uploading the images and storing them with MongoLab and GridFS using the mongoid gem. Setting this up requires reading environment variables on application startup. This can be accomplished by creating a file in the initializer folder as seen here.

Single Sign On via GitHub

Github can be used for Single Sign On into apps using OAuth2 and REST APIs. OAuth2 is a protocol that lets third party clients or apps request authorization to protected resources, such as a user’s profile data, in a particular service without exchanging sensitive usernames and passwords. Instead, it uses interactions via the browser to obtain a code which is then exchanged for an access token and can be revoked by the service at any point in time. Think of the access token as a valet key which gives access to a subset of features. This is preferred over Basic Authentication, because tokens can be limited to specific types of data and can be revoked by users at any time. Specifically, we used omniauth-github gem for authentication through Github. This reduces the burden of having a custom login system. More on OAuth2 can be found here. Github was used for authentication as the participants of our code competition are required to have an account to submit the code to Github. More importantly, the authentication is done by Github and the app does not store any passwords. This is where we may encounter a drawback to OAuth2, which is the unavailability of the service you use for authentication, e.g., Github in our case, will in turn affect the users’ ability to log into your app.

User Experience

The use of modal box and having a single page app improves the user experience as the user is never more than two clicks away from the target section.

Twitter Bootstrap was used to enhance the look and feel by utilizing the type-ahead feature for autocomplete functionality and carousel was used to display the teams and contributors. The front-end form validator, native jquery plugin, validationhelper is written  as a wrapper around validate.js  to validate forms in the frontend with the help of Twitter Bootstrap. This was made as a separate open source code and can be used irrespective of the backend framework. Using front end validation reduces unwanted server requests. For example, the email entered while registering can be validated before the user registration form is submitted to the server.

Deploying the App on CloudFoundry.com

Deployment was the easiest step of all and took just seconds to push the app to production. Here are the steps you can follow to push this app to Cloud Foundry using your credentials. Fork the project.

git clone git@github.com:<your_name>/contributingcode.git contributingcode
cd contributingcode
bundle install;bundle package

Before precompiling the assets make sure mongo is running in the local and set the following environmental variables.

export mysql_pwd='your mysql password'
export github_client_id= 'github client id'
export github_client_secret= 'github client secret'
rake assets:precompile

Start the Core app.

$ vmc push --runtime ruby19 --nostart
Would you like to deploy from the current directory? [Yn]: y
Application Name: contributingcode
Detected a Rails Application, is this correct? [Yn]: y
Application Deployed URL [contributingcod.cloudfoundry.com]: y
Memory reservation (128M, 256M, 512M, 1G, 2G) [256M]: 256M
How many instances? [1]: 1
Create services to bind to 'contributingcode'? [yN]: y
1: mongodb
2: mysql
3: postgresql
4: rabbitmq
5: redis
What kind of service?: 2
Specify the name of the service [mysql-3e25d]: mydb
Create another? [yN]: y
1: mongodb
2: mysql
3: postgresql
4: rabbitmq
5: redis
What kind of service?: 5
Specify the name of the service [redis-2a4c2]: myque
Create another? [yN]: n
Would you like to save this configuration? [yN]: y
Manifest written to manifest.yml.
Creating Application: OK
Creating Service [mydb]: OK
Binding Service [mydb]: OK
Creating Service [myque]: OK
Binding Service [myque]: OK
Uploading Application:
  Checking for available resources: OK
  Processing resources: OK
  Packing application: OK
  Uploading (558K): OK   
Push Status: OK

Start the worker app. Rename the manifest file if you have one.

vmc push ccworker  --nostart
Would you like to deploy from the current directory? [Yn]: y
Detected a Rails Application, is this correct? [Yn]: n
1: Rails
2: Spring
3: Grails
4: Lift
5: JavaWeb
6: Standalone
7: Sinatra
8: Node
9: Rack
10: Play
Select Application Type: 6
Selected Standalone Application
1: java
2: node
3: node06
4: ruby18
5: ruby19
Select Runtime [ruby18]: 5
Selected ruby19
Start Command: bundle exec rake VERBOSE=true QUEUE="*" resque:work    
Application Deployed URL [None]: 
Memory reservation (128M, 256M, 512M, 1G, 2G) [128M]: 128M
How many instances? [1]: 1
Bind existing services to 'ccworker'? [yN]: y
1: mydb
2: myque
Which one?: 1
Bind another? [yN]: y
1: mydb
2: myque
Which one?: 2
Create services to bind to 'ccworker'? [yN]: n
Would you like to save this configuration? [yN]: y
Manifest written to manifest.yml.
Creating Application: OK
Binding Service [mydb]: OK
Binding Service [myque]: OK
Uploading Application:
  Checking for available resources: OK
  Processing resources: OK
  Packing application: OK
  Uploading (39K): OK   
Push Status: OK

Set the configuration. Set the Github and Mongolab credentials for core app.

vmc env-add contributingcode github_client_id= 'github client id'
vmc env-add contributingcode github_client_secret= 'github client secret'
vmc env-add contributingcode mongodb_host= 'host'
vmc env-add contributingcode mongodb_port= 'port'
vmc env-add contributingcode mongodb_username= 'username'
vmc env-add contributingcode mongodb_password= 'password'
vmc env-add contributingcode mongodb_db= 'db name'

Similarly, set the environmental variables to the worker app too.

The standalone worker app requires the same environment as the core app and thus all the environment variables should be set for the worker app as well. In addition, set the SendGrid credentials.

vmc env-add ccworker github_client_id= 'github client id'
vmc env-add ccworker github_client_secret= 'github client secret'
vmc env-add ccworker mongodb_host= 'host'
vmc env-add ccworker mongodb_port= 'port'
vmc env-add ccworker mongodb_username= 'username'
vmc env-add ccworker mongodb_password= 'password'
vmc env-add ccworker mongodb_db= 'db name'
vmc env-add ccworker sendgrid_username= 'sendgrid username'
vmc env-add ccworker sendgrid_password= 'sendgrid_password'

While running on localhost set mysql_pwd as follows:

export mysql_pwd='mysql password'

Finally to start your app on CloudFoundry.com.

vmc app start contributingcode
vmc app start ccworker

Visit contributingcode.cloudfoundry.com to view my  web application. Your application should look similar, but as the core app and worker app names are subject to availability, your app name will be different from ‘contributingcode’.

To get the app running, fork the app from here.

The Right Tools For the Job

The bootstrapping of the app was done in one day with the basic registration system in place. Relying on some expert website mockups, I had all the functionality and HTML in place in just a couple of weeks. With some further testing and fine tuning, the app was easily deployed to CloudFoundry.com and open for registrations. While, the website needed several updates with new content and sections (e.g. FAQ’s) it was all done with barely any downtime. Using a single Cloud Foundry command “vmc update” reduced the overhead in updating the website in multiple iterations to one step. This confirmed that we had made the right choice in using a PaaS rather than standing up our own infrastructure. This registration app has registered over 100 students, helping them to connect with each other and learn about the theme of the competition. I hope this blog helps you jumpstart your own web application development as well.

– Magizharasu Thirunavukkarasu, Developer Advocate Intern

Don’t have a Cloud Foundry account yet?  Sign up for free today

This entry was posted in CloudFoundry. Bookmark the permalink.

4 Responses to Using Cloud Foundry to Create the #ContributingCode Competition App

  1. Matt says:

    Great work Magizh! The app (and blog) are quite impressive. I think your contribution to the Service Foundation will be felt for years to come. It’s heartwarming to see how well you have done in your short time at VMware.

  2. Mahilis says:

    Thanks Matt :) The finale is on July 28th. Expecting some real good apps for social good.

  3. Adalarasu says:

    great job

  4. Pingback: VMware Intern Spotlight: Magizharasu Thirunavukkarasu – Cloud Foundry | VMware Careers Blog - VMware Blogs

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title="" rel=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>