An idiomatic, data-driven, REPL friendly Clojure Docker client inspired from Cognitect's AWS client.
See this for documentation for versions before 0.4.0.
The README here is for the current master branch and may not reflect the released version.
Please raise issues here for any new feature requests!
Leiningen/Boot
[lispyclouds/clj-docker-client "1.0.3"]
Clojure CLI/deps.edn
{lispyclouds/clj-docker-client {:mvn/version "1.0.3"}}
Gradle
compile 'lispyclouds:clj-docker-client:1.0.3'
Maven
<dependency>
<groupId>lispyclouds</groupId>
<artifactId>clj-docker-client</artifactId>
<version>1.0.3</version>
</dependency>
Auto generated code docs can be found here
Since this is fully data driven, using Reveal is really beneficial as it allows us to walk through the output from Docker, see potential errors and be more productive with instant visual feedback.
lein with-profile +reveal repl
. This fires up the the Reveal UI alongside the usual REPL.This uses Docker's HTTP REST API to run. See the API version matrix to find the corresponding API version for the Docker daemon you're running.
clj-docker-client works by parsing the Swagger 2.0 YAMLs from the docker client API and vendors it in this directory. This defaults to using the latest version available there if no versions are pinned. It is recommended to use a pinned version to have consistent behavior across different engine versions.
See the page about the docker REST API to learn more about the usage and params to pass.
(require '[clj-docker-client.core :as docker])
This library aims to be a as thin layer as possible between you and Docker. This consists of following public functions:
Lists the categories of operations supported. Can be bound to an API version.
(docker/categories) ; Latest version
(docker/categories "v1.40") ; Locked to v1.40
#_=> #{:system
:exec
:images
:secrets
:events
:_ping
:containers
:auth
:tasks
:volumes
:networks
:build
:nodes
:commit
:plugins
:info
:swarm
:distribution
:version
:services
:configs
:session}
Connect to the docker daemon's UNIX socket andcreate a client scoped to the operations of a given category. Can be bound to an API version.
(def images (docker/client {:category :images
:conn {:uri "unix:///var/run/docker.sock"}})) ; Latest version
(def containers (docker/client {:category :containers
:conn {:uri "unix:///var/run/docker.sock"}
:api-version "v1.40"})) ; Container client for v1.40
Using a timeout for the connections. Thanks olymk2 for the suggestion.Docker actions can take quite a long time so set the timeout accordingly. When you don't provide timeoutsthen there will be no timeout on the client side.
(def ping (docker/client {:category :_ping
:conn {:uri "unix:///var/run/docker.sock"
:timeouts {:connect-timeout 10
:read-timeout 30000
:write-timeout 30000
:call-timeout 30000}}}))
Alternatively if connecting to a remote docker daemon over TCP supply the :uri
as http://your.docker.host:2376
. NOTE: unix://
, http://
, tcp://
and https://
are the currently supported protocols.
Lists the supported ops by a client.
(docker/ops images)
#_=> (:ImageList
:ImageCreate
:ImageInspect
:ImageHistory
:ImagePush
:ImageTag
:ImageDelete
:ImageSearch
:ImagePrune
:ImageGet
:ImageGetAll
:ImageLoad)
Returns the doc of an operation in a client.
(docker/doc images :ImageList)
#_=> {:doc
"List Images\nReturns a list of images on the server. Note that it uses a different, smaller representation of an image than inspecting a single image.",
:params
({:name "all", :type "boolean"}
{:name "filters", :type "string"}
{:name "digests", :type "boolean"})}
Invokes an operation via the client and a given operation map and returns the result data.
; Pulls the busybox:musl image from Docker hub
(docker/invoke images {:op :ImageCreate
:params {:fromImage "busybox:musl"}})
; Creates a container named conny from it
(docker/invoke containers {:op :ContainerCreate
:params {:name "conny"
:body {:Image "busybox:musl"
:Cmd "ls"}}})
The operation map is of the following structure:
{:op :NameOfOp
:params {:param-1 "value1"
:param-2 true}}
Takes an optional key as
. Defaults to :data
. Returns an InputStream if passed as :stream
, the raw underlying network socket if passed as :socket
. :stream
is useful for streaming responses like logs, events etc, which run till the container is up. :socket
is useful for events when bidirectional streams are returned by docker in operations like :ContainerAttach
.
{:op :NameOfOp
:params {:param-1 "value1"
:param-2 true}
:as :stream}
Takes another optional key :throw-exception?
. Defaults to false
. If set to true will throw an exception for exceptional status codes from the Docker API i.e. status >= 400
. Throws an java.lang.RuntimeException
with the message.
{:op :NameOfOp
:throw-exception? true}
v1.40
API docs.stream
is mapped to java.io.InputStream
and when the API needs a stream as an input, send an InputStream. When it returns a stream, the call can possibly block till the container or source is up and it's recommended to pass the as
param as :stream
to the invoke call and read it asynchronously. See this section for more info.(def images (docker/client {:category :images
:conn {:uri "unix:///var/run/docker.sock"}}))
(docker/invoke images {:op :ImageCreate
:params {:fromImage "busybox:musl"}})
(def containers (docker/client {:category :containers
:conn {:uri "unix:///var/run/docker.sock"}}))
(docker/invoke containers {:op :ContainerCreate
:params {:name "conny"
:body {:Image "busybox:musl"
:Cmd ["sh"
"-c"
"i=1; while :; do echo $i; sleep 1; i=$((i+1)); done"]}}})
(docker/invoke containers {:op :ContainerStart
:params {:id "conny"}})
(def networks (docker/client {:category :networks
:conn {:uri "unix:///var/run/docker.sock"}
:api-version "v1.40"}))
(docker/invoke networks {:op :NetworkCreate
:params {:networkConfig {:Name "conny-network"}}})
; fn to react when data is available
(defn react-to-stream
[stream reaction-fn]
(future
(with-open [rdr (clojure.java.io/reader stream)]
(loop [r (java.io.BufferedReader. rdr)]
(when-let [line (.readLine r)]
(reaction-fn line)
(recur r))))))
(def log-stream (docker/invoke containers {:op :ContainerLogs
:params {:id "conny"
:follow true
:stdout true}
:as :stream}))
(react-to-stream log-stream println) ; prints the logs line by line when they come.
;; This is a raw bidirectional java.net.Socket, so both reads and writes are possible.
;; conny-reader has been started with: docker run -d -i --name conny-reader alpine:latest sh -c "cat - >/out"
(def sock (docker/invoke containers {:op :ContainerAttach
:params {:id "conny-reader"
:stream true
:stdin true}
:as :socket}))
(clojure.java.io/copy "hello" (.getOutputStream sock))
(.close sock) ; Important for freeing up resources.
Thanks @AustinC for this example.
(ns dclj.core
(:require [clj-docker-client.core :as d]
[cheshire.core :as json])
(:import [java.util Base64]))
(defn b64-encode
[to-encode]
(.encodeToString (Base64/getEncoder) (.getBytes to-encode)))
(def auth
(-> {"username" "un"
"password" "pw"
"serveraddress" "docker.acme.com"}
json/encode
b64-encode))
(def images
(d/client {:category :images
:conn {:uri "unix:///var/run/docker.sock"}
:api-version "v1.40"}))
(d/invoke images
{:op :ImageCreate
:params {:fromImage "docker.acme.com/eg:2.1.995"
:X-Registry-Auth auth}
:throw-exception? true})
Since both https and unix sockets are suppported, and generally docker deamons exposed over HTTPS are protected via mTLS, here is an example using mTLS to connect to docker via HTTPS:
;; Create a client using https
;; The ca.pem, key.pem and cert.pem are produced by the docker daemon when protected via mTLS
(def http-tls-ping
(client {:category :_ping
:conn {:uri "https://my.remote.docker.host:8000"
:mtls {:ca "ca.pem"
:key "key.pem"
:cert "cert.pem"}}}))
(invoke http-tls-ping {:op :SystemPing}) ;; => Returns "OK"
The caveat here is password protected PEM files aren't supported yet. Please raise an issue if there is a need for it.
There are some cases where you may need access to an API that is either experimental or is not in the swagger docs.Docker checkpoint is one such example. Thanks @mk for bringing it up!
Since this uses the published APIs from the swagger spec, the way to access them is to use the lower level fn fetch
from the clj-docker-client/requests
ns. The caveat is the response will be totally raw(data, stream or the socket itself).
fetch takes the following params as a map:
:get
./v1.40/containers/{id}/checkpoints
. Pass {:id "conny"}
here.invoke
. Default: :data
.(require '[clj-docker-client.requests :as req])
(require '[clj-docker-client.core :as docker])
;; This is the undocumented API in the Docker Daemon.
;; See https://github.com/moby/moby/pull/22049/files#diff-8038ade87553e3a654366edca850f83dR11
(req/fetch {:conn (req/connect* {:uri "unix:///var/run/docker.sock"})
:url "/v1.40/containers/conny/checkpoints"})
More examples of low level calls:
;; Ping the server
(req/fetch {:conn (req/connect* {:uri "unix:///var/run/docker.sock"})
:url "/v1.40/_ping"})
;; Copy a folder to a container
(req/fetch {:conn (req/connect* {:uri "unix:///var/run/docker.sock"})
:url "/v1.40/containers/conny/archive"
:method :put
:query {:path "/root/src"}
:body (-> "src.tar.gz"
io/file
io/input-stream)})
When :throw-exception?
is passed as true
and the :as
is set to :stream
, to read the response stream, pass throw-entire-message?
as true
to the invoke. The stream is available as :body
in the ex-data of the exception.
(try
(invoke containers
{:op :ContainerArchive
:params {:id "conny"
:path "/this-does-not-exist"}
:as :stream
:throw-exception? true
:throw-entire-message? true})
(catch Exception e
(-> e ex-data :body slurp println))) ; Prints out the body of error from docker.
And anything else is possible!
Copyright © 2020 Rahul De and contributors.
Distributed under the LGPLv3+ License. See LICENSE.
Docker之Harbor 一、Harbor简介 1、什么是Harbor? Harbor 是VMware 公司开源的企业级Docker Registry 项目,其目标是帮助用户迅速搭建一个企业级的Docker Registry服务。 Harbor以Docker 公司开源的Registry 为基础,提供了图形管理UI、基于角色的访问控制(Role Based AccessControl)、AD/LD
docker编排 首先创建基础的文件: [root@localhost lamp]# tree ├── apache │ ├── authorized_keys │ ├── Dockerfile │ └── run.sh ├── conf │ └── my.cnf ├── docker-compose.yml ├── log │ ├── apache │ └── mysq
一、构建SSH镜像 ① 创建镜像目录方便管理 mkdir /opt/sshd cd /opt/sshd ② 创建编写dockerfile文件 vim Dockerfile #第一行必须指明基于的基础镜像 FROM centos:7 #作者信息 MAINTAINER this is ssh image <clj> #镜像的操作指令 RUN yum -y update RUN yum -y inst
graphql-clj A Clojure library designed to provide GraphQL implementation. Demo Demo Project with GraphiQL What's new in version 0.2 Simplified APIs Rewrite schema and query validator for simplicity an
clj-ml A machine learning library for Clojure built on top of Weka and friends. This library (specifically, some dependencies) requires Java 1.7+. Installation Installing from Clojars [cc.artifice/clj
Current Status (January 16, 2020) After developing and using this Clojure wrapper for AWS CDK for the pastsix months, we've decided to use TypeScript and AWS' library directly. We arediscontinuing mai
clj-http 是一个新的 Clojure HTTP 客户端开发包,主要特点是设计简单、可靠、可扩展和可测试。 示例代码: (require '[clj-http.client :as client])(client/get "http://rest-test.heroku.com/")=> {:status 200 :headers {"date" "Sun, 01 Aug 2010 0
我最近开始学习Clojure,想知道是否有一种执行简单. clj文件的标准方法。 我已经安装了Leiningen,并创建了我的第一个项目称为我的东西使用lein新应用程序我的东西。 管理我的东西。core,我从lein run开始: 接下来,我尝试了lein repl,接着是: 我还使用lein repl进行了一些基本评估: 我试着在我的东西里定义这个函数。核心: 我得到以下错误:clojure.
我遵循这里的说明,并试图通过调用lein jar从clj源生成一个Java类。 但是,当我稍微编辑代码以添加自己的测试函数时: .. 然后用lein jar生成一个Java类文件(我在文章末尾追加了project.clj),我发现生成的jar包含作为内部类的方法: 那是一些。示例类只包含main方法,不包含foo: 所以问题是:我们如何指定一个clj Clojure文件来生成一个Java类,该类包