In my previous post I described the process of build a private cloud using the “experimental” script provide on github. Part of the reason for using the newer script is that the older script is broken. Another reason is that the newer script seems to be the preferred direction. On the other hand, the new script creates a rather different environment from the old script, and most of the instructions and blog posts deal with the traditional environment. Because of this, I went back to the old setup and have been able to add a runtime and framework. This blog post will describe that effort (and a subsequent one will describe how to work in the new environment).

Cloud Foundry comes with support for a few “frameworks” (including Spring, Rails, Sinatra, Node.js, and Grails). Each framework is based on a “runtime” (including Java, Ruby 1.8, and Ruby 1.9). I found a brief description of how to add a new framework and a helpful write-up by someone who added Haskell & Happstack. Both of these seem to be focused on the old environment, and I will describe my experience in that environment before moving on to the “experimental” environment.

A Trivial Perl Application

While my eventual goal is to add Smalltalk, I wanted to start with a runtime based on executables that already exists in Cloud Foundry and selected Perl. (As will probably be evident, I’m not particularly strong in Perl and Ruby, so the following could probably be done better. You are welcome to add advice in the comments!) The first step was to build a Perl web application and I found one here. On my Mac (the “client” with respect to the cloud), I created a new directory (~/cloud/perl), and created three new files. The first, webserver.pl, is a copy of René Nyffenegger’s file with one change at line 77 to avoid an endless loop if there is nothing available on the port:

while (defined $request_line && $request_line ne "\r\n") {

The second, http_handler.pl, is modeled on René’s code (with help from Alvin Alexander), and follows:


# from http://www.adp-gmbh.ch/perl/webserver/index.html with modifications
sub http_request_handler {
  my $fh = shift;
  my $req_ = shift;
  my %req = %$req_;
  my %header = %{$req{HEADER}};
  print $fh "HTTP/1.0 200 OK\r\n";
  print $fh "Server: adp perl webserver\r\n";
  #print $fh "content-length: ... \r\n";
  print $fh "\r\n";
  print $fh "<html><h1>hello at ";
  print $fh scalar localtime();
  print $fh "</h1></html>";
  print $fh "Method: $req{METHOD}<br>";
  print $fh "Object: $req{OBJECT}<br>";
  foreach my $r (keys %header) {
    print $fh $r, " = ", $header{$r} , "<br>";
  }
 # inspired by http://www.devdaily.com/perl/edu/articles/pl020001.shtml
  print $fh "<hr />";
  foreach $key (sort keys(%ENV)) {
    print $fh "$key = $ENV{$key}<br>";
  }
}
sub init_webserver_extension {
  $port_listen = $ENV{"VCAP_APP_PORT"} || 8888;
}
1;

The third file, main.pl, is one line:

require "webserver.pl"

With these three files in a directory, we can run the application with the following shell command:

perl main.pl

Open a web browser and navigate to http://localhost:8888/ to see the application in operation, then in the client shell use <Ctrl>+<C> to stop the application. Our goal is to run this application on Cloud Foundry.

Changes to VMC on the Client

In order to push our Perl application we need to have VMC recognize it as a valid framework and this will require changes to the code in frameworks.rb. You can find the existing file with the following:

gem contents vmc | grep frameworks.rb

While we can modify the VMC gem directly, I believe that the proper way to do this is to rebuild the gem, starting with a checkout in an appropriate directory (e.g., ~/cloud/):

cd ~/cloud
git clone https://github.com/cloudfoundry/vmc.git
cd vmc
git checkout -b addPerl
bundle install
bundle exec rake build

Now we edit lib/cli/frameworks.rb by inserting the following to the FRAMEWORKS at line 9:

'Perl'    => ['perl',  { :mem => '128M', :description => 'Perl Application'}],

Then we insert the following detection code staring at line 40 (after the Rails detection):

# Perl
elsif File.exist?('main.pl')
 return Framework.lookup('Perl')

If you are modifying the installed gem directly, then you are done. Otherwise, you need to build and install the gem. To be extra proper, I modified lib/cli/version.rb to add ‘perl.1’ to the version number. Then, I entered the following shell commands:

gem build vmc.gemspec
sudo gem install -f vmc-0.3.16.beta.2.perl.1.gem

This concludes the changes made on the client. As before, we need to log in to the server and in a server shell start VCAP (this is slightly different because we are using the traditional environment):

cd cloudfoundry/vcap
bin/vcap start
bin/vcap status

Then from the client we set up a tunnel to the cloud, set the target, create a user, and verify the info (described here in Targeting the Micro Cloud).

Changes to VCAP on the Server

There are three files on the server that need to be modified and two files that need to be created (one in a new directory). First, we modify the following:

vim ~/cloudfoundry/vcap/cloud_controller/app/models/app.rb

We want to modify lines 26-27 to add perl5 as a runtime and perl as a framework:

Runtimes = %w[perl5 ruby18 ruby19 java node php erlangR14B02 python26]
Frameworks = %w[perl sinatra rails3 java_web spring grails node php otp_rebar lift wsgi django unknown]

Then we add three lines to normalize_legacy_staging_strings (starting at 541):

when "perl/1.0"
  self.framework = 'perl'
  self.runtime = 'perl5'

Then we modify the following:

vim ~/cloudfoundry/vcap/cloud_controller/config/cloud_controller.yml

We add two lines starting at 147:

perl5:
 version: 5.10

Then we modify the following:

vim ~/cloudfoundry/vcap/dea/config/dea.yml

We add the following six lines starting at line 44:

perl5:
  executable: /usr/bin/perl
  version: 5.10
  version_flag: '-v' 
  additional_checks: 
  environment:

Next we create a new file:

vim ~/cloudfoundry/vcap/staging/lib/vcap/staging/plugin/manifests/perl.yml

The contents of the file are modeled on the node.yml file:

---
name: "perl"
runtimes:
  - perl5:
      version: '5.10'
      description: 'Perl 5'
      executable: perl
      default: true
app_servers:
detection:
  - "*.pl": '.'
staged_services:

Then we need a new directory:

mkdir ~/cloudfoundry/vcap/staging/lib/vcap/staging/plugin/perl/

Then we create a new file in that directory:

vim ~/cloudfoundry/vcap/staging/lib/vcap/staging/plugin/perl/plugin.rb

The contents of that file are as follows:

class PerlPlugin < StagingPlugin
  def framework
    'perl'
  end
  def stage_application
    Dir.chdir(destination_directory) do
      create_app_directories
      copy_source_files
      create_startup_script
      create_stop_script
    end
  end
  def start_command
    cmds = []
    cmds << "/usr/bin/perl main.pl"
    cmds.join("\n")
  end
  private
  def startup_script
    vars = environment_hash
    generate_startup_script(vars) do
      "#no perl setup code needed"
    end
  end
  def stop_script
    vars = environment_hash
    generate_stop_script(vars)
  end
end

With this file, we have our changes ready to be built and installed:

cd ~/cloudfoundry/vcap/staging
rake build
gem install vcap-staging
gem contents vcap_staging | grep perl

Finally, we restart the cloud to pick up our changes:

cd ~/cloudfoundry/vcap/
bin/vcap restart

At this point we return to a client shell and see that Perl is a recognized framework:

vmc info
vmc frameworks
vmc runtimes

In the application directory on the client we can push the application to the server:

cd ~/cloud/perl
vmc push

We name the app ‘perl-env’ and otherwise accept the defaults. In a web browser, navigate to http://perl-env.vcap.me/ and observe the application in operation. At this point you should be able to try the various commands listed at the end of this post.

Next we will investigate using an environment built using the new “experimental” script.