# Installation ## Traefik-proxy installation 1. Install **JupyterHub**: ``` $ python3 -m pip install jupyterhub ``` 2. Install **jupyterhub-traefik-proxy**, which is available now as pre-release: ``` python3 -m pip install jupyterhub-traefik-proxy ``` 3. In order to be able to launch JupyterHub with traefik-proxy or run the tests, **traefik**, **etcd** and **consul** must first be installed and added to your `PATH`. There are two ways you can install traefik, etcd and consul: 1. Through traefik-proxy's **install utility**. ``` $ python3 -m jupyterhub_traefik_proxy.install --traefik --etcd --consul --output=/usr/local/bin ``` This will install the default versions of traefik, etcd and consul, namely `traefik-1.7.5`, `etcd-3.3.10` and `consul_1.5.0` to `/usr/local/bin` specified through the `--output` option. It is also possible to install the binaries individually. For example to install traefik only: ``` $ python3 -m jupyterhub_traefik_proxy.install --traefik --output=/usr/local/bin ``` If no directory is passed to the installer, a *dependencies* directory will be created in the `traefik-proxy` directory. In this case, you **must** add this directory to `PATH`, e.g. ``` $ export PATH=$PATH:{$PWD}/dependencies ``` If you want to install other versions of traefik, etcd and consul in a directory of your choice, just specify it to the installer through the following arguments: * `--traefik-version` * `--etcd-version` * `--consul-version` * `--output` Example: ``` $ python3 -m jupyterhub_traefik_proxy.install --traefik --etcd --consul --output=dep \ --traefik-version=1.6.6 --etcd-version=3.2.24 --consul-version=1.5.0 ``` If the desired install directory doesn't exist, it will be created by the installer. To get a list of all the available options: ``` $ python3 -m jupyterhub_traefik_proxy.install --help ``` 2. From traefik, etcd and consul **release pages**: * Install [`traefik`](https://traefik.io/#easy-to-install) * Install [`etcd`](https://github.com/etcd-io/etcd/releases) * Install [`consul`](https://github.com/hashicorp/consul/releases) ## Enabling traefik-proxy in JupyterHub [TraefikTomlProxy](https://github.com/jupyterhub/traefik-proxy/blob/master/jupyterhub_traefik_proxy/toml.py), [TraefikEtcdProxy](https://github.com/jupyterhub/traefik-proxy/blob/master/jupyterhub_traefik_proxy/etcd.py) and [TraefikConsulProxy](https://github.com/jupyterhub/traefik-proxy/blob/master/jupyterhub_traefik_proxy/consul.py) are custom proxy implementations that subclass [Proxy](https://github.com/jupyterhub/jupyterhub/blob/master/jupyterhub/proxy.py) and can register in JupyterHub config using `c.JupyterHub.proxy_class` entrypoint. On startup, JupyterHub will look by default for a configuration file, *jupyterhub_config.py*, in the current working directory. If the configuration file is not in the current working directory, you can load a specific config file and start JupyterHub using: ``` $ jupyterhub -f /path/to/jupyterhub_config.py ``` There is an example configuration file [here](https://github.com/jupyterhub/traefik-proxy/blob/master/examples/jupyterhub_config.py) that configures JupyterHub to run with *TraefikEtcdProxy* as the proxy and uses dummyauthenticator and simplespawner to enable testing without administrative privileges. In *jupyterhub_config.py*: ``` c.JupyterHub.proxy_class = "traefik_toml" # will configure JupyterHub to run with TraefikTomlProxy ``` ``` c.JupyterHub.proxy_class = "traefik_etcd" # will configure JupyterHub to run with TraefikEtcdProxy ``` ``` c.JupyterHub.proxy_class = "traefik_consul" # will configure JupyterHub to run with TraefikConsulProxy ``` ## Implementation details 1. **Traefik Dashboard** Traefik provides a Web UI **dashboard** where you can see the frontends and backends registered, the routing rules, some metrics, but also other configuration elements. Find out more about traefik api's, [here](https://docs.traefik.io/configuration/api/#security). Because of **security** concerns, in traefik-proxy implementation, traefik api endpoint isn't exposed on the public http endpoint. Instead, it runs on a dedicated **authenticated endpoint** that's on localhost by default. The port on which traefik-proxy's api will run, as well as the username and password used for authenticating, can be passed to the proxy through `jupyterhub_config.py`, e.g.: ``` c.TraefikTomlProxy.traefik_api_url = "http://127.0.0.1:8099" c.TraefikTomlProxy.traefik_api_password = "admin" c.TraefikTomlProxy.traefik_api_username = "admin" ``` Check out TraefikProxy's **API Reference** for more configuration options.

2. **TKvProxy class** TKvProxy is a JupyterHub Proxy implementation that uses traefik and a key-value store. **TraefikEtcdProxy** and **TraefikConsulProxy** are proxy implementations that sublass `TKvProxy`. Other custom proxies that wish to implementat a JupyterHub Trafik KV store Proxy can sublass `TKvProxy`. **TKvProxy** implements JupyterHub's Proxy public API and there is no need to override these public methods. The methods that **must be implemented** by the proxies that sublass `TKvProxy` are: * ***_define_kv_specific_static_config()*** * Define the traefik static configuration that configures traefik's communication with the key-value store. * Will be called during startup if should_start is True. * Subclasses must define this method if the proxy is to be started by the Hub. * In order to be picked up by the proxy, the static configuration must be stored into `proxy.static_config` dict under the `kv_name` key. * ***_kv_atomic_add_route_parts(jupyterhub_routespec, target, data, route_keys, rule)*** * Add the key-value pairs associated with a route within a key-value store transaction. * Will be called during add_route. * When retrieving or deleting a route, the parts of a route are expected to have the following structure: ``` [ key: jupyterhub_routespec , value: target ] [ key: target , value: data ] [ key: route_keys.backend_url_path , value: target ] [ key: route_keys.frontend_rule_path , value: rule ] [ key: route_keys.frontend_backend_path, value: route_keys.backend_alias] [ key: route_keys.backend_weight_path , value: w(int) ] # where w is the weight of the backend to be used during load balancing) ``` * Returns: * result (tuple): * The transaction status (int, 0: failure, positive: success) * The transaction response(str) * ***_kv_atomic_delete_route_parts(jupyterhub_routespec, route_keys)*** * Delete the key-value pairs associated with a route, within a key-value store transaction (if the route exists). * Will be called during delete_route. * The keys associated with a route are: * jupyterhub_routespec * target * route_keys.backend_url_path * route_keys.frontend_rule_path * route_keys.frontend_backend_path * route_keys.backend_weight_path * Returns: * result (tuple): * The transaction status (int, 0: failure, positive: success) * The transaction response (str) * ***_kv_get_target(jupyterhub_routespec)*** * Retrive the target from the key-value store. * The target is the value associated with `jupyterhub_routespec` key. * Returns: * The full URL associated with this route (str) * ***_kv_get_data(target)*** * Retrive the data associated with the `target` from the key-value store. * Returns: * A JSONable dict that holds extra info about the route (dict) * ***_kv_get_route_parts(kv_entry)*** * Retrive all the parts that make up a route (i.e. routespec, target, data) from the key-value store given a `kv_entry`. * A `kv_entry` is a key-value store entry where the key starts with `proxy.jupyterhub_prefix`. It is expected that only the routespecs will be prefixed with `proxy.jupyterhub_prefix` when added to the kv store. * Returns: * routespec: The normalized route specification passed in to add_route ([host]/path/) * target: The target host for this route (proto://host) * data: The arbitrary data dict that was passed in by JupyterHub when adding this route. * ***_kv_get_jupyterhub_prefixed_entries()*** * Retrive from the kv store all the key-value pairs where the key starts with `proxy.jupyterhub_prefix`. * It is expected that only the routespecs will be prefixed with `proxy.jupyterhub_prefix` when added to the kv store. * Returns: * routes: A list of key-value store entries where the keys start with `proxy.jupyterhub_prefix`. ## Testing jupyterhub-traefik-proxy There are some tests that use *etcdctl* command line client for etcd. Make sure to set environment variable ETCDCTL_API=3 before running the tests, so that the v3 API to be used, e.g.: ``` $ export ETCDCTL_API=3 ``` You can then run the all the test suite from the *traefik-proxy* directory with: ``` $ pytest -v ./tests ``` Or you can run a specific test with: ``` $ pytest -v ./tests/ ```