Search code examples
cloud-foundryruncgarden-runc

Execute process in Runc container via Garden API


I have a Cloud Foundry Bosh Lite install with Garden/Runc support running on my local dev machine with an app hosted on it. I can ssh into the Diego cell VM and execute

cell_z1/0# runc exec 5f9c8b67-9170-4c53-4bab-bbb2e6a3acdf /usr/bin/printenv

This will produce the following output:

INSTANCE_GUID=5f9c8b67-9170-4c53-4bab-bbb2e6a3acdf
INSTANCE_INDEX=0
CF_INSTANCE_GUID=5f9c8b67-9170-4c53-4bab-bbb2e6a3acdf
CF_INSTANCE_INDEX=0
LANG=en_US.UTF-8
CF_INSTANCE_CERT=/etc/cf-instance-credentials/instance.crt
CF_INSTANCE_KEY=/etc/cf-instance-credentials/instance.key
HOME=/root

I assumed I could achieve the same through the Garden API by invoking

cell_z1/0# curl -X POST -d '{"path":"/usr/bin/printenv"}' localhost:7777/containers/5f9c8b67-9170-4c53-4bab-bbb2e6a3acdf/processes

However, this will return

{"Type":"","Message":"EOF","Handle":""}

which unfortunately tells me nothing. I tried adding "user":"vcap" in the JSON payload but the result is the same. When I add -H "Content-Type: application/json" -d I get

curl: (56) Problem (2) in the Chunked-Encoded data

Question: how can I execute an arbitrary command inside a container via the Garden API and retrieve it's output?


Solution

  • Go Library

    The supported way to execute commands within a Garden container is via the Go client library. For example:

    gdnClient := client.New(connection.New("tcp", "127.0.0.1:7777"))
    
    container, err := gdnClient.Create(garden.ContainerSpec{})
    if err != nil {
      os.Exit(1)
    }
    
    buffer := &bytes.Buffer{}
    process, err := container.Run(garden.ProcessSpec{
      Path: "/usr/bin/printenv",
    }, garden.ProcessIO{
      Stdout: buffer,
      Stderr: buffer,
    })
    if err != nil {
      os.Exit(1)
    }
    
    exitCode, err := process.Wait()
    if err != nil {
      os.Exit(1)
    }
    
    fmt.Printf("Exit code: %d, Process output %s", exitCode, buffer.String())
    

    Gaol CLI

    If you would prefer not to write any Go code you can use the unsupported (but very useful) Gaol CLI. For example:

    gaol create -n myContainer
    gaol run -c "/usr/bin/printenv" myContainer
    

    Ensure that you set your GAOL_TARGET appropriately e.g. 10.244.16.6:7777 on bosh-lite if you are running from your dev machine.

    HTTP Clients

    Finally, it is technically feasible to work with the entire Garden API through any HTTP client, but the supported client/server libraries do some connection hijacking which might confuse things. In particular, running a process is the most complicated API interaction.

    Firstly, a request is made to the run endpoint and then the connection is hijacked. The garden client starts streaming stdio for the process by using the hijacked connection for stdin and hitting two other endpoints.

    It shouldn't be too hard to get a process running in a container, requiring only a json encoded garden.ProcessSpec in the request body but streaming the output is quite a bit trickier.