         s = replace_all(s, "{#{k}}", "#{v}")
     #log('s = ', s)
     return s
+
+# split on whitespace
+split = (s) -> s.match(/\S+/g)
 
 run = (s, cb) ->
     log("running '#{s}'")

# Periodically check for idle running projects, and if so switch
# them from the run=true to run=false.
idle_timeout_check = () ->
    now = new Date()
    for project_id, project of projects
        if project.run and project.idle_timeout and now - project.last_edited >= project.idle_timeout * 1000*60

        for cb in w
            cb?(err)

# Watch output of "kubectl pod" for all projects; this gives us the ip addresses, and other pod-specific info
## TODO: DEAL with multiple pods for same deployment!!!
init_kubectl_pod_watch = (cb) ->
    headers = undefined
    pod_names = {}
    process = (line) ->
        v = split(line)
        if not v or v.length == 0
            return
        if line.indexOf('smc-project') == -1   # Headers
            headers = v
        else
            # New info

            # When changing things about a project, the pod output gives info both
            # about the newly creating pod and the old pod being destroyed, in potentially
            # random order.  So we only record info about the most recent one when
            # watching.  This is also why we sort by creationTimestamp (for the initial load).
            name = v[0]
            if not pod_names[project_id]?
                # initialize
                pod_names[project_id] = [name]
            else
                # already seen a pod with this project_id
                z = pod_names[project_id]
                if name in z
                    if z[z.length-1] != name
                        # already known but not most recent pod -- ignore
                        return
                else
                    # not known -- becomes new one we watch
                    z.push(name)

            # extract project_id from final labels column (properly and safely)
            project_id = project_id_from_labels(v[v.length-1])

            # update changed fields of projects[project_id].kubernetes in the database (and the projects object)
            x = projects[project_id] ?= {}
            x.kubernetes ?= {}
            k = {}
            for i in [0...v.length]
                k[headers[i]] = if v[i] == '<none>' then '' else v[i]
            update = undefined
            for field in ['READY', 'STATUS', 'RESTARTS', 'IP', 'NODE']
                field0 = field.toLowerCase()
                if x.kubernetes[field0] != k[field]
                    update ?= {}
                    update[field0] = k[field]
            if update?
                update_kubernetes_db_info(project_id, update)

    # Initialize
    args = 'get pods --sort-by=metadata.creationTimestamp -o wide --show-labels --selector=run=smc-project'
    run "kubectl #{args}", (err, output) ->
        if err
            cb?(err)
            return
        for line in output.split('\n')
            process(line)

        cb?()

        # Watch
        r = child_process.spawn('kubectl', split("#{args} --watch-only"))
        stdout = ''
        r.stdout.on 'data', (data) ->
            stdout += data.toString()
            while true
                i = stdout.indexOf('\n')
                if i == -1
                    break
                process(stdout.slice(0,i))
                stdout = stdout.slice(i+1)
        r.on 'exit', (code) ->
            log("kubectl watch pods terminated normally", code)
            init_kubectl_pod_watch()
        r.on 'error', (err) ->
            log("kubectl watch pods subprocess error", err)
            init_kubectl_pod_watch()

update_kubernetes_db_info = (project_id, update, cb) ->
    # TODO error handling
    rethinkdb.db(DATABASE).table('projects').get(project_id).update(kubernetes:update).run(conn, (err) -> cb?(err))

main = () ->
    async.series [connect_to_rethinkdb,
                  init_projects_changefeed,
                  init_kubectl_deployment_watch,
                  init_kubectl_pod_watch,
                  reconcile_all,
                  start_idle_timeout_monitor], (err) ->
        if err
