commit
fbc6fa2f84
@ -0,0 +1,2 @@ |
||||
kind: template |
||||
load: docker.yaml |
@ -0,0 +1,3 @@ |
||||
FROM harbor.k-space.ee/k-space/microservice-base |
||||
ADD generated-secret-operator.py / |
||||
ENTRYPOINT /generated-secret-operator.py |
@ -0,0 +1,16 @@ |
||||
# Generated secret operator |
||||
|
||||
This operator can be used to provision secrets easily |
||||
|
||||
To instantiate secret: |
||||
|
||||
``` |
||||
apiVersion: codemowers.io/v1alpha1 |
||||
kind: GeneratedSecret |
||||
metadata: |
||||
name: my-secret |
||||
spec: |
||||
mapping: |
||||
- key: SECRET |
||||
value: "%(password)s" |
||||
``` |
@ -0,0 +1,43 @@ |
||||
--- |
||||
apiVersion: apiextensions.k8s.io/v1 |
||||
kind: CustomResourceDefinition |
||||
metadata: |
||||
name: generatedsecrets.codemowers.io |
||||
spec: |
||||
group: codemowers.io |
||||
names: |
||||
plural: generatedsecrets |
||||
singular: generatedsecret |
||||
kind: GeneratedSecret |
||||
shortNames: |
||||
- generatedsecret |
||||
scope: Namespaced |
||||
versions: |
||||
- name: v1alpha1 |
||||
served: true |
||||
storage: true |
||||
schema: |
||||
openAPIV3Schema: |
||||
type: object |
||||
properties: |
||||
spec: |
||||
type: object |
||||
properties: |
||||
mapping: |
||||
type: array |
||||
items: |
||||
type: object |
||||
properties: |
||||
key: |
||||
type: string |
||||
description: |
||||
Secret key |
||||
value: |
||||
type: string |
||||
description: |
||||
Secret value with suitable placeholders |
||||
size: |
||||
default: 20 |
||||
type: integer |
||||
description: Generated secret length |
||||
required: ["spec"] |
@ -0,0 +1,84 @@ |
||||
#!/usr/bin/env python3 |
||||
import asyncio |
||||
import os |
||||
import prometheus_async |
||||
import random |
||||
import string |
||||
from prometheus_client import Counter, Gauge |
||||
from kubernetes_asyncio.client.api_client import ApiClient |
||||
from kubernetes_asyncio.client.exceptions import ApiException |
||||
from kubernetes_asyncio import client, config, watch |
||||
from time import time |
||||
|
||||
counter_opened_watch_streams = Counter( |
||||
"meta_operator_opened_watch_streams", |
||||
"Count of watch streams opened") |
||||
gauge_watch_stream_begin_timestamp = Gauge( |
||||
"meta_operator_watch_stream_begin_timestamp", |
||||
"Timestamp of last watch stream open") |
||||
|
||||
|
||||
async def apply_changes(item, v1, apps_api, api_instance): |
||||
namespace = item["metadata"]["namespace"] |
||||
name = item["metadata"]["name"] |
||||
password = "".join([random.choice(string.ascii_letters + string.digits) for j in range(item["spec"]["size"])]) |
||||
data = {} |
||||
for o in item["spec"]["mapping"]: |
||||
data[o["key"]] = o["value"] % {"password": password} |
||||
try: |
||||
await v1.create_namespaced_secret(namespace, body={ |
||||
"metadata": { |
||||
"name": name, |
||||
"annotations": {}, |
||||
"ownerReferences": [{ |
||||
"apiVersion": "codemowers.io/v1alpha1", |
||||
"kind": "GeneratedSecret", |
||||
"uid": item["metadata"]["uid"], |
||||
"name": name |
||||
}] |
||||
}, |
||||
"stringData": data |
||||
}) |
||||
print("Generated secret %s/%s" % (namespace, name)) |
||||
except ApiException as e: |
||||
if e.status == 409: |
||||
print("Secret %s/%s already generated" % (namespace, name)) |
||||
else: |
||||
raise |
||||
|
||||
|
||||
async def main(): |
||||
if os.getenv("KUBECONFIG"): |
||||
await config.load_kube_config() |
||||
else: |
||||
config.load_incluster_config() |
||||
async with ApiClient() as api: |
||||
v1 = client.CoreV1Api(api) |
||||
apps_api = client.AppsV1Api() |
||||
api_instance = client.CustomObjectsApi(api) |
||||
while True: |
||||
try: |
||||
gauge_watch_stream_begin_timestamp.set(time()) |
||||
print("Subscribing to updates in every namespace") |
||||
w = watch.Watch() |
||||
counter_opened_watch_streams.inc() |
||||
flt = "codemowers.io", "v1alpha1", "", "generatedsecrets" |
||||
method = api_instance.list_namespaced_custom_object |
||||
async for event in w.stream(method, *flt): |
||||
if event["type"] == "ADDED": |
||||
await apply_changes(event["object"], v1, apps_api, api_instance) |
||||
except ApiException as e: |
||||
print("Caught exception: %s" % e) |
||||
if e.status == 410: |
||||
pass |
||||
else: |
||||
raise |
||||
except asyncio.exceptions.TimeoutError: |
||||
pass |
||||
|
||||
|
||||
if __name__ == "__main__": |
||||
loop = asyncio.new_event_loop() |
||||
loop.create_task(prometheus_async.aio.web.start_http_server(port=5000)) |
||||
loop.run_until_complete(main()) |
||||
loop.close() |
Loading…
Reference in new issue