Fork me on GitHub
#docker
<
2020-05-29
>
jvtrigueros15:05:08

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)))

jvtrigueros15:05:24

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 {}}})))

jvtrigueros15:05:33

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. ¯\(ツ)

lispyclouds16:05:36

@U0JAE119P 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

lispyclouds16:05:27

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

jvtrigueros16:05:35

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

lispyclouds17:05:09

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

jvtrigueros17:05:52

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

jvtrigueros17:05:02

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"})}

jvtrigueros17:05:20

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

jvtrigueros17:05:50

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?

lispyclouds17:05:51

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

lispyclouds17:05:07

this is the best it can for now unfortunately 😞

jvtrigueros17:05:40

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

lispyclouds17:05:42

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

lispyclouds17:05:35

you can try to use REBL if you want

lispyclouds17:05:58

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

lispyclouds17:05:11

better doc and help IMO

jvtrigueros17:05:24

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

lispyclouds17:05:07

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

jvtrigueros17:05:43

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!

lispyclouds17:05:49

awesome! hope its useful

lispyclouds17:05:10

this is how REBL drilling down looks like 😄