Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/jgaskins/kubernetes
Kubernetes API client in Crystal, providing a framework for writing controllers/operators
https://github.com/jgaskins/kubernetes
kubernetes
Last synced: 2 months ago
JSON representation
Kubernetes API client in Crystal, providing a framework for writing controllers/operators
- Host: GitHub
- URL: https://github.com/jgaskins/kubernetes
- Owner: jgaskins
- License: mit
- Created: 2021-08-17T18:36:48.000Z (over 3 years ago)
- Default Branch: main
- Last Pushed: 2024-05-10T15:38:29.000Z (8 months ago)
- Last Synced: 2024-10-25T01:40:16.048Z (2 months ago)
- Topics: kubernetes
- Language: Crystal
- Homepage:
- Size: 77.1 KB
- Stars: 16
- Watchers: 2
- Forks: 4
- Open Issues: 1
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# Kubernetes
A Kubernetes client that allows you to manage Kubernetes resources programmatically, similarly to how you might with `kubectl`.
## Installation
Add this to your `shards.yml`:
```yaml
dependencies:
kubernetes:
github: jgaskins/kubernetes
```## Usage
First, instantiate your Kubernetes client:
```crystal
require "kubernetes"k8s = Kubernetes::Client.new(
server: URI.parse("https://#{ENV["KUBERNETES_SERVICE_HOST"]}:#{ENV["KUBERNETES_SERVICE_PORT"]}"),
token: File.read("/var/run/secrets/kubernetes.io/serviceaccount/token"),
certificate_file: "/var/run/secrets/kubernetes.io/serviceaccount/ca.crt",
)
```When you're running this inside a Kubernetes cluster, it will automatically find the Kubernetes API server, token, and cert file, so you can simplify even more:
```crystal
require "kubernetes"k8s = Kubernetes::Client.new
```Then you can fetch information about your deployments or pods:
```crystal
pp k8s.deployments
pp k8s.pods
```### `CustomResourceDefinition`s
You can import a CRD directly from YAML. Let's say you have this CRD (taken from [the example in the Kubernetes CRD docs](https://kubernetes.io/docs/tasks/extend-kubernetes/custom-resources/custom-resource-definitions/)):
```yaml
# k8s/crd-crontab.yaml
---
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
name: crontabs.stable.example.com
spec:
group: stable.example.com
versions:
- name: v1
served: true
storage: true
schema:
openAPIV3Schema:
type: object
properties:
spec:
type: object
properties:
cronSpec:
type: string
image:
type: string
replicas:
type: integer
scope: Namespaced
names:
plural: crontabs
singular: crontab
kind: CronTab
shortNames:
- ct
```When you run `Kubernetes.import_crd("k8s/crd-crontab.yaml")`, the CRD will provide the following:
1. A `CronTab` struct
- Represents the `spec` of the top-level `CronTab` resource in Kubernetes
- The top-level resource is a `Kubernetes::Resource(CronTab)`
- Has the following `getter` methods:
- `cron_spec : String`
- `image : String`
- `replicas : Int64`
2. The following methods added to `Kubernetes::Client`:
- `crontabs(namespace : String? = nil) : Array(Kubernetes::Resource(CronTab))`
- When `namespace` is `nil`, _all_ `CronTab`s are returned
- `crontab(name : String, namespace : String = "default") : Kubernetes::Resource(CronTab)`
- `apply_crontab(api_version : String, kind : String, metadata, spec, force = false)`
- Allows you to specify a complete Kubernetes manifest programmatically
- On success, returns a `Kubernetes::Resource(CronTab)` representing the applied configuration
- On failure, returns a `Kubernetes::Status` with information about the failure
- `delete_crontab(name : String, namespace : String)`
- `watch_crontabs(namespace : String, &on_change : Kubernetes::Watch(Kubernetes::Resource(CronTab)) ->)`
- Allows you to define controllers that respond to changes in your custom resources### Building a Controller
Kubernetes controllers respond to changes in resources, often by making changes to other resources. For example, you might deploy a service that needs the following:
- `Deployment` for a web app
- `Service` to direct requests to that web app
- `Ingress` to bring web requests to that serviceAll of these things can be configured with a `ServiceApp` CRD. Let's say we have the following CRD:
```yaml
# k8s/crd-serviceapp.yaml
---
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
name: serviceapps.example.com
spec:
group: example.com
versions:
- name: v1
served: true
storage: true
schema:
openAPIV3Schema:
type: object
properties:
spec:
type: object
properties:
domain:
type: string
command:
type: array
items:
type: string
minItems: 1
image:
type: string
replicas:
type: integer
scope: Namespaced
names:
plural: serviceapps
singular: serviceapp
kind: ServiceApp
shortNames:
- svcapp
```And whenever someone creates, updates, or deletes a `ServiceApp` resource, it needs to create, update, or delete the `Deployment`, `Service`, and `Ingress` resources accordingly:
```crystal
require "kubernetes"Kubernetes.import_crd "k8s/crd-serviceapps.yaml"
log = Log.for("serviceapps.controller", level: :info)k8s = Kubernetes::Client.new(
server: URI.parse(ENV["K8S"]),
token: ENV["TOKEN"],
certificate_file: ENV["CA_CERT"],
)k8s.watch_serviceapps do |watch|
svcapp = watch.objectcase watch
when .added?, .modified?
log.info { "ServiceApp #{svcapp.metadata.namespace}/#{svcapp.metadata.name} updated" }
labels = {app: svcapp.metadata.name}
metadata = {
name: svcapp.metadata.name,
namespace: svcapp.metadata.namespace,
}deployment = k8s.apply_deployment(
api_version: "apps/v1",
kind: "Deployment",
metadata: metadata,
spec: {
replicas: svcapp.spec.replicas,
selector: {matchLabels: labels},
template: {
metadata: {labels: labels},
spec: {
containers: [
{
image: svcapp.spec.image,
command: svcapp.spec.command,
name: "web",
env: [
{ name: "VAR_NAME", value: "var value" },
# ...
],
},
],
},
},
},
)
log.info { "Deployment #{deployment} applied" }svc = k8s.apply_service(
api_version: "v1",
kind: "Service",
metadata: metadata,
spec: {
type: "ClusterIP",
selector: labels,
ports: [{ port: 3000 }],
sessionAffinity: "None",
},
)
case svc
in Kubernetes::Service
log.info { "Service #{svc.metadata.namespace}/#{svc.metadata.name} applied" }
in Kubernetes::Status
log.info { "Service #{svcapp.metadata.namespace}/#{svcapp.metadata.name} could not be applied!" }
endingress = k8s.apply_ingress(
api_version: "networking.k8s.io/v1",
kind: "Ingress",
metadata: metadata,
spec: {
rules: [
{
host: svcapp.spec.domain,
http: {
paths: [
{
backend: {
service: {
name: metadata[:name],
port: { number: 3000 },
},
},
path: "/",
pathType: "Prefix",
},
],
},
},
],
},
)
case ingress
in Kubernetes::Resource(Kubernetes::Networking::Ingress)
log.info { "Ingress #{ingress.metadata.namespace}/#{ingress.metadata.name} applied" }
in Kubernetes::Status
log.info { "Ingress #{svcapp.metadata.namespace}/#{svcapp.metadata.name} could not be applied: #{ingress.inspect}" }
end
when .deleted?
name = svcapp.metadata.name
namespace = svcapp.metadata.namespacek8s.delete_ingress(name: name, namespace: namespace)
log.info { "Ingress #{namespace}/#{name} deleted" }
k8s.delete_service(name: name, namespace: namespace)
log.info { "Service #{namespace}/#{name} deleted" }
k8s.delete_deployment(name: name, namespace: namespace)
log.info { "Deployment #{namespace}/#{name} deleted" }
end
end
```## Contributing
1. Fork it ()
2. Create your feature branch (`git checkout -b my-new-feature`)
3. Commit your changes (`git commit -am 'Add some feature'`)
4. Push to the branch (`git push origin my-new-feature`)
5. Create a new Pull Request## Contributors
- [Jamie Gaskins](https://github.com/jgaskins) - creator and maintainer