def start(self): if self.user_storage_class is not None and self.user_storage_capacity is not None: pvc_manifest = self.get_pvc_manifest() try: yield self.httpclient.fetch(self.request( url=k8s_url(self.namespace, 'persistentvolumeclaims'), body=json.dumps(pvc_manifest), method='POST', headers={'Content-Type': 'application/json'} )) except: self.log.info("Pvc " + self.pvc_name + " already exists, so did not create new pod.") pod_manifest = self.get_pod_manifest() yield self.httpclient.fetch(self.request( url=k8s_url(self.namespace, 'pods'), body=json.dumps(pod_manifest), method='POST', headers={'Content-Type': 'application/json'} )) while True: data = yield self.get_pod_info(self.pod_name) if data is not None and self.is_pod_running(data): break yield gen.sleep(1) self.user.server.ip = data['status']['podIP'] self.user.server.port = 8888 self.db.commit()
def stop(self, now=False): body = { 'kind': "DeleteOptions", 'apiVersion': 'v1', 'gracePeriodSeconds': 0 } req_args = { 'method': 'DELETE', 'body': json.dumps(body), 'headers': { 'Content-Type': 'application/json' }, # Tornado's client thinks DELETE requests shouldn't have a body # which is a bogus restriction 'allow_nonstandard_methods': True } yield self.httpclient.fetch( self.request(url=k8s_url(self.namespace, 'pods', self.pod_name), **req_args)) yield self.httpclient.fetch( self.request(url=k8s_url(self.namespace, 'services', self.svc_name), **req_args)) if not now: # If now is true, just return immediately, do not wait for # shut down to complete while True: pod = yield self.get_pod_info(self.pod_name) svc = yield self.get_nodeport_info(self.svc_name) if pod is None and svc is None: break yield gen.sleep(1)
def test_k8s_url(): assert k8s_url('default', 'pods') == '/api/v1/namespaces/default/pods' assert k8s_url('jupyter', 'pods', 'test') == '/api/v1/namespaces/jupyter/pods/test' assert k8s_url( 'default', 'persistentvolumeclaims', 'test') == '/api/v1/namespaces/default/persistentvolumeclaims/test' assert k8s_url( 'jupyter', 'persistentvolumeclaims', 'test') == '/api/v1/namespaces/jupyter/persistentvolumeclaims/test'
def start(self): if self.user_storage_class is not None and self.user_storage_capacity is not None: pvc_manifest = self.get_pvc_manifest() try: yield self.httpclient.fetch( self.request(url=k8s_url(self.namespace, 'persistentvolumeclaims'), body=json.dumps(pvc_manifest), method='POST', headers={'Content-Type': 'application/json'})) except: self.log.info("Pvc " + self.pvc_name + " already exists, so did not create new pvc.") # If we run into a 409 Conflict error, it means a pod with the # same name already exists. We stop it, wait for it to stop, and # try again. We try 4 times, and if it still fails we give up. # FIXME: Have better / cleaner retry logic! retry_times = 4 pod_manifest = yield self.get_pod_manifest() for i in range(retry_times): try: yield self.httpclient.fetch( self.request(url=k8s_url(self.namespace, 'pods'), body=json.dumps(pod_manifest), method='POST', headers={'Content-Type': 'application/json'})) break except HTTPError as e: if e.code != 409: # We only want to handle 409 conflict errors self.log.exception("Failed for %s", json.dumps(pod_manifest)) raise self.log.info('Found existing pod %s, attempting to kill', self.pod_name) yield self.stop(True) self.log.info( 'Killed pod %s, will try starting singleuser pod again', self.pod_name) else: raise Exception( 'Can not create user pod %s already exists & could not be deleted' % self.pod_name) while True: data = yield self.get_pod_info(self.pod_name) if data is not None and self.is_pod_running(data): break yield gen.sleep(1) return (data['status']['podIP'], self.port)
def stop(self, now=False): body = { 'kind': "DeleteOptions", 'apiVersion': 'v1', 'gracePeriodSeconds': 0 } yield self.httpclient.fetch( self.request( url=k8s_url(self.namespace, 'pods', self.pod_name), method='DELETE', body=json.dumps(body), headers={'Content-Type': 'application/json'}, # Tornado's client thinks DELETE requests shouldn't have a body # which is a bogus restriction allow_nonstandard_methods=True, ) ) if not now: # If now is true, just return immediately, do not wait for # shut down to complete while True: data = yield self.get_pod_info(self.pod_name) if data is None: break yield gen.sleep(1)
def start(self): pod_manifest = self.get_pod_manifest() yield self.httpclient.fetch(self.request( url=k8s_url(self.namespace, 'pods'), body=json.dumps(pod_manifest), method='POST', headers={'Content-Type': 'application/json'} )) while True: data = yield self.get_pod_info(self.pod_name) if data is not None and self.is_pod_running(data): break yield gen.sleep(1) self.user.server.ip = data['status']['podIP'] self.user.server.port = 8888 self.db.commit()
def get_pod_info(self, pod_name): """ Fetch info about a specific pod with the given pod name in current namespace Return `None` if pod with given name does not exist in current namespace """ try: response = yield self.httpclient.fetch( self.request(k8s_url( self.namespace, 'pods', pod_name, ))) except HTTPError as e: if e.code == 404: return None raise data = response.body.decode('utf-8') return json.loads(data)
def get_pvc_info(self, pvc_name): """ Fetch info about a specific pvc with the given pvc name in current namespace Return `None` if pvc with given name does not exist in current namespace """ try: response = yield self.httpclient.fetch(self.request( k8s_url( self.namespace, 'persistentvolumeclaims', pvc_name, ) )) except HTTPError as e: if e.code == 404: return None raise data = response.body.decode('utf-8') return json.loads(data)
def get_nodeport_info(self, svc_name): """ Fetch a service definition in the current namespace The interesting bits are in `spec.ports[*].nodePort` which is where dynamically allocated node ports can be found Return `None` if the service doesn't exist """ try: response = yield self.httpclient.fetch( self.request(k8s_url( self.namespace, 'services', svc_name, ))) except HTTPError as e: if e.code == 404: return None raise data = response.body.decode('utf-8') return json.loads(data)
def test_k8s_url(): assert k8s_url('default', 'pods') == '/api/v1/namespaces/default/pods' assert k8s_url('jupyter', 'pods', 'test') == '/api/v1/namespaces/jupyter/pods/test' assert k8s_url('default', 'persistentvolumeclaims', 'test') == '/api/v1/namespaces/default/persistentvolumeclaims/test' assert k8s_url('jupyter', 'persistentvolumeclaims', 'test') == '/api/v1/namespaces/jupyter/persistentvolumeclaims/test'
def test_k8s_url(): assert k8s_url('default', 'pods') == '/api/v1/namespaces/default/pods' assert k8s_url('jupyter', 'pods', 'test') == '/api/v1/namespaces/jupyter/pods/test'