In our last post we looked at how to add Perl as a runtime and framework to Cloud Foundry and in this post we examine some of the challenges raised by adding Smalltalk to Cloud Foundry. As part of exploring these issues, we set up a client Smalltalk environment that will be used to push an application to the cloud.

One of the reasons for selecting Perl for the previous demo was that it is reasonably well known and already present on both the client and the server. (Since Smalltalk is not already present, we will need to add it.) Another reason for selecting Perl for the previous demo is that it, like most programming environments, follows the Cloud Foundry model of separating application code from the runtime/framework. This is important when we consider what makes up an application that will be pushed from the developer’s (client) machine to the cloud (on the server).

Our first exposure to Cloud Foundry used a Ruby runtime and a Sinatra framework where a single file contained source code was copied to the server. In our subsequent example with Perl, the application was represented by a couple Perl files containing source code that was copied to the server. In each case the runtime and framework were in other directories, separate from our application on both the client and the server.

With Smalltalk, we (typically) work in an image that contains all the code. Further, when we use a one-click (such as for AidaWeb) we get a directory structure that contains not just the image, but also the runtime, and the total package can be large (over 150 MB for AidaWeb). While we could copy everything to the server, this could be expensive and doesn’t fit the Cloud Foundry model of separating the application, framework, and runtime. (Furthermore, looking ahead to supporting GemStone in Cloud Foundry, it would be even less appropriate to copy an entire repository to the server!)

With Smalltalk we have typically assume that image-based persistence is an option. That is, we can create objects, attach them to a persistent root (e.g., Smalltalk), and then save the image. When the image is restarted we have all the same objects available. This does not work so well in Cloud Foundry. To support scaling, Cloud Foundry allows multiple instances of an application to be started and with (non-GemStone) Smalltalk each instance will have its own image. Also, to support reliability, Cloud Foundry execution environments are somewhat transient. If one crashes (or becomes non-responsive) another is started, perhaps on another machine. For security reasons, access to the file system is intentionally limited. Thus, image persistence is not an option in Cloud Foundry.

While we may deal with persistence in a later blog post, we raise the issue of the image to make the point that we should try to think of the application as something separate from the image. As we shall see, this is not so difficult. In fact, a good Smalltalk engineering practice is to reload code into a fresh image regularly so that you can be sure that you can do so if you lose your development image. So, our goal is to create a directory containing only the application (i.e., code; not the runtime or framework) so that it can be pushed to the cloud and run elsewhere just as it runs locally.

For this exercise I have selected AidaWeb 6.4 as the framework and CogVM 3.9 as the runtime, though it should be relatively obvious through the process how to use other frameworks (including Seaside) and other runtimes (including Cincom Smalltalk or VA Smalltalk from Instantiations).

Sample Smalltalk Application

[Update:] If you don’t have an /opt directory, then create it:

sudo mkdir /opt; sudo chmod 777 /opt

We start by obtaining a CogVM runtime appropriate for the client (I’m on a MacBook Pro) from the INRIA site (scroll down to the Pharo-vm section) and unzipped it in /opt/smalltalk/cog:

mkdir /opt/smalltalk/ /opt/smalltalk/cog/
cd /opt/smalltalk/cog/
curl https://gforge.inria.fr/frs/download.php/29040/CogVM-Mac-13307.zip \
  > CogVM.zip
unzip CogVM.zip

Then we obtain an AidaWeb one-click package and unzipped it in /opt/smalltalk/aida:

mkdir /opt/smalltalk/aida/
cd /opt/smalltalk/aida/
curl http://ftp.eranova.si/aida/Aida6.4-OneClickPharo1.3-2jan12.zip \
  > Aida.zip
unzip Aida.zip

Next, we create the application environment:

mkdir ~/cloud/ ~/cloud/aida/ ~/cloud/aida/app/
cd ~/cloud/aida/
from=/opt/smalltalk/aida/AidaOneClickPharo.app/Contents/Resources
cp $from/AidaOneClickPharo.image ./Aida.image
cp $from/AidaOneClickPharo.changes ./Aida.changes
ln -s $from/PharoV10.sources .
chmod 400 ./Aida.image

Note that we changed the image to be read-only so that it ensures we operate in an environment similar to the cloud. We then created a directory, ~/cloud/aida/app, to hold the “application” that will get pushed to Cloud Foundry. The application directory needs to have at least one file, ~/cloud/aida/app/aida.st, and we start with the following:

Author fullName: 'CloudFoundry'.
WebDemoApp compile: 'introductionElement
  | e |
  e := WebElement new.
  e addText: self observee introduction.
  e addText: ''<p>Listening on port: '' , 
  session parent site port printString , ''</p>''.
  ^e'.

This file serves to tell VMC that the directory holds an AidaWeb application and it also contains code that will be evaluated each time the application starts. Here is where you would put the code to load your application code (I expect that it would involve a Metacello load, possibly of MCZ files that are in the local directory and thus automatically copied to the server). The edit I made to Aida is to tell us what port it is listening on. While this is relatively obvious when running on a local machine, it will not be so obvious when running in the cloud behind a router with a random port (discussed below).

Next we create a script to launch the application on the client, and then call the script:

echo '#! /bin/bash
open -a /opt/smalltalk/cog/CogVM.app/ --args \
  ~/cloud/aida/Aida.image \
  ~/cloud/aida/app/aida.st' > ~/cloud/aida/start
chmod 750 ~/cloud/aida/start
~/cloud/aida/start

We can then go to a web browser and enter the address http://localhost:8888 and see the application with our modification. After testing, quit the application. (You can select the Save and Quit menu and then look at the image and see that the timestamp has not changed, though the timestamp for the changes file will have changed.)

Listening Port for Cloud Foundry

The Cloud Foundry architecture provides that one machine may contain many application instances, and the instances are reachable through a router on an ephemeral port assigned at the time the instance is started. Thus, the application needs to be modified to listen on the proper port. Cloud Foundry puts the port in an environment variable ($VCAP_APP_PORT), and we would like to test this before we get to the cloud (where debugging might be more difficult). First, modify the start script at ~/cloud/aida/start to add another argument:

#! /bin/bash
open -a /opt/smalltalk/cog/CogVM.app/ --args \
  ~/cloud/aida/Aida.image \
  ~/cloud/aida/app/aida.st \
  "$VCAP_APP_PORT"

Then we modify ~/cloud/aida/app/aida.st to take the command line argument and change the port:

| portString |
portString := SmalltalkImage current getSystemAttribute: 3.
(portString isNil or: [portString isEmpty]) 
  ifTrue: [portString := '8888'].
AIDASite default 
  stop;
  port: portString asNumber;
  start.
Author fullName: 'CloudFoundry'.
WebDemoApp compile: 'introductionElement
  | e |
  e := WebElement new.
  e addText: self observee introduction.
  e addText: ''<p>Listening on port: '' , 
    session parent site port printString , ''</p>''.
  ^e'.

With this change we can try different ports:

VCAP_APP_PORT="8889" ~/cloud/aida/start

We can tell with a web browser that the new port is being used.

Note that the changes we made in this section are simply to test changes we will make on the server. It is not necessary for developers to manage the port explicitly since we can do it on the server.

VMC Changes on the Client

Before we move to the cloud (coming in the next post), you should set up a client environment with VMC (as described here) with the following addition to lib/cli/frameworks.rb at line 9:

'Aida' => ['aida', { :mem => '128M', :description => 'AidaWeb Application'}

and the following addition starting at line 40:

# Aida
elsif File.exist?('aida.st')
  return Framework.lookup('Aida')

With these changes the client can recognize the AidaWeb application and apply the proper framework and runtime.

 Conclusion

We have set up a simple (but non-trivial) Smalltalk application that is ready to be deployed to the cloud. Setting up the cloud to accept this Smalltalk application will be the next step!