commit 02c4f8ac53df4a4b0e8bd4e7e6e2997ddc07ee86 Author: Chris Cowley Date: Mon Sep 30 16:50:20 2024 +0200 initial commit diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..82f9275 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,162 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +#poetry.lock + +# pdm +# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. +#pdm.lock +# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it +# in version control. +# https://pdm.fming.dev/latest/usage/project/#working-with-version-control +.pdm.toml +.pdm-python +.pdm-build/ + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# PyCharm +# JetBrains specific template is maintained in a separate JetBrains.gitignore that can +# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# and can be added to the global gitignore or merged into this file. For a more nuclear +# option (not recommended) you can uncomment the following to ignore the entire idea folder. +#.idea/ diff --git a/.envrc b/.envrc new file mode 100644 index 0000000..fece937 --- /dev/null +++ b/.envrc @@ -0,0 +1,2 @@ +export FLASK_APP=api.py +export FLASK_DEBUG=1 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..82f9275 --- /dev/null +++ b/.gitignore @@ -0,0 +1,162 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +#poetry.lock + +# pdm +# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. +#pdm.lock +# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it +# in version control. +# https://pdm.fming.dev/latest/usage/project/#working-with-version-control +.pdm.toml +.pdm-python +.pdm-build/ + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# PyCharm +# JetBrains specific template is maintained in a separate JetBrains.gitignore that can +# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# and can be added to the global gitignore or merged into this file. For a more nuclear +# option (not recommended) you can uncomment the following to ignore the entire idea folder. +#.idea/ diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..d9d6f1b --- /dev/null +++ b/Dockerfile @@ -0,0 +1,12 @@ +FROM python:alpine + +WORKDIR /app + +COPY ./requirements.txt /app/requirements.txt +RUN pip install -r requirements.txt + +COPY . /app/ + +EXPOSE 5000 + +CMD ["gunicorn", "--bind", "0.0.0.0:5000", "wsgi:app"] diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..c85de17 --- /dev/null +++ b/Makefile @@ -0,0 +1,10 @@ +dev: + docker run -p 5000:5000 -v ~/.kube/config:/root/.kube/config -e FLASK_ENV=development -e FLASK_DEBUG=1 -v ./:/app db-deployer flask run --host 0.0.0.0 +run: + docker run -p 5000:5000 -v ~/.kube/config:/root/.kube/config db-deployer + +clean: + for resource in $$(kubectl -n test get mariadbs.k8s.mariadb.com,pvc -o name); do kubectl -n test delete $$resource; done + +build: + docker build -t db-deployer . diff --git a/README.md b/README.md new file mode 100644 index 0000000..6d43c36 --- /dev/null +++ b/README.md @@ -0,0 +1 @@ +https://faizanbashir.github.io/creating-kubernetes-custom-resource-definition-using-python#listing-resources-using-the-dynamic-client diff --git a/api.py b/api.py new file mode 100644 index 0000000..df86951 --- /dev/null +++ b/api.py @@ -0,0 +1,111 @@ +import os +import random +from flask import ( + Flask, + request, + jsonify, +) +from flask_restful import reqparse, abort, Resource, Api +from kubernetes import client as k8s_client +from kubernetes.client import api_client +from kubernetes import config, watch, dynamic +from kubernetes.dynamic.exceptions import ResourceNotFoundError + + +app = Flask(__name__) +api = Api(app) + + +if "KUBERNETES_SERVICE_HOST" in os.environ: + config.load_incluster_config() +else: + config.load_kube_config() + +v1 = k8s_client.CoreV1Api() +client = dynamic.DynamicClient( + api_client.ApiClient(configuration=config.load_kube_config()) +) +custom_object_api = k8s_client.CustomObjectsApi() + +first_names = ['Ada', 'Bela', 'Cade', 'Dax', 'Eva', 'Fynn', 'Gia', 'Hugo', 'Ivy', 'Jax'] +last_names = ['Smith', 'Johnson', 'Williams', 'Jones', 'Brown', 'Davis', 'Miller', 'Wilson', 'Moore', 'Taylor'] + +def create_name(): + first_name = random.choice(first_names) + last_name = random.choice(last_names) + return f"{first_name}{last_name}" + +class DBCluster(Resource): + def get(self): + namespace = "test" + clusters = [] + mariadb_resource = client.resources.get(api_version='k8s.mariadb.com/v1alpha1', kind='MariaDB') + mariadbs = mariadb_resource.get(namespace=namespace) + for mariadb in mariadbs.items: + name = mariadb.metadata.name + clusters.append({'name': name}) + return clusters + + def post(self): + namespace = "test" + name = random.choice(first_names).lower() + print(name ) + mariadb = { + 'apiVersion': 'k8s.mariadb.com/v1alpha1', + 'kind': 'MariaDB', + 'metadata': { + 'name': name, + 'namespace': namespace, + }, + 'spec': { + 'rootPasswordSecretKeyRef': { + 'name': 'mariadb-root', + 'key': 'password', + 'generate': True + }, + 'username': 'mariadb', + 'passwordSecretKeyRef': { + 'name': 'mariadb-password', + 'key': 'password', + 'generate': True + }, + 'port': 3306, + 'storage': { + 'size': '1Gi', + } + } + } + + mariadb_instance = custom_object_api.create_namespaced_custom_object( + group='k8s.mariadb.com', + version='v1alpha1', + namespace=namespace, + plural='mariadbs', + body=mariadb + ) + return mariadb_instance + +class DBClusterDetails(Resource): + def get(self, name): + namespace = "test" + cluster = {} + mariadb_resource = client.resources.get(api_version='k8s.mariadb.com/v1alpha1', kind='MariaDB') + mariadbs = mariadb_resource.get(namespace=namespace) + for mariadb in mariadbs.items: + if mariadb.metadata.name == name: + for k,v in mariadb.metadata: + print(k,v) + + data = { + "name": mariadb.metadata.name, + "storage": mariadb.spec.storage.size, + } + return data + + + +api.add_resource(DBCluster, '/clusters') +api.add_resource(DBClusterDetails, '/clusters/') + +if __name__ == '__main__': + app.run(host='0.0.0.0', debug=True) diff --git a/manage.py b/manage.py new file mode 100644 index 0000000..59d772a --- /dev/null +++ b/manage.py @@ -0,0 +1,8 @@ +from flask.cli import FlaskGroup + +from api import api + +cli = FlaskGroup(api) + +if __name__ == "__main__": + cli() diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..3503e3f --- /dev/null +++ b/requirements.txt @@ -0,0 +1,30 @@ +aniso8601==9.0.1 +blinker==1.8.2 +cachetools==5.5.0 +certifi==2024.8.30 +charset-normalizer==3.3.2 +click==8.1.7 +durationpy==0.7 +Flask==3.0.3 +Flask-RESTful==0.3.10 +google-auth==2.35.0 +gunicorn==23.0.0 +idna==3.10 +itsdangerous==2.2.0 +Jinja2==3.1.4 +kubernetes==31.0.0 +MarkupSafe==2.1.5 +oauthlib==3.2.2 +packaging==24.1 +pyasn1==0.6.1 +pyasn1_modules==0.4.1 +python-dateutil==2.9.0.post0 +pytz==2024.2 +PyYAML==6.0.2 +requests==2.32.3 +requests-oauthlib==2.0.0 +rsa==4.9 +six==1.16.0 +urllib3==2.2.3 +websocket-client==1.8.0 +Werkzeug==3.0.4 diff --git a/wsgi.py b/wsgi.py new file mode 100644 index 0000000..235e230 --- /dev/null +++ b/wsgi.py @@ -0,0 +1,5 @@ +from api import app + + +if __name__ == '__main__': + app.run()