|
16 | 16 | import jsonschema |
17 | 17 | from copy import deepcopy |
18 | 18 | from django.db import models |
| 19 | +from django.conf import settings |
| 20 | +from django.core.cache import cache |
| 21 | +from requests.auth import HTTPBasicAuth |
19 | 22 | from requests_toolbelt import user_agent |
20 | 23 | from api import __version__ as drycc_version |
21 | 24 | from rest_framework.exceptions import ValidationError |
| 25 | +from scheduler import KubeException |
22 | 26 |
|
23 | 27 | logger = logging.getLogger(__name__) |
24 | 28 |
|
@@ -196,6 +200,78 @@ def validate_json(value, schema, raise_exception=ValidationError): |
196 | 200 | return value |
197 | 201 |
|
198 | 202 |
|
| 203 | +class VolumeClient(object): |
| 204 | + |
| 205 | + def __init__(self, app_id, volume, scheduler): |
| 206 | + self.bind = ":9000" |
| 207 | + self.path = "/data" |
| 208 | + self.app_id = app_id |
| 209 | + self.volume = volume |
| 210 | + self.scheduler = scheduler |
| 211 | + |
| 212 | + @property |
| 213 | + def server(self): |
| 214 | + _server = cache.get(self.cache_key, None) |
| 215 | + if not _server or not self.health(_server): |
| 216 | + self.cleanup() |
| 217 | + k8s_volume = {"name": self.volume.name} |
| 218 | + if self.volume.type == "csi": |
| 219 | + k8s_volume.update({"persistentVolumeClaim": {"claimName": self.volume.name}}) |
| 220 | + else: |
| 221 | + k8s_volume.update(self.volume.parameters) |
| 222 | + username, password = random_string(32), random_string(32) |
| 223 | + self.scheduler.pod.create(self.app_id, self.pod_name, settings.DRYCC_FILER_IMAGE, **{ |
| 224 | + "args": [ |
| 225 | + "filer", |
| 226 | + "--bind", self.bind, "--path", self.path, |
| 227 | + "--duration", f"{settings.DRYCC_FILER_DURATION}", |
| 228 | + "--waittime", f"{settings.DRYCC_FILER_WAITTIME}", |
| 229 | + "--username", f"{username}", "--password", f"{password}", |
| 230 | + ], |
| 231 | + "app_type": "filer", |
| 232 | + "replicas": 1, "deploy_timeout": 120, "volumes": [k8s_volume], |
| 233 | + "volume_mounts": [{"mountPath": self.path, "name": self.volume.name}], |
| 234 | + "image_pull_policy": "Always", |
| 235 | + }) |
| 236 | + address = self.scheduler.pod.get(self.app_id, self.pod_name).json()["status"]["podIP"] |
| 237 | + _server = {"address": address, "username": username, "password": password} |
| 238 | + cache.set(self.cache_key, _server, timeout=settings.DRYCC_FILER_DURATION) |
| 239 | + return _server |
| 240 | + |
| 241 | + @property |
| 242 | + def pod_name(self): |
| 243 | + return f"drycc-filer-{self.volume.name}" |
| 244 | + |
| 245 | + @property |
| 246 | + def cache_key(self): |
| 247 | + return f"filer:{self.pod_name}" |
| 248 | + |
| 249 | + def health(self, server): |
| 250 | + try: |
| 251 | + return self.request( |
| 252 | + "OPTIONS", server, timeout=2).headers.get('server') == 'drycc-filer' |
| 253 | + except requests.exceptions.Timeout: |
| 254 | + return False |
| 255 | + |
| 256 | + def cleanup(self): |
| 257 | + try: |
| 258 | + self.scheduler.pod.delete(self.app_id, self.pod_name) |
| 259 | + except KubeException: |
| 260 | + pass |
| 261 | + |
| 262 | + def request(self, method, server, path="/", **kwargs): |
| 263 | + cache.touch(self.cache_key, timeout=settings.DRYCC_FILER_DURATION) |
| 264 | + url = f"http://{server["address"]}:{self.bind.split(":")[1]}/{path}" |
| 265 | + kwargs["auth"] = HTTPBasicAuth(server["username"], server["password"]) |
| 266 | + return get_session().request(method, url, **kwargs) |
| 267 | + |
| 268 | + def get(self, path, **kwargs): |
| 269 | + return self.request("GET", self.server, path, **kwargs) |
| 270 | + |
| 271 | + def post(self, path, **kwargs): |
| 272 | + return self.request("POST", self.server, path, **kwargs) |
| 273 | + |
| 274 | + |
199 | 275 | if __name__ == "__main__": |
200 | 276 | import doctest |
201 | 277 | doctest.testmod() |
0 commit comments