Repository: replicatedhq/kots
Pull request #14 (2019-08-22 15:06:10)

Parsing replicated license files
This PR adds initial support to kots CLI for pulling and installing replicated apps using a local license file.


Patch:
diff --git a/cmd/kots/cli/pull.go b/cmd/kots/cli/pull.go
index e43c4344e..24042630e 100644
--- a/cmd/kots/cli/pull.go
+++ b/cmd/kots/cli/pull.go
@@ -34,6 +34,7 @@ func PullCmd() *cobra.Command {
 				Overwrite:   v.GetBool("overwrite"),
 				Namespace:   v.GetString("namespace"),
 				Downstreams: v.GetStringSlice("downstream"),
+				LocalPath:   ExpandDir(v.GetString("local-path")),
 			}
 			if err := pull.Pull(args[0], pullOptions); err != nil {
 				return err
@@ -60,6 +61,7 @@ func PullCmd() *cobra.Command {
 	cmd.Flags().Bool("overwrite", false, "if the upstream already exists, overwrite it")
 	cmd.Flags().String("namespace", "default", "namespace to render the upstream to in the base")
 	cmd.Flags().StringSlice("downstream", []string{}, "the list of any downstreams to create/update")
+	cmd.Flags().String("local-path", "", "specify a local-path to test the behavior of rendering a replicated app locally (only supported on replicated app types currently)")
 
 	return cmd
 }
diff --git a/cmd/kots/cli/util.go b/cmd/kots/cli/util.go
index b73924102..eb4b62455 100644
--- a/cmd/kots/cli/util.go
+++ b/cmd/kots/cli/util.go
@@ -2,8 +2,18 @@ package cli
 
 import (
 	"os"
+	"path"
+	"strings"
 )
 
+func ExpandDir(input string) string {
+	if !strings.HasPrefix(input, "~") {
+		return input
+	}
+
+	return path.Join(homeDir(), input[1:])
+}
+
 func homeDir() string {
 	if h := os.Getenv("HOME"); h != "" {
 		return h
diff --git a/docs/apps/replicated/README.md b/docs/apps/replicated/README.md
index ddca3e026..74a799d0c 100644
--- a/docs/apps/replicated/README.md
+++ b/docs/apps/replicated/README.md
@@ -20,14 +20,14 @@ For disambiguation when there are multiple releases with the same name, use the
 kubectl kots pull replicated://app-slug#12
 ```
 
-To specify the channel to use (the license must be assigned to the requested channel):
+Some application channels require that they are specified on the URL:
 
 ```shell
 kubectl kots pull replicated://app-slug/channel
 ```
 
-To use a different upstream provider than the default (replicated.app):
+For local testing, you can point to a directory that contains the extracted YAML:
 
 ```shell
-kubectl kots pull replicated://app-slug?host=my.com
+kuebctl kots pull replicated://app-slug --local-path=./workdir
 ```
diff --git a/go.mod b/go.mod
index 8bd0cdd44..2bb935642 100644
--- a/go.mod
+++ b/go.mod
@@ -4,39 +4,43 @@ go 1.12
 
 require (
 	github.com/Azure/go-autorest v11.1.2+incompatible // indirect
-	github.com/Masterminds/goutils v1.1.0 // indirect
 	github.com/Masterminds/semver v1.4.2
-	github.com/Masterminds/sprig v2.20.0+incompatible // indirect
+	github.com/Masterminds/sprig v2.20.0+incompatible
 	github.com/ahmetalpbalkan/go-cursor v0.0.0-20131010032410-8136607ea412
 	github.com/apex/log v1.1.1
+	github.com/cjbassi/drawille-go v0.0.0-20190126131713-27dc511fe6fd // indirect
+	github.com/cloudflare/cfssl v0.0.0-20190808011637-b1ec8c586c2a // indirect
 	github.com/cyphar/filepath-securejoin v0.2.2 // indirect
-	github.com/docker/spdystream v0.0.0-20181023171402-6480d4af844c // indirect
-	github.com/dsnet/compress v0.0.1 // indirect
+	github.com/docker/go-units v0.4.0
 	github.com/fatih/color v1.7.0
-	github.com/gizak/termui/v3 v3.0.0
+	github.com/gizak/termui/v3 v3.1.0
+	github.com/go-kit/kit v0.8.0
 	github.com/gobwas/glob v0.2.3 // indirect
-	github.com/golang/snappy v0.0.1 // indirect
+	github.com/google/certificate-transparency-go v1.0.21 // indirect
 	github.com/google/uuid v1.1.1
-	github.com/gophercloud/gophercloud v0.0.0-20190126172459-c818fa66e4c8 // indirect
-	github.com/gregjones/httpcache v0.0.0-20170728041850-787624de3eb7 // indirect
-	github.com/huandu/xstrings v1.2.0 // indirect
+	github.com/hashicorp/terraform v0.12.7 // indirect
 	github.com/imdario/mergo v0.3.7 // indirect
 	github.com/manifoldco/promptui v0.3.2
 	github.com/mholt/archiver v3.1.1+incompatible
-	github.com/nwaples/rardecode v1.0.0 // indirect
 	github.com/peterbourgon/diskv v2.0.1+incompatible // indirect
 	github.com/pierrec/lz4 v2.2.6+incompatible // indirect
 	github.com/pkg/errors v0.8.1
+	github.com/replicatedhq/libyaml v0.0.0-20190513182811-dcb60811454e
+	github.com/replicatedhq/troubleshoot v0.9.5 // indirect
 	github.com/securego/gosec v0.0.0-20190709033609-4b59c948083c // indirect
+	github.com/spf13/afero v1.2.2
 	github.com/spf13/cobra v0.0.5
 	github.com/spf13/viper v1.4.0
 	github.com/stretchr/testify v1.3.0
 	github.com/tj/go-spin v1.1.0
-	github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 // indirect
-	golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8
+	github.com/zmap/zlint v1.0.0 // indirect
+	golang.org/x/arch v0.0.0-20181203225421-5a4828bb7045 // indirect
+	golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4
 	golang.org/x/oauth2 v0.0.0-20190402181905-9f3314589c9a // indirect
 	google.golang.org/appengine v1.5.0 // indirect
+	gopkg.in/laverya/yaml.v3 v3.0.0-beta3 // indirect
 	gopkg.in/yaml.v2 v2.2.2
+	gopkg.in/yaml.v3 v3.0.0-20190709130402-674ba3eaed22 // indirect
 	k8s.io/api v0.0.0-20190409021203-6e4e0e4f393b
 	k8s.io/apiextensions-apiserver v0.0.0-20190409022649-727a075fdec8
 	k8s.io/apimachinery v0.0.0-20190404173353-6a84e37a896d
diff --git a/go.sum b/go.sum
index deab2283f..bcaae0583 100644
--- a/go.sum
+++ b/go.sum
@@ -1,14 +1,26 @@
 cloud.google.com/go v0.26.0 h1:e0WKqKTd5BnrG8aKH3J3h+QvEIQtSUcf2n5UZ5ZgLtQ=
 cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
+cloud.google.com/go v0.31.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
 cloud.google.com/go v0.34.0 h1:eOI3/cP2VTU6uZLDYAoic+eyzzB9YyGmJ7eIjl8rOPg=
 cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
+cloud.google.com/go v0.36.0 h1:+aCSj7tOo2LODWVEuZDZeGCckdt6MlSF+X/rB3wUiS8=
+cloud.google.com/go v0.36.0/go.mod h1:RUoy9p/M4ge0HzT8L+SDZ8jg+Q6fth0CiBuhFJpSV40=
 cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
 cloud.google.com/go v0.41.0 h1:NFvqUTDnSNYPX5oReekmB+D+90jrJIcVImxQ3qrBVgM=
 cloud.google.com/go v0.41.0/go.mod h1:OauMR7DV8fzvZIl2qg6rkaIhD/vmgk4iwEw/h6ercmg=
+dmitri.shuralyov.com/app/changes v0.0.0-20180602232624-0a106ad413e3/go.mod h1:Yl+fi1br7+Rr3LqpNJf1/uxUdtRUV+Tnj0o93V2B9MU=
+dmitri.shuralyov.com/html/belt v0.0.0-20180602232347-f7d459c86be0/go.mod h1:JLBrvjyP0v+ecvNYvCpyZgu5/xkfAUhi6wJj28eUfSU=
+dmitri.shuralyov.com/service/change v0.0.0-20181023043359-a85b471d5412/go.mod h1:a1inKt/atXimZ4Mv927x+r7UpyzRUf4emIoiiSC2TN4=
+dmitri.shuralyov.com/state v0.0.0-20180228185332-28bcc343414c/go.mod h1:0PRwlb0D6DFvNNtx+9ybjezNCa8XF0xaYcETyp6rHWU=
+git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg=
+github.com/Azure/azure-sdk-for-go v21.3.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
+github.com/Azure/go-autorest v10.15.4+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24=
 github.com/Azure/go-autorest v11.1.2+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24=
+github.com/Azure/go-ntlmssp v0.0.0-20180810175552-4a21cbd618b4/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU=
 github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
 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/ChrisTrenkamp/goxpath v0.0.0-20170922090931-c385f95c6022/go.mod h1:nuWgzSkT5PnyOd+272uUmV0dnAnAn42Mk7PiQC5VzN4=
 github.com/Masterminds/goutils v1.1.0 h1:zukEsf/1JZwCMgHiK3GZftabmxiCw4apj3a28RPBiVg=
 github.com/Masterminds/goutils v1.1.0/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU=
 github.com/Masterminds/semver v1.4.2 h1:WBLTQ37jOCzSLtXNdoo8bNM8876KhNqOKvrlGITgsTc=
@@ -23,36 +35,83 @@ github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbt
 github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
 github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M=
 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/Unknwon/com v0.0.0-20151008135407-28b053d5a292/go.mod h1:KYCjqMOeHpNuTOiFQU6WEcTG7poCJrUs0YgyHNtn1no=
+github.com/abdullin/seq v0.0.0-20160510034733-d5467c17e7af/go.mod h1:5Jv4cbFiHJMsVxt52+i0Ha45fjshj6wxYr1r19tB9bw=
+github.com/agext/levenshtein v1.2.1/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558=
+github.com/agext/levenshtein v1.2.2 h1:0S/Yg6LYmFJ5stwQeRp6EeOcCbj7xiqQSdNelsXvaqE=
+github.com/agext/levenshtein v1.2.2/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558=
+github.com/agl/ed25519 v0.0.0-20150830182803-278e1ec8e8a6/go.mod h1:WPjqKcmVOxf0XSf3YxCJs6N6AOSrOx3obionmG7T0y0=
 github.com/ahmetalpbalkan/go-cursor v0.0.0-20131010032410-8136607ea412 h1:vOVO0ypMfTt6tZacyI0kp+iCZb1XSNiYDqnzBWYgfe4=
 github.com/ahmetalpbalkan/go-cursor v0.0.0-20131010032410-8136607ea412/go.mod h1:AI9hp1tkp10pAlK5TCwL+7yWbRgtDm9jhToq6qij2xs=
 github.com/alecthomas/gometalinter v2.0.11+incompatible/go.mod h1:qfIpQGGz3d+NmgyPBqv+LSh50emm1pt72EtcX2vKYQk=
 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/aliyun/alibaba-cloud-sdk-go v0.0.0-20190329064014-6e358769c32a/go.mod h1:T9M45xf79ahXVelWoOBmH0y4aC1t5kXO5BxwyakgIGA=
+github.com/aliyun/aliyun-oss-go-sdk v0.0.0-20190103054945-8205d1f41e70/go.mod h1:T/Aws4fEfogEE9v+HPhhw+CntffsBHJ8nXQCwKr0/g8=
+github.com/aliyun/aliyun-tablestore-go-sdk v4.1.2+incompatible/go.mod h1:LDQHRZylxvcg8H7wBIDfvO5g/cy4/sz1iucBlc2l3Jw=
+github.com/andrewchambers/go-jqpipe v0.0.0-20180509223707-2d54cef8cd94/go.mod h1:8KsRln2ynWOaJwxYOUwfPU/ix+H9oh3aCJ6L0DsHW3Q=
+github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
+github.com/antchfx/xpath v0.0.0-20190129040759-c8489ed3251e/go.mod h1:Yee4kTMuNiPYJ7nSNorELQMr1J33uOpXDMByNYhvtNk=
+github.com/antchfx/xquery v0.0.0-20180515051857-ad5b8c7a47b0/go.mod h1:LzD22aAzDP8/dyiCKFp31He4m2GPjl0AFyzDtZzUu9M=
+github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
 github.com/apex/log v1.1.1 h1:BwhRZ0qbjYtTob0I+2M+smavV0kOC8XgcnGZcyL9liA=
 github.com/apex/log v1.1.1/go.mod h1:Ls949n1HFtXfbDcjiTTFQqkVUrte0puoIBfO3SVgwOA=
 github.com/aphistic/golf v0.0.0-20180712155816-02c07f170c5a/go.mod h1:3NqKYiepwy8kCu4PNA+aP7WUV72eXWJeP9/r3/K9aLE=
 github.com/aphistic/sweet v0.2.0/go.mod h1:fWDlIh/isSE9n6EPsRmC0det+whmX6dJid3stzu0Xys=
+github.com/apparentlymart/go-cidr v1.0.0 h1:lGDvXx8Lv9QHjrAVP7jyzleG4F9+FkRhJcEsDFxeb8w=
+github.com/apparentlymart/go-cidr v1.0.0/go.mod h1:EBcsNrHc3zQeuaeCeCtQruQm+n9/YjEn/vI25Lg7Gwc=
+github.com/apparentlymart/go-dump v0.0.0-20180507223929-23540a00eaa3/go.mod h1:oL81AME2rN47vu18xqj1S1jPIPuN7afo62yKTNn3XMM=
+github.com/apparentlymart/go-dump v0.0.0-20190214190832-042adf3cf4a0/go.mod h1:oL81AME2rN47vu18xqj1S1jPIPuN7afo62yKTNn3XMM=
+github.com/apparentlymart/go-textseg v1.0.0 h1:rRmlIsPEEhUTIKQb7T++Nz/A5Q6C9IuX2wFoYVvnCs0=
+github.com/apparentlymart/go-textseg v1.0.0/go.mod h1:z96Txxhf3xSFMPmb5X/1W05FF/Nj9VFpLOpjS5yuumk=
 github.com/appscode/jsonpatch v0.0.0-20190108182946-7c0e3b262f30 h1:Kn3rqvbUFqSepE2OqVu0Pn1CbDw9IuMlONapol0zuwk=
 github.com/appscode/jsonpatch v0.0.0-20190108182946-7c0e3b262f30/go.mod h1:4AJxUpXUhv4N+ziTvIcWWXgeorXpxPZOfk9HdEVr96M=
 github.com/appscode/jsonpatch v2.0.0+incompatible h1:DEsgcSnA7ui6pICc75uxDpyN8Bx4DLFTS8aRym702nE=
+github.com/armon/circbuf v0.0.0-20190214190532-5111143e8da2/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
 github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
+github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
+github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
+github.com/armon/go-radix v1.0.0 h1:F4z6KzEeeQIMeLFa97iZU6vupzoecKdU5TX24SNppXI=
+github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
+github.com/aws/aws-sdk-go v1.15.78/go.mod h1:E3/ieXAlvM0XWO57iftYVDLLvQ824smPP3ATZkfNZeM=
+github.com/aws/aws-sdk-go v1.16.36/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
 github.com/aws/aws-sdk-go v1.20.6/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
+github.com/aws/aws-sdk-go v1.22.0 h1:e88V6+dSEyBibUy0ekOydtTfNWzqG3hrtCR8SF6UqqY=
+github.com/aws/aws-sdk-go v1.22.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
 github.com/aybabtme/rgbterm v0.0.0-20170906152045-cc83f3b3ce59/go.mod h1:q/89r3U2H7sSsE2t6Kca0lfwTK8JdoNGS/yzM/4iH5I=
+github.com/baiyubin/aliyun-sts-go-sdk v0.0.0-20180326062324-cfa1a18b161f/go.mod h1:AuiFmCCPBSrqvVMvuqFuk0qogytodnVFVSN5CeJB8Gc=
 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/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d h1:xDfNPAt8lFiC1UJrqV3uuy861HCTo708pDMbjHHdCas=
+github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d/go.mod h1:6QX/PXZ00z/TKoufEY6K/a0k6AhaJrQKdFe6OfVXsa4=
+github.com/bgentry/speakeasy v0.1.0 h1:ByYyxL9InA1OWqxJqqp2A5pYHUrCiAL6K3J+LKSsQkY=
+github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
+github.com/blang/semver v3.5.1+incompatible h1:cQNTCjp13qL8KC3Nbxr/y2Bqb63oX6wdnnjpJbkM4JQ=
+github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
+github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps=
+github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g=
+github.com/bsm/go-vlq v0.0.0-20150828105119-ec6e8d4f5f4e/go.mod h1:N+BjUcTjSxc2mtRGSCPsat1kze3CUtvJN3/jTXlp29k=
 github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
+github.com/cheggaaa/pb v1.0.27/go.mod h1:pQciLPpbU0oxA0h+VJYYLxO+XeDQb5pZijXscXHm81s=
 github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
+github.com/chzyer/readline v0.0.0-20161106042343-c914be64f07d/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
 github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e h1:fY5BOSpyZCqRo5OhCuC+XN+r/bBCmeuuJtjz+bCNIf8=
 github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
 github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
 github.com/cjbassi/drawille-go v0.0.0-20190126131713-27dc511fe6fd h1:XtfPmj9tQRilnrEmI1HjQhxXWRhEM+m8CACtaMJE/kM=
 github.com/cjbassi/drawille-go v0.0.0-20190126131713-27dc511fe6fd/go.mod h1:vjcQJUZJYD3MeVGhtZXSMnCHfUNZxsyYzJt90eCYxK4=
 github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
+github.com/cloudflare/cfssl v0.0.0-20190808011637-b1ec8c586c2a h1:ym8P2+ZvUvVtpLzy8wFLLvdggUIU31mvldvxixQQI2o=
+github.com/cloudflare/cfssl v0.0.0-20190808011637-b1ec8c586c2a/go.mod h1:yMWuSON2oQp+43nFtAV/uvKQIFpSPerB57DCt9t8sSA=
+github.com/coreos/bbolt v1.3.0/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
 github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
 github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
 github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
 github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
+github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
 github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
 github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
 github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
@@ -65,6 +124,13 @@ github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
 github.com/dgrijalva/jwt-go v0.0.0-20160705203006-01aeca54ebda/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
 github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
 github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
+github.com/dimchansky/utfbom v1.0.0/go.mod h1:rO41eb7gLfo8SF1jd9F8HplJm1Fewwi4mQvIirEdv+8=
+github.com/dnaeon/go-vcr v0.0.0-20180920040454-5637cf3d8a31/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E=
+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/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
+github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw=
+github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
 github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96 h1:cenwrSVm+Z7QLSV/BsnenAOcDXdX4cMv4wP0B/5QbPg=
 github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM=
 github.com/docker/spdystream v0.0.0-20181023171402-6480d4af844c h1:ZfSZ3P3BedhKGUhzj7BQlPSU4OvT6tfOKe3DVHzOA7s=
@@ -72,6 +138,12 @@ github.com/docker/spdystream v0.0.0-20181023171402-6480d4af844c/go.mod h1:Qh8CwZ
 github.com/dsnet/compress v0.0.1 h1:PlZu0n3Tuv04TzpfPbrnI0HW/YwodEXDS+oPKahKF0Q=
 github.com/dsnet/compress v0.0.1/go.mod h1:Aw8dCMJ7RioblQeTqt88akK31OvO8Dhf5JflhBbQEHo=
 github.com/dsnet/golib v0.0.0-20171103203638-1ea166775780/go.mod h1:Lj+Z9rebOhdfkVLjJ8T6VcRQv3SXugXy999NBtR9aFY=
+github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
+github.com/dylanmei/iso8601 v0.1.0/go.mod h1:w9KhXSgIyROl1DefbMYIE7UVSIvELTbMrCfx+QkYnoQ=
+github.com/dylanmei/winrmtest v0.0.0-20190225150635-99b7fe2fddf1/go.mod h1:lcy9/2gH1jn/VCLouHA6tOEwLoNVd4GW6zhuKLmHC2Y=
+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/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.6+incompatible h1:tfrHha8zJ01ywiOEC1miGY8st1/igzWB8OmvPgoYX7w=
@@ -87,6 +159,7 @@ github.com/evanphx/json-patch v4.5.0+incompatible h1:ouOWdg56aJriqS0huScTkVXPC5I
 github.com/evanphx/json-patch v4.5.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
 github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys=
 github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
+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/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
@@ -98,8 +171,12 @@ github.com/gin-gonic/gin v1.4.0 h1:3tMoCCfM7ppqsR0ptz/wi1impNpT7/9wQtMZ8lr1mCQ=
 github.com/gin-gonic/gin v1.4.0/go.mod h1:OW2EZn3DO8Ln9oIKOvM++LBO+5UPHJJDH72/q/3rZdM=
 github.com/gizak/termui/v3 v3.0.0 h1:NYTUG6ig/sJK05O5FyhWemwlVPO8ilNpvS/PgRtrKAE=
 github.com/gizak/termui/v3 v3.0.0/go.mod h1:uinu2dMdtMI+FTIdEFUJQT5y+KShnhQRshvPblXq3lY=
+github.com/gizak/termui/v3 v3.1.0/go.mod h1:bXQEBkJpzxUAKf0+xq9MSWAvWZlE7c+aidmyFlkYTrY=
+github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
+github.com/go-kit/kit v0.8.0 h1:Wz+5lgoB0kkuqLEc6NVmwRknTKP6dTGbSqvhZtBI/j0=
 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-logfmt/logfmt v0.4.0 h1:MP4Eh7ZCb31lleYCFuwm0oe4/YGak+5l1vA2NOE80nA=
 github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
 github.com/go-logr/logr v0.1.0 h1:M1Tv3VzNlEHg6uyACnRdtrploV2P7wZqH8BoQMtz0cg=
 github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas=
@@ -120,6 +197,8 @@ github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dp
 github.com/go-openapi/swag v0.19.2 h1:jvO6bCMBEilGwMfHhrd61zIID4oIFdwb76V17SM88dE=
 github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
 github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
+github.com/go-test/deep v1.0.3 h1:ZrJSEWsXzPOxaZnFteGEfooLba+ju3FYIbOrS+rQd68=
+github.com/go-test/deep v1.0.3/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA=
 github.com/gobuffalo/envy v1.6.5/go.mod h1:N+GkhhZ/93bGZc6ZKhJLP6+m+tCNPKwgSpH9kaifseQ=
 github.com/gobuffalo/envy v1.6.15/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI=
 github.com/gobuffalo/envy v1.7.0/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI=
@@ -130,6 +209,7 @@ github.com/gogo/protobuf v1.0.0 h1:2jyBKDKU/8v3v2xVR2PtiWQviFUyiaGk2rpfyFT8rTM=
 github.com/gogo/protobuf v1.0.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
 github.com/gogo/protobuf v1.1.1 h1:72R+M5VuhED/KujmZVcIquuo8mBgX4oVda//DQb3PXo=
 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=
 github.com/gogo/protobuf v1.2.1 h1:/s5zKNz0uPFCZ5hddgPdo2TK2TVrUNMn0OOX8/aZMTE=
 github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
 github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
@@ -140,24 +220,31 @@ github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef h1:veQD95Isof8w9
 github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
 github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6 h1:ZgQEtGgCBiWRM39fZuwSd1LwSqqSW0hOdXCYYDX0R3I=
 github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
+github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E=
 github.com/golang/lint v0.0.0-20181026193005-c67002cb31c3/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/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
 github.com/golang/protobuf v0.0.0-20161109072736-4bd1920723d7/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/protobuf v1.1.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
 github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM=
 github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
 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/certificate-transparency-go v1.0.21 h1:Yf1aXowfZ2nuboBsg7iYGLmwsOARdV86pfH3g95wXmE=
+github.com/google/certificate-transparency-go v1.0.21/go.mod h1:QeJfpSbVSfYc7RgB3gJFj9cbuQMMchQxrWXz8Ruopmg=
 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 h1:+RRA9JqSOZFfKrOeqr2z77+8R2RKyh8PG66dcu1V0ck=
 github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI=
@@ -171,6 +258,10 @@ github.com/google/uuid v1.0.0 h1:b4Gk+7WdP/d3HZH8EJsZpvV7EtDOgaZLtnaNGIu1adA=
 github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
 github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY=
 github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/googleapis/gax-go v2.0.0+incompatible h1:j0GKcs05QVmm7yesiZq2+9cxHkNK9YM6zKx4D2qucQU=
+github.com/googleapis/gax-go v2.0.0+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY=
+github.com/googleapis/gax-go/v2 v2.0.3 h1:siORttZ36U2R/WjiJuDz8znElWBiAlO9rVt+mqJt0Cc=
+github.com/googleapis/gax-go/v2 v2.0.3/go.mod h1:LLvjysVCY1JZeum8Z6l8qUty8fiNwE08qbEPm1M08qg=
 github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
 github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
 github.com/googleapis/gnostic v0.0.0-20170426233943-68f4ded48ba9/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY=
@@ -181,22 +272,75 @@ github.com/googleapis/gnostic v0.2.0/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTV
 github.com/googleapis/gnostic v0.3.0 h1:CcQijm0XKekKjP/YCz28LXVSpgguuB+nCxaSjCe09y0=
 github.com/googleapis/gnostic v0.3.0/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY=
 github.com/gophercloud/gophercloud v0.0.0-20190126172459-c818fa66e4c8/go.mod h1:3WdhXV3rUYy9p6AUW8d94kr+HS62Y4VL9mBnFxsD8q4=
+github.com/gophercloud/gophercloud v0.0.0-20190208042652-bc37892e1968/go.mod h1:3WdhXV3rUYy9p6AUW8d94kr+HS62Y4VL9mBnFxsD8q4=
+github.com/gophercloud/utils v0.0.0-20190128072930-fbb6ab446f01/go.mod h1:wjDF8z83zTeg5eMLml5EBSlAhbF7G8DobyI1YsMuyzw=
+github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
 github.com/gordonklaus/ineffassign v0.0.0-20180909121442-1003c8bd00dc/go.mod h1:cuNKsD1zp2v6XfE/orVX2QE1LC+i254ceGcVeDT3pTU=
+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/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
 github.com/gregjones/httpcache v0.0.0-20170728041850-787624de3eb7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
+github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
 github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 h1:+ngKgrYPPJrOjhax5N+uePQ0Fh1Z7PheYoUI/0nzkPA=
 github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
 github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
 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/grpc-ecosystem/grpc-gateway v1.8.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
 github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
+github.com/hashicorp/aws-sdk-go-base v0.3.0/go.mod h1:ZIWACGGi0N7a4DZbf15yuE1JQORmWLtBcVM6F5SXNFU=
+github.com/hashicorp/consul v0.0.0-20171026175957-610f3c86a089/go.mod h1:mFrjN1mfidgJfYP1xrJCF+AfRhr6Eaqhb2+sfyn/OOI=
+github.com/hashicorp/errwrap v0.0.0-20180715044906-d6c0cd880357/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
+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-azure-helpers v0.0.0-20190129193224-166dfd221bb2/go.mod h1:lu62V//auUow6k0IykxLK2DCNW8qTmpm8KqhYVWattA=
+github.com/hashicorp/go-checkpoint v0.5.0/go.mod h1:7nfLNL10NsxqO4iWuW6tWW0HjZuDrwkBuEQsVcpCOgg=
+github.com/hashicorp/go-cleanhttp v0.5.0 h1:wvCrVc9TjDls6+YGAF2hAifE1E5U1+b4tH6KdvN3Gig=
+github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
+github.com/hashicorp/go-getter v1.3.0/go.mod h1:/O1k/AizTN0QmfEKknCYGvICeyKUDqCYA8vvWtGWDeQ=
+github.com/hashicorp/go-getter v1.3.1-0.20190627223108-da0323b9545e h1:6krcdHPiS+aIP9XKzJzSahfjD7jG7Z+4+opm0z39V1M=
+github.com/hashicorp/go-getter v1.3.1-0.20190627223108-da0323b9545e/go.mod h1:/O1k/AizTN0QmfEKknCYGvICeyKUDqCYA8vvWtGWDeQ=
+github.com/hashicorp/go-hclog v0.0.0-20180709165350-ff2cf002a8dd/go.mod h1:9bjs9uLqI8l75knNv3lV1kA55veR+WUPSiKIWcQHudI=
+github.com/hashicorp/go-hclog v0.0.0-20181001195459-61d530d6c27f/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ=
+github.com/hashicorp/go-immutable-radix v0.0.0-20180129170900-7f3cd4390caa/go.mod h1:6ij3Z20p+OhOkCSrA0gImAWoHYQRGbnlcuk6XYTiaRw=
+github.com/hashicorp/go-msgpack v0.5.4/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
+github.com/hashicorp/go-multierror v0.0.0-20180717150148-3d5d8f294aa0/go.mod h1:JMRHfdO9jKNzS/+BTlxCjKNQHg/jZAft8U7LloJvN7I=
+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-plugin v1.0.1-0.20190610192547-a1bc61569a26/go.mod h1:++UyYGoz3o5w9ZzAdZxtQKrWWP+iqPBn3cQptSMzBuY=
+github.com/hashicorp/go-retryablehttp v0.5.2/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs=
+github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=
+github.com/hashicorp/go-safetemp v1.0.0 h1:2HR189eFNrjHQyENnQMMpCiBAsRxzbTMIgBhEyExpmo=
+github.com/hashicorp/go-safetemp v1.0.0/go.mod h1:oaerMy3BhqiTbVye6QuFhFtIceqFoDHxNAB65b+Rj1I=
+github.com/hashicorp/go-slug v0.3.0/go.mod h1:I5tq5Lv0E2xcNXNkmx7BSfzi1PsJ2cNjs3cC3LwyhK8=
+github.com/hashicorp/go-sockaddr v0.0.0-20180320115054-6d291a969b86/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
+github.com/hashicorp/go-tfe v0.3.16/go.mod h1:SuPHR+OcxvzBZNye7nGPfwZTEyd3rWPfLVbCgyZPezM=
+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-version v1.1.0 h1:bPIoEKD27tNdebFGGxxYwcL4nepeY4j1QP23PFRGzg0=
+github.com/hashicorp/go-version v1.1.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
 github.com/hashicorp/golang-lru v0.0.0-20180201235237-0fb14efe8c47 h1:UnszMmmmm5vLwWzDjTFVIkfhvWF1NdrmChl8L2NUDCw=
 github.com/hashicorp/golang-lru v0.0.0-20180201235237-0fb14efe8c47/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
 github.com/hashicorp/golang-lru v0.5.0 h1:CL2msUPvZTLb5O648aiLNJw3hnBxN2+1Jq8rCOH9wdo=
 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 v0.0.0-20170504190234-a4b07c25de5f/go.mod h1:oZtUIOe8dh44I2q6ScRibXws4Ajl+d+nod3AaR9vL5w=
 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/hcl2 v0.0.0-20190821123243-0c888d1241f6 h1:JImQpEeUQ+0DPFMaWzLA0GdUNPaUlCXLpfiqkSZBUfc=
+github.com/hashicorp/hcl2 v0.0.0-20190821123243-0c888d1241f6/go.mod h1:Cxv+IJLuBiEhQ7pBYGEuORa0nr4U994pE8mYLuFd7v0=
+github.com/hashicorp/hil v0.0.0-20190212112733-ab17b08d6590 h1:2yzhWGdgQUWZUCNK+AoO35V+HTsgEmcM4J9IkArh7PI=
+github.com/hashicorp/hil v0.0.0-20190212112733-ab17b08d6590/go.mod h1:n2TSygSNwsLJ76m8qFXTSc7beTb+auJxYdqrnoqwZWE=
+github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
+github.com/hashicorp/memberlist v0.1.0/go.mod h1:ncdBp14cuox2iFOq3kDiquKU6fqsTBc3W6JvZwjxxsE=
+github.com/hashicorp/serf v0.0.0-20160124182025-e4ec8cc423bb/go.mod h1:h/Ru6tmZazX7WO/GDmwdpS975F019L4t5ng5IgwbNrE=
+github.com/hashicorp/terraform v0.12.7 h1:4pD+XguGRMGBnERUO3Wbtmv23R26ovWpGSQ2yqIL73Q=
+github.com/hashicorp/terraform v0.12.7/go.mod h1:dpIRVHTSvPpGZPDKBLEw3U7nNQaJCybZoZkeJSYxg/w=
+github.com/hashicorp/terraform-config-inspect v0.0.0-20190821133035-82a99dc22ef4/go.mod h1:JDmizlhaP5P0rYTTZB0reDMefAiJyfWPEtugV4in1oI=
+github.com/hashicorp/vault v0.10.4/go.mod h1:KfSyffbKxoVyspOdlaGVjIuwLobi07qD1bAbosPMpP0=
+github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM=
 github.com/helm/helm v2.14.2+incompatible h1:0bkkGEtnACklpGbWYBc+fHo81c4UbzpFe85YeGNZ74Y=
 github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
 github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
@@ -208,9 +352,14 @@ github.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJ
 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/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1/go.mod h1:E0B/fFc00Y+Rasa88328GlI/XbtyysCtTHZS8h7IrBU=
+github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
+github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
+github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af h1:pmfjZENx5imkbgOkpRUYLnmbU7UEFbjtDA2hxJ1ichM=
 github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
 github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
 github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
+github.com/joyent/triton-go v0.0.0-20180313100802-d8f9c0314926/go.mod h1:U+RSyWxWd04xTqnuOQxnai7XGS2PrPY2cfGoDKtMHjA=
 github.com/jpillora/backoff v0.0.0-20180909062703-3050d21c67d7/go.mod h1:2iMrUgbbvHEiQClaW2NsSzMyGHqN+rDFqY705q49KG0=
 github.com/json-iterator/go v0.0.0-20180612202835-f2b4162afba3/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
 github.com/json-iterator/go v0.0.0-20180701071628-ab8a2e0c74be h1:AHimNtVIpiBjPUhEF5KNCkrUyqTSA5zWUl8sQ2bfGBE=
@@ -219,9 +368,13 @@ github.com/json-iterator/go v1.1.5/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCV
 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/jtolds/gls v4.2.1+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
 github.com/juju/ansiterm v0.0.0-20180109212912-720a0952cc2a h1:FaWFmfWdAUKbSCtOU2QjDaorUexogfaMgbipgYATUMU=
 github.com/juju/ansiterm v0.0.0-20180109212912-720a0952cc2a/go.mod h1:UJSiEoRfvx3hP73CvoARgeLjaIOjybY9vj8PUPPFGeU=
 github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
+github.com/kardianos/osext v0.0.0-20170510131534-ae77be60afb1/go.mod h1:1NbS8ALrpOvjt0rHPNLyCIeMtbizbir8U//inJ+zuB8=
+github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0/go.mod h1:1NbS8ALrpOvjt0rHPNLyCIeMtbizbir8U//inJ+zuB8=
+github.com/keybase/go-crypto v0.0.0-20161004153544-93f5b35093ba/go.mod h1:ghbZscTyKdM07+Fw3KSi0hcJm+AlEUWj8QLlPtijN/M=
 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/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
@@ -231,12 +384,17 @@ 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/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA=
 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/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348/go.mod h1:B69LEHPfb2qLo0BaaOLcbitczOKLWTsrBG9LczfCD4k=
+github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
+github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
 github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
 github.com/lunixbochs/vtclean v0.0.0-20180621232353-2d01aacdc34a h1:weJVJJRzAJBFRlAiJQROKQs8oC9vOxvm4rZmBBk0ONw=
 github.com/lunixbochs/vtclean v0.0.0-20180621232353-2d01aacdc34a/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI=
+github.com/lusis/go-artifactory v0.0.0-20160115162124-7e4ce345df82/go.mod h1:y54tfGmO3NKssKveTEFFzH8C/akrSOy/iW9qEAUDV84=
 github.com/magiconair/properties v1.8.0 h1:LLgXmsheXeRoUOBOjtwPQCWIYqM/LU1ayDtDePerRcY=
 github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
 github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
@@ -246,11 +404,15 @@ github.com/mailru/easyjson v0.0.0-20190620125010-da37f6c1e481/go.mod h1:C1wdFJiN
 github.com/manifoldco/promptui v0.3.2 h1:rir7oByTERac6jhpHUPErHuopoRDvO3jxS+FdadEns8=
 github.com/manifoldco/promptui v0.3.2/go.mod h1:8JU+igZ+eeiiRku4T5BjtKh2ms8sziGpSYl1gN8Bazw=
 github.com/markbates/inflect v1.0.4/go.mod h1:1fR9+pO2KHEO9ZRtto13gDwwZaAKstQzferVeWqbgNs=
+github.com/marstr/guid v1.1.0/go.mod h1:74gB1z2wpxxInTG6yaqA7KrtM0NZ+RbrcqDvYHefzho=
+github.com/masterzen/simplexml v0.0.0-20160608183007-4572e39b1ab9/go.mod h1:kCEbxUJlNDEBNbdQMkPSp6yaKcRXVI6f4ddk8Riv4bc=
+github.com/masterzen/winrm v0.0.0-20190223112901-5e5c9a7fe54b/go.mod h1:wr1VqkwW0AB5JS0QLy5GpVMS9E3VtRoSYXUYyVk46KY=
 github.com/mattn/go-colorable v0.0.9 h1:UVL0vNpWh04HeJXV0KLcaT7r06gOH2l4OW6ddYRUIY4=
 github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
 github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ=
 github.com/mattn/go-colorable v0.1.2 h1:/bC9yWikZXAL9uJdulbSfyVNIR3n3trXl+v8+1sx8mU=
 github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
+github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
 github.com/mattn/go-isatty v0.0.4 h1:bnP0vzxcAdeI1zdubAl5PjU6zsERjGZb7raWodagDYs=
 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=
@@ -260,16 +422,39 @@ github.com/mattn/go-isatty v0.0.8 h1:HLtExJ+uU2HOZ+wI0Tt5DtUDrx8yhUqDcp7fYERX4CE
 github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
 github.com/mattn/go-runewidth v0.0.2 h1:UnlwIPBGaTZfPQ6T1IGzPI0EkYAQmT9fAEJ/poFC63o=
 github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
+github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
+github.com/mattn/go-shellwords v1.0.4/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o=
 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/mholt/archiver v3.1.1+incompatible h1:1dCVxuqs0dJseYEhi5pl7MYPH9zDa1wBi7mF09cbNkU=
 github.com/mholt/archiver v3.1.1+incompatible/go.mod h1:Dh2dOXnSdiLxRiPoVfIr/fI1TwETms9B8CTWfeh7ROU=
+github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4=
+github.com/miekg/dns v1.0.8/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/colorstring v0.0.0-20190213212951-d06e56a500db/go.mod h1:l0dey0ia/Uv7NcFFVbCLtqEBQbrT4OCwCSKTEv6enCw=
+github.com/mitchellh/copystructure v1.0.0 h1:Laisrj+bAB6b/yJwB5Bt3ITZhGJdqmxquMKeZ+mmkFQ=
+github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw=
+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-linereader v0.0.0-20190213213312-1b945b3263eb/go.mod h1:OaY7UOoTkkrX3wRwjpYRKafIkkyeD0UtweSHAWWiqQM=
+github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
+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 v0.0.0-20150314170334-ad45545899c7 h1:DpOJ2HYzCv8LZP15IdmG+YdwD2luVPHITV96TkirNBM=
 github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo=
+github.com/mitchellh/go-wordwrap v1.0.0 h1:6GlHJ/LTGMrIJbwgdqdl2eEH8o+Exx/0m8ir9Gns0u4=
+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 v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=
 github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
+github.com/mitchellh/panicwrap v0.0.0-20190213213626-17011010aaa4/go.mod h1:YYMf4xtQnR8LRC0vKi3afvQ5QwRPQ17zjcpkBCufb+I=
+github.com/mitchellh/prefixedio v0.0.0-20190213213902-5733675afd51/go.mod h1:kB1naBgV9ORnkiTVeyJOI1DavaJkG4oNIq0Af6ZVKUo=
+github.com/mitchellh/reflectwalk v1.0.0 h1:9D+8oIskB4VJBN5SFlmc27fSlIBZaov1Wpk/IfikLNY=
+github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
 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=
@@ -277,25 +462,38 @@ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lN
 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/mozilla/tls-observatory v0.0.0-20190404164649-a3c1b6cfecfd/go.mod h1:SrKMQvPiws7F7iqYp8/TX+IhxCYhzr6N/1yb8cwHsGk=
+github.com/mreiferson/go-httpclient v0.0.0-20160630210159-31f0106b4474/go.mod h1:OQA4XLvDbMgS8P0CevmM4m9Q3Jq4phKUzcocxuGJ5m8=
 github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
 github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
 github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw=
 github.com/nbutton23/zxcvbn-go v0.0.0-20180912185939-ae427f1e4c1d h1:AREM5mwr4u1ORQBMvzfzBgpsctsbQikCVpvC+tX285E=
 github.com/nbutton23/zxcvbn-go v0.0.0-20180912185939-ae427f1e4c1d/go.mod h1:o96djdrsSGy3AWPyBgZMAGfxZNfgntdJG+11KU4QvbU=
+github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo=
+github.com/neelance/sourcemap v0.0.0-20151028013722-8c68805598ab/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM=
 github.com/nsf/termbox-go v0.0.0-20190121233118-02980233997d h1:x3S6kxmy49zXVVyhcnrFqxvNVCBPb2KZ9hV2RBdS840=
 github.com/nsf/termbox-go v0.0.0-20190121233118-02980233997d/go.mod h1:IuKpRQcYE1Tfu+oAQqaLisqDeXgjyyltCfsaoYN18NQ=
+github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d/go.mod h1:YUTz3bUH2ZwIWBy3CJBeOBEugqcmXREj14T+iG/4k4U=
 github.com/nwaples/rardecode v1.0.0 h1:r7vGuS5akxOnR4JQSkko62RJ1ReCMXxQRPtxsiFMBOs=
 github.com/nwaples/rardecode v1.0.0/go.mod h1:5DzqNKiOdpKKBH87u8VlvAnPZMXcGRhxWkRpHbbfGS0=
+github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA=
 github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
 github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
 github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
+github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
 github.com/onsi/ginkgo v1.8.0 h1:VkHVNpR4iVnU8XQR6DBm8BqYjN7CRzw+xKUbVVbbW9w=
 github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
 github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
 github.com/onsi/gomega v0.0.0-20190113212917-5533ce8a0da3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
 github.com/onsi/gomega v1.4.2/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
+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/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk=
+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/packer-community/winrmcp v0.0.0-20180102160824-81144009af58/go.mod h1:f6Izs6JvFTdnRbziASagjZ2vmf55NSIkC/weStxCHqk=
+github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
 github.com/pborman/uuid v0.0.0-20170612153648-e790cca94e6c h1:MUyE44mTvnI5A0xrxIxaMqoWFzPfQvtE2IWUollMDMs=
 github.com/pborman/uuid v0.0.0-20170612153648-e790cca94e6c/go.mod h1:VyrYX9gd7irzKovcSS6BIIEwPRkP2Wm2m9ufcdFSJ34=
 github.com/pborman/uuid v0.0.0-20180906182336-adf5a7427709 h1:zNBQb37RGLmJybyMcs983HfUfpkw9OTFD9tbBfAViHE=
@@ -318,18 +516,25 @@ 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/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
+github.com/posener/complete v1.2.1 h1:LrvDIY//XNo65Lq84G/akBuMGlawHvGBABv8f/ZN6DI=
+github.com/posener/complete v1.2.1/go.mod h1:6gapUrK/U1TAN7ciCoNRIdVC5sbdBTUh1DKN0g6uH7E=
+github.com/prometheus/client_golang v0.8.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
 github.com/prometheus/client_golang v0.9.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.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs=
 github.com/prometheus/client_golang v0.9.3 h1:9iH4JKXLzFbOAdtqv/a+j8aewx2Y8lAjAydhbaScPF8=
 github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
 github.com/prometheus/client_golang v0.9.4/go.mod h1:oCXIBxdI62A4cR6aTRJCgetEjecSIYzOEaeAn4iYEpM=
 github.com/prometheus/client_golang v1.0.0 h1:vrDKnkGzuGvhNAL56c7DBz29ZL+KxnoR0x7enabFceM=
 github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
 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-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
+github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
 github.com/prometheus/common v0.4.0 h1:7etb9YClo3a6HjLzfl6rIQaU+FDfi0VSX39io3aQ+DM=
 github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
 github.com/prometheus/common v0.4.1 h1:K0MGApIoQvMw27RTdJkPbr3JZ7DNbtxQNyi5STVM6Kw=
@@ -338,6 +543,7 @@ github.com/prometheus/common v0.6.0 h1:kRhiuYSXR3+uv2IbVbZhUxK5zVD/2pp3Gd2PpvPkp
 github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc=
 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-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
 github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084 h1:sofwID9zm4tzrgykg80hfFph1mryUeLRsUfoocVVmRY=
 github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
 github.com/prometheus/procfs v0.0.2 h1:6LJUbpNm42llc4HRCuvApCSWB/WfhuNo9K98Q9sNGfs=
@@ -345,7 +551,12 @@ github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsT
 github.com/prometheus/procfs v0.0.3 h1:CTwfnzjQ+8dS6MhHHu4YswVAD99sL2wjPqP+VkURmKE=
 github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ=
 github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
+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/replicatedhq/libyaml v0.0.0-20190513182811-dcb60811454e h1:RXdwHs9LU87NnosFc73hsx/qrsQ1GmP/CpgF+hJv1Yw=
+github.com/replicatedhq/libyaml v0.0.0-20190513182811-dcb60811454e/go.mod h1:YMOt2+bUJu3C4Ix+SVu8KGYHOO4ohgeYfN6M9W3IHFQ=
+github.com/replicatedhq/troubleshoot v0.9.5 h1:NZ3H5Va8qQn50efrICx8FcuypX9HvvVknw+V8+dPhV8=
+github.com/replicatedhq/troubleshoot v0.9.5/go.mod h1:Lj5wzI89B9ZKvfMkJYUWbZmcIPPd83N62gKog1RBuWA=
 github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
 github.com/rogpeppe/fastuuid v1.1.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
 github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
@@ -353,18 +564,48 @@ github.com/rogpeppe/go-internal v1.2.2/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFR
 github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
 github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
 github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc=
+github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
 github.com/schemahero/schemahero v0.1.0 h1:qQ6fkozo/9TpCbyFMuoto0JB4SabLjY7QFonm61Vk/Y=
+github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
 github.com/securego/gosec v0.0.0-20190709033609-4b59c948083c h1:e77ClRtOeEikSJwd2kx9yHx8Q7S4gTKIMlYB7J9LlCA=
 github.com/securego/gosec v0.0.0-20190709033609-4b59c948083c/go.mod h1:shk+oGa7JTGg9taMxXk2skTwpt9KQAbryuwFIHCm/fw=
 github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
+github.com/shurcooL/component v0.0.0-20170202220835-f88ec8f54cc4/go.mod h1:XhFIlyj5a1fBNx5aJTbKoIq0mNaPvOagO+HjB3EtxrY=
+github.com/shurcooL/events v0.0.0-20181021180414-410e4ca65f48/go.mod h1:5u70Mqkb5O5cxEA8nxTsgrgLehJeAw6Oc4Ab1c/P1HM=
+github.com/shurcooL/github_flavored_markdown v0.0.0-20181002035957-2122de532470/go.mod h1:2dOwnU2uBioM+SGy2aZoq1f/Sd1l9OkAeAUvjSyvgU0=
+github.com/shurcooL/go v0.0.0-20180423040247-9e1955d9fb6e/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk=
+github.com/shurcooL/go-goon v0.0.0-20170922171312-37c2f522c041/go.mod h1:N5mDOmsrJOB+vfqUK+7DmDyjhSLIIBnXo9lvZJj3MWQ=
+github.com/shurcooL/gofontwoff v0.0.0-20180329035133-29b52fc0a18d/go.mod h1:05UtEgK5zq39gLST6uB0cf3NEHjETfB4Fgr3Gx5R9Vw=
+github.com/shurcooL/gopherjslib v0.0.0-20160914041154-feb6d3990c2c/go.mod h1:8d3azKNyqcHP1GaQE/c6dDgjkgSx2BZ4IoEi4F1reUI=
+github.com/shurcooL/highlight_diff v0.0.0-20170515013008-09bb4053de1b/go.mod h1:ZpfEhSmds4ytuByIcDnOLkTHGUI6KNqRNPDLHDk+mUU=
+github.com/shurcooL/highlight_go v0.0.0-20181028180052-98c3abbbae20/go.mod h1:UDKB5a1T23gOMUJrI+uSuH0VRDStOiUVSjBTRDVBVag=
+github.com/shurcooL/home v0.0.0-20181020052607-80b7ffcb30f9/go.mod h1:+rgNQw2P9ARFAs37qieuu7ohDNQ3gds9msbT2yn85sg=
+github.com/shurcooL/htmlg v0.0.0-20170918183704-d01228ac9e50/go.mod h1:zPn1wHpTIePGnXSHpsVPWEktKXHr6+SS6x/IKRb7cpw=
+github.com/shurcooL/httperror v0.0.0-20170206035902-86b7830d14cc/go.mod h1:aYMfkZ6DWSJPJ6c4Wwz3QtW22G7mf/PEgaB9k/ik5+Y=
+github.com/shurcooL/httpfs v0.0.0-20171119174359-809beceb2371/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg=
+github.com/shurcooL/httpgzip v0.0.0-20180522190206-b1c53ac65af9/go.mod h1:919LwcH0M7/W4fcZ0/jy0qGght1GIhqyS/EgWGH2j5Q=
+github.com/shurcooL/issues v0.0.0-20181008053335-6292fdc1e191/go.mod h1:e2qWDig5bLteJ4fwvDAc2NHzqFEthkqn7aOZAOpj+PQ=
+github.com/shurcooL/issuesapp v0.0.0-20180602232740-048589ce2241/go.mod h1:NPpHK2TI7iSaM0buivtFUc9offApnI0Alt/K8hcHy0I=
+github.com/shurcooL/notifications v0.0.0-20181007000457-627ab5aea122/go.mod h1:b5uSkrEVM1jQUspwbixRBhaIjIzL2xazXp6kntxYle0=
+github.com/shurcooL/octicon v0.0.0-20181028054416-fa4f57f9efb2/go.mod h1:eWdoE5JD4R5UVWDucdOPg1g2fqQRq78IQa9zlOV1vpQ=
+github.com/shurcooL/reactions v0.0.0-20181006231557-f2e0b4ca5b82/go.mod h1:TCR1lToEk4d2s07G3XGfz2QrgHXg4RJBvjrOozvoWfk=
+github.com/shurcooL/sanitized_anchor_name v0.0.0-20170918181015-86672fcb3f95/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
+github.com/shurcooL/users v0.0.0-20180125191416-49c67e49c537/go.mod h1:QJTqeLYEDaXHZDBsXlPCDqdhQuJkuw4NOtaxYe3xii4=
+github.com/shurcooL/webdavfs v0.0.0-20170829043945-18c3829fa133/go.mod h1:hKmq5kWdCj2z2KEozexVbfEZIWiTjhE0+UjmZgPqehw=
 github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
+github.com/sirupsen/logrus v1.3.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
+github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
 github.com/smartystreets/assertions v1.0.0/go.mod h1:kHHU4qYBaI3q23Pp3VPrmWhuIUrLW/7eUrw0BU5VaoM=
 github.com/smartystreets/go-aws-auth v0.0.0-20180515143844-0c1422d1fdb9/go.mod h1:SnhjPscd9TpLiy1LpzGSKh3bXCfxxXuqd9xmQJy3slM=
+github.com/smartystreets/goconvey v0.0.0-20180222194500-ef6db91d284a/go.mod h1:XDJAKZRPZ1CvBcN2aX5YOUTYGHki24fSF0Iv48Ibg0s=
 github.com/smartystreets/gunit v1.0.0/go.mod h1:qwPWnhz6pn0NnRBP++URONOVyNkPyr4SauJk4cUOwJs=
 github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
+github.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d/go.mod h1:UdhH50NIW0fCiwBSr0co2m7BnFLdv4fQTgdqdJTHFeE=
+github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e/go.mod h1:HuIsMU8RRBOtsCgI77wP899iHVBQpCmg4ErYMZB+2IA=
 github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
 github.com/spf13/afero v1.1.2 h1:m8/z1t7/fwjysjQRYbP0RD+bUIF/8tJwPdEZsI83ACI=
 github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
+github.com/spf13/afero v1.2.1/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
 github.com/spf13/afero v1.2.2 h1:5jhuqJyZCZf2JRofRvN/nIFgIWNzPa3/Vz8mYylgbWc=
 github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
 github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8=
@@ -393,24 +634,53 @@ github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1
 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/svanharmelen/jsonapi v0.0.0-20180618144545-0c0828c3f16d/go.mod h1:BSTlc8jOjh0niykqEGVXOLXdi9o0r0kR8tCYiMvjFgw=
+github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA=
+github.com/terraform-providers/terraform-provider-openstack v1.15.0/go.mod h1:2aQ6n/BtChAl1y2S60vebhyJyZXBsuAI5G4+lHrT1Ew=
 github.com/tj/assert v0.0.0-20171129193455-018094318fb0/go.mod h1:mZ9/Rh9oLWpLLDRpvE+3b7gP/C2YyLFYxNmcLnPTMe0=
 github.com/tj/go-elastic v0.0.0-20171221160941-36157cbbebc2/go.mod h1:WjeM0Oo1eNAjXGDx2yma7uG2XoyRZTq1uv3M/o7imD0=
 github.com/tj/go-kinesis v0.0.0-20171128231115-08b17f58cb1b/go.mod h1:/yhzCV0xPfx6jb1bBgRFjl5lytqVqZXEaeqWP8lTEao=
 github.com/tj/go-spin v1.1.0 h1:lhdWZsvImxvZ3q1C5OIB7d72DuOwP4O2NdBg9PyzNds=
 github.com/tj/go-spin v1.1.0/go.mod h1:Mg1mzmePZm4dva8Qz60H2lHwmJ2loum4VIrLgVnKwh4=
+github.com/tmc/grpc-websocket-proxy v0.0.0-20171017195756-830351dc03c6/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
 github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
 github.com/tsenart/deadcode v0.0.0-20160724212837-210d2dc333e9/go.mod h1:q+QjxYvZ+fpjMXqs+XEriussHjSYqeXVnAdSV1tkMYk=
+github.com/ugorji/go v0.0.0-20180813092308-00b869d2f4a5/go.mod h1:hnLbHMwcvSihnDhEfx2/BzKp2xb0Y+ErdfYcrs9tkJQ=
 github.com/ugorji/go v1.1.4 h1:j4s+tAvLfL3bZyefP2SEWmhBzmuIlH/eqNuPdFPgngw=
 github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
 github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
+github.com/ulikunitz/xz v0.5.5/go.mod h1:2bypXElzHzzJZwzH67Y6wb67pO62Rzfn7BSiF4ABRW8=
 github.com/ulikunitz/xz v0.5.6 h1:jGHAfXawEGZQ3blwU5wnWKQJvAraT7Ftq9EXjnXYgt8=
 github.com/ulikunitz/xz v0.5.6/go.mod h1:2bypXElzHzzJZwzH67Y6wb67pO62Rzfn7BSiF4ABRW8=
+github.com/vmihailenco/msgpack v3.3.3+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk=
+github.com/vmihailenco/msgpack v4.0.1+incompatible h1:RMF1enSPeKTlXrXdOcqjFUElywVZjjC6pqse21bKbEU=
+github.com/vmihailenco/msgpack v4.0.1+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk=
+github.com/weppos/publicsuffix-go v0.4.0 h1:YSnfg3V65LcCFKtIGKGoBhkyKolEd0hlipcXaOjdnQw=
+github.com/weppos/publicsuffix-go v0.4.0/go.mod h1:z3LCPQ38eedDQSwmsSRW4Y7t2L8Ln16JPQ02lHAdn5k=
+github.com/xanzy/ssh-agent v0.2.1/go.mod h1:mLlQY/MoOhWBj+gOGMQkOeiEvkx+8pJSI+0Bx9h2kr4=
 github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 h1:nIPpBwaJSVYIxUFsDv3M8ofmx9yWTog9BfvIu0q41lo=
 github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8/go.mod h1:HUYIGzjTL3rfEspMxjDjgmT5uz5wzYJKVo23qUhYTos=
+github.com/xiang90/probing v0.0.0-20160813154853-07dd2e8dfe18/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
 github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
+github.com/xlab/treeprint v0.0.0-20161029104018-1d6e34225557/go.mod h1:ce1O1j6UtZfjr22oyGxGLbauSBp2YVXpARAosm7dHBg=
 github.com/xo/dburl v0.0.0-20190203050942-98997a05b24f/go.mod h1:g6rdekR8vgfVZrkLWfobLTm0kVez7GAN23mWtkGCJ14=
 github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
+github.com/zclconf/go-cty v1.0.0/go.mod h1:xnAOWiHeOqg2nWS62VtQ7pbOu17FtxJNW8RLEih+O3s=
+github.com/zclconf/go-cty v1.0.1-0.20190708163926-19588f92a98f h1:sq2p8SN6ji66CFEQFIWLlD/gFmGtr5hBrOzv5nLlGfA=
+github.com/zclconf/go-cty v1.0.1-0.20190708163926-19588f92a98f/go.mod h1:xnAOWiHeOqg2nWS62VtQ7pbOu17FtxJNW8RLEih+O3s=
+github.com/zclconf/go-cty-yaml v1.0.1 h1:up11wlgAaDvlAGENcFDnZgkn0qUJurso7k6EpURKNF8=
+github.com/zclconf/go-cty-yaml v1.0.1/go.mod h1:IP3Ylp0wQpYm50IHK8OZWKMu6sPJIUgKa8XhiVHura0=
+github.com/zmap/rc2 v0.0.0-20131011165748-24b9757f5521/go.mod h1:3YZ9o3WnatTIZhuOtot4IcUfzoKVjUHqu6WALIyI0nE=
+github.com/zmap/zcertificate v0.0.0-20180516150559-0e3d58b1bac4/go.mod h1:5iU54tB79AMBcySS0R2XIyZBAVmeHranShAFELYx7is=
+github.com/zmap/zcrypto v0.0.0-20190729165852-9051775e6a2e h1:mvOa4+/DXStR4ZXOks/UsjeFdn5O5JpLUtzqk9U8xXw=
+github.com/zmap/zcrypto v0.0.0-20190729165852-9051775e6a2e/go.mod h1:w7kd3qXHh8FNaczNjslXqvFQiv5mMWRXlL9klTUAHc8=
+github.com/zmap/zlint v1.0.0 h1:+1ZVHTl5iQv32erzzAwqkEgVJ83mljFTfxCeIE2Lx8E=
+github.com/zmap/zlint v1.0.0/go.mod h1:29UiAJNsiVdvTBFCJW8e3q6dcDbOoPkhMgttOSCIMMY=
 go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
+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.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
 go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
 go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
@@ -421,11 +691,17 @@ go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/
 go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
 go.uber.org/zap v1.10.0 h1:ORx85nbTijNz8ljznvCMR1ZBIPKFn3jQrag10X2AsuM=
 go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
+go4.org v0.0.0-20180809161055-417644f6feb5/go.mod h1:MkTOUMDaeVYJUOUsaDXIhWPZYa1yOyC1qaOBpL57BhE=
 golang.org/x/arch v0.0.0-20181203225421-5a4828bb7045/go.mod h1:cYlCBUl1MsqxdiKgmc4uh7TxZfWSFLOGSRR090WDxt8=
+golang.org/x/build v0.0.0-20190111050920-041ab4dc3f9d/go.mod h1:OWs+y06UdEOHN4y+MfF/py+xQ/tYqIWW03b70/CG9Rw=
 golang.org/x/crypto v0.0.0-20180820150726-614d502a4dac/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
 golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
 golang.org/x/crypto v0.0.0-20181025213731-e84da0312774/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
+golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
+golang.org/x/crypto v0.0.0-20181112202954-3d3f9f413869/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
 golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
+golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
+golang.org/x/crypto v0.0.0-20190222235706-ffb98f73852f/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
 golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M=
 golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
 golang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
@@ -439,6 +715,7 @@ golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL
 golang.org/x/exp v0.0.0-20190312203227-4b39c73a6495/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
 golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/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=
@@ -447,17 +724,22 @@ golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHl
 golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
 golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20180811021610-c39426892332/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20181029044818-c44066c5c816/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20181106065722-10aee1819953/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20190206173232-65e2d4e15006/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
 golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
 golang.org/x/net v0.0.0-20190420063019-afa5a82059c6/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
 golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190502183928-7f726cade0ab/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
 golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
 golang.org/x/net v0.0.0-20190522155817-f3200d17e092 h1:4QSRKanuywn15aTZvI/mIDEgPQpswuFndXpOj3rKEco=
 golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
@@ -470,26 +752,34 @@ golang.org/x/net v0.0.0-20190628185345-da137c7871d7 h1:rTIdg5QFRR7XCaK4LCjBiPbx8
 golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be h1:vEDujvNQGv4jgYKudGeI/+DAX4Jffq6hpD55MmoEvKs=
 golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
+golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/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/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
 golang.org/x/oauth2 v0.0.0-20190402181905-9f3314589c9a h1:tImsplftrFpALCYumobsd0K86vlAs/eXGFms2txfJfA=
 golang.org/x/oauth2 v0.0.0-20190402181905-9f3314589c9a/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
 golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 h1:SVwTIAaPC2U/AvvLNZ2a7OVsmBpC8L5BlwK1whH3hm0=
 golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
+golang.org/x/perf v0.0.0-20180704124530-6e6d33e29852/go.mod h1:JLpeXjPJfIyPr5TlbXLkXWLhP8nz10XfvxElABhCtcw=
 golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sys v0.0.0-20170830134202-bb24a47a89ea/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20181029174526-d69651ed3497/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20181213200352-4d1cda033e06/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190129075346-302c3dd5f1cc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU=
 golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190221075227-b4e8571b14e0/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20190312061237-fead79001313 h1:pczuHS43Cp2ktBEEmLwScxgjWsBSzdaQiKzUyf3DTTc=
 golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -498,6 +788,7 @@ golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7w
 golang.org/x/sys v0.0.0-20190429190828-d89cdac9e872 h1:cGjJzUd8RgBw428LXP65YXni0aiGNA4Bl+ls8SmLOm8=
 golang.org/x/sys v0.0.0-20190429190828-d89cdac9e872/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190502175342-a43fa875dd82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f h1:25KHgbfyiSm6vwQLbM3zZIe1v9p/3ea4Rz+nnM5K/i4=
@@ -521,8 +812,10 @@ golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxb
 golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 h1:SvFZT6jyqRaOeXpc5h/JSfZenJ2O330aBsf7JfSUXmQ=
 golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 golang.org/x/tools v0.0.0-20181011042414-1f849cf54d09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20181030000716-a0a13e073c7b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 golang.org/x/tools v0.0.0-20181122213734-04b5d21e00f1/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 golang.org/x/tools v0.0.0-20190206041539-40960b6deb8e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
@@ -543,20 +836,36 @@ golang.org/x/tools v0.0.0-20190703212419-2214986f1668/go.mod h1:jcCCGcm9btYwXyDq
 gonum.org/v1/gonum v0.0.0-20190331200053-3d26580ed485/go.mod h1:2ltnJ7xHfj0zHS40VVPYEAAMTa3ZGguvHGBSJeRWqE0=
 gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw=
 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.0.0-20181030000543-1d582fd0359e/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
+google.golang.org/api v0.1.0/go.mod h1:UGEZY7KEX120AnNLIHFMKIo4obdJhkp2tPbaPlQx13Y=
+google.golang.org/api v0.3.1 h1:oJra/lMfmtm13/rgY/8i3MzjFWYXvQIAKjQ3HqofMk8=
+google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk=
 google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
 google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
 google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
+google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
+google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
 google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
 google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
 google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
 google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8 h1:Nw54tB0rB7hY/N0NQvRW8DG4Yk3Q6T9cu9RcFQDu1tc=
 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-20181029155118-b69ba1387ce2/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
+google.golang.org/genproto v0.0.0-20181202183823-bd91e49a0898/go.mod h1:7Ep/1NZk928CDR8SjdVbjWNpdIf6nzjE3BTgJDr2Atg=
+google.golang.org/genproto v0.0.0-20190201180003-4b09977fb922/go.mod h1:L3J43x8/uS+qIUoksaLKe6OS3nUKxOKuIFz1sl2/jx4=
+google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19 h1:Lj2SnHtxkRGJDqnGaSjo+CCdIieEnwVazbOXILwQemk=
 google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
 google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
 google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
 google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
 google.golang.org/genproto v0.0.0-20190626174449-989357319d63/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s=
+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/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
 google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
 google.golang.org/grpc v1.21.0 h1:G+97AoqBnmZIT91cLG/EkCoK9NSelj64P8bOHHNmGn0=
 google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
@@ -565,6 +874,7 @@ gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLks
 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=
 gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/cheggaaa/pb.v1 v1.0.27/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw=
 gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
 gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
 gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
@@ -575,6 +885,9 @@ gopkg.in/inf.v0 v0.9.0 h1:3zYtXIO92bvsdS3ggAdA8Gb4Azj0YU+TVY1uGYNFA8o=
 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/ini.v1 v1.42.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
+gopkg.in/laverya/yaml.v3 v3.0.0-beta3 h1:LBRobUz9D6nMyfyoE42elUYIaKpUhmNzYcIhOKdEuEc=
+gopkg.in/laverya/yaml.v3 v3.0.0-beta3/go.mod h1:b5w3YJ/R5MxUkx01JYEDFaW9XiYHxEm6iaCHIN+jaRE=
 gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
 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=
@@ -582,9 +895,14 @@ gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bl
 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=
+gopkg.in/yaml.v3 v3.0.0-20190709130402-674ba3eaed22 h1:0efs3hwEZhFKsCoP8l6dDB1AZWMgnEl3yWXWRZTOaEA=
+gopkg.in/yaml.v3 v3.0.0-20190709130402-674ba3eaed22/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+grpc.go4.org v0.0.0-20170609214715-11d0a25b4919/go.mod h1:77eQGdRu53HpSqPFJFmuJdjuHRquDANNeA4x7B8WQ9o=
+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=
 honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
+howett.net/plist v0.0.0-20181124034731-591f970eefbb/go.mod h1:vMygbs4qMhSZSc4lCUl2OEE+rDiIIJAIdR4m7MiMcm0=
 k8s.io/api v0.0.0-20190222213804-5cb15d344471 h1:MzQGt8qWQCR+39kbYRd0uQqsvSidpYqJLFeWiJ9l4OE=
 k8s.io/api v0.0.0-20190222213804-5cb15d344471/go.mod h1:iuAfoD4hCxJ8Onx9kaTIt30j7jUFS00AXQi6QMi99vA=
 k8s.io/api v0.0.0-20190313235455-40a48860b5ab/go.mod h1:iuAfoD4hCxJ8Onx9kaTIt30j7jUFS00AXQi6QMi99vA=
@@ -678,3 +996,5 @@ sigs.k8s.io/testing_frameworks v0.1.1 h1:cP2l8fkA3O9vekpy5Ks8mmA0NW/F7yBdXf8brkW
 sigs.k8s.io/testing_frameworks v0.1.1/go.mod h1:VVBKrHmJ6Ekkfz284YKhQePcdycOzNH9qL6ht1zEr/U=
 sigs.k8s.io/yaml v1.1.0 h1:4A07+ZFc2wgJwo8YNlQpr1rVlgUDlxXHhPJciaPY5gs=
 sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
+sourcegraph.com/sourcegraph/go-diff v0.5.0/go.mod h1:kuch7UrkMzY0X+p9CRK03kfuPQ2zzQcaEFbx8wA8rck=
+sourcegraph.com/sqs/pbtypes v0.0.0-20180604144634-d3ebe8f20ae4/go.mod h1:ketZ/q3QxT9HOBeFhu6RdvsftgpsbFHBF5Cas6cDKZ0=
diff --git a/kotskinds/Makefile b/kotskinds/Makefile
index 6d2b31dc5..4ad7ff31b 100644
--- a/kotskinds/Makefile
+++ b/kotskinds/Makefile
@@ -3,8 +3,15 @@ export GO111MODULE=on
 
 .PHONY: generate
 generate: controller-gen client-gen
-	controller-gen object:headerFile=./hack/boilerplate.go.txt paths=./apis/...
-	client-gen --output-package=github.com/replicatedhq/kots/kotskinds/client --clientset-name kotsclientset --input-base github.com/replicatedhq/kots/kotskinds/apis --input kots/v1beta1 -h ./hack/boilerplate.go.txt
+	controller-gen \
+		object:headerFile=./hack/boilerplate.go.txt \
+		paths=./apis/...
+	client-gen \
+		--output-package=github.com/replicatedhq/kots/kotskinds/client \
+		--clientset-name kotsclientset \
+		--input-base github.com/replicatedhq/kots/kotskinds/apis \
+		--input kots/v1beta1 \
+		-h ./hack/boilerplate.go.txt
 
 # find or download controller-gen
 # download controller-gen if necessary
diff --git a/kotskinds/README.md b/kotskinds/README.md
index e69de29bb..d5f552007 100644
--- a/kotskinds/README.md
+++ b/kotskinds/README.md
@@ -0,0 +1,7 @@
+# kotskinds
+
+This directory contains the definitions and clientsets for the kots.io kinds. These aren't CRDs and controllers, but are implemented as normal Kubernetes objects. This allows us to use the client-go and other functionality to parse and ensure conformance.
+
+## Building
+
+To build these, simply run `make` in this directory. This will execute the kubernetes controller-gen and client-gen code to generate a typed client and the necessary deepcopy methods for these types.
diff --git a/kotskinds/apis/kots/v1beta1/application_types.go b/kotskinds/apis/kots/v1beta1/application_types.go
new file mode 100644
index 000000000..60790b5d8
--- /dev/null
+++ b/kotskinds/apis/kots/v1beta1/application_types.go
@@ -0,0 +1,57 @@
+/*
+Copyright 2019 Replicated, Inc..
+
+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.
+*/
+
+package v1beta1
+
+import (
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+)
+
+// ApplicationSpec defines the desired state of ApplicationSpec
+type ApplicationSpec struct {
+	Title string `json:"title"`
+	Icon  string `json:"icon,omitempty"`
+}
+
+// ApplicationStatus defines the observed state of Application
+type ApplicationStatus struct {
+}
+
+// +genclient
+// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
+// Application is the Schema for the application API
+// +k8s:openapi-gen=true
+// +kubebuilder:subresource:status
+type Application struct {
+	metav1.TypeMeta   `json:",inline"`
+	metav1.ObjectMeta `json:"metadata,omitempty"`
+
+	Spec   ApplicationSpec   `json:"spec,omitempty"`
+	Status ApplicationStatus `json:"status,omitempty"`
+}
+
+// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
+
+// ApplicationList contains a list of Applications
+type ApplicationList struct {
+	metav1.TypeMeta `json:",inline"`
+	metav1.ListMeta `json:"metadata,omitempty"`
+	Items           []Application `json:"items"`
+}
+
+func init() {
+	SchemeBuilder.Register(&Application{}, &ApplicationList{})
+}
diff --git a/kotskinds/apis/kots/v1beta1/config_types.go b/kotskinds/apis/kots/v1beta1/config_types.go
new file mode 100644
index 000000000..ce586c8d7
--- /dev/null
+++ b/kotskinds/apis/kots/v1beta1/config_types.go
@@ -0,0 +1,95 @@
+/*
+Copyright 2019 Replicated, Inc..
+
+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.
+*/
+
+package v1beta1
+
+import (
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+)
+
+type ConfigChildItem struct {
+	Name        string `json:"name"`
+	Title       string `json:"title"`
+	Recommended bool   `json:"recommended,omitempty"`
+	Default     string `json:"default,omitempty"`
+	Value       string `son:"value,omitempty"`
+}
+
+type ConfigItem struct {
+	Name        string            `json:"name"`
+	Type        string            `json:"type"`
+	Title       string            `json:"title,omitempty"`
+	HelpText    string            `json:"help_text,omitempty"`
+	Recommended bool              `json:"recommended,omitempty"`
+	Default     string            `json:"default,omitempty"`
+	Value       string            `json:"value,omitempty"`
+	MultiValue  []string          `json:"multi_value,omitempty"`
+	ReadOnly    bool              `json:"readonly,omitempty"`
+	WriteOnce   bool              `json:"write_once,omitempty"`
+	When        string            `json:"when,omitempty"`
+	Multiple    bool              `json:"multiple,omitempty"`
+	Hidden      bool              `json:"hidden,omitempty"`
+	Position    int               `json:"-"`
+	Affix       string            `json:"affix,omitempty"`
+	Required    bool              `json:"required,omitempty"`
+	Items       []ConfigChildItem `json:"items,omitempty"`
+	// Props       map[string]interface{} `json:"props,omitempty"`
+	// DefaultCmd  *ConfigItemCmd         `json:"default_cmd,omitempty"`
+	// ValueCmd    *ConfigItemCmd         `json:"value_cmd,omitempty"`
+	// DataCmd     *ConfigItemCmd         `json:"data_cmd,omitempty"`
+}
+
+type ConfigGroup struct {
+	Name        string       `json:"name"`
+	Title       string       `json:"title"`
+	Description string       `json:"description,omitempty"`
+	Items       []ConfigItem `json:"items,omitempty"`
+}
+
+// ConfigSpec defines the desired state of ConfigSpec
+type ConfigSpec struct {
+	Groups []ConfigGroup `json:"groups"`
+}
+
+// ConfigStatus defines the observed state of Config
+type ConfigStatus struct {
+}
+
+// +genclient
+// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
+// Config is the Schema for the config API
+// +k8s:openapi-gen=true
+// +kubebuilder:subresource:status
+type Config struct {
+	metav1.TypeMeta   `json:",inline"`
+	metav1.ObjectMeta `json:"metadata,omitempty"`
+
+	Spec   ConfigSpec   `json:"spec,omitempty"`
+	Status ConfigStatus `json:"status,omitempty"`
+}
+
+// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
+
+// ConfigList contains a list of Configs
+type ConfigList struct {
+	metav1.TypeMeta `json:",inline"`
+	metav1.ListMeta `json:"metadata,omitempty"`
+	Items           []Config `json:"items"`
+}
+
+func init() {
+	SchemeBuilder.Register(&Config{}, &ConfigList{})
+}
diff --git a/kotskinds/apis/kots/v1beta1/app_types.go b/kotskinds/apis/kots/v1beta1/configvalues_types.go
similarity index 65%
rename from kotskinds/apis/kots/v1beta1/app_types.go
rename to kotskinds/apis/kots/v1beta1/configvalues_types.go
index 73aada00b..35008ad27 100644
--- a/kotskinds/apis/kots/v1beta1/app_types.go
+++ b/kotskinds/apis/kots/v1beta1/configvalues_types.go
@@ -20,13 +20,13 @@ import (
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 )
 
-// AppSpec defines the desired state of AppSpec
-type AppSpec struct {
-	Name string `json:"name,omitempty"`
+// ConfigValuesSpec defines the desired state of ConfigValue
+type ConfigValuesSpec struct {
+	Values map[string]string `json:"values"`
 }
 
-// AppStatus defines the observed state of App
-type AppStatus struct {
+// ConfigValuesStatus defines the observed state of ConfigValues
+type ConfigValuesStatus struct {
 }
 
 // +genclient
@@ -34,23 +34,23 @@ type AppStatus struct {
 // App is the Schema for the app API
 // +k8s:openapi-gen=true
 // +kubebuilder:subresource:status
-type App struct {
+type ConfigValues struct {
 	metav1.TypeMeta   `json:",inline"`
 	metav1.ObjectMeta `json:"metadata,omitempty"`
 
-	Spec   AppSpec   `json:"spec,omitempty"`
-	Status AppStatus `json:"status,omitempty"`
+	Spec   ConfigValuesSpec   `json:"spec,omitempty"`
+	Status ConfigValuesStatus `json:"status,omitempty"`
 }
 
 // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
 
-// AppList contains a list of Apps
-type AppList struct {
+// ApConfigValuesListpList contains a list of ConfigValues
+type ConfigValuesList struct {
 	metav1.TypeMeta `json:",inline"`
 	metav1.ListMeta `json:"metadata,omitempty"`
-	Items           []App `json:"items"`
+	Items           []ConfigValues `json:"items"`
 }
 
 func init() {
-	SchemeBuilder.Register(&App{}, &AppList{})
+	SchemeBuilder.Register(&ConfigValues{}, &ConfigValuesList{})
 }
diff --git a/kotskinds/apis/kots/v1beta1/license_types.go b/kotskinds/apis/kots/v1beta1/license_types.go
new file mode 100644
index 000000000..4d55ee3f1
--- /dev/null
+++ b/kotskinds/apis/kots/v1beta1/license_types.go
@@ -0,0 +1,59 @@
+/*
+Copyright 2019 Replicated, Inc..
+
+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.
+*/
+
+package v1beta1
+
+import (
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+)
+
+// LicenseSpec defines the desired state of LicenseSpec
+type LicenseSpec struct {
+	Signature []byte `json:"signature"`
+	AppSlug   string `json:"appSlug"`
+	Endpoint  string `json:"endpoint,omitempty"`
+	LicenseID string `json:"licenseID"`
+}
+
+// LicenseStatus defines the observed state of License
+type LicenseStatus struct {
+}
+
+// +genclient
+// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
+// License is the Schema for the license API
+// +k8s:openapi-gen=true
+// +kubebuilder:subresource:status
+type License struct {
+	metav1.TypeMeta   `json:",inline"`
+	metav1.ObjectMeta `json:"metadata,omitempty"`
+
+	Spec   LicenseSpec   `json:"spec,omitempty"`
+	Status LicenseStatus `json:"status,omitempty"`
+}
+
+// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
+
+// LicenseList contains a list of Licenses
+type LicenseList struct {
+	metav1.TypeMeta `json:",inline"`
+	metav1.ListMeta `json:"metadata,omitempty"`
+	Items           []License `json:"items"`
+}
+
+func init() {
+	SchemeBuilder.Register(&License{}, &LicenseList{})
+}
diff --git a/kotskinds/apis/kots/v1beta1/zz_generated.deepcopy.go b/kotskinds/apis/kots/v1beta1/zz_generated.deepcopy.go
index 993f8ab24..355f49a59 100644
--- a/kotskinds/apis/kots/v1beta1/zz_generated.deepcopy.go
+++ b/kotskinds/apis/kots/v1beta1/zz_generated.deepcopy.go
@@ -25,7 +25,7 @@ import (
 )
 
 // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
-func (in *App) DeepCopyInto(out *App) {
+func (in *Application) DeepCopyInto(out *Application) {
 	*out = *in
 	out.TypeMeta = in.TypeMeta
 	in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
@@ -33,18 +33,18 @@ func (in *App) DeepCopyInto(out *App) {
 	out.Status = in.Status
 }
 
-// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new App.
-func (in *App) DeepCopy() *App {
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Application.
+func (in *Application) DeepCopy() *Application {
 	if in == nil {
 		return nil
 	}
-	out := new(App)
+	out := new(Application)
 	in.DeepCopyInto(out)
 	return out
 }
 
 // DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
-func (in *App) DeepCopyObject() runtime.Object {
+func (in *Application) DeepCopyObject() runtime.Object {
 	if c := in.DeepCopy(); c != nil {
 		return c
 	}
@@ -52,31 +52,31 @@ func (in *App) DeepCopyObject() runtime.Object {
 }
 
 // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
-func (in *AppList) DeepCopyInto(out *AppList) {
+func (in *ApplicationList) DeepCopyInto(out *ApplicationList) {
 	*out = *in
 	out.TypeMeta = in.TypeMeta
 	out.ListMeta = in.ListMeta
 	if in.Items != nil {
 		in, out := &in.Items, &out.Items
-		*out = make([]App, len(*in))
+		*out = make([]Application, len(*in))
 		for i := range *in {
 			(*in)[i].DeepCopyInto(&(*out)[i])
 		}
 	}
 }
 
-// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AppList.
-func (in *AppList) DeepCopy() *AppList {
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ApplicationList.
+func (in *ApplicationList) DeepCopy() *ApplicationList {
 	if in == nil {
 		return nil
 	}
-	out := new(AppList)
+	out := new(ApplicationList)
 	in.DeepCopyInto(out)
 	return out
 }
 
 // DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
-func (in *AppList) DeepCopyObject() runtime.Object {
+func (in *ApplicationList) DeepCopyObject() runtime.Object {
 	if c := in.DeepCopy(); c != nil {
 		return c
 	}
@@ -84,31 +84,379 @@ func (in *AppList) DeepCopyObject() runtime.Object {
 }
 
 // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
-func (in *AppSpec) DeepCopyInto(out *AppSpec) {
+func (in *ApplicationSpec) DeepCopyInto(out *ApplicationSpec) {
 	*out = *in
 }
 
-// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AppSpec.
-func (in *AppSpec) DeepCopy() *AppSpec {
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ApplicationSpec.
+func (in *ApplicationSpec) DeepCopy() *ApplicationSpec {
 	if in == nil {
 		return nil
 	}
-	out := new(AppSpec)
+	out := new(ApplicationSpec)
 	in.DeepCopyInto(out)
 	return out
 }
 
 // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
-func (in *AppStatus) DeepCopyInto(out *AppStatus) {
+func (in *ApplicationStatus) DeepCopyInto(out *ApplicationStatus) {
 	*out = *in
 }
 
-// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AppStatus.
-func (in *AppStatus) DeepCopy() *AppStatus {
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ApplicationStatus.
+func (in *ApplicationStatus) DeepCopy() *ApplicationStatus {
 	if in == nil {
 		return nil
 	}
-	out := new(AppStatus)
+	out := new(ApplicationStatus)
+	in.DeepCopyInto(out)
+	return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *Config) DeepCopyInto(out *Config) {
+	*out = *in
+	out.TypeMeta = in.TypeMeta
+	in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
+	in.Spec.DeepCopyInto(&out.Spec)
+	out.Status = in.Status
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Config.
+func (in *Config) DeepCopy() *Config {
+	if in == nil {
+		return nil
+	}
+	out := new(Config)
+	in.DeepCopyInto(out)
+	return out
+}
+
+// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
+func (in *Config) 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 *ConfigChildItem) DeepCopyInto(out *ConfigChildItem) {
+	*out = *in
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ConfigChildItem.
+func (in *ConfigChildItem) DeepCopy() *ConfigChildItem {
+	if in == nil {
+		return nil
+	}
+	out := new(ConfigChildItem)
+	in.DeepCopyInto(out)
+	return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *ConfigGroup) DeepCopyInto(out *ConfigGroup) {
+	*out = *in
+	if in.Items != nil {
+		in, out := &in.Items, &out.Items
+		*out = make([]ConfigItem, len(*in))
+		for i := range *in {
+			(*in)[i].DeepCopyInto(&(*out)[i])
+		}
+	}
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ConfigGroup.
+func (in *ConfigGroup) DeepCopy() *ConfigGroup {
+	if in == nil {
+		return nil
+	}
+	out := new(ConfigGroup)
+	in.DeepCopyInto(out)
+	return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *ConfigItem) DeepCopyInto(out *ConfigItem) {
+	*out = *in
+	if in.MultiValue != nil {
+		in, out := &in.MultiValue, &out.MultiValue
+		*out = make([]string, len(*in))
+		copy(*out, *in)
+	}
+	if in.Items != nil {
+		in, out := &in.Items, &out.Items
+		*out = make([]ConfigChildItem, len(*in))
+		copy(*out, *in)
+	}
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ConfigItem.
+func (in *ConfigItem) DeepCopy() *ConfigItem {
+	if in == nil {
+		return nil
+	}
+	out := new(ConfigItem)
+	in.DeepCopyInto(out)
+	return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *ConfigList) DeepCopyInto(out *ConfigList) {
+	*out = *in
+	out.TypeMeta = in.TypeMeta
+	out.ListMeta = in.ListMeta
+	if in.Items != nil {
+		in, out := &in.Items, &out.Items
+		*out = make([]Config, len(*in))
+		for i := range *in {
+			(*in)[i].DeepCopyInto(&(*out)[i])
+		}
+	}
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ConfigList.
+func (in *ConfigList) DeepCopy() *ConfigList {
+	if in == nil {
+		return nil
+	}
+	out := new(ConfigList)
+	in.DeepCopyInto(out)
+	return out
+}
+
+// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
+func (in *ConfigList) 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 *ConfigSpec) DeepCopyInto(out *ConfigSpec) {
+	*out = *in
+	if in.Groups != nil {
+		in, out := &in.Groups, &out.Groups
+		*out = make([]ConfigGroup, len(*in))
+		for i := range *in {
+			(*in)[i].DeepCopyInto(&(*out)[i])
+		}
+	}
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ConfigSpec.
+func (in *ConfigSpec) DeepCopy() *ConfigSpec {
+	if in == nil {
+		return nil
+	}
+	out := new(ConfigSpec)
+	in.DeepCopyInto(out)
+	return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *ConfigStatus) DeepCopyInto(out *ConfigStatus) {
+	*out = *in
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ConfigStatus.
+func (in *ConfigStatus) DeepCopy() *ConfigStatus {
+	if in == nil {
+		return nil
+	}
+	out := new(ConfigStatus)
+	in.DeepCopyInto(out)
+	return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *ConfigValues) DeepCopyInto(out *ConfigValues) {
+	*out = *in
+	out.TypeMeta = in.TypeMeta
+	in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
+	in.Spec.DeepCopyInto(&out.Spec)
+	out.Status = in.Status
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ConfigValues.
+func (in *ConfigValues) DeepCopy() *ConfigValues {
+	if in == nil {
+		return nil
+	}
+	out := new(ConfigValues)
+	in.DeepCopyInto(out)
+	return out
+}
+
+// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
+func (in *ConfigValues) 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 *ConfigValuesList) DeepCopyInto(out *ConfigValuesList) {
+	*out = *in
+	out.TypeMeta = in.TypeMeta
+	out.ListMeta = in.ListMeta
+	if in.Items != nil {
+		in, out := &in.Items, &out.Items
+		*out = make([]ConfigValues, len(*in))
+		for i := range *in {
+			(*in)[i].DeepCopyInto(&(*out)[i])
+		}
+	}
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ConfigValuesList.
+func (in *ConfigValuesList) DeepCopy() *ConfigValuesList {
+	if in == nil {
+		return nil
+	}
+	out := new(ConfigValuesList)
+	in.DeepCopyInto(out)
+	return out
+}
+
+// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
+func (in *ConfigValuesList) 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 *ConfigValuesSpec) DeepCopyInto(out *ConfigValuesSpec) {
+	*out = *in
+	if in.Values != nil {
+		in, out := &in.Values, &out.Values
+		*out = make(map[string]string, len(*in))
+		for key, val := range *in {
+			(*out)[key] = val
+		}
+	}
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ConfigValuesSpec.
+func (in *ConfigValuesSpec) DeepCopy() *ConfigValuesSpec {
+	if in == nil {
+		return nil
+	}
+	out := new(ConfigValuesSpec)
+	in.DeepCopyInto(out)
+	return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *ConfigValuesStatus) DeepCopyInto(out *ConfigValuesStatus) {
+	*out = *in
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ConfigValuesStatus.
+func (in *ConfigValuesStatus) DeepCopy() *ConfigValuesStatus {
+	if in == nil {
+		return nil
+	}
+	out := new(ConfigValuesStatus)
+	in.DeepCopyInto(out)
+	return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *License) DeepCopyInto(out *License) {
+	*out = *in
+	out.TypeMeta = in.TypeMeta
+	in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
+	in.Spec.DeepCopyInto(&out.Spec)
+	out.Status = in.Status
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new License.
+func (in *License) DeepCopy() *License {
+	if in == nil {
+		return nil
+	}
+	out := new(License)
+	in.DeepCopyInto(out)
+	return out
+}
+
+// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
+func (in *License) 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 *LicenseList) DeepCopyInto(out *LicenseList) {
+	*out = *in
+	out.TypeMeta = in.TypeMeta
+	out.ListMeta = in.ListMeta
+	if in.Items != nil {
+		in, out := &in.Items, &out.Items
+		*out = make([]License, len(*in))
+		for i := range *in {
+			(*in)[i].DeepCopyInto(&(*out)[i])
+		}
+	}
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LicenseList.
+func (in *LicenseList) DeepCopy() *LicenseList {
+	if in == nil {
+		return nil
+	}
+	out := new(LicenseList)
+	in.DeepCopyInto(out)
+	return out
+}
+
+// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
+func (in *LicenseList) 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 *LicenseSpec) DeepCopyInto(out *LicenseSpec) {
+	*out = *in
+	if in.Signature != nil {
+		in, out := &in.Signature, &out.Signature
+		*out = make([]byte, len(*in))
+		copy(*out, *in)
+	}
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LicenseSpec.
+func (in *LicenseSpec) DeepCopy() *LicenseSpec {
+	if in == nil {
+		return nil
+	}
+	out := new(LicenseSpec)
+	in.DeepCopyInto(out)
+	return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *LicenseStatus) DeepCopyInto(out *LicenseStatus) {
+	*out = *in
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LicenseStatus.
+func (in *LicenseStatus) DeepCopy() *LicenseStatus {
+	if in == nil {
+		return nil
+	}
+	out := new(LicenseStatus)
 	in.DeepCopyInto(out)
 	return out
 }
diff --git a/kotskinds/client/kotsclientset/typed/kots/v1beta1/application.go b/kotskinds/client/kotsclientset/typed/kots/v1beta1/application.go
new file mode 100644
index 000000000..51ed96f5a
--- /dev/null
+++ b/kotskinds/client/kotsclientset/typed/kots/v1beta1/application.go
@@ -0,0 +1,190 @@
+/*
+Copyright 2019 Replicated, Inc..
+
+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 v1beta1
+
+import (
+	"time"
+
+	v1beta1 "github.com/replicatedhq/kots/kotskinds/apis/kots/v1beta1"
+	scheme "github.com/replicatedhq/kots/kotskinds/client/kotsclientset/scheme"
+	v1 "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"
+)
+
+// ApplicationsGetter has a method to return a ApplicationInterface.
+// A group's client should implement this interface.
+type ApplicationsGetter interface {
+	Applications(namespace string) ApplicationInterface
+}
+
+// ApplicationInterface has methods to work with Application resources.
+type ApplicationInterface interface {
+	Create(*v1beta1.Application) (*v1beta1.Application, error)
+	Update(*v1beta1.Application) (*v1beta1.Application, error)
+	UpdateStatus(*v1beta1.Application) (*v1beta1.Application, error)
+	Delete(name string, options *v1.DeleteOptions) error
+	DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error
+	Get(name string, options v1.GetOptions) (*v1beta1.Application, error)
+	List(opts v1.ListOptions) (*v1beta1.ApplicationList, error)
+	Watch(opts v1.ListOptions) (watch.Interface, error)
+	Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1beta1.Application, err error)
+	ApplicationExpansion
+}
+
+// applications implements ApplicationInterface
+type applications struct {
+	client rest.Interface
+	ns     string
+}
+
+// newApplications returns a Applications
+func newApplications(c *KotsV1beta1Client, namespace string) *applications {
+	return &applications{
+		client: c.RESTClient(),
+		ns:     namespace,
+	}
+}
+
+// Get takes name of the application, and returns the corresponding application object, and an error if there is any.
+func (c *applications) Get(name string, options v1.GetOptions) (result *v1beta1.Application, err error) {
+	result = &v1beta1.Application{}
+	err = c.client.Get().
+		Namespace(c.ns).
+		Resource("applications").
+		Name(name).
+		VersionedParams(&options, scheme.ParameterCodec).
+		Do().
+		Into(result)
+	return
+}
+
+// List takes label and field selectors, and returns the list of Applications that match those selectors.
+func (c *applications) List(opts v1.ListOptions) (result *v1beta1.ApplicationList, err error) {
+	var timeout time.Duration
+	if opts.TimeoutSeconds != nil {
+		timeout = time.Duration(*opts.TimeoutSeconds) * time.Second
+	}
+	result = &v1beta1.ApplicationList{}
+	err = c.client.Get().
+		Namespace(c.ns).
+		Resource("applications").
+		VersionedParams(&opts, scheme.ParameterCodec).
+		Timeout(timeout).
+		Do().
+		Into(result)
+	return
+}
+
+// Watch returns a watch.Interface that watches the requested applications.
+func (c *applications) Watch(opts v1.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("applications").
+		VersionedParams(&opts, scheme.ParameterCodec).
+		Timeout(timeout).
+		Watch()
+}
+
+// Create takes the representation of a application and creates it.  Returns the server's representation of the application, and an error, if there is any.
+func (c *applications) Create(application *v1beta1.Application) (result *v1beta1.Application, err error) {
+	result = &v1beta1.Application{}
+	err = c.client.Post().
+		Namespace(c.ns).
+		Resource("applications").
+		Body(application).
+		Do().
+		Into(result)
+	return
+}
+
+// Update takes the representation of a application and updates it. Returns the server's representation of the application, and an error, if there is any.
+func (c *applications) Update(application *v1beta1.Application) (result *v1beta1.Application, err error) {
+	result = &v1beta1.Application{}
+	err = c.client.Put().
+		Namespace(c.ns).
+		Resource("applications").
+		Name(application.Name).
+		Body(application).
+		Do().
+		Into(result)
+	return
+}
+
+// UpdateStatus was generated because the type contains a Status member.
+// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus().
+
+func (c *applications) UpdateStatus(application *v1beta1.Application) (result *v1beta1.Application, err error) {
+	result = &v1beta1.Application{}
+	err = c.client.Put().
+		Namespace(c.ns).
+		Resource("applications").
+		Name(application.Name).
+		SubResource("status").
+		Body(application).
+		Do().
+		Into(result)
+	return
+}
+
+// Delete takes name of the application and deletes it. Returns an error if one occurs.
+func (c *applications) Delete(name string, options *v1.DeleteOptions) error {
+	return c.client.Delete().
+		Namespace(c.ns).
+		Resource("applications").
+		Name(name).
+		Body(options).
+		Do().
+		Error()
+}
+
+// DeleteCollection deletes a collection of objects.
+func (c *applications) DeleteCollection(options *v1.DeleteOptions, listOptions v1.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("applications").
+		VersionedParams(&listOptions, scheme.ParameterCodec).
+		Timeout(timeout).
+		Body(options).
+		Do().
+		Error()
+}
+
+// Patch applies the patch and returns the patched application.
+func (c *applications) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1beta1.Application, err error) {
+	result = &v1beta1.Application{}
+	err = c.client.Patch(pt).
+		Namespace(c.ns).
+		Resource("applications").
+		SubResource(subresources...).
+		Name(name).
+		Body(data).
+		Do().
+		Into(result)
+	return
+}
diff --git a/kotskinds/client/kotsclientset/typed/kots/v1beta1/config.go b/kotskinds/client/kotsclientset/typed/kots/v1beta1/config.go
new file mode 100644
index 000000000..437c7bd9b
--- /dev/null
+++ b/kotskinds/client/kotsclientset/typed/kots/v1beta1/config.go
@@ -0,0 +1,190 @@
+/*
+Copyright 2019 Replicated, Inc..
+
+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 v1beta1
+
+import (
+	"time"
+
+	v1beta1 "github.com/replicatedhq/kots/kotskinds/apis/kots/v1beta1"
+	scheme "github.com/replicatedhq/kots/kotskinds/client/kotsclientset/scheme"
+	v1 "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"
+)
+
+// ConfigsGetter has a method to return a ConfigInterface.
+// A group's client should implement this interface.
+type ConfigsGetter interface {
+	Configs(namespace string) ConfigInterface
+}
+
+// ConfigInterface has methods to work with Config resources.
+type ConfigInterface interface {
+	Create(*v1beta1.Config) (*v1beta1.Config, error)
+	Update(*v1beta1.Config) (*v1beta1.Config, error)
+	UpdateStatus(*v1beta1.Config) (*v1beta1.Config, error)
+	Delete(name string, options *v1.DeleteOptions) error
+	DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error
+	Get(name string, options v1.GetOptions) (*v1beta1.Config, error)
+	List(opts v1.ListOptions) (*v1beta1.ConfigList, error)
+	Watch(opts v1.ListOptions) (watch.Interface, error)
+	Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1beta1.Config, err error)
+	ConfigExpansion
+}
+
+// configs implements ConfigInterface
+type configs struct {
+	client rest.Interface
+	ns     string
+}
+
+// newConfigs returns a Configs
+func newConfigs(c *KotsV1beta1Client, namespace string) *configs {
+	return &configs{
+		client: c.RESTClient(),
+		ns:     namespace,
+	}
+}
+
+// Get takes name of the config, and returns the corresponding config object, and an error if there is any.
+func (c *configs) Get(name string, options v1.GetOptions) (result *v1beta1.Config, err error) {
+	result = &v1beta1.Config{}
+	err = c.client.Get().
+		Namespace(c.ns).
+		Resource("configs").
+		Name(name).
+		VersionedParams(&options, scheme.ParameterCodec).
+		Do().
+		Into(result)
+	return
+}
+
+// List takes label and field selectors, and returns the list of Configs that match those selectors.
+func (c *configs) List(opts v1.ListOptions) (result *v1beta1.ConfigList, err error) {
+	var timeout time.Duration
+	if opts.TimeoutSeconds != nil {
+		timeout = time.Duration(*opts.TimeoutSeconds) * time.Second
+	}
+	result = &v1beta1.ConfigList{}
+	err = c.client.Get().
+		Namespace(c.ns).
+		Resource("configs").
+		VersionedParams(&opts, scheme.ParameterCodec).
+		Timeout(timeout).
+		Do().
+		Into(result)
+	return
+}
+
+// Watch returns a watch.Interface that watches the requested configs.
+func (c *configs) Watch(opts v1.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("configs").
+		VersionedParams(&opts, scheme.ParameterCodec).
+		Timeout(timeout).
+		Watch()
+}
+
+// Create takes the representation of a config and creates it.  Returns the server's representation of the config, and an error, if there is any.
+func (c *configs) Create(config *v1beta1.Config) (result *v1beta1.Config, err error) {
+	result = &v1beta1.Config{}
+	err = c.client.Post().
+		Namespace(c.ns).
+		Resource("configs").
+		Body(config).
+		Do().
+		Into(result)
+	return
+}
+
+// Update takes the representation of a config and updates it. Returns the server's representation of the config, and an error, if there is any.
+func (c *configs) Update(config *v1beta1.Config) (result *v1beta1.Config, err error) {
+	result = &v1beta1.Config{}
+	err = c.client.Put().
+		Namespace(c.ns).
+		Resource("configs").
+		Name(config.Name).
+		Body(config).
+		Do().
+		Into(result)
+	return
+}
+
+// UpdateStatus was generated because the type contains a Status member.
+// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus().
+
+func (c *configs) UpdateStatus(config *v1beta1.Config) (result *v1beta1.Config, err error) {
+	result = &v1beta1.Config{}
+	err = c.client.Put().
+		Namespace(c.ns).
+		Resource("configs").
+		Name(config.Name).
+		SubResource("status").
+		Body(config).
+		Do().
+		Into(result)
+	return
+}
+
+// Delete takes name of the config and deletes it. Returns an error if one occurs.
+func (c *configs) Delete(name string, options *v1.DeleteOptions) error {
+	return c.client.Delete().
+		Namespace(c.ns).
+		Resource("configs").
+		Name(name).
+		Body(options).
+		Do().
+		Error()
+}
+
+// DeleteCollection deletes a collection of objects.
+func (c *configs) DeleteCollection(options *v1.DeleteOptions, listOptions v1.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("configs").
+		VersionedParams(&listOptions, scheme.ParameterCodec).
+		Timeout(timeout).
+		Body(options).
+		Do().
+		Error()
+}
+
+// Patch applies the patch and returns the patched config.
+func (c *configs) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1beta1.Config, err error) {
+	result = &v1beta1.Config{}
+	err = c.client.Patch(pt).
+		Namespace(c.ns).
+		Resource("configs").
+		SubResource(subresources...).
+		Name(name).
+		Body(data).
+		Do().
+		Into(result)
+	return
+}
diff --git a/kotskinds/client/kotsclientset/typed/kots/v1beta1/configvalues.go b/kotskinds/client/kotsclientset/typed/kots/v1beta1/configvalues.go
new file mode 100644
index 000000000..7beac38f4
--- /dev/null
+++ b/kotskinds/client/kotsclientset/typed/kots/v1beta1/configvalues.go
@@ -0,0 +1,190 @@
+/*
+Copyright 2019 Replicated, Inc..
+
+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 v1beta1
+
+import (
+	"time"
+
+	v1beta1 "github.com/replicatedhq/kots/kotskinds/apis/kots/v1beta1"
+	scheme "github.com/replicatedhq/kots/kotskinds/client/kotsclientset/scheme"
+	v1 "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"
+)
+
+// ConfigValuesesGetter has a method to return a ConfigValuesInterface.
+// A group's client should implement this interface.
+type ConfigValuesesGetter interface {
+	ConfigValueses(namespace string) ConfigValuesInterface
+}
+
+// ConfigValuesInterface has methods to work with ConfigValues resources.
+type ConfigValuesInterface interface {
+	Create(*v1beta1.ConfigValues) (*v1beta1.ConfigValues, error)
+	Update(*v1beta1.ConfigValues) (*v1beta1.ConfigValues, error)
+	UpdateStatus(*v1beta1.ConfigValues) (*v1beta1.ConfigValues, error)
+	Delete(name string, options *v1.DeleteOptions) error
+	DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error
+	Get(name string, options v1.GetOptions) (*v1beta1.ConfigValues, error)
+	List(opts v1.ListOptions) (*v1beta1.ConfigValuesList, error)
+	Watch(opts v1.ListOptions) (watch.Interface, error)
+	Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1beta1.ConfigValues, err error)
+	ConfigValuesExpansion
+}
+
+// configValueses implements ConfigValuesInterface
+type configValueses struct {
+	client rest.Interface
+	ns     string
+}
+
+// newConfigValueses returns a ConfigValueses
+func newConfigValueses(c *KotsV1beta1Client, namespace string) *configValueses {
+	return &configValueses{
+		client: c.RESTClient(),
+		ns:     namespace,
+	}
+}
+
+// Get takes name of the configValues, and returns the corresponding configValues object, and an error if there is any.
+func (c *configValueses) Get(name string, options v1.GetOptions) (result *v1beta1.ConfigValues, err error) {
+	result = &v1beta1.ConfigValues{}
+	err = c.client.Get().
+		Namespace(c.ns).
+		Resource("configvalueses").
+		Name(name).
+		VersionedParams(&options, scheme.ParameterCodec).
+		Do().
+		Into(result)
+	return
+}
+
+// List takes label and field selectors, and returns the list of ConfigValueses that match those selectors.
+func (c *configValueses) List(opts v1.ListOptions) (result *v1beta1.ConfigValuesList, err error) {
+	var timeout time.Duration
+	if opts.TimeoutSeconds != nil {
+		timeout = time.Duration(*opts.TimeoutSeconds) * time.Second
+	}
+	result = &v1beta1.ConfigValuesList{}
+	err = c.client.Get().
+		Namespace(c.ns).
+		Resource("configvalueses").
+		VersionedParams(&opts, scheme.ParameterCodec).
+		Timeout(timeout).
+		Do().
+		Into(result)
+	return
+}
+
+// Watch returns a watch.Interface that watches the requested configValueses.
+func (c *configValueses) Watch(opts v1.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("configvalueses").
+		VersionedParams(&opts, scheme.ParameterCodec).
+		Timeout(timeout).
+		Watch()
+}
+
+// Create takes the representation of a configValues and creates it.  Returns the server's representation of the configValues, and an error, if there is any.
+func (c *configValueses) Create(configValues *v1beta1.ConfigValues) (result *v1beta1.ConfigValues, err error) {
+	result = &v1beta1.ConfigValues{}
+	err = c.client.Post().
+		Namespace(c.ns).
+		Resource("configvalueses").
+		Body(configValues).
+		Do().
+		Into(result)
+	return
+}
+
+// Update takes the representation of a configValues and updates it. Returns the server's representation of the configValues, and an error, if there is any.
+func (c *configValueses) Update(configValues *v1beta1.ConfigValues) (result *v1beta1.ConfigValues, err error) {
+	result = &v1beta1.ConfigValues{}
+	err = c.client.Put().
+		Namespace(c.ns).
+		Resource("configvalueses").
+		Name(configValues.Name).
+		Body(configValues).
+		Do().
+		Into(result)
+	return
+}
+
+// UpdateStatus was generated because the type contains a Status member.
+// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus().
+
+func (c *configValueses) UpdateStatus(configValues *v1beta1.ConfigValues) (result *v1beta1.ConfigValues, err error) {
+	result = &v1beta1.ConfigValues{}
+	err = c.client.Put().
+		Namespace(c.ns).
+		Resource("configvalueses").
+		Name(configValues.Name).
+		SubResource("status").
+		Body(configValues).
+		Do().
+		Into(result)
+	return
+}
+
+// Delete takes name of the configValues and deletes it. Returns an error if one occurs.
+func (c *configValueses) Delete(name string, options *v1.DeleteOptions) error {
+	return c.client.Delete().
+		Namespace(c.ns).
+		Resource("configvalueses").
+		Name(name).
+		Body(options).
+		Do().
+		Error()
+}
+
+// DeleteCollection deletes a collection of objects.
+func (c *configValueses) DeleteCollection(options *v1.DeleteOptions, listOptions v1.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("configvalueses").
+		VersionedParams(&listOptions, scheme.ParameterCodec).
+		Timeout(timeout).
+		Body(options).
+		Do().
+		Error()
+}
+
+// Patch applies the patch and returns the patched configValues.
+func (c *configValueses) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1beta1.ConfigValues, err error) {
+	result = &v1beta1.ConfigValues{}
+	err = c.client.Patch(pt).
+		Namespace(c.ns).
+		Resource("configvalueses").
+		SubResource(subresources...).
+		Name(name).
+		Body(data).
+		Do().
+		Into(result)
+	return
+}
diff --git a/kotskinds/client/kotsclientset/typed/kots/v1beta1/fake/fake_application.go b/kotskinds/client/kotsclientset/typed/kots/v1beta1/fake/fake_application.go
new file mode 100644
index 000000000..8d773421d
--- /dev/null
+++ b/kotskinds/client/kotsclientset/typed/kots/v1beta1/fake/fake_application.go
@@ -0,0 +1,139 @@
+/*
+Copyright 2019 Replicated, Inc..
+
+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 (
+	v1beta1 "github.com/replicatedhq/kots/kotskinds/apis/kots/v1beta1"
+	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"
+)
+
+// FakeApplications implements ApplicationInterface
+type FakeApplications struct {
+	Fake *FakeKotsV1beta1
+	ns   string
+}
+
+var applicationsResource = schema.GroupVersionResource{Group: "kots.io", Version: "v1beta1", Resource: "applications"}
+
+var applicationsKind = schema.GroupVersionKind{Group: "kots.io", Version: "v1beta1", Kind: "Application"}
+
+// Get takes name of the application, and returns the corresponding application object, and an error if there is any.
+func (c *FakeApplications) Get(name string, options v1.GetOptions) (result *v1beta1.Application, err error) {
+	obj, err := c.Fake.
+		Invokes(testing.NewGetAction(applicationsResource, c.ns, name), &v1beta1.Application{})
+
+	if obj == nil {
+		return nil, err
+	}
+	return obj.(*v1beta1.Application), err
+}
+
+// List takes label and field selectors, and returns the list of Applications that match those selectors.
+func (c *FakeApplications) List(opts v1.ListOptions) (result *v1beta1.ApplicationList, err error) {
+	obj, err := c.Fake.
+		Invokes(testing.NewListAction(applicationsResource, applicationsKind, c.ns, opts), &v1beta1.ApplicationList{})
+
+	if obj == nil {
+		return nil, err
+	}
+
+	label, _, _ := testing.ExtractFromListOptions(opts)
+	if label == nil {
+		label = labels.Everything()
+	}
+	list := &v1beta1.ApplicationList{ListMeta: obj.(*v1beta1.ApplicationList).ListMeta}
+	for _, item := range obj.(*v1beta1.ApplicationList).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 applications.
+func (c *FakeApplications) Watch(opts v1.ListOptions) (watch.Interface, error) {
+	return c.Fake.
+		InvokesWatch(testing.NewWatchAction(applicationsResource, c.ns, opts))
+
+}
+
+// Create takes the representation of a application and creates it.  Returns the server's representation of the application, and an error, if there is any.
+func (c *FakeApplications) Create(application *v1beta1.Application) (result *v1beta1.Application, err error) {
+	obj, err := c.Fake.
+		Invokes(testing.NewCreateAction(applicationsResource, c.ns, application), &v1beta1.Application{})
+
+	if obj == nil {
+		return nil, err
+	}
+	return obj.(*v1beta1.Application), err
+}
+
+// Update takes the representation of a application and updates it. Returns the server's representation of the application, and an error, if there is any.
+func (c *FakeApplications) Update(application *v1beta1.Application) (result *v1beta1.Application, err error) {
+	obj, err := c.Fake.
+		Invokes(testing.NewUpdateAction(applicationsResource, c.ns, application), &v1beta1.Application{})
+
+	if obj == nil {
+		return nil, err
+	}
+	return obj.(*v1beta1.Application), err
+}
+
+// UpdateStatus was generated because the type contains a Status member.
+// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus().
+func (c *FakeApplications) UpdateStatus(application *v1beta1.Application) (*v1beta1.Application, error) {
+	obj, err := c.Fake.
+		Invokes(testing.NewUpdateSubresourceAction(applicationsResource, "status", c.ns, application), &v1beta1.Application{})
+
+	if obj == nil {
+		return nil, err
+	}
+	return obj.(*v1beta1.Application), err
+}
+
+// Delete takes name of the application and deletes it. Returns an error if one occurs.
+func (c *FakeApplications) Delete(name string, options *v1.DeleteOptions) error {
+	_, err := c.Fake.
+		Invokes(testing.NewDeleteAction(applicationsResource, c.ns, name), &v1beta1.Application{})
+
+	return err
+}
+
+// DeleteCollection deletes a collection of objects.
+func (c *FakeApplications) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error {
+	action := testing.NewDeleteCollectionAction(applicationsResource, c.ns, listOptions)
+
+	_, err := c.Fake.Invokes(action, &v1beta1.ApplicationList{})
+	return err
+}
+
+// Patch applies the patch and returns the patched application.
+func (c *FakeApplications) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1beta1.Application, err error) {
+	obj, err := c.Fake.
+		Invokes(testing.NewPatchSubresourceAction(applicationsResource, c.ns, name, pt, data, subresources...), &v1beta1.Application{})
+
+	if obj == nil {
+		return nil, err
+	}
+	return obj.(*v1beta1.Application), err
+}
diff --git a/kotskinds/client/kotsclientset/typed/kots/v1beta1/fake/fake_config.go b/kotskinds/client/kotsclientset/typed/kots/v1beta1/fake/fake_config.go
new file mode 100644
index 000000000..e0850aae9
--- /dev/null
+++ b/kotskinds/client/kotsclientset/typed/kots/v1beta1/fake/fake_config.go
@@ -0,0 +1,139 @@
+/*
+Copyright 2019 Replicated, Inc..
+
+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 (
+	v1beta1 "github.com/replicatedhq/kots/kotskinds/apis/kots/v1beta1"
+	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"
+)
+
+// FakeConfigs implements ConfigInterface
+type FakeConfigs struct {
+	Fake *FakeKotsV1beta1
+	ns   string
+}
+
+var configsResource = schema.GroupVersionResource{Group: "kots.io", Version: "v1beta1", Resource: "configs"}
+
+var configsKind = schema.GroupVersionKind{Group: "kots.io", Version: "v1beta1", Kind: "Config"}
+
+// Get takes name of the config, and returns the corresponding config object, and an error if there is any.
+func (c *FakeConfigs) Get(name string, options v1.GetOptions) (result *v1beta1.Config, err error) {
+	obj, err := c.Fake.
+		Invokes(testing.NewGetAction(configsResource, c.ns, name), &v1beta1.Config{})
+
+	if obj == nil {
+		return nil, err
+	}
+	return obj.(*v1beta1.Config), err
+}
+
+// List takes label and field selectors, and returns the list of Configs that match those selectors.
+func (c *FakeConfigs) List(opts v1.ListOptions) (result *v1beta1.ConfigList, err error) {
+	obj, err := c.Fake.
+		Invokes(testing.NewListAction(configsResource, configsKind, c.ns, opts), &v1beta1.ConfigList{})
+
+	if obj == nil {
+		return nil, err
+	}
+
+	label, _, _ := testing.ExtractFromListOptions(opts)
+	if label == nil {
+		label = labels.Everything()
+	}
+	list := &v1beta1.ConfigList{ListMeta: obj.(*v1beta1.ConfigList).ListMeta}
+	for _, item := range obj.(*v1beta1.ConfigList).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 configs.
+func (c *FakeConfigs) Watch(opts v1.ListOptions) (watch.Interface, error) {
+	return c.Fake.
+		InvokesWatch(testing.NewWatchAction(configsResource, c.ns, opts))
+
+}
+
+// Create takes the representation of a config and creates it.  Returns the server's representation of the config, and an error, if there is any.
+func (c *FakeConfigs) Create(config *v1beta1.Config) (result *v1beta1.Config, err error) {
+	obj, err := c.Fake.
+		Invokes(testing.NewCreateAction(configsResource, c.ns, config), &v1beta1.Config{})
+
+	if obj == nil {
+		return nil, err
+	}
+	return obj.(*v1beta1.Config), err
+}
+
+// Update takes the representation of a config and updates it. Returns the server's representation of the config, and an error, if there is any.
+func (c *FakeConfigs) Update(config *v1beta1.Config) (result *v1beta1.Config, err error) {
+	obj, err := c.Fake.
+		Invokes(testing.NewUpdateAction(configsResource, c.ns, config), &v1beta1.Config{})
+
+	if obj == nil {
+		return nil, err
+	}
+	return obj.(*v1beta1.Config), err
+}
+
+// UpdateStatus was generated because the type contains a Status member.
+// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus().
+func (c *FakeConfigs) UpdateStatus(config *v1beta1.Config) (*v1beta1.Config, error) {
+	obj, err := c.Fake.
+		Invokes(testing.NewUpdateSubresourceAction(configsResource, "status", c.ns, config), &v1beta1.Config{})
+
+	if obj == nil {
+		return nil, err
+	}
+	return obj.(*v1beta1.Config), err
+}
+
+// Delete takes name of the config and deletes it. Returns an error if one occurs.
+func (c *FakeConfigs) Delete(name string, options *v1.DeleteOptions) error {
+	_, err := c.Fake.
+		Invokes(testing.NewDeleteAction(configsResource, c.ns, name), &v1beta1.Config{})
+
+	return err
+}
+
+// DeleteCollection deletes a collection of objects.
+func (c *FakeConfigs) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error {
+	action := testing.NewDeleteCollectionAction(configsResource, c.ns, listOptions)
+
+	_, err := c.Fake.Invokes(action, &v1beta1.ConfigList{})
+	return err
+}
+
+// Patch applies the patch and returns the patched config.
+func (c *FakeConfigs) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1beta1.Config, err error) {
+	obj, err := c.Fake.
+		Invokes(testing.NewPatchSubresourceAction(configsResource, c.ns, name, pt, data, subresources...), &v1beta1.Config{})
+
+	if obj == nil {
+		return nil, err
+	}
+	return obj.(*v1beta1.Config), err
+}
diff --git a/kotskinds/client/kotsclientset/typed/kots/v1beta1/fake/fake_configvalues.go b/kotskinds/client/kotsclientset/typed/kots/v1beta1/fake/fake_configvalues.go
new file mode 100644
index 000000000..6809f7acd
--- /dev/null
+++ b/kotskinds/client/kotsclientset/typed/kots/v1beta1/fake/fake_configvalues.go
@@ -0,0 +1,139 @@
+/*
+Copyright 2019 Replicated, Inc..
+
+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 (
+	v1beta1 "github.com/replicatedhq/kots/kotskinds/apis/kots/v1beta1"
+	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"
+)
+
+// FakeConfigValueses implements ConfigValuesInterface
+type FakeConfigValueses struct {
+	Fake *FakeKotsV1beta1
+	ns   string
+}
+
+var configvaluesesResource = schema.GroupVersionResource{Group: "kots.io", Version: "v1beta1", Resource: "configvalueses"}
+
+var configvaluesesKind = schema.GroupVersionKind{Group: "kots.io", Version: "v1beta1", Kind: "ConfigValues"}
+
+// Get takes name of the configValues, and returns the corresponding configValues object, and an error if there is any.
+func (c *FakeConfigValueses) Get(name string, options v1.GetOptions) (result *v1beta1.ConfigValues, err error) {
+	obj, err := c.Fake.
+		Invokes(testing.NewGetAction(configvaluesesResource, c.ns, name), &v1beta1.ConfigValues{})
+
+	if obj == nil {
+		return nil, err
+	}
+	return obj.(*v1beta1.ConfigValues), err
+}
+
+// List takes label and field selectors, and returns the list of ConfigValueses that match those selectors.
+func (c *FakeConfigValueses) List(opts v1.ListOptions) (result *v1beta1.ConfigValuesList, err error) {
+	obj, err := c.Fake.
+		Invokes(testing.NewListAction(configvaluesesResource, configvaluesesKind, c.ns, opts), &v1beta1.ConfigValuesList{})
+
+	if obj == nil {
+		return nil, err
+	}
+
+	label, _, _ := testing.ExtractFromListOptions(opts)
+	if label == nil {
+		label = labels.Everything()
+	}
+	list := &v1beta1.ConfigValuesList{ListMeta: obj.(*v1beta1.ConfigValuesList).ListMeta}
+	for _, item := range obj.(*v1beta1.ConfigValuesList).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 configValueses.
+func (c *FakeConfigValueses) Watch(opts v1.ListOptions) (watch.Interface, error) {
+	return c.Fake.
+		InvokesWatch(testing.NewWatchAction(configvaluesesResource, c.ns, opts))
+
+}
+
+// Create takes the representation of a configValues and creates it.  Returns the server's representation of the configValues, and an error, if there is any.
+func (c *FakeConfigValueses) Create(configValues *v1beta1.ConfigValues) (result *v1beta1.ConfigValues, err error) {
+	obj, err := c.Fake.
+		Invokes(testing.NewCreateAction(configvaluesesResource, c.ns, configValues), &v1beta1.ConfigValues{})
+
+	if obj == nil {
+		return nil, err
+	}
+	return obj.(*v1beta1.ConfigValues), err
+}
+
+// Update takes the representation of a configValues and updates it. Returns the server's representation of the configValues, and an error, if there is any.
+func (c *FakeConfigValueses) Update(configValues *v1beta1.ConfigValues) (result *v1beta1.ConfigValues, err error) {
+	obj, err := c.Fake.
+		Invokes(testing.NewUpdateAction(configvaluesesResource, c.ns, configValues), &v1beta1.ConfigValues{})
+
+	if obj == nil {
+		return nil, err
+	}
+	return obj.(*v1beta1.ConfigValues), err
+}
+
+// UpdateStatus was generated because the type contains a Status member.
+// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus().
+func (c *FakeConfigValueses) UpdateStatus(configValues *v1beta1.ConfigValues) (*v1beta1.ConfigValues, error) {
+	obj, err := c.Fake.
+		Invokes(testing.NewUpdateSubresourceAction(configvaluesesResource, "status", c.ns, configValues), &v1beta1.ConfigValues{})
+
+	if obj == nil {
+		return nil, err
+	}
+	return obj.(*v1beta1.ConfigValues), err
+}
+
+// Delete takes name of the configValues and deletes it. Returns an error if one occurs.
+func (c *FakeConfigValueses) Delete(name string, options *v1.DeleteOptions) error {
+	_, err := c.Fake.
+		Invokes(testing.NewDeleteAction(configvaluesesResource, c.ns, name), &v1beta1.ConfigValues{})
+
+	return err
+}
+
+// DeleteCollection deletes a collection of objects.
+func (c *FakeConfigValueses) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error {
+	action := testing.NewDeleteCollectionAction(configvaluesesResource, c.ns, listOptions)
+
+	_, err := c.Fake.Invokes(action, &v1beta1.ConfigValuesList{})
+	return err
+}
+
+// Patch applies the patch and returns the patched configValues.
+func (c *FakeConfigValueses) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1beta1.ConfigValues, err error) {
+	obj, err := c.Fake.
+		Invokes(testing.NewPatchSubresourceAction(configvaluesesResource, c.ns, name, pt, data, subresources...), &v1beta1.ConfigValues{})
+
+	if obj == nil {
+		return nil, err
+	}
+	return obj.(*v1beta1.ConfigValues), err
+}
diff --git a/kotskinds/client/kotsclientset/typed/kots/v1beta1/fake/fake_kots_client.go b/kotskinds/client/kotsclientset/typed/kots/v1beta1/fake/fake_kots_client.go
index 93754123f..e34d9a362 100644
--- a/kotskinds/client/kotsclientset/typed/kots/v1beta1/fake/fake_kots_client.go
+++ b/kotskinds/client/kotsclientset/typed/kots/v1beta1/fake/fake_kots_client.go
@@ -27,8 +27,20 @@ type FakeKotsV1beta1 struct {
 	*testing.Fake
 }
 
-func (c *FakeKotsV1beta1) Apps(namespace string) v1beta1.AppInterface {
-	return &FakeApps{c, namespace}
+func (c *FakeKotsV1beta1) Applications(namespace string) v1beta1.ApplicationInterface {
+	return &FakeApplications{c, namespace}
+}
+
+func (c *FakeKotsV1beta1) Configs(namespace string) v1beta1.ConfigInterface {
+	return &FakeConfigs{c, namespace}
+}
+
+func (c *FakeKotsV1beta1) ConfigValueses(namespace string) v1beta1.ConfigValuesInterface {
+	return &FakeConfigValueses{c, namespace}
+}
+
+func (c *FakeKotsV1beta1) Licenses(namespace string) v1beta1.LicenseInterface {
+	return &FakeLicenses{c, namespace}
 }
 
 // RESTClient returns a RESTClient that is used to communicate
diff --git a/kotskinds/client/kotsclientset/typed/kots/v1beta1/fake/fake_license.go b/kotskinds/client/kotsclientset/typed/kots/v1beta1/fake/fake_license.go
new file mode 100644
index 000000000..50dbd6355
--- /dev/null
+++ b/kotskinds/client/kotsclientset/typed/kots/v1beta1/fake/fake_license.go
@@ -0,0 +1,139 @@
+/*
+Copyright 2019 Replicated, Inc..
+
+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 (
+	v1beta1 "github.com/replicatedhq/kots/kotskinds/apis/kots/v1beta1"
+	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"
+)
+
+// FakeLicenses implements LicenseInterface
+type FakeLicenses struct {
+	Fake *FakeKotsV1beta1
+	ns   string
+}
+
+var licensesResource = schema.GroupVersionResource{Group: "kots.io", Version: "v1beta1", Resource: "licenses"}
+
+var licensesKind = schema.GroupVersionKind{Group: "kots.io", Version: "v1beta1", Kind: "License"}
+
+// Get takes name of the license, and returns the corresponding license object, and an error if there is any.
+func (c *FakeLicenses) Get(name string, options v1.GetOptions) (result *v1beta1.License, err error) {
+	obj, err := c.Fake.
+		Invokes(testing.NewGetAction(licensesResource, c.ns, name), &v1beta1.License{})
+
+	if obj == nil {
+		return nil, err
+	}
+	return obj.(*v1beta1.License), err
+}
+
+// List takes label and field selectors, and returns the list of Licenses that match those selectors.
+func (c *FakeLicenses) List(opts v1.ListOptions) (result *v1beta1.LicenseList, err error) {
+	obj, err := c.Fake.
+		Invokes(testing.NewListAction(licensesResource, licensesKind, c.ns, opts), &v1beta1.LicenseList{})
+
+	if obj == nil {
+		return nil, err
+	}
+
+	label, _, _ := testing.ExtractFromListOptions(opts)
+	if label == nil {
+		label = labels.Everything()
+	}
+	list := &v1beta1.LicenseList{ListMeta: obj.(*v1beta1.LicenseList).ListMeta}
+	for _, item := range obj.(*v1beta1.LicenseList).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 licenses.
+func (c *FakeLicenses) Watch(opts v1.ListOptions) (watch.Interface, error) {
+	return c.Fake.
+		InvokesWatch(testing.NewWatchAction(licensesResource, c.ns, opts))
+
+}
+
+// Create takes the representation of a license and creates it.  Returns the server's representation of the license, and an error, if there is any.
+func (c *FakeLicenses) Create(license *v1beta1.License) (result *v1beta1.License, err error) {
+	obj, err := c.Fake.
+		Invokes(testing.NewCreateAction(licensesResource, c.ns, license), &v1beta1.License{})
+
+	if obj == nil {
+		return nil, err
+	}
+	return obj.(*v1beta1.License), err
+}
+
+// Update takes the representation of a license and updates it. Returns the server's representation of the license, and an error, if there is any.
+func (c *FakeLicenses) Update(license *v1beta1.License) (result *v1beta1.License, err error) {
+	obj, err := c.Fake.
+		Invokes(testing.NewUpdateAction(licensesResource, c.ns, license), &v1beta1.License{})
+
+	if obj == nil {
+		return nil, err
+	}
+	return obj.(*v1beta1.License), err
+}
+
+// UpdateStatus was generated because the type contains a Status member.
+// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus().
+func (c *FakeLicenses) UpdateStatus(license *v1beta1.License) (*v1beta1.License, error) {
+	obj, err := c.Fake.
+		Invokes(testing.NewUpdateSubresourceAction(licensesResource, "status", c.ns, license), &v1beta1.License{})
+
+	if obj == nil {
+		return nil, err
+	}
+	return obj.(*v1beta1.License), err
+}
+
+// Delete takes name of the license and deletes it. Returns an error if one occurs.
+func (c *FakeLicenses) Delete(name string, options *v1.DeleteOptions) error {
+	_, err := c.Fake.
+		Invokes(testing.NewDeleteAction(licensesResource, c.ns, name), &v1beta1.License{})
+
+	return err
+}
+
+// DeleteCollection deletes a collection of objects.
+func (c *FakeLicenses) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error {
+	action := testing.NewDeleteCollectionAction(licensesResource, c.ns, listOptions)
+
+	_, err := c.Fake.Invokes(action, &v1beta1.LicenseList{})
+	return err
+}
+
+// Patch applies the patch and returns the patched license.
+func (c *FakeLicenses) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1beta1.License, err error) {
+	obj, err := c.Fake.
+		Invokes(testing.NewPatchSubresourceAction(licensesResource, c.ns, name, pt, data, subresources...), &v1beta1.License{})
+
+	if obj == nil {
+		return nil, err
+	}
+	return obj.(*v1beta1.License), err
+}
diff --git a/kotskinds/client/kotsclientset/typed/kots/v1beta1/generated_expansion.go b/kotskinds/client/kotsclientset/typed/kots/v1beta1/generated_expansion.go
index f28f3d3d2..e687eadc9 100644
--- a/kotskinds/client/kotsclientset/typed/kots/v1beta1/generated_expansion.go
+++ b/kotskinds/client/kotsclientset/typed/kots/v1beta1/generated_expansion.go
@@ -17,4 +17,10 @@ limitations under the License.
 
 package v1beta1
 
-type AppExpansion interface{}
+type ApplicationExpansion interface{}
+
+type ConfigExpansion interface{}
+
+type ConfigValuesExpansion interface{}
+
+type LicenseExpansion interface{}
diff --git a/kotskinds/client/kotsclientset/typed/kots/v1beta1/kots_client.go b/kotskinds/client/kotsclientset/typed/kots/v1beta1/kots_client.go
index 410a86329..b04850780 100644
--- a/kotskinds/client/kotsclientset/typed/kots/v1beta1/kots_client.go
+++ b/kotskinds/client/kotsclientset/typed/kots/v1beta1/kots_client.go
@@ -26,7 +26,10 @@ import (
 
 type KotsV1beta1Interface interface {
 	RESTClient() rest.Interface
-	AppsGetter
+	ApplicationsGetter
+	ConfigsGetter
+	ConfigValuesesGetter
+	LicensesGetter
 }
 
 // KotsV1beta1Client is used to interact with features provided by the kots.io group.
@@ -34,8 +37,20 @@ type KotsV1beta1Client struct {
 	restClient rest.Interface
 }
 
-func (c *KotsV1beta1Client) Apps(namespace string) AppInterface {
-	return newApps(c, namespace)
+func (c *KotsV1beta1Client) Applications(namespace string) ApplicationInterface {
+	return newApplications(c, namespace)
+}
+
+func (c *KotsV1beta1Client) Configs(namespace string) ConfigInterface {
+	return newConfigs(c, namespace)
+}
+
+func (c *KotsV1beta1Client) ConfigValueses(namespace string) ConfigValuesInterface {
+	return newConfigValueses(c, namespace)
+}
+
+func (c *KotsV1beta1Client) Licenses(namespace string) LicenseInterface {
+	return newLicenses(c, namespace)
 }
 
 // NewForConfig creates a new KotsV1beta1Client for the given config.
diff --git a/kotskinds/client/kotsclientset/typed/kots/v1beta1/license.go b/kotskinds/client/kotsclientset/typed/kots/v1beta1/license.go
new file mode 100644
index 000000000..3f8b8d38e
--- /dev/null
+++ b/kotskinds/client/kotsclientset/typed/kots/v1beta1/license.go
@@ -0,0 +1,190 @@
+/*
+Copyright 2019 Replicated, Inc..
+
+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 v1beta1
+
+import (
+	"time"
+
+	v1beta1 "github.com/replicatedhq/kots/kotskinds/apis/kots/v1beta1"
+	scheme "github.com/replicatedhq/kots/kotskinds/client/kotsclientset/scheme"
+	v1 "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"
+)
+
+// LicensesGetter has a method to return a LicenseInterface.
+// A group's client should implement this interface.
+type LicensesGetter interface {
+	Licenses(namespace string) LicenseInterface
+}
+
+// LicenseInterface has methods to work with License resources.
+type LicenseInterface interface {
+	Create(*v1beta1.License) (*v1beta1.License, error)
+	Update(*v1beta1.License) (*v1beta1.License, error)
+	UpdateStatus(*v1beta1.License) (*v1beta1.License, error)
+	Delete(name string, options *v1.DeleteOptions) error
+	DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error
+	Get(name string, options v1.GetOptions) (*v1beta1.License, error)
+	List(opts v1.ListOptions) (*v1beta1.LicenseList, error)
+	Watch(opts v1.ListOptions) (watch.Interface, error)
+	Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1beta1.License, err error)
+	LicenseExpansion
+}
+
+// licenses implements LicenseInterface
+type licenses struct {
+	client rest.Interface
+	ns     string
+}
+
+// newLicenses returns a Licenses
+func newLicenses(c *KotsV1beta1Client, namespace string) *licenses {
+	return &licenses{
+		client: c.RESTClient(),
+		ns:     namespace,
+	}
+}
+
+// Get takes name of the license, and returns the corresponding license object, and an error if there is any.
+func (c *licenses) Get(name string, options v1.GetOptions) (result *v1beta1.License, err error) {
+	result = &v1beta1.License{}
+	err = c.client.Get().
+		Namespace(c.ns).
+		Resource("licenses").
+		Name(name).
+		VersionedParams(&options, scheme.ParameterCodec).
+		Do().
+		Into(result)
+	return
+}
+
+// List takes label and field selectors, and returns the list of Licenses that match those selectors.
+func (c *licenses) List(opts v1.ListOptions) (result *v1beta1.LicenseList, err error) {
+	var timeout time.Duration
+	if opts.TimeoutSeconds != nil {
+		timeout = time.Duration(*opts.TimeoutSeconds) * time.Second
+	}
+	result = &v1beta1.LicenseList{}
+	err = c.client.Get().
+		Namespace(c.ns).
+		Resource("licenses").
+		VersionedParams(&opts, scheme.ParameterCodec).
+		Timeout(timeout).
+		Do().
+		Into(result)
+	return
+}
+
+// Watch returns a watch.Interface that watches the requested licenses.
+func (c *licenses) Watch(opts v1.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("licenses").
+		VersionedParams(&opts, scheme.ParameterCodec).
+		Timeout(timeout).
+		Watch()
+}
+
+// Create takes the representation of a license and creates it.  Returns the server's representation of the license, and an error, if there is any.
+func (c *licenses) Create(license *v1beta1.License) (result *v1beta1.License, err error) {
+	result = &v1beta1.License{}
+	err = c.client.Post().
+		Namespace(c.ns).
+		Resource("licenses").
+		Body(license).
+		Do().
+		Into(result)
+	return
+}
+
+// Update takes the representation of a license and updates it. Returns the server's representation of the license, and an error, if there is any.
+func (c *licenses) Update(license *v1beta1.License) (result *v1beta1.License, err error) {
+	result = &v1beta1.License{}
+	err = c.client.Put().
+		Namespace(c.ns).
+		Resource("licenses").
+		Name(license.Name).
+		Body(license).
+		Do().
+		Into(result)
+	return
+}
+
+// UpdateStatus was generated because the type contains a Status member.
+// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus().
+
+func (c *licenses) UpdateStatus(license *v1beta1.License) (result *v1beta1.License, err error) {
+	result = &v1beta1.License{}
+	err = c.client.Put().
+		Namespace(c.ns).
+		Resource("licenses").
+		Name(license.Name).
+		SubResource("status").
+		Body(license).
+		Do().
+		Into(result)
+	return
+}
+
+// Delete takes name of the license and deletes it. Returns an error if one occurs.
+func (c *licenses) Delete(name string, options *v1.DeleteOptions) error {
+	return c.client.Delete().
+		Namespace(c.ns).
+		Resource("licenses").
+		Name(name).
+		Body(options).
+		Do().
+		Error()
+}
+
+// DeleteCollection deletes a collection of objects.
+func (c *licenses) DeleteCollection(options *v1.DeleteOptions, listOptions v1.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("licenses").
+		VersionedParams(&listOptions, scheme.ParameterCodec).
+		Timeout(timeout).
+		Body(options).
+		Do().
+		Error()
+}
+
+// Patch applies the patch and returns the patched license.
+func (c *licenses) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1beta1.License, err error) {
+	result = &v1beta1.License{}
+	err = c.client.Patch(pt).
+		Namespace(c.ns).
+		Resource("licenses").
+		SubResource(subresources...).
+		Name(name).
+		Body(data).
+		Do().
+		Into(result)
+	return
+}
diff --git a/pkg/base/replicated.go b/pkg/base/replicated.go
index 250026e4f..1874e103f 100644
--- a/pkg/base/replicated.go
+++ b/pkg/base/replicated.go
@@ -1,16 +1,31 @@
 package base
 
 import (
+	"github.com/pkg/errors"
+	"github.com/replicatedhq/kots/pkg/template"
 	"github.com/replicatedhq/kots/pkg/upstream"
 )
 
 func renderReplicated(u *upstream.Upstream, renderOptions *RenderOptions) (*Base, error) {
 	baseFiles := []BaseFile{}
 
+	builder := template.Builder{}
+	builder.AddCtx(template.StaticCtx{})
+	configCtx, err := builder.NewConfigContext(nil, nil)
+	if err != nil {
+		return nil, errors.Wrap(err, "failed to create config context")
+	}
+	builder.AddCtx(configCtx)
+
 	for _, upstreamFile := range u.Files {
+		rendered, err := builder.RenderTemplate(upstreamFile.Path, string(upstreamFile.Content))
+		if err != nil {
+			return nil, errors.Wrap(err, "failed to render template")
+		}
+
 		baseFile := BaseFile{
 			Path:    upstreamFile.Path,
-			Content: upstreamFile.Content,
+			Content: []byte(rendered),
 		}
 
 		baseFiles = append(baseFiles, baseFile)
diff --git a/pkg/pull/pull.go b/pkg/pull/pull.go
index 4b995fe33..940a13df6 100644
--- a/pkg/pull/pull.go
+++ b/pkg/pull/pull.go
@@ -17,6 +17,7 @@ type PullOptions struct {
 	Overwrite   bool
 	Namespace   string
 	Downstreams []string
+	LocalPath   string
 }
 
 func Pull(upstreamURI string, pullOptions PullOptions) error {
@@ -25,6 +26,7 @@ func Pull(upstreamURI string, pullOptions PullOptions) error {
 
 	fetchOptions := upstream.FetchOptions{}
 	fetchOptions.HelmRepoURI = pullOptions.HelmRepoURI
+	fetchOptions.LocalPath = pullOptions.LocalPath
 
 	log.ActionWithSpinner("Pulling upstream")
 	u, err := upstream.FetchUpstream(upstreamURI, &fetchOptions)
diff --git a/pkg/template/builder.go b/pkg/template/builder.go
new file mode 100644
index 000000000..5b5c9f5aa
--- /dev/null
+++ b/pkg/template/builder.go
@@ -0,0 +1,167 @@
+package template
+
+import (
+	"bytes"
+	"regexp"
+	"strconv"
+	"text/template"
+
+	"github.com/pkg/errors"
+)
+
+var (
+	templateNotDefinedRegexp = regexp.MustCompile(`template.*not defined$`)
+)
+
+type Builder struct {
+	Ctx    []Ctx
+	Functs template.FuncMap
+}
+
+func (b *Builder) AddCtx(ctx Ctx) {
+	b.Ctx = append(b.Ctx, ctx)
+}
+
+func (b *Builder) String(text string) (string, error) {
+	if text == "" {
+		return "", nil
+	}
+	return b.RenderTemplate(text, text)
+}
+
+func (b *Builder) Bool(text string, defaultVal bool) (bool, error) {
+	if text == "" {
+		return defaultVal, nil
+	}
+
+	value, err := b.RenderTemplate(text, text)
+	if err != nil {
+		return defaultVal, errors.Wrap(err, "failed to render template")
+	}
+
+	// If the template didn't parse (turns into an empty string), then we should
+	// return the default
+	if value == "" {
+		return defaultVal, nil
+	}
+
+	result, err := strconv.ParseBool(value)
+	if err != nil {
+		// for now we are assuming default value if we fail to parse
+		return defaultVal, nil
+	}
+
+	return result, nil
+}
+
+func (b *Builder) Int(text string, defaultVal int64) (int64, error) {
+	if text == "" {
+		return defaultVal, nil
+	}
+
+	value, err := b.RenderTemplate(text, text)
+	if err != nil {
+		return defaultVal, errors.Wrap(err, "failed to render template")
+	}
+
+	// If the template didn't parse (turns into an empty string), then we should
+	// return the default
+	if value == "" {
+		return defaultVal, nil
+	}
+
+	result, err := strconv.ParseInt(value, 10, 64)
+	if err != nil {
+		// for now we are assuming default value if we fail to parse
+		return defaultVal, nil
+	}
+
+	return result, nil
+}
+
+func (b *Builder) Uint(text string, defaultVal uint64) (uint64, error) {
+	if text == "" {
+		return defaultVal, nil
+	}
+
+	value, err := b.RenderTemplate(text, text)
+	if err != nil {
+		return defaultVal, errors.Wrap(err, "failed to render template")
+	}
+
+	// If the template didn't parse (turns into an empty string), then we should
+	// return the default
+	if value == "" {
+		return defaultVal, nil
+	}
+
+	result, err := strconv.ParseUint(value, 10, 64)
+	if err != nil {
+		// for now we are assuming default value if we fail to parse
+		return defaultVal, nil
+	}
+
+	return result, nil
+}
+
+func (b *Builder) Float64(text string, defaultVal float64) (float64, error) {
+	if text == "" {
+		return defaultVal, nil
+	}
+
+	value, err := b.RenderTemplate(text, text)
+	if err != nil {
+		return defaultVal, errors.Wrap(err, "failed to render template")
+	}
+
+	// If the template didn't parse (turns into an empty string), then we should
+	// return the default
+	if value == "" {
+		return defaultVal, nil
+	}
+
+	result, err := strconv.ParseFloat(value, 64)
+	if err != nil {
+		// for now we are assuming default value if we fail to parse
+		return defaultVal, nil
+	}
+
+	return result, nil
+}
+
+func (b *Builder) BuildFuncMap() template.FuncMap {
+	if b.Functs == nil {
+		b.Functs = template.FuncMap{}
+	}
+
+	funcMap := b.Functs
+	for _, ctx := range b.Ctx {
+		for name, fn := range ctx.FuncMap() {
+			funcMap[name] = fn
+		}
+	}
+	return funcMap
+}
+
+func (b *Builder) GetTemplate(name, text string) (*template.Template, error) {
+	tmpl, err := template.New(name).Delims("{{repl ", "}}").Funcs(b.BuildFuncMap()).Parse(text)
+	if err != nil {
+		return nil, err
+	}
+
+	return tmpl, nil
+}
+
+func (b *Builder) RenderTemplate(name string, text string) (string, error) {
+	tmpl, err := b.GetTemplate(name, text)
+	if err != nil {
+		return "", errors.Wrap(err, "failed to get template")
+	}
+
+	var contents bytes.Buffer
+	if err := tmpl.Execute(&contents, nil); err != nil {
+		return "", errors.Wrap(err, "failed to execute template")
+	}
+
+	return contents.String(), nil
+}
diff --git a/pkg/template/config_context.go b/pkg/template/config_context.go
new file mode 100644
index 000000000..007b950ed
--- /dev/null
+++ b/pkg/template/config_context.go
@@ -0,0 +1,110 @@
+package template
+
+import (
+	"encoding/base64"
+	"fmt"
+	"text/template"
+
+	"github.com/pkg/errors"
+	kotsv1beta1 "github.com/replicatedhq/kots/kotskinds/apis/kots/v1beta1"
+)
+
+func (b *Builder) NewConfigContext(configGroups []kotsv1beta1.ConfigGroup, templateContext map[string]interface{}) (*ConfigCtx, error) {
+	configCtx := &ConfigCtx{
+		ItemValues: templateContext,
+	}
+
+	for _, configGroup := range configGroups {
+		for _, configItem := range configGroup.Items {
+			// if the pending value is different from the built, then use the pending every time
+			// We have to ignore errors here because we only have the static context loaded
+			// for rendering. some items have templates that need the config context,
+			// so we can ignore these.
+			builtDefault, _ := b.String(configItem.Default)
+			builtValue, _ := b.String(configItem.Value)
+
+			var built string
+			if builtValue != "" {
+				built = builtValue
+			} else {
+				built = builtDefault
+			}
+
+			if v, ok := templateContext[configItem.Name]; ok {
+				built = fmt.Sprintf("%v", v)
+			}
+
+			configCtx.ItemValues[configItem.Name] = built
+		}
+	}
+
+	return configCtx, nil
+}
+
+// ConfigCtx is the context for builder functions before the application has started.
+type ConfigCtx struct {
+	ItemValues map[string]interface{}
+}
+
+// FuncMap represents the available functions in the ConfigCtx.
+func (ctx ConfigCtx) FuncMap() template.FuncMap {
+	return template.FuncMap{
+		"ConfigOption":          ctx.configOption,
+		"ConfigOptionIndex":     ctx.configOptionIndex,
+		"ConfigOptionData":      ctx.configOptionData,
+		"ConfigOptionEquals":    ctx.configOptionEquals,
+		"ConfigOptionNotEquals": ctx.configOptionNotEquals,
+	}
+}
+
+func (ctx ConfigCtx) configOption(name string) string {
+	v, err := ctx.getConfigOptionValue(name)
+	if err != nil {
+		return ""
+	}
+	return v
+}
+
+func (ctx ConfigCtx) configOptionIndex(name string) string {
+	return ""
+}
+
+func (ctx ConfigCtx) configOptionData(name string) string {
+	v, err := ctx.getConfigOptionValue(name)
+	if err != nil {
+		return ""
+	}
+
+	decoded, err := base64.StdEncoding.DecodeString(v)
+	if err != nil {
+		return ""
+	}
+
+	return string(decoded)
+}
+
+func (ctx ConfigCtx) configOptionEquals(name string, value string) bool {
+	val, err := ctx.getConfigOptionValue(name)
+	if err != nil {
+		return false
+	}
+
+	return value == val
+}
+
+func (ctx ConfigCtx) configOptionNotEquals(name string, value string) bool {
+	val, err := ctx.getConfigOptionValue(name)
+	if err != nil {
+		return false
+	}
+
+	return value != val
+}
+
+func (ctx ConfigCtx) getConfigOptionValue(itemName string) (string, error) {
+	if val, ok := ctx.ItemValues[itemName]; ok {
+		return fmt.Sprintf("%v", val), nil
+	}
+
+	return "", errors.New("unable to find config item")
+}
diff --git a/pkg/template/random.go b/pkg/template/random.go
new file mode 100644
index 000000000..36cc46345
--- /dev/null
+++ b/pkg/template/random.go
@@ -0,0 +1,73 @@
+package template
+
+import (
+	"bytes"
+	"crypto/rand"
+	"math/big"
+	"regexp/syntax"
+
+	"github.com/pkg/errors"
+)
+
+const (
+	DefaultCharset = "[_A-Za-z0-9]"
+)
+
+// stolen from https://github.com/replicatedhq/replicated/blob/8ce3ed40436e38b8089387d103623dbe09bbf1c0/pkg/commands/random.go#L22
+func (ctx *StaticCtx) RandomString(length uint64, providedCharset ...string) string {
+	charset := DefaultCharset
+	if len(providedCharset) >= 1 {
+		charset = providedCharset[0]
+	}
+	regExp, err := syntax.Parse(charset, syntax.Perl)
+	if err != nil {
+		return ""
+	}
+
+	regExp = regExp.Simplify()
+	var b bytes.Buffer
+	for i := 0; i < int(length); i++ {
+		if err := ctx.genString(&b, regExp); err != nil {
+			return ""
+		}
+	}
+
+	result := b.String()
+	return result
+}
+
+func (ctx *StaticCtx) genString(w *bytes.Buffer, rx *syntax.Regexp) error {
+	switch rx.Op {
+	case syntax.OpCharClass:
+		sum := 0
+		for i := 0; i < len(rx.Rune); i += 2 {
+			sum += 1 + int(rx.Rune[i+1]-rx.Rune[i])
+		}
+
+		for i, nth := 0, rune(randint(sum)); i < len(rx.Rune); i += 2 {
+			min, max := rx.Rune[i], rx.Rune[i+1]
+			delta := max - min
+			if nth <= delta {
+				w.WriteRune(min + nth)
+				return nil
+			}
+			nth -= 1 + delta
+		}
+	default:
+		return errors.New("invalid charset")
+	}
+
+	return nil
+}
+
+func randint(max int) int {
+	var bigmax big.Int
+	bigmax.SetInt64(int64(max))
+
+	res, err := rand.Int(rand.Reader, &bigmax)
+	if err != nil {
+		panic(err)
+	}
+
+	return int(res.Int64())
+}
diff --git a/pkg/template/static_context.go b/pkg/template/static_context.go
new file mode 100644
index 000000000..f9576a956
--- /dev/null
+++ b/pkg/template/static_context.go
@@ -0,0 +1,301 @@
+package template
+
+/*
+  This was taken from https://github.com/replicatedhq/replicated/blob/master/templates/context.go
+*/
+
+import (
+	"crypto/aes"
+	"crypto/cipher"
+	"crypto/rand"
+	"crypto/rsa"
+	"crypto/sha256"
+	"encoding/base64"
+	"encoding/binary"
+	"fmt"
+	"io"
+	"net/url"
+	"reflect"
+	"strconv"
+	"strings"
+	"text/template"
+	"time"
+
+	"github.com/Masterminds/sprig"
+	units "github.com/docker/go-units"
+	"github.com/pkg/errors"
+	certUtil "k8s.io/client-go/util/cert"
+)
+
+type Ctx interface {
+	FuncMap() template.FuncMap
+}
+
+type StaticCtx struct {
+}
+
+func (ctx StaticCtx) FuncMap() template.FuncMap {
+	sprigMap := sprig.TxtFuncMap()
+
+	sprigMap["Now"] = ctx.now
+	sprigMap["NowFmt"] = ctx.nowFormat
+	sprigMap["ToLower"] = strings.ToLower
+	sprigMap["ToUpper"] = strings.ToUpper
+	sprigMap["TrimSpace"] = strings.TrimSpace
+	sprigMap["Trim"] = ctx.trim
+	sprigMap["UrlEncode"] = url.QueryEscape
+	sprigMap["Base64Encode"] = ctx.base64Encode
+	sprigMap["Base64Decode"] = ctx.base64Decode
+	sprigMap["Split"] = strings.Split
+	sprigMap["RandomString"] = ctx.RandomString
+	sprigMap["Add"] = ctx.add
+	sprigMap["Sub"] = ctx.sub
+	sprigMap["Mult"] = ctx.mult
+	sprigMap["Div"] = ctx.div
+	sprigMap["ParseBool"] = ctx.parseBool
+	sprigMap["ParseFloat"] = ctx.parseFloat
+	sprigMap["ParseInt"] = ctx.parseInt
+	sprigMap["ParseUint"] = ctx.parseUint
+	sprigMap["HumanSize"] = ctx.humanSize
+	sprigMap["KubeSeal"] = ctx.kubeSeal
+
+	return sprigMap
+}
+
+func (ctx StaticCtx) now() string {
+	return ctx.nowFormat("")
+}
+
+func (ctx StaticCtx) nowFormat(format string) string {
+	if format == "" {
+		format = time.RFC3339
+	}
+	return time.Now().UTC().Format(format)
+}
+
+func (ctx StaticCtx) trim(s string, args ...string) string {
+	if len(args) == 0 {
+		return strings.TrimSpace(s)
+	}
+	return strings.Trim(s, args[0])
+}
+
+func (ctx StaticCtx) base64Encode(plain string) string {
+	return base64.StdEncoding.EncodeToString([]byte(plain))
+}
+
+func (ctx StaticCtx) base64Decode(encoded string) string {
+	plain, err := base64.StdEncoding.DecodeString(encoded)
+	if err != nil {
+		return ""
+	}
+	return string(plain)
+}
+
+func (ctx StaticCtx) add(a, b interface{}) interface{} {
+	av := reflect.ValueOf(a)
+	bv := reflect.ValueOf(b)
+
+	if ctx.isFloat(av) || ctx.isFloat(bv) {
+		return ctx.reflectToFloat(av) + ctx.reflectToFloat(bv)
+	}
+	if ctx.isInt(av) {
+		return av.Int() + ctx.reflectToInt(bv)
+	}
+	if ctx.isUint(av) {
+		return av.Uint() + ctx.reflectToUint(bv)
+	}
+
+	return 0
+}
+
+func (ctx StaticCtx) sub(a, b interface{}) interface{} {
+	av := reflect.ValueOf(a)
+	bv := reflect.ValueOf(b)
+
+	if ctx.isFloat(av) || ctx.isFloat(bv) {
+		return ctx.reflectToFloat(av) - ctx.reflectToFloat(bv)
+	}
+	if ctx.isInt(av) {
+		return av.Int() - ctx.reflectToInt(bv)
+	}
+	if ctx.isUint(av) {
+		return av.Uint() - ctx.reflectToUint(bv)
+	}
+
+	return 0
+}
+
+func (ctx StaticCtx) mult(a, b interface{}) interface{} {
+	av := reflect.ValueOf(a)
+	bv := reflect.ValueOf(b)
+
+	if ctx.isFloat(av) || ctx.isFloat(bv) {
+		return ctx.reflectToFloat(av) * ctx.reflectToFloat(bv)
+	}
+	if ctx.isInt(av) {
+		return av.Int() * ctx.reflectToInt(bv)
+	}
+	if ctx.isUint(av) {
+		return av.Uint() * ctx.reflectToUint(bv)
+	}
+
+	return 0
+}
+
+func (ctx StaticCtx) div(a, b interface{}) interface{} {
+	av := reflect.ValueOf(a)
+	bv := reflect.ValueOf(b)
+
+	if ctx.isFloat(av) || ctx.isFloat(bv) {
+		return ctx.reflectToFloat(av) / ctx.reflectToFloat(bv)
+	}
+	if ctx.isInt(av) {
+		return av.Int() / ctx.reflectToInt(bv)
+	}
+	if ctx.isUint(av) {
+		return av.Uint() / ctx.reflectToUint(bv)
+	}
+
+	return 0
+}
+
+func (ctx StaticCtx) parseBool(str string) bool {
+	val, _ := strconv.ParseBool(str)
+	return val
+}
+
+func (ctx StaticCtx) parseFloat(str string) float64 {
+	val, _ := strconv.ParseFloat(str, 64)
+	return val
+}
+
+func (ctx StaticCtx) parseInt(str string, args ...int) int64 {
+	base := 10
+	if len(args) > 0 {
+		base = args[0]
+	}
+	val, _ := strconv.ParseInt(str, base, 64)
+	return val
+}
+
+func (ctx StaticCtx) parseUint(str string, args ...int) uint64 {
+	base := 10
+	if len(args) > 0 {
+		base = args[0]
+	}
+	val, _ := strconv.ParseUint(str, base, 64)
+	return val
+}
+
+func (ctx StaticCtx) humanSize(size interface{}) string {
+	v := reflect.ValueOf(size)
+	return units.HumanSize(ctx.reflectToFloat(v))
+}
+
+func (ctx StaticCtx) reflectToFloat(val reflect.Value) float64 {
+	if ctx.isFloat(val) {
+		return val.Float()
+	}
+	if ctx.isInt(val) {
+		return float64(val.Int())
+	}
+	if ctx.isUint(val) {
+		return float64(val.Uint())
+	}
+
+	return 0
+}
+
+func (ctx StaticCtx) reflectToInt(val reflect.Value) int64 {
+	if ctx.isFloat(val) {
+		return int64(val.Float())
+	}
+	if ctx.isInt(val) || ctx.isUint(val) {
+		return val.Int()
+	}
+
+	return 0
+}
+
+func (ctx StaticCtx) reflectToUint(val reflect.Value) uint64 {
+	if ctx.isFloat(val) {
+		return uint64(val.Float())
+	}
+	if ctx.isInt(val) || ctx.isUint(val) {
+		return val.Uint()
+	}
+
+	return 0
+}
+
+func (ctx StaticCtx) isFloat(val reflect.Value) bool {
+	kind := val.Kind()
+	return kind == reflect.Float32 || kind == reflect.Float64
+}
+
+func (ctx StaticCtx) isInt(val reflect.Value) bool {
+	kind := val.Kind()
+	return kind == reflect.Int || kind == reflect.Int8 || kind == reflect.Int16 || kind == reflect.Int32 || kind == reflect.Int64
+}
+
+func (ctx StaticCtx) isUint(val reflect.Value) bool {
+	kind := val.Kind()
+	return kind == reflect.Uint || kind == reflect.Uint8 || kind == reflect.Uint16 || kind == reflect.Uint32 || kind == reflect.Uint64
+}
+
+// kubeSeal will use the same encryption techniques as the kubeseal application found at
+// https://github.com/bitnami-labs/sealed-secrets
+// This function simply returns the encrypted value that can be written into a kind: SealedSecret
+// resource, but it does not create the entire resource. That's left to the application developer.
+func (ctx StaticCtx) kubeSeal(certData string, namespace string, name string, value string) (string, error) {
+	certs, err := certUtil.ParseCertsPEM([]byte(certData))
+	if err != nil {
+		return "", errors.Wrap(err, "failed to parse cert")
+	}
+
+	if len(certs) == 0 {
+		return "", errors.New("unable to find cert")
+	}
+
+	pubKey, ok := certs[0].PublicKey.(*rsa.PublicKey)
+	if !ok {
+		return "", errors.New("failed to get public key")
+	}
+
+	rnd := rand.Reader
+	sessionKey := make([]byte, 32)
+
+	if _, err := io.ReadFull(rnd, sessionKey); err != nil {
+		return "", errors.Wrap(err, "failed to read random")
+	}
+
+	block, err := aes.NewCipher(sessionKey)
+	if err != nil {
+		return "", errors.Wrap(err, "failed to create cipher")
+	}
+
+	aed, err := cipher.NewGCM(block)
+	if err != nil {
+		return "", errors.Wrap(err, "failed to create galois cipher")
+	}
+
+	// TODO consider options for clusterwide and namespacewide sealed secrets
+	// But this currently only supports creation of a single type of a sealed secret
+	label := []byte(fmt.Sprintf("%s/%s", namespace, name))
+	rsaCiphertext, err := rsa.EncryptOAEP(sha256.New(), rnd, pubKey, sessionKey, label)
+	if err != nil {
+		return "", errors.Wrap(err, "failed to encrypt")
+	}
+
+	cipherText := make([]byte, 2)
+	binary.BigEndian.PutUint16(cipherText, uint16(len(rsaCiphertext)))
+	cipherText = append(cipherText, rsaCiphertext...)
+
+	zeroNonce := make([]byte, aed.NonceSize())
+
+	cipherText = aed.Seal(cipherText, zeroNonce, []byte(value), nil)
+
+	encodedCipherText := base64.StdEncoding.EncodeToString(cipherText)
+	return encodedCipherText, nil
+}
diff --git a/pkg/upstream/fetch.go b/pkg/upstream/fetch.go
index 9fd2869f7..f04eb2341 100644
--- a/pkg/upstream/fetch.go
+++ b/pkg/upstream/fetch.go
@@ -10,6 +10,7 @@ import (
 type FetchOptions struct {
 	HelmRepoName string
 	HelmRepoURI  string
+	LocalPath    string
 }
 
 func FetchUpstream(upstreamURI string, fetchOptions *FetchOptions) (*Upstream, error) {
@@ -34,7 +35,7 @@ func downloadUpstream(upstreamURI string, fetchOptions *FetchOptions) (*Upstream
 		return downloadHelm(u, fetchOptions.HelmRepoURI)
 	}
 	if u.Scheme == "replicated" {
-		return downloadReplicated(u)
+		return downloadReplicated(u, fetchOptions.LocalPath)
 	}
 	if u.Scheme == "file" {
 		return readFilesFromURI(upstreamURI)
diff --git a/pkg/upstream/replicated.go b/pkg/upstream/replicated.go
index b08b733ef..4605ff1de 100644
--- a/pkg/upstream/replicated.go
+++ b/pkg/upstream/replicated.go
@@ -2,6 +2,7 @@ package upstream
 
 import (
 	"archive/tar"
+	"bytes"
 	"compress/gzip"
 	"encoding/base64"
 	"fmt"
@@ -11,17 +12,21 @@ import (
 	"net/url"
 	"os"
 	"path"
+	"path/filepath"
 	"strings"
 
 	"github.com/manifoldco/promptui"
 	"github.com/pkg/errors"
+	kotsv1beta1 "github.com/replicatedhq/kots/kotskinds/apis/kots/v1beta1"
 	kotsscheme "github.com/replicatedhq/kots/kotskinds/client/kotsclientset/scheme"
+	"github.com/replicatedhq/kots/pkg/template"
 	"github.com/replicatedhq/kots/pkg/util"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"k8s.io/apimachinery/pkg/runtime/serializer/json"
 	"k8s.io/client-go/kubernetes/scheme"
 )
 
 type ReplicatedUpstream struct {
-	Host         string
 	Channel      *string
 	AppSlug      string
 	VersionLabel *string
@@ -36,34 +41,54 @@ type Release struct {
 	Manifests map[string][]byte
 }
 
-func downloadReplicated(u *url.URL) (*Upstream, error) {
-	replicatedUpstream, err := parseReplicatedURL(u)
-	if err != nil {
-		return nil, errors.Wrap(err, "failed to parse replicated upstream")
-	}
+func downloadReplicated(u *url.URL, localPath string) (*Upstream, error) {
+	var release *Release
 
-	licenseID, err := getSuccessfulHeadResponse(replicatedUpstream)
-	if err != nil {
-		return nil, errors.Wrap(err, "failed to get successful head response")
-	}
+	if localPath == "" {
+		replicatedUpstream, err := parseReplicatedURL(u)
+		if err != nil {
+			return nil, errors.Wrap(err, "failed to parse replicated upstream")
+		}
 
-	release, err := downloadReplicatedApp(replicatedUpstream, licenseID)
-	if err != nil {
-		return nil, errors.Wrap(err, "failed to download replicated app")
+		license, err := getSuccessfulHeadResponse(replicatedUpstream)
+		if err != nil {
+			return nil, errors.Wrap(err, "failed to get successful head response")
+		}
+
+		downloadedRelease, err := downloadReplicatedApp(replicatedUpstream, license)
+		if err != nil {
+			return nil, errors.Wrap(err, "failed to download replicated app")
+		}
+
+		release = downloadedRelease
+	} else {
+		parsedLocalRelease, err := readReplicatedAppFromLocalPath(localPath)
+		if err != nil {
+			return nil, errors.Wrap(err, "failed to read replicated app from local path")
+		}
+
+		release = parsedLocalRelease
 	}
 
-	app, err := findAppInRelease(release)
-	if err != nil {
-		return nil, errors.Wrap(err, "failed to get app from release")
+	application := findAppInRelease(release)
+	config := findConfigInRelease(release)
+	if config != nil {
+		configValues, err := createEmptyConfigValues(application.Name, config)
+		if err != nil {
+			return nil, errors.Wrap(err, "failed to create empty config values")
+		}
+
+		release.Manifests["config/values.yaml"] = mustMarshalConfigValues(configValues)
 	}
 
 	files, err := releaseToFiles(release)
 	if err != nil {
 		return nil, errors.Wrap(err, "failed to get files from release")
 	}
+
 	upstream := &Upstream{
 		URI:   u.RequestURI(),
-		Name:  app.Name,
+		Name:  application.Name,
 		Files: files,
 		Type:  "replicated",
 	}
@@ -71,15 +96,19 @@ func downloadReplicated(u *url.URL) (*Upstream, error) {
 	return upstream, nil
 }
 
-func (r *ReplicatedUpstream) getRequest(method string, licenseID string) (*http.Request, error) {
-	proto := "https"
+func (r *ReplicatedUpstream) getRequest(method string, license *kotsv1beta1.License) (*http.Request, error) {
+	u, err := url.Parse(license.Spec.Endpoint)
+	if err != nil {
+		return nil, errors.Wrap(err, "failed to parse endpoint from license")
+	}
 
-	// quick and dirty hack to ensure we always have https, except don't require it when running completely local (dev)
-	if strings.HasPrefix(r.Host, "localhost") {
-		proto = "http"
+	hostname := u.Hostname()
+	if u.Port() != "" {
+		hostname = fmt.Sprintf("%s:%s", u.Hostname(), u.Port())
 	}
 
-	url := fmt.Sprintf("%s://%s/release/%s", proto, r.Host, r.AppSlug)
+	url := fmt.Sprintf("%s://%s/release/%s", u.Scheme, hostname, license.Spec.AppSlug)
+
 	if r.Channel != nil {
 		url = fmt.Sprintf("%s/%s", url, *r.Channel)
 	}
@@ -89,21 +118,19 @@ func (r *ReplicatedUpstream) getRequest(method string, licenseID string) (*http.
 		return nil, errors.Wrap(err, "failed to call newrequest")
 	}
 
-	req.Header.Set("Authorization", fmt.Sprintf("Basic %s", base64.StdEncoding.EncodeToString([]byte(fmt.Sprintf("%s:%s", licenseID, licenseID)))))
+	req.Header.Set("Authorization", fmt.Sprintf("Basic %s", base64.StdEncoding.EncodeToString([]byte(fmt.Sprintf("%s:%s", license.Spec.LicenseID, license.Spec.LicenseID)))))
 
 	return req, nil
 }
 
 func parseReplicatedURL(u *url.URL) (*ReplicatedUpstream, error) {
-	replicatedUpstream := ReplicatedUpstream{
-		Host: "replicated.app",
-	}
+	replicatedUpstream := ReplicatedUpstream{}
 
 	if u.User != nil {
 		if u.User.Username() != "" {
 			replicatedUpstream.AppSlug = u.User.Username()
-			host := u.Hostname()
-			replicatedUpstream.VersionLabel = &host
+			versionLabel := u.Hostname()
+			replicatedUpstream.VersionLabel = &versionLabel
 		}
 	}
 
@@ -115,14 +142,10 @@ func parseReplicatedURL(u *url.URL) (*ReplicatedUpstream, error) {
 		}
 	}
 
-	if u.Query().Get("host") != "" {
-		replicatedUpstream.Host = u.Query().Get("host")
-	}
-
 	return &replicatedUpstream, nil
 }
 
-func getSuccessfulHeadResponse(replicatedUpstream *ReplicatedUpstream) (string, error) {
+func getSuccessfulHeadResponse(replicatedUpstream *ReplicatedUpstream) (*kotsv1beta1.License, error) {
 	templates := &promptui.PromptTemplates{
 		Prompt:  "{{ . | bold }} ",
 		Valid:   "{{ . | green }} ",
@@ -131,12 +154,19 @@ func getSuccessfulHeadResponse(replicatedUpstream *ReplicatedUpstream) (string,
 	}
 
 	prompt := promptui.Prompt{
-		Label:     "License ID",
+		Label:     "Enter the path to your license file",
 		Templates: templates,
 		Validate: func(input string) error {
-			if len(input) < 24 {
-				return errors.New("invalid license")
+			_, err := os.Stat(input)
+			if os.IsNotExist(err) {
+				return errors.New("File does not exist")
 			}
+
+			_, err = parseLicenseFromFile(input)
+			if err != nil {
+				return errors.New("Not a valid license file")
+			}
+
 			return nil
 		},
 	}
@@ -150,29 +180,69 @@ func getSuccessfulHeadResponse(replicatedUpstream *ReplicatedUpstream) (string,
 			continue
 		}
 
-		headReq, err := replicatedUpstream.getRequest("HEAD", result)
+		license, err := parseLicenseFromFile(result)
+		if err != nil {
+			return nil, errors.Wrap(err, "failed to parse license from file")
+		}
+
+		headReq, err := replicatedUpstream.getRequest("HEAD", license)
 		if err != nil {
-			return "", errors.Wrap(err, "failed to create http request")
+			return nil, errors.Wrap(err, "failed to create http request")
 		}
 		headResp, err := http.DefaultClient.Do(headReq)
 		if err != nil {
-			return "", errors.Wrap(err, "failed to execute head request")
+			return nil, errors.Wrap(err, "failed to execute head request")
 		}
 
 		if headResp.StatusCode == 401 {
-			continue
+			return nil, errors.Wrap(err, "license was not accepted")
 		}
+
 		if headResp.StatusCode >= 400 {
-			return "", errors.Errorf("expected result from head request: %d", headResp.StatusCode)
+			return nil, errors.Errorf("expected result from head request: %d", headResp.StatusCode)
 		}
 
-		return result, nil
+		return license, nil
+	}
+}
+
+func readReplicatedAppFromLocalPath(localPath string) (*Release, error) {
+	release := Release{
+		Manifests: make(map[string][]byte),
+	}
+
+	err := filepath.Walk(localPath,
+		func(path string, info os.FileInfo, err error) error {
+			if err != nil {
+				return err
+			}
+
+			if info.IsDir() {
+				return nil
+			}
+
+			contents, err := ioutil.ReadFile(path)
+			if err != nil {
+				return err
+			}
+
+			// remove localpath prefix
+			appPath := strings.TrimPrefix(path, localPath)
+			appPath = strings.TrimLeft(appPath, string(os.PathSeparator))
+
+			release.Manifests[appPath] = contents
+
+			return nil
+		})
+	if err != nil {
+		return nil, errors.Wrap(err, "failed to walk local path")
 	}
 
+	return &release, nil
 }
 
-func downloadReplicatedApp(replicatedUpstream *ReplicatedUpstream, licenseID string) (*Release, error) {
-	getReq, err := replicatedUpstream.getRequest("GET", licenseID)
+func downloadReplicatedApp(replicatedUpstream *ReplicatedUpstream, license *kotsv1beta1.License) (*Release, error) {
+	getReq, err := replicatedUpstream.getRequest("GET", license)
 	if err != nil {
 		return nil, errors.Wrap(err, "failed to create http request")
 	}
@@ -227,29 +297,108 @@ func downloadReplicatedApp(replicatedUpstream *ReplicatedUpstream, licenseID str
 	return &release, nil
 }
 
-func findAppInRelease(release *Release) (*App, error) {
+func mustMarshalConfigValues(configValues *kotsv1beta1.ConfigValues) []byte {
+	kotsscheme.AddToScheme(scheme.Scheme)
+
+	s := json.NewYAMLSerializer(json.DefaultMetaFactory, scheme.Scheme, scheme.Scheme)
+
+	var b bytes.Buffer
+	if err := s.Encode(configValues, &b); err != nil {
+		panic(err)
+	}
+
+	return b.Bytes()
+}
+
+func createEmptyConfigValues(applicationName string, config *kotsv1beta1.Config) (*kotsv1beta1.ConfigValues, error) {
+	emptyValues := kotsv1beta1.ConfigValuesSpec{
+		Values: map[string]string{},
+	}
+
+	builder := template.Builder{}
+	builder.AddCtx(template.StaticCtx{})
+
+	for _, group := range config.Spec.Groups {
+		for _, item := range group.Items {
+			if item.Value != "" {
+				rendered, err := builder.RenderTemplate(item.Name, item.Value)
+				if err != nil {
+					return nil, errors.Wrap(err, "failed to render config item value")
+				}
+
+				emptyValues.Values[item.Name] = rendered
+			}
+		}
+	}
+
+	configValues := kotsv1beta1.ConfigValues{
+		TypeMeta: metav1.TypeMeta{
+			APIVersion: "kots.io/v1beta1",
+			Kind:       "ConfigValues",
+		},
+		ObjectMeta: metav1.ObjectMeta{
+			Name: applicationName,
+		},
+		Spec: emptyValues,
+	}
+
+	return &configValues, nil
+}
+
+func findConfigInRelease(release *Release) *kotsv1beta1.Config {
+	kotsscheme.AddToScheme(scheme.Scheme)
+	for _, content := range release.Manifests {
+		decode := scheme.Codecs.UniversalDeserializer().Decode
+		obj, gvk, err := decode(content, nil, nil)
+		if err != nil {
+			continue
+		}
+
+		if gvk.Group == "kots.io" {
+			if gvk.Version == "v1beta1" {
+				if gvk.Kind == "Config" {
+					return obj.(*kotsv1beta1.Config)
+				}
+			}
+		}
+	}
+
+	return nil
+}
+
+func findAppInRelease(release *Release) *kotsv1beta1.Application {
 	kotsscheme.AddToScheme(scheme.Scheme)
 	for _, content := range release.Manifests {
 		decode := scheme.Codecs.UniversalDeserializer().Decode
-		_, gvk, err := decode(content, nil, nil)
+		obj, gvk, err := decode(content, nil, nil)
 		if err != nil {
 			continue
 		}
 
 		if gvk.Group == "kots.io" {
 			if gvk.Version == "v1beta1" {
-				if gvk.Kind == "App" {
-					return nil, nil
+				if gvk.Kind == "Application" {
+					return obj.(*kotsv1beta1.Application)
 				}
 			}
 		}
 	}
 
 	// Using Ship apps for now, so let's create an app manifest on the fly
-	app := &App{
-		Name: "Ship App",
+	app := &kotsv1beta1.Application{
+		TypeMeta: metav1.TypeMeta{
+			APIVersion: "kots.io/v1beta1",
+			Kind:       "Application",
+		},
+		ObjectMeta: metav1.ObjectMeta{
+			Name: "replicated-kots-app",
+		},
+		Spec: kotsv1beta1.ApplicationSpec{
+			Title: "Replicated Kots App",
+			Icon:  "",
+		},
 	}
-	return app, nil
+	return app
 }
 
 func releaseToFiles(release *Release) ([]UpstreamFile, error) {
@@ -293,3 +442,23 @@ func releaseToFiles(release *Release) ([]UpstreamFile, error) {
 	}
 	return upstreamFiles, nil
 }
+
+func parseLicenseFromFile(filename string) (*kotsv1beta1.License, error) {
+	contents, err := ioutil.ReadFile(filename)
+	if err != nil {
+		return nil, errors.Wrap(err, "failed to read license file")
+	}
+
+	kotsscheme.AddToScheme(scheme.Scheme)
+	decode := scheme.Codecs.UniversalDeserializer().Decode
+	license, gvk, err := decode(contents, nil, nil)
+	if err != nil {
+		return nil, errors.Wrap(err, "unable to decode license file")
+	}
+
+	if gvk.Group != "kots.io" || gvk.Version != "v1beta1" || gvk.Kind != "License" {
+		return nil, errors.New("not an application license")
+	}
+
+	return license.(*kotsv1beta1.License), nil
+}
diff --git a/pkg/upstream/write.go b/pkg/upstream/write.go
index 3b956f64f..33bf532ae 100644
--- a/pkg/upstream/write.go
+++ b/pkg/upstream/write.go
@@ -26,6 +26,8 @@ func (u *Upstream) WriteUpstream(options WriteOptions) error {
 	_, err := os.Stat(renderDir)
 	if err == nil {
 		if options.Overwrite {
+			// TODO there are some files (config/values.yaml) that we need to keep
+
 			if err := os.RemoveAll(renderDir); err != nil {
 				return errors.Wrap(err, "failed to remove previous content in upstream")
 			}


Test patch:
diff --git a/pkg/template/builder_test.go b/pkg/template/builder_test.go
new file mode 100644
index 000000000..b39529b46
--- /dev/null
+++ b/pkg/template/builder_test.go
@@ -0,0 +1,229 @@
+package template
+
+import (
+	"testing"
+	"text/template"
+
+	"github.com/stretchr/testify/require"
+)
+
+type testContext struct {
+}
+
+func (ctx testContext) FuncMap() template.FuncMap {
+	return map[string]interface{}{
+		"ConfigOption": func(key string) interface{} {
+			switch key {
+			case "option_1":
+				return "Option 1"
+			case "option_2":
+				return "Option 2"
+			}
+			return ""
+		},
+	}
+}
+
+type testcase interface {
+	name() string
+	runTest(t *testing.T, builder Builder)
+}
+
+type strcase struct {
+	Name     string
+	Template string
+	Expected string
+}
+
+func (c strcase) name() string {
+	return c.Name
+}
+func (c strcase) runTest(t *testing.T, builder Builder) {
+	built, _ := builder.String(c.Template)
+	require.New(t).Equal(c.Expected, built)
+}
+
+type intcase struct {
+	Name     string
+	Template string
+	Expected int64
+}
+
+func (c intcase) name() string {
+	return c.Name
+}
+func (c intcase) runTest(t *testing.T, builder Builder) {
+	built, _ := builder.Int(c.Template, 0)
+	require.New(t).Equal(c.Expected, built)
+}
+
+type uintcase struct {
+	Name     string
+	Template string
+	Expected uint64
+}
+
+func (c uintcase) name() string {
+	return c.Name
+}
+func (c uintcase) runTest(t *testing.T, builder Builder) {
+	built, _ := builder.Uint(c.Template, 0)
+	require.New(t).Equal(c.Expected, built)
+}
+
+type floatcase struct {
+	Name     string
+	Template string
+	Expected float64
+}
+
+func (c floatcase) name() string {
+	return c.Name
+}
+func (c floatcase) runTest(t *testing.T, builder Builder) {
+	built, _ := builder.Float64(c.Template, 0.0)
+	require.New(t).Equal(c.Expected, built)
+}
+
+type boolcase struct {
+	Name     string
+	Template string
+	Expected bool
+}
+
+func (c boolcase) name() string {
+	return c.Name
+}
+func (c boolcase) runTest(t *testing.T, builder Builder) {
+	built, _ := builder.Bool(c.Template, false)
+	require.New(t).Equal(c.Expected, built)
+}
+
+func TestBuildStrings(t *testing.T) {
+	// "Now"
+	// "NowFmt"
+	cases := []testcase{
+		strcase{
+			Name:     "Test ToLower",
+			Template: `{{repl ToLower "ABcd87-()"}}`,
+			Expected: "abcd87-()",
+		},
+		strcase{
+			Name:     "Test ToUpper",
+			Template: `{{repl ToUpper "ABcd87-()"}}`,
+			Expected: "ABCD87-()",
+		},
+		strcase{
+			Name:     "Test TrimSpace",
+			Template: `{{repl TrimSpace "   maybe spaces,\tmaybe not \t  "}}`,
+			Expected: "maybe spaces,\tmaybe not",
+		},
+		strcase{
+			Name:     "Test Trim",
+			Template: `{{repl Trim "aaaaamaybe spaces,\tmaybe not \taaa" "a"}}`,
+			Expected: "maybe spaces,\tmaybe not \t",
+		},
+		strcase{
+			Name:     "Test UrlEncode",
+			Template: `{{repl UrlEncode "?some url unsafe=%value"}}`,
+			Expected: "%3Fsome+url+unsafe%3D%25value",
+		},
+		strcase{
+			Name:     "Test Base64Encode",
+			Template: `{{repl Base64Encode "clear text"}}`,
+			Expected: "Y2xlYXIgdGV4dA==",
+		},
+		strcase{
+			Name:     "Test Base64Decode",
+			Template: `{{repl Base64Decode "Y2xlYXIgdGV4dA=="}}`,
+			Expected: "clear text",
+		},
+		strcase{
+			Name:     "Test Split",
+			Template: `{{repl Split "1,2,3,4" ","}}`,
+			Expected: "[1 2 3 4]",
+		},
+		strcase{
+			Name:     "Test HumanSize",
+			Template: `{{repl HumanSize 387346587344}}`,
+			Expected: "387.3GB",
+		},
+		strcase{
+			Name:     "Test ConfigOption",
+			Template: `{{repl ConfigOption "option_1"}}`,
+			Expected: "Option 1",
+		},
+		strcase{
+			Name:     "Test ConfigOption",
+			Template: `{{repl ConfigOption "option_2"}}`,
+			Expected: "Option 2",
+		},
+		strcase{
+			Name:     "Test ConfigOption",
+			Template: `{{repl ConfigOption "option_3"}}`,
+			Expected: "",
+		},
+		intcase{
+			Name:     "Test Mult",
+			Template: `{{repl Mult 34 2}}`,
+			Expected: 68,
+		},
+		intcase{
+			Name:     "Test ParseInt",
+			Template: `{{repl ParseInt "-15"}}`,
+			Expected: -15,
+		},
+		uintcase{
+			Name:     "Test Add",
+			Template: `{{repl Add 2 3}}`,
+			Expected: 5,
+		},
+		uintcase{
+			Name:     "Test ParseUint",
+			Template: `{{repl ParseUint "15"}}`,
+			Expected: 15,
+		},
+		floatcase{
+			Name:     "Test Sub",
+			Template: `{{repl Sub 1.5 1}}`,
+			Expected: 0.5,
+		},
+		floatcase{
+			Name:     "Test Div",
+			Template: `{{repl Div 15.0 2}}`,
+			Expected: 7.5,
+		},
+		floatcase{
+			Name:     "Test ParseFloat",
+			Template: `{{repl ParseFloat "1.5"}}`,
+			Expected: 1.5,
+		},
+		boolcase{
+			Name:     "Test ParseBool",
+			Template: `{{repl ParseBool "True"}}`,
+			Expected: true,
+		},
+	}
+
+	builder := Builder{}
+	builder.AddCtx(StaticCtx{})
+	builder.AddCtx(testContext{})
+
+	for _, test := range cases {
+		t.Run(test.name(), func(t *testing.T) {
+			test.runTest(t, builder)
+		})
+	}
+
+	t.Run("Test bad values", func(t *testing.T) {
+		built, err := builder.String("{{repl ParseBool True}}")
+		require.New(t).Error(err)
+		require.New(t).Equal("", built)
+	})
+
+	t.Run("Test broken template syntax", func(t *testing.T) {
+		built, err := builder.String("{{repl SomeFunc")
+		require.New(t).Error(err)
+		require.New(t).Equal("", built)
+	})
+}
diff --git a/pkg/template/random_test.go b/pkg/template/random_test.go
new file mode 100644
index 000000000..59a1430ea
--- /dev/null
+++ b/pkg/template/random_test.go
@@ -0,0 +1,92 @@
+package template
+
+import (
+	"fmt"
+	_ "regexp"
+	"testing"
+	_ "unicode/utf8"
+
+	"github.com/stretchr/testify/assert"
+	_ "github.com/stretchr/testify/require"
+)
+
+func TestGenerateRandomString(t *testing.T) {
+	ctx := &StaticCtx{}
+	str := ctx.RandomString(100)
+	assert.Len(t, str, 100)
+	assert.Regexp(t, DefaultCharset, str)
+}
+
+func TestGenerateRandomStringTemplates(t *testing.T) {
+	tests := []struct {
+		name           string
+		length         int
+		outputRegex    string
+		templateString string
+	}{
+		{
+			name:           "DefaultCharset",
+			length:         100,
+			outputRegex:    DefaultCharset,
+			templateString: `{{repl RandomString 100}}`,
+		},
+		{
+			name:           "ExplicitDefaultCharset",
+			length:         100,
+			outputRegex:    DefaultCharset,
+			templateString: fmt.Sprintf("{{repl RandomString 100 %q}}", DefaultCharset),
+		},
+		{
+			name:           "lowercase",
+			length:         100,
+			outputRegex:    "[a-z]",
+			templateString: `{{repl RandomString 100 "[a-z]"}}`,
+		},
+		{
+			name:           "very restricted charset aB",
+			length:         100,
+			outputRegex:    "[aB]",
+			templateString: `{{repl RandomString 100 "[aB]"}}`,
+		},
+		{
+			name:           "not lowercase",
+			length:         1000,
+			outputRegex:    "[^a-z]",
+			templateString: `{{repl RandomString 1000 "[^a-z]"}}`,
+		},
+		{
+			name:           "base64",
+			length:         1000,
+			outputRegex:    "[A-Za-z0-9+/]",
+			templateString: `{{repl RandomString 1000 "[A-Za-z0-9+/]"}}`,
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			// TODO
+
+			// req := require.New(t)
+
+			// builderBuilder := &BuilderBuilder{
+			// 	Logger: &logger.TestLogger{T: t},
+			// 	Viper:  viper.New(),
+			// }
+
+			// builder := builderBuilder.NewBuilder(
+			// 	builderBuilder.NewStaticContext(),
+			// )
+
+			// outputString, err := builder.String(tt.templateString)
+			// req.NoError(err)
+
+			// req.Equal(utf8.RuneCountInString(outputString), tt.length, "%q should have length %d", outputString, tt.length)
+
+			// tt.outputRegex = fmt.Sprintf("^%s+$", tt.outputRegex)
+
+			// matcher := regexp.MustCompile(tt.outputRegex)
+
+			// req.True(matcher.MatchString(outputString), "String %q must match %q", outputString, tt.outputRegex)
+		})
+	}
+}
diff --git a/pkg/template/static_context_test.go b/pkg/template/static_context_test.go
new file mode 100644
index 000000000..dfa6f8c9b
--- /dev/null
+++ b/pkg/template/static_context_test.go
@@ -0,0 +1,60 @@
+package template
+
+import (
+	"testing"
+
+	"github.com/stretchr/testify/require"
+)
+
+func TestStaticContext_kubeSeal_badCert(t *testing.T) {
+	req := require.New(t)
+
+	ctx := StaticCtx{}
+
+	cert := `-----BEGIN CERTIFICATE-----
+SPA=
+-----END CERTIFICATE-----`
+
+	sealed, err := ctx.kubeSeal(cert, "default", "mysecret", "clear")
+	req.Error(err, "invalid cert should return an error")
+	req.Empty(sealed, "should return empty string with provided invalid cert")
+}
+
+func TestStaticContext_kubeSeal_goodCert(t *testing.T) {
+	req := require.New(t)
+
+	ctx := StaticCtx{}
+
+	cert := `-----BEGIN CERTIFICATE-----
+MIIErTCCApWgAwIBAgIQCDeIIck6VL8U9rDEP8ZECjANBgkqhkiG9w0BAQsFADAA
+MB4XDTE5MDEyNTE1MTIzMFoXDTI5MDEyMjE1MTIzMFowADCCAiIwDQYJKoZIhvcN
+AQEBBQADggIPADCCAgoCggIBALVuzNLJdwJ+cNkSINeMOmSgusRkkbZ5pqb7oeyw
+l6CwoqE2pshplKwqs5jDjidkGyJ/TjIa8Ar1cOTY3GO+OVc5xNQoTrCWhFZA9v/Q
+cY8aw91BV1Fha80gBgN4hghdiZnATLLBgT1pgCGnQhH8GLunQqSH+lV3TS0fnUGZ
+ivUld2gU8bvjoBzdd0o8aW/5VDYlyOmP9MnMldOcQ4kFNpdAtnKOBAvoxv4C94v6
+2aUo8j7XFpJpYJeECtlTATRvRC1pvDok3c5Mr7hgPy8Gsdatc6xvMkY2sZ0GBP4j
+ta2k2wGT8pmXUFMlSKrBYoOZ5t/VjalMZBUUYcCnkcFrDrm5p7HW1upei+z5THkg
+Y/H0foPj91vkB0BeL00QZOu30uTAVL+F/1Al0lg4VtTHiNMIruos91+F7SweSIXZ
+TkvFPpPK3d2Jt55o/cuW5m/JxzNqMlpVD2Foc7OoWy0PodPy+rFWYgRBUiGShq/2
+rC2UXCCisK/NL1ellW5p6K12tkBwoYLROe2IVPGVF7OCzLU8IbKHCzZ3E/O25CdL
+7cxC62kvCDNJI+s+cpRDg1OZppir6NCbEPPlQbmRPS/8NN0HJKULPCiGcpvGHG4r
+tWdkN8tc2wRyRQGAdxJr1CD0XLQO+Xo3f2wfvLiBwLpgqn33jfH3Ybnl9vU1jEYm
+abi9AgMBAAGjIzAhMA4GA1UdDwEB/wQEAwIAATAPBgNVHRMBAf8EBTADAQH/MA0G
+CSqGSIb3DQEBCwUAA4ICAQCTIwRIbCg+TB6/ZSzj1/52o/yEuqlOne2Og22B14pU
+JiwfpWm5GgG0jOBNHZ7f3EL/bdB5lQJXtvcAx8SwBLk8g/1ieSCcm23kDgdkls+F
+om0uiyXWTqJC+vktPhWyzFm3ltvHFNOcxo7eRXica7gNKbcFTQWmdkI+SwuBkBYp
+gQjxAZUnpN8byAyuZwPTRWVNmUF21DarC6Ol5TDt4HBGFv5ItLVxVh40t10jghOg
+xgr1i38dxZzaYQA+WdeDlJqbsu5tKGihEjBAgAMbhDe194y0ROjZJtdKG/gH7P0g
+ttIrjatPvoVLeHiS1bRpLEwbSOYi2yecQcvHLlsKUedQ/168oLR94jnlS8jejRAH
+WKwVf1QBx/UJWDW6SsunFuLMNUDUhhTfR6zOQNkD8AwIH7wsENIz0Denp/R0rsyJ
+bUrDhgY9tB4JpDXYcquL+zkC7Ivc3XBFNCEp5H/9njoDXHhit6Dv2eTG3Tu05Uzf
+Jrav2KaQ9pp8Oj28C1VTmTPMqZPpF7ZyfrFOHj/9wzOM5ecsQ4Ee91MyTkdyOB29
+f+ipeUSsw5HLNxJpkAZYclBkN+ZnL35FsH4wmEyqZodj+dDgkIiSQq6p3QfQ8NUu
+JgtOJZLvegJ7oeZsG8zHDP2BvmScSPJq6nLTD5i4P04EOIDNjH6GfNFKVwp9GSPT
+yw==
+-----END CERTIFICATE-----`
+
+	sealed, err := ctx.kubeSeal(cert, "default", "mysecret", "clear")
+	req.NoError(err, "kubeSeal should not return an error with a valid cert")
+	req.NotEmpty(sealed, "should return a non empty encrypted secret")
+}
diff --git a/pkg/upstream/replicated_test.go b/pkg/upstream/replicated_test.go
index 34b6ac0c4..ef8fbc5aa 100644
--- a/pkg/upstream/replicated_test.go
+++ b/pkg/upstream/replicated_test.go
@@ -17,7 +17,6 @@ func Test_parseReplicatedURL(t *testing.T) {
 	tests := []struct {
 		name                 string
 		uri                  string
-		expectedHost         string
 		expectedAppSlug      string
 		expectedChannel      *string
 		expectedVersionLabel *string
@@ -26,7 +25,6 @@ func Test_parseReplicatedURL(t *testing.T) {
 		{
 			name:                 "replicated://app-slug",
 			uri:                  "replicated://app-slug",
-			expectedHost:         "replicated.app",
 			expectedAppSlug:      "app-slug",
 			expectedChannel:      nil,
 			expectedVersionLabel: nil,
@@ -35,7 +33,6 @@ func Test_parseReplicatedURL(t *testing.T) {
 		{
 			name:                 "replicated://app-slug@v1.2.0",
 			uri:                  "replicated://app-slug@v1.2.0",
-			expectedHost:         "replicated.app",
 			expectedAppSlug:      "app-slug",
 			expectedChannel:      nil,
 			expectedVersionLabel: &v1_2_0,
@@ -44,21 +41,11 @@ func Test_parseReplicatedURL(t *testing.T) {
 		{
 			name:                 "replicated://app-slug/channel",
 			uri:                  "replicated://app-slug/channel",
-			expectedHost:         "replicated.app",
 			expectedAppSlug:      "app-slug",
 			expectedChannel:      &channel,
 			expectedVersionLabel: nil,
 			expectedSequence:     nil,
 		},
-		{
-			name:                 "replicated://app-slug?host=my.hostname",
-			uri:                  "replicated://app-slug?host=my.hostname",
-			expectedHost:         "my.hostname",
-			expectedAppSlug:      "app-slug",
-			expectedChannel:      nil,
-			expectedVersionLabel: nil,
-			expectedSequence:     nil,
-		},
 	}
 
 	for _, test := range tests {
@@ -70,7 +57,6 @@ func Test_parseReplicatedURL(t *testing.T) {
 
 			replicatedUpstream, err := parseReplicatedURL(u)
 			req.NoError(err)
-			assert.Equal(t, test.expectedHost, replicatedUpstream.Host)
 			assert.Equal(t, test.expectedAppSlug, replicatedUpstream.AppSlug)
 
 			if test.expectedVersionLabel != nil || replicatedUpstream.VersionLabel != nil {