Skip to content

Commit ae59dc6

Browse files
committed
minio storage: support dry run and container spec overrides
1 parent f97c97e commit ae59dc6

File tree

5 files changed

+198
-22
lines changed

5 files changed

+198
-22
lines changed

ckan_cloud_operator/providers/storage/cli.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,13 @@ def storage():
1717
@click.option('--provider-id')
1818
@click.option('--storage-suffix')
1919
@click.option('--disk-name')
20-
def initialize(interactive, provider_id, storage_suffix, disk_name):
20+
@click.option('--dry-run', is_flag=True)
21+
def initialize(interactive, provider_id, storage_suffix, disk_name, dry_run):
2122
manager.initialize(interactive=interactive,
2223
provider_id=provider_id,
2324
storage_suffix=storage_suffix,
24-
use_existing_disk_name=disk_name)
25+
use_existing_disk_name=disk_name,
26+
dry_run=dry_run)
2527
logs.exit_great_success()
2628

2729
@storage.command()

ckan_cloud_operator/providers/storage/manager.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,15 @@
33
from .constants import PROVIDER_SUBMODULE
44

55

6-
def initialize(interactive=False, provider_id=None, storage_suffix=None, use_existing_disk_name=None):
6+
def initialize(interactive=False, provider_id=None, storage_suffix=None, use_existing_disk_name=None, dry_run=False):
77
get_provider(
88
default=minio_provider_id,
99
provider_id=provider_id
1010
).initialize(
1111
interactive=interactive,
1212
storage_suffix=storage_suffix,
13-
use_existing_disk_name=use_existing_disk_name
13+
use_existing_disk_name=use_existing_disk_name,
14+
dry_run=dry_run
1415
)
1516

1617

ckan_cloud_operator/providers/storage/minio/manager.py

Lines changed: 25 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,14 @@ def _config_interactive_set(default_values, namespace=None, is_secret=False, suf
2121
import os
2222
import binascii
2323
import yaml
24+
import json
2425

2526
from ckan_cloud_operator import kubectl
27+
from ckan_cloud_operator import logs
2628
from ckan_cloud_operator.routers import manager as routers_manager
2729

2830

29-
def initialize(interactive=False, storage_suffix=None, use_existing_disk_name=None):
31+
def initialize(interactive=False, storage_suffix=None, use_existing_disk_name=None, dry_run=False):
3032
_config_interactive_set({
3133
'disk-size-gb': None,
3234
**({} 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
3739
storage_suffix=storage_suffix,
3840
use_existing_disk_name=use_existing_disk_name
3941
),
40-
storage_suffix=storage_suffix
42+
storage_suffix=storage_suffix,
43+
dry_run=dry_run
4144
)
42-
_apply_service(storage_suffix=storage_suffix)
45+
_apply_service(storage_suffix=storage_suffix, dry_run=dry_run)
4346
if not storage_suffix:
44-
_update_route(storage_suffix=storage_suffix)
47+
_update_route(storage_suffix=storage_suffix, dry_run=dry_run)
4548
_set_provider()
4649

4750

@@ -76,12 +79,13 @@ def _apply_secret(storage_suffix=None):
7679
_config_set(values={'MINIO_ACCESS_KEY': access_key, 'MINIO_SECRET_KEY': secret_key}, is_secret=True, suffix=storage_suffix)
7780

7881

79-
def _apply_deployment(volume_spec, storage_suffix=None):
82+
def _apply_deployment(volume_spec, storage_suffix=None, dry_run=False):
8083
node_selector = volume_spec.pop('nodeSelector', None)
8184
if node_selector:
8285
pod_scheduling = {'nodeSelector': node_selector}
8386
else:
8487
pod_scheduling = {}
88+
container_spec_overrides = _config_get('container-spec-overrides', required=False, default=None, suffix=storage_suffix)
8589
kubectl.apply(kubectl.get_deployment(
8690
_get_resource_name(suffix=storage_suffix),
8791
_get_resource_labels(for_deployment=True, suffix=storage_suffix),
@@ -109,6 +113,7 @@ def _apply_deployment(volume_spec, storage_suffix=None):
109113
'mountPath': '/export',
110114
}
111115
],
116+
**(json.loads(container_spec_overrides) if container_spec_overrides else {})
112117
}
113118
],
114119
'volumes': [
@@ -117,10 +122,10 @@ def _apply_deployment(volume_spec, storage_suffix=None):
117122
}
118123
}
119124
}
120-
))
125+
), dry_run=dry_run)
121126

122127

123-
def _apply_service(storage_suffix=None):
128+
def _apply_service(storage_suffix=None, dry_run=False):
124129
kubectl.apply(kubectl.get_resource(
125130
'v1', 'Service',
126131
_get_resource_name(suffix=storage_suffix),
@@ -133,7 +138,7 @@ def _apply_service(storage_suffix=None):
133138
'app': _get_resource_labels(for_deployment=True, suffix=storage_suffix)['app']
134139
}
135140
}
136-
))
141+
), dry_run=dry_run)
137142

138143

139144
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):
152157
return volume_spec
153158

154159

155-
def _update_route(storage_suffix=None):
160+
def _update_route(storage_suffix=None, dry_run=False):
156161
backend_url_target_id = _get_backend_url_target_id(storage_suffix=storage_suffix)
157162
router_name = _config_get('router-name', required=True, suffix=storage_suffix)
158163
if not routers_manager.get_backend_url_routes(backend_url_target_id):
159164
deployment_name = _get_resource_name(suffix=storage_suffix)
160165
namespace = _get_namespace()
161-
routers_manager.create_subdomain_route(
162-
router_name,
163-
{
164-
'target-type': 'backend-url',
165-
'target-resource-id': backend_url_target_id,
166-
'backend-url': f'http://{deployment_name}.{namespace}:9000',
167-
}
168-
)
169-
routers_manager.update(router_name, wait_ready=True)
166+
subdomain_route = {
167+
'target-type': 'backend-url',
168+
'target-resource-id': backend_url_target_id,
169+
'backend-url': f'http://{deployment_name}.{namespace}:9000',
170+
}
171+
if dry_run:
172+
logs.info('create_subdomain_route', router_name, subdomain_route)
173+
else:
174+
routers_manager.create_subdomain_route(router_name, subdomain_route)
175+
if not dry_run:
176+
routers_manager.update(router_name, wait_ready=True)
170177

171178

172179
def _get_namespace():

docs/INFRA-MANAGEMENT.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,3 +123,19 @@ Depending on instance, some paths can be set to public download:
123123
```
124124
mc policy download prod/ckan/instance/storage'*'
125125
```
126+
127+
### Setting minio deployment container spec overrides
128+
129+
You can set overrides for the minio deployment container spec
130+
131+
For example, set resources:
132+
133+
```
134+
ckan-cloud-operator config set --configmap-name ckan-cloud-provider-storage-minio container-spec-overrides '{"resources":{"requests":{"cpu": "1", memory": "1Gi"},"limits":{"memory":"2Gi"}}'
135+
```
136+
137+
Initialize the storage provider to update the deployment, first with dry-run to validate:
138+
139+
```
140+
ckan-cloud-operator storage initialize --dry-run
141+
```
Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
{
2+
"cells": [
3+
{
4+
"cell_type": "markdown",
5+
"metadata": {},
6+
"source": [
7+
"# Set CPU/Memory resource requirements for Minio"
8+
]
9+
},
10+
{
11+
"cell_type": "code",
12+
"execution_count": null,
13+
"metadata": {},
14+
"outputs": [],
15+
"source": [
16+
"from ckan_cloud_operator import kubectl\n",
17+
"\n",
18+
"old_minio_pods = [pod for pod in kubectl.get('pod')['items'] if pod['metadata']['labels'].get('app') == 'provider-storage-minio']\n",
19+
"assert len(old_minio_pods) == 1\n",
20+
"old_minio_containers = old_minio_pods[0]['spec']['containers']\n",
21+
"assert len(old_minio_containers) == 1"
22+
]
23+
},
24+
{
25+
"cell_type": "code",
26+
"execution_count": null,
27+
"metadata": {},
28+
"outputs": [],
29+
"source": [
30+
"import yaml\n",
31+
"from IPython.core.display import HTML\n",
32+
"\n",
33+
"HTML('<h3>OLD RESOURECS</h3>\\n{}'.format(yaml.dump(old_minio_containers[0]['resources'], default_flow_style=False)))"
34+
]
35+
},
36+
{
37+
"cell_type": "markdown",
38+
"metadata": {},
39+
"source": [
40+
"## Set new resources"
41+
]
42+
},
43+
{
44+
"cell_type": "code",
45+
"execution_count": null,
46+
"metadata": {},
47+
"outputs": [],
48+
"source": [
49+
"import json\n",
50+
"from ckan_cloud_operator.config import manager as config_manager\n",
51+
"\n",
52+
"config_manager.set(\n",
53+
" key='container-spec-overrides', \n",
54+
" value=json.dumps({\n",
55+
" \"resources\": {\n",
56+
" \"requests\": {\n",
57+
" \"cpu\": \"1\", \n",
58+
" \"memory\": \"1Gi\"\n",
59+
" },\n",
60+
" \"limits\": {\n",
61+
" \"memory\":\"2Gi\"\n",
62+
" }\n",
63+
" }\n",
64+
" }),\n",
65+
" configmap_name='ckan-cloud-provider-storage-minio'\n",
66+
")"
67+
]
68+
},
69+
{
70+
"cell_type": "markdown",
71+
"metadata": {},
72+
"source": [
73+
"## Apply deployment: Dry Run"
74+
]
75+
},
76+
{
77+
"cell_type": "code",
78+
"execution_count": null,
79+
"metadata": {},
80+
"outputs": [],
81+
"source": [
82+
"from ckan_cloud_operator.providers.storage.minio import manager as minio_manager\n",
83+
"\n",
84+
"minio_manager.initialize(dry_run=True)"
85+
]
86+
},
87+
{
88+
"cell_type": "markdown",
89+
"metadata": {},
90+
"source": [
91+
"## Apply Deployment"
92+
]
93+
},
94+
{
95+
"cell_type": "code",
96+
"execution_count": null,
97+
"metadata": {},
98+
"outputs": [],
99+
"source": [
100+
"from ckan_cloud_operator.providers.storage.minio import manager as minio_manager\n",
101+
"\n",
102+
"minio_manager.initialize()"
103+
]
104+
},
105+
{
106+
"cell_type": "markdown",
107+
"metadata": {},
108+
"source": [
109+
"## Check deployment progress"
110+
]
111+
},
112+
{
113+
"cell_type": "code",
114+
"execution_count": null,
115+
"metadata": {},
116+
"outputs": [],
117+
"source": [
118+
"import yaml\n",
119+
"from ckan_cloud_operator import kubectl\n",
120+
"\n",
121+
"minio_pods = [pod for pod in kubectl.get('pod')['items'] if pod['metadata']['labels'].get('app') == 'provider-storage-minio']\n",
122+
"pod_names = [pod['metadata']['name'] for pod in minio_pods]\n",
123+
"print(yaml.dump(pod_names, default_flow_style=False))\n",
124+
"\n",
125+
"[print(kubectl.check_output(f'describe pod {pod_name}').decode()) for pod_name in pod_names]"
126+
]
127+
}
128+
],
129+
"metadata": {
130+
"kernelspec": {
131+
"display_name": "Python 3",
132+
"language": "python",
133+
"name": "python3"
134+
},
135+
"language_info": {
136+
"codemirror_mode": {
137+
"name": "ipython",
138+
"version": 3
139+
},
140+
"file_extension": ".py",
141+
"mimetype": "text/x-python",
142+
"name": "python",
143+
"nbconvert_exporter": "python",
144+
"pygments_lexer": "ipython3",
145+
"version": "3.7.3"
146+
}
147+
},
148+
"nbformat": 4,
149+
"nbformat_minor": 2
150+
}

0 commit comments

Comments
 (0)