Skip to content

Commit

Permalink
[184] Add an example of embedded operator
Browse files Browse the repository at this point in the history
  • Loading branch information
Sergey Vasilyev committed Sep 3, 2019
1 parent ee0717b commit 1452aa1
Show file tree
Hide file tree
Showing 3 changed files with 160 additions and 0 deletions.
49 changes: 49 additions & 0 deletions examples/12-embedded/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# Kopf example for embedded operator

Kopf operators can be embedded into arbitrary applications, such as UI;
or they can be orchestrated explicitly by the developers instead of `kopf run`.

In this example, we start the operator in a side thread, while simulating
an application activity in the main thread. In this case, the "application"
just creates and deletes the example objects, but it can be any activity.

Start the operator:

```bash
python example.py
```

Let it run for 6 seconds (mostly due to sleeps: 3 times by 1+1 second).
Here is what it will print (shortened; the actual output is more verbose):

```
Starting the main app.
[DEBUG ] Pykube is configured via kubeconfig file.
[DEBUG ] Client is configured via kubeconfig file.
[WARNING ] Default peering object not found, falling back to the standalone mode.
[WARNING ] OS signals are ignored: running not in the main thread.
Do the main app activity here. Step 1/3.
[DEBUG ] [default/kopf-example-0] Creation event: ...
[DEBUG ] [default/kopf-example-0] Deletion event: ...
Do the main app activity here. Step 2/3.
[DEBUG ] [default/kopf-example-1] Creation event: ...
[DEBUG ] [default/kopf-example-1] Deletion event: ...
Do the main app activity here. Step 3/3.
[DEBUG ] [default/kopf-example-2] Creation event: ...
[DEBUG ] [default/kopf-example-2] Deletion event: ...
Exiting the main app.
[INFO ] Stop-flag is set to True. Operator is stopping.
[DEBUG ] Root task 'poster of events' is cancelled.
[DEBUG ] Root task 'watcher of kopfexamples.zalando.org' is cancelled.
[DEBUG ] Root tasks are stopped: finished normally; tasks left: set()
[DEBUG ] Hung tasks stopping is skipped: no tasks given.
```
103 changes: 103 additions & 0 deletions examples/12-embedded/example.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
import asyncio
import threading
import time

import kopf
import kubernetes.client.rest


@kopf.on.create('zalando.org', 'v1', 'kopfexamples')
def create_fn(**_):
pass


@kopf.on.delete('zalando.org', 'v1', 'kopfexamples')
def delete_fn(**_):
pass


def kopf_thread(
ready_flag: threading.Event,
stop_flag: threading.Event,
):
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)

kopf.configure(verbose=True) # log formatting
kopf.login() # tokens & certs

loop.run_until_complete(kopf.operator(
ready_flag=ready_flag,
stop_flag=stop_flag,
))


def main(steps=3):
kopf.login()

# Start the operator and let it initialise.
print(f"Starting the main app.")
ready_flag = threading.Event()
stop_flag = threading.Event()
thread = threading.Thread(target=kopf_thread, kwargs=dict(
stop_flag=stop_flag,
ready_flag=ready_flag,
))
thread.start()
ready_flag.wait()

# The operator is active: run the app's activity.
for step in range(steps):
print(f"Do the main app activity here. Step {step+1}/{steps}.")
_create_object(step)
time.sleep(1.0)
_delete_object(step)
time.sleep(1.0)

# Ask the operator to terminate gracefully (can take a couple of seconds).
print(f"Exiting the main app.")
stop_flag.set()
thread.join()


def _create_object(step):
try:
api = kubernetes.client.CustomObjectsApi()
api.create_namespaced_custom_object(
group='zalando.org',
version='v1',
plural='kopfexamples',
namespace='default',
body=dict(
apiVersion='zalando.org/v1',
kind='KopfExample',
metadata=dict(name=f'kopf-example-{step}'),
),
)
except kubernetes.client.rest.ApiException as e:
if e.status in [409]:
pass
else:
raise


def _delete_object(step):
try:
api = kubernetes.client.CustomObjectsApi()
api.delete_namespaced_custom_object(
group='zalando.org',
version='v1',
plural='kopfexamples',
namespace='default',
name=f'kopf-example-{step}',
body={},
)
except kubernetes.client.rest.ApiException as e:
if e.status in [404]:
pass
else:
raise


if __name__ == '__main__':
main()
8 changes: 8 additions & 0 deletions examples/12-embedded/test_nothing.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
"""
Embeddable operators require very customised application-specific testing.
Kopf cannot help here beyond its regular `kopf.testing.KopfRunner` helper,
which is an equivalent of `kopf run` command.
This file exists to disable the implicit e2e tests
(they skip if explicit e2e tests exist in the example directory).
"""

0 comments on commit 1452aa1

Please sign in to comment.