Repository: jupyterhub/jupyterhub

Issue:
Deleting and recreating a named server results in lost name in GUI
<!--
Welcome to Zero to JupyterHub!

Before filing an issue, please search through the issues to see
if your question has been discussed before. If you
need more information after searching, feel
free to message us on the gitter channel. Many
JupyterHub community members watch the gitter channel
so you will have the benefit of other users' experience
as well as the JupyterHub team.

If you still wish to file an issue, please submit
as much detail about your issue as possible. If
you think it would be helpful, include a
scrubbed version of your `config.yaml` file. We've put
a place below where you can paste this in.

*** WARNING ***
Make sure you remove all sensitive information that's
in your `config.yaml` file, as GitHub is a public space.

Please remove at *least* the following fields:

* any special keys under auth
* proxy.secretToken
* hub.cookieSecret

If you post any sensitive information we reserve the
right to edit your comment in order to remove it.
-->

## Description

I've been working on a POC for my place of work to examine the feasibility of using JupyterHub to serve Jupyter Notebook/Lab servers with custom images containing a Python SDK we're working on.

Recently, I've been working on testing out named servers. In that process, I've discovered that if you delete a named server from the browser GUI, then recreate it in (in any fashion, whether by the REST API or through the GUI), that server will no longer appear listed.

## To reproduce

1. Create a named server:

![image](https://user-images.githubusercontent.com/11052254/55366470-c26c8000-549d-11e9-9fff-f5ff4e7ac5b4.png)

2. Delete it:

![image](https://user-images.githubusercontent.com/11052254/55366831-4b37eb80-549f-11e9-9fec-20d4298e55ab.png)

3. Create it again: `curl -X POST -H "Authorization: token a_very_secret_token" "http://my.host.domain/hub/api/users/pmende/servers/serverA"`

Now the user's Hub Control Panel/Home still no longer lists the server (i.e., it is identical to the image after 2, above), but there is definitely a running pod with the server name:

```
$ kubectl get pods -n jhub
NAME                         READY   STATUS    RESTARTS   AGE
hub-949c864ff-v7dx2          1/1     Running   0          18m
jupyter-pmende-2dserver-41   1/1     Running   0          3m44s
proxy-c88fd6f59-s8k82        1/1     Running   0          18m
```

## Hub creation

`helm upgrade --install jhub jupyterhub/jupyterhub --namespace jhub --version=0.9-8ed2f81 --values config.yaml`

## Contents of `config.yaml`
```
#########################
# Networking Config     #
#########################
proxy:
  secretToken: "mysupersecrettoken"
  service:
    type: NodePort
    nodePorts:
      http: 31212
  chp:
    resources:
      requests:
        memory: 0
        cpu: 0
ingress:
  enabled: true
  hosts:
    - my.host.domain
  rules:
  http:
    - paths: /hub/api
      backend:
        serviceName: hub
        servicePort: 8081

#########################
# Hardware/Image Config #
#########################
singleuser:
  image:
    name: jupyter/scipy-notebook
    tag: 59b402ce701d
  cpu:
    guarantee: 0.25
    limit: 0.5
  memory:
    guarantee: "256M"
    limit: "320M"
  profileList:
    - display_name: "Default"
      description: "0.25 CPU; 256M Ram"
      default: True
    - display_name: "BIG"
      description: "0.5 Whole CPUs, 512M Ram"
      kubespawner_override:
        cpu_guarantee: 0.5
        cpu_limit: 0.75
        mem_guarantee: "512M"
        mem_limit: "640M"

#########################
# Hub Config            #
#########################
hub:
  allowNamedServers: true
  extraConfig: |
    c.JupyterHub.admin_access = True
    c.JupyterHub.api_tokens = {
      "a_very_secret_token": "pmende"
    }

```



Discussion:
I think this may be very valuable feedback for the JupyterHub repository, but I fail to transfer the issue there.

Ping: @minrk 
Yes, this is likely a result of the underlying JupyterHub implementation details, and not specific to JupyterHub on Kubernetes. Happy to have it transferred.
Migrated issue to jupyterhub. Will need to do some testing. There appear to be two things wrong:

1. when deleting the server, the pod should have been deleted. This *may* be a kubespawner bug, but I'm suspecting it's not. Delete is two actions: stop the server and then delete the entry. It looks like stop may have been skipped. Stop is what deletes the pod.
2. removal from the UI is a different, should be unrelated bug. I'm not sure how that would happen, but thanks for the test case.

Resolving patch:
diff --git a/jupyterhub/apihandlers/users.py b/jupyterhub/apihandlers/users.py
--- a/jupyterhub/apihandlers/users.py
+++ b/jupyterhub/apihandlers/users.py
@@ -427,6 +427,7 @@ def _remove_spawner(f=None):
                 return
             self.log.info("Deleting spawner %s", spawner._log_name)
             self.db.delete(spawner.orm_spawner)
+            user.spawners.pop(server_name, None)
             self.db.commit()
 
         if server_name:


Test patch:
diff --git a/jupyterhub/tests/test_named_servers.py b/jupyterhub/tests/test_named_servers.py
--- a/jupyterhub/tests/test_named_servers.py
+++ b/jupyterhub/tests/test_named_servers.py
@@ -162,8 +162,10 @@ async def test_delete_named_server(app, named_servers):
     )
     r.raise_for_status()
     assert r.status_code == 204
-    # low-level record is now removes
+    # low-level record is now removed
     assert servername not in user.orm_spawners
+    # and it's still not in the high-level wrapper dict
+    assert servername not in user.spawners
 
 
 async def test_named_server_disabled(app):