From ae59dc6cebaa5850cf25d7f3e6ca8adf70edbe54 Mon Sep 17 00:00:00 2001 From: Ori Hoch Date: Mon, 22 Apr 2019 12:18:27 +0300 Subject: [PATCH] minio storage: support dry run and container spec overrides --- ckan_cloud_operator/providers/storage/cli.py | 6 +- .../providers/storage/manager.py | 5 +- .../providers/storage/minio/manager.py | 43 ++--- docs/INFRA-MANAGEMENT.md | 16 ++ .../set minio deployment resources.ipynb | 150 ++++++++++++++++++ 5 files changed, 198 insertions(+), 22 deletions(-) create mode 100644 notebooks/set minio deployment resources.ipynb diff --git a/ckan_cloud_operator/providers/storage/cli.py b/ckan_cloud_operator/providers/storage/cli.py index 49349f94..75ffd08c 100644 --- a/ckan_cloud_operator/providers/storage/cli.py +++ b/ckan_cloud_operator/providers/storage/cli.py @@ -17,11 +17,13 @@ def storage(): @click.option('--provider-id') @click.option('--storage-suffix') @click.option('--disk-name') -def initialize(interactive, provider_id, storage_suffix, disk_name): +@click.option('--dry-run', is_flag=True) +def initialize(interactive, provider_id, storage_suffix, disk_name, dry_run): manager.initialize(interactive=interactive, provider_id=provider_id, storage_suffix=storage_suffix, - use_existing_disk_name=disk_name) + use_existing_disk_name=disk_name, + dry_run=dry_run) logs.exit_great_success() @storage.command() diff --git a/ckan_cloud_operator/providers/storage/manager.py b/ckan_cloud_operator/providers/storage/manager.py index 41e76ead..2880db0a 100644 --- a/ckan_cloud_operator/providers/storage/manager.py +++ b/ckan_cloud_operator/providers/storage/manager.py @@ -3,14 +3,15 @@ from .constants import PROVIDER_SUBMODULE -def initialize(interactive=False, provider_id=None, storage_suffix=None, use_existing_disk_name=None): +def initialize(interactive=False, provider_id=None, storage_suffix=None, use_existing_disk_name=None, dry_run=False): get_provider( default=minio_provider_id, provider_id=provider_id ).initialize( interactive=interactive, storage_suffix=storage_suffix, - use_existing_disk_name=use_existing_disk_name + use_existing_disk_name=use_existing_disk_name, + dry_run=dry_run ) diff --git a/ckan_cloud_operator/providers/storage/minio/manager.py b/ckan_cloud_operator/providers/storage/minio/manager.py index b72989d9..1c457faa 100644 --- a/ckan_cloud_operator/providers/storage/minio/manager.py +++ b/ckan_cloud_operator/providers/storage/minio/manager.py @@ -21,12 +21,14 @@ def _config_interactive_set(default_values, namespace=None, is_secret=False, suf import os import binascii import yaml +import json from ckan_cloud_operator import kubectl +from ckan_cloud_operator import logs from ckan_cloud_operator.routers import manager as routers_manager -def initialize(interactive=False, storage_suffix=None, use_existing_disk_name=None): +def initialize(interactive=False, storage_suffix=None, use_existing_disk_name=None, dry_run=False): _config_interactive_set({ 'disk-size-gb': None, **({} if storage_suffix else {'router-name': routers_manager.get_default_infra_router_name()}) @@ -37,11 +39,12 @@ def initialize(interactive=False, storage_suffix=None, use_existing_disk_name=No storage_suffix=storage_suffix, use_existing_disk_name=use_existing_disk_name ), - storage_suffix=storage_suffix + storage_suffix=storage_suffix, + dry_run=dry_run ) - _apply_service(storage_suffix=storage_suffix) + _apply_service(storage_suffix=storage_suffix, dry_run=dry_run) if not storage_suffix: - _update_route(storage_suffix=storage_suffix) + _update_route(storage_suffix=storage_suffix, dry_run=dry_run) _set_provider() @@ -76,12 +79,13 @@ def _apply_secret(storage_suffix=None): _config_set(values={'MINIO_ACCESS_KEY': access_key, 'MINIO_SECRET_KEY': secret_key}, is_secret=True, suffix=storage_suffix) -def _apply_deployment(volume_spec, storage_suffix=None): +def _apply_deployment(volume_spec, storage_suffix=None, dry_run=False): node_selector = volume_spec.pop('nodeSelector', None) if node_selector: pod_scheduling = {'nodeSelector': node_selector} else: pod_scheduling = {} + container_spec_overrides = _config_get('container-spec-overrides', required=False, default=None, suffix=storage_suffix) kubectl.apply(kubectl.get_deployment( _get_resource_name(suffix=storage_suffix), _get_resource_labels(for_deployment=True, suffix=storage_suffix), @@ -109,6 +113,7 @@ def _apply_deployment(volume_spec, storage_suffix=None): 'mountPath': '/export', } ], + **(json.loads(container_spec_overrides) if container_spec_overrides else {}) } ], 'volumes': [ @@ -117,10 +122,10 @@ def _apply_deployment(volume_spec, storage_suffix=None): } } } - )) + ), dry_run=dry_run) -def _apply_service(storage_suffix=None): +def _apply_service(storage_suffix=None, dry_run=False): kubectl.apply(kubectl.get_resource( 'v1', 'Service', _get_resource_name(suffix=storage_suffix), @@ -133,7 +138,7 @@ def _apply_service(storage_suffix=None): 'app': _get_resource_labels(for_deployment=True, suffix=storage_suffix)['app'] } } - )) + ), dry_run=dry_run) def _get_or_create_volume(storage_suffix=None, use_existing_disk_name=None): @@ -152,21 +157,23 @@ def _get_or_create_volume(storage_suffix=None, use_existing_disk_name=None): return volume_spec -def _update_route(storage_suffix=None): +def _update_route(storage_suffix=None, dry_run=False): backend_url_target_id = _get_backend_url_target_id(storage_suffix=storage_suffix) router_name = _config_get('router-name', required=True, suffix=storage_suffix) if not routers_manager.get_backend_url_routes(backend_url_target_id): deployment_name = _get_resource_name(suffix=storage_suffix) namespace = _get_namespace() - routers_manager.create_subdomain_route( - router_name, - { - 'target-type': 'backend-url', - 'target-resource-id': backend_url_target_id, - 'backend-url': f'http://{deployment_name}.{namespace}:9000', - } - ) - routers_manager.update(router_name, wait_ready=True) + subdomain_route = { + 'target-type': 'backend-url', + 'target-resource-id': backend_url_target_id, + 'backend-url': f'http://{deployment_name}.{namespace}:9000', + } + if dry_run: + logs.info('create_subdomain_route', router_name, subdomain_route) + else: + routers_manager.create_subdomain_route(router_name, subdomain_route) + if not dry_run: + routers_manager.update(router_name, wait_ready=True) def _get_namespace(): diff --git a/docs/INFRA-MANAGEMENT.md b/docs/INFRA-MANAGEMENT.md index 95d15ae6..cad5b15c 100644 --- a/docs/INFRA-MANAGEMENT.md +++ b/docs/INFRA-MANAGEMENT.md @@ -123,3 +123,19 @@ Depending on instance, some paths can be set to public download: ``` mc policy download prod/ckan/instance/storage'*' ``` + +### Setting minio deployment container spec overrides + +You can set overrides for the minio deployment container spec + +For example, set resources: + +``` +ckan-cloud-operator config set --configmap-name ckan-cloud-provider-storage-minio container-spec-overrides '{"resources":{"requests":{"cpu": "1", memory": "1Gi"},"limits":{"memory":"2Gi"}}' +``` + +Initialize the storage provider to update the deployment, first with dry-run to validate: + +``` +ckan-cloud-operator storage initialize --dry-run +``` diff --git a/notebooks/set minio deployment resources.ipynb b/notebooks/set minio deployment resources.ipynb new file mode 100644 index 00000000..2b935959 --- /dev/null +++ b/notebooks/set minio deployment resources.ipynb @@ -0,0 +1,150 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Set CPU/Memory resource requirements for Minio" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from ckan_cloud_operator import kubectl\n", + "\n", + "old_minio_pods = [pod for pod in kubectl.get('pod')['items'] if pod['metadata']['labels'].get('app') == 'provider-storage-minio']\n", + "assert len(old_minio_pods) == 1\n", + "old_minio_containers = old_minio_pods[0]['spec']['containers']\n", + "assert len(old_minio_containers) == 1" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import yaml\n", + "from IPython.core.display import HTML\n", + "\n", + "HTML('

OLD RESOURECS

\\n{}'.format(yaml.dump(old_minio_containers[0]['resources'], default_flow_style=False)))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Set new resources" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import json\n", + "from ckan_cloud_operator.config import manager as config_manager\n", + "\n", + "config_manager.set(\n", + " key='container-spec-overrides', \n", + " value=json.dumps({\n", + " \"resources\": {\n", + " \"requests\": {\n", + " \"cpu\": \"1\", \n", + " \"memory\": \"1Gi\"\n", + " },\n", + " \"limits\": {\n", + " \"memory\":\"2Gi\"\n", + " }\n", + " }\n", + " }),\n", + " configmap_name='ckan-cloud-provider-storage-minio'\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Apply deployment: Dry Run" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from ckan_cloud_operator.providers.storage.minio import manager as minio_manager\n", + "\n", + "minio_manager.initialize(dry_run=True)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Apply Deployment" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from ckan_cloud_operator.providers.storage.minio import manager as minio_manager\n", + "\n", + "minio_manager.initialize()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Check deployment progress" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import yaml\n", + "from ckan_cloud_operator import kubectl\n", + "\n", + "minio_pods = [pod for pod in kubectl.get('pod')['items'] if pod['metadata']['labels'].get('app') == 'provider-storage-minio']\n", + "pod_names = [pod['metadata']['name'] for pod in minio_pods]\n", + "print(yaml.dump(pod_names, default_flow_style=False))\n", + "\n", + "[print(kubectl.check_output(f'describe pod {pod_name}').decode()) for pod_name in pod_names]" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.3" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +}