Geoffroy Couprie is a consultant in software security and a independant developer. After testing Clever Cloud for a while, he challenged himself trying to run Smalltalk on our PaaS. Here's the recap of his findings.

The Smalltalk world offers a radically different way to create and run software, and I have always loved to tinker with it. Thanks to the recent work around Docker at Clever Cloud, it is now possible to run Pharo Smalltalk apps, even Seaside ones, on this PaaS.

Thinking in Smalltalk

One of the most interesting features of Smalltalk is the image, which is a file containing a list of serialized objects. When you start your virtual machine, you load the image. When you stop, you serialise everything: classes, methods, instances of classes, windows, even running web servers. And when you start, you get back your environment in the same state. Even writing code is different: you are not editing a file, you are editing the implementation of a method, serialised like everything else in the image. That way, you can modify code while it is running easily.

The traditional method for deploying a web server consists in preparing the image with the code, and uploading the whole image on the server. This is at odds with deployment process at Clever Cloud, based on a simple Git push. So it requires a few steps.

Docker

Thanks to the recent developments around Docker support, it is now possible to run any language or platform on these servers. So I prepared two images, one to install Pharo Smalltalk on an archlinux Docker container, and the other, based on the first, to install the Seaside web framework.

There is nothing tricky in the first image, except needing to install lib32-zlib, lib32-ncurses and lib32-bzip2 to run the 32 bits VM on a 64 bits CPU.

The second image runs a .st file to download Seaside, remove default Seaside apps and deactivate the debug bar:

Gofer new
url:'http://www.smalltalkhub.com/mc/Seaside/MetacelloConfigurations/main';
package: 'ConfigurationOfSeaside3';
load.
((Smalltalk at: #ConfigurationOfSeaside3) project version: #stable) load.

WAAdmin applicationDefaults removeParent: WADevelopmentConfiguration instance. WADispatcher default handlers keys do:[:name | WAAdmin unregister:name]. Smalltalk snapshot: true andQuit: true.

Now, we have an image ready for deployment, and usable for all the future Seaside projects.

Preparing the code for deployment

We could save the image in a git repository, and push the whole file to Clever Cloud, but that would be too easy. What if we could send just the required code through git? Here comes FileTree, a library pre-installed in Pharo, that you can use to export code from the image and add it to a Git repository.

First, let's get some code to deploy. I will spare you the details, just get the WebCounter example from the Seaside book, it is doable under 10 minutes.

Done? Alright. Choose a folder that will contain the code you will push to the server, and "git init" in that folder. Now open the Monticello browser, add a new repository, select "filetree://" then choose your git folder.

Now select your WebCounter package in the Monticello browser, and save the code.

It should now appear in file form in the git folder:

$ tree .
.
└── WebCounter.package
    ├── WebCounter.class
    │   ├── README.md
    │   ├── instance
    │   │   ├── decrease.st
    │   │   ├── increase.st
    │   │   ├── initialize.st
    │   │   └── renderContentOn..st
    │   ├── methodProperties.json
    │   └── properties.json
    ├── monticello.meta
    │   ├── categories.st
    │   ├── initializers.st
    │   ├── package
    │   └── version
    └── properties.json

Loading the code

Now that we have an export of the Seaside component, we will create a Docker image to load the code. First, we need a small script to give the virtual machine at startup, to load the code and run it:

"this uses FileTree to load back the code from the /home/deploy folder"
(MCFileTreeRepository new directory: '/home/deploy' asFileReference) packageDescriptionsFromReadableFileNames  do:
  [:name || version |
  version := (MCFileTreeRepository new directory: '/home/deploy' asFileReference) versionFromFileNamed: name first, '.package'.
  [version load ]
        on: MCMergeOrLoadWarning
        do: [ :ex | [ ex load  ] on: MCNoChangesException do: [] ] ].

FileStream stdout nextPutAll: 'WebCounter installed'; lf.

"the Clever Cloud platform expects the app to listen on the 8080 port" ZnZincServerAdaptor startOn: 8080.

"Register the code we just loaded on the /webcounter address" WAAdmin register: WebCounter asApplicationAt: 'webcounter'.

Save that code as seaside.st, then we will create the Dockerfile to make it work on the server:

# -- sh --
FROM geal/archlinux-seaside
MAINTAINER Geoffroy Couprie, contact@geoffroycouprie.com

ADD . /home/deploy

EXPOSE 8080

CMD ./pharo Pharo.image ./deploy/seaside.st

It reuses the archlinux-seaside image I prepared previously, copies the code from the repository in /home/deploy, then starts the image with the code loading script.

With this, you can build and run the image to test it:

$ docker build -t "geal/seaside-example" .
$ docker run -t -i -p 8080:8080 geal/seaside-example /bin/bash
[root@8e726df1bf4e home]# ./pharo Pharo.image ./deploy/seaside.st

UndefinedObject>>DoIt (WebCounter is Undeclared) WebCounter installed

If you are on Linux, the website will be available on http://localhost:8080/webcounter. If you use OS X, run "boot2docker ip", then access the server at http://:8080/webcounter.

Creating the app on Clever Cloud and deploying

Go to https://console.clever-cloud.com, create a new application of type Docker, then get the git URL for your app, and do in your repository:

$ git remote add clever git+ssh://git@push.par.clever-cloud.com/<your app id>.git
$ git push clever master
And that's it! You can now access the Seaside component at http://.cleverapps.io/webcounter .

At each new version of the code, export it to Git then push again, and the platform will redeploy everything automatically, rebuilding the Docker containers and the Pharo images. Now, you just need to add a few components to store state, like a database or S3 component, and you are good to go!

All the code is available on Github if you need to play with it:

blog comments powered by Disqus