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()
Example #2
0
 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)
Example #3
0
 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()
Example #4
0
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'
Example #5
0
    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)
Example #6
0
 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 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)
Example #8
0
 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()
Example #9
0
    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)
Example #11
0
    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'
Example #13
0
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'