Repository: weaveworks/flagger
Pull request #344 (2019-10-21 13:36:46)

Gloo integration refactoring
- build Gloo UpstreamGroup clientset
- rewrite Gloo router to use built-in clientset 
- drop solo-io, envoyproxy, hcl, consul, opencensus, apiextensions deps
- use the native routers with supergloo for istio, appmesh and linkerd

This PR drops all external dependencies that bring their own Kubernetes deps thus solving the go mod hell.


Patch:
diff --git a/.circleci/config.yml b/.circleci/config.yml
index a9942efa..29e9ae62 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -100,17 +100,6 @@ jobs:
       - run: test/e2e-smi-istio.sh
       - run: test/e2e-tests.sh canary
 
-  e2e-supergloo-testing:
-    machine: true
-    steps:
-      - checkout
-      - attach_workspace:
-          at: /tmp/bin
-      - run: test/container-build.sh
-      - run: test/e2e-kind.sh 0.2.1
-      - run: test/e2e-supergloo.sh
-      - run: test/e2e-tests.sh canary
-
   e2e-gloo-testing:
     machine: true
     steps:
@@ -203,9 +192,6 @@ workflows:
       - e2e-kubernetes-testing:
           requires:
             - build-binary
-#      - e2e-supergloo-testing:
-#          requires:
-#            - build-binary
       - e2e-gloo-testing:
           requires:
             - build-binary
@@ -220,7 +206,6 @@ workflows:
             - build-binary
             - e2e-istio-testing
             - e2e-kubernetes-testing
-            #- e2e-supergloo-testing
             - e2e-gloo-testing
             - e2e-nginx-testing
             - e2e-linkerd-testing
diff --git a/artifacts/gloo/canary.yaml b/artifacts/gloo/canary.yaml
index 3be05a78..2412f6ae 100644
--- a/artifacts/gloo/canary.yaml
+++ b/artifacts/gloo/canary.yaml
@@ -4,6 +4,7 @@ metadata:
   name: podinfo
   namespace: test
 spec:
+  provider: gloo
   targetRef:
     apiVersion: apps/v1
     kind: Deployment
@@ -28,9 +29,24 @@ spec:
       threshold: 500
       interval: 30s
     webhooks:
+      - name: acceptance-test
+        type: pre-rollout
+        url: http://flagger-loadtester.test/
+        timeout: 10s
+        metadata:
+          type: bash
+          cmd: "curl -sd 'test' http://podinfo-canary:9898/token | grep token"
+      - name: gloo-acceptance-test
+        type: pre-rollout
+        url: http://flagger-loadtester.test/
+        timeout: 10s
+        metadata:
+          type: bash
+          cmd: "curl -sd 'test' -H 'Host: app.example.com' http://gateway-proxy-v2.gloo-system/token | grep token"
       - name: load-test
         url: http://flagger-loadtester.test/
         timeout: 5s
         metadata:
           type: cmd
-          cmd: "hey -z 1m -q 10 -c 2 http://gloo.example.com/"
+          cmd: "hey -z 2m -q 5 -c 2 -host app.example.com http://gateway-proxy-v2.gloo-system"
+          logCmdOutput: "true"
diff --git a/artifacts/gloo/deployment.yaml b/artifacts/gloo/deployment.yaml
deleted file mode 100644
index dacb34be..00000000
--- a/artifacts/gloo/deployment.yaml
+++ /dev/null
@@ -1,67 +0,0 @@
-apiVersion: apps/v1
-kind: Deployment
-metadata:
-  name: podinfo
-  namespace: test
-  labels:
-    app: podinfo
-spec:
-  minReadySeconds: 5
-  revisionHistoryLimit: 5
-  progressDeadlineSeconds: 60
-  strategy:
-    rollingUpdate:
-      maxUnavailable: 0
-    type: RollingUpdate
-  selector:
-    matchLabels:
-      app: podinfo
-  template:
-    metadata:
-      annotations:
-        prometheus.io/scrape: "true"
-      labels:
-        app: podinfo
-    spec:
-      containers:
-      - name: podinfod
-        image: quay.io/stefanprodan/podinfo:1.7.0
-        imagePullPolicy: IfNotPresent
-        ports:
-        - containerPort: 9898
-          name: http
-          protocol: TCP
-        command:
-        - ./podinfo
-        - --port=9898
-        - --level=info
-        - --random-delay=false
-        - --random-error=false
-        env:
-        - name: PODINFO_UI_COLOR
-          value: blue
-        livenessProbe:
-          exec:
-            command:
-            - podcli
-            - check
-            - http
-            - localhost:9898/healthz
-          initialDelaySeconds: 5
-          timeoutSeconds: 5
-        readinessProbe:
-          exec:
-            command:
-            - podcli
-            - check
-            - http
-            - localhost:9898/readyz
-          initialDelaySeconds: 5
-          timeoutSeconds: 5
-        resources:
-          limits:
-            cpu: 2000m
-            memory: 512Mi
-          requests:
-            cpu: 100m
-            memory: 64Mi
diff --git a/artifacts/gloo/hpa.yaml b/artifacts/gloo/hpa.yaml
deleted file mode 100644
index 48ec76e8..00000000
--- a/artifacts/gloo/hpa.yaml
+++ /dev/null
@@ -1,19 +0,0 @@
-apiVersion: autoscaling/v2beta1
-kind: HorizontalPodAutoscaler
-metadata:
-  name: podinfo
-  namespace: test
-spec:
-  scaleTargetRef:
-    apiVersion: apps/v1
-    kind: Deployment
-    name: podinfo
-  minReplicas: 1
-  maxReplicas: 4
-  metrics:
-  - type: Resource
-    resource:
-      name: cpu
-      # scale up if usage is above
-      # 99% of the requested CPU (100m)
-      targetAverageUtilization: 99
diff --git a/artifacts/gloo/virtual-service.yaml b/artifacts/gloo/virtual-service.yaml
index 53d010dd..169e561a 100644
--- a/artifacts/gloo/virtual-service.yaml
+++ b/artifacts/gloo/virtual-service.yaml
@@ -7,11 +7,11 @@ spec:
   virtualHost:
     domains:
       - '*'
-    name: podinfo.default
+    name: podinfo
     routes:
       - matcher:
           prefix: /
         routeAction:
           upstreamGroup:
             name: podinfo
-            namespace: gloo
+            namespace: test
diff --git a/docs/gitbook/usage/gloo-progressive-delivery.md b/docs/gitbook/usage/gloo-progressive-delivery.md
index 994fba73..bb68506b 100644
--- a/docs/gitbook/usage/gloo-progressive-delivery.md
+++ b/docs/gitbook/usage/gloo-progressive-delivery.md
@@ -54,15 +54,13 @@ kubectl create ns test
 Create a deployment and a horizontal pod autoscaler:
 
 ```bash
-kubectl apply -f ${REPO}/artifacts/gloo/deployment.yaml
-kubectl apply -f ${REPO}/artifacts/gloo/hpa.yaml
+kubectl -n test apply -k github.com/weaveworks/flagger//kustomize/podinfo
 ```
 
 Deploy the load testing service to generate traffic during the canary analysis:
 
 ```bash
-helm upgrade -i flagger-loadtester flagger/loadtester \
---namespace=test
+kubectl -n test apply -k github.com/weaveworks/flagger//kustomize/tester
 ```
 
 Create an virtual service definition that references an upstream group that will be generated by Flagger
@@ -145,14 +143,21 @@ spec:
       # milliseconds
       threshold: 500
       interval: 30s
-    # load testing (optional)
+    # testing (optinal)
     webhooks:
+      - name: acceptance-test
+        type: pre-rollout
+        url: http://flagger-loadtester.test/
+        timeout: 10s
+        metadata:
+          type: bash
+          cmd: "curl -sd 'test' http://podinfo-canary:9898/token | grep token"
       - name: load-test
         url: http://flagger-loadtester.test/
         timeout: 5s
         metadata:
           type: cmd
-          cmd: "hey -z 1m -q 10 -c 2 http://app.example.com/"
+          cmd: "hey -z 2m -q 5 -c 2 -host app.example.com http://gateway-proxy-v2.gloo-system"
 ```
 
 Save the above resource as podinfo-canary.yaml and then apply it:
@@ -260,13 +265,13 @@ podinfod=stefanprodan/podinfo:3.1.2
 Generate HTTP 500 errors:
 
 ```bash
-watch curl http://app.example.com/status/500
+watch curl -H 'Host: app.example.com' http://gateway-proxy-v2.gloo-system/status/500
 ```
 
 Generate high latency:
 
 ```bash
-watch curl http://app.example.com/delay/2
+watch curl -H 'Host: app.example.com' http://gateway-proxy-v2.gloo-system/delay/2
 ```
 
 When the number of failed checks reaches the canary analysis threshold, the traffic is routed back to the primary, 
@@ -343,7 +348,7 @@ podinfod=stefanprodan/podinfo:3.1.3
 Generate 404s:
 
 ```bash
-watch curl http://app.example.com/status/400
+watch curl -H 'Host: app.example.com' http://gateway-proxy-v2.gloo-system/status/400
 ```
 
 Watch Flagger logs:
diff --git a/go.mod b/go.mod
index fd0ae8e2..f5560031 100644
--- a/go.mod
+++ b/go.mod
@@ -6,53 +6,37 @@ require (
 	cloud.google.com/go v0.37.4 // indirect
 	github.com/Masterminds/semver v1.4.2
 	github.com/beorn7/perks v1.0.0 // indirect
-	github.com/bxcodec/faker v2.0.1+incompatible // indirect
-	github.com/envoyproxy/go-control-plane v0.8.0 // indirect
-	github.com/gogo/googleapis v1.2.0 // indirect
-	github.com/gogo/protobuf v1.2.1
+	github.com/gogo/protobuf v1.2.1 // indirect
+	github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef // indirect
 	github.com/golang/protobuf v1.3.1 // indirect
-	github.com/golang/snappy v0.0.1 // indirect
-	github.com/google/btree v1.0.0 // indirect
 	github.com/google/go-cmp v0.3.0
-	github.com/hashicorp/consul v1.4.4 // indirect
-	github.com/hashicorp/go-cleanhttp v0.5.1 // indirect
-	github.com/hashicorp/go-retryablehttp v0.5.3 // indirect
-	github.com/hashicorp/go-rootcerts v1.0.0 // indirect
-	github.com/hashicorp/go-sockaddr v1.0.2 // indirect
-	github.com/hashicorp/hcl v1.0.0 // indirect
-	github.com/hashicorp/serf v0.8.3 // indirect
-	github.com/hashicorp/vault v1.1.0 // indirect
+	github.com/googleapis/gnostic v0.2.0 // indirect
+	github.com/hashicorp/golang-lru v0.5.1 // indirect
 	github.com/imdario/mergo v0.3.7 // indirect
-	github.com/k0kubun/pp v3.0.1+incompatible // indirect
-	github.com/linkerd/linkerd2 v0.0.0-20190221030352-5e47cb150a33 // indirect
-	github.com/lyft/protoc-gen-validate v0.0.14 // indirect
-	github.com/mattn/go-colorable v0.1.1 // indirect
-	github.com/mattn/go-isatty v0.0.7 // indirect
-	github.com/mitchellh/go-homedir v1.1.0 // indirect
-	github.com/mitchellh/go-testing-interface v1.0.0 // indirect
+	github.com/kr/pretty v0.1.0 // indirect
 	github.com/mitchellh/hashstructure v1.0.0
 	github.com/pkg/errors v0.8.1
 	github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829
 	github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90 // indirect
 	github.com/prometheus/common v0.3.0 // indirect
 	github.com/prometheus/procfs v0.0.0-20190416084830-8368d24ba045 // indirect
-	github.com/radovskyb/watcher v1.0.6 // indirect
-	github.com/ryanuber/go-glob v1.0.0 // indirect
-	github.com/solo-io/gloo v0.13.17
-	github.com/solo-io/go-utils v0.7.11 // indirect
-	github.com/solo-io/solo-kit v0.6.3
-	github.com/solo-io/supergloo v0.3.11
-	go.opencensus.io v0.20.2 // indirect
+	go.uber.org/atomic v1.3.2 // indirect
+	go.uber.org/multierr v1.1.0 // indirect
 	go.uber.org/zap v1.9.1
 	golang.org/x/crypto v0.0.0-20190418161225-b43e412143f9 // indirect
+	golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c // indirect
+	golang.org/x/sys v0.0.0-20190508220229-2d0786266e9c // indirect
+	golang.org/x/text v0.3.2 // indirect
 	golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 // indirect
+	gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect
 	gopkg.in/h2non/gock.v1 v1.0.14
+	gopkg.in/inf.v0 v0.9.1 // indirect
 	k8s.io/api v0.0.0-20190620073856-dcce3486da33
-	k8s.io/apiextensions-apiserver v0.0.0-20190315093550-53c4693659ed // indirect
 	k8s.io/apimachinery v0.0.0-20190620073744-d16981aedf33
 	k8s.io/client-go v11.0.0+incompatible
 	k8s.io/code-generator v0.0.0-20190620073620-d55040311883
 	k8s.io/kube-openapi v0.0.0-20190418160015-6b3d3b2d5666 // indirect
+	k8s.io/utils v0.0.0-20190308190857-21c4ce38f2a7 // indirect
 )
 
 replace (
diff --git a/go.sum b/go.sum
index 465cc6eb..2540b722 100644
--- a/go.sum
+++ b/go.sum
@@ -2,126 +2,46 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMT
 cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
 cloud.google.com/go v0.37.4 h1:glPeL3BQJsbF6aIIYfZizMwc5LTYz250bDMjttbBGAU=
 cloud.google.com/go v0.37.4/go.mod h1:NHPJ89PdicEuT9hdPXMROBD91xc5uRDxsMtSB16k7hw=
-git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg=
 github.com/Azure/go-autorest v11.1.2+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24=
 github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
 github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
-github.com/MakeNowJust/heredoc v0.0.0-20171113091838-e9091a26100e/go.mod h1:64YHyfSL2R96J44Nlwm39UHepQbyR5q10x7iYa1ks2E=
-github.com/Masterminds/goutils v1.1.0/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU=
 github.com/Masterminds/semver v1.4.2 h1:WBLTQ37jOCzSLtXNdoo8bNM8876KhNqOKvrlGITgsTc=
 github.com/Masterminds/semver v1.4.2/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y=
-github.com/Masterminds/sprig v2.18.0+incompatible/go.mod h1:y6hNFY5UBTIWBxnzTeuNhlNS5hqE0NB0E6fgfo2Br3o=
 github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ=
-github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMomdKFjzJNB0c=
-github.com/Netflix/go-expect v0.0.0-20180615182759-c93bf25de8e8/go.mod h1:oX5x61PbNXchhh0oikYAH+4Pcfw5LKv21+Jnpr6r6Pc=
-github.com/Netflix/go-expect v0.0.0-20180928190340-9d1f4485533b/go.mod h1:oX5x61PbNXchhh0oikYAH+4Pcfw5LKv21+Jnpr6r6Pc=
 github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
-github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
 github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
-github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
 github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo=
 github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI=
-github.com/airbrake/gobrake v3.6.1+incompatible/go.mod h1:wM4gu3Cn0W0K7GUuVWnlXZU11AGBXMILnrdOU8Kn00o=
-github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs=
 github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
 github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
-github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
 github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
-github.com/appscode/jsonpatch v0.0.0-20190108182946-7c0e3b262f30/go.mod h1:4AJxUpXUhv4N+ziTvIcWWXgeorXpxPZOfk9HdEVr96M=
-github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
-github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da h1:8GUt8eRujhVEGZFFEjBj46YV4rDjvGrNxb0KMWYkL2I=
-github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
-github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310 h1:BUAU3CGlLvorLI26FmByPp2eC2qla6E1Tw+scpcg/to=
-github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
-github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
-github.com/avast/retry-go v2.2.0+incompatible h1:m+w7mVLWa/oKqX2xYqiEKQQkeGH8DDEXB/XnjS54Wyw=
-github.com/avast/retry-go v2.2.0+incompatible/go.mod h1:XtSnn+n/sHqQIpZ10K1qAevBhOOCWBLXXy3hyiqqBrY=
 github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
 github.com/beorn7/perks v1.0.0 h1:HWo1m869IqiPhD389kmkxeTalrjNbbJTC8LXupb+sl0=
 github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
-github.com/bgentry/speakeasy v0.1.0 h1:ByYyxL9InA1OWqxJqqp2A5pYHUrCiAL6K3J+LKSsQkY=
-github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
-github.com/bitly/go-simplejson v0.5.0/go.mod h1:cXHtHw4XUPsvGaxgjIAn8PhEWG9NfngEKAMDJEczWVA=
-github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4=
-github.com/bugsnag/bugsnag-go v1.4.0/go.mod h1:2oa8nejYd4cQ/b0hMIopN0lCRxU0bueqREvZLWFrtK8=
-github.com/bugsnag/panicwrap v1.2.0/go.mod h1:D/8v3kj0zr8ZAKg1AQ6crr+5VwKN5eIywRkfhyM/+dE=
-github.com/bxcodec/faker v2.0.1+incompatible h1:P0KUpUw5w6WJXwrPfv35oc91i4d8nf40Nwln+M/+faA=
-github.com/bxcodec/faker v2.0.1+incompatible/go.mod h1:BNzfpVdTwnFJ6GtfYTcQu6l6rHShT+veBxNCnjCx5XM=
-github.com/certifi/gocertifi v0.0.0-20190105021004-abcd57078448/go.mod h1:GJKEexRPVJrBSOjoqN5VNOIKJ5Q3RViH6eu3puDRwx4=
-github.com/chai2010/gettext-go v0.0.0-20170215093142-bf70f2a70fb1/go.mod h1:/iP1qXHoty45bqomnu2LM+VVyAEdWN+vtSHGlQgyxbw=
 github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
-github.com/coreos/etcd v3.3.12+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
-github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
-github.com/cpuguy83/go-md2man v1.0.8/go.mod h1:N6JayAiVKtlHSnuTCeuLSQVs75hb8q+dYQLjr7cDsKY=
-github.com/cyphar/filepath-securejoin v0.2.2/go.mod h1:FpkQEhXnPnOthhzymB7CGsFk2G9VLXONKD9G7QGMM+4=
 github.com/davecgh/go-spew v0.0.0-20151105211317-5215b55f46b2/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
 github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/dgrijalva/jwt-go v0.0.0-20160705203006-01aeca54ebda/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
-github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
-github.com/docker/docker v1.13.1/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
 github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM=
-github.com/docker/spdystream v0.0.0-20181023171402-6480d4af844c/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM=
 github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs=
 github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU=
 github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I=
-github.com/elazarl/go-bindata-assetfs v1.0.0/go.mod h1:v+YaWX3bdea5J/mo8dSETolEo7R71Vk1u8bnjau5yw4=
 github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc=
 github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
-github.com/emicklei/go-restful v2.9.3+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
-github.com/emicklei/go-restful-swagger12 v0.0.0-20170926063155-7524189396c6/go.mod h1:qr0VowGBT4CS4Q8vFF8BSeKz34PuqKGxs/L0IAQA9DQ=
-github.com/emirpasic/gods v1.9.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o=
-github.com/envoyproxy/go-control-plane v0.8.0 h1:uE6Fp4fOcAJdc1wTQXLJ+SYistkbG1dNoi6Zs1+Ybvk=
-github.com/envoyproxy/go-control-plane v0.8.0/go.mod h1:GSSbY9P1neVhdY7G4wu+IK1rk/dqhiCC/4ExuWJZVuk=
-github.com/envoyproxy/protoc-gen-validate v0.0.14 h1:YBW6/cKy9prEGRYLnaGa4IDhzxZhRCtKsax8srGKDnM=
-github.com/envoyproxy/protoc-gen-validate v0.0.14/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
-github.com/evanphx/json-patch v4.0.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
-github.com/evanphx/json-patch v4.1.0+incompatible h1:K1MDoo4AZ4wU0GIU/fPmtZg7VpzLjCxu+UwBD1FvwOc=
-github.com/evanphx/json-patch v4.1.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
 github.com/evanphx/json-patch v4.2.0+incompatible h1:fUDGZCv/7iAN7u0puUVhvKCcsR6vRfwrJatElLBEf0I=
 github.com/evanphx/json-patch v4.2.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
-github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d/go.mod h1:ZZMPRZwes7CROmyNKgQzC3XPs6L/G2EJLHddWejkmf4=
-github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys=
-github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
-github.com/fgrosse/zaptest v1.1.0 h1:sK9hP0/xBoNX5qfFo3KWFluDXfc809APomI1QXuYELA=
-github.com/fgrosse/zaptest v1.1.0/go.mod h1:vMnRSul6kW7kIUXZgnZZcDwyTn8k49ODfAULL8nmL5w=
-github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
 github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
 github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
-github.com/getsentry/raven-go v0.2.0/go.mod h1:KungGk8q33+aIAZUIVWZDr2OfAEBsO49PX4NzFV5kcQ=
 github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
-github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
-github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
-github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
-github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q=
 github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
 github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
-github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas=
-github.com/go-logr/zapr v0.1.1/go.mod h1:tabnROwaDl0UNxkVeFRbY8bwB37GwRv0P8lg6aAiEnk=
-github.com/go-openapi/analysis v0.0.0-20180825180245-b006789cd277/go.mod h1:k70tL6pCuVxPJOHXQ+wIac1FUrvNkHolPie/cLEU6hI=
-github.com/go-openapi/analysis v0.17.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik=
-github.com/go-openapi/errors v0.17.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0=
 github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0=
-github.com/go-openapi/jsonpointer v0.17.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M=
 github.com/go-openapi/jsonreference v0.0.0-20160704190145-13c6e3589ad9/go.mod h1:W3Z9FmVs9qj+KR4zFKmDPGiLdk1D9Rlm7cyMvf57TTg=
-github.com/go-openapi/jsonreference v0.17.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I=
-github.com/go-openapi/loads v0.17.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU=
-github.com/go-openapi/runtime v0.0.0-20180920151709-4f900dc2ade9/go.mod h1:6v9a6LTXWQCdL8k1AO3cvqx5OtZY/Y9wKTgaoP6YRfA=
 github.com/go-openapi/spec v0.0.0-20160808142527-6aced65f8501/go.mod h1:J8+jY1nAiCcj+friV/PDoE1/3eeccG9LYBs0tYvLOWc=
-github.com/go-openapi/spec v0.17.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI=
-github.com/go-openapi/spec v0.19.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI=
-github.com/go-openapi/strfmt v0.17.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU=
-github.com/go-openapi/strfmt v0.19.0/go.mod h1:+uW+93UVvGGq2qGaZxdDeJqSAqBqBdl+ZPMF/cC8nDY=
 github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I=
-github.com/go-openapi/swag v0.17.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg=
-github.com/go-openapi/validate v0.19.0/go.mod h1:Uh4HdOzKt19xGIGm1qHf/ofbX1YQ4Y+MYsct2VUrAJ4=
 github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
-github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
-github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
-github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s=
-github.com/gogo/googleapis v1.2.0 h1:Z0v3OJDotX9ZBpdz2V+AI7F4fITSZhVE5mg6GQppwMM=
-github.com/gogo/googleapis v1.2.0/go.mod h1:Njal3psf3qN6dwBtQfUmBZh2ybovJ0tlu3o/AC7HYjU=
 github.com/gogo/protobuf v0.0.0-20171007142547-342cbe0a0415/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
 github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
 github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
@@ -132,7 +52,6 @@ github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfU
 github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
 github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef h1:veQD95Isof8w9/WXiA+pa3tz3fJXkt5B7QaRBrM62gk=
 github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
-github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E=
 github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
 github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
 github.com/golang/protobuf v0.0.0-20161109072736-4bd1920723d7/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
@@ -140,19 +59,12 @@ github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5y
 github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg=
 github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
 github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
-github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4=
-github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
 github.com/google/btree v0.0.0-20160524151835-7d79101e329e/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
 github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
-github.com/google/btree v1.0.0 h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo=
-github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
 github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
 github.com/google/go-cmp v0.3.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY=
 github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
-github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ=
-github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
 github.com/google/gofuzz v0.0.0-20161122191042-44d81051d367/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI=
-github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI=
 github.com/google/gofuzz v1.0.0 h1:A8PeW59pxE9IoFRqBp37U+mSNaQoZ46F1f0f863XSXw=
 github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
 github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
@@ -164,83 +76,25 @@ github.com/googleapis/gnostic v0.0.0-20170426233943-68f4ded48ba9/go.mod h1:sJBsC
 github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY=
 github.com/googleapis/gnostic v0.2.0 h1:l6N3VoaVzTncYYW+9yOz2LJJammFZGBO13sqgEhpy9g=
 github.com/googleapis/gnostic v0.2.0/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY=
-github.com/goph/emperror v0.17.1/go.mod h1:+ZbQ+fUNO/6FNiUo0ujtMjhgad9Xa6fQL9KhH4LNHic=
 github.com/gophercloud/gophercloud v0.0.0-20190126172459-c818fa66e4c8/go.mod h1:3WdhXV3rUYy9p6AUW8d94kr+HS62Y4VL9mBnFxsD8q4=
 github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
 github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
 github.com/gregjones/httpcache v0.0.0-20170728041850-787624de3eb7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
-github.com/gregjones/httpcache v0.0.0-20181110185634-c63ab54fda8f h1:ShTPMJQes6tubcjzGMODIVG5hlrCeImaBnZzKF2N8SM=
-github.com/gregjones/httpcache v0.0.0-20181110185634-c63ab54fda8f/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
-github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
-github.com/grpc-ecosystem/grpc-gateway v1.5.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw=
 github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 h1:2VTzZjLZBgl62/EtslCrtky5vbi9dd7HrQPQIx6wqiw=
 github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542/go.mod h1:Ow0tF8D4Kplbc8s8sSb3V2oUCygFHVp8gC3Dn6U4MNI=
-github.com/hashicorp/consul v1.4.4 h1:DR1+5EGgnPsd/LIsK3c9RDvajcsV5GOkGQBSNd3dpn8=
-github.com/hashicorp/consul v1.4.4/go.mod h1:mFrjN1mfidgJfYP1xrJCF+AfRhr6Eaqhb2+sfyn/OOI=
-github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA=
-github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
-github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
-github.com/hashicorp/go-cleanhttp v0.5.1 h1:dH3aiDG9Jvb5r5+bYHsikaOUIpcM0xvgMXVoDkXMzJM=
-github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
-github.com/hashicorp/go-immutable-radix v1.0.0 h1:AKDB1HM5PWEA7i4nhcpwOrO2byshxBjXVn/J/3+z5/0=
-github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
-github.com/hashicorp/go-msgpack v0.5.3 h1:zKjpN5BK/P5lMYrLmBHdBULWbJ0XpYR+7NGzqkZzoD4=
-github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
-github.com/hashicorp/go-multierror v1.0.0 h1:iVjPR7a6H0tWELX5NxNe7bYopibicUzc7uPribsnS6o=
-github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
-github.com/hashicorp/go-retryablehttp v0.5.3 h1:QlWt0KvWT0lq8MFppF9tsJGF+ynG7ztc2KIPhzRGk7s=
-github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs=
-github.com/hashicorp/go-rootcerts v1.0.0 h1:Rqb66Oo1X/eSV1x66xbDccZjhJigjg0+e82kpwzSwCI=
-github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=
-github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
-github.com/hashicorp/go-sockaddr v1.0.2 h1:ztczhD1jLxIRjVejw8gFomI1BQZOe2WoVOu0SyteCQc=
-github.com/hashicorp/go-sockaddr v1.0.2/go.mod h1:rB4wwRAUzs07qva3c5SdrY/NEtAUjGlgmH/UkBUC97A=
-github.com/hashicorp/go-syslog v1.0.0 h1:KaodqZuhUoZereWVIYmpUgZysurB1kBLX2j0MwMrUAE=
-github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
-github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
-github.com/hashicorp/go-uuid v1.0.1 h1:fv1ep09latC32wFoVwnqcnKJGnMSdBanPczbHAYm1BE=
-github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
-github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=
 github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
 github.com/hashicorp/golang-lru v0.5.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+dAcgU=
 github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
-github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
-github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
-github.com/hashicorp/logutils v1.0.0 h1:dLEQVugN8vlakKOUE3ihGLTZJRB4j+M2cdTm/ORI65Y=
-github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
-github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=
-github.com/hashicorp/memberlist v0.1.3 h1:EmmoJme1matNzb+hMpDuR/0sbJSUisxyqBGG676r31M=
-github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
-github.com/hashicorp/serf v0.8.3 h1:MWYcmct5EtKz0efYooPcL0yNkem+7kWxqXDi/UIh+8k=
-github.com/hashicorp/serf v0.8.3/go.mod h1:UpNcs7fFbpKIyZaUuSW6EPiH+eZC7OuyFD+wc1oal+k=
-github.com/hashicorp/vault v1.1.0 h1:v79NUgO5xCZnXVzUkIqFOXtP8YhpnHAi1fk3eo9cuOE=
-github.com/hashicorp/vault v1.1.0/go.mod h1:KfSyffbKxoVyspOdlaGVjIuwLobi07qD1bAbosPMpP0=
-github.com/hinshun/vt10x v0.0.0-20180616224451-1954e6464174/go.mod h1:DqJ97dSdRW1W22yXSB90986pcOyQ7r45iio1KN2ez1A=
-github.com/hinshun/vt10x v0.0.0-20180809195222-d55458df857c/go.mod h1:DqJ97dSdRW1W22yXSB90986pcOyQ7r45iio1KN2ez1A=
 github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
 github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
-github.com/huandu/xstrings v1.2.0/go.mod h1:DvyZB1rfVYsBIigL8HwpZgxHwXozlTgGqn63UyNX5k4=
 github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
-github.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
 github.com/imdario/mergo v0.3.7 h1:Y+UAYTZ7gDEuOfhxKWy+dvb5dRQ6rJjFSdX2HZY1/gI=
 github.com/imdario/mergo v0.3.7/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
-github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
-github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
-github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
 github.com/json-iterator/go v0.0.0-20180612202835-f2b4162afba3/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
-github.com/json-iterator/go v1.1.5/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
 github.com/json-iterator/go v1.1.6 h1:MrUvLMLTMxbqFJ9kzlvat/rYZqZnW3u4wkLzWTaFwKs=
 github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
 github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
 github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
-github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88 h1:uC1QfSlInpQF+M0ao65imhwqKnz3Q2z/d8PWZRMQvDM=
-github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88/go.mod h1:3w7q1U84EfirKl04SVQ/s7nPm1ZPhiXd34z40TNz36k=
-github.com/k0kubun/pp v2.3.0+incompatible/go.mod h1:GWse8YhT0p8pT4ir3ZgBbfZild3tgzSScAn6HmfYukg=
-github.com/k0kubun/pp v3.0.1+incompatible h1:3tqvf7QgUnZ5tXO6pNAZlrvHgl6DvifjDrd9g2S9Z40=
-github.com/k0kubun/pp v3.0.1+incompatible/go.mod h1:GWse8YhT0p8pT4ir3ZgBbfZild3tgzSScAn6HmfYukg=
-github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0/go.mod h1:1NbS8ALrpOvjt0rHPNLyCIeMtbizbir8U//inJ+zuB8=
-github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
-github.com/kevinburke/ssh_config v0.0.0-20180830205328-81db2a75821e/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
 github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
 github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
 github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
@@ -248,45 +102,16 @@ github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFB
 github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
 github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
 github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
-github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
 github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
 github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
-github.com/linkerd/linkerd2 v0.0.0-20190221030352-5e47cb150a33 h1:+eM/rkJK2iCSi0fDzp218TzJSAglSGeI985YYgRS/mY=
-github.com/linkerd/linkerd2 v0.0.0-20190221030352-5e47cb150a33/go.mod h1:n9QnL65Uv2gAG97S0t1q2aYmP33wPQ3oAh0+DJhQSSw=
-github.com/lyft/protoc-gen-validate v0.0.14 h1:xbdDVIHd0Xq5Bfzu+8JR9s7mFmJPMvNLmfGhgcHJdFU=
-github.com/lyft/protoc-gen-validate v0.0.14/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ=
 github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
-github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
-github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
-github.com/mattn/go-colorable v0.1.1 h1:G1f5SKeVxmagw/IyvzvtZE4Gybcc4Tr1tf7I8z0XgOg=
-github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ=
-github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
-github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
-github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
-github.com/mattn/go-isatty v0.0.7 h1:UvyT9uN+3r7yLEYSlJsbQGdsaB/a0DlgWP3pql6iwOc=
-github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
 github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
 github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
-github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
-github.com/miekg/dns v1.0.14 h1:9jZdLNd/P4+SfEJ0TNyxYpsK8N4GtfylBLqtbYN1sbA=
-github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
-github.com/mitchellh/cli v1.0.0 h1:iGBIsUe3+HZ/AD/Vd7DErOt5sU9fa8Uj7A2s1aggv1Y=
-github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
-github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
-github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
-github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
-github.com/mitchellh/go-testing-interface v1.0.0 h1:fzU/JVNcaqHQEcVFAKeR41fkiLdIPrefOvVG1VZ96U0=
-github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
-github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo=
 github.com/mitchellh/hashstructure v1.0.0 h1:ZkRJX1CyOoTkar7p/mLS5TZU4nJ1Rn/F8u9dGS02Q3Y=
 github.com/mitchellh/hashstructure v1.0.0/go.mod h1:QjSHrPWS+BGUVBYkbTZWEnOh3G1DutKwClXU/ABz6AQ=
-github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
-github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=
-github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
 github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
 github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
 github.com/modern-go/reflect2 v0.0.0-20180320133207-05fbef0ca5da/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
-github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
 github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
 github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
 github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
@@ -303,14 +128,7 @@ github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGV
 github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
 github.com/onsi/gomega v1.5.0 h1:izbySO9zDPmjJ8rDjLvkA2zJHIo+HkYXHnf7eN7SSyo=
 github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
-github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
-github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8=
 github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw=
-github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c h1:Lgl0gzECD8GnQ5QCWA8o6BtfL6mDH5rQgM4/fX3avOs=
-github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
-github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
-github.com/pelletier/go-buffruneio v0.2.0/go.mod h1:JkE26KsDizTr40EUHkXVtNPvgGtbSNq5BcowyYOWdKo=
-github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
 github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI=
 github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
 github.com/pierrec/lz4 v2.0.5+incompatible h1:2xWsjqPFWcplujydGg4WmhC/6fZqK42wMM8aXeqhl0I=
@@ -321,73 +139,35 @@ github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE
 github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
 github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
-github.com/posener/complete v1.1.1 h1:ccV59UEOTzVDnDUEFdT95ZzHVZ+5+158q8+SJb2QV5w=
-github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
-github.com/prometheus/client_golang v0.8.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
 github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
-github.com/prometheus/client_golang v0.9.2/go.mod h1:OsXs2jCmiKlQ1lTBmv21f2mNfw4xf/QclQDMrYNZzcM=
 github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829 h1:D+CiwcpGTW6pL6bv6KI3KbyEyCKyS+1JWS2h8PNDnGA=
 github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs=
 github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
 github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
 github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90 h1:S/YWwWx/RA8rT8tKFRuGUZhuA90OyIBpPCXkcbwU8DE=
 github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
-github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
-github.com/prometheus/common v0.0.0-20181126121408-4724e9255275/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
-github.com/prometheus/common v0.0.0-20190104105734-b1c43a6df3ae/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
 github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
 github.com/prometheus/common v0.3.0 h1:taZ4h8Tkxv2kNyoSctBvfXEHmBmxrwmIidZTIaHons4=
 github.com/prometheus/common v0.3.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
-github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
 github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
-github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
-github.com/prometheus/procfs v0.0.0-20190104112138-b1a0a9a36d74/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
 github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
 github.com/prometheus/procfs v0.0.0-20190416084830-8368d24ba045 h1:Raos9GP+3BlCBicScEQ+SjTLpYYac34fZMoeqj9McSM=
 github.com/prometheus/procfs v0.0.0-20190416084830-8368d24ba045/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
-github.com/radovskyb/watcher v1.0.6 h1:8WIQ9UxEYMZjem1OwU7dVH94DXXk9mAIE1i8eqHD+IY=
-github.com/radovskyb/watcher v1.0.6/go.mod h1:78okwvY5wPdzcb1UYnip1pvrZNIVEIh/Cm+ZuvsUYIg=
 github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
 github.com/remyoudompheng/bigfft v0.0.0-20170806203942-52369c62f446/go.mod h1:uYEyJGbgTkfkS4+E/PavXkNJcbFIpEtjt2B0KDQ5+9M=
-github.com/rollbar/rollbar-go v1.0.2/go.mod h1:AcFs5f0I+c71bpHlXNNDbOWJiKwjFDtISeXco0L5PKQ=
-github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
-github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
-github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
-github.com/ryanuber/go-glob v1.0.0 h1:iQh3xXAumdQ+4Ufa5b25cRpC5TYKlno6hsv6Cb3pkBk=
-github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc=
-github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 h1:nn5Wsu0esKSJiIVhscUtVbo7ada43DJhG55ua/hjS5I=
-github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
-github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
 github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
-github.com/solo-io/gloo v0.13.17 h1:rbNmO7e5+0vEq5krkO9/Rcp16PqqvepyGD0j3xPdmhg=
-github.com/solo-io/gloo v0.13.17/go.mod h1:dNnxchbq5F4ITJhX/0fy5brfbsj6vlW+AwgnTlqZN0A=
-github.com/solo-io/go-utils v0.7.11 h1:3Kmk50e6nYqyf7MBY1473XkH5L7qO/Nigjx+t6jEOQo=
-github.com/solo-io/go-utils v0.7.11/go.mod h1:7r+dFKdqJNOjx+odeLFqg8SOwVHyVVG1P0EPt6rNLN8=
-github.com/solo-io/solo-kit v0.6.3 h1:s/SxcgG7YSjW7wu7iQER5MCHSzeXg1b/lCZRazQ0IMw=
-github.com/solo-io/solo-kit v0.6.3/go.mod h1:oBaQ6tOwuO97u7w+s3TeI08YLHcbiWemInx0XkDfKFw=
-github.com/solo-io/supergloo v0.3.11 h1:IwnrL2xojowzb7k+V2wCG3I6WrelzXsezqJiraaVxIM=
-github.com/solo-io/supergloo v0.3.11/go.mod h1:hJuUwop5IMBL9Qc2/G+f+/PfIWPt/2nGr66fDcuhrn8=
-github.com/spf13/afero v1.2.1/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
-github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
 github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
 github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg=
 github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
-github.com/src-d/gcfg v1.4.0/go.mod h1:p/UMsR43ujA89BJY9duynAwIpvqEujIH/jFlfL7jWoI=
 github.com/stefanprodan/klog v0.0.0-20190418165334-9cbb78b20423 h1:qTtUiiNM+iq4IXOwHofKW5+jzvkvnNVz0GFRxwukUlY=
 github.com/stefanprodan/klog v0.0.0-20190418165334-9cbb78b20423/go.mod h1:TYstY5LQfzxFVm9MiiMg7kZ39sc5cue/6CFoY5KgXn8=
 github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
 github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
 github.com/stretchr/testify v0.0.0-20151208002404-e3a8ff8ce365/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
-github.com/stretchr/testify v1.2.1/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
 github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
 github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
 github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
-github.com/technosophos/moniker v0.0.0-20180509230615-a5dbd03a2245/go.mod h1:O1c8HleITsZqzNZDjSNzirUGsMT0oGu9LhHKoJrqO+A=
-github.com/xanzy/ssh-agent v0.2.0/go.mod h1:0NyE30eGUDliuLEHJgYte/zncp2zdTStcOnWhgSqHD8=
-go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA=
 go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
-go.opencensus.io v0.20.2 h1:NAfh7zF0/3/HqtMvJNZ/RFrSlCE6ZTlHmKfhL/Dm1Jk=
-go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
 go.uber.org/atomic v1.3.2 h1:2Oa65PReHzfn29GpvgsYwloV9AVFHPDk8tYxt2c2tr4=
 go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
 go.uber.org/multierr v1.1.0 h1:HoEmRHQPVSqub6w2z2d2EOVs2fjyFRGyofhKuyDq0QI=
@@ -401,7 +181,6 @@ golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL
 golang.org/x/exp v0.0.0-20190312203227-4b39c73a6495 h1:I6A9Ag9FpEKOjcKrRNjQkPHawoXIhKyTGfvvjFAiiAk=
 golang.org/x/exp v0.0.0-20190312203227-4b39c73a6495/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
 golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
-golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
 golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
 golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
 golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
@@ -409,7 +188,6 @@ golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU
 golang.org/x/net v0.0.0-20190206173232-65e2d4e15006 h1:bfLnR+k0tq5Lqt6dflRLcZiz6UaXCMt3vhYJ1l4FQ80=
 golang.org/x/net v0.0.0-20190206173232-65e2d4e15006/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
-golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
 golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421 h1:Wo7BWFiOk0QRFMLYMqJGFMd9CgUAcGx7V+qEg/h5IBI=
 golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
 golang.org/x/oauth2 v0.0.0-20190402181905-9f3314589c9a h1:tImsplftrFpALCYumobsd0K86vlAs/eXGFms2txfJfA=
@@ -435,7 +213,6 @@ gonum.org/v1/gonum v0.0.0-20190331200053-3d26580ed485/go.mod h1:2ltnJ7xHfj0zHS40
 gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw=
 gonum.org/v1/netlib v0.0.0-20190331212654-76723241ea4e h1:jRyg0XfpwWlhEV8mDfdNGBeSJM2fuyh9Yjrnd8kF2Ts=
 gonum.org/v1/netlib v0.0.0-20190331212654-76723241ea4e/go.mod h1:kS+toOQn6AQKjmKJ7gzohV1XkqsFehRA2FbsbkopSuQ=
-google.golang.org/api v0.0.0-20180910000450-7ca32eb868bf/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
 google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk=
 google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
 google.golang.org/appengine v1.4.0 h1:/wp5JvzpHIxhs/dumFmF7BXTf3Z+dd4uXta4kVyO508=
@@ -443,18 +220,11 @@ google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7
 google.golang.org/appengine v1.5.0 h1:KxkO13IPW4Lslp2bz+KHP2E3gtFlrIGNThxkZQ3g+4c=
 google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
 google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
-google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
-google.golang.org/genproto v0.0.0-20181221175505-bd9b4fb69e2f/go.mod h1:7Ep/1NZk928CDR8SjdVbjWNpdIf6nzjE3BTgJDr2Atg=
 google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
 google.golang.org/genproto v0.0.0-20190404172233-64821d5d2107 h1:xtNn7qFlagY2mQNFHMSRPjT2RkOV4OXM7P5TVy9xATo=
 google.golang.org/genproto v0.0.0-20190404172233-64821d5d2107/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
-google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
-google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio=
 google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
 google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
-google.golang.org/grpc v1.19.1 h1:TrBcJ1yqAl1G++wO39nD/qtgpsW9/1+QGrluyMGEYgM=
-google.golang.org/grpc v1.19.1/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
-gopkg.in/AlecAivazis/survey.v1 v1.8.2/go.mod h1:iBNOmqKz/NUbZx3bA+4hAGLRC7fSK7tgtVDT4tB22XA=
 gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
@@ -466,30 +236,18 @@ gopkg.in/h2non/gock.v1 v1.0.14/go.mod h1:sX4zAkdYX1TRGJ2JY156cFspQn4yRWn6p9EMdOD
 gopkg.in/inf.v0 v0.9.0/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
 gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
 gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
-gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=
-gopkg.in/square/go-jose.v2 v2.3.0/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
-gopkg.in/src-d/go-billy.v4 v4.2.1/go.mod h1:tm33zBoOwxjYHZIE+OV8bxTWFMJLrconzFMd38aARFk=
-gopkg.in/src-d/go-git-fixtures.v3 v3.1.1/go.mod h1:dLBcvytrw/TYZsNTWCnkNF2DSIlzWYqTe3rJR56Ac7g=
-gopkg.in/src-d/go-git.v4 v4.10.0/go.mod h1:Vtut8izDyrM8BUVQnzJ+YvmNcem2J89EmfZYCkLokZk=
 gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
 gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
-gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
 gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
 gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
 honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
 honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
-istio.io/gogo-genproto v0.0.0-20190124151557-6d926a6e6feb/go.mod h1:eIDJ6jNk/IeJz6ODSksHl5Aiczy5JUq6vFhJWI5OtiI=
 k8s.io/api v0.0.0-20190620073856-dcce3486da33 h1:aC/EvF9PT1h8NeMEOVwTel8xxbZwq0SZnxXNThEROnE=
 k8s.io/api v0.0.0-20190620073856-dcce3486da33/go.mod h1:ldk709UQo/iedNLOW7J06V9QSSGY5heETKeWqnPoqF8=
-k8s.io/apiextensions-apiserver v0.0.0-20190111034747-7d26de67f177+incompatible/go.mod h1:IxkesAMoaCRoLrPJdZNZUQp9NfZnzqaVzLhb2VEQzXE=
-k8s.io/apiextensions-apiserver v0.0.0-20190315093550-53c4693659ed h1:rCteec//ELIjZMfjIGQbVtZooyaofqDJwsmWwWKItNs=
-k8s.io/apiextensions-apiserver v0.0.0-20190315093550-53c4693659ed/go.mod h1:IxkesAMoaCRoLrPJdZNZUQp9NfZnzqaVzLhb2VEQzXE=
 k8s.io/apimachinery v0.0.0-20190620073744-d16981aedf33 h1:Lkd+QNFOB3DqrDyWo796aodJgFJautn/M+t9IGearPc=
 k8s.io/apimachinery v0.0.0-20190620073744-d16981aedf33/go.mod h1:9q5NW/mMno/nwbRZd/Ks2TECgi2PTZ9cwarf4q+ze6Q=
-k8s.io/apiserver v0.0.0-20190111033246-d50e9ac5404f+incompatible/go.mod h1:6bqaTSOSJavUIXUtfaR9Os9JtTCm8ZqH2SUl2S60C4w=
-k8s.io/cli-runtime v0.0.0-20190111035321-c7263d800665+incompatible/go.mod h1:qWnH3/b8sp/l7EvlDh7ulDU3UWA4P4N1NFbEEP791tM=
 k8s.io/client-go v0.0.0-20190620074045-585a16d2e773 h1:XyjDnwRO9icfyrN7HRSa8o3NqdPOEQoVW8vWizuqyQQ=
 k8s.io/client-go v0.0.0-20190620074045-585a16d2e773/go.mod h1:miKCC7C/WGwJqcDctyJtAnP3Gss0Y5KwURqJ7q5pfEw=
 k8s.io/code-generator v0.0.0-20190620073620-d55040311883 h1:NWWNvN6IdpmQvZ43rVccCI8GPUrheK8XNdqeKycw0DI=
@@ -497,12 +255,9 @@ k8s.io/code-generator v0.0.0-20190620073620-d55040311883/go.mod h1:+a+9g9W0llgbg
 k8s.io/gengo v0.0.0-20190116091435-f8a0810f38af/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
 k8s.io/gengo v0.0.0-20190128074634-0689ccc1d7d6 h1:4s3/R4+OYYYUKptXPhZKjQ04WJ6EhQQVFdjOFvCazDk=
 k8s.io/gengo v0.0.0-20190128074634-0689ccc1d7d6/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
-k8s.io/helm v2.13.0+incompatible/go.mod h1:LZzlS4LQBHfciFOurYBFkCMTaZ0D1l+p0teMg7TSULI=
 k8s.io/kube-openapi v0.0.0-20190228160746-b3a7cee44a30/go.mod h1:BXM9ceUBTj2QnfH2MK1odQs778ajze1RxcmP6S8RVVc=
-k8s.io/kube-openapi v0.0.0-20190401085232-94e1e7b7574c/go.mod h1:BXM9ceUBTj2QnfH2MK1odQs778ajze1RxcmP6S8RVVc=
 k8s.io/kube-openapi v0.0.0-20190418160015-6b3d3b2d5666 h1:hlzz2EvLPcefAcG/j0tOZpds4LWSElZzxpZuhxbblbc=
 k8s.io/kube-openapi v0.0.0-20190418160015-6b3d3b2d5666/go.mod h1:jqYp7BKXW0Jl+F1dWXBieUmcHKMPpGHGWA0uqfpOZZ4=
-k8s.io/kubernetes v1.13.2/go.mod h1:ocZa8+6APFNC2tX1DZASIbocyYT5jHzqFVsY5aoB7Jk=
 k8s.io/utils v0.0.0-20190221042446-c2654d5206da/go.mod h1:8k8uAuAQ0rXslZKaEWd0c3oVhZz7sSzSiPnVZayjIX0=
 k8s.io/utils v0.0.0-20190308190857-21c4ce38f2a7 h1:8r+l4bNWjRlsFYlQJnKJ2p7s1YQPj4XyXiJVqDHRx7c=
 k8s.io/utils v0.0.0-20190308190857-21c4ce38f2a7/go.mod h1:8k8uAuAQ0rXslZKaEWd0c3oVhZz7sSzSiPnVZayjIX0=
@@ -511,8 +266,6 @@ modernc.org/golex v1.0.0/go.mod h1:b/QX9oBD/LhixY6NDh+IdGv17hgB+51fET1i2kPSmvk=
 modernc.org/mathutil v1.0.0/go.mod h1:wU0vUrJsVWBZ4P6e7xtFJEhFSNsfRLJ8H458uRjg03k=
 modernc.org/strutil v1.0.0/go.mod h1:lstksw84oURvj9y3tn8lGvRxyRC1S2+g5uuIzNfIOBs=
 modernc.org/xc v1.0.0/go.mod h1:mRNCo0bvLjGhHO9WsyuKVU4q0ceiDDDoEeWDJHrNx8I=
-sigs.k8s.io/controller-runtime v0.1.10/go.mod h1:HFAYoOh6XMV+jKF1UjFwrknPbowfyHEHHRdJMf2jMX8=
 sigs.k8s.io/structured-merge-diff v0.0.0-20181214233322-d43a45b8663b/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI=
 sigs.k8s.io/yaml v1.1.0 h1:4A07+ZFc2wgJwo8YNlQpr1rVlgUDlxXHhPJciaPY5gs=
 sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
-vbom.ml/util v0.0.0-20180919145318-efcd4e0f9787/go.mod h1:so/NYdZXCz+E3ZpW0uAoCj6uzU2+8OWDFv/HxUSs7kI=
diff --git a/hack/update-codegen.sh b/hack/update-codegen.sh
index 2913d77e..4176e93d 100755
--- a/hack/update-codegen.sh
+++ b/hack/update-codegen.sh
@@ -30,7 +30,7 @@ chmod +x ${CODEGEN_PKG}/generate-groups.sh
 
 ${CODEGEN_PKG}/generate-groups.sh all \
     github.com/weaveworks/flagger/pkg/client github.com/weaveworks/flagger/pkg/apis \
-    "appmesh:v1beta1 istio:v1alpha3 flagger:v1alpha3 smi:v1alpha1" \
+    "flagger:v1alpha3 appmesh:v1beta1 istio:v1alpha3 smi:v1alpha1 gloo:v1" \
     --output-base "${TEMP_DIR}" \
     --go-header-file ${SCRIPT_ROOT}/hack/boilerplate.go.txt
 
diff --git a/pkg/apis/gloo/register.go b/pkg/apis/gloo/register.go
new file mode 100644
index 00000000..1e6e9016
--- /dev/null
+++ b/pkg/apis/gloo/register.go
@@ -0,0 +1,5 @@
+package gloo
+
+const (
+	GroupName = "gloo.solo.io"
+)
diff --git a/pkg/apis/gloo/v1/doc.go b/pkg/apis/gloo/v1/doc.go
new file mode 100644
index 00000000..0c641332
--- /dev/null
+++ b/pkg/apis/gloo/v1/doc.go
@@ -0,0 +1,5 @@
+// +k8s:deepcopy-gen=package
+
+// Package v1 is the v1 version of the API.
+// +groupName=gloo.solo.io
+package v1
diff --git a/pkg/apis/gloo/v1/register.go b/pkg/apis/gloo/v1/register.go
new file mode 100755
index 00000000..f9ce797e
--- /dev/null
+++ b/pkg/apis/gloo/v1/register.go
@@ -0,0 +1,36 @@
+package v1
+
+import (
+	"github.com/weaveworks/flagger/pkg/apis/gloo"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"k8s.io/apimachinery/pkg/runtime"
+	"k8s.io/apimachinery/pkg/runtime/schema"
+)
+
+// SchemeGroupVersion is group version used to register these objects
+var SchemeGroupVersion = schema.GroupVersion{Group: gloo.GroupName, Version: "v1"}
+
+// Kind takes an unqualified kind and returns back a Group qualified GroupKind
+func Kind(kind string) schema.GroupKind {
+	return SchemeGroupVersion.WithKind(kind).GroupKind()
+}
+
+// Resource takes an unqualified resource and returns a Group qualified GroupResource
+func Resource(resource string) schema.GroupResource {
+	return SchemeGroupVersion.WithResource(resource).GroupResource()
+}
+
+var (
+	SchemeBuilder = runtime.NewSchemeBuilder(addKnownTypes)
+	AddToScheme   = SchemeBuilder.AddToScheme
+)
+
+// Adds the list of known types to Scheme.
+func addKnownTypes(scheme *runtime.Scheme) error {
+	scheme.AddKnownTypes(SchemeGroupVersion,
+		&UpstreamGroup{},
+		&UpstreamGroupList{},
+	)
+	metav1.AddToGroupVersion(scheme, SchemeGroupVersion)
+	return nil
+}
diff --git a/pkg/apis/gloo/v1/types.go b/pkg/apis/gloo/v1/types.go
new file mode 100644
index 00000000..5209206c
--- /dev/null
+++ b/pkg/apis/gloo/v1/types.go
@@ -0,0 +1,49 @@
+package v1
+
+import (
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+)
+
+// +genclient
+// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
+
+// UpstreamGroup is a specification for a Gloo UpstreamGroup resource
+type UpstreamGroup struct {
+	metav1.TypeMeta   `json:",inline"`
+	metav1.ObjectMeta `json:"metadata,omitempty"`
+
+	Spec UpstreamGroupSpec `json:"spec"`
+}
+
+type UpstreamGroupSpec struct {
+	Destinations []WeightedDestination `json:"destinations,omitempty"`
+}
+
+// WeightedDestination attaches a weight to a single destination.
+type WeightedDestination struct {
+	Destination Destination `json:"destination,omitempty"`
+	// Weight must be greater than zero
+	// Routing to each destination will be balanced by the ratio of the destination's weight to the total weight on a route
+	Weight uint32 `json:"weight,omitempty"`
+}
+
+// Destinations define routable destinations for proxied requests
+type Destination struct {
+	Upstream ResourceRef `json:"upstream"`
+}
+
+// ResourceRef references resources across namespaces
+type ResourceRef struct {
+	Name      string `json:"name,omitempty"`
+	Namespace string `json:"namespace,omitempty"`
+}
+
+// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
+
+// UpstreamGroupList is a list of UpstreamGroup resources
+type UpstreamGroupList struct {
+	metav1.TypeMeta `json:",inline"`
+	metav1.ListMeta `json:"metadata"`
+
+	Items []UpstreamGroup `json:"items"`
+}
diff --git a/pkg/apis/gloo/v1/zz_generated.deepcopy.go b/pkg/apis/gloo/v1/zz_generated.deepcopy.go
new file mode 100644
index 00000000..909575ed
--- /dev/null
+++ b/pkg/apis/gloo/v1/zz_generated.deepcopy.go
@@ -0,0 +1,156 @@
+// +build !ignore_autogenerated
+
+/*
+Copyright The Flagger Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+// Code generated by deepcopy-gen. DO NOT EDIT.
+
+package v1
+
+import (
+	runtime "k8s.io/apimachinery/pkg/runtime"
+)
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *Destination) DeepCopyInto(out *Destination) {
+	*out = *in
+	out.Upstream = in.Upstream
+	return
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Destination.
+func (in *Destination) DeepCopy() *Destination {
+	if in == nil {
+		return nil
+	}
+	out := new(Destination)
+	in.DeepCopyInto(out)
+	return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *ResourceRef) DeepCopyInto(out *ResourceRef) {
+	*out = *in
+	return
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ResourceRef.
+func (in *ResourceRef) DeepCopy() *ResourceRef {
+	if in == nil {
+		return nil
+	}
+	out := new(ResourceRef)
+	in.DeepCopyInto(out)
+	return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *UpstreamGroup) DeepCopyInto(out *UpstreamGroup) {
+	*out = *in
+	out.TypeMeta = in.TypeMeta
+	in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
+	in.Spec.DeepCopyInto(&out.Spec)
+	return
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new UpstreamGroup.
+func (in *UpstreamGroup) DeepCopy() *UpstreamGroup {
+	if in == nil {
+		return nil
+	}
+	out := new(UpstreamGroup)
+	in.DeepCopyInto(out)
+	return out
+}
+
+// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
+func (in *UpstreamGroup) DeepCopyObject() runtime.Object {
+	if c := in.DeepCopy(); c != nil {
+		return c
+	}
+	return nil
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *UpstreamGroupList) DeepCopyInto(out *UpstreamGroupList) {
+	*out = *in
+	out.TypeMeta = in.TypeMeta
+	in.ListMeta.DeepCopyInto(&out.ListMeta)
+	if in.Items != nil {
+		in, out := &in.Items, &out.Items
+		*out = make([]UpstreamGroup, len(*in))
+		for i := range *in {
+			(*in)[i].DeepCopyInto(&(*out)[i])
+		}
+	}
+	return
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new UpstreamGroupList.
+func (in *UpstreamGroupList) DeepCopy() *UpstreamGroupList {
+	if in == nil {
+		return nil
+	}
+	out := new(UpstreamGroupList)
+	in.DeepCopyInto(out)
+	return out
+}
+
+// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
+func (in *UpstreamGroupList) DeepCopyObject() runtime.Object {
+	if c := in.DeepCopy(); c != nil {
+		return c
+	}
+	return nil
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *UpstreamGroupSpec) DeepCopyInto(out *UpstreamGroupSpec) {
+	*out = *in
+	if in.Destinations != nil {
+		in, out := &in.Destinations, &out.Destinations
+		*out = make([]WeightedDestination, len(*in))
+		copy(*out, *in)
+	}
+	return
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new UpstreamGroupSpec.
+func (in *UpstreamGroupSpec) DeepCopy() *UpstreamGroupSpec {
+	if in == nil {
+		return nil
+	}
+	out := new(UpstreamGroupSpec)
+	in.DeepCopyInto(out)
+	return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *WeightedDestination) DeepCopyInto(out *WeightedDestination) {
+	*out = *in
+	out.Destination = in.Destination
+	return
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WeightedDestination.
+func (in *WeightedDestination) DeepCopy() *WeightedDestination {
+	if in == nil {
+		return nil
+	}
+	out := new(WeightedDestination)
+	in.DeepCopyInto(out)
+	return out
+}
diff --git a/pkg/client/clientset/versioned/clientset.go b/pkg/client/clientset/versioned/clientset.go
index 65e60a62..ba7a2a12 100644
--- a/pkg/client/clientset/versioned/clientset.go
+++ b/pkg/client/clientset/versioned/clientset.go
@@ -21,6 +21,7 @@ package versioned
 import (
 	appmeshv1beta1 "github.com/weaveworks/flagger/pkg/client/clientset/versioned/typed/appmesh/v1beta1"
 	flaggerv1alpha3 "github.com/weaveworks/flagger/pkg/client/clientset/versioned/typed/flagger/v1alpha3"
+	gloov1 "github.com/weaveworks/flagger/pkg/client/clientset/versioned/typed/gloo/v1"
 	networkingv1alpha3 "github.com/weaveworks/flagger/pkg/client/clientset/versioned/typed/istio/v1alpha3"
 	splitv1alpha1 "github.com/weaveworks/flagger/pkg/client/clientset/versioned/typed/smi/v1alpha1"
 	discovery "k8s.io/client-go/discovery"
@@ -32,6 +33,7 @@ type Interface interface {
 	Discovery() discovery.DiscoveryInterface
 	AppmeshV1beta1() appmeshv1beta1.AppmeshV1beta1Interface
 	FlaggerV1alpha3() flaggerv1alpha3.FlaggerV1alpha3Interface
+	GlooV1() gloov1.GlooV1Interface
 	NetworkingV1alpha3() networkingv1alpha3.NetworkingV1alpha3Interface
 	SplitV1alpha1() splitv1alpha1.SplitV1alpha1Interface
 }
@@ -42,6 +44,7 @@ type Clientset struct {
 	*discovery.DiscoveryClient
 	appmeshV1beta1     *appmeshv1beta1.AppmeshV1beta1Client
 	flaggerV1alpha3    *flaggerv1alpha3.FlaggerV1alpha3Client
+	glooV1             *gloov1.GlooV1Client
 	networkingV1alpha3 *networkingv1alpha3.NetworkingV1alpha3Client
 	splitV1alpha1      *splitv1alpha1.SplitV1alpha1Client
 }
@@ -56,6 +59,11 @@ func (c *Clientset) FlaggerV1alpha3() flaggerv1alpha3.FlaggerV1alpha3Interface {
 	return c.flaggerV1alpha3
 }
 
+// GlooV1 retrieves the GlooV1Client
+func (c *Clientset) GlooV1() gloov1.GlooV1Interface {
+	return c.glooV1
+}
+
 // NetworkingV1alpha3 retrieves the NetworkingV1alpha3Client
 func (c *Clientset) NetworkingV1alpha3() networkingv1alpha3.NetworkingV1alpha3Interface {
 	return c.networkingV1alpha3
@@ -90,6 +98,10 @@ func NewForConfig(c *rest.Config) (*Clientset, error) {
 	if err != nil {
 		return nil, err
 	}
+	cs.glooV1, err = gloov1.NewForConfig(&configShallowCopy)
+	if err != nil {
+		return nil, err
+	}
 	cs.networkingV1alpha3, err = networkingv1alpha3.NewForConfig(&configShallowCopy)
 	if err != nil {
 		return nil, err
@@ -112,6 +124,7 @@ func NewForConfigOrDie(c *rest.Config) *Clientset {
 	var cs Clientset
 	cs.appmeshV1beta1 = appmeshv1beta1.NewForConfigOrDie(c)
 	cs.flaggerV1alpha3 = flaggerv1alpha3.NewForConfigOrDie(c)
+	cs.glooV1 = gloov1.NewForConfigOrDie(c)
 	cs.networkingV1alpha3 = networkingv1alpha3.NewForConfigOrDie(c)
 	cs.splitV1alpha1 = splitv1alpha1.NewForConfigOrDie(c)
 
@@ -124,6 +137,7 @@ func New(c rest.Interface) *Clientset {
 	var cs Clientset
 	cs.appmeshV1beta1 = appmeshv1beta1.New(c)
 	cs.flaggerV1alpha3 = flaggerv1alpha3.New(c)
+	cs.glooV1 = gloov1.New(c)
 	cs.networkingV1alpha3 = networkingv1alpha3.New(c)
 	cs.splitV1alpha1 = splitv1alpha1.New(c)
 
diff --git a/pkg/client/clientset/versioned/fake/clientset_generated.go b/pkg/client/clientset/versioned/fake/clientset_generated.go
index 5419a6b1..8541f087 100644
--- a/pkg/client/clientset/versioned/fake/clientset_generated.go
+++ b/pkg/client/clientset/versioned/fake/clientset_generated.go
@@ -24,6 +24,8 @@ import (
 	fakeappmeshv1beta1 "github.com/weaveworks/flagger/pkg/client/clientset/versioned/typed/appmesh/v1beta1/fake"
 	flaggerv1alpha3 "github.com/weaveworks/flagger/pkg/client/clientset/versioned/typed/flagger/v1alpha3"
 	fakeflaggerv1alpha3 "github.com/weaveworks/flagger/pkg/client/clientset/versioned/typed/flagger/v1alpha3/fake"
+	gloov1 "github.com/weaveworks/flagger/pkg/client/clientset/versioned/typed/gloo/v1"
+	fakegloov1 "github.com/weaveworks/flagger/pkg/client/clientset/versioned/typed/gloo/v1/fake"
 	networkingv1alpha3 "github.com/weaveworks/flagger/pkg/client/clientset/versioned/typed/istio/v1alpha3"
 	fakenetworkingv1alpha3 "github.com/weaveworks/flagger/pkg/client/clientset/versioned/typed/istio/v1alpha3/fake"
 	splitv1alpha1 "github.com/weaveworks/flagger/pkg/client/clientset/versioned/typed/smi/v1alpha1"
@@ -92,6 +94,11 @@ func (c *Clientset) FlaggerV1alpha3() flaggerv1alpha3.FlaggerV1alpha3Interface {
 	return &fakeflaggerv1alpha3.FakeFlaggerV1alpha3{Fake: &c.Fake}
 }
 
+// GlooV1 retrieves the GlooV1Client
+func (c *Clientset) GlooV1() gloov1.GlooV1Interface {
+	return &fakegloov1.FakeGlooV1{Fake: &c.Fake}
+}
+
 // NetworkingV1alpha3 retrieves the NetworkingV1alpha3Client
 func (c *Clientset) NetworkingV1alpha3() networkingv1alpha3.NetworkingV1alpha3Interface {
 	return &fakenetworkingv1alpha3.FakeNetworkingV1alpha3{Fake: &c.Fake}
diff --git a/pkg/client/clientset/versioned/fake/register.go b/pkg/client/clientset/versioned/fake/register.go
index b5910b2e..32fcf73c 100644
--- a/pkg/client/clientset/versioned/fake/register.go
+++ b/pkg/client/clientset/versioned/fake/register.go
@@ -21,6 +21,7 @@ package fake
 import (
 	appmeshv1beta1 "github.com/weaveworks/flagger/pkg/apis/appmesh/v1beta1"
 	flaggerv1alpha3 "github.com/weaveworks/flagger/pkg/apis/flagger/v1alpha3"
+	gloov1 "github.com/weaveworks/flagger/pkg/apis/gloo/v1"
 	networkingv1alpha3 "github.com/weaveworks/flagger/pkg/apis/istio/v1alpha3"
 	splitv1alpha1 "github.com/weaveworks/flagger/pkg/apis/smi/v1alpha1"
 	v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -36,6 +37,7 @@ var parameterCodec = runtime.NewParameterCodec(scheme)
 var localSchemeBuilder = runtime.SchemeBuilder{
 	appmeshv1beta1.AddToScheme,
 	flaggerv1alpha3.AddToScheme,
+	gloov1.AddToScheme,
 	networkingv1alpha3.AddToScheme,
 	splitv1alpha1.AddToScheme,
 }
diff --git a/pkg/client/clientset/versioned/scheme/register.go b/pkg/client/clientset/versioned/scheme/register.go
index 7337f87a..6692b192 100644
--- a/pkg/client/clientset/versioned/scheme/register.go
+++ b/pkg/client/clientset/versioned/scheme/register.go
@@ -21,6 +21,7 @@ package scheme
 import (
 	appmeshv1beta1 "github.com/weaveworks/flagger/pkg/apis/appmesh/v1beta1"
 	flaggerv1alpha3 "github.com/weaveworks/flagger/pkg/apis/flagger/v1alpha3"
+	gloov1 "github.com/weaveworks/flagger/pkg/apis/gloo/v1"
 	networkingv1alpha3 "github.com/weaveworks/flagger/pkg/apis/istio/v1alpha3"
 	splitv1alpha1 "github.com/weaveworks/flagger/pkg/apis/smi/v1alpha1"
 	v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -36,6 +37,7 @@ var ParameterCodec = runtime.NewParameterCodec(Scheme)
 var localSchemeBuilder = runtime.SchemeBuilder{
 	appmeshv1beta1.AddToScheme,
 	flaggerv1alpha3.AddToScheme,
+	gloov1.AddToScheme,
 	networkingv1alpha3.AddToScheme,
 	splitv1alpha1.AddToScheme,
 }
diff --git a/pkg/client/clientset/versioned/typed/gloo/v1/doc.go b/pkg/client/clientset/versioned/typed/gloo/v1/doc.go
new file mode 100644
index 00000000..fb0cec7d
--- /dev/null
+++ b/pkg/client/clientset/versioned/typed/gloo/v1/doc.go
@@ -0,0 +1,20 @@
+/*
+Copyright The Flagger Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+// Code generated by client-gen. DO NOT EDIT.
+
+// This package has the automatically generated typed clients.
+package v1
diff --git a/pkg/client/clientset/versioned/typed/gloo/v1/fake/doc.go b/pkg/client/clientset/versioned/typed/gloo/v1/fake/doc.go
new file mode 100644
index 00000000..7a3b19cb
--- /dev/null
+++ b/pkg/client/clientset/versioned/typed/gloo/v1/fake/doc.go
@@ -0,0 +1,20 @@
+/*
+Copyright The Flagger Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+// Code generated by client-gen. DO NOT EDIT.
+
+// Package fake has the automatically generated clients.
+package fake
diff --git a/pkg/client/clientset/versioned/typed/gloo/v1/fake/fake_gloo_client.go b/pkg/client/clientset/versioned/typed/gloo/v1/fake/fake_gloo_client.go
new file mode 100644
index 00000000..21e3167d
--- /dev/null
+++ b/pkg/client/clientset/versioned/typed/gloo/v1/fake/fake_gloo_client.go
@@ -0,0 +1,40 @@
+/*
+Copyright The Flagger Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+// Code generated by client-gen. DO NOT EDIT.
+
+package fake
+
+import (
+	v1 "github.com/weaveworks/flagger/pkg/client/clientset/versioned/typed/gloo/v1"
+	rest "k8s.io/client-go/rest"
+	testing "k8s.io/client-go/testing"
+)
+
+type FakeGlooV1 struct {
+	*testing.Fake
+}
+
+func (c *FakeGlooV1) UpstreamGroups(namespace string) v1.UpstreamGroupInterface {
+	return &FakeUpstreamGroups{c, namespace}
+}
+
+// RESTClient returns a RESTClient that is used to communicate
+// with API server by this client implementation.
+func (c *FakeGlooV1) RESTClient() rest.Interface {
+	var ret *rest.RESTClient
+	return ret
+}
diff --git a/pkg/client/clientset/versioned/typed/gloo/v1/fake/fake_upstreamgroup.go b/pkg/client/clientset/versioned/typed/gloo/v1/fake/fake_upstreamgroup.go
new file mode 100644
index 00000000..264f8ff6
--- /dev/null
+++ b/pkg/client/clientset/versioned/typed/gloo/v1/fake/fake_upstreamgroup.go
@@ -0,0 +1,128 @@
+/*
+Copyright The Flagger Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+// Code generated by client-gen. DO NOT EDIT.
+
+package fake
+
+import (
+	gloov1 "github.com/weaveworks/flagger/pkg/apis/gloo/v1"
+	v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	labels "k8s.io/apimachinery/pkg/labels"
+	schema "k8s.io/apimachinery/pkg/runtime/schema"
+	types "k8s.io/apimachinery/pkg/types"
+	watch "k8s.io/apimachinery/pkg/watch"
+	testing "k8s.io/client-go/testing"
+)
+
+// FakeUpstreamGroups implements UpstreamGroupInterface
+type FakeUpstreamGroups struct {
+	Fake *FakeGlooV1
+	ns   string
+}
+
+var upstreamgroupsResource = schema.GroupVersionResource{Group: "gloo.solo.io", Version: "v1", Resource: "upstreamgroups"}
+
+var upstreamgroupsKind = schema.GroupVersionKind{Group: "gloo.solo.io", Version: "v1", Kind: "UpstreamGroup"}
+
+// Get takes name of the upstreamGroup, and returns the corresponding upstreamGroup object, and an error if there is any.
+func (c *FakeUpstreamGroups) Get(name string, options v1.GetOptions) (result *gloov1.UpstreamGroup, err error) {
+	obj, err := c.Fake.
+		Invokes(testing.NewGetAction(upstreamgroupsResource, c.ns, name), &gloov1.UpstreamGroup{})
+
+	if obj == nil {
+		return nil, err
+	}
+	return obj.(*gloov1.UpstreamGroup), err
+}
+
+// List takes label and field selectors, and returns the list of UpstreamGroups that match those selectors.
+func (c *FakeUpstreamGroups) List(opts v1.ListOptions) (result *gloov1.UpstreamGroupList, err error) {
+	obj, err := c.Fake.
+		Invokes(testing.NewListAction(upstreamgroupsResource, upstreamgroupsKind, c.ns, opts), &gloov1.UpstreamGroupList{})
+
+	if obj == nil {
+		return nil, err
+	}
+
+	label, _, _ := testing.ExtractFromListOptions(opts)
+	if label == nil {
+		label = labels.Everything()
+	}
+	list := &gloov1.UpstreamGroupList{ListMeta: obj.(*gloov1.UpstreamGroupList).ListMeta}
+	for _, item := range obj.(*gloov1.UpstreamGroupList).Items {
+		if label.Matches(labels.Set(item.Labels)) {
+			list.Items = append(list.Items, item)
+		}
+	}
+	return list, err
+}
+
+// Watch returns a watch.Interface that watches the requested upstreamGroups.
+func (c *FakeUpstreamGroups) Watch(opts v1.ListOptions) (watch.Interface, error) {
+	return c.Fake.
+		InvokesWatch(testing.NewWatchAction(upstreamgroupsResource, c.ns, opts))
+
+}
+
+// Create takes the representation of a upstreamGroup and creates it.  Returns the server's representation of the upstreamGroup, and an error, if there is any.
+func (c *FakeUpstreamGroups) Create(upstreamGroup *gloov1.UpstreamGroup) (result *gloov1.UpstreamGroup, err error) {
+	obj, err := c.Fake.
+		Invokes(testing.NewCreateAction(upstreamgroupsResource, c.ns, upstreamGroup), &gloov1.UpstreamGroup{})
+
+	if obj == nil {
+		return nil, err
+	}
+	return obj.(*gloov1.UpstreamGroup), err
+}
+
+// Update takes the representation of a upstreamGroup and updates it. Returns the server's representation of the upstreamGroup, and an error, if there is any.
+func (c *FakeUpstreamGroups) Update(upstreamGroup *gloov1.UpstreamGroup) (result *gloov1.UpstreamGroup, err error) {
+	obj, err := c.Fake.
+		Invokes(testing.NewUpdateAction(upstreamgroupsResource, c.ns, upstreamGroup), &gloov1.UpstreamGroup{})
+
+	if obj == nil {
+		return nil, err
+	}
+	return obj.(*gloov1.UpstreamGroup), err
+}
+
+// Delete takes name of the upstreamGroup and deletes it. Returns an error if one occurs.
+func (c *FakeUpstreamGroups) Delete(name string, options *v1.DeleteOptions) error {
+	_, err := c.Fake.
+		Invokes(testing.NewDeleteAction(upstreamgroupsResource, c.ns, name), &gloov1.UpstreamGroup{})
+
+	return err
+}
+
+// DeleteCollection deletes a collection of objects.
+func (c *FakeUpstreamGroups) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error {
+	action := testing.NewDeleteCollectionAction(upstreamgroupsResource, c.ns, listOptions)
+
+	_, err := c.Fake.Invokes(action, &gloov1.UpstreamGroupList{})
+	return err
+}
+
+// Patch applies the patch and returns the patched upstreamGroup.
+func (c *FakeUpstreamGroups) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *gloov1.UpstreamGroup, err error) {
+	obj, err := c.Fake.
+		Invokes(testing.NewPatchSubresourceAction(upstreamgroupsResource, c.ns, name, pt, data, subresources...), &gloov1.UpstreamGroup{})
+
+	if obj == nil {
+		return nil, err
+	}
+	return obj.(*gloov1.UpstreamGroup), err
+}
diff --git a/pkg/client/clientset/versioned/typed/gloo/v1/generated_expansion.go b/pkg/client/clientset/versioned/typed/gloo/v1/generated_expansion.go
new file mode 100644
index 00000000..a727e452
--- /dev/null
+++ b/pkg/client/clientset/versioned/typed/gloo/v1/generated_expansion.go
@@ -0,0 +1,21 @@
+/*
+Copyright The Flagger Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+// Code generated by client-gen. DO NOT EDIT.
+
+package v1
+
+type UpstreamGroupExpansion interface{}
diff --git a/pkg/client/clientset/versioned/typed/gloo/v1/gloo_client.go b/pkg/client/clientset/versioned/typed/gloo/v1/gloo_client.go
new file mode 100644
index 00000000..143d844c
--- /dev/null
+++ b/pkg/client/clientset/versioned/typed/gloo/v1/gloo_client.go
@@ -0,0 +1,89 @@
+/*
+Copyright The Flagger Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+// Code generated by client-gen. DO NOT EDIT.
+
+package v1
+
+import (
+	v1 "github.com/weaveworks/flagger/pkg/apis/gloo/v1"
+	"github.com/weaveworks/flagger/pkg/client/clientset/versioned/scheme"
+	rest "k8s.io/client-go/rest"
+)
+
+type GlooV1Interface interface {
+	RESTClient() rest.Interface
+	UpstreamGroupsGetter
+}
+
+// GlooV1Client is used to interact with features provided by the gloo.solo.io group.
+type GlooV1Client struct {
+	restClient rest.Interface
+}
+
+func (c *GlooV1Client) UpstreamGroups(namespace string) UpstreamGroupInterface {
+	return newUpstreamGroups(c, namespace)
+}
+
+// NewForConfig creates a new GlooV1Client for the given config.
+func NewForConfig(c *rest.Config) (*GlooV1Client, error) {
+	config := *c
+	if err := setConfigDefaults(&config); err != nil {
+		return nil, err
+	}
+	client, err := rest.RESTClientFor(&config)
+	if err != nil {
+		return nil, err
+	}
+	return &GlooV1Client{client}, nil
+}
+
+// NewForConfigOrDie creates a new GlooV1Client for the given config and
+// panics if there is an error in the config.
+func NewForConfigOrDie(c *rest.Config) *GlooV1Client {
+	client, err := NewForConfig(c)
+	if err != nil {
+		panic(err)
+	}
+	return client
+}
+
+// New creates a new GlooV1Client for the given RESTClient.
+func New(c rest.Interface) *GlooV1Client {
+	return &GlooV1Client{c}
+}
+
+func setConfigDefaults(config *rest.Config) error {
+	gv := v1.SchemeGroupVersion
+	config.GroupVersion = &gv
+	config.APIPath = "/apis"
+	config.NegotiatedSerializer = scheme.Codecs.WithoutConversion()
+
+	if config.UserAgent == "" {
+		config.UserAgent = rest.DefaultKubernetesUserAgent()
+	}
+
+	return nil
+}
+
+// RESTClient returns a RESTClient that is used to communicate
+// with API server by this client implementation.
+func (c *GlooV1Client) RESTClient() rest.Interface {
+	if c == nil {
+		return nil
+	}
+	return c.restClient
+}
diff --git a/pkg/client/clientset/versioned/typed/gloo/v1/upstreamgroup.go b/pkg/client/clientset/versioned/typed/gloo/v1/upstreamgroup.go
new file mode 100644
index 00000000..06c254fa
--- /dev/null
+++ b/pkg/client/clientset/versioned/typed/gloo/v1/upstreamgroup.go
@@ -0,0 +1,174 @@
+/*
+Copyright The Flagger Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+// Code generated by client-gen. DO NOT EDIT.
+
+package v1
+
+import (
+	"time"
+
+	v1 "github.com/weaveworks/flagger/pkg/apis/gloo/v1"
+	scheme "github.com/weaveworks/flagger/pkg/client/clientset/versioned/scheme"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	types "k8s.io/apimachinery/pkg/types"
+	watch "k8s.io/apimachinery/pkg/watch"
+	rest "k8s.io/client-go/rest"
+)
+
+// UpstreamGroupsGetter has a method to return a UpstreamGroupInterface.
+// A group's client should implement this interface.
+type UpstreamGroupsGetter interface {
+	UpstreamGroups(namespace string) UpstreamGroupInterface
+}
+
+// UpstreamGroupInterface has methods to work with UpstreamGroup resources.
+type UpstreamGroupInterface interface {
+	Create(*v1.UpstreamGroup) (*v1.UpstreamGroup, error)
+	Update(*v1.UpstreamGroup) (*v1.UpstreamGroup, error)
+	Delete(name string, options *metav1.DeleteOptions) error
+	DeleteCollection(options *metav1.DeleteOptions, listOptions metav1.ListOptions) error
+	Get(name string, options metav1.GetOptions) (*v1.UpstreamGroup, error)
+	List(opts metav1.ListOptions) (*v1.UpstreamGroupList, error)
+	Watch(opts metav1.ListOptions) (watch.Interface, error)
+	Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1.UpstreamGroup, err error)
+	UpstreamGroupExpansion
+}
+
+// upstreamGroups implements UpstreamGroupInterface
+type upstreamGroups struct {
+	client rest.Interface
+	ns     string
+}
+
+// newUpstreamGroups returns a UpstreamGroups
+func newUpstreamGroups(c *GlooV1Client, namespace string) *upstreamGroups {
+	return &upstreamGroups{
+		client: c.RESTClient(),
+		ns:     namespace,
+	}
+}
+
+// Get takes name of the upstreamGroup, and returns the corresponding upstreamGroup object, and an error if there is any.
+func (c *upstreamGroups) Get(name string, options metav1.GetOptions) (result *v1.UpstreamGroup, err error) {
+	result = &v1.UpstreamGroup{}
+	err = c.client.Get().
+		Namespace(c.ns).
+		Resource("upstreamgroups").
+		Name(name).
+		VersionedParams(&options, scheme.ParameterCodec).
+		Do().
+		Into(result)
+	return
+}
+
+// List takes label and field selectors, and returns the list of UpstreamGroups that match those selectors.
+func (c *upstreamGroups) List(opts metav1.ListOptions) (result *v1.UpstreamGroupList, err error) {
+	var timeout time.Duration
+	if opts.TimeoutSeconds != nil {
+		timeout = time.Duration(*opts.TimeoutSeconds) * time.Second
+	}
+	result = &v1.UpstreamGroupList{}
+	err = c.client.Get().
+		Namespace(c.ns).
+		Resource("upstreamgroups").
+		VersionedParams(&opts, scheme.ParameterCodec).
+		Timeout(timeout).
+		Do().
+		Into(result)
+	return
+}
+
+// Watch returns a watch.Interface that watches the requested upstreamGroups.
+func (c *upstreamGroups) Watch(opts metav1.ListOptions) (watch.Interface, error) {
+	var timeout time.Duration
+	if opts.TimeoutSeconds != nil {
+		timeout = time.Duration(*opts.TimeoutSeconds) * time.Second
+	}
+	opts.Watch = true
+	return c.client.Get().
+		Namespace(c.ns).
+		Resource("upstreamgroups").
+		VersionedParams(&opts, scheme.ParameterCodec).
+		Timeout(timeout).
+		Watch()
+}
+
+// Create takes the representation of a upstreamGroup and creates it.  Returns the server's representation of the upstreamGroup, and an error, if there is any.
+func (c *upstreamGroups) Create(upstreamGroup *v1.UpstreamGroup) (result *v1.UpstreamGroup, err error) {
+	result = &v1.UpstreamGroup{}
+	err = c.client.Post().
+		Namespace(c.ns).
+		Resource("upstreamgroups").
+		Body(upstreamGroup).
+		Do().
+		Into(result)
+	return
+}
+
+// Update takes the representation of a upstreamGroup and updates it. Returns the server's representation of the upstreamGroup, and an error, if there is any.
+func (c *upstreamGroups) Update(upstreamGroup *v1.UpstreamGroup) (result *v1.UpstreamGroup, err error) {
+	result = &v1.UpstreamGroup{}
+	err = c.client.Put().
+		Namespace(c.ns).
+		Resource("upstreamgroups").
+		Name(upstreamGroup.Name).
+		Body(upstreamGroup).
+		Do().
+		Into(result)
+	return
+}
+
+// Delete takes name of the upstreamGroup and deletes it. Returns an error if one occurs.
+func (c *upstreamGroups) Delete(name string, options *metav1.DeleteOptions) error {
+	return c.client.Delete().
+		Namespace(c.ns).
+		Resource("upstreamgroups").
+		Name(name).
+		Body(options).
+		Do().
+		Error()
+}
+
+// DeleteCollection deletes a collection of objects.
+func (c *upstreamGroups) DeleteCollection(options *metav1.DeleteOptions, listOptions metav1.ListOptions) error {
+	var timeout time.Duration
+	if listOptions.TimeoutSeconds != nil {
+		timeout = time.Duration(*listOptions.TimeoutSeconds) * time.Second
+	}
+	return c.client.Delete().
+		Namespace(c.ns).
+		Resource("upstreamgroups").
+		VersionedParams(&listOptions, scheme.ParameterCodec).
+		Timeout(timeout).
+		Body(options).
+		Do().
+		Error()
+}
+
+// Patch applies the patch and returns the patched upstreamGroup.
+func (c *upstreamGroups) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1.UpstreamGroup, err error) {
+	result = &v1.UpstreamGroup{}
+	err = c.client.Patch(pt).
+		Namespace(c.ns).
+		Resource("upstreamgroups").
+		SubResource(subresources...).
+		Name(name).
+		Body(data).
+		Do().
+		Into(result)
+	return
+}
diff --git a/pkg/client/informers/externalversions/factory.go b/pkg/client/informers/externalversions/factory.go
index c19c5ca2..ee1d153d 100644
--- a/pkg/client/informers/externalversions/factory.go
+++ b/pkg/client/informers/externalversions/factory.go
@@ -26,6 +26,7 @@ import (
 	versioned "github.com/weaveworks/flagger/pkg/client/clientset/versioned"
 	appmesh "github.com/weaveworks/flagger/pkg/client/informers/externalversions/appmesh"
 	flagger "github.com/weaveworks/flagger/pkg/client/informers/externalversions/flagger"
+	gloo "github.com/weaveworks/flagger/pkg/client/informers/externalversions/gloo"
 	internalinterfaces "github.com/weaveworks/flagger/pkg/client/informers/externalversions/internalinterfaces"
 	istio "github.com/weaveworks/flagger/pkg/client/informers/externalversions/istio"
 	smi "github.com/weaveworks/flagger/pkg/client/informers/externalversions/smi"
@@ -177,6 +178,7 @@ type SharedInformerFactory interface {
 
 	Appmesh() appmesh.Interface
 	Flagger() flagger.Interface
+	Gloo() gloo.Interface
 	Networking() istio.Interface
 	Split() smi.Interface
 }
@@ -189,6 +191,10 @@ func (f *sharedInformerFactory) Flagger() flagger.Interface {
 	return flagger.New(f, f.namespace, f.tweakListOptions)
 }
 
+func (f *sharedInformerFactory) Gloo() gloo.Interface {
+	return gloo.New(f, f.namespace, f.tweakListOptions)
+}
+
 func (f *sharedInformerFactory) Networking() istio.Interface {
 	return istio.New(f, f.namespace, f.tweakListOptions)
 }
diff --git a/pkg/client/informers/externalversions/generic.go b/pkg/client/informers/externalversions/generic.go
index 31be5272..54a23b99 100644
--- a/pkg/client/informers/externalversions/generic.go
+++ b/pkg/client/informers/externalversions/generic.go
@@ -23,6 +23,7 @@ import (
 
 	v1beta1 "github.com/weaveworks/flagger/pkg/apis/appmesh/v1beta1"
 	v1alpha3 "github.com/weaveworks/flagger/pkg/apis/flagger/v1alpha3"
+	v1 "github.com/weaveworks/flagger/pkg/apis/gloo/v1"
 	istiov1alpha3 "github.com/weaveworks/flagger/pkg/apis/istio/v1alpha3"
 	v1alpha1 "github.com/weaveworks/flagger/pkg/apis/smi/v1alpha1"
 	schema "k8s.io/apimachinery/pkg/runtime/schema"
@@ -67,6 +68,10 @@ func (f *sharedInformerFactory) ForResource(resource schema.GroupVersionResource
 	case v1alpha3.SchemeGroupVersion.WithResource("canaries"):
 		return &genericInformer{resource: resource.GroupResource(), informer: f.Flagger().V1alpha3().Canaries().Informer()}, nil
 
+		// Group=gloo.solo.io, Version=v1
+	case v1.SchemeGroupVersion.WithResource("upstreamgroups"):
+		return &genericInformer{resource: resource.GroupResource(), informer: f.Gloo().V1().UpstreamGroups().Informer()}, nil
+
 		// Group=networking.istio.io, Version=v1alpha3
 	case istiov1alpha3.SchemeGroupVersion.WithResource("destinationrules"):
 		return &genericInformer{resource: resource.GroupResource(), informer: f.Networking().V1alpha3().DestinationRules().Informer()}, nil
diff --git a/pkg/client/informers/externalversions/gloo/interface.go b/pkg/client/informers/externalversions/gloo/interface.go
new file mode 100644
index 00000000..7f06b3cf
--- /dev/null
+++ b/pkg/client/informers/externalversions/gloo/interface.go
@@ -0,0 +1,46 @@
+/*
+Copyright The Flagger Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+// Code generated by informer-gen. DO NOT EDIT.
+
+package gloo
+
+import (
+	v1 "github.com/weaveworks/flagger/pkg/client/informers/externalversions/gloo/v1"
+	internalinterfaces "github.com/weaveworks/flagger/pkg/client/informers/externalversions/internalinterfaces"
+)
+
+// Interface provides access to each of this group's versions.
+type Interface interface {
+	// V1 provides access to shared informers for resources in V1.
+	V1() v1.Interface
+}
+
+type group struct {
+	factory          internalinterfaces.SharedInformerFactory
+	namespace        string
+	tweakListOptions internalinterfaces.TweakListOptionsFunc
+}
+
+// New returns a new Interface.
+func New(f internalinterfaces.SharedInformerFactory, namespace string, tweakListOptions internalinterfaces.TweakListOptionsFunc) Interface {
+	return &group{factory: f, namespace: namespace, tweakListOptions: tweakListOptions}
+}
+
+// V1 returns a new v1.Interface.
+func (g *group) V1() v1.Interface {
+	return v1.New(g.factory, g.namespace, g.tweakListOptions)
+}
diff --git a/pkg/client/informers/externalversions/gloo/v1/interface.go b/pkg/client/informers/externalversions/gloo/v1/interface.go
new file mode 100644
index 00000000..b86cea53
--- /dev/null
+++ b/pkg/client/informers/externalversions/gloo/v1/interface.go
@@ -0,0 +1,45 @@
+/*
+Copyright The Flagger Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+// Code generated by informer-gen. DO NOT EDIT.
+
+package v1
+
+import (
+	internalinterfaces "github.com/weaveworks/flagger/pkg/client/informers/externalversions/internalinterfaces"
+)
+
+// Interface provides access to all the informers in this group version.
+type Interface interface {
+	// UpstreamGroups returns a UpstreamGroupInformer.
+	UpstreamGroups() UpstreamGroupInformer
+}
+
+type version struct {
+	factory          internalinterfaces.SharedInformerFactory
+	namespace        string
+	tweakListOptions internalinterfaces.TweakListOptionsFunc
+}
+
+// New returns a new Interface.
+func New(f internalinterfaces.SharedInformerFactory, namespace string, tweakListOptions internalinterfaces.TweakListOptionsFunc) Interface {
+	return &version{factory: f, namespace: namespace, tweakListOptions: tweakListOptions}
+}
+
+// UpstreamGroups returns a UpstreamGroupInformer.
+func (v *version) UpstreamGroups() UpstreamGroupInformer {
+	return &upstreamGroupInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions}
+}
diff --git a/pkg/client/informers/externalversions/gloo/v1/upstreamgroup.go b/pkg/client/informers/externalversions/gloo/v1/upstreamgroup.go
new file mode 100644
index 00000000..8ec9b8f4
--- /dev/null
+++ b/pkg/client/informers/externalversions/gloo/v1/upstreamgroup.go
@@ -0,0 +1,89 @@
+/*
+Copyright The Flagger Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+// Code generated by informer-gen. DO NOT EDIT.
+
+package v1
+
+import (
+	time "time"
+
+	gloov1 "github.com/weaveworks/flagger/pkg/apis/gloo/v1"
+	versioned "github.com/weaveworks/flagger/pkg/client/clientset/versioned"
+	internalinterfaces "github.com/weaveworks/flagger/pkg/client/informers/externalversions/internalinterfaces"
+	v1 "github.com/weaveworks/flagger/pkg/client/listers/gloo/v1"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	runtime "k8s.io/apimachinery/pkg/runtime"
+	watch "k8s.io/apimachinery/pkg/watch"
+	cache "k8s.io/client-go/tools/cache"
+)
+
+// UpstreamGroupInformer provides access to a shared informer and lister for
+// UpstreamGroups.
+type UpstreamGroupInformer interface {
+	Informer() cache.SharedIndexInformer
+	Lister() v1.UpstreamGroupLister
+}
+
+type upstreamGroupInformer struct {
+	factory          internalinterfaces.SharedInformerFactory
+	tweakListOptions internalinterfaces.TweakListOptionsFunc
+	namespace        string
+}
+
+// NewUpstreamGroupInformer constructs a new informer for UpstreamGroup type.
+// Always prefer using an informer factory to get a shared informer instead of getting an independent
+// one. This reduces memory footprint and number of connections to the server.
+func NewUpstreamGroupInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer {
+	return NewFilteredUpstreamGroupInformer(client, namespace, resyncPeriod, indexers, nil)
+}
+
+// NewFilteredUpstreamGroupInformer constructs a new informer for UpstreamGroup type.
+// Always prefer using an informer factory to get a shared informer instead of getting an independent
+// one. This reduces memory footprint and number of connections to the server.
+func NewFilteredUpstreamGroupInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer {
+	return cache.NewSharedIndexInformer(
+		&cache.ListWatch{
+			ListFunc: func(options metav1.ListOptions) (runtime.Object, error) {
+				if tweakListOptions != nil {
+					tweakListOptions(&options)
+				}
+				return client.GlooV1().UpstreamGroups(namespace).List(options)
+			},
+			WatchFunc: func(options metav1.ListOptions) (watch.Interface, error) {
+				if tweakListOptions != nil {
+					tweakListOptions(&options)
+				}
+				return client.GlooV1().UpstreamGroups(namespace).Watch(options)
+			},
+		},
+		&gloov1.UpstreamGroup{},
+		resyncPeriod,
+		indexers,
+	)
+}
+
+func (f *upstreamGroupInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer {
+	return NewFilteredUpstreamGroupInformer(client, f.namespace, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions)
+}
+
+func (f *upstreamGroupInformer) Informer() cache.SharedIndexInformer {
+	return f.factory.InformerFor(&gloov1.UpstreamGroup{}, f.defaultInformer)
+}
+
+func (f *upstreamGroupInformer) Lister() v1.UpstreamGroupLister {
+	return v1.NewUpstreamGroupLister(f.Informer().GetIndexer())
+}
diff --git a/pkg/client/listers/gloo/v1/expansion_generated.go b/pkg/client/listers/gloo/v1/expansion_generated.go
new file mode 100644
index 00000000..aee9e464
--- /dev/null
+++ b/pkg/client/listers/gloo/v1/expansion_generated.go
@@ -0,0 +1,27 @@
+/*
+Copyright The Flagger Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+// Code generated by lister-gen. DO NOT EDIT.
+
+package v1
+
+// UpstreamGroupListerExpansion allows custom methods to be added to
+// UpstreamGroupLister.
+type UpstreamGroupListerExpansion interface{}
+
+// UpstreamGroupNamespaceListerExpansion allows custom methods to be added to
+// UpstreamGroupNamespaceLister.
+type UpstreamGroupNamespaceListerExpansion interface{}
diff --git a/pkg/client/listers/gloo/v1/upstreamgroup.go b/pkg/client/listers/gloo/v1/upstreamgroup.go
new file mode 100644
index 00000000..52b8be19
--- /dev/null
+++ b/pkg/client/listers/gloo/v1/upstreamgroup.go
@@ -0,0 +1,94 @@
+/*
+Copyright The Flagger Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+// Code generated by lister-gen. DO NOT EDIT.
+
+package v1
+
+import (
+	v1 "github.com/weaveworks/flagger/pkg/apis/gloo/v1"
+	"k8s.io/apimachinery/pkg/api/errors"
+	"k8s.io/apimachinery/pkg/labels"
+	"k8s.io/client-go/tools/cache"
+)
+
+// UpstreamGroupLister helps list UpstreamGroups.
+type UpstreamGroupLister interface {
+	// List lists all UpstreamGroups in the indexer.
+	List(selector labels.Selector) (ret []*v1.UpstreamGroup, err error)
+	// UpstreamGroups returns an object that can list and get UpstreamGroups.
+	UpstreamGroups(namespace string) UpstreamGroupNamespaceLister
+	UpstreamGroupListerExpansion
+}
+
+// upstreamGroupLister implements the UpstreamGroupLister interface.
+type upstreamGroupLister struct {
+	indexer cache.Indexer
+}
+
+// NewUpstreamGroupLister returns a new UpstreamGroupLister.
+func NewUpstreamGroupLister(indexer cache.Indexer) UpstreamGroupLister {
+	return &upstreamGroupLister{indexer: indexer}
+}
+
+// List lists all UpstreamGroups in the indexer.
+func (s *upstreamGroupLister) List(selector labels.Selector) (ret []*v1.UpstreamGroup, err error) {
+	err = cache.ListAll(s.indexer, selector, func(m interface{}) {
+		ret = append(ret, m.(*v1.UpstreamGroup))
+	})
+	return ret, err
+}
+
+// UpstreamGroups returns an object that can list and get UpstreamGroups.
+func (s *upstreamGroupLister) UpstreamGroups(namespace string) UpstreamGroupNamespaceLister {
+	return upstreamGroupNamespaceLister{indexer: s.indexer, namespace: namespace}
+}
+
+// UpstreamGroupNamespaceLister helps list and get UpstreamGroups.
+type UpstreamGroupNamespaceLister interface {
+	// List lists all UpstreamGroups in the indexer for a given namespace.
+	List(selector labels.Selector) (ret []*v1.UpstreamGroup, err error)
+	// Get retrieves the UpstreamGroup from the indexer for a given namespace and name.
+	Get(name string) (*v1.UpstreamGroup, error)
+	UpstreamGroupNamespaceListerExpansion
+}
+
+// upstreamGroupNamespaceLister implements the UpstreamGroupNamespaceLister
+// interface.
+type upstreamGroupNamespaceLister struct {
+	indexer   cache.Indexer
+	namespace string
+}
+
+// List lists all UpstreamGroups in the indexer for a given namespace.
+func (s upstreamGroupNamespaceLister) List(selector labels.Selector) (ret []*v1.UpstreamGroup, err error) {
+	err = cache.ListAllByNamespace(s.indexer, s.namespace, selector, func(m interface{}) {
+		ret = append(ret, m.(*v1.UpstreamGroup))
+	})
+	return ret, err
+}
+
+// Get retrieves the UpstreamGroup from the indexer for a given namespace and name.
+func (s upstreamGroupNamespaceLister) Get(name string) (*v1.UpstreamGroup, error) {
+	obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name)
+	if err != nil {
+		return nil, err
+	}
+	if !exists {
+		return nil, errors.NewNotFound(v1.Resource("upstreamgroup"), name)
+	}
+	return obj.(*v1.UpstreamGroup), nil
+}
diff --git a/pkg/router/factory.go b/pkg/router/factory.go
index 80ab2471..67ba1b12 100644
--- a/pkg/router/factory.go
+++ b/pkg/router/factory.go
@@ -1,7 +1,6 @@
 package router
 
 import (
-	"context"
 	"strings"
 
 	"go.uber.org/zap"
@@ -84,18 +83,40 @@ func (factory *Factory) MeshRouter(provider string) Interface {
 			smiClient:     factory.meshClient,
 			targetMesh:    "linkerd",
 		}
-	case strings.HasPrefix(provider, "supergloo"):
-		supergloo, err := NewSuperglooRouter(context.TODO(), provider, factory.flaggerClient, factory.logger, factory.kubeConfig)
-		if err != nil {
-			panic("failed creating supergloo client")
-		}
-		return supergloo
 	case strings.HasPrefix(provider, "gloo"):
-		gloo, err := NewGlooRouter(context.TODO(), provider, factory.flaggerClient, factory.logger, factory.kubeConfig)
-		if err != nil {
-			panic("failed creating gloo client")
+		upstreamDiscoveryNs := "gloo-system"
+		if strings.HasPrefix(provider, "gloo:") {
+			upstreamDiscoveryNs = strings.TrimPrefix(provider, "gloo:")
+		}
+		return &GlooRouter{
+			logger:              factory.logger,
+			flaggerClient:       factory.flaggerClient,
+			kubeClient:          factory.kubeClient,
+			glooClient:          factory.meshClient,
+			upstreamDiscoveryNs: upstreamDiscoveryNs,
+		}
+	case strings.HasPrefix(provider, "supergloo:appmesh"):
+		return &AppMeshRouter{
+			logger:        factory.logger,
+			flaggerClient: factory.flaggerClient,
+			kubeClient:    factory.kubeClient,
+			appmeshClient: factory.meshClient,
+		}
+	case strings.HasPrefix(provider, "supergloo:istio"):
+		return &IstioRouter{
+			logger:        factory.logger,
+			flaggerClient: factory.flaggerClient,
+			kubeClient:    factory.kubeClient,
+			istioClient:   factory.meshClient,
+		}
+	case strings.HasPrefix(provider, "supergloo:linkerd"):
+		return &SmiRouter{
+			logger:        factory.logger,
+			flaggerClient: factory.flaggerClient,
+			kubeClient:    factory.kubeClient,
+			smiClient:     factory.meshClient,
+			targetMesh:    "linkerd",
 		}
-		return gloo
 	default:
 		return &IstioRouter{
 			logger:        factory.logger,
diff --git a/pkg/router/gloo.go b/pkg/router/gloo.go
index 3c2b1f9c..ceae8c0f 100644
--- a/pkg/router/gloo.go
+++ b/pkg/router/gloo.go
@@ -1,21 +1,16 @@
 package router
 
 import (
-	"context"
 	"fmt"
-	"strings"
-
-	gloov1 "github.com/solo-io/gloo/projects/gloo/pkg/api/v1"
-	solokitclients "github.com/solo-io/solo-kit/pkg/api/v1/clients"
-	"github.com/solo-io/solo-kit/pkg/api/v1/clients/factory"
-	"github.com/solo-io/solo-kit/pkg/api/v1/clients/kube"
-	crdv1 "github.com/solo-io/solo-kit/pkg/api/v1/clients/kube/crd/solo.io/v1"
-	solokitcore "github.com/solo-io/solo-kit/pkg/api/v1/resources/core"
-	solokiterror "github.com/solo-io/solo-kit/pkg/errors"
+
+	"github.com/google/go-cmp/cmp"
+	"github.com/google/go-cmp/cmp/cmpopts"
+	gloov1 "github.com/weaveworks/flagger/pkg/apis/gloo/v1"
 	"go.uber.org/zap"
+	"k8s.io/apimachinery/pkg/api/errors"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/apimachinery/pkg/runtime/schema"
-	"k8s.io/client-go/rest"
+	"k8s.io/client-go/kubernetes"
 
 	flaggerv1 "github.com/weaveworks/flagger/pkg/apis/flagger/v1alpha3"
 	clientset "github.com/weaveworks/flagger/pkg/client/clientset/versioned"
@@ -23,54 +18,92 @@ import (
 
 // GlooRouter is managing Istio virtual services
 type GlooRouter struct {
-	ugClient            gloov1.UpstreamGroupClient
+	kubeClient          kubernetes.Interface
+	glooClient          clientset.Interface
+	flaggerClient       clientset.Interface
 	logger              *zap.SugaredLogger
 	upstreamDiscoveryNs string
 }
 
-func NewGlooRouter(ctx context.Context, provider string, flaggerClient clientset.Interface, logger *zap.SugaredLogger, cfg *rest.Config) (*GlooRouter, error) {
-	// TODO if cfg is nil use memory client instead?
-	sharedCache := kube.NewKubeCache(ctx)
-	upstreamGroupClient, err := gloov1.NewUpstreamGroupClient(&factory.KubeResourceClientFactory{
-		Crd:             gloov1.UpstreamGroupCrd,
-		Cfg:             cfg,
-		SharedCache:     sharedCache,
-		SkipCrdCreation: true,
-	})
-	if err != nil {
-		// this should never happen.
-		return nil, fmt.Errorf("creating UpstreamGroup client %v", err)
-	}
-	if err := upstreamGroupClient.Register(); err != nil {
-		return nil, err
-	}
-	upstreamDiscoveryNs := ""
-	if strings.HasPrefix(provider, "gloo:") {
-		upstreamDiscoveryNs = strings.TrimPrefix(provider, "gloo:")
+// Reconcile creates or updates the Istio virtual service
+func (gr *GlooRouter) Reconcile(canary *flaggerv1.Canary) error {
+	targetName := canary.Spec.TargetRef.Name
+	canaryName := fmt.Sprintf("%s-%s-canary-%v", canary.Namespace, canary.Spec.TargetRef.Name, canary.Spec.Service.Port)
+	primaryName := fmt.Sprintf("%s-%s-primary-%v", canary.Namespace, canary.Spec.TargetRef.Name, canary.Spec.Service.Port)
+
+	newSpec := gloov1.UpstreamGroupSpec{
+		Destinations: []gloov1.WeightedDestination{
+			{
+				Destination: gloov1.Destination{
+					Upstream: gloov1.ResourceRef{
+						Name:      primaryName,
+						Namespace: gr.upstreamDiscoveryNs,
+					},
+				},
+				Weight: 100,
+			},
+			{
+				Destination: gloov1.Destination{
+					Upstream: gloov1.ResourceRef{
+						Name:      canaryName,
+						Namespace: gr.upstreamDiscoveryNs,
+					},
+				},
+				Weight: 0,
+			},
+		},
 	}
 
-	return NewGlooRouterWithClient(ctx, upstreamGroupClient, upstreamDiscoveryNs, logger), nil
-}
+	upstreamGroup, err := gr.glooClient.GlooV1().UpstreamGroups(canary.Namespace).Get(targetName, metav1.GetOptions{})
+	if errors.IsNotFound(err) {
+		upstreamGroup = &gloov1.UpstreamGroup{
+			ObjectMeta: metav1.ObjectMeta{
+				Name:      targetName,
+				Namespace: canary.Namespace,
+				OwnerReferences: []metav1.OwnerReference{
+					*metav1.NewControllerRef(canary, schema.GroupVersionKind{
+						Group:   flaggerv1.SchemeGroupVersion.Group,
+						Version: flaggerv1.SchemeGroupVersion.Version,
+						Kind:    flaggerv1.CanaryKind,
+					}),
+				},
+			},
+			Spec: newSpec,
+		}
 
-func NewGlooRouterWithClient(ctx context.Context, routingRuleClient gloov1.UpstreamGroupClient, upstreamDiscoveryNs string, logger *zap.SugaredLogger) *GlooRouter {
+		_, err = gr.glooClient.GlooV1().UpstreamGroups(canary.Namespace).Create(upstreamGroup)
+		if err != nil {
+			return fmt.Errorf("UpstreamGroup %s.%s create error %v", targetName, canary.Namespace, err)
+		}
+		gr.logger.With("canary", fmt.Sprintf("%s.%s", canary.Name, canary.Namespace)).
+			Infof("UpstreamGroup %s.%s created", upstreamGroup.GetName(), canary.Namespace)
+		return nil
+	}
 
-	if upstreamDiscoveryNs == "" {
-		upstreamDiscoveryNs = "gloo-system"
+	if err != nil {
+		return fmt.Errorf("UpstreamGroup %s.%s query error %v", targetName, canary.Namespace, err)
 	}
-	return &GlooRouter{ugClient: routingRuleClient, logger: logger, upstreamDiscoveryNs: upstreamDiscoveryNs}
-}
 
-// Reconcile creates or updates the Istio virtual service
-func (gr *GlooRouter) Reconcile(canary *flaggerv1.Canary) error {
-	// do we have routes already?
-	if _, _, _, err := gr.GetRoutes(canary); err == nil {
-		// we have routes, no need to do anything else
-		return nil
-	} else if solokiterror.IsNotExist(err) {
-		return gr.SetRoutes(canary, 100, 0, false)
-	} else {
-		return err
+	// update upstreamGroup but keep the original destination weights
+	if upstreamGroup != nil {
+		if diff := cmp.Diff(
+			newSpec,
+			upstreamGroup.Spec,
+			cmpopts.IgnoreFields(gloov1.WeightedDestination{}, "Weight"),
+		); diff != "" {
+			clone := upstreamGroup.DeepCopy()
+			clone.Spec = newSpec
+
+			_, err = gr.glooClient.GlooV1().UpstreamGroups(canary.Namespace).Update(clone)
+			if err != nil {
+				return fmt.Errorf("UpstreamGroup %s.%s update error %v", targetName, canary.Namespace, err)
+			}
+			gr.logger.With("canary", fmt.Sprintf("%s.%s", canary.Name, canary.Namespace)).
+				Infof("UpstreamGroup %s.%s updated", upstreamGroup.GetName(), canary.Namespace)
+		}
 	}
+
+	return nil
 }
 
 // GetRoutes returns the destinations weight for primary and canary
@@ -81,29 +114,31 @@ func (gr *GlooRouter) GetRoutes(canary *flaggerv1.Canary) (
 	err error,
 ) {
 	targetName := canary.Spec.TargetRef.Name
-	var ug *gloov1.UpstreamGroup
-	ug, err = gr.ugClient.Read(canary.Namespace, targetName, solokitclients.ReadOpts{})
+	primaryName := fmt.Sprintf("%s-%s-primary-%v", canary.Namespace, canary.Spec.TargetRef.Name, canary.Spec.Service.Port)
+
+	upstreamGroup, err := gr.glooClient.GlooV1().UpstreamGroups(canary.Namespace).Get(targetName, metav1.GetOptions{})
 	if err != nil {
+		if errors.IsNotFound(err) {
+			err = fmt.Errorf("UpstreamGroup %s.%s not found", targetName, canary.Namespace)
+			return
+		}
+		err = fmt.Errorf("UpstreamGroup %s.%s query error %v", targetName, canary.Namespace, err)
 		return
 	}
 
-	dests := ug.GetDestinations()
-	for _, dest := range dests {
-		if dest.GetDestination().GetUpstream().Name == upstreamName(canary.Namespace, fmt.Sprintf("%s-primary", targetName), canary.Spec.Service.Port) {
-			primaryWeight = int(dest.Weight)
-		}
-		if dest.GetDestination().GetUpstream().Name == upstreamName(canary.Namespace, fmt.Sprintf("%s-canary", targetName), canary.Spec.Service.Port) {
-			canaryWeight = int(dest.Weight)
-		}
+	if len(upstreamGroup.Spec.Destinations) < 2 {
+		err = fmt.Errorf("UpstreamGroup %s.%s destinations not found", targetName, canary.Namespace)
+		return
 	}
 
-	if primaryWeight == 0 && canaryWeight == 0 {
-		err = fmt.Errorf("RoutingRule %s.%s does not contain routes for %s-primary and %s-canary",
-			targetName, canary.Namespace, targetName, targetName)
+	for _, dst := range upstreamGroup.Spec.Destinations {
+		if dst.Destination.Upstream.Name == primaryName {
+			primaryWeight = int(dst.Weight)
+			canaryWeight = 100 - primaryWeight
+			return
+		}
 	}
 
-	mirrored = false
-
 	return
 }
 
@@ -115,77 +150,48 @@ func (gr *GlooRouter) SetRoutes(
 	mirrored bool,
 ) error {
 	targetName := canary.Spec.TargetRef.Name
+	canaryName := fmt.Sprintf("%s-%s-canary-%v", canary.Namespace, canary.Spec.TargetRef.Name, canary.Spec.Service.Port)
+	primaryName := fmt.Sprintf("%s-%s-primary-%v", canary.Namespace, canary.Spec.TargetRef.Name, canary.Spec.Service.Port)
 
 	if primaryWeight == 0 && canaryWeight == 0 {
 		return fmt.Errorf("RoutingRule %s.%s update failed: no valid weights", targetName, canary.Namespace)
 	}
 
-	destinations := []*gloov1.WeightedDestination{}
-	destinations = append(destinations, &gloov1.WeightedDestination{
-		Destination: &gloov1.Destination{
-			Upstream: solokitcore.ResourceRef{
-				Name:      upstreamName(canary.Namespace, fmt.Sprintf("%s-primary", targetName), canary.Spec.Service.Port),
-				Namespace: gr.upstreamDiscoveryNs,
-			},
-		},
-		Weight: uint32(primaryWeight),
-	})
-
-	destinations = append(destinations, &gloov1.WeightedDestination{
-		Destination: &gloov1.Destination{
-			Upstream: solokitcore.ResourceRef{
-				Name:      upstreamName(canary.Namespace, fmt.Sprintf("%s-canary", targetName), canary.Spec.Service.Port),
-				Namespace: gr.upstreamDiscoveryNs,
-			},
-		},
-		Weight: uint32(canaryWeight),
-	})
-
-	upstreamGroup := &gloov1.UpstreamGroup{
-		Metadata: solokitcore.Metadata{
-			Name:      canary.Spec.TargetRef.Name,
-			Namespace: canary.Namespace,
-		},
-		Destinations: destinations,
-	}
-
-	return gr.writeUpstreamGroupRuleForCanary(canary, upstreamGroup)
-}
-
-func (gr *GlooRouter) writeUpstreamGroupRuleForCanary(canary *flaggerv1.Canary, ug *gloov1.UpstreamGroup) error {
-	targetName := canary.Spec.TargetRef.Name
+	upstreamGroup, err := gr.glooClient.GlooV1().UpstreamGroups(canary.Namespace).Get(targetName, metav1.GetOptions{})
+	if err != nil {
+		if errors.IsNotFound(err) {
+			return fmt.Errorf("UpstreamGroup %s.%s not found", targetName, canary.Namespace)
 
-	if oldUg, err := gr.ugClient.Read(ug.Metadata.Namespace, ug.Metadata.Name, solokitclients.ReadOpts{}); err != nil {
-		if solokiterror.IsNotExist(err) {
-			gr.logger.With("canary", fmt.Sprintf("%s.%s", canary.Name, canary.Namespace)).
-				Infof("UpstreamGroup %s created", ug.Metadata.Name)
-		} else {
-			return fmt.Errorf("RoutingRule %s.%s read failed: %v", targetName, canary.Namespace, err)
-		}
-	} else {
-		ug.Metadata.ResourceVersion = oldUg.Metadata.ResourceVersion
-		// if the old and the new one are equal, no need to do anything.
-		oldUg.Status = solokitcore.Status{}
-		if oldUg.Equal(ug) {
-			return nil
 		}
+		return fmt.Errorf("UpstreamGroup %s.%s query error %v", targetName, canary.Namespace, err)
 	}
 
-	kubeWriteOpts := &kube.KubeWriteOpts{
-		PreWriteCallback: func(r *crdv1.Resource) {
-			r.ObjectMeta.OwnerReferences = []metav1.OwnerReference{
-				*metav1.NewControllerRef(canary, schema.GroupVersionKind{
-					Group:   flaggerv1.SchemeGroupVersion.Group,
-					Version: flaggerv1.SchemeGroupVersion.Version,
-					Kind:    flaggerv1.CanaryKind,
-				}),
-			}
+	upstreamGroup.Spec = gloov1.UpstreamGroupSpec{
+		Destinations: []gloov1.WeightedDestination{
+			{
+				Destination: gloov1.Destination{
+					Upstream: gloov1.ResourceRef{
+						Name:      primaryName,
+						Namespace: gr.upstreamDiscoveryNs,
+					},
+				},
+				Weight: uint32(primaryWeight),
+			},
+			{
+				Destination: gloov1.Destination{
+					Upstream: gloov1.ResourceRef{
+						Name:      canaryName,
+						Namespace: gr.upstreamDiscoveryNs,
+					},
+				},
+				Weight: uint32(canaryWeight),
+			},
 		},
 	}
-	writeOpts := solokitclients.WriteOpts{OverwriteExisting: true, StorageWriteOpts: kubeWriteOpts}
-	_, err := gr.ugClient.Write(ug, writeOpts)
+
+	_, err = gr.glooClient.GlooV1().UpstreamGroups(canary.Namespace).Update(upstreamGroup)
 	if err != nil {
-		return fmt.Errorf("UpstreamGroup %s.%s update failed: %v", targetName, canary.Namespace, err)
+		return fmt.Errorf("UpstreamGroup %s.%s update error %v", targetName, canary.Namespace, err)
 	}
 	return nil
 }
diff --git a/pkg/router/supergloo.go b/pkg/router/supergloo.go
deleted file mode 100644
index a5c2344e..00000000
--- a/pkg/router/supergloo.go
+++ /dev/null
@@ -1,349 +0,0 @@
-package router
-
-import (
-	"context"
-	"fmt"
-	"strings"
-	"time"
-
-	solokitclients "github.com/solo-io/solo-kit/pkg/api/v1/clients"
-	"github.com/solo-io/solo-kit/pkg/api/v1/clients/factory"
-	"github.com/solo-io/solo-kit/pkg/api/v1/clients/kube"
-	crdv1 "github.com/solo-io/solo-kit/pkg/api/v1/clients/kube/crd/solo.io/v1"
-	solokitcore "github.com/solo-io/solo-kit/pkg/api/v1/resources/core"
-	solokiterror "github.com/solo-io/solo-kit/pkg/errors"
-
-	"github.com/gogo/protobuf/types"
-	gloov1 "github.com/solo-io/gloo/projects/gloo/pkg/api/v1"
-	supergloov1alpha3 "github.com/solo-io/supergloo/pkg/api/external/istio/networking/v1alpha3"
-	supergloov1 "github.com/solo-io/supergloo/pkg/api/v1"
-	"go.uber.org/zap"
-	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
-	"k8s.io/apimachinery/pkg/runtime/schema"
-	"k8s.io/client-go/rest"
-
-	flaggerv1 "github.com/weaveworks/flagger/pkg/apis/flagger/v1alpha3"
-	istiov1alpha3 "github.com/weaveworks/flagger/pkg/apis/istio/v1alpha3"
-	clientset "github.com/weaveworks/flagger/pkg/client/clientset/versioned"
-)
-
-// SuperglooRouter is managing Istio virtual services
-type SuperglooRouter struct {
-	rrClient   supergloov1.RoutingRuleClient
-	logger     *zap.SugaredLogger
-	targetMesh solokitcore.ResourceRef
-}
-
-func NewSuperglooRouter(ctx context.Context, provider string, flaggerClient clientset.Interface, logger *zap.SugaredLogger, cfg *rest.Config) (*SuperglooRouter, error) {
-	// TODO if cfg is nil use memory client instead?
-	sharedCache := kube.NewKubeCache(ctx)
-	routingRuleClient, err := supergloov1.NewRoutingRuleClient(&factory.KubeResourceClientFactory{
-		Crd:             supergloov1.RoutingRuleCrd,
-		Cfg:             cfg,
-		SharedCache:     sharedCache,
-		SkipCrdCreation: true,
-	})
-	if err != nil {
-		// this should never happen.
-		return nil, fmt.Errorf("creating RoutingRule client %v", err)
-	}
-	if err := routingRuleClient.Register(); err != nil {
-		return nil, err
-	}
-
-	// remove the supergloo: prefix
-	provider = strings.TrimPrefix(provider, "supergloo:")
-	// split name.namespace:
-	parts := strings.Split(provider, ".")
-	if len(parts) != 2 {
-		return nil, fmt.Errorf("invalid format for supergloo provider")
-	}
-	targetMesh := solokitcore.ResourceRef{
-		Namespace: parts[1],
-		Name:      parts[0],
-	}
-	return NewSuperglooRouterWithClient(ctx, routingRuleClient, targetMesh, logger), nil
-}
-
-func NewSuperglooRouterWithClient(ctx context.Context, routingRuleClient supergloov1.RoutingRuleClient, targetMesh solokitcore.ResourceRef, logger *zap.SugaredLogger) *SuperglooRouter {
-	return &SuperglooRouter{rrClient: routingRuleClient, logger: logger, targetMesh: targetMesh}
-}
-
-// Reconcile creates or updates the Istio virtual service
-func (sr *SuperglooRouter) Reconcile(canary *flaggerv1.Canary) error {
-
-	if err := sr.setRetries(canary); err != nil {
-		return err
-	}
-	if err := sr.setHeaders(canary); err != nil {
-		return err
-	}
-	if err := sr.setCors(canary); err != nil {
-		return err
-	}
-
-	// do we have routes already?
-	if _, _, _, err := sr.GetRoutes(canary); err == nil {
-		// we have routes, no need to do anything else
-		return nil
-	} else if solokiterror.IsNotExist(err) {
-		return sr.SetRoutes(canary, 100, 0, false)
-	} else {
-		return err
-	}
-}
-
-func (sr *SuperglooRouter) setRetries(canary *flaggerv1.Canary) error {
-	if canary.Spec.Service.Retries == nil {
-		return nil
-	}
-	retries, err := convertRetries(canary.Spec.Service.Retries)
-	if err != nil {
-		return err
-	}
-	rule := sr.createRule(canary, "retries", &supergloov1.RoutingRuleSpec{
-		RuleType: &supergloov1.RoutingRuleSpec_Retries{
-			Retries: retries,
-		},
-	})
-
-	return sr.writeRuleForCanary(canary, rule)
-}
-func (sr *SuperglooRouter) setHeaders(canary *flaggerv1.Canary) error {
-	if canary.Spec.Service.Headers == nil {
-		return nil
-	}
-	headerManipulation, err := convertHeaders(canary.Spec.Service.Headers)
-	if err != nil {
-		return err
-	}
-	if headerManipulation == nil {
-		return nil
-	}
-	rule := sr.createRule(canary, "headers", &supergloov1.RoutingRuleSpec{
-		RuleType: &supergloov1.RoutingRuleSpec_HeaderManipulation{
-			HeaderManipulation: headerManipulation,
-		},
-	})
-
-	return sr.writeRuleForCanary(canary, rule)
-}
-
-func convertHeaders(headers *istiov1alpha3.Headers) (*supergloov1.HeaderManipulation, error) {
-	var headersMaipulation *supergloov1.HeaderManipulation
-
-	if headers.Request != nil {
-		headersMaipulation = &supergloov1.HeaderManipulation{}
-
-		headersMaipulation.RemoveRequestHeaders = headers.Request.Remove
-		headersMaipulation.AppendRequestHeaders = make(map[string]string)
-		for k, v := range headers.Request.Add {
-			headersMaipulation.AppendRequestHeaders[k] = v
-		}
-	}
-	if headers.Response != nil {
-		if headersMaipulation == nil {
-			headersMaipulation = &supergloov1.HeaderManipulation{}
-		}
-
-		headersMaipulation.RemoveResponseHeaders = headers.Response.Remove
-		headersMaipulation.AppendResponseHeaders = make(map[string]string)
-		for k, v := range headers.Response.Add {
-			headersMaipulation.AppendResponseHeaders[k] = v
-		}
-	}
-
-	return headersMaipulation, nil
-}
-
-func convertRetries(retries *istiov1alpha3.HTTPRetry) (*supergloov1.RetryPolicy, error) {
-	perTryTimeout, err := time.ParseDuration(retries.PerTryTimeout)
-	return &supergloov1.RetryPolicy{
-		MaxRetries: &supergloov1alpha3.HTTPRetry{
-			Attempts:      int32(retries.Attempts),
-			PerTryTimeout: types.DurationProto(perTryTimeout),
-			RetryOn:       retries.RetryOn,
-		},
-	}, err
-}
-
-func (sr *SuperglooRouter) setCors(canary *flaggerv1.Canary) error {
-	corsPolicy := canary.Spec.Service.CorsPolicy
-	if corsPolicy == nil {
-		return nil
-	}
-	var maxAgeDuration *types.Duration
-	if maxAge, err := time.ParseDuration(corsPolicy.MaxAge); err == nil {
-		maxAgeDuration = types.DurationProto(maxAge)
-	}
-
-	rule := sr.createRule(canary, "cors", &supergloov1.RoutingRuleSpec{
-		RuleType: &supergloov1.RoutingRuleSpec_CorsPolicy{
-			CorsPolicy: &supergloov1alpha3.CorsPolicy{
-				AllowOrigin:      corsPolicy.AllowOrigin,
-				AllowMethods:     corsPolicy.AllowMethods,
-				AllowHeaders:     corsPolicy.AllowHeaders,
-				ExposeHeaders:    corsPolicy.ExposeHeaders,
-				MaxAge:           maxAgeDuration,
-				AllowCredentials: &types.BoolValue{Value: corsPolicy.AllowCredentials},
-			},
-		},
-	})
-	return sr.writeRuleForCanary(canary, rule)
-}
-
-func (sr *SuperglooRouter) createRule(canary *flaggerv1.Canary, namesuffix string, spec *supergloov1.RoutingRuleSpec) *supergloov1.RoutingRule {
-	if namesuffix != "" {
-		namesuffix = "-" + namesuffix
-	}
-	return &supergloov1.RoutingRule{
-		Metadata: solokitcore.Metadata{
-			Name:      canary.Spec.TargetRef.Name + namesuffix,
-			Namespace: canary.Namespace,
-		},
-		TargetMesh: &sr.targetMesh,
-		DestinationSelector: &supergloov1.PodSelector{
-			SelectorType: &supergloov1.PodSelector_UpstreamSelector_{
-				UpstreamSelector: &supergloov1.PodSelector_UpstreamSelector{
-					Upstreams: []solokitcore.ResourceRef{{
-						Name:      upstreamName(canary.Namespace, fmt.Sprintf("%s", canary.Spec.TargetRef.Name), canary.Spec.Service.Port),
-						Namespace: sr.targetMesh.Namespace,
-					}},
-				},
-			},
-		},
-		Spec: spec,
-	}
-}
-
-// GetRoutes returns the destinations weight for primary and canary
-func (sr *SuperglooRouter) GetRoutes(canary *flaggerv1.Canary) (
-	primaryWeight int,
-	canaryWeight int,
-	mirrored bool,
-	err error,
-) {
-	targetName := canary.Spec.TargetRef.Name
-	var rr *supergloov1.RoutingRule
-	rr, err = sr.rrClient.Read(canary.Namespace, targetName, solokitclients.ReadOpts{})
-	if err != nil {
-		return
-	}
-	traffic := rr.GetSpec().GetTrafficShifting()
-	if traffic == nil {
-		err = fmt.Errorf("target rule is not for traffic shifting")
-		return
-	}
-	dests := traffic.GetDestinations().GetDestinations()
-	for _, dest := range dests {
-		if dest.GetDestination().GetUpstream().Name == upstreamName(canary.Namespace, fmt.Sprintf("%s-primary", targetName), canary.Spec.Service.Port) {
-			primaryWeight = int(dest.Weight)
-		}
-		if dest.GetDestination().GetUpstream().Name == upstreamName(canary.Namespace, fmt.Sprintf("%s-canary", targetName), canary.Spec.Service.Port) {
-			canaryWeight = int(dest.Weight)
-		}
-	}
-
-	if primaryWeight == 0 && canaryWeight == 0 {
-		err = fmt.Errorf("RoutingRule %s.%s does not contain routes for %s-primary and %s-canary",
-			targetName, canary.Namespace, targetName, targetName)
-	}
-
-	mirrored = false
-
-	return
-}
-
-func upstreamName(serviceNamespace, serviceName string, port int32) string {
-	return fmt.Sprintf("%s-%s-%d", serviceNamespace, serviceName, port)
-}
-
-// SetRoutes updates the destinations weight for primary and canary
-func (sr *SuperglooRouter) SetRoutes(
-	canary *flaggerv1.Canary,
-	primaryWeight int,
-	canaryWeight int,
-	mirrored bool,
-) error {
-	// upstream name is
-	// in gloo-system
-	// and is the same as
-	targetName := canary.Spec.TargetRef.Name
-
-	destinations := []*gloov1.WeightedDestination{}
-	if primaryWeight != 0 {
-		destinations = append(destinations, &gloov1.WeightedDestination{
-			Destination: &gloov1.Destination{
-				Upstream: solokitcore.ResourceRef{
-					Name:      upstreamName(canary.Namespace, fmt.Sprintf("%s-primary", targetName), canary.Spec.Service.Port),
-					Namespace: sr.targetMesh.Namespace,
-				},
-			},
-			Weight: uint32(primaryWeight),
-		})
-	}
-
-	if canaryWeight != 0 {
-		destinations = append(destinations, &gloov1.WeightedDestination{
-			Destination: &gloov1.Destination{
-				Upstream: solokitcore.ResourceRef{
-					Name:      upstreamName(canary.Namespace, fmt.Sprintf("%s-canary", targetName), canary.Spec.Service.Port),
-					Namespace: sr.targetMesh.Namespace,
-				},
-			},
-			Weight: uint32(canaryWeight),
-		})
-	}
-
-	if len(destinations) == 0 {
-		return fmt.Errorf("RoutingRule %s.%s update failed: no valid weights", targetName, canary.Namespace)
-	}
-
-	rule := sr.createRule(canary, "", &supergloov1.RoutingRuleSpec{
-		RuleType: &supergloov1.RoutingRuleSpec_TrafficShifting{
-			TrafficShifting: &supergloov1.TrafficShifting{
-				Destinations: &gloov1.MultiDestination{
-					Destinations: destinations,
-				},
-			},
-		},
-	})
-
-	return sr.writeRuleForCanary(canary, rule)
-}
-
-func (sr *SuperglooRouter) writeRuleForCanary(canary *flaggerv1.Canary, rule *supergloov1.RoutingRule) error {
-	targetName := canary.Spec.TargetRef.Name
-
-	if oldRr, err := sr.rrClient.Read(rule.Metadata.Namespace, rule.Metadata.Name, solokitclients.ReadOpts{}); err != nil {
-		// ignore not exist errors..
-		if !solokiterror.IsNotExist(err) {
-			return fmt.Errorf("RoutingRule %s.%s read failed: %v", targetName, canary.Namespace, err)
-		}
-	} else {
-		rule.Metadata.ResourceVersion = oldRr.Metadata.ResourceVersion
-		// if the old and the new one are equal, no need to do anything.
-		oldRr.Status = solokitcore.Status{}
-		if oldRr.Equal(rule) {
-			return nil
-		}
-	}
-
-	kubeWriteOpts := &kube.KubeWriteOpts{
-		PreWriteCallback: func(r *crdv1.Resource) {
-			r.ObjectMeta.OwnerReferences = []metav1.OwnerReference{
-				*metav1.NewControllerRef(canary, schema.GroupVersionKind{
-					Group:   flaggerv1.SchemeGroupVersion.Group,
-					Version: flaggerv1.SchemeGroupVersion.Version,
-					Kind:    flaggerv1.CanaryKind,
-				}),
-			}
-		},
-	}
-	writeOpts := solokitclients.WriteOpts{OverwriteExisting: true, StorageWriteOpts: kubeWriteOpts}
-	_, err := sr.rrClient.Write(rule, writeOpts)
-	if err != nil {
-		return fmt.Errorf("RoutingRule %s.%s update failed: %v", targetName, canary.Namespace, err)
-	}
-	return nil
-}


Test patch:
diff --git a/pkg/router/gloo_test.go b/pkg/router/gloo_test.go
index b6ae7d30..dad25a59 100644
--- a/pkg/router/gloo_test.go
+++ b/pkg/router/gloo_test.go
@@ -1,40 +1,34 @@
 package router
 
 import (
-	"context"
 	"fmt"
 	"testing"
 
-	gloov1 "github.com/solo-io/gloo/projects/gloo/pkg/api/v1"
-	solokitclients "github.com/solo-io/solo-kit/pkg/api/v1/clients"
-	"github.com/solo-io/solo-kit/pkg/api/v1/clients/factory"
-	solokitmemory "github.com/solo-io/solo-kit/pkg/api/v1/clients/memory"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+
+	gloov1 "github.com/weaveworks/flagger/pkg/apis/gloo/v1"
 )
 
 func TestGlooRouter_Sync(t *testing.T) {
 	mocks := setupfakeClients()
-
-	upstreamGroupClient, err := gloov1.NewUpstreamGroupClient(&factory.MemoryResourceClientFactory{
-		Cache: solokitmemory.NewInMemoryResourceCache(),
-	})
-	if err != nil {
-		t.Fatal(err.Error())
-	}
-	if err := upstreamGroupClient.Register(); err != nil {
-		t.Fatal(err.Error())
+	router := &GlooRouter{
+		logger:        mocks.logger,
+		flaggerClient: mocks.flaggerClient,
+		glooClient:    mocks.meshClient,
+		kubeClient:    mocks.kubeClient,
 	}
-	router := NewGlooRouterWithClient(context.TODO(), upstreamGroupClient, "gloo-system", mocks.logger)
-	err = router.Reconcile(mocks.canary)
+
+	err := router.Reconcile(mocks.canary)
 	if err != nil {
 		t.Fatal(err.Error())
 	}
 
 	// test insert
-	ug, err := upstreamGroupClient.Read("default", "podinfo", solokitclients.ReadOpts{})
+	ug, err := router.glooClient.GlooV1().UpstreamGroups("default").Get("podinfo", metav1.GetOptions{})
 	if err != nil {
 		t.Fatal(err.Error())
 	}
-	dests := ug.GetDestinations()
+	dests := ug.Spec.Destinations
 	if len(dests) != 2 {
 		t.Errorf("Got Destinations %v wanted %v", len(dests), 2)
 	}
@@ -49,21 +43,15 @@ func TestGlooRouter_Sync(t *testing.T) {
 }
 
 func TestGlooRouter_SetRoutes(t *testing.T) {
-
 	mocks := setupfakeClients()
-
-	upstreamGroupClient, err := gloov1.NewUpstreamGroupClient(&factory.MemoryResourceClientFactory{
-		Cache: solokitmemory.NewInMemoryResourceCache(),
-	})
-	if err != nil {
-		t.Fatal(err.Error())
-	}
-	if err := upstreamGroupClient.Register(); err != nil {
-		t.Fatal(err.Error())
+	router := &GlooRouter{
+		logger:        mocks.logger,
+		flaggerClient: mocks.flaggerClient,
+		glooClient:    mocks.meshClient,
+		kubeClient:    mocks.kubeClient,
 	}
-	router := NewGlooRouterWithClient(context.TODO(), upstreamGroupClient, "gloo-system", mocks.logger)
 
-	err = router.Reconcile(mocks.canary)
+	err := router.Reconcile(mocks.canary)
 	if err != nil {
 		t.Fatal(err.Error())
 	}
@@ -82,20 +70,21 @@ func TestGlooRouter_SetRoutes(t *testing.T) {
 		t.Fatal(err.Error())
 	}
 
-	ug, err := upstreamGroupClient.Read("default", "podinfo", solokitclients.ReadOpts{})
+	ug, err := router.glooClient.GlooV1().UpstreamGroups("default").Get("podinfo", metav1.GetOptions{})
 	if err != nil {
 		t.Fatal(err.Error())
 	}
 
-	var pRoute *gloov1.WeightedDestination
-	var cRoute *gloov1.WeightedDestination
-	targetName := mocks.canary.Spec.TargetRef.Name
+	var pRoute gloov1.WeightedDestination
+	var cRoute gloov1.WeightedDestination
+	canaryName := fmt.Sprintf("%s-%s-canary-%v", mocks.canary.Namespace, mocks.canary.Spec.TargetRef.Name, mocks.canary.Spec.Service.Port)
+	primaryName := fmt.Sprintf("%s-%s-primary-%v", mocks.canary.Namespace, mocks.canary.Spec.TargetRef.Name, mocks.canary.Spec.Service.Port)
 
-	for _, dest := range ug.GetDestinations() {
-		if dest.GetDestination().GetUpstream().Name == upstreamName(mocks.canary.Namespace, fmt.Sprintf("%s-primary", targetName), mocks.canary.Spec.Service.Port) {
+	for _, dest := range ug.Spec.Destinations {
+		if dest.Destination.Upstream.Name == primaryName {
 			pRoute = dest
 		}
-		if dest.GetDestination().GetUpstream().Name == upstreamName(mocks.canary.Namespace, fmt.Sprintf("%s-canary", targetName), mocks.canary.Spec.Service.Port) {
+		if dest.Destination.Upstream.Name == canaryName {
 			cRoute = dest
 		}
 	}
@@ -112,18 +101,14 @@ func TestGlooRouter_SetRoutes(t *testing.T) {
 
 func TestGlooRouter_GetRoutes(t *testing.T) {
 	mocks := setupfakeClients()
-
-	upstreamGroupClient, err := gloov1.NewUpstreamGroupClient(&factory.MemoryResourceClientFactory{
-		Cache: solokitmemory.NewInMemoryResourceCache(),
-	})
-	if err != nil {
-		t.Fatal(err.Error())
-	}
-	if err := upstreamGroupClient.Register(); err != nil {
-		t.Fatal(err.Error())
+	router := &GlooRouter{
+		logger:        mocks.logger,
+		flaggerClient: mocks.flaggerClient,
+		glooClient:    mocks.meshClient,
+		kubeClient:    mocks.kubeClient,
 	}
-	router := NewGlooRouterWithClient(context.TODO(), upstreamGroupClient, "gloo-system", mocks.logger)
-	err = router.Reconcile(mocks.canary)
+
+	err := router.Reconcile(mocks.canary)
 	if err != nil {
 		t.Fatal(err.Error())
 	}
diff --git a/pkg/router/supergloo_test.go b/pkg/router/supergloo_test.go
deleted file mode 100644
index 9e54e8f1..00000000
--- a/pkg/router/supergloo_test.go
+++ /dev/null
@@ -1,154 +0,0 @@
-package router
-
-import (
-	"context"
-	"fmt"
-	"testing"
-
-	gloov1 "github.com/solo-io/gloo/projects/gloo/pkg/api/v1"
-	solokitclients "github.com/solo-io/solo-kit/pkg/api/v1/clients"
-	"github.com/solo-io/solo-kit/pkg/api/v1/clients/factory"
-	solokitmemory "github.com/solo-io/solo-kit/pkg/api/v1/clients/memory"
-	solokitcore "github.com/solo-io/solo-kit/pkg/api/v1/resources/core"
-	supergloov1 "github.com/solo-io/supergloo/pkg/api/v1"
-)
-
-func TestSuperglooRouter_Sync(t *testing.T) {
-	mocks := setupfakeClients()
-
-	routingRuleClient, err := supergloov1.NewRoutingRuleClient(&factory.MemoryResourceClientFactory{
-		Cache: solokitmemory.NewInMemoryResourceCache(),
-	})
-	if err != nil {
-		t.Fatal(err.Error())
-	}
-	if err := routingRuleClient.Register(); err != nil {
-		t.Fatal(err.Error())
-	}
-	targetMesh := solokitcore.ResourceRef{
-		Namespace: "supergloo-system",
-		Name:      "mesh",
-	}
-	router := NewSuperglooRouterWithClient(context.TODO(), routingRuleClient, targetMesh, mocks.logger)
-	err = router.Reconcile(mocks.canary)
-	if err != nil {
-		t.Fatal(err.Error())
-	}
-
-	// test insert
-	rr, err := routingRuleClient.Read("default", "podinfo", solokitclients.ReadOpts{})
-	if err != nil {
-		t.Fatal(err.Error())
-	}
-	dests := rr.Spec.GetTrafficShifting().GetDestinations().GetDestinations()
-	if len(dests) != 1 {
-		t.Errorf("Got RoutingRule Destinations %v wanted %v", len(dests), 1)
-	}
-
-}
-
-func TestSuperglooRouter_SetRoutes(t *testing.T) {
-
-	mocks := setupfakeClients()
-
-	routingRuleClient, err := supergloov1.NewRoutingRuleClient(&factory.MemoryResourceClientFactory{
-		Cache: solokitmemory.NewInMemoryResourceCache(),
-	})
-	if err != nil {
-		t.Fatal(err.Error())
-	}
-	if err := routingRuleClient.Register(); err != nil {
-		t.Fatal(err.Error())
-	}
-	targetMesh := solokitcore.ResourceRef{
-		Namespace: "supergloo-system",
-		Name:      "mesh",
-	}
-	router := NewSuperglooRouterWithClient(context.TODO(), routingRuleClient, targetMesh, mocks.logger)
-
-	err = router.Reconcile(mocks.canary)
-	if err != nil {
-		t.Fatal(err.Error())
-	}
-
-	p, c, m, err := router.GetRoutes(mocks.canary)
-	if err != nil {
-		t.Fatal(err.Error())
-	}
-
-	p = 50
-	c = 50
-	m = false
-
-	err = router.SetRoutes(mocks.canary, p, c, m)
-	if err != nil {
-		t.Fatal(err.Error())
-	}
-
-	rr, err := routingRuleClient.Read("default", "podinfo", solokitclients.ReadOpts{})
-	if err != nil {
-		t.Fatal(err.Error())
-	}
-
-	var pRoute *gloov1.WeightedDestination
-	var cRoute *gloov1.WeightedDestination
-	targetName := mocks.canary.Spec.TargetRef.Name
-
-	for _, dest := range rr.GetSpec().GetTrafficShifting().GetDestinations().GetDestinations() {
-		if dest.GetDestination().GetUpstream().Name == upstreamName(mocks.canary.Namespace, fmt.Sprintf("%s-primary", targetName), mocks.canary.Spec.Service.Port) {
-			pRoute = dest
-		}
-		if dest.GetDestination().GetUpstream().Name == upstreamName(mocks.canary.Namespace, fmt.Sprintf("%s-canary", targetName), mocks.canary.Spec.Service.Port) {
-			cRoute = dest
-		}
-	}
-
-	if pRoute.Weight != uint32(p) {
-		t.Errorf("Got primary weight %v wanted %v", pRoute.Weight, p)
-	}
-
-	if cRoute.Weight != uint32(c) {
-		t.Errorf("Got canary weight %v wanted %v", cRoute.Weight, c)
-	}
-
-}
-
-func TestSuperglooRouter_GetRoutes(t *testing.T) {
-	mocks := setupfakeClients()
-
-	routingRuleClient, err := supergloov1.NewRoutingRuleClient(&factory.MemoryResourceClientFactory{
-		Cache: solokitmemory.NewInMemoryResourceCache(),
-	})
-	if err != nil {
-		t.Fatal(err.Error())
-	}
-	if err := routingRuleClient.Register(); err != nil {
-		t.Fatal(err.Error())
-	}
-	targetMesh := solokitcore.ResourceRef{
-		Namespace: "supergloo-system",
-		Name:      "mesh",
-	}
-	router := NewSuperglooRouterWithClient(context.TODO(), routingRuleClient, targetMesh, mocks.logger)
-	err = router.Reconcile(mocks.canary)
-	if err != nil {
-		t.Fatal(err.Error())
-	}
-
-	p, c, m, err := router.GetRoutes(mocks.canary)
-	if err != nil {
-		t.Fatal(err.Error())
-	}
-
-	if p != 100 {
-		t.Errorf("Got primary weight %v wanted %v", p, 100)
-	}
-
-	if c != 0 {
-		t.Errorf("Got canary weight %v wanted %v", c, 0)
-	}
-
-	if m != false {
-		t.Errorf("Got mirror %v wanted %v", m, false)
-	}
-}
diff --git a/test/e2e-kind.sh b/test/e2e-kind.sh
index 7120a1e9..ea41df54 100755
--- a/test/e2e-kind.sh
+++ b/test/e2e-kind.sh
@@ -3,6 +3,7 @@
 set -o errexit
 
 REPO_ROOT=$(git rev-parse --show-toplevel)
+HELM_VERSION=v2.14.3
 KIND_VERSION=v0.5.1
 
 if [[ "$1" ]]; then
@@ -26,7 +27,7 @@ export KUBECONFIG="$(kind get kubeconfig-path --name="kind")"
 kubectl get pods --all-namespaces
 
 echo ">>> Installing Helm"
-curl https://raw.githubusercontent.com/kubernetes/helm/master/scripts/get | bash
+curl -sSL https://get.helm.sh/helm-${HELM_VERSION}-linux-amd64.tar.gz | tar xz && sudo mv linux-amd64/helm /usr/local/bin/ && rm -rf linux-amd64
 
 echo '>>> Installing Tiller'
 kubectl --namespace kube-system create sa tiller