-
-
Notifications
You must be signed in to change notification settings - Fork 166
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 #185 from nolar/184-embedded-signals
Add an example for embedded operator (and stop/ready flags)
- Loading branch information
Showing
5 changed files
with
242 additions
and
9 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
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,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. | ||
``` |
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,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() |
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,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). | ||
""" |
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