docker 2020-05-29

I'm in the process of migrating to version 1.0.1 of clj-docker-client and am having issues understanding how exec works now. I'm running this:

(docker/invoke
  client
  {:op               :ContainerExec
   :params           {:id         "couchbase"
                      :execConfig {:Cmd [,,,]}}})
Which returns an Id, but I don't think this has run anything in the container. This is the function that I used to have before upgrading:
(defn exec
  "Run a command inside a Docker container"
  [^DockerClient conn id cmdvec]
  (let [^ExecStartResultCallback callback (ExecStartResultCallback. System/out System/err)
        ^ExecCreateCmd exec-cmd           (-> conn
                                              (.execCreateCmd id)
                                              (.withAttachStdout true)
                                              (.withAttachStderr true)
                                              (.withCmd (into-array String cmdvec)))]
    (-> conn
        (.execStartCmd (.getId ^ExecCreateCmdResponse (.exec exec-cmd)))
        ^ExecStartResultCallback (.exec callback)
        .awaitCompletion)))

Ok, I think I've got it. The invoke above creates an Exec instance that must be executed, and the result is a stream that can be watched if desired. Here's the working function:

(defn exec
  "Run command inside docker container"
  [client image-id cmd]
  (let [instance    (docker/invoke client
                                   {:op     :ContainerExec
                                    :params {:id         image-id
                                             :execConfig {:AttachStdout true
                                                          :Cmd          cmd}}})
        exec-client (docker/client {:category :exec
                                    :conn     {:uri "unix:///var/run/docker.sock"}})]
    (docker/invoke exec-client
                   {:as     :stream
                    :op     :ExecStart
                    :params {:id              (:Id instance)
                             :execStartConfig {}}})))

I did have to create an :exec client, I don't know if there's a way around that. It is a bit odd that I can create the Exec instance with the :containers client, but not start it. ¯\(ツ)

@jvtrigueros yes the way youre doing it is correct. The reasoning is that clj-docker-client is now a very thin layer on top of docker's REST API, documented here https://docs.docker.com/engine/api/v1.40 To create the exec instance the api is /containers/{id}/exec hence uses the containers client: https://docs.docker.com/engine/api/v1.40/#operation/ContainerExec To start it the API is /exec/{id}/start: https://docs.docker.com/engine/api/v1.40/#operation/ExecStart

the categories are the prefixes in the API paths: /images, /containers, /_ping etc

Right, that's how I was able to piece it together. Thanks for following up!

you can have a look at the (maybe more more searchable) vendored swagger YAML: https://github.com/into-docker/clj-docker-client/blob/master/resources/clj_docker_client/api/latest.yaml

hopefully this simplification and reducing layers between you and docker helps! thats what allowed it to reach 1.0 too 😄

Yes, that does make it much much simpler. It's straightforward to make the connection. Although, I did have a question regarding (docker/doc ,,,)

For example:

(docker/doc client :ContainerExec)
=>
{:doc "Create an exec instance\nRun a command inside a running container.",
 :params ({:name "execConfig"} {:name "id", :type "string"})}

Id tells me it's a type string, but execConfig doesn't.

Going to the YAML file, I was able to see that it was an object type and was able to see which keys were needed, is there a different way to drill down?

yeah thats something ive been planning to work on, do a proper spec based info and data generation: https://github.com/into-docker/clj-docker-client/issues/15

this is the best it can for now unfortunately 😞

So if it's missing a type, it is implicitly object?

finding it hard to find time for this, some PRs would be really swiftly merged though! 😄

you can try to use REBL if you want

you can create a client and use REBL to drill down in the UI

better doc and help IMO

I'll have to give that a shot, I've yet to try out REBL 😛

you can very very minutely inspect the client thats created here 😄

Sorry it took me this long to get up to speed with the latest version, it's much much nicer now 🙂 No more dealing with Java interop, just data!

awesome! hope its useful

this is how REBL drilling down looks like 😄