diff --git a/README.cjs b/README.cjs new file mode 100644 index 0000000..16f76b9 --- /dev/null +++ b/README.cjs @@ -0,0 +1,3 @@ +// call with `node =0.10.0" } }, + "node_modules/asap": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", + "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==", + "dev": true + }, "node_modules/assertion-error": { "version": "1.1.0", "dev": true, @@ -16213,6 +16226,40 @@ "url": "https://github.com/fb55/nth-check?sponsor=1" } }, + "node_modules/nunjucks": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/nunjucks/-/nunjucks-3.2.4.tgz", + "integrity": "sha512-26XRV6BhkgK0VOxfbU5cQI+ICFUtMLixv1noZn1tGU38kQH5A5nmmbk/O45xdyBhD1esk47nKrY0mvQpZIhRjQ==", + "dev": true, + "dependencies": { + "a-sync-waterfall": "^1.0.0", + "asap": "^2.0.3", + "commander": "^5.1.0" + }, + "bin": { + "nunjucks-precompile": "bin/precompile" + }, + "engines": { + "node": ">= 6.9.0" + }, + "peerDependencies": { + "chokidar": "^3.3.0" + }, + "peerDependenciesMeta": { + "chokidar": { + "optional": true + } + } + }, + "node_modules/nunjucks/node_modules/commander": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-5.1.0.tgz", + "integrity": "sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, "node_modules/nx": { "version": "15.9.2", "resolved": "https://registry.npmjs.org/nx/-/nx-15.9.2.tgz", diff --git a/package.json b/package.json index 49726b9..72649c8 100644 --- a/package.json +++ b/package.json @@ -12,6 +12,7 @@ "devDependencies": { "auto": "10.45.0", "lerna": "6.6.1", + "nunjucks": "3.2.4", "prettier": "2.8.7", "typescript": "5.0.4" }, diff --git a/packages/kubernetes-client/README.md b/packages/kubernetes-client/README.md new file mode 100644 index 0000000..ae807fc --- /dev/null +++ b/packages/kubernetes-client/README.md @@ -0,0 +1,147 @@ +# Kubernetes Client + +Query Kubernetes API from the browser. +Ideal for SPA-like apps and CRDs. + +## Features + +* Common CRUD operations on resources: + * `get` + * `list` + * `create` + * `update` + * `patch` + * `delete` + +* Support for most query parameters + * Excluding `watch` for now... + +* Generic types + * Some built-in, basic types available, growing as needed. + * Add your own types + +* Interface-first, includes default implementation using the Fetch API. +* Authentication with Kubernetes token (JWT). + +## Getting started + +Install the client +```bash +npm install @ccremer/kubernetes-client +``` + +Setup the client and make a request +```typescript +import { KubeClientBuilder } from '@ccremer/kubernetes-client/fetch' +import { SelfSubjectRulesReview } from '@ccremer/kubernetes-client/types/authorization.k8s.io' + +// token: +// a valid JWT for Kubernetes. +// This could come from a password-field, oauth service etc. +const token = '...' +// apiUrl: +// Either a CORS-enabled remote URL with https, +// or proxied by the webserver to the actual API. +const apiUrl = '/api' + +const client = KubeClientBuilder.DefaultClient(token, apiUrl) +client + .create({ + apiVersion: 'authorization.k8s.io/v1', + kind: 'SelfSubjectRulesReview', + spec: { + namespace: 'default', + }, + }) + .then((selfSubjectRulesReview) => { + console.debug('Created client with permissions', selfSubjectRulesReview.status) + }) + .catch((err) => { + console.error('could not fetch object', err) + }) +``` + +You can also provide your own resource type, as long as it fulfills the `KubeMeta` interface contract. + +```typescript +import { KubeObject } from '@ccremer/kubernetes-client/types/core' + +export interface MyCustomResource extends KubeObject { + apiVersion: 'customgroup/v1' + kind: 'CustomResource' + spec: { + field: string + } +} +``` + +ℹ️ The client doesn't set a default name or namespace when querying, so be sure you set the correct metadata. + +## Accessing Kubernetes API + +Because of CORS, the default implementation expects the Kubernetes API to be available at `/api` and `/apis`, proxied by whatever webserver you are running. +You can change the endpoint for it, e.g. set to `/kube/` (no trailing slash), so that the endpoints are concatenated to `/kube/api` and `/kube/apis`. + +Example configuration for Vite in Dev mode: +```typescript +import { defineConfig } from 'vite' +import 'dotenv/config' + +// https://vitejs.dev/config/ +export default defineConfig(() => { + return { + server: { + proxy: { + '/apis': { + target: process.env.VITE_KUBERNETES_API_URL, + changeOrigin: true, + secure: false, + }, + '/api': { + target: process.env.VITE_KUBERNETES_API_URL, + changeOrigin: true, + secure: false, + }, + }, + }, + } +}) +``` + +The `/api` endpoint is for core resources like `Namespace`s or `Pod`s, while `/apis` is for resources under a group like `apps` for `Deployment`s. + +Alternatively, you can set the endpoint to a full URL like `https://console.cluster.6443`. +However, keep CORS and other browser limitations in mind if your app is served under a different domain. + +## Extension points + +The default built-in client can be extended in various points. + +* The `KubeClientBuilder` accepts various interfaces that are injected into the `Client` class. +* Each API endpoint for a resource is generated based on metadata like `apiVersion` and `kind`. + Implement `UrlGenerator` interface to provide your own generator and supply it to the builder. +* Each HTTP request requires authorization. + Implement the `Authorizer` interface and supply your implementation to the builder. +* You can provide your own custom `fetch()`-like function to the builder. + +## Known Issues + +* The `Config` class returns a KubeConfig-like structure complete with cluster and user information. + However, currently only a variant with a JWT token is supported, created with `Config.FromToken()` combined with `DefaultAuthorizer`. +* `watch` operation isn't yet supported. +* There is no validation to the passed in payloads or returned results. +* Many Kubernetes resource types are missing, and they're not (yet?) generated from the Kubernetes API scheme. + Implement your own or better yet, contribute to this package :) + +## Production readiness + +This library is fairly new. +Expect breaking changes as new experience is gained. + +Other than that, this package follows SemVer. + +## Why + +There is an official [Kubernetes client](https://github.com/kubernetes-client/javascript). +However, it's not yet ready for browsers and development seems a bit slow. + diff --git a/packages/kubernetes-client/README.md.njk b/packages/kubernetes-client/README.md.njk new file mode 100644 index 0000000..f508097 --- /dev/null +++ b/packages/kubernetes-client/README.md.njk @@ -0,0 +1,91 @@ +# Kubernetes Client + +Query Kubernetes API from the browser. +Ideal for SPA-like apps and CRDs. + +## Features + +* Common CRUD operations on resources: + * `get` + * `list` + * `create` + * `update` + * `patch` + * `delete` + +* Support for most query parameters + * Excluding `watch` for now... + +* Generic types + * Some built-in, basic types available, growing as needed. + * Add your own types + +* Interface-first, includes default implementation using the Fetch API. +* Authentication with Kubernetes token (JWT). + +## Getting started + +Install the client +```bash +npm install @ccremer/kubernetes-client +``` + +Setup the client and make a request +```typescript +{% include "examples/getting-started.ts" -%} +``` + +You can also provide your own resource type, as long as it fulfills the `KubeMeta` interface contract. + +```typescript +{% include "examples/custom-type.ts" -%} +``` + +ℹ️ The client doesn't set a default name or namespace when querying, so be sure you set the correct metadata. + +## Accessing Kubernetes API + +Because of CORS, the default implementation expects the Kubernetes API to be available at `/api` and `/apis`, proxied by whatever webserver you are running. +You can change the endpoint for it, e.g. set to `/kube/` (no trailing slash), so that the endpoints are concatenated to `/kube/api` and `/kube/apis`. + +Example configuration for Vite in Dev mode: +```typescript +{% include "examples/vite.config.ts" -%} +``` + +The `/api` endpoint is for core resources like `Namespace`s or `Pod`s, while `/apis` is for resources under a group like `apps` for `Deployment`s. + +Alternatively, you can set the endpoint to a full URL like `https://console.cluster.6443`. +However, keep CORS and other browser limitations in mind if your app is served under a different domain. + +## Extension points + +The default built-in client can be extended in various points. + +* The `KubeClientBuilder` accepts various interfaces that are injected into the `Client` class. +* Each API endpoint for a resource is generated based on metadata like `apiVersion` and `kind`. + Implement `UrlGenerator` interface to provide your own generator and supply it to the builder. +* Each HTTP request requires authorization. + Implement the `Authorizer` interface and supply your implementation to the builder. +* You can provide your own custom `fetch()`-like function to the builder. + +## Known Issues + +* The `Config` class returns a KubeConfig-like structure complete with cluster and user information. + However, currently only a variant with a JWT token is supported, created with `Config.FromToken()` combined with `DefaultAuthorizer`. +* `watch` operation isn't yet supported. +* There is no validation to the passed in payloads or returned results. +* Many Kubernetes resource types are missing, and they're not (yet?) generated from the Kubernetes API scheme. + Implement your own or better yet, contribute to this package :) + +## Production readiness + +This library is fairly new. +Expect breaking changes as new experience is gained. + +Other than that, this package follows SemVer. + +## Why + +There is an official [Kubernetes client](https://github.com/kubernetes-client/javascript). +However, it's not yet ready for browsers and development seems a bit slow. diff --git a/packages/kubernetes-client/examples/custom-type.ts b/packages/kubernetes-client/examples/custom-type.ts new file mode 100644 index 0000000..9741ff3 --- /dev/null +++ b/packages/kubernetes-client/examples/custom-type.ts @@ -0,0 +1,9 @@ +import { KubeObject } from '@ccremer/kubernetes-client/types/core' + +export interface MyCustomResource extends KubeObject { + apiVersion: 'customgroup/v1' + kind: 'CustomResource' + spec: { + field: string + } +} diff --git a/packages/kubernetes-client/examples/getting-started.ts b/packages/kubernetes-client/examples/getting-started.ts new file mode 100644 index 0000000..5a095a5 --- /dev/null +++ b/packages/kubernetes-client/examples/getting-started.ts @@ -0,0 +1,27 @@ +import { KubeClientBuilder } from '@ccremer/kubernetes-client/fetch' +import { SelfSubjectRulesReview } from '@ccremer/kubernetes-client/types/authorization.k8s.io' + +// token: +// a valid JWT for Kubernetes. +// This could come from a password-field, oauth service etc. +const token = '...' +// apiUrl: +// Either a CORS-enabled remote URL with https, +// or proxied by the webserver to the actual API. +const apiUrl = '/api' + +const client = KubeClientBuilder.DefaultClient(token, apiUrl) +client + .create({ + apiVersion: 'authorization.k8s.io/v1', + kind: 'SelfSubjectRulesReview', + spec: { + namespace: 'default', + }, + }) + .then((selfSubjectRulesReview) => { + console.debug('Created client with permissions', selfSubjectRulesReview.status) + }) + .catch((err) => { + console.error('could not fetch object', err) + }) diff --git a/packages/kubernetes-client/examples/vite.config.ts b/packages/kubernetes-client/examples/vite.config.ts new file mode 100644 index 0000000..1da7413 --- /dev/null +++ b/packages/kubernetes-client/examples/vite.config.ts @@ -0,0 +1,22 @@ +import { defineConfig } from 'vite' +import 'dotenv/config' + +// https://vitejs.dev/config/ +export default defineConfig(() => { + return { + server: { + proxy: { + '/apis': { + target: process.env.VITE_KUBERNETES_API_URL, + changeOrigin: true, + secure: false, + }, + '/api': { + target: process.env.VITE_KUBERNETES_API_URL, + changeOrigin: true, + secure: false, + }, + }, + }, + } +}) diff --git a/packages/kubernetes-client/package.json b/packages/kubernetes-client/package.json index 6f47e00..ee72a7f 100644 --- a/packages/kubernetes-client/package.json +++ b/packages/kubernetes-client/package.json @@ -3,6 +3,7 @@ "version": "0.2.0", "description": "Generic Kubernetes client run in the Browser", "scripts": { + "preformat": "node ../../README.cjs README.md.njk > README.md", "format": "prettier --write ./**/*.{js,ts,json}", "lint": "eslint './**/*.{js,ts}'", "prelint": "npm run format",