Learning about docker this weekend… it always is hard to find resources for people who understand the concepts of VMs and containers and need to dive into something just a little bit complicated. I had been through lots of intro tutorials, when docker first arrived on the scene, and was seeking to set up a hosted dev instance for existing open source project which already had a docker set up.

Here’s an outline of the key concepts:

  • docker-machine: commands to create and manage VMs, whether locally or on a remote server
  • docker: commands to talk to your active VM that is already set up with docker-machine
  • docker-compose: a way to create and manage multiple containers on a docker-machine

As happens, I see parallels between human spoken language and new technical terms, which makes sense since these are things made by and for humans. The folks who made Docker invented a kind of language for us to talk to their software.

I felt like I was learning to read in a new language, like pig-latin, where words have a prefix of docker, like some kind of honorific

They use docker- to speak to VMs locally or remotely, and docker (without a dash) is an intimate form of communication with your active VM

Writing notes here, so I remember when I pick this up again. If there are Docker experts reading this, I’d be interested to know if I got this right and if there are other patterns or naming conventions that might help fast-track my learning of this new dialect for deploying apps in this land of containers and virtual machines.

Also, if a kind and generous soul wants to help an open source project, I’ve written up my work-in-progress steps for setting up OpenOpps-platform dev instance and would appreciate any advice, and of course, would welcome a pull request.

SailsJS is a NodeJS MVC framework that we use for the OpenOpps Platform. Sails has some basic testing docs, but it doesn’t explain how to set up the framework nicely where the database is dropped in between tests. I find myself always re-figuring out these patterns when I write experimental apps.

With an in-memory database, this bootstrap.test.js will drop the database between tests:

var sails = require('sails');

before(function(done) {
  sails.lift({
    // test config
    environment: 'test',
    port: 9999,   // so we can run the app and tests at the same time
    hostName: 'localhost:9999',
    connections: {
      testDB: {
        adapter: 'sails-memory'
      }
    },
    models: {
      connection: 'testDB'
    },
  }, function(err, server) {
    if (err) return done(err);
    done(err, sails);
  });
});

after(function(done) {
  sails.lower(done);
});

beforeEach(function(done) {
  // Drops database between each test.  This works because we use
  // the memory database
  sails.once('hook:orm:reloaded', done);
  sails.emit('hook:orm:reload');
});

When I need to use postgres, Waterline doesn’t expose SQL truncate which would be much faster, instead this bootstrap.test.js will destroy all the models:

var async = require('async');
var sails = require('sails');

before(function(done) {
  sails.lift({
    // test config
    environment: 'test',
    port: 9999,   // so we can run the app and tests at the same time
    hostName: 'localhost:9999',
    models: {
      connection: 'postgresql'
    },
  }, function(err, server) {
    if (err) return done(err);
    done(err, sails);
  });
});

afterEach(function(done) {
  destroyFuncs = [];
  for (modelName in sails.models) {
    destroyFuncs.push(function(callback) {
      sails.models[modelName].destroy({})
      .exec(function(err) {
        callback(null, err)
      });
    })
  }
  async.parallel(destroyFuncs, function(err, results) {
    done(err);
  })
});

after(function(done) {
  sails.lower(done);
})

Xcode playgrounds are a delightful way to explore the Swift language — once you get them set up. I admit that I’ve grown to love environments where I can code with simple text files, however, I also love to develop iOS apps and so I find myself digging once again thru the byzantine Xcode GUI. It seems I always forget the magic incantations!

Here are the notes for my future self and other intrepid explorers:

Experimenting with Parse.com in Swift 2, Xcode 7.2, OSX 10.10.5 (Yosemite)

note: cocoapods 0.39.0 has issue with Ruby 2.3 (https://github.com/CocoaPods/CocoaPods/issues/4345) — likely fixed in the 1.0 beta, but I stuck with older ruby and released cocoapod for this exercise.

rvm use ruby-2.2.4
gem install cocoapods

Make a new Xcode project, and add a new Empty file to your project called Podfile (New > File… > Other > Empty), with the following contents:

platform :ios, '8.1'
xcodeproj 'ParsePlayground'
 
use_frameworks!
target :ParsePlayground, :exclusive => true do
  pod 'Parse'
end

back on the command-line:

pod install

This creates a new Xcode workspace, so we need to close the project in Xcode, then re-open Xcode with the file named something ending in .xcworkspace.

This new workspace should have both the original project and a new one called Pods. Now we can create the Playground, by selecting the menu: File > New > Playground… Then we save it in the root of our project directory, then add it to the project with the menu: File > Add Files To “ParsePlayground”

Now this code should compile:

import Parse
import XCPlayground
XCPlaygroundPage.currentPage.needsIndefiniteExecution = true

We want to set needsIndefiniteExecution so that the async commands will work.

I made a new Parse app to experiment, then in the parse docs, in the QuickStart Guide, they set up some sample code already customized with the app ID and client key, it should be something like:

Parse.setApplicationId("abunchofnumbersandletters",
clientKey: "morelettersandnumbers")

Then we can start coding! I made an item class in Parse with a string property “info.” Here some code that worked for me:

let testObject = PFObject(className: "TestObject")
 
testObject["foo"] = "bar"
testObject.saveInBackgroundWithBlock { (success: Bool, error: NSError?) -> Void in
print("Object has been saved: (success)")
}
 
var query = PFQuery(className: "item")
var items = try query.findObjects()
print("got some items")
for item in items {
var info = item["info"]
print(" item #(item.objectId!) info:(info)")
}
 
print("not the end... wait for the sync call to finish")

When I run the above code it works fine, but I do get some of the following errors. Not sure why…

Failed to obtain sandbox extension for path=/var/folders/hz/vnvkxt7x4236fp6nqt5zy2wr0000gn/T/com.apple.dt.Xcode.pg/containers/com.apple.dt.playground.stub.iOS_Simulator.experiment-3C8D4B75-8EA0-4BE8-8467-1B06C9B1F881/Library/Caches

I’ve posted the full ParsePlayground project on Github with some notes on how to set up the Parse app if you want to jump right in!