- Cluster state is a value in an atom, the API calls are abstracted away
- Manage your resources from the comfort of your Clojure REPL
- Or, use it to build a Kubernetes abstraction...
This one time, when I had completed the setup already...
;; I started poking around...
(count @pods) ;; => 0
(count @replicationcontrollers) ;; => 0
(count @services) ;; => 0
;; and then I was all, like
(swap! pods assoc :my-awesome-podz
{:metadata
{:labels {:app "bunk" :special "false"} :namespace "playground" :name "my-awesome-podz"}
:spec
{:containers
[{:name
"two-peas"
:env
[{:name "REDIS_URL" :value "prolly.xwykgm.ng.0001.use1.cache.amazonaws.com:6379"}]
:ports
[{:containerPort 420, :protocol "TCP"}]
:image
"1337.dkr.ecr.us-east-1.amazonaws.com/two-peas:v1.0"}]
:restartPolicy "Always",
:terminationGracePeriodSeconds 30}})
;; (or if we're being optimistic, a new value for the pods atom)
;; and after a moment, a running pod:
(-> @pods
:my-awesome-podz
:status
:containerStatuses
first
:ready) ;; => true
;; and similarly for other resources...
;; the Clojure data matches the JSON of the k8s API
;; which is good, because there are no docs (yet)
- Kubernetes' API is an uncommonly good one, it's very consistent
- It bends the REST rules and supports streaming updates to a client
- given a
WATCH
request, it will keep the response body open, and continue to send lines of JSON
- On initialization,
keenest-rube
GET
s a (versioned) list of each kind of resource, initializes the atom, and then starts tailing aWATCH
request starting from that version
- this ensures that the atom is kept closely in sync with Kubernetes (few hundred ms, tops)
- this uses
aleph
behind the scenes
- Reading the state of the cluster is as easy as dereferencing it...
- The abstraction over mutations is provided by a "derived atom" from
lentes
for each kind of resource
- the
pods
atom in the demo, for example- the value in it is a map (by name) of all the pods in the namespace
- these atoms make an API call as a side-effect of an update (by diffing)
- You
swap!
the atom,keenest-rube
doesPUT
,DELETE
, andPOST
- All API errors throw, and the state of the atom is updated only using data from Kubernetes.
- Your own updates to it are more like suggestions...
- Only API responses from mutations, and the data from a
WATCH
stream, update the atom- Multiple resource mutations in a single
swap!
are explicitly disallowed
- Because you'll be wanting to see the error message if a mutation fails
👉 If you just want to try it out,
just clone this repo, launch a repl, and look at
dev/user.clj
If you want to use it in your own project, you'll want something to manage the state...
Supposing we want to use mount
and leiningen
:
In
project.clj
:dependencies [[keenest-rube "0.1.0-alpha0"]] ;; the "alpha" is for realz
:profiles {:dev {:dependencies [[mount "0.1.11"]]}}
and in
src/your/playground.clj
(ns your.playground
(:require
[mount.core :as mount :refer [defstate]]
[rube.core :as k]))
(defstate kube
:start (k/intern-resources ;; this interns a var for each k8s resource
(k/cluster
{:server "http://localhost:8080" ;; kubectl proxy (or whatever)
:namespace "playground"})) ;; not production! (yet)
:stop (k/disconnect! kube))
This is
alpha
for a reason. It has not been thoroughly tested, it may misbehave, and the API may change.
Use it at your own risk.
That being said, it is restricted to operating within a k8s namespace
so it should be mostly harmless...
Yes.
Fork it, send me a pull request!
https://github.com/nubank/clj-kubernetes-api
This library is quite different:
It's solid, much more low-level, providing Clojure helpers for each API call, generated from the official Swagger specs. It's also more complete than keenest-rube
, which is young and has limitations. There's no reason you can't use them side by side, though.
Also I found it to be a valuable resource, and borrowed a couple of helper functions, so with many thanks to the author(s) of that library, I'll call this a derivative work.