Skip to content

Commit

Permalink
Add secret creation to operator
Browse files Browse the repository at this point in the history
  • Loading branch information
seb-schulz committed Oct 25, 2023
1 parent d20bf45 commit 4dd6959
Show file tree
Hide file tree
Showing 5 changed files with 117 additions and 3 deletions.
10 changes: 10 additions & 0 deletions deploy/mariadb/templates/cdr/user.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,16 @@ spec:
namespace:
type: string
required: ["name"]
secrets:
type: array
items:
type: object
properties:
name:
type: string
namespace:
type: string
required: ["name"]
scope: Namespaced
names:
plural: users
Expand Down
5 changes: 4 additions & 1 deletion features/environment.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import os
from behave.runner import Context
import subprocess
import time


@contextmanager
Expand Down Expand Up @@ -44,7 +45,8 @@ def after_scenario(context, scenario):

for ns in context.cleanup_namespaces:
subprocess.run([
'kubectl', 'delete', 'namespace', '--now', '--ignore-not-found', ns
'kubectl', 'delete', 'namespace', '--now', '--ignore-not-found',
'--wait', ns
])

for config in context.cleanup_k8s_configs:
Expand All @@ -53,3 +55,4 @@ def after_scenario(context, scenario):
text=True,
input=config,
capture_output=True)
time.sleep(2)
20 changes: 20 additions & 0 deletions features/steps/user.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,26 @@ def step_impl(context, namespace):
assert r.returncode == 0, f'failed to create namespace: {r!r}'


@then(u'secret "{name}" does exist in namespace "{ns}" with following entries')
def step_impl(context, name, ns):
for i in range(3):
time.sleep(i + 1)
r = subprocess.run([
'kubectl', 'get', 'secret', '-n', ns, name,
r"-o=jsonpath='{.data}'"
],
text=True,
capture_output=True)
if r.returncode == 0:
break
assert r.returncode == 0, "secret was not created"
assert len(r.stdout) > 2, f'secret has no data: {r!r}'
cm = json.loads(r.stdout[1:-1])
for row in context.table:
k = row['key']
assert k in cm.keys(), f"key {k} does not exist in secret"


@then(
u'config map "{name}" does exist in namespace "{ns}" with following entries'
)
Expand Down
29 changes: 28 additions & 1 deletion features/user.feature
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,34 @@ Feature: User custom declarative resource
name: mydb
"""
Then config map "mycm" does exist in namespace "myapp" with following entries
| key | value |
| key | value |
| MARIADB_SERVICE_DOMAIN | mariadb.default.svc.cluster.local |
| MARIADB_SERVICE_PORT | 3306 |
| MARIADB_USER | hello |


Scenario: Secret does exist on configured namespace
Given a running shell-operator
When namespace "myapp2" exists
And following kubernetes configuration is applied
"""
apiVersion: "k8s.sebatec.eu/v1alpha1"
kind: Database
metadata:
name: mydb
"""
And following kubernetes configuration is applied
"""
apiVersion: "k8s.sebatec.eu/v1alpha1"
kind: User
metadata:
name: hello
spec:
database: mydb
secrets:
- namespace: myapp2
name: mysecret
"""
Then secret "mysecret" does exist in namespace "myapp2" with following entries
| key |
| MARIADB_PASSWORD |
56 changes: 55 additions & 1 deletion hooks/user-hook.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import mysql.connector
import json
from kubernetes import client, config
import base64

CONFIG = {
'configVersion':
Expand Down Expand Up @@ -141,6 +142,30 @@ def create_or_replace_config_map(name, namespace, user):
print(f"failed to create config: {e!r}")


def create_or_replace_secret(name, namespace, passwd):
secret = client.V1Secret()
secret.metadata = client.V1ObjectMeta(name=name)
secret.data = {}
secret.data['MARIADB_PASSWORD'] = base64.b64encode(
passwd.encode('utf-8')).decode('utf-8')

api_instance = client.CoreV1Api()
try:
api_instance.replace_namespaced_secret(
name=name,
namespace=namespace,
body=secret,
)
except client.exceptions.ApiException as e:
if e.status == 404:
api_instance.create_namespaced_secret(
namespace=namespace,
body=secret,
)
else:
print(f"failed to create secret: {e!r}")


def delete_config_map(name, namespace):
api_instance = client.CoreV1Api()
try:
Expand All @@ -163,7 +188,18 @@ def handle_sync(cur, objects):
cm.get(
'namespace',
obj.get('metadata', {}).get('namespace', 'default'),
), get_user(obj))
),
get_user(obj),
)
for secret in obj.get('spec', {}).get('secrets'):
create_or_replace_secret(
secret.get('name'),
secret.get(
'namespace',
obj.get('metadata', {}).get('namespace', 'default'),
),
get_passwd(obj),
)


def handle_event(cur, watch_ev, obj):
Expand Down Expand Up @@ -195,6 +231,15 @@ def handle_event(cur, watch_ev, obj):
),
get_user(obj),
)
for secret in obj.get('spec', {}).get('secrets'):
create_or_replace_secret(
secret.get('name'),
secret.get(
'namespace',
obj.get('metadata', {}).get('namespace', 'default'),
),
get_passwd(obj),
)
elif watch_ev == 'Modified':
set_password(cur, get_user(obj), get_passwd(obj))

Expand All @@ -213,6 +258,15 @@ def handle_event(cur, watch_ev, obj):
),
get_user(obj),
)
for secret in obj.get('spec', {}).get('secrets'):
create_or_replace_secret(
secret.get('name'),
secret.get(
'namespace',
obj.get('metadata', {}).get('namespace', 'default'),
),
get_passwd(obj),
)
else:
print(f'Cannot handle watchEvent {watch_ev!r}')

Expand Down

0 comments on commit 4dd6959

Please sign in to comment.