We looked at deploying a trivial Smalltalk application to Cloud Foundry (here) and we looked at creating a Smalltalk application that uses MySQL for persistence (here). Now we combine the two so that we use MySQL in a Smalltalk application in Cloud Foundry.

Changes on the Client

Cloud Foundry has MySQL 5.1 built-in, and connection information is provided as a JSON document in an environment variable (VCAP_SERVICES). Our sample application already gets MySQL configuration information from this environment variable, so no change is needed to the Smalltalk code. We do, however, have a slight problem with the startup script. On the client we have set up a development directory as follows:

  • ~/cloud/aida/
    • Aida.image
    • Aida.changes
    • PharoV10.sources
    • start
    • app/
      • aida.st
      • *.mcz

When testing on the client we start the application using a shell script, ~/cloud/aida/start, and it launches Pharo and executes the Smalltalk code found in app/aida.st. We did this to isolate the “application” (the things that need to be pushed to Cloud Foundry) and keep it as small as possible. By contrast, the deployment directory on Cloud Foundry is structured as follows:

  • /var/vcap.loca/dea/apps/<name>-<instance #>-<random hex number>/
    • run.pid
    • startup (build by CF)
    • stop (build by CF)
    • logs/
    • app/
      • Aida.image (copied from /opt/smalltalk/aida/ on the server by CF)
      • Aida.changes (copied from /opt/smalltalk/aida/ on the serverby CF)
      • PharoV10.sources (copied from /opt/smalltalk/aida/ on the serverby CF)
      • main.st (built by CF)
      • staging.st (built by CF)
      • aida.st (copied from the client)
      • *.mcz (copied from the client)

Note that on the client the image is in the parent of aida.st while on the server the image is in the same directory as aida.st. This affects the default directory visible to Smalltalk and at present it is different on the client and on the server. Until now, this was not an issue, but our MySQL demo installs MCZ files from the app directory and the aida.st script we provided here includes lines like the following:

MczInstaller installFileNamed: 'app/BalanceDemo-JamesFoster.1.mcz'.

On the client this works fine where the default is the parent of app/, but on the server where the default is app/ this fails because it can’t find the file to install. Our goal, of course, is to have a client environment that is as close to the server as possible so that debugging is easier. We will fix this by modifying the client so that it changes the default directory to app/ during the startup process. On the client we have a bash script that sets up environment variables to simulate the server and then launch CogVM with a particular image and startup script. Previously we pointed to app/aida.st as the startup script. We will change our Bash startup script, start, so that we use a different Smalltalk startup script, start.st:

#! /bin/bash 
export VCAP_SERVICES='{
  "mysql-5.1":[ {
    "credentials":{
      "name":"test",
      "host":"localhost",
      "port":3306,
      "user":"jfoster",
      "password":"swordfish"
    }
  }
]}'
export VCAP_APP_PORT=8888
open -a /opt/smalltalk/cog/CogVM.app/ --args \
  ~/cloud/aida/Aida.image \
  ~/cloud/aida/start.st \
$VCAP_APP_PORT

Now we create the Smalltalk script:

| file contents |
FileDirectory setDefaultDirectory:
  (FileDirectory default entryAt: 'app')
  asFileDirectory fullName.
file := FileStream readOnlyFileNamed: 'aida.st'.
contents := file contentsOfEntireFile.
Compiler evaluate: contents.

This script changes the default directory and then evaluates aida.st (which is now in the default directory). We then modify aida.st so that it assumes that it is in the default directory by removing the ‘app/‘ portion of the file path:

Author fullName: 'CloudFoundry'. "Name for edits"
MczInstaller installFileNamed: 'JSON-ul.35.mcz'.
MczInstaller installFileNamed: 'Stdb-Core-ah.7.mcz'.
MczInstaller installFileNamed: 'Stdb-MysqlProtocol-ah.15.mcz'.
MczInstaller installFileNamed: 'Stdb-MysqlImpl-ah.9.mcz'.
MczInstaller installFileNamed: 'BalanceDemo-JamesFoster.1.mcz'.
MySqlBalanceApp register.
ImageBalanceApp register.

We can test the application by starting it on the client:

~/cloud/aida/start

We can navigate to http://localhost:8888/MySqlBalance and see that the application works.

Deploying to the Server

We can then try deploying the application to our private Cloud Foundry that is configured to accept Smalltalk applications (created here). On the client enter ‘vmc push‘ in a shell from the app directory:

pitcairn:app jfoster$ vmc push
Would you like to deploy from the current directory? [Yn]: 
Application Name: balance
Application Deployed URL [balance.vcap.me]: 
Detected a AidaWeb Application, is this correct? [Yn]: 
Memory reservation (128M, 256M, 512M, 1G, 2G) [128M]: 
How many instances? [1]: 
Create services to bind to 'balance'? [yN]: y
1: mongodb
2: mysql
3: neo4j
4: redis
What kind of service?: 2
Specify the name of the service [mysql-29b9]: balance-data
Create another? [yN]: 
Would you like to save this configuration? [yN]: 
Creating Application: OK
Creating Service [balance-data]: OK
Binding Service [balance-data]: OK
Uploading Application:
 Checking for available resources: OK
 Processing resources: OK
 Packing application: OK
 Uploading (0K): OK 
Push Status: OK
Staging Application 'balance': ..............................

In response to the questions, accept the defaults with the following exceptions: name the application ‘balance’; ask to create a service (y); select mysql (2) as the service; and name the service something meaningful (e.g., ‘balance-data’). Depending on the speed of your cloud, you might get a timeout during the staging or it might report that the application failed to start. In my experience things started and we can see that the balance app is using the balance-data service:

pitcairn:app jfoster$ vmc apps
+-------------+----+---------+-----------------+--------------+
| Application |  # | Health  |       URLS      |   Services   |
+-------------+----+---------+-----------------+--------------+
|   balance   |  1 | RUNNING | balance.vcap.me | balance-data |
+-------------+----+---------+-----------------+--------------+
pitcairn:app jfoster$ vmc services
============== System Services ==============
+---------+---------+-------------------------------+
| Service | Version |          Description          |
+---------+---------+-------------------------------+
| mongodb |   1.8   |      MongoDB NoSQL store      |
|  mysql  |   5.1   |    MySQL database service     |
|  neo4j  |   1.4   |       Neo4j NOSQL store       |
|  redis  |   2.2   | Redis key-value store service |
+---------+---------+-------------------------------+
=========== Provisioned Services ============
+--------------+---------+
|     Name     | Service |
+--------------+---------+
| balance-data |  mysql  |
+--------------+---------+
pitcairn:app jfoster$

Conclusion

We have deployed a Smalltalk (Aida/Cog) application that uses MySQL to Cloud Foundry. In this case we deployed only one instance because there is still some state being saved in the image and one instance is a trivial way to achieve session affinity. It seems that we cannot get to the balance application on Aida without logging in (using the default admin/password). When we have multiple instances, the web requests can go to any instance and the image that presented the login page might not be the same image that gets the login attempt. Of course, this problem would exist even more with (non-GemStone) Seaside, where session state is stored in the image and a redirect is even done between the callback and rendering.

Advertisements