Code Monkey home page Code Monkey logo

k8s-api's Introduction

kubernetes-api

Clojars Project

kubernetes-api is a Clojure library that acts as a kubernetes client

Motivation

We had a good experience with cognitect-labs/aws-api, and missed something like that for Kubernetes API. We had some client libraries that generated a lot of code, but it lacked discoverability and documentation.

clojure.deps

{:deps {nubank/k8s-api {:mvn/version "1.0.0"}}}

Leiningen

[nubank/k8s-api "1.0.0"]
;; In your ns statement
(ns my.ns
  (:require [kubernetes-api.core :as k8s]))

Usage

Instantiate a client

There're multiple options for authentication while instantiating a client. You can explicit set a token:

(def k8s (k8s/client "http://some.host" {:token "..."}))

Or a function that returns the token

(def k8s (k8s/client "http://some.host" {:token-fn (constantly "...")}))

You can also define client certificates

(def k8s (k8s/client "http://some.host" {:ca-cert     "/some/path/ca-docker.crt"
                                         :client-cert "/some/path/client-cert.pem"
                                         :client-key  "/some/path/client-java.key"}))

OpenAPI config

Discovery

It's possible but NOT RECOMMENDED to disable the OpenAPI specification discovery. This will prevent requests to /openapi/... endpoints and use the specification from the resources folder. This specification has no guarantees in terms of versioning, so it will be outdated.

(def k8s (k8s/client "http://some.host" {:token "..."
                                         :openapi {:discovery :disabled}}))
Filter paths from api

You can filter the paths from the OpenAPI specification. This is useful when you want to use a specific version of the api, or when you want to use a specific group of resources.

(def k8s (k8s/client "http://some.host" {:token "..."
                                         :apis  ["some.api/v1alpha1", "another.api"]}))

Discover

You can list all operations with

(k8s/explore k8s)

or specify a specific entity

(k8s/explore k8s :Deployment)
;=>
[:Deployment
 [:get "read the specified Deployment"]
 [:update "replace the specified Deployment"]
 [:delete "delete a Deployment"]
 [:patch "partially update the specified Deployment"]
 [:list "list or watch objects of kind Deployment"]
 [:create "create a Deployment"]
 [:deletecollection "delete collection of Deployment"]
 [:list-all "list or watch objects of kind Deployment"]]

get info on an operation

(k8s/info k8s {:kind :Deployment
               :action :create})
;=>
{:summary "create a Deployment",
 :parameters {:namespace java.lang.String,
              #schema.core.OptionalKey{:k :pretty} (maybe Str),
              #schema.core.OptionalKey{:k :dry-run} (maybe Str),
              #schema.core.OptionalKey{:k :field-manager} (maybe Str),
              :body ...},
 :returns {200 {#schema.core.OptionalKey{:k :apiVersion} (maybe Str),
                #schema.core.OptionalKey{:k :kind} (maybe Str),
                #schema.core.OptionalKey{:k :metadata} ...,
                #schema.core.OptionalKey{:k :spec} ...,
                #schema.core.OptionalKey{:k :status} ...},
           201 {#schema.core.OptionalKey{:k :apiVersion} (maybe Str),
                #schema.core.OptionalKey{:k :kind} (maybe Str),
                #schema.core.OptionalKey{:k :metadata} ...,
                #schema.core.OptionalKey{:k :spec} ...,
                #schema.core.OptionalKey{:k :status} ...},
           202 {#schema.core.OptionalKey{:k :apiVersion} (maybe Str),
                #schema.core.OptionalKey{:k :kind} (maybe Str),
                #schema.core.OptionalKey{:k :metadata} ...,
                #schema.core.OptionalKey{:k :spec} ...,
                #schema.core.OptionalKey{:k :status} ...},
           401 Any}}

Invoke

You can call an operation with

(k8s/invoke k8s {:kind    :ConfigMap
                 :action  :create
                 :request {:namespace "default"
                           :body      {:api-version "v1"
                                       :data       {"foo" "bar"}}}})

invoke it will return the body, with :request and :response in metadata

(meta (k8s/invoke ...))
;=>
{:request ...
 :response {:status ...
            :body ...}}

You can debug the request map with

(k8s/request k8s {:kind    :ConfigMap
                  :action  :create
                  :request {:namespace "default"
                            :body      {:api-version "v1"
                                        :data       {"foo" "bar"}}}})

If you want to read more about all different patch strategies Kubernetes offers, check this document

k8s-api's People

Contributors

franpilot avatar mateusduboli avatar rafaeleal avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

k8s-api's Issues

Automatically obtain hosts and tokens from `~/.kube/config`

I've been struggling to obtain the correct hosts and (decoded) tokens from the ~/.kube/config directory on my developer machine to use this library for a few days. This has become a bigger challenge than I anticipated, as my organization uses Azure CLI with certificates to obtain these tokens for several environments via Azure Active Directory/SSO with 2FA.

Upon researching, I discovered that the official Kubernetes clients support automatically loading config files from ~/.kube/config. Here is the relevant code from their Java client, and for their JavaScript client. In particular, loadFromDetault (link) or loadFromCluster (link) seem to be the relevant functions from the JavaScript client.

The Clojure library kube-api (link) also contains an implementation in kubeconfig.clj (link) and auth.clj (link).

Another reference that might be useful is the K9s codebase (here is their config-related Go package).

Would it be interesting to have a similar feature implemented in k8s-api? This could make it easier for people with their requisite hosts and tokens in their ~/.kube/config directory to use the library with less hassle.

One possible implementation might be to use the official Kubernetes Java client to avoid porting all that code to Clojure. However, creating that dependency and using Java interop might not be desirable.

`kubernetes-api.internals.client/find-preferred-route` behaves not as expected on multiple match

kubernetes-api.core APIs uses internals.client/find-preferred-route for finding the route to be called.

Now kubernetes-api.internals.client/find-preferred-route does

  1. Search routes with find-route which only uses :kind and :action fields, this could lead to multiple match
  2. Filter out Status routes (which related to #6 )
  3. For multiple matched route, choose the one route for which its version is the current preferred version. If multiple such routes meets the condition, just choose the first one.

I'm encountering that two route matches all the condition:

(require '[kubernetes-api.internals.client :refer [kind action all-namespaces-route?] :as internals.client])   

(->> (internals.client/find-route k8s {:kind :Ingress
                                       :action :patch/json-merge})
     (filter (fn [x] (not (clojure.string/ends-with? (name x) "Status"))))
     (map (fn [route]
            (filter #(and (= (:name %) (internals.client/group-of k8s route))
                        (= (:version (:preferredVersion %)) (internals.client/version-of k8s route)))
                  (internals.client/all-versions k8s)))))

;; ->

(({:name "networking.internal.knative.dev",
   :versions
   [{:groupVersion "networking.internal.knative.dev/v1alpha1",
     :version "v1alpha1"}],
   :preferredVersion
   {:groupVersion "networking.internal.knative.dev/v1alpha1",
    :version "v1alpha1"}})
 ({:name "networking.k8s.io",
   :versions [{:groupVersion "networking.k8s.io/v1", :version "v1"}],
   :preferredVersion {:groupVersion "networking.k8s.io/v1", :version "v1"}}))

And it chooses "networking.internal.knative.dev" which is not what I expected and it can not be opted out (though I can hack it around thanks to with-redefs).

It would be great if route search & preferring option can be customized.

Enable 'Status' routes

Status routes are currently explicitly disabled in kubernetes-api.internals.client/find-preferred-route. This prevents access to things like CronJobs/Status, but when I comment this out it seems to work just fine. Is there a reason for this explicit filtering out of routes ending in Status?

(defn find-preferred-route [k8s search-params]
  (->> (find-route k8s search-params)
       (filter (fn [x] (not (string/ends-with? (name x) "Status"))))
       ((partial choose-preffered-version k8s))))

Watch Support

Been playing with k8s-api for a while and I really like it.

However, I need watch support. Saw the docstring mentioning kubernetes/kubernetes#50857. Watch is the gateway to operators, and I am wondering how to get support in.

I also like http-kit in general but it appears to lack maintenance and it also appears to require some patching to get watch working. The next issue I encountered was lack of tls support with Java 11 in the stable version. Not super important at the moment but I'd expect graal support to be a problem as well.

Don't get me wrong: I am not saying we should be departing from http-kit immediately. I'd be fine using something else to watch for now. However, I'd also be willing to help getting support into k8s-api. ;)

Do you have any long term vision with regards to the http library/libraries to use in k8s-api?

Fix http ports coerced schema

Http ports should be allowed to be either the port number itself (integer) or a reference to a previously specified port number (string starting with a letter). Although the spec in the current schema is called int or string, the type is defaulting to a string. But passing the port number as a string is rejected by k8s api, as given a string it would expect it to start with a letter.

openapi v2 spec

curl -k https://staging-global-blue-kubernetes-api.nubank.com.br/openapi/v2 -H "Authorization: Bearer $(cat /tmp/token2)" | jq '.definitions["io.k8s.apimachinery.pkg.util.intstr.IntOrString"]'

{
  "description": "IntOrString is a type that can hold an int32 or a string.  When used in JSON or YAML marshalling and unmarshalling, it produces or consumes the inner type.  This allows you to have, for example, a JSON field that can accept a name or number.",
  "type": "string",
  "format": "int-or-string"
}

Opt out of key rewriting

It looks like this library does non-trivial key rewriting when (de)-serializing JSON: transforming strings to keywords, kebab-case, etc.

Could we opt out of this? When it comes to API wrappers, some users (myself included) favor straightforwardness over prettiness.

So rather than write:

{:kind    :Namespace
 :action  :list
 :request {:namespace "default"
           :body      {:api-version "v1"}}}

... I would much rather write:

{:kind    :Namespace
 :action  :list
 :request {"namespace" "default"
           "body"      {"apiVersion" "v1"}}}

thus enabling more straightforward transformation between Clojure expressions and, say, JSON files passed to kubectl.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.