-
-
Notifications
You must be signed in to change notification settings - Fork 165
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #7 from zalando-incubator/initial-import
Import the project as it is right now, with the squashed history
- Loading branch information
Showing
45 changed files
with
3,139 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
# Byte-compiled / optimized / DLL files | ||
__pycache__/ | ||
*.py[cod] | ||
|
||
# Distribution / packaging | ||
build/ | ||
dist/ | ||
.eggs/ | ||
*.egg-info/ | ||
*.egg | ||
|
||
# Unit test / coverage reports | ||
htmlcov/ | ||
.tox/ | ||
.coverage | ||
.coverage.* | ||
.cache | ||
.pytest_cache | ||
nosetests.xml | ||
coverage.xml | ||
junit.xml | ||
*.cover | ||
.hypothesis/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,144 @@ | ||
# Bootstrap the development environment | ||
|
||
## Minikube cluster | ||
|
||
To develop the framework and the operators in an isolated Kubernetes cluster, | ||
use [minikube](https://github.com/kubernetes/minikube): | ||
|
||
MacOS: | ||
|
||
```bash | ||
brew install docker-machine-driver-hyperkit | ||
sudo chown root:wheel /usr/local/opt/docker-machine-driver-hyperkit/bin/docker-machine-driver-hyperkit | ||
sudo chmod u+s /usr/local/opt/docker-machine-driver-hyperkit/bin/docker-machine-driver-hyperkit | ||
|
||
brew cask install minikube | ||
minikube config set vm-driver hyperkit | ||
``` | ||
|
||
Start the minikube cluster: | ||
|
||
```bash | ||
minikube start | ||
minikube dashboard | ||
``` | ||
|
||
It will automatically create and activate the kubectl context named `minikube`. | ||
If not, or if you have multiple clusters, activate it explicitly: | ||
|
||
```bash | ||
kubectl config get-contexts | ||
kubectl config current-context | ||
kubectl config use-context minikube | ||
``` | ||
|
||
|
||
## Cluster setup | ||
|
||
Apply the framework's peering resource definition (for neighbourhood awareness): | ||
|
||
```bash | ||
kubectl apply -f peering.yaml | ||
``` | ||
|
||
Apply the custom resource definitions of your application | ||
(here, we use an example application and resource): | ||
|
||
```bash | ||
kubectl apply -f examples/crd.yaml | ||
``` | ||
|
||
|
||
## Runtime setup | ||
|
||
Install the operator to your virtualenv in the editable mode | ||
(and all its dependencies): | ||
|
||
```bash | ||
pip install -e . | ||
kopf --help | ||
``` | ||
|
||
Run the operator in the background console/terminal tab: | ||
|
||
```bash | ||
kopf run examples/01-minimal/example.py --verbose | ||
``` | ||
|
||
Create and delete a sample object (just an example here). | ||
Observe how the operator reacts and prints the logs, | ||
and how the handling progress is reported on the object's events. | ||
|
||
```bash | ||
kubectl apply -f examples/obj.yaml | ||
kubectl describe -f examples/obj.yaml | ||
kubectl delete -f examples/obj.yaml | ||
``` | ||
|
||
## PyCharm & IDEs | ||
|
||
If you use PyCharm, create a Run/Debug Configuration as follows: | ||
|
||
* Mode: `module name` | ||
* Module name: `kopf` | ||
* Arguments: `run examples/01-minimal/example.py --verbose` | ||
* Python Interpreter: anything with Python>=3.7 | ||
|
||
Stop the console operator, and start the IDE debug session. | ||
Put a breakpoint in the used operator script on the first line of the function. | ||
Repeat the object creation, and ensure the IDE stops at the breakpoint. | ||
|
||
Congratulations! You are ready to develop and debug your own operator. | ||
|
||
|
||
## Real cluster | ||
|
||
**WARNING:** Running the operator against a real cluster can influence | ||
the real applications in the ways not expected by other team members. | ||
The dev-mode operator's logs will not be visible in the central loggging, | ||
as there are not sent there. Use the real clusters only when you have | ||
the strong reasons to do so, such as the system resource requests | ||
(CPU, RAM, PVC), which are not achievable in the minikube's VMs. | ||
|
||
**WARNING:** Running multiple operators for the same cluster without isolation | ||
can cause infinite loops, conflicting changes, and duplicated side effects | ||
(such as the children object creation, e.g. jobs, pods, etc). | ||
It is your responsibility to design the deployment in such a way that | ||
the operators do not collide. The framework helps by providing the `--peering` | ||
and `--namespace` CLI options, but does not prevent the mis-configurations. | ||
|
||
To run against the real cluster, use the dev-mode of the framework. | ||
This will set the operator's priority to 666 (just a high number), | ||
and will freeze all other running operators (the default priority is 0) | ||
for the runtime, so that they do not collide with each other: | ||
|
||
```bash | ||
kopf run examples/01-minimal/example.py --verbose --dev | ||
``` | ||
|
||
Alternatively, explicitly freeze/resume all other operators, | ||
and it will freeze them even if your operator is not running | ||
(e.g., for 2 hours): | ||
|
||
```bash | ||
kopf freeze --lifetime $((2*60*60)) | ||
kopf resume | ||
``` | ||
|
||
|
||
## Cleanup | ||
|
||
To cleanup the cluster from the framework-related objects: | ||
|
||
```bash | ||
kubectl delete -f peering.yaml | ||
kubectl delete -f examples/obj.yaml | ||
kubectl delete -f examples/crd.yaml | ||
``` | ||
|
||
For the minikube cleanup (to release the CPU/RAM/disk resources): | ||
|
||
```bash | ||
minikube stop | ||
minikube delete | ||
``` |
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,111 @@ | ||
# Kubernetes Operator Pythonic Framework (Kopf) | ||
|
||
**Kopf** -- "Kubernetes Operator Pythonic Framework" -- a framework and a library | ||
to make Kubernetes operators development easier, just in few lines of Python code. | ||
|
||
The main goal is to bring the Domain-Driven Design to the infrastructure level, | ||
with Kubernetes being an orchestrator/database of the domain objects (custom resources), | ||
and the operators containing the domain logic (with no or minimal infrastructure logic). | ||
|
||
|
||
## Features: | ||
|
||
* A full-featured operator in just 2 files: `Dockerfile` + a Python module. | ||
* Implicit object's status updates, as returned from the Python functions. | ||
* Multiple creation/update/deletion handlers to track the object handling process. | ||
* Update handlers for the selected fields with automatic value diffs. | ||
* Dynamically generated sub-handlers using the same handling tracking feature. | ||
* Retries of the handlers in case of failures or exceptions. | ||
* Easy object hierarchy building with the labels/naming propagation. | ||
* Built-in _events_ for the objects to reflect their state (as seen in `kubectl describe`). | ||
* Automatic logging/reporting of the handling process (as logs + _events_). | ||
* Handling of multiple CRDs in one process. | ||
* The development instance temporarily suppresses the deployed ones. | ||
|
||
Not yet, todos: | ||
|
||
* ~~Multiple operators are automatically synced to avoid double-processing.~~ | ||
* ~~CLI commands to investigate the status of the handled objects in the cluster.~~ | ||
|
||
|
||
## Examples | ||
|
||
See `./examples/` for the examples of the typical use-cases. | ||
|
||
The minimalistic operator can look like this: | ||
|
||
```python | ||
import kopf | ||
|
||
@kopf.on.create('zalando.org', 'v1', 'kopfexamples') | ||
def create_fn(spec, meta, status, **kwargs): | ||
print(f"And here we are! Creating: {spec}") | ||
``` | ||
|
||
The keyword arguments available to the handlers: | ||
|
||
* `body` for the whole body of the handled objects. | ||
* `spec` as an alias for `body['spec']`. | ||
* `meta` as an alias for `body['metadata']`. | ||
* `status` as an alias for `body['status']`. | ||
* `patch` is a dict with the object changes to applied after the handler. | ||
* `retry` (`int`) is the sequential number of retry of this handler. | ||
* `started` (`datetime.datetime`) is the start time of the handler, in case of retries & errors. | ||
* `runtime` (`datetime.timedelat`) is the duration of the handler run, in case of retries & errors. | ||
* `diff` is a list of changes of the object (only for the update events). | ||
* `old` is the old state of the object or a field (only for the update events). | ||
* `new` is the new state of the object or a field (only for the update events). | ||
* `logger` is a per-object logger, with the messages prefixed with the object's namespace/name. | ||
* `event` is the raw event as received from the Kubernetes API. | ||
* `cause` is the processed cause of the handler as detected by the framework (create/update/delete). | ||
|
||
`**kwargs` (or `**_` to stop lint warnings) is required for the forward | ||
compatibility: the framework can add new keyword arguments in the future, | ||
and the existing handlers should accept them. | ||
|
||
|
||
## Usage | ||
|
||
### In-cluster | ||
|
||
We assume that when the operator is executed in the cluster, it must be packaged | ||
into a docker image with CI/CD tool of your preference. | ||
|
||
```bash | ||
FROM python:3.7 | ||
ADD . /src | ||
RUN pip install kopf | ||
CMD kopf run handlers.py | ||
``` | ||
|
||
Where `handlers.py` is your Python script with the handlers | ||
(see `examples/*/example.py` for the examples). | ||
|
||
See `kopf run --help` for others ways of attaching the handlers. | ||
|
||
|
||
## Contributing | ||
|
||
Please read [CONTRIBUTING.md](CONTRIBUTING.md) for details | ||
on our process for submitting pull requests to us, and please ensure | ||
you follow the [CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md). | ||
|
||
To install the environment for the local development, | ||
read [DEVELOPMENT.md](DEVELOPMENT.md). | ||
|
||
|
||
## Versioning | ||
|
||
We use [SemVer](http://semver.org/) for versioning. For the versions available, | ||
see the [tags on this repository](https://github.com/zalando-incubator/kopf/tags). | ||
|
||
|
||
## License | ||
|
||
This project is licensed under the MIT License - see the [LICENSE.md](LICENSE.md) file for details | ||
|
||
|
||
## Acknowledgments | ||
|
||
* Thanks to [@side8](https://github.com/side8) and their [k8s-operator](https://github.com/side8/k8s-operator) | ||
for the inspiration. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
# Kopf minimal example | ||
|
||
The minimum codebase needed for to make a runnable Kubernetes operator. | ||
|
||
Start the operator: | ||
|
||
```bash | ||
kopf run example.py --verbose | ||
``` | ||
|
||
It does nothing useful, just notices the object creation, | ||
and prints the message to stdout -- can be seen in the operator's output. | ||
|
||
In addition, the object's status is updated, as can be seen here: | ||
|
||
```bash | ||
$ kubectl apply -f ../obj.yaml | ||
$ kubectl get kopfexamples | ||
NAME DURATION CHILDREN MESSAGE | ||
kopf-example-1 1m hello world | ||
``` | ||
|
||
```bash | ||
$ kubectl describe KopfExample kopf-example-1 | ||
Name: kopf-example-1 | ||
Namespace: default | ||
Labels: somelabel=somevalue | ||
... | ||
Status: | ||
Message: hello world | ||
Events: | ||
Type Reason Age From Message | ||
---- ------ ---- ---- ------- | ||
Normal Finished 42s kopf All handlers succeeded. | ||
Normal Success 43s kopf Handler create_fn succeeded. | ||
``` | ||
|
||
```bash | ||
$ kubectl get KopfExample kopf-example-1 -o yaml | ||
apiVersion: zalando.org/v1 | ||
kind: KopfExample | ||
metadata: | ||
... | ||
spec: | ||
duration: 1m | ||
field: value | ||
items: | ||
- item1 | ||
- item2 | ||
status: | ||
message: hello world | ||
``` | ||
|
||
Cleanup in the end: | ||
|
||
```bash | ||
$ kubectl delete -f ../obj.yaml | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
import kopf | ||
|
||
|
||
@kopf.on.create('zalando.org', 'v1', 'kopfexamples') | ||
def create_fn(spec, **kwargs): | ||
print(f"And here we are! Creating: {spec}") | ||
return {'message': 'hello world'} # will be the new status |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
# Kopf example with children | ||
|
||
This example creates a `Pod` for every created `KopfExample` object, | ||
and attaches it as a child of that example object. The latter means that | ||
when the parent object is deleted, the child pod is also terminated. | ||
|
||
Start the operator: | ||
|
||
```bash | ||
kopf run example.py --verbose | ||
``` | ||
|
||
The child pod's id is stored as the parent's status field, | ||
so that it can be seen on the object listing (see also `crd.yaml`): | ||
|
||
```bash | ||
$ kubectl apply -f ../obj.yaml | ||
$ kubectl get kopfexamples | ||
NAME FIELD CHILDREN | ||
kopf-example-1 value [aed7f7ac-2971-11e9-b4d3-061441377794] | ||
|
||
$ kubectl get pod -l somelabel=somevalue | ||
NAME READY STATUS RESTARTS AGE | ||
kopf-example-1-jvlfs 1/1 Running 0 26s | ||
``` | ||
|
||
```bash | ||
$ kubectl delete -f ../obj.yaml | ||
$ kubectl get pod -l somelabel=somevalue | ||
NAME READY STATUS RESTARTS AGE | ||
kopf-example-1-jvlfs 1/1 Terminating 0 52s | ||
``` | ||
|
||
Cleanup in the end: | ||
|
||
```bash | ||
$ kubectl delete -f ../obj.yaml | ||
``` |
Oops, something went wrong.