Explorar el Código

HUE-8724 [k8s] Provide how to run Hue server in Kubernetes

Romain Rigaux hace 6 años
padre
commit
dcca94e
Se han modificado 31 ficheros con 83 adiciones y 2665 borrados
  1. 6 0
      README.md
  2. 12 237
      tools/kubernetes/README.md
  3. 0 5
      tools/kubernetes/helm/frontend/Chart.yaml
  4. 0 95
      tools/kubernetes/helm/frontend/README.md
  5. 0 72
      tools/kubernetes/helm/frontend/templates/provisioner.yaml
  6. 0 0
      tools/kubernetes/helm/hue/.helmignore
  7. 5 0
      tools/kubernetes/helm/hue/Chart.yaml
  8. 33 0
      tools/kubernetes/helm/hue/README.md
  9. 1 9
      tools/kubernetes/helm/hue/templates/NOTES.txt
  10. 0 0
      tools/kubernetes/helm/hue/templates/hue-postgres.yaml
  11. 3 6
      tools/kubernetes/helm/hue/templates/hue.yaml
  12. 0 0
      tools/kubernetes/helm/hue/templates/traefik.yaml
  13. 3 5
      tools/kubernetes/helm/hue/values.yaml
  14. 1 1
      tools/kubernetes/helm/impala-engine/README.md
  15. 2 4
      tools/kubernetes/helm/mock-storage/README.md
  16. 0 0
      tools/kubernetes/services/hue/Dockerfile
  17. 17 0
      tools/kubernetes/services/hue/README.md
  18. 0 0
      tools/kubernetes/services/hue/conf/hue.ini
  19. 0 0
      tools/kubernetes/services/hue/conf/log.conf
  20. 0 0
      tools/kubernetes/services/hue/startup.sh
  21. 0 1
      tools/kubernetes/services/mock-provisioner/.gitignore
  22. 0 36
      tools/kubernetes/services/mock-provisioner/Dockerfile
  23. 0 15
      tools/kubernetes/services/mock-provisioner/README.md
  24. 0 13
      tools/kubernetes/services/mock-provisioner/command.js
  25. 0 7
      tools/kubernetes/services/mock-provisioner/config
  26. 0 7
      tools/kubernetes/services/mock-provisioner/config.templ
  27. 0 250
      tools/kubernetes/services/mock-provisioner/model.js
  28. 0 1372
      tools/kubernetes/services/mock-provisioner/package-lock.json
  29. 0 26
      tools/kubernetes/services/mock-provisioner/package.json
  30. 0 419
      tools/kubernetes/services/mock-provisioner/provisioner.yaml
  31. 0 85
      tools/kubernetes/services/mock-provisioner/server.js

+ 6 - 0
README.md

@@ -44,6 +44,12 @@ Start Hue in a single click with the [Docker Guide](https://github.com/cloudera/
 [video blog post](http://gethue.com/getting-started-with-hue-in-2-minutes-with-docker/).
 
 
+Kubernetes
+----------
+One Helm chart to start a Hue server is available at ``tools/kubernetes/helm/frontend``. The Kubernetes [hue.yaml](tools/kubernetes/helm/frontend/templates/hue.yaml)
+configuration is available in the template directoy.
+
+
 Community
 -----------
    * User group: http://groups.google.com/a/cloudera.org/group/hue-user

+ 12 - 237
tools/kubernetes/README.md

@@ -1,242 +1,26 @@
-# Cloud-Native Data Warehouse
+# Hue on Kubernetes
 
-Cloud-native data warehouse app powered by Impala, Hue and Kubernetes.
-
-
-Goals
-
-* Rapidly install the product in the public and private cloud
-* Self service per-tenant data warehouses provisioniong
-* Browse and query data across multiple data warehouses from a unified UI
-* Self-heal on process or node failures
-* Gracefully scale down resources without query failures
-* Autoscale data warehouses and cluster nodes based on usage
-* Intelligently tune performance without human intervention
-
-Done
-
-* Single line install in any Kubernetes cluster
-* Create, scale up/down compute warehouse (with v1 graceful shutdown of executors)
-* Autoscale warehouse based on CPU (HPA)
-* Basic warehouse metrics like queries in flight, total number of queries... (note: to change to directly come the impalads)
-* Easy troubleshooting of queries: via humanized query profile, query improvement recommendations...
-
-Todo
-
-* [ ] Readiness/Liveness probes
-* [ ] Auto pause/Auto resume warehouses
-* [ ] Productionize provisioning service in Thunderhead
-* [ ] Add priority class and preemption support
-* [ ] Session cookie affinity for Hue when load-balanced
-* [ ] Validate external storage (HDFS, HMS)
-* [ ] Run containers as non root
-* [ ] Add proper metric endpoint to impalads and scrape every 5s
-* [ ] Persist DB
-* [ ] Log collection
-* [ ] Security: Istio, etc. TBD
-* [ ] Lot of stability testing when warehouses come up & down, shrink & grow...
+How to run Hue in Kubernetes.
 
 
 ## Quick Start
 
-Assuming you have a Kubernetes cluster configured with [Helm][3] installed and images pushed (if not, check the [K8s Cluster](#K8s_Cluster) section below):
-
-1. Remote storage
-
-Boot a `finance` compute pointing to a remote storage (here in cluster `data-lake-1.gethue.com`):
-
-```
-helm install impala-engine --set-string metastore.uris=thrift://data-lake-1.gethue.com:9083,hdfs.namenode.host=data-lake-1.gethue.com,hdfs.namenode.port=8020,name=finance -n finance --repo=http://dataware-1.gethue.com:8879
-```
-
-And follow-up the instructions printed on the screen to connect and execute a SQL query. Just before doing this thought, double check that all the containers are running:
-
-```
-kubectl get pods
-NAME                                          READY   STATUS              RESTARTS   AGE
-impala-catalog-fin2-0                     1/1     Running   0          8s
-impala-coordinator-fin2-0                 1/1     Running   0          8s
-impala-statestore-fin2-84b9844686-rng9n   1/1     Running   0          8s
-impala-worker-fin2-7d6df9ff54-wpt5h       1/1     Running   0          8s
-```
-
-Afterwards, to delete the `finance` compute:
-
-```
-helm delete finance --purge
-```
-
-2. Local storage
-
-For quick use and pointing to a local storage, boot the local storage and warehouse:
-
-```
-helm repo add cloudera http://gethue.com:8879
-
-helm install cloudera/mock-storage
-helm install cloudera/impala-engine --set-string name=finance -n finance
-```
-
-And follow-up the instructions printed on the screen.
-
-Note:
-
-To serve the packages
-
-```
-mkdir repo
-
-helm package impala-engine -d repo
-helm package frontend -d repo
-helm package mock-storage -d repo
-
-helm serve --repo-path repo --address gethue.com:8879
-```
-
-3. Web UI
-
-Instead of using the CLI, use the Web UI to create the `finance` warehouse in 3 clicks:
-
-```
-helm install cloudera/frontend
-helm install cloudera/mock-storage
-
-```
-
-And follow-up the instructions printed on the screen.
-
-## Installation
-
-Get the Helm charts packages:
-
-```
-cd tools/kubernetes
-```
-
-We have no pods:
-
-```
-kubectl get pods
-No resources found.
-```
-
-Boot the warehouse app:
-
-```
-helm install mock-storage
-helm install frontend
-```
-
-Optional: make sure to set [config.yaml](helm/config.yaml) to match your configuration with S3 keys, e.g.:
-
-```
-helm upgrade -f config.yaml frontend
-```
-
-
-Now you should see something like:
-
-```
-kubectl get pods
-NAME                                          READY     STATUS    RESTARTS   AGE
-hdfs-datanode-285-0                           1/1       Running   0          4h
-hdfs-namenode-285-0                           1/1       Running   0          4h
-hive-78dbbdcf96-qrhtf                         2/2       Running   0          4h
-hue-f9bph                                     1/1       Running   0          4h
-hue-postgres-n65kz                            1/1       Running   0          4h
-provisioner-4xgb4                             1/1       Running   0          4h
-traefik-ingress-controller-657d9596f9-kzg7f   1/1       Running   0          4h
-```
-
-Optional: if not present, install Prometheus in order to get the warehouse metrics in the Frontend:
-
-```
-helm install stable/prometheus --name prometheus --namespace prometheus
-```
-
-Now, let's go querying!
-
-1. From CLI
-
-Via the Impala engine chart and boot a `finance` compute warehouse:
-
-```
-helm install impala-engine --set-string name=finance -n finance
-```
-
-Now you should see something like this, where this is one `finance` warehouse with 1 worker pod:
-
-```
-kubectl get pods
-NAME                                          READY     STATUS    RESTARTS   AGE
-hdfs-datanode-285-0                           1/1       Running   0          4h
-hdfs-namenode-285-0                           1/1       Running   0          4h
-hive-78dbbdcf96-qrhtf                         2/2       Running   0          4h
-hue-f9bph                                     1/1       Running   0          4h
-hue-postgres-n65kz                            1/1       Running   0          4h
-impala-catalog-finance-0                            1/1       Running   0          4h
-impala-coordinator-finance-0                        1/1       Running   0          4h
-impala-statestore-finance-84bcb88684-mlvr7          1/1       Running   0          4h
-impala-worker-finance-7d4fd7445f-6fjlt              1/1       Running   0          4h
-provisioner-4xgb4                             1/1       Running   0          4h
-traefik-ingress-controller-657d9596f9-kzg7f   1/1       Running   0          4h
-```
-
-Go to a worker node:
-
-```
-export WORKER_POD=$(kubectl get pods -l name=impala-worker-finance -o jsonpath="{.items[0].metadata.name"})
-kubectl exec -it $WORKER_POD bash
-```
-
-```
-impala-shell -i impala-coordinator-finance
-```
-
-And now you can type some SQL:
-
-```
-SHOW TABLES;
-CREATE TABLE logs (message string);
-```
-
-If S3 was configured, pick up some external tables here.
-
-Read more about the Compute Warehouse services at:
-
-* [Impala engine service](helm/impala-engine)
-* Altus CLI command (TBD)
-
-2. Manage and query the Data Warehouse via the Web UI or API:
-
-* [Web App](helm/frontend#Web)
-* [REST API](helm/frontend#API)
-
-
-## Architecture
-
-To understand the architecture, look at the `chart` folder.  Installing the chart installs Hue, HMS,
-PostgreSQL and an Impala provisioning service.  The Impala provisioning service is a thin Altus-style
-API that proxies the Kubernetes API.  For details see the OpenAPI provisioner [service spec](services/provisioner/provisioner.yaml).  Impala
-components (`impalad`, `statestored`, and `catalogd`) run as Kubernetes [Deployments][4].
-
-Kubernetes [lifecycle hooks][5] ensure graceful termination by leveraging [IMPALA-1760][2].
-
-Autoscaling of the underlying Kubernetes cluster relies on built in cluster autoscaling
-capabilities.  When `impalad` pods cannot be scheduled due to inadequate CPU or memory, new nodes
-are added automatically by the cluster autoscaler.
-
-The autoscaling design for Impala is TBD.  It could be accomplished with using [Horizontal Pod Autoscaling][7]
-and [custom metrics][8] or a custom controller.  A custom controller may
-be more appropriate if the long-term vision is to automatically tune the Impala cluster.
+Assuming you have a Kubernetes cluster configured with Helm installed and images pushed (if not, check the [K8s Cluster](#k8s-cluster) section below).
 
+* [Helm](helm)
+   * [Hue](helm/hue)
+* YAML
+   * NGINX (TBD)
+   * Task Server (TBD)
+* Containers
+   * [Hue](services/hue)
 
 ## K8s Cluster
 
 ### Ubuntu
 
-OS: Ubuntu 16.04 or 18.04.
-Node count and size: 4 primary instances of m3.xlarge (4CPU 15GB) or 1 m3.2xlarge (8CPU 30GB).
+* OS: Ubuntu 16.04 or 18.04.
+* Nodes: 1 primary instance of m3.medium (1CPU 3GB).
 
 https://microk8s.io/#quick-start
 
@@ -279,12 +63,3 @@ HTTP LoadBalancer. This avoids creating global static ips.
 
 All the images can currently can be built via the [services](services).
 
-
-[1]: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/
-[2]: https://jira.apache.org/jira/browse/IMPALA-1760
-[3]: https://helm.sh/
-[4]: https://kubernetes.io/docs/concepts/workloads/controllers/deployment/
-[5]: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/
-[6]: https://kubernetes.io/docs/concepts/services-networking/service/#proxy-mode-ipvs
-[7]: https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/
-[8]: https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/#support-for-custom-metrics

+ 0 - 5
tools/kubernetes/helm/frontend/Chart.yaml

@@ -1,5 +0,0 @@
-name: frontend
-description: Cloudera Data Warehouse UI
-version: 1.0.0
-appVersion: "1.0"
-apiVersion: v1

+ 0 - 95
tools/kubernetes/helm/frontend/README.md

@@ -1,95 +0,0 @@
-# Cloudera Data Warehouse Frontend Chart
-
-This is a MVP to get an Helm chart to boot the frontend of the Data Warehouse. It is the main dependency as
-the warehouses can then be created directly via the Web UI.
-
-
-## Install
-
-To boot a the Web UI:
-
-```
-helm install frontend --set-string helmRepo=http://dataware-1.vpc.cloudera.com:8879 -n frontend
-```
-
-Or copy [values.yaml](values.yaml) and edit and run:
-
-```
-helm install frontend -f values.yaml
-```
-
-E.g. by default it comes with the UI, a temporary DB and a REST API:
-
-```
-kubectl get pods
-NAME                                          READY   STATUS    RESTARTS   AGE
-hue-4n2ck                                     1/1       Running   0          3h
-hue-postgres-5jg77                            1/1       Running   0          12d
-provisioner-cp2df                             1/1       Running   0          12d
-traefik-ingress-controller-6fbd76695d-nkxnz   1/1       Running   0          12d
-```
-
-## Web App
-
-The URL of the frontend is printed when the chart is installed. If you missed it, you could:
-
-Currently get the URL via:
-
-```
-export WEB_HOST=$(kubectl get node -o jsonpath="{.items[0].metadata.name}")
-export WEB_PORT=$(kubectl get service hue -o jsonpath="{.spec.ports[*].nodePort}"
-```
-
-http://$WEB_HOST:$WEB_PORT
-
-On the Data Warehouse page, create warehouses that will spawn Impala compute containers.
-
-Query table via the Editor. If using S3, pick up some [CREATE TABLES here](http://github.mtv.cloudera.com/romain/SQL-test-cases/tree/master/altus-dw).
-If using HDFS, use the Data Import http://$WEB_HOST:$WEB_PORT/hue/importer.
-
-## API
-
-Same as the Web App, the API URL is listed at installation. To get it later, either port-forward the API or use the NodePort, e.g.:
-
-```
-export API_HOST=$(kubectl get node -o jsonpath="{.items[0].metadata.name}")
-export API_PORT=$(kubectl get service provisioner -o jsonpath="{.spec.ports[*].nodePort}")
-
-echo http://$API_HOST:$API_PORT
-```
-
-or port forward to localhost:
-
-```
-kubectl port-forward svc/provisioner 4747:4747 &
-```
-
-Make sure the API runs:
-
-```sh
-curl localhost:4747
-{"app":"cloudera-dw-provisioner","version":"0.1"}
-```
-
-List warehouses:
-
-```sh
-curl -X POST localhost:4747/dw/listClusters
-{"clusters":[{"cdhVersion":"CDH6.3","workerCpuCores":2,"workerMemoryInGib":4,"workerReplicas":1,"workerAutoResize":false,"workercurrentCPUUtilizationPercentage":0,"clusterName":"a6","name":"a6","crn":"a6","creationDate":"2018-12-21T03:50:04.525Z","status":"ONLINE","workerReplicasOnline":0,"coordinatorEndpoint":{"privateHost":"impala-coordinatora6","publicHost":"impala-coordinatora6","port":21050}}]}
-```
-
-Create a warehouse:
-
-Note: currently cluster names should be **alpha** only with possible **number** as suffix, e.g. finance, trailpay5...
-
-
-```sh
-curl -d '{"clusterName":"finance2", "workerCpuCores":1, "workerMemoryInGib":1, "workerReplicas":1}' -H "Content-Type: application/json" -X POST localhost:4747/dw/createCluster
-```
-
-See the complete list of operations at [provisioner.yaml](../../services/mock-provisioner/provisioner.yaml).
-
-## Follow-ups
-
-* Move provisioner to Thunderhead API
-* Productionize DB persistence

+ 0 - 72
tools/kubernetes/helm/frontend/templates/provisioner.yaml

@@ -1,72 +0,0 @@
-apiVersion: v1
-kind: ConfigMap
-metadata:
-  name: provisioner-config
-data:
-  config: |
-    var config = {};
-    
-    config.helmRepo = "{{ .Values.helmRepo }}";
-    config.registry = "{{ .Values.registry }}";
-    config.registryImpalaTag = "{{ .Values.registryImpalaTag }}";
-
-    module.exports = config;
----
-apiVersion: v1
-kind: ReplicationController
-metadata:
-  name: provisioner
-spec:
-  replicas: 1
-  template:
-    metadata:
-      labels:
-        app: provisioner
-      # Until Impala has native metric support
-      annotations:
-        prometheus.io/scrape: 'true'
-        prometheus.io/port: '4747'
-    spec:
-      containers:
-        - name: provisioner
-          image: {{ .Values.registry }}/mock-provisioner:{{ .Values.tag }}
-          imagePullPolicy: Always
-          ports:
-            - containerPort: 4747
-          volumeMounts:
-            - name: config-volume
-              mountPath: /app/config
-              subPath: config
-      volumes:
-        - name: config-volume
-          configMap:
-            name: provisioner-config
----
-apiVersion: v1
-kind: Service
-metadata:
-  name: provisioner
-spec:
-  selector:
-    app: provisioner
-  ports:
-  - name: provisioner
-    port: 80
-    targetPort: 4747
-  type: NodePort
----
-apiVersion: extensions/v1beta1
-kind: Ingress
-metadata:
-  name: provisioner
-  annotations:
-    kubernetes.io/ingress.class: traefik
-spec:
-  rules:
-  - host: provisioner.{{ .Values.domain }}
-    http:
-      paths:
-      - path: /
-        backend:
-          serviceName: provisioner
-          servicePort: provisioner

+ 0 - 0
tools/kubernetes/helm/frontend/.helmignore → tools/kubernetes/helm/hue/.helmignore


+ 5 - 0
tools/kubernetes/helm/hue/Chart.yaml

@@ -0,0 +1,5 @@
+name: hue
+description: Hue SQL Data Warehouse Editor
+version: 1.0.0
+appVersion: "1.0"
+apiVersion: v1

+ 33 - 0
tools/kubernetes/helm/hue/README.md

@@ -0,0 +1,33 @@
+# Hue Chart
+
+This is a MVP to get an Helm chart for Hue.
+
+
+## Install
+
+```
+cd tools/kubernetes/helm/hue
+```
+
+If needed, copy [values.yaml](values.yaml) and edit and run:
+
+```
+helm install hue -f values.yaml
+```
+
+And follow-up the instructions printed on the screen.
+
+E.g. by default it comes with the UI, a temporary DB and a REST API:
+
+```
+kubectl get pods
+NAME                                          READY   STATUS    RESTARTS   AGE
+hue-4n2ck                                     1/1       Running   0          3h
+hue-postgres-5jg77                            1/1       Running   0          12d
+traefik-ingress-controller-6fbd76695d-nkxnz   1/1       Running   0          12d
+```
+
+## Follow-ups
+
+* Parameterize the Database
+* Productionize DB persistence

+ 1 - 9
tools/kubernetes/helm/frontend/templates/NOTES.txt → tools/kubernetes/helm/hue/templates/NOTES.txt

@@ -1,4 +1,4 @@
-Congratulations, you've launched the Cloudera Data Warehouse!
+Congratulations, you've launched the Hue SQL Editor for Data Warehouses!
 
 To check the status of your installation run:
 
@@ -19,12 +19,4 @@ Or directly:
 
   echo http://$WEB_HOST:$WEB_PORT
 
-
-The REST API is available at:
-
-  export API_HOST=$(kubectl get node -o jsonpath="{.items[0].metadata.name}")
-  export API_PORT=$(kubectl get service provisioner -o jsonpath="{.spec.ports[*].nodePort}" --namespace {{ .Release.Namespace }})
-
-  echo http://$API_HOST:$API_PORT
-
 Happy Querying!

+ 0 - 0
tools/kubernetes/helm/frontend/templates/hue-postgres.yaml → tools/kubernetes/helm/hue/templates/hue-postgres.yaml


+ 3 - 6
tools/kubernetes/helm/frontend/templates/hue.yaml → tools/kubernetes/helm/hue/templates/hue.yaml

@@ -4,11 +4,8 @@ metadata:
   name: hue-config
 data:
   hue-ini: |
-    [metadata]
-    [[k8s]]
-    api_url={{ .Values.hue.provisionerApi }}
-    [[prometheus]]
-    api_url=http://{{ .Values.hue.prometheusHost }}/api
+    [impala]
+    server_host={{ .Values.impala.server_host }}
 
     [aws]
     [[aws_accounts]]
@@ -30,7 +27,7 @@ spec:
     spec:
       containers:
         - name: hue
-          image: {{ .Values.registry }}/hue-frontend:{{ .Values.tag }}
+          image: {{ .Values.registry }}/hue:{{ .Values.tag }}
           imagePullPolicy: Always
           ports:
             - containerPort: 8888

+ 0 - 0
tools/kubernetes/helm/frontend/templates/traefik.yaml → tools/kubernetes/helm/hue/templates/traefik.yaml


+ 3 - 5
tools/kubernetes/helm/frontend/values.yaml → tools/kubernetes/helm/hue/values.yaml

@@ -1,7 +1,4 @@
-registry: "docker-registry.infra.cloudera.com/cloudera/datawarehouse"
-helmRepo: "http://dataware-1.vpc.cloudera.com:8879"
-tag: "v2"
-registryImpalaTag: "v2"
+registry: "gethue"
 loadBalancerIp: "127.0.0.1"
 domain: "127.0.0.1.nip.io"
 hue:
@@ -10,4 +7,5 @@ hue:
   awsSecretAccessKey: ""
   awsRegion: "us-east-1"
   prometheusHost: "prometheus-server.prometheus"
-  provisionerApi: "http://provisioner"
+impala:
+  server_host: "impala-coordinator.com"

+ 1 - 1
tools/kubernetes/helm/impala-engine/README.md

@@ -1,4 +1,4 @@
-# Cloudera Impala Chart
+# Impala Chart
 
 This is a MVP to get an Helm chart to get an Impala compute cluster ported to Kubernetes.
 It provides a basic Impala warehouses creation and autoscaling.

+ 2 - 4
tools/kubernetes/helm/mock-storage/README.md

@@ -1,8 +1,6 @@
-# Cloudera Data Warehouse Mock Storage Chart
+# Mock Storage Chart
 
-This chart schedules a mini HDFS and Hive to run inside the K8s cluster. It enables quick demoing of the Data Warehouse app.
-
-**Long term** is to also provide to use a remote storage and point to it via the [values.yaml](values.yaml) configuration.
+This chart schedules a mini HDFS and Hive to run inside the K8s cluster.
 
 ## Install
 

+ 0 - 0
tools/kubernetes/services/hue-frontend/Dockerfile → tools/kubernetes/services/hue/Dockerfile


+ 17 - 0
tools/kubernetes/services/hue/README.md

@@ -0,0 +1,17 @@
+# Hue Docker
+
+TODO: unify with current [Hue docker](/tools/docker).
+
+
+Build and push:
+
+```
+sudo docker build . -t gethue/hue
+sudo docker push gethue/hue
+```
+
+Update the Hue pods:
+
+```
+kubectl delete pods `kubectl get pods | grep hue | grep -v postgres | cut -d" " -f1`
+```

+ 0 - 0
tools/kubernetes/services/hue-frontend/conf/hue.ini → tools/kubernetes/services/hue/conf/hue.ini


+ 0 - 0
tools/kubernetes/services/hue-frontend/conf/log.conf → tools/kubernetes/services/hue/conf/log.conf


+ 0 - 0
tools/kubernetes/services/hue-frontend/startup.sh → tools/kubernetes/services/hue/startup.sh


+ 0 - 1
tools/kubernetes/services/mock-provisioner/.gitignore

@@ -1 +0,0 @@
-/node_modules/

+ 0 - 36
tools/kubernetes/services/mock-provisioner/Dockerfile

@@ -1,36 +0,0 @@
-# Use an official Ubuntu Xenial as a parent image
-FROM ubuntu:16.04
-
-# Install Node.js 8 and npm 5
-RUN apt-get update
-RUN apt-get install -y build-essential curl gettext
-RUN curl -sL https://deb.nodesource.com/setup_8.x | bash
-RUN apt-get install -y nodejs
-
-RUN curl -LO https://storage.googleapis.com/kubernetes-release/release/$(curl -s https://storage.googleapis.com/kubernetes-release/release/stable.txt)/bin/linux/amd64/kubectl
-RUN chmod +x ./kubectl && mv ./kubectl /usr/local/bin/kubectl
-
-# Install Helm
-ENV HELM_FILENAME helm-v2.10.0-linux-amd64.tar.gz
-ENV HELM_URL https://storage.googleapis.com/kubernetes-helm/${HELM_FILENAME}
-
-RUN echo $HELM_URL
-
-RUN curl -o /tmp/${HELM_FILENAME} ${HELM_URL} \
-  && tar -zxvf /tmp/${HELM_FILENAME} -C /tmp \
-  && mv /tmp/linux-amd64/helm /bin/helm \
-  && rm -rf /tmp/linux-amd64 \
-  && rm -rf /tmp/${HELM_FILENAME}
-
-RUN helm init --client-only
-
-# Set the working directory to /app
-WORKDIR /app
-
-# Copy the current directory contents into the container
-ADD . /app
-
-# Install any needed packages specified in requirements.txt
-RUN npm install
-
-CMD ["npm", "start"]

+ 0 - 15
tools/kubernetes/services/mock-provisioner/README.md

@@ -1,15 +0,0 @@
-# Provisioning Service
-
-A quick and dirty provisioning service to manage the lifecycle of Impala clusters.  See
-[provisioner.yaml](./provisioner.yaml) for the Altus-style API specification.  In a proper
-implementation this would be split into an Altus API gateway and backend provisioning service.
-
-## Development
-
-```sh
-npm install
-node server.js
-```
-
-## Deployment
-

+ 0 - 13
tools/kubernetes/services/mock-provisioner/command.js

@@ -1,13 +0,0 @@
-
-const util = require('util');
-const exec = util.promisify(require('child_process').exec);
-
-var exports = module.exports = {};
-
-exports.runCommand = async function(command) {
-    console.log("Running shell command: " + command);
-    const { stdout, stderr } = await exec(command, { shell: true });
-    if (stderr != "") {
-        console.error(`error running ${command}: ${stderr}`);
-    }
-};

+ 0 - 7
tools/kubernetes/services/mock-provisioner/config

@@ -1,7 +0,0 @@
-var config = {};
-
-config.helmRepo = "http://dataware-1.vpc.cloudera.com:8879"
-config.registry = "docker-registry.infra.cloudera.com/cloudera/datawarehouse";
-config.registryImpalaTag = "v2";
-
-module.exports = config;

+ 0 - 7
tools/kubernetes/services/mock-provisioner/config.templ

@@ -1,7 +0,0 @@
-var config = {};
-
-config.helmRepo = "http://dataware-1.vpc.cloudera.com:8879"
-config.registry = "docker-registry.infra.cloudera.com/cloudera/datawarehouse";
-config.registryImpalaTag = "v2";
-
-module.exports = config;

+ 0 - 250
tools/kubernetes/services/mock-provisioner/model.js

@@ -1,250 +0,0 @@
-
-const uuid = require('uuid/v4');
-const Client = require('kubernetes-client').Client;
-const config = require('kubernetes-client').config;
-var client;
-try {
-  client = new Client({ config: config.fromKubeconfig(), version: '1.10' });
-} catch(error) {
-  client = new Client({ config: config.getInCluster(), version: '1.10' });
-}
-
-const Command = require('./command.js');
-const Prometheus = require('prom-client');
-const request = require("request");
-var configuration = require('./config');
-
-
-const TENANT = "12a0079b-1591-4ca0-b721-a446bda74e67"
-
-// Store in memory for now.
-const clusters = {};
-
-// Basic CRUD operations, no validation, no pagination, etc.
-const model = {};
-
-//impala_queries_open_running
-//impala_queries_open_queued
-//impala_queries_closed_failed
-//...
-const impalaQueriesMetrics = new Prometheus.Gauge({
-  name: 'impala_queries',
-  help: 'Query metrics',
-  labelNames: ['datawarehouse', 'status']
-});
-const impalaQueriesCounter = new Prometheus.Gauge({
-  name: 'impala_queries_count',
-  help: 'Query metrics',
-  labelNames: ['datawarehouse']
-});
-
-
-function createCluster(options) {
-  const cluster = Object.assign({}, {
-    cdhVersion: "CDH6.3",
-    hdfsNamenodeHost: "hdfs-namenode",
-    hdfsNamenodePort: 9820, // 8020
-    // metastore.uris=thrift://spark2-envelope515-1.gce.cloudera.com:9083,hdfs.namenode.host=spark2-envelope515-1.gce.cloudera.com,hdfs.namenode.port=8020
-    workerCpuCores: 2,
-    workerMemoryInGib: 4,
-    workerReplicas: 1,
-    workerAutoResize: false,
-    workercurrentCPUUtilizationPercentage: 0
-  }, options);
-
-  cluster.name = cluster.clusterName;
-  cluster.crn = `crn:altus:dataware:k8s:${TENANT}:cluster:${cluster.clusterName}/${uuid()}`;
-  cluster.creationDate = new Date().toISOString();
-  cluster.status = "STARTING";
-  cluster.workerReplicasOnline = 0;
-  endpointHost = "impala-coordinator-" + cluster.clusterName;
-  cluster.coordinatorEndpoint = {privateHost: endpointHost, publicHost: endpointHost, port: 21050};
-
-  return cluster;
-}
-
-
-model.createCluster = async function(options) {
-  console.log("Create cluster: " + JSON.stringify(options));
-  const cluster = createCluster(options);
-
-  clusters[cluster.crn] = cluster;
-
-  await Command.runCommand(`helm install impala-engine --set-string registry=${configuration.registry},tag=${configuration.registryImpalaTag},name=${cluster.clusterName},worker.replicas=${cluster.workerReplicas},hdfs.namenode.host=${cluster.hdfsNamenodeHost},hdfs.namenode.port=${cluster.hdfsNamenodePort} -n ${cluster.clusterName} --repo=${configuration.helmRepo}`);
-
-  return {"cluster": cluster};
-};
-
-async function updateClusterStatus(cluster) {
-  if (cluster.status == 'TERMINATED') {
-    return
-  }
-
-  var statefulset;
-  try {
-   // TODO: use labels
-    statefulset = await client.apis.apps.v1.namespaces('default').deployments("impala-worker-" + cluster.clusterName).get()
-  }
-  catch(error) {
-   console.log(error);
-   // If killed manually?
-    //if (clusters.status == 'TERMINATING') {
-      cluster.status = 'TERMINATED';
-      cluster.workerReplicasOnline = 0;
-      return
-    //}
-  }
-
-  if (statefulset == null) {
-    cluster.status = "STARTING";
-    cluster.workerReplicasOnline = 0;
-    return
-  }
-
-  status = statefulset.body.status;
-  if (status.readyReplicas == null) {
-    // Statefulset when just launched will have undefined ready replicas.
-    return
-  }
-
-  if ((cluster.status == "SCALING_UP" || cluster.status == "SCALING_DOWN") && status.replicas != cluster.workerReplicas) {
-    // We are still in progress updating the cluster, don't update the status until statefulset replicas are updated.
-    return
-  }
-
-  if (status.replicas == status.readyReplicas) {
-    cluster.status = "ONLINE";
-  }
-
-  if (cluster.workerAutoResize) {
-    const hpa = await client.apis.autoscaling.v1.namespaces('default').horizontalpodautoscalers("impala-worker-" + cluster.clusterName).get()
-    hpaStatus = hpa.body.status;
-    // TODO: polish a bit status when hpa not fully running yet
-    cluster.workerReplicasOnline = hpaStatus.currentReplicas;
-    cluster.workerReplicas = hpaStatus.currentCPUUtilizationPercentage >= 0 ? hpaStatus.desiredReplicas : hpaStatus.currentReplicas;
-    cluster.workercurrentCPUUtilizationPercentage = hpaStatus.currentCPUUtilizationPercentage >= 0 ? hpaStatus.currentCPUUtilizationPercentage : "N/A";
-  } else {
-    cluster.workerReplicasOnline = status.readyReplicas;
-  }
-}
-
-model.describeCluster = async function(options) {
-  console.log("Describe cluster: " + JSON.stringify(options));
-  const cluster = clusters[options.clusterName];
-  if (cluster != undefined) {
-    updateClusterStatus(cluster);
-    return {"cluster": cluster};
-  } // TODO else
-
-  return {}
-}
-
-
-model.listClusters = async function(options) {
-  var promises = [];
-  var clusterNames = [];
-
-  for (var crn in clusters) {
-    promises.push(updateClusterStatus(clusters[crn]));
-    clusterNames.push(clusters[crn].name);
-  }
-
-  var statefulsets = await client.apis.apps.v1.namespaces('default').statefulsets().get(); // Also append DW clusters created outside of provisioner
-
-  statefulsets.body.items.forEach(function(statefulset) {
-    var name = statefulset['metadata']['name'].substring("impala-coordinator-".length);
-    if (statefulset['metadata']['name'].startsWith("impala-coordinator-") && clusterNames.indexOf(name) == -1) {
-      var cluster = createCluster({clusterName: name});
-      cluster.status = 'ONLINE';
-      cluster.crn = cluster.name // For now crn is the cluster name
-      clusters[cluster.crn] = cluster;
-    }
-  });
-
-  Promise.all(promises);
-
-  return Object.values(clusters);
-}
-
-model.updateCluster = async function(options) {
-  console.log("Update cluster: " + JSON.stringify(options));
-
-  var cluster = clusters[options.clusterName];
-  var command = "";
-
-  if (options.updateClusterAutoResizeChanged) {
-    cluster.workerAutoResize = options.updateClusterAutoResize;
-    if (options.updateClusterAutoResize) {
-      cluster.workerAutoResizeMin = options.updateClusterAutoResizeMin;
-      cluster.workerAutoResizeMax = options.updateClusterAutoResizeMax;
-      cluster.workerAutoResizeCpu = options.updateClusterAutoResizeCpu;
-      command = `kubectl autoscale deployment impala-worker-${cluster.clusterName} --min=${options.updateClusterAutoResizeMin} --max=${options.updateClusterAutoResizeMax} --cpu-percent=${options.updateClusterAutoResizeCpu}`;
-    } else {
-      command = `kubectl delete hpa impala-worker-${cluster.clusterName}`;
-    }
-    // TODO: check SCALING UP/DOWN status too
-  } else {
-    if (cluster.workerReplicas != options.workerReplicas) {
-      let originalReplicas = cluster.workerReplicas;
-      cluster.workerReplicas = options.workerReplicas;
-      if (cluster.workerReplicas > originalReplicas) {
-        cluster.status = "SCALING_UP";
-      } else if (cluster.workerReplicas < originalReplicas) {
-        cluster.status = "SCALING_DOWN";
-      }
-      await Command.runCommand(`helm upgrade ${cluster.clusterName} impala-engine --set-string registry=${configuration.registry},tag=${configuration.registryImpalaTag},name=${cluster.clusterName},worker.replicas=${cluster.workerReplicas},hdfs.namenode.host=${cluster.hdfsNamenodeHost},hdfs.namenode.port=${cluster.hdfsNamenodePort} --repo=${configuration.helmRepo}`);
-    }
-  }
-
-  if (command) {
-    await Command.runCommand(command);
-  }
-
-  return {"cluster": cluster};
-}
-
-model.deleteCluster = async function(options) {
-  console.log("Delete cluster: " + JSON.stringify(options));
-  const cluster = clusters[options.clusterName];
-  cluster.status = "TERMINATING";
-
-  await Command.runCommand(`helm delete --purge ${cluster.clusterName}`);
-  if (options.workerAutoResize) {
-    await Command.runCommand(`kubectl delete hpa impala-worker-${cluster.clusterName}`);
-  }
-
-  return;
-}
-
-
-model.getClusterMetrics = async function(options) {
-  console.log("Metric cluster: " + JSON.stringify(options));
-
-  var metrics = null;
-
-  Object.keys(clusters).forEach(function(key) {
-    var cluster = clusters[key];
-    console.log("Scrapping: " + key);
-    if (cluster.status != "TERMINATED") {
-      request({
-        url: `http://${cluster.coordinatorEndpoint.privateHost}:25000/metrics?json=true`,
-        json: true
-      }, function (error, response, body) {
-       console.log("Scapped: " + error + " " + response);
-       if (!error && response.statusCode === 200) {
-          metrics = body.metric_group.child_groups.filter(group => group.name == "impala-server")[0].metrics;
-
-          var metric = metrics.filter(metric => metric.name == "impala-server.num-queries-registered")[0];
-          impalaQueriesMetrics.labels(cluster.clusterName, 'num-queries-registered').set(metric.value);
-
-          var metric = metrics.filter(metric => metric.name == "impala-server.num-queries")[0];
-          impalaQueriesCounter.labels(cluster.clusterName).set(metric.value);
-        }
-      });
-    }
-  });
-
-  return {"metrics": metrics};
-}
-
-module.exports = model;

+ 0 - 1372
tools/kubernetes/services/mock-provisioner/package-lock.json

@@ -1,1372 +0,0 @@
-{
-  "name": "cloudera-dw-provisioner",
-  "version": "0.1.0",
-  "lockfileVersion": 1,
-  "requires": true,
-  "dependencies": {
-    "@sindresorhus/is": {
-      "version": "0.7.0",
-      "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.7.0.tgz",
-      "integrity": "sha512-ONhaKPIufzzrlNbqtWFFd+jlnemX6lJAgq9ZeiZtS7I1PIf/la7CW4m83rTXRnVnsMbW2k56pGYu7AUFJD9Pow=="
-    },
-    "accepts": {
-      "version": "1.3.5",
-      "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.5.tgz",
-      "integrity": "sha1-63d99gEXI6OxTopywIBcjoZ0a9I=",
-      "requires": {
-        "mime-types": "~2.1.18",
-        "negotiator": "0.6.1"
-      }
-    },
-    "aggregate-error": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-1.0.0.tgz",
-      "integrity": "sha1-iINE2tAiCnLjr1CQYRf0h3GSX6w=",
-      "requires": {
-        "clean-stack": "^1.0.0",
-        "indent-string": "^3.0.0"
-      }
-    },
-    "ajv": {
-      "version": "4.11.8",
-      "resolved": "https://registry.npmjs.org/ajv/-/ajv-4.11.8.tgz",
-      "integrity": "sha1-gv+wKynmYq5TvcIK8VlHcGc5xTY=",
-      "requires": {
-        "co": "^4.6.0",
-        "json-stable-stringify": "^1.0.1"
-      }
-    },
-    "argparse": {
-      "version": "1.0.10",
-      "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
-      "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
-      "requires": {
-        "sprintf-js": "~1.0.2"
-      }
-    },
-    "array-flatten": {
-      "version": "1.1.1",
-      "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
-      "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI="
-    },
-    "asn1": {
-      "version": "0.2.4",
-      "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz",
-      "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==",
-      "requires": {
-        "safer-buffer": "~2.1.0"
-      }
-    },
-    "assert-plus": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
-      "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU="
-    },
-    "asynckit": {
-      "version": "0.4.0",
-      "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
-      "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k="
-    },
-    "aws-sign2": {
-      "version": "0.7.0",
-      "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz",
-      "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg="
-    },
-    "aws4": {
-      "version": "1.8.0",
-      "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.8.0.tgz",
-      "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ=="
-    },
-    "base64url": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/base64url/-/base64url-3.0.0.tgz",
-      "integrity": "sha512-LIVmqIrIWuiqTvn4RzcrwCOuHo2DD6tKmKBPXXlr4p4n4l6BZBkwFTIa3zu1XkX5MbZgro4a6BvPi+n2Mns5Gg=="
-    },
-    "basic-auth": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.0.tgz",
-      "integrity": "sha1-AV2z81PgLlY3d1X5YnQuiYHnu7o=",
-      "requires": {
-        "safe-buffer": "5.1.1"
-      }
-    },
-    "bcrypt-pbkdf": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz",
-      "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=",
-      "optional": true,
-      "requires": {
-        "tweetnacl": "^0.14.3"
-      }
-    },
-    "bintrees": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/bintrees/-/bintrees-1.0.1.tgz",
-      "integrity": "sha1-DmVcm5wkNeqraL9AJyJtK1WjRSQ="
-    },
-    "body-parse": {
-      "version": "0.1.0",
-      "resolved": "https://registry.npmjs.org/body-parse/-/body-parse-0.1.0.tgz",
-      "integrity": "sha512-k0PDF7vZZpspXlwoM8ywh9PIHZokooS0Rek4M8Vekoro7XuuaWVhjgTpdzIRrfKj5oLQahwjn621/4kG4d91xw=="
-    },
-    "body-parser": {
-      "version": "1.18.3",
-      "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.3.tgz",
-      "integrity": "sha1-WykhmP/dVTs6DyDe0FkrlWlVyLQ=",
-      "requires": {
-        "bytes": "3.0.0",
-        "content-type": "~1.0.4",
-        "debug": "2.6.9",
-        "depd": "~1.1.2",
-        "http-errors": "~1.6.3",
-        "iconv-lite": "0.4.23",
-        "on-finished": "~2.3.0",
-        "qs": "6.5.2",
-        "raw-body": "2.3.3",
-        "type-is": "~1.6.16"
-      },
-      "dependencies": {
-        "qs": {
-          "version": "6.5.2",
-          "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz",
-          "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA=="
-        }
-      }
-    },
-    "bytes": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz",
-      "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg="
-    },
-    "cacheable-request": {
-      "version": "2.1.4",
-      "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-2.1.4.tgz",
-      "integrity": "sha1-DYCIAbY0KtM8kd+dC0TcCbkeXD0=",
-      "requires": {
-        "clone-response": "1.0.2",
-        "get-stream": "3.0.0",
-        "http-cache-semantics": "3.8.1",
-        "keyv": "3.0.0",
-        "lowercase-keys": "1.0.0",
-        "normalize-url": "2.0.1",
-        "responselike": "1.0.2"
-      },
-      "dependencies": {
-        "lowercase-keys": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.0.tgz",
-          "integrity": "sha1-TjNms55/VFfjXxMkvfb4jQv8cwY="
-        }
-      }
-    },
-    "caseless": {
-      "version": "0.12.0",
-      "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz",
-      "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw="
-    },
-    "clean-stack": {
-      "version": "1.3.0",
-      "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-1.3.0.tgz",
-      "integrity": "sha1-noIVAa6XmYbEax1m0tQy2y/UrjE="
-    },
-    "clone-response": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.2.tgz",
-      "integrity": "sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws=",
-      "requires": {
-        "mimic-response": "^1.0.0"
-      }
-    },
-    "co": {
-      "version": "4.6.0",
-      "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz",
-      "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ="
-    },
-    "combined-stream": {
-      "version": "1.0.6",
-      "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.6.tgz",
-      "integrity": "sha1-cj599ugBrFYTETp+RFqbactjKBg=",
-      "requires": {
-        "delayed-stream": "~1.0.0"
-      }
-    },
-    "content-disposition": {
-      "version": "0.5.2",
-      "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz",
-      "integrity": "sha1-DPaLud318r55YcOoUXjLhdunjLQ="
-    },
-    "content-type": {
-      "version": "1.0.4",
-      "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz",
-      "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA=="
-    },
-    "cookie": {
-      "version": "0.3.1",
-      "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz",
-      "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s="
-    },
-    "cookie-signature": {
-      "version": "1.0.6",
-      "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
-      "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw="
-    },
-    "core-util-is": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
-      "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac="
-    },
-    "dashdash": {
-      "version": "1.14.1",
-      "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz",
-      "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=",
-      "requires": {
-        "assert-plus": "^1.0.0"
-      }
-    },
-    "debug": {
-      "version": "2.6.9",
-      "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
-      "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
-      "requires": {
-        "ms": "2.0.0"
-      }
-    },
-    "decode-uri-component": {
-      "version": "0.2.0",
-      "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz",
-      "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU="
-    },
-    "decompress-response": {
-      "version": "3.3.0",
-      "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz",
-      "integrity": "sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M=",
-      "requires": {
-        "mimic-response": "^1.0.0"
-      }
-    },
-    "delayed-stream": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
-      "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk="
-    },
-    "depd": {
-      "version": "1.1.2",
-      "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz",
-      "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak="
-    },
-    "destroy": {
-      "version": "1.0.4",
-      "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz",
-      "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA="
-    },
-    "duplexer3": {
-      "version": "0.1.4",
-      "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz",
-      "integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI="
-    },
-    "ecc-jsbn": {
-      "version": "0.1.2",
-      "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz",
-      "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=",
-      "optional": true,
-      "requires": {
-        "jsbn": "~0.1.0",
-        "safer-buffer": "^2.1.0"
-      }
-    },
-    "ee-first": {
-      "version": "1.1.1",
-      "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
-      "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0="
-    },
-    "encodeurl": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
-      "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k="
-    },
-    "es6-promise": {
-      "version": "4.2.5",
-      "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.5.tgz",
-      "integrity": "sha512-n6wvpdE43VFtJq+lUDYDBFUwV8TZbuGXLV4D6wKafg13ldznKsyEvatubnmUe31zcvelSzOHF+XbaT+Bl9ObDg=="
-    },
-    "escape-html": {
-      "version": "1.0.3",
-      "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
-      "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg="
-    },
-    "esprima": {
-      "version": "4.0.1",
-      "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
-      "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A=="
-    },
-    "etag": {
-      "version": "1.8.1",
-      "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
-      "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc="
-    },
-    "express": {
-      "version": "4.16.3",
-      "resolved": "http://registry.npmjs.org/express/-/express-4.16.3.tgz",
-      "integrity": "sha1-avilAjUNsyRuzEvs9rWjTSL37VM=",
-      "requires": {
-        "accepts": "~1.3.5",
-        "array-flatten": "1.1.1",
-        "body-parser": "1.18.2",
-        "content-disposition": "0.5.2",
-        "content-type": "~1.0.4",
-        "cookie": "0.3.1",
-        "cookie-signature": "1.0.6",
-        "debug": "2.6.9",
-        "depd": "~1.1.2",
-        "encodeurl": "~1.0.2",
-        "escape-html": "~1.0.3",
-        "etag": "~1.8.1",
-        "finalhandler": "1.1.1",
-        "fresh": "0.5.2",
-        "merge-descriptors": "1.0.1",
-        "methods": "~1.1.2",
-        "on-finished": "~2.3.0",
-        "parseurl": "~1.3.2",
-        "path-to-regexp": "0.1.7",
-        "proxy-addr": "~2.0.3",
-        "qs": "6.5.1",
-        "range-parser": "~1.2.0",
-        "safe-buffer": "5.1.1",
-        "send": "0.16.2",
-        "serve-static": "1.13.2",
-        "setprototypeof": "1.1.0",
-        "statuses": "~1.4.0",
-        "type-is": "~1.6.16",
-        "utils-merge": "1.0.1",
-        "vary": "~1.1.2"
-      },
-      "dependencies": {
-        "body-parser": {
-          "version": "1.18.2",
-          "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.2.tgz",
-          "integrity": "sha1-h2eKGdhLR9hZuDGZvVm84iKxBFQ=",
-          "requires": {
-            "bytes": "3.0.0",
-            "content-type": "~1.0.4",
-            "debug": "2.6.9",
-            "depd": "~1.1.1",
-            "http-errors": "~1.6.2",
-            "iconv-lite": "0.4.19",
-            "on-finished": "~2.3.0",
-            "qs": "6.5.1",
-            "raw-body": "2.3.2",
-            "type-is": "~1.6.15"
-          }
-        },
-        "iconv-lite": {
-          "version": "0.4.19",
-          "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.19.tgz",
-          "integrity": "sha512-oTZqweIP51xaGPI4uPa56/Pri/480R+mo7SeU+YETByQNhDG55ycFyNLIgta9vXhILrxXDmF7ZGhqZIcuN0gJQ=="
-        },
-        "raw-body": {
-          "version": "2.3.2",
-          "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.3.2.tgz",
-          "integrity": "sha1-vNYMd9Prk83gBQKVw/N5OJvIj4k=",
-          "requires": {
-            "bytes": "3.0.0",
-            "http-errors": "1.6.2",
-            "iconv-lite": "0.4.19",
-            "unpipe": "1.0.0"
-          },
-          "dependencies": {
-            "depd": {
-              "version": "1.1.1",
-              "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.1.tgz",
-              "integrity": "sha1-V4O04cRZ8G+lyif5kfPQbnoxA1k="
-            },
-            "http-errors": {
-              "version": "1.6.2",
-              "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.2.tgz",
-              "integrity": "sha1-CgAsyFcHGSp+eUbO7cERVfYOxzY=",
-              "requires": {
-                "depd": "1.1.1",
-                "inherits": "2.0.3",
-                "setprototypeof": "1.0.3",
-                "statuses": ">= 1.3.1 < 2"
-              }
-            },
-            "setprototypeof": {
-              "version": "1.0.3",
-              "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.0.3.tgz",
-              "integrity": "sha1-ZlZ+NwQ+608E2RvWWMDL77VbjgQ="
-            }
-          }
-        }
-      }
-    },
-    "extend": {
-      "version": "3.0.2",
-      "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
-      "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g=="
-    },
-    "extsprintf": {
-      "version": "1.3.0",
-      "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz",
-      "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU="
-    },
-    "fast-deep-equal": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz",
-      "integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ="
-    },
-    "fast-json-stable-stringify": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz",
-      "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I="
-    },
-    "finalhandler": {
-      "version": "1.1.1",
-      "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz",
-      "integrity": "sha512-Y1GUDo39ez4aHAw7MysnUD5JzYX+WaIj8I57kO3aEPT1fFRL4sr7mjei97FgnwhAyyzRYmQZaTHb2+9uZ1dPtg==",
-      "requires": {
-        "debug": "2.6.9",
-        "encodeurl": "~1.0.2",
-        "escape-html": "~1.0.3",
-        "on-finished": "~2.3.0",
-        "parseurl": "~1.3.2",
-        "statuses": "~1.4.0",
-        "unpipe": "~1.0.0"
-      }
-    },
-    "fluent-openapi": {
-      "version": "0.1.1",
-      "resolved": "https://registry.npmjs.org/fluent-openapi/-/fluent-openapi-0.1.1.tgz",
-      "integrity": "sha512-JTnCiuaAm66gGuizv4g46MqN1QVldPxySn9DxZ+hCjMaEGFx6ciJhz0iMfS5iv1YKqZo5pBW6qFJ9Zu0jaqvaw==",
-      "requires": {
-        "lodash.merge": "^4.6.1"
-      }
-    },
-    "forever-agent": {
-      "version": "0.6.1",
-      "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz",
-      "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE="
-    },
-    "form-data": {
-      "version": "2.3.2",
-      "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.2.tgz",
-      "integrity": "sha1-SXBJi+YEwgwAXU9cI67NIda0kJk=",
-      "requires": {
-        "asynckit": "^0.4.0",
-        "combined-stream": "1.0.6",
-        "mime-types": "^2.1.12"
-      }
-    },
-    "forwarded": {
-      "version": "0.1.2",
-      "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz",
-      "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ="
-    },
-    "fresh": {
-      "version": "0.5.2",
-      "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
-      "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac="
-    },
-    "from2": {
-      "version": "2.3.0",
-      "resolved": "https://registry.npmjs.org/from2/-/from2-2.3.0.tgz",
-      "integrity": "sha1-i/tVAr3kpNNs/e6gB/zKIdfjgq8=",
-      "requires": {
-        "inherits": "^2.0.1",
-        "readable-stream": "^2.0.0"
-      }
-    },
-    "get-stream": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz",
-      "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ="
-    },
-    "getpass": {
-      "version": "0.1.7",
-      "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz",
-      "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=",
-      "requires": {
-        "assert-plus": "^1.0.0"
-      }
-    },
-    "got": {
-      "version": "8.3.2",
-      "resolved": "https://registry.npmjs.org/got/-/got-8.3.2.tgz",
-      "integrity": "sha512-qjUJ5U/hawxosMryILofZCkm3C84PLJS/0grRIpjAwu+Lkxxj5cxeCU25BG0/3mDSpXKTyZr8oh8wIgLaH0QCw==",
-      "requires": {
-        "@sindresorhus/is": "^0.7.0",
-        "cacheable-request": "^2.1.1",
-        "decompress-response": "^3.3.0",
-        "duplexer3": "^0.1.4",
-        "get-stream": "^3.0.0",
-        "into-stream": "^3.1.0",
-        "is-retry-allowed": "^1.1.0",
-        "isurl": "^1.0.0-alpha5",
-        "lowercase-keys": "^1.0.0",
-        "mimic-response": "^1.0.0",
-        "p-cancelable": "^0.4.0",
-        "p-timeout": "^2.0.1",
-        "pify": "^3.0.0",
-        "safe-buffer": "^5.1.1",
-        "timed-out": "^4.0.1",
-        "url-parse-lax": "^3.0.0",
-        "url-to-options": "^1.0.1"
-      }
-    },
-    "har-schema": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz",
-      "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI="
-    },
-    "har-validator": {
-      "version": "5.1.0",
-      "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.0.tgz",
-      "integrity": "sha512-+qnmNjI4OfH2ipQ9VQOw23bBd/ibtfbVdK2fYbY4acTDqKTW/YDp9McimZdDbG8iV9fZizUqQMD5xvriB146TA==",
-      "requires": {
-        "ajv": "^5.3.0",
-        "har-schema": "^2.0.0"
-      },
-      "dependencies": {
-        "ajv": {
-          "version": "5.5.2",
-          "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz",
-          "integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=",
-          "requires": {
-            "co": "^4.6.0",
-            "fast-deep-equal": "^1.0.0",
-            "fast-json-stable-stringify": "^2.0.0",
-            "json-schema-traverse": "^0.3.0"
-          }
-        }
-      }
-    },
-    "has-symbol-support-x": {
-      "version": "1.4.2",
-      "resolved": "https://registry.npmjs.org/has-symbol-support-x/-/has-symbol-support-x-1.4.2.tgz",
-      "integrity": "sha512-3ToOva++HaW+eCpgqZrCfN51IPB+7bJNVT6CUATzueB5Heb8o6Nam0V3HG5dlDvZU1Gn5QLcbahiKw/XVk5JJw=="
-    },
-    "has-to-string-tag-x": {
-      "version": "1.4.1",
-      "resolved": "https://registry.npmjs.org/has-to-string-tag-x/-/has-to-string-tag-x-1.4.1.tgz",
-      "integrity": "sha512-vdbKfmw+3LoOYVr+mtxHaX5a96+0f3DljYd8JOqvOLsf5mw2Otda2qCDT9qRqLAhrjyQ0h7ual5nOiASpsGNFw==",
-      "requires": {
-        "has-symbol-support-x": "^1.4.1"
-      }
-    },
-    "http-cache-semantics": {
-      "version": "3.8.1",
-      "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-3.8.1.tgz",
-      "integrity": "sha512-5ai2iksyV8ZXmnZhHH4rWPoxxistEexSi5936zIQ1bnNTW5VnA85B6P/VpXiRM017IgRvb2kKo1a//y+0wSp3w=="
-    },
-    "http-errors": {
-      "version": "1.6.3",
-      "resolved": "http://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz",
-      "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=",
-      "requires": {
-        "depd": "~1.1.2",
-        "inherits": "2.0.3",
-        "setprototypeof": "1.1.0",
-        "statuses": ">= 1.4.0 < 2"
-      }
-    },
-    "http-signature": {
-      "version": "1.2.0",
-      "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz",
-      "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=",
-      "requires": {
-        "assert-plus": "^1.0.0",
-        "jsprim": "^1.2.2",
-        "sshpk": "^1.7.0"
-      }
-    },
-    "iconv-lite": {
-      "version": "0.4.23",
-      "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz",
-      "integrity": "sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==",
-      "requires": {
-        "safer-buffer": ">= 2.1.2 < 3"
-      }
-    },
-    "indent-string": {
-      "version": "3.2.0",
-      "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-3.2.0.tgz",
-      "integrity": "sha1-Sl/W0nzDMvN+VBmlBNu4NxBckok="
-    },
-    "inherits": {
-      "version": "2.0.3",
-      "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
-      "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
-    },
-    "into-stream": {
-      "version": "3.1.0",
-      "resolved": "https://registry.npmjs.org/into-stream/-/into-stream-3.1.0.tgz",
-      "integrity": "sha1-lvsKk2wSur1v8XUqF9BWFqvQlMY=",
-      "requires": {
-        "from2": "^2.1.1",
-        "p-is-promise": "^1.1.0"
-      }
-    },
-    "ipaddr.js": {
-      "version": "1.8.0",
-      "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.8.0.tgz",
-      "integrity": "sha1-6qM9bd16zo9/b+DJygRA5wZzix4="
-    },
-    "is-object": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/is-object/-/is-object-1.0.1.tgz",
-      "integrity": "sha1-iVJojF7C/9awPsyF52ngKQMINHA="
-    },
-    "is-plain-obj": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz",
-      "integrity": "sha1-caUMhCnfync8kqOQpKA7OfzVHT4="
-    },
-    "is-retry-allowed": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/is-retry-allowed/-/is-retry-allowed-1.1.0.tgz",
-      "integrity": "sha1-EaBgVotnM5REAz0BJaYaINVk+zQ="
-    },
-    "is-typedarray": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
-      "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo="
-    },
-    "isarray": {
-      "version": "0.0.1",
-      "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
-      "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8="
-    },
-    "isstream": {
-      "version": "0.1.2",
-      "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz",
-      "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo="
-    },
-    "isurl": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/isurl/-/isurl-1.0.0.tgz",
-      "integrity": "sha512-1P/yWsxPlDtn7QeRD+ULKQPaIaN6yF368GZ2vDfv0AL0NwpStafjWCDDdn0k8wgFMWpVAqG7oJhxHnlud42i9w==",
-      "requires": {
-        "has-to-string-tag-x": "^1.2.0",
-        "is-object": "^1.0.1"
-      }
-    },
-    "js-yaml": {
-      "version": "3.12.0",
-      "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.12.0.tgz",
-      "integrity": "sha512-PIt2cnwmPfL4hKNwqeiuz4bKfnzHTBv6HyVgjahA6mPLwPDzjDWrplJBMjHUFxku/N3FlmrbyPclad+I+4mJ3A==",
-      "requires": {
-        "argparse": "^1.0.7",
-        "esprima": "^4.0.0"
-      }
-    },
-    "jsbn": {
-      "version": "0.1.1",
-      "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz",
-      "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=",
-      "optional": true
-    },
-    "json-buffer": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz",
-      "integrity": "sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg="
-    },
-    "json-schema": {
-      "version": "0.2.3",
-      "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz",
-      "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM="
-    },
-    "json-schema-traverse": {
-      "version": "0.3.1",
-      "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz",
-      "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A="
-    },
-    "json-stable-stringify": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz",
-      "integrity": "sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8=",
-      "requires": {
-        "jsonify": "~0.0.0"
-      }
-    },
-    "json-stringify-safe": {
-      "version": "5.0.1",
-      "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz",
-      "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus="
-    },
-    "jsonify": {
-      "version": "0.0.0",
-      "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz",
-      "integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM="
-    },
-    "jsprim": {
-      "version": "1.4.1",
-      "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz",
-      "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=",
-      "requires": {
-        "assert-plus": "1.0.0",
-        "extsprintf": "1.3.0",
-        "json-schema": "0.2.3",
-        "verror": "1.10.0"
-      }
-    },
-    "keyv": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/keyv/-/keyv-3.0.0.tgz",
-      "integrity": "sha512-eguHnq22OE3uVoSYG0LVWNP+4ppamWr9+zWBe1bsNcovIMy6huUJFPgy4mGwCd/rnl3vOLGW1MTlu4c57CT1xA==",
-      "requires": {
-        "json-buffer": "3.0.0"
-      }
-    },
-    "kubernetes-client": {
-      "version": "6.1.0",
-      "resolved": "https://registry.npmjs.org/kubernetes-client/-/kubernetes-client-6.1.0.tgz",
-      "integrity": "sha512-H4Phnw7zWV3nYzvMqc84DDexr1Da8sRokPWoDLxLuVJSTPnqVuXpAPAWPqNcZzZdm4kWddTfKTjpg8iOYHiDDQ==",
-      "requires": {
-        "fluent-openapi": "0.1.1",
-        "js-yaml": "^3.10.0",
-        "openid-client": "^2.0.0",
-        "request": "^2.83.0"
-      }
-    },
-    "lodash": {
-      "version": "4.17.11",
-      "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz",
-      "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg=="
-    },
-    "lodash.assign": {
-      "version": "4.2.0",
-      "resolved": "https://registry.npmjs.org/lodash.assign/-/lodash.assign-4.2.0.tgz",
-      "integrity": "sha1-DZnzzNem0mHRm9rrkkUAXShYCOc="
-    },
-    "lodash.clone": {
-      "version": "4.5.0",
-      "resolved": "https://registry.npmjs.org/lodash.clone/-/lodash.clone-4.5.0.tgz",
-      "integrity": "sha1-GVhwRQ9aExkkeN9Lw9I9LeoZB7Y="
-    },
-    "lodash.fill": {
-      "version": "3.4.0",
-      "resolved": "https://registry.npmjs.org/lodash.fill/-/lodash.fill-3.4.0.tgz",
-      "integrity": "sha1-o8dK5kDQU63w3CB5+HIHiOi/74U="
-    },
-    "lodash.flatten": {
-      "version": "4.4.0",
-      "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz",
-      "integrity": "sha1-8xwiIlqWMtK7+OSt2+8kCqdlph8="
-    },
-    "lodash.intersection": {
-      "version": "4.4.0",
-      "resolved": "https://registry.npmjs.org/lodash.intersection/-/lodash.intersection-4.4.0.tgz",
-      "integrity": "sha1-ChG6Yx0OlcI8fy9Mu5ppLtF45wU="
-    },
-    "lodash.merge": {
-      "version": "4.6.1",
-      "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.1.tgz",
-      "integrity": "sha512-AOYza4+Hf5z1/0Hztxpm2/xiPZgi/cjMqdnKTUWTBSKchJlxXXuUSxCCl8rJlf4g6yww/j6mA8nC8Hw/EZWxKQ=="
-    },
-    "lodash.omit": {
-      "version": "4.5.0",
-      "resolved": "https://registry.npmjs.org/lodash.omit/-/lodash.omit-4.5.0.tgz",
-      "integrity": "sha1-brGa5aHuHdnfC5aeZs4Lf6MLXmA="
-    },
-    "lodash.partialright": {
-      "version": "4.2.1",
-      "resolved": "https://registry.npmjs.org/lodash.partialright/-/lodash.partialright-4.2.1.tgz",
-      "integrity": "sha1-ATDYDoM2MmTUAHTzKbij56ihzEs="
-    },
-    "lodash.pick": {
-      "version": "4.4.0",
-      "resolved": "https://registry.npmjs.org/lodash.pick/-/lodash.pick-4.4.0.tgz",
-      "integrity": "sha1-UvBWEP/53tQiYRRB7R/BI6AwAbM="
-    },
-    "lodash.uniq": {
-      "version": "4.5.0",
-      "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz",
-      "integrity": "sha1-0CJTc662Uq3BvILklFM5qEJ1R3M="
-    },
-    "long": {
-      "version": "4.0.0",
-      "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz",
-      "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA=="
-    },
-    "lowercase-keys": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz",
-      "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA=="
-    },
-    "lru-cache": {
-      "version": "4.1.3",
-      "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.3.tgz",
-      "integrity": "sha512-fFEhvcgzuIoJVUF8fYr5KR0YqxD238zgObTps31YdADwPPAp82a4M8TrckkWyx7ekNlf9aBcVn81cFwwXngrJA==",
-      "requires": {
-        "pseudomap": "^1.0.2",
-        "yallist": "^2.1.2"
-      }
-    },
-    "media-typer": {
-      "version": "0.3.0",
-      "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
-      "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g="
-    },
-    "merge-descriptors": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
-      "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E="
-    },
-    "methods": {
-      "version": "1.1.2",
-      "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
-      "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4="
-    },
-    "mime": {
-      "version": "1.4.1",
-      "resolved": "https://registry.npmjs.org/mime/-/mime-1.4.1.tgz",
-      "integrity": "sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ=="
-    },
-    "mime-db": {
-      "version": "1.36.0",
-      "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.36.0.tgz",
-      "integrity": "sha512-L+xvyD9MkoYMXb1jAmzI/lWYAxAMCPvIBSWur0PZ5nOf5euahRLVqH//FKW9mWp2lkqUgYiXPgkzfMUFi4zVDw=="
-    },
-    "mime-types": {
-      "version": "2.1.20",
-      "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.20.tgz",
-      "integrity": "sha512-HrkrPaP9vGuWbLK1B1FfgAkbqNjIuy4eHlIYnFi7kamZyLLrGlo2mpcx0bBmNpKqBtYtAfGbodDddIgddSJC2A==",
-      "requires": {
-        "mime-db": "~1.36.0"
-      }
-    },
-    "mimic-response": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz",
-      "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ=="
-    },
-    "morgan": {
-      "version": "1.9.1",
-      "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.9.1.tgz",
-      "integrity": "sha512-HQStPIV4y3afTiCYVxirakhlCfGkI161c76kKFca7Fk1JusM//Qeo1ej2XaMniiNeaZklMVrh3vTtIzpzwbpmA==",
-      "requires": {
-        "basic-auth": "~2.0.0",
-        "debug": "2.6.9",
-        "depd": "~1.1.2",
-        "on-finished": "~2.3.0",
-        "on-headers": "~1.0.1"
-      }
-    },
-    "ms": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
-      "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
-    },
-    "negotiator": {
-      "version": "0.6.1",
-      "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz",
-      "integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk="
-    },
-    "node-forge": {
-      "version": "0.7.6",
-      "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.7.6.tgz",
-      "integrity": "sha512-sol30LUpz1jQFBjOKwbjxijiE3b6pjd74YwfD0fJOKPjF+fONKb2Yg8rYgS6+bK6VDl+/wfr4IYpC7jDzLUIfw=="
-    },
-    "node-jose": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/node-jose/-/node-jose-1.0.0.tgz",
-      "integrity": "sha512-RE3P8l60Rj9ELrpPmvw6sOQ1hSyYfmQdNUMCa4EN7nCE1ux5JVX+GfXv+mfUTEMhZwNMwxBtI0+X1CKKeukSVQ==",
-      "requires": {
-        "base64url": "^3.0.0",
-        "es6-promise": "^4.0.5",
-        "lodash.assign": "^4.0.8",
-        "lodash.clone": "^4.3.2",
-        "lodash.fill": "^3.2.2",
-        "lodash.flatten": "^4.2.0",
-        "lodash.intersection": "^4.1.2",
-        "lodash.merge": "^4.3.5",
-        "lodash.omit": "^4.2.1",
-        "lodash.partialright": "^4.1.3",
-        "lodash.pick": "^4.2.0",
-        "lodash.uniq": "^4.2.1",
-        "long": "^4.0.0",
-        "node-forge": "^0.7.1",
-        "uuid": "^3.0.1"
-      }
-    },
-    "normalize-url": {
-      "version": "2.0.1",
-      "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-2.0.1.tgz",
-      "integrity": "sha512-D6MUW4K/VzoJ4rJ01JFKxDrtY1v9wrgzCX5f2qj/lzH1m/lW6MhUZFKerVsnyjOhOsYzI9Kqqak+10l4LvLpMw==",
-      "requires": {
-        "prepend-http": "^2.0.0",
-        "query-string": "^5.0.1",
-        "sort-keys": "^2.0.0"
-      }
-    },
-    "oauth-sign": {
-      "version": "0.9.0",
-      "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz",
-      "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ=="
-    },
-    "object-assign": {
-      "version": "4.1.1",
-      "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
-      "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM="
-    },
-    "oidc-token-hash": {
-      "version": "3.0.1",
-      "resolved": "https://registry.npmjs.org/oidc-token-hash/-/oidc-token-hash-3.0.1.tgz",
-      "integrity": "sha512-oLnVSEcNZkw01sB5aFR+2iJmW4oyC1PIMJmd3FMBGDuPTy5ZtEuX5WNhKMRarJIMOq8NiOwIB6eJB9AhgYwBTg==",
-      "requires": {
-        "base64url": "^3.0.0"
-      }
-    },
-    "on-finished": {
-      "version": "2.3.0",
-      "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
-      "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=",
-      "requires": {
-        "ee-first": "1.1.1"
-      }
-    },
-    "on-headers": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.1.tgz",
-      "integrity": "sha1-ko9dD0cNSTQmUepnlLCFfBAGk/c="
-    },
-    "openid-client": {
-      "version": "2.4.1",
-      "resolved": "https://registry.npmjs.org/openid-client/-/openid-client-2.4.1.tgz",
-      "integrity": "sha512-x4YkoRVGLWG7VfLdm6abHV8LpcWU6BSQYmUaQcZX9sLrFHuhytaoiz/4Lf6VtrSA6/9qL4OaUMe/mgCwh2LwCw==",
-      "requires": {
-        "base64url": "^3.0.0",
-        "got": "^8.3.1",
-        "lodash": "^4.17.10",
-        "lru-cache": "^4.1.3",
-        "node-jose": "^1.0.0",
-        "oidc-token-hash": "^3.0.1",
-        "p-any": "^1.1.0",
-        "uuid": "^3.2.1"
-      }
-    },
-    "p-any": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/p-any/-/p-any-1.1.0.tgz",
-      "integrity": "sha512-Ef0tVa4CZ5pTAmKn+Cg3w8ABBXh+hHO1aV8281dKOoUHfX+3tjG2EaFcC+aZyagg9b4EYGsHEjz21DnEE8Og2g==",
-      "requires": {
-        "p-some": "^2.0.0"
-      }
-    },
-    "p-cancelable": {
-      "version": "0.4.1",
-      "resolved": "http://registry.npmjs.org/p-cancelable/-/p-cancelable-0.4.1.tgz",
-      "integrity": "sha512-HNa1A8LvB1kie7cERyy21VNeHb2CWJJYqyyC2o3klWFfMGlFmWv2Z7sFgZH8ZiaYL95ydToKTFVXgMV/Os0bBQ=="
-    },
-    "p-finally": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz",
-      "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4="
-    },
-    "p-is-promise": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-1.1.0.tgz",
-      "integrity": "sha1-nJRWmJ6fZYgBewQ01WCXZ1w9oF4="
-    },
-    "p-some": {
-      "version": "2.0.1",
-      "resolved": "https://registry.npmjs.org/p-some/-/p-some-2.0.1.tgz",
-      "integrity": "sha1-Zdh8ixVO289SIdFnd4ttLhUPbwY=",
-      "requires": {
-        "aggregate-error": "^1.0.0"
-      }
-    },
-    "p-timeout": {
-      "version": "2.0.1",
-      "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-2.0.1.tgz",
-      "integrity": "sha512-88em58dDVB/KzPEx1X0N3LwFfYZPyDc4B6eF38M1rk9VTZMbxXXgjugz8mmwpS9Ox4BDZ+t6t3QP5+/gazweIA==",
-      "requires": {
-        "p-finally": "^1.0.0"
-      }
-    },
-    "parseurl": {
-      "version": "1.3.2",
-      "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.2.tgz",
-      "integrity": "sha1-/CidTtiZMRlGDBViUyYs3I3mW/M="
-    },
-    "path-to-regexp": {
-      "version": "0.1.7",
-      "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
-      "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w="
-    },
-    "performance-now": {
-      "version": "2.1.0",
-      "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz",
-      "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns="
-    },
-    "pify": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz",
-      "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY="
-    },
-    "prepend-http": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz",
-      "integrity": "sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc="
-    },
-    "prettier": {
-      "version": "1.14.3",
-      "resolved": "https://registry.npmjs.org/prettier/-/prettier-1.14.3.tgz",
-      "integrity": "sha512-qZDVnCrnpsRJJq5nSsiHCE3BYMED2OtsI+cmzIzF1QIfqm5ALf8tEJcO27zV1gKNKRPdhjO0dNWnrzssDQ1tFg==",
-      "dev": true
-    },
-    "process-nextick-args": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz",
-      "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw=="
-    },
-    "prom-client": {
-      "version": "11.2.0",
-      "resolved": "https://registry.npmjs.org/prom-client/-/prom-client-11.2.0.tgz",
-      "integrity": "sha512-4gUAq/GR5C8q5eWxOa7tA60AtmkMpbyBd/2btCayvd3h/7HzS0p/kESKRwggJgbFrfdhTCBpOwPAwKiI01Q0VQ==",
-      "requires": {
-        "tdigest": "^0.1.1"
-      }
-    },
-    "proxy-addr": {
-      "version": "2.0.4",
-      "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.4.tgz",
-      "integrity": "sha512-5erio2h9jp5CHGwcybmxmVqHmnCBZeewlfJ0pex+UW7Qny7OOZXTtH56TGNyBizkgiOwhJtMKrVzDTeKcySZwA==",
-      "requires": {
-        "forwarded": "~0.1.2",
-        "ipaddr.js": "1.8.0"
-      }
-    },
-    "pseudomap": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz",
-      "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM="
-    },
-    "psl": {
-      "version": "1.1.29",
-      "resolved": "https://registry.npmjs.org/psl/-/psl-1.1.29.tgz",
-      "integrity": "sha512-AeUmQ0oLN02flVHXWh9sSJF7mcdFq0ppid/JkErufc3hGIV/AMa8Fo9VgDo/cT2jFdOWoFvHp90qqBH54W+gjQ=="
-    },
-    "punycode": {
-      "version": "1.4.1",
-      "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz",
-      "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4="
-    },
-    "qs": {
-      "version": "6.5.1",
-      "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.1.tgz",
-      "integrity": "sha512-eRzhrN1WSINYCDCbrz796z37LOe3m5tmW7RQf6oBntukAG1nmovJvhnwHHRMAfeoItc1m2Hk02WER2aQ/iqs+A=="
-    },
-    "query-string": {
-      "version": "5.1.1",
-      "resolved": "https://registry.npmjs.org/query-string/-/query-string-5.1.1.tgz",
-      "integrity": "sha512-gjWOsm2SoGlgLEdAGt7a6slVOk9mGiXmPFMqrEhLQ68rhQuBnpfs3+EmlvqKyxnCo9/PPlF+9MtY02S1aFg+Jw==",
-      "requires": {
-        "decode-uri-component": "^0.2.0",
-        "object-assign": "^4.1.0",
-        "strict-uri-encode": "^1.0.0"
-      }
-    },
-    "range-parser": {
-      "version": "1.2.0",
-      "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz",
-      "integrity": "sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4="
-    },
-    "raw-body": {
-      "version": "2.3.3",
-      "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.3.3.tgz",
-      "integrity": "sha512-9esiElv1BrZoI3rCDuOuKCBRbuApGGaDPQfjSflGxdy4oyzqghxu6klEkkVIvBje+FF0BX9coEv8KqW6X/7njw==",
-      "requires": {
-        "bytes": "3.0.0",
-        "http-errors": "1.6.3",
-        "iconv-lite": "0.4.23",
-        "unpipe": "1.0.0"
-      }
-    },
-    "readable-stream": {
-      "version": "2.3.6",
-      "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
-      "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
-      "requires": {
-        "core-util-is": "~1.0.0",
-        "inherits": "~2.0.3",
-        "isarray": "~1.0.0",
-        "process-nextick-args": "~2.0.0",
-        "safe-buffer": "~5.1.1",
-        "string_decoder": "~1.1.1",
-        "util-deprecate": "~1.0.1"
-      },
-      "dependencies": {
-        "isarray": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
-          "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
-        }
-      }
-    },
-    "request": {
-      "version": "2.88.0",
-      "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz",
-      "integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==",
-      "requires": {
-        "aws-sign2": "~0.7.0",
-        "aws4": "^1.8.0",
-        "caseless": "~0.12.0",
-        "combined-stream": "~1.0.6",
-        "extend": "~3.0.2",
-        "forever-agent": "~0.6.1",
-        "form-data": "~2.3.2",
-        "har-validator": "~5.1.0",
-        "http-signature": "~1.2.0",
-        "is-typedarray": "~1.0.0",
-        "isstream": "~0.1.2",
-        "json-stringify-safe": "~5.0.1",
-        "mime-types": "~2.1.19",
-        "oauth-sign": "~0.9.0",
-        "performance-now": "^2.1.0",
-        "qs": "~6.5.2",
-        "safe-buffer": "^5.1.2",
-        "tough-cookie": "~2.4.3",
-        "tunnel-agent": "^0.6.0",
-        "uuid": "^3.3.2"
-      },
-      "dependencies": {
-        "qs": {
-          "version": "6.5.2",
-          "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz",
-          "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA=="
-        },
-        "safe-buffer": {
-          "version": "5.1.2",
-          "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
-          "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
-        }
-      }
-    },
-    "responselike": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/responselike/-/responselike-1.0.2.tgz",
-      "integrity": "sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec=",
-      "requires": {
-        "lowercase-keys": "^1.0.0"
-      }
-    },
-    "safe-buffer": {
-      "version": "5.1.1",
-      "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz",
-      "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg=="
-    },
-    "safer-buffer": {
-      "version": "2.1.2",
-      "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
-      "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
-    },
-    "send": {
-      "version": "0.16.2",
-      "resolved": "https://registry.npmjs.org/send/-/send-0.16.2.tgz",
-      "integrity": "sha512-E64YFPUssFHEFBvpbbjr44NCLtI1AohxQ8ZSiJjQLskAdKuriYEP6VyGEsRDH8ScozGpkaX1BGvhanqCwkcEZw==",
-      "requires": {
-        "debug": "2.6.9",
-        "depd": "~1.1.2",
-        "destroy": "~1.0.4",
-        "encodeurl": "~1.0.2",
-        "escape-html": "~1.0.3",
-        "etag": "~1.8.1",
-        "fresh": "0.5.2",
-        "http-errors": "~1.6.2",
-        "mime": "1.4.1",
-        "ms": "2.0.0",
-        "on-finished": "~2.3.0",
-        "range-parser": "~1.2.0",
-        "statuses": "~1.4.0"
-      }
-    },
-    "serve-static": {
-      "version": "1.13.2",
-      "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.13.2.tgz",
-      "integrity": "sha512-p/tdJrO4U387R9oMjb1oj7qSMaMfmOyd4j9hOFoxZe2baQszgHcSWjuya/CiT5kgZZKRudHNOA0pYXOl8rQ5nw==",
-      "requires": {
-        "encodeurl": "~1.0.2",
-        "escape-html": "~1.0.3",
-        "parseurl": "~1.3.2",
-        "send": "0.16.2"
-      }
-    },
-    "setprototypeof": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz",
-      "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ=="
-    },
-    "sort-keys": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/sort-keys/-/sort-keys-2.0.0.tgz",
-      "integrity": "sha1-ZYU1WEhh7JfXMNbPQYIuH1ZoQSg=",
-      "requires": {
-        "is-plain-obj": "^1.0.0"
-      }
-    },
-    "sprintf-js": {
-      "version": "1.0.3",
-      "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
-      "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw="
-    },
-    "sshpk": {
-      "version": "1.14.2",
-      "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.14.2.tgz",
-      "integrity": "sha1-xvxhZIo9nE52T9P8306hBeSSupg=",
-      "requires": {
-        "asn1": "~0.2.3",
-        "assert-plus": "^1.0.0",
-        "bcrypt-pbkdf": "^1.0.0",
-        "dashdash": "^1.12.0",
-        "ecc-jsbn": "~0.1.1",
-        "getpass": "^0.1.1",
-        "jsbn": "~0.1.0",
-        "safer-buffer": "^2.0.2",
-        "tweetnacl": "~0.14.0"
-      }
-    },
-    "statuses": {
-      "version": "1.4.0",
-      "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz",
-      "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew=="
-    },
-    "strict-uri-encode": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz",
-      "integrity": "sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM="
-    },
-    "string_decoder": {
-      "version": "1.1.1",
-      "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
-      "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
-      "requires": {
-        "safe-buffer": "~5.1.0"
-      }
-    },
-    "swagger-express-validator": {
-      "version": "0.1.0",
-      "resolved": "https://registry.npmjs.org/swagger-express-validator/-/swagger-express-validator-0.1.0.tgz",
-      "integrity": "sha512-HSwg8qHhZxl1EKPMTHaFkbbpvcSaXZ3y21g+8bB+HMCVxVFD1+FA8MC1Ix41Du82AFACaiSXriNTk86o+RQhNg==",
-      "requires": {
-        "ajv": "^4.10.3",
-        "debug": "^2.6.0",
-        "lodash": "^4.17.3",
-        "path-to-regexp": "^1.7.0",
-        "validator": "^6.2.1"
-      },
-      "dependencies": {
-        "path-to-regexp": {
-          "version": "1.7.0",
-          "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.7.0.tgz",
-          "integrity": "sha1-Wf3g9DW62suhA6hOnTvGTpa5k30=",
-          "requires": {
-            "isarray": "0.0.1"
-          }
-        }
-      }
-    },
-    "tdigest": {
-      "version": "0.1.1",
-      "resolved": "https://registry.npmjs.org/tdigest/-/tdigest-0.1.1.tgz",
-      "integrity": "sha1-Ljyyw56kSeVdHmzZEReszKRYgCE=",
-      "requires": {
-        "bintrees": "1.0.1"
-      }
-    },
-    "timed-out": {
-      "version": "4.0.1",
-      "resolved": "https://registry.npmjs.org/timed-out/-/timed-out-4.0.1.tgz",
-      "integrity": "sha1-8y6srFoXW+ol1/q1Zas+2HQe9W8="
-    },
-    "tough-cookie": {
-      "version": "2.4.3",
-      "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz",
-      "integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==",
-      "requires": {
-        "psl": "^1.1.24",
-        "punycode": "^1.4.1"
-      }
-    },
-    "tunnel-agent": {
-      "version": "0.6.0",
-      "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
-      "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=",
-      "requires": {
-        "safe-buffer": "^5.0.1"
-      }
-    },
-    "tweetnacl": {
-      "version": "0.14.5",
-      "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz",
-      "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=",
-      "optional": true
-    },
-    "type-is": {
-      "version": "1.6.16",
-      "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.16.tgz",
-      "integrity": "sha512-HRkVv/5qY2G6I8iab9cI7v1bOIdhm94dVjQCPFElW9W+3GeDOSHmy2EBYe4VTApuzolPcmgFTN3ftVJRKR2J9Q==",
-      "requires": {
-        "media-typer": "0.3.0",
-        "mime-types": "~2.1.18"
-      }
-    },
-    "unpipe": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
-      "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw="
-    },
-    "url-parse-lax": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-3.0.0.tgz",
-      "integrity": "sha1-FrXK/Afb42dsGxmZF3gj1lA6yww=",
-      "requires": {
-        "prepend-http": "^2.0.0"
-      }
-    },
-    "url-to-options": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/url-to-options/-/url-to-options-1.0.1.tgz",
-      "integrity": "sha1-FQWgOiiaSMvXpDTvuu7FBV9WM6k="
-    },
-    "util-deprecate": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
-      "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8="
-    },
-    "utils-merge": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
-      "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM="
-    },
-    "uuid": {
-      "version": "3.3.2",
-      "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz",
-      "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA=="
-    },
-    "validator": {
-      "version": "6.3.0",
-      "resolved": "https://registry.npmjs.org/validator/-/validator-6.3.0.tgz",
-      "integrity": "sha1-R84j7Y1Ord+p1LjvAHG2zxB418g="
-    },
-    "vary": {
-      "version": "1.1.2",
-      "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
-      "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw="
-    },
-    "verror": {
-      "version": "1.10.0",
-      "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz",
-      "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=",
-      "requires": {
-        "assert-plus": "^1.0.0",
-        "core-util-is": "1.0.2",
-        "extsprintf": "^1.2.0"
-      }
-    },
-    "yallist": {
-      "version": "2.1.2",
-      "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz",
-      "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI="
-    }
-  }
-}

+ 0 - 26
tools/kubernetes/services/mock-provisioner/package.json

@@ -1,26 +0,0 @@
-{
-  "name": "cloudera-dw-provisioner",
-  "version": "0.1.0",
-  "description": "Cloudera Data Warehouse Provisioning Service",
-  "main": "server.js",
-  "scripts": {
-    "test": "echo \"Error: no test specified\" && exit 1",
-    "start": "node server.js"
-  },
-  "author": "Cloudera, Inc.",
-  "dependencies": {
-    "body-parse": "^0.1.0",
-    "body-parser": "^1.18.3",
-    "express": "^4.16.3",
-    "js-yaml": "^3.12.0",
-    "kubernetes-client": "^6.1.0",
-    "morgan": "^1.9.1",
-    "prom-client": "^11.2.0",
-    "request": "^2.88.0",
-    "swagger-express-validator": "^0.1.0",
-    "uuid": "^3.3.2"
-  },
-  "devDependencies": {
-    "prettier": "1.14.3"
-  }
-}

+ 0 - 419
tools/kubernetes/services/mock-provisioner/provisioner.yaml

@@ -1,419 +0,0 @@
-swagger: '2.0'
-x-interface-model: cluster
-x-endpoint-name: dw
-x-releases: DW,SDX
-info:
-  version: 0.1-SNAPSHOT
-  title: Cloudera Data Warehouse
-  license:
-    name: Apache 2.0
-  description: Cloudera Data Warehouse is a service that makes it easy to analyze large amounts of data efficiently.
-schemes:
-  - http
-consumes:
-  - application/json
-produces:
-  - application/json
-paths:
-  /dw/createCluster:
-    post:
-      summary: Creates a new cluster.
-      description: Creates a new cluster.
-      operationId: createCluster
-      x-entitlement: DW
-      parameters:
-        - name: input
-          in: body
-          required: true
-          schema:
-            $ref: '#/definitions/CreateClusterRequest'
-      responses:
-        200:
-          description: Expected response to a valid request.
-          schema:
-            $ref: '#/definitions/CreateClusterResponse'
-        default:
-          description: The default response on an error.
-          schema:
-            $ref: '#/definitions/Error'
-  /dw/deleteCluster:
-    post:
-      summary: Deletes a cluster.
-      description: Deletes a cluster.
-      operationId: deleteCluster
-      parameters:
-        - name: input
-          in: body
-          required: true
-          schema:
-            $ref: '#/definitions/DeleteClusterRequest'
-      responses:
-        200:
-          description: Expected response to a valid request.
-          schema:
-            $ref: '#/definitions/DeleteClusterResponse'
-        default:
-          description: The default response on an error.
-          schema:
-            $ref: '#/definitions/Error'
-  /dw/listClusters:
-    post:
-      summary: Lists clusters.
-      description: Lists clusters. If no cluster names are specified, the call lists all clusters.
-      operationId: listClusters
-      x-right: dw/listClusters
-      x-paginates: true
-      x-paging-default-max-items: 100
-      parameters:
-        - name: input
-          in: body
-          required: true
-          schema:
-            $ref: '#/definitions/ListClustersRequest'
-      responses:
-        200:
-          description: Expected response to a valid request.
-          schema:
-            $ref: '#/definitions/ListClustersResponse'
-        default:
-          description: The default response on an error.
-          schema:
-            $ref: '#/definitions/Error'
-  /dw/describeCluster:
-    post:
-      summary: Describe a cluster.
-      description: Describe a cluster.
-      operationId: describeCluster
-      x-right: dw/describeCluster
-      parameters:
-        - name: input
-          in: body
-          required: true
-          schema:
-            $ref: '#/definitions/DescribeClusterRequest'
-      responses:
-        200:
-          description: Expected response to a valid request.
-          schema:
-            $ref: '#/definitions/DescribeClusterResponse'
-        default:
-          description: The default response on an error.
-          schema:
-            $ref: '#/definitions/Error'
-  /dw/updateCluster:
-    post:
-      summary: Update a cluster.
-      description: Update a cluster.
-      operationId: updateCluster
-      x-right: dw/updateClusters
-      parameters:
-        - name: input
-          in: body
-          required: true
-          schema:
-            $ref: '#/definitions/UpdateClusterRequest'
-      responses:
-        200:
-          description: Expected response to a valid request.
-          schema:
-            $ref: '#/definitions/UpdateClusterResponse'
-        default:
-          description: The default response on an error.
-          schema:
-            $ref: '#/definitions/Error'
-definitions:
-  Error:
-    type: object
-    description: An object returned on an error.
-    properties:
-      code:
-        type: string
-        description: The error code.
-      message:
-        type: string
-        description: The error message.
-  ClusterSummary:
-    type: object
-    description: Information about a cluster.
-    required:
-      - clusterName
-      - crn
-      - creationDate
-      - cdhVersion
-      - workerCpuCores
-      - workerMemoryInGib
-      - workerReplicas
-      - workerReplicasOnline
-      - workerAutoResize
-      - status
-    properties:
-      clusterName:
-        type: string
-        description: The name of the cluster.
-      crn:
-        type: string
-        description: The CRN of the cluster.
-      creationDate:
-        type: string
-        format: date-time
-        description: The date when the cluster was created.
-      status:
-        $ref: '#/definitions/ClusterStatus'
-        description: The status of the cluster.
-      cdhVersion:
-        type: string
-        description: The CDH version for the cluster.
-      workerCpuCores:
-        type: integer
-        format: int32
-        description: CPU cores per worker.
-      workerMemoryInGib:
-        type: integer
-        format: int32
-        description: Memory in GiB per worker.
-      workerReplicas:
-        type: integer
-        format: int32
-        description: Number of worker replicas.
-      workerReplicasOnline:
-        type: integer
-        format: int32
-        description: Number of worker replicas currently online.
-      workerAutoResize:
-        type: boolean
-        description: If the number of worker replicas is auto scaling.
-  Cluster:
-    type: object
-    description: Information about a cluster.
-    required:
-      - clusterName
-      - crn
-      - creationDate
-      - cdhVersion
-      - workerCpuCores
-      - workerMemoryInGib
-      - workerReplicas
-      - workerReplicasOnline
-      - workerAutoResize
-      - status
-    properties:
-      clusterName:
-        type: string
-        description: The name of the cluster.
-      crn:
-        type: string
-        description: The CRN of the cluster.
-      creationDate:
-        type: string
-        format: date-time
-        description: The date when the cluster was created.
-      status:
-        $ref: '#/definitions/ClusterStatus'
-        description: The status of the cluster.
-      cdhVersion:
-        type: string
-        description: The CDH version for the cluster.
-      workerCpuCores:
-        type: integer
-        format: int32
-        description: CPU cores per worker.
-      workerMemoryInGib:
-        type: integer
-        format: int32
-        description: Memory in GiB per worker.
-      workerReplicas:
-        type: integer
-        format: int32
-        description: Number of worker replicas.
-      workerReplicasOnline:
-        type: integer
-        format: int32
-        description: Number of worker replicas currently online.
-      workerAutoResize:
-        type: boolean
-        description: If the number of worker replicas is auto scaling.
-      coordinatorEndpoint:
-        $ref: '#/definitions/Endpoint'
-        description: The connection endpoint for the cluster coordinator.
-      adminEndpoint:
-        $ref: '#/definitions/Endpoint'
-        description: The connection endpoint for the cluster administration UI.
-  Endpoint:
-    type: object
-    description: A network endpoint that has both a public and a private hostname or IP address.
-    required:
-      - privateHost
-      - publicHost
-      - port
-    properties:
-      privateHost:
-        type: string
-        description: A private IP address or hostname. This is only accessible from within the pod network.
-      publicHost:
-        type: string
-        description: A public IP address or hostname. This is routable from external services.
-      port:
-        type: integer
-        format: int32
-        description: A port.
-  ClusterStatus:
-    type: string
-    description: The status of a cluster.
-    enum:
-      - STARTING
-      - ONLINE
-      - SCALING_UP
-      - SCALING_DOWN
-      - TERMINATING
-      - STOPPED
-      - FAILED
-  CreateClusterRequest:
-    type: object
-    description: Request object for a create cluster request.
-    required:
-      - clusterName
-    properties:
-      clusterName:
-        type: string
-        description: The name of the cluster. This name must be unique, must have a maximum of 128 characters, and must contain only alphanumeric characters and hyphens. Names are case-sensitive.
-      cdhVersion:
-        type: string
-        description: The CDH version.
-      workerCpuCores:
-        type: integer
-        format: int32
-        description: CPU cores per worker.
-      workerMemoryInGib:
-        type: integer
-        format: int32
-        description: Memory in GiB per worker.
-      workerReplicas:
-        type: integer
-        format: int32
-        description: Number of worker replicas.
-  CreateClusterResponse:
-    type: object
-    description: Response object for a create cluster request.
-    required:
-      - cluster
-    properties:
-      cluster:
-        $ref: '#/definitions/Cluster'
-        description: The cluster that was created.
-  UpdateClusterRequest:
-    type: object
-    description: Request object for an update cluster request.
-    required:
-      - clusterName
-    properties:
-      clusterName:
-        type: string
-        description: The name of the cluster. This name must be unique, must have a maximum of 128 characters, and must contain only alphanumeric characters and hyphens. Names are case-sensitive.
-      cdhVersion:
-        type: string
-        description: The CDH version.
-      workerCpuCores:
-        type: integer
-        format: int32
-        description: CPU cores per worker.
-      workerMemoryInGib:
-        type: integer
-        format: int32
-        description: Memory in GiB per worker.
-      workerReplicas:
-        type: integer
-        format: int32
-        description: Number of worker replicas.
-      updateClusterAutoResizeChanged:
-        type: boolean
-        description: If we change the worker autoscaling policy.
-      updateClusterAutoResize:
-        type: boolean
-        description: If the autoscaling was turned off or on.
-      updateClusterAutoResizeMax:
-        type: integer
-        format: int32
-        description: Max number of replica when autoscaling if on.
-      updateClusterAutoResizeMin:
-        type: integer
-        format: int32
-        description: Max number of replica when autoscaling if on (optional).
-      updateClusterAutoResizeCpu:
-        type: integer
-        format: int32
-        description: CPU integer % (e.g. 80) when autoscaling if on (optional).
-  UpdateClusterResponse:
-    type: object
-    description: Response object for an update cluster request.
-    required:
-      - cluster
-    properties:
-      cluster:
-        $ref: '#/definitions/Cluster'
-        description: The cluster that was updated.
-  DeleteClusterRequest:
-    type: object
-    description: Request object for a delete cluster request.
-    required:
-      - clusterName
-    properties:
-      clusterName:
-        type: string
-        description: The name or CRN of the cluster.
-  DeleteClusterResponse:
-    type: object
-    description: Response object for a delete cluster request.
-  ListClustersRequest:
-    type: object
-    description: Request object for a list clusters request.
-    properties:
-      clusterNames:
-        type: array
-        items:
-          type: string
-        description: The names or CRNs of the clusters.
-      pageSize:
-        type: integer
-        format: int32
-        minimum: 1
-        maximum: 100
-        x-paging-page-size: true
-        description: The size of each page.
-      startingToken:
-        type: string
-        x-paging-input-token: true
-        description: A token to specify where to start paginating. This is the nextToken from a previously truncated response.
-  ListClustersResponse:
-    type: object
-    description: Response object for a list clusters request.
-    required:
-      - clusters
-    properties:
-      clusters:
-        type: array
-        items:
-          $ref: '#/definitions/ClusterSummary'
-        x-paging-result: true
-        description: The clusters.
-      nextToken:
-        type: string
-        x-paging-output-token: true
-        description: The token to use when requesting the next set of results. If not present, there are no additional results.
-  DescribeClusterRequest:
-    type: object
-    description: Request object for a describe cluster request.
-    required:
-      - clusterName
-    properties:
-      clusterName:
-        type: string
-        description: The name or CRN of the cluster.
-  DescribeClusterResponse:
-    type: object
-    description: Response object for a describe cluster request.
-    required:
-      - cluster
-    properties:
-      cluster:
-        $ref: '#/definitions/Cluster'
-        description: The cluster.

+ 0 - 85
tools/kubernetes/services/mock-provisioner/server.js

@@ -1,85 +0,0 @@
-// Quick and dirty provisioning service.
-
-const util = require("util");
-const express = require("express");
-const morgan = require('morgan');
-const bodyParser = require("body-parser");
-const validator = require("swagger-express-validator");
-const yaml = require("js-yaml");
-const fs = require("fs");
-const model = require("./model");
-const Prometheus = require('prom-client');
-
-
-// Get api spec, or throw exception on error
-let schema = null;
-try {
-  schema = yaml.safeLoad(fs.readFileSync("provisioner.yaml", "utf8"));
-} catch (e) {
-  console.error(e);
-  process.exit(1);
-}
-
-const app = express();
-app.use(bodyParser.json());
-app.use(morgan('tiny'));
-
-const opts = {
-  schema,
-  validateRequest: true,
-  validateResponse: true,
-  requestValidationFn: (req, data, errors) => {
-    console.error(
-      `failed request validation: ${req.method} ${
-        req.originalUrl
-      }\n ${util.inspect(errors)}`
-    );
-  },
-  responseValidationFn: (req, data, errors) => {
-    console.error(
-      `failed response validation: ${req.method} ${
-        req.originalUrl
-      }\n ${util.inspect(errors)}`
-    );
-  }
-};
-app.use(validator(opts));
-
-
-app.post("/dw/createCluster", async (req, res) => {
-  res.json(await model.createCluster(req.body));
-});
-
-app.post("/dw/describeCluster", async (req, res) => {
-  res.json(await model.describeCluster(req.body));
-});
-
-app.post("/dw/listClusters", async (req, res) => {
-  res.json({
-    clusters: await model.listClusters(req.body)
-  });
-});
-
-app.post("/dw/updateCluster", async (req, res) => {
-  res.json(await model.updateCluster(req.body));
-});
-
-app.post("/dw/deleteCluster", async (req, res) => {
-  res.json(await model.deleteCluster(req.body));
-});
-
-app.get("/", async (req, res) => {
-  res.json({app: "cloudera-dw-provisioner", version: "0.1"});
-});
-
-// Proxying metrics until natively supported by Impala
-app.get('/metrics', async (req, res) => {
-  await model.getClusterMetrics(req.body);
-
-  res.set('Content-Type', Prometheus.register.contentType)
-  res.end(Prometheus.register.metrics())
-})
-
-return app.listen(4747, "0.0.0.0", () =>
-  console.log("provisioning service listening on 0.0.0.0:4747")
-);