From c359809660c176231bb9f14303adfd3da355364b Mon Sep 17 00:00:00 2001 From: Aaron Schlesinger Date: Fri, 2 Sep 2016 10:43:48 -0700 Subject: [PATCH] feat(*): add a helm mode to steward (#123) Fixes #10 Fixes #139 Ref #23 Tests forthcoming as part of #158 --- DATA_STRUCTURES.md | 1 + Makefile | 89 +- README.md | 48 +- config.go | 2 +- glide.lock | 836 ++++++++++++++---- glide.yaml | 26 +- k8s/claim/event_actions.go | 24 +- k8s/claim/event_actions_test.go | 9 +- k8s/claim/loop.go | 3 +- k8s/claim/loops.go | 1 + main.go | 4 +- ...template.yaml => steward-template-cf.yaml} | 4 + manifests/steward-template-helm.yaml | 77 ++ manifests/tiller-service.yaml | 14 + mode/deprovision_request.go | 5 +- mode/fake/provisioner.go | 6 +- mode/helm/binder.go | 54 ++ mode/helm/cataloger.go | 33 + mode/helm/chart_util.go | 52 ++ mode/helm/common.go | 13 + mode/helm/deprovisioner.go | 57 ++ mode/helm/doc.go | 2 + mode/helm/helm_config_maps.go | 24 + mode/helm/lifecycler.go | 32 + mode/helm/provision_behavior.go | 39 + mode/helm/provisioner.go | 55 ++ mode/helm/release_creator_deleter.go | 22 + mode/helm/steward_values.go | 29 + mode/helm/steward_values_test.go | 9 + mode/helm/tiller_release_creator_deleter.go | 51 ++ mode/helm/unbinder.go | 36 + mode/json_object.go | 39 + mode/json_object_test.go | 22 + mode/provision_response.go | 3 +- mode/service_plan_claim.go | 27 +- mode/utils/helm_config.go | 29 + mode/utils/helm_mode.go | 55 ++ mode/utils/mode_runner.go | 28 +- web/ctxhttp/ctxhttp.go | 73 ++ 39 files changed, 1704 insertions(+), 229 deletions(-) rename manifests/{steward-template.yaml => steward-template-cf.yaml} (93%) create mode 100644 manifests/steward-template-helm.yaml create mode 100644 manifests/tiller-service.yaml create mode 100644 mode/helm/binder.go create mode 100644 mode/helm/cataloger.go create mode 100644 mode/helm/chart_util.go create mode 100644 mode/helm/common.go create mode 100644 mode/helm/deprovisioner.go create mode 100644 mode/helm/doc.go create mode 100644 mode/helm/helm_config_maps.go create mode 100644 mode/helm/lifecycler.go create mode 100644 mode/helm/provision_behavior.go create mode 100644 mode/helm/provisioner.go create mode 100644 mode/helm/release_creator_deleter.go create mode 100644 mode/helm/steward_values.go create mode 100644 mode/helm/steward_values_test.go create mode 100644 mode/helm/tiller_release_creator_deleter.go create mode 100644 mode/helm/unbinder.go create mode 100644 mode/utils/helm_config.go create mode 100644 mode/utils/helm_mode.go create mode 100644 web/ctxhttp/ctxhttp.go diff --git a/DATA_STRUCTURES.md b/DATA_STRUCTURES.md index a2af28a..70ca06d 100644 --- a/DATA_STRUCTURES.md +++ b/DATA_STRUCTURES.md @@ -46,6 +46,7 @@ This object is submitted by the application as JSON in a [`ConfigMap`][configMap - `status-description` - a human-readable explanation of the current `status`. Steward will modify this value, but will ignore any modifications by the application - `instance-id` - for internal use only. The application should not modify this field - `bind-id` - for internal use only. The application should not modify this field +- `extra` - for internal use only. The application should not modify this field # `ServicePlanCreation` diff --git a/Makefile b/Makefile index 95bbccc..15de31a 100644 --- a/Makefile +++ b/Makefile @@ -44,7 +44,7 @@ install-3prs: STEWARD_IMAGE ?= quay.io/deisci/steward:devel -install-steward: +install-cf-steward: ifndef CF_BROKER_NAME $(error CF_BROKER_NAME is undefined) endif @@ -63,19 +63,78 @@ endif ifndef CF_BROKER_PASSWORD $(error CF_BROKER_PASSWORD is undefined) endif - sed "s/#cf_broker_name#/${CF_BROKER_NAME}/g" manifests/steward-template.yaml > manifests/${CF_BROKER_NAME}-steward.yaml - sed -i.bak "s/#cf_broker_scheme#/${CF_BROKER_SCHEME}/g" manifests/${CF_BROKER_NAME}-steward.yaml - sed -i.bak "s/#cf_broker_hostname#/${CF_BROKER_HOSTNAME}/g" manifests/${CF_BROKER_NAME}-steward.yaml - sed -i.bak "s/#cf_broker_port#/${CF_BROKER_PORT}/g" manifests/${CF_BROKER_NAME}-steward.yaml - sed -i.bak "s/#cf_broker_username#/${CF_BROKER_USERNAME}/g" manifests/${CF_BROKER_NAME}-steward.yaml - sed -i.bak "s/#cf_broker_password#/${CF_BROKER_PASSWORD}/g" manifests/${CF_BROKER_NAME}-steward.yaml - sed -i.bak "s#\#steward_image\##${STEWARD_IMAGE}#g" manifests/${CF_BROKER_NAME}-steward.yaml - rm manifests/${CF_BROKER_NAME}-steward.yaml.bak + sed "s/#cf_broker_name#/${CF_BROKER_NAME}/g" manifests/steward-template-cf.yaml > manifests/${CF_BROKER_NAME}-cf-steward.yaml + sed -i.bak "s/#cf_broker_scheme#/${CF_BROKER_SCHEME}/g" manifests/${CF_BROKER_NAME}-cf-steward.yaml + sed -i.bak "s/#cf_broker_hostname#/${CF_BROKER_HOSTNAME}/g" manifests/${CF_BROKER_NAME}-cf-steward.yaml + sed -i.bak "s/#cf_broker_port#/${CF_BROKER_PORT}/g" manifests/${CF_BROKER_NAME}-cf-steward.yaml + sed -i.bak "s/#cf_broker_username#/${CF_BROKER_USERNAME}/g" manifests/${CF_BROKER_NAME}-cf-steward.yaml + sed -i.bak "s/#cf_broker_password#/${CF_BROKER_PASSWORD}/g" manifests/${CF_BROKER_NAME}-cf-steward.yaml + sed -i.bak "s#\#steward_image\##${STEWARD_IMAGE}#g" manifests/${CF_BROKER_NAME}-cf-steward.yaml + rm manifests/${CF_BROKER_NAME}-cf-steward.yaml.bak kubectl get deployment ${CF_BROKER_NAME}-steward --namespace=steward && \ - kubectl apply -f manifests/${CF_BROKER_NAME}-steward.yaml || \ - kubectl create -f manifests/${CF_BROKER_NAME}-steward.yaml + kubectl apply -f manifests/${CF_BROKER_NAME}-cf-steward.yaml || \ + kubectl create -f manifests/${CF_BROKER_NAME}-cf-steward.yaml -deploy: install-namespace install-3prs install-steward - -dev-deploy: docker-build docker-push - STEWARD_IMAGE=${IMAGE} $(MAKE) deploy +install-helm-steward: +ifndef HELM_CHART_NAME + $(error HELM_CHART_NAME is undefined) +endif +ifndef HELM_TILLER_IP + $(error HELM_TILLER_IP is undefined) +endif +ifndef HELM_TILLER_PORT + $(error HELM_TILLER_PORT is undefined) +endif +ifndef HELM_CHART_URL + $(error HELM_CHART_URL is undefined) +endif +ifndef HELM_CHART_INSTALL_NAMESPACE + $(error HELM_CHART_INSTALL_NAMESPACE is undefined) +endif +ifndef HELM_PROVISION_BEHAVIOR + $(error HELM_PROVISION_BEHAVIOR is undefined) +endif +ifndef HELM_SERVICE_ID + $(error HELM_SERVICE_ID is undefined) +endif +ifndef HELM_SERVICE_NAME + $(error HELM_SERVICE_NAME is undefined) +endif +ifndef HELM_SERVICE_DESCRIPTION + $(error HELM_SERVICE_DESCRIPTION is undefined) +endif +ifndef HELM_PLAN_ID + $(error HELM_PLAN_ID is undefined) +endif +ifndef HELM_PLAN_NAME + $(error HELM_PLAN_NAME is undefined) +endif +ifndef HELM_PLAN_DESCRIPTION + $(error HELM_PLAN_DESCRIPTION is undefined) +endif + sed "s/#helm_name#/${HELM_CHART_NAME}/g" manifests/steward-template-helm.yaml > manifests/${HELM_CHART_NAME}-steward.yaml + sed "s/#helm_tiller_ip#/${HELM_TILLER_IP}/g" manifests/steward-template-helm.yaml > manifests/${HELM_CHART_NAME}-steward.yaml + sed -i.bak "s/#helm_tiller_port#/${HELM_TILLER_PORT}/g" manifests/${HELM_CHART_NAME}-helm-steward.yaml + sed -i.bak "s/#helm_chart_url#/${HELM_CHART_URL}/g" manifests/${HELM_CHART_NAME}-helm-steward.yaml + sed -i.bak "s/#helm_chart_install_namespace#/${HELM_CHART_INSTALL_NAMESPACE}/g" manifests/${HELM_CHART_NAME}-helm-steward.yaml + sed -i.bak "s/#helm_provision_behavior#/${HELM_PROVISION_BEHAVIOR}/g" manifests/${HELM_CHART_NAME}-helm-steward.yaml + sed -i.bak "s/#helm_service_id#/${HELM_SERVICE_ID}/g" manifests/${HELM_CHART_NAME}-helm-steward.yaml + sed -i.bak "s#\#helm_service_name\##${HELM_SERVICE_NAME}#g" manifests/${HELM_CHART_NAME}-helm-steward.yaml + sed -i.bak "s#\#helm_service_description\##${HELM_SERVICE_DESCRIPTION}#g" manifests/${HELM_CHART_NAME}-helm-steward.yaml + sed -i.bak "s#\#helm_plan_id\##${HELM_PLAN_ID}#g" manifests/${HELM_CHART_NAME}-helm-steward.yaml + sed -i.bak "s#\#helm_plan_name\##${HELM_PLAN_NAME}#g" manifests/${HELM_CHART_NAME}-helm-steward.yaml + sed -i.bak "s#\#helm_plan_description\##${HELM_PLAN_DESCRIPTION}#g" manifests/${HELM_CHART_NAME}-helm-steward.yaml + rm manifests/${HELM_CHART_NAME}-helm-steward.yaml.bak + kubectl get deployment ${HELM_CHART_NAME}-steward --namespace=steward && \ + kubectl apply -f manifests/${HELM_CHART_NAME}-helm-steward.yaml || \ + kubectl create -f manifests/${HELM_CHART_NAME}-helm-steward.yaml + +deploy-cf: install-namespace install-3prs install-cf-steward + +deploy-helm: install-namespace install-3prs install-helm-steward + +dev-deploy-cf: docker-build docker-push + STEWARD_IMAGE=${IMAGE} $(MAKE) deploy-cf + +dev-deploy-helm: docker-build docker-push + STEWARD_IMAGE=${IMAGE} $(MAKE) deploy-helm diff --git a/README.md b/README.md index 6abc4ba..8c609be 100644 --- a/README.md +++ b/README.md @@ -92,7 +92,53 @@ export STEWARD_CF_PASSWORD=foo ## Helm mode -TODO +Steward can integrate with any standard [Helm Tiller](https://github.com/kubernetes/helm) server to provide a broker in front of [Helm charts](https://github.com/kubernetes/charts). See Helm's [quick start](https://github.com/kubernetes/helm/blob/master/docs/quickstart.md) documentation for a guide on installing both the Helm CLI and the Tiller server. Note that because of [Helm issue #1083](https://github.com/kubernetes/helm/issues/1083#issuecomment-243520610), you'll have to install the Tiller server v2.0.0-alpha3 with the following command: + +```console +helm init --image gcr.io/kubernetes-helm/tiller:v2.0.0-alpha.3 +``` + +### Configuration + +Configure steward to run in helm mode by setting the `STEWARD_MODE` environment variable to `helm`. Then, configure its behavior with the following environment variables: + +- `HELM_TILLER_IP` - the IP address of the Tiller server to talk to +- `HELM_TILLER_PORT` - the port of that the Tiller server at `HELM_TILLER_IP` is listening on +- `HELM_CHART_URL` - the URL of the chart to install +- `HELM_CHART_INSTALL_NAMESPACE` - the Kubernetes namespace in which to install charts +- `HELM_PROVISION_BEHAVIOR` - See the "Provision and Deprovision Operations" section below +- `HELM_SERVICE_ID` - the service ID to list in the service catalog for this steward instance +- `HELM_SERVICE_NAME` - the service name to list in the service catalog for this steward instance +- `HELM_SERVICE_DESCRIPTION` - the service description to list in the service catalog for this steward instance +- `HELM_PLAN_ID` - the plan ID to list in the service catalog for this steward instance +- `HELM_PLAN_NAME` - the plan name to list in the service catalog for this steward instance +- `HELM_PLAN_DESCRIPTION` - the plan description to list in the service catalog for this steward instance + + +### Provision and Deprovision Operations + +Steward can be configured to take one of two different actions when it receives a provision or deprovision operation. Configure this behavior with one of the folowing two values for the `HELM_PROVISION_BEHAVIOR` env var: + +- `noop` - steward will not install or uninstall the chart specified at `HELM_CHART_URL`. In this confifguration, steward expects the following state of the cluster when it starts: + - The operator has installed the same chart as specfied in the `HELM_CHART_URL` environment variable + - The ConfigMaps specified in the chart's `values.yaml` file (see below) exist and represent valid credentials for the bindable services the chart has started + - The operator will not uninstall or otherwise modify the chart in such a way that bound consumers cannot properly interact with the chart's exposed services +- `active` - steward will download and install the chart specified at `HELM_CHART_URL` on provision operations, and uninstall it on deprovision operations + +### Bind and Unbind Operations + +On bind operations, steward reads a set of chart-specified `ConfigMap`s to get the credentials to return to the consumer. The namespace and name for each `ConfigMap` should be specified in the chart's top-level `values.yaml` file as such: + +```yaml +stewardConfigMaps: + - name: cm1 + namespace: ns1 + - name: cm2 + namespace: ns2 +``` + +On unbind operations, steward will attempt to delete this same set of `ConfigMap`s. + # Development & Testing diff --git a/config.go b/config.go index 3974100..55e66f1 100644 --- a/config.go +++ b/config.go @@ -6,7 +6,7 @@ import ( ) type rootConfig struct { - Mode string `envconfig:"MODE" default:"cf"` + Mode string `envconfig:"MODE" required:"true"` LogLevel string `envconfig:"LOG_LEVEL" default:"info"` WatchNamespaces []string `envconfig:"WATCH_NAMESPACES" default:"default"` } diff --git a/glide.lock b/glide.lock index b45ad32..92ab028 100644 --- a/glide.lock +++ b/glide.lock @@ -1,22 +1,158 @@ -hash: 72191335d7ebff941b316f234dc345ee83d936d7f2d700e8c30d7c2bbe367598 -updated: 2016-08-18T12:05:27.761812622-07:00 +hash: 2a17c59f0eec6d7627aff6bbbee885e5f34491307564af92a91d15ef9e4754d1 +updated: 2016-09-02T09:44:12.649681968-07:00 imports: -- name: context - version: 76e74a336561c500a376b406eb649727eeb3477f - repo: https://github.com/golang/net.git - vcs: git +- name: bitbucket.org/bertimus9/systemstat + version: 1468fd0db20598383c9393cccaa547de6ad99e5e +- name: bitbucket.org/ww/goautoneg + version: 75cd24fc2f2c +- name: github.com/abbot/go-http-auth + version: c0ef4539dfab4d21c8ef20ba2924f9fc6f186d35 +- name: github.com/agtorre/gocolorize + version: f42b554bf7f006936130c9bb4f971afd2d87f671 + repo: https://github.com/agtorre/gocolorize +- name: github.com/akrennmair/gopcap + version: 00e11033259acb75598ba416495bb708d864a010 + repo: https://github.com/akrennmair/gopcap +- name: github.com/aokoli/goutils + version: 9c37978a95bd5c709a15883b6242714ea6709e64 +- name: github.com/appc/cni + version: 2a58bd9379ca33579f0cf631945b717aa4fa373d subpackages: - - context + - libcni + - pkg/invoke + - pkg/types +- name: github.com/appc/docker2aci + version: 3b996e400e8937a7433b1759f846215e1d3b2fbd + subpackages: + - lib + - lib/common + - lib/internal + - lib/internal/backend/file + - lib/internal/backend/repository + - lib/internal/docker + - lib/internal/tarball + - lib/internal/types + - lib/internal/util + - pkg/log +- name: github.com/appc/goaci + version: ba7b591f930783c0ab75f5bbfd6ff65c7bd011bf + subpackages: + - proj2aci +- name: github.com/appc/spec + version: ab50d12e88f57788bf84b83fef2be236eb1fcc0b + subpackages: + - schema + - schema/common + - schema/types - name: github.com/arschles/assert version: fc2da9844984ce5093111298174706e14d4c0c47 - name: github.com/arschles/testsrv version: 1ca51fb1a3b6e612411883cf1c9dbc58319b1d5b +- name: github.com/asaskevich/govalidator + version: 7664702784775e51966f0885f5cd27435916517b - name: github.com/beorn7/perks version: 3ac7bf7a47d159a033b107610db8a1b6575507a4 subpackages: - quantile +- name: github.com/bgentry/speakeasy + version: a1ccbf2c40dfc8ce514b5c5c6e6d1429ea6880da + repo: https://github.com/bgentry/speakeasy +- name: github.com/bitly/go-simplejson + version: aabad6e819789e569bd6aabf444c935aa9ba1e44 - name: github.com/blang/semver version: 31b736133b98f26d5e078ec9eb591666edfd091f +- name: github.com/boltdb/bolt + version: ee4a0888a9abe7eefe5a0992ca4cb06864839873 +- name: github.com/bradfitz/gomemcache + version: fb1f79c6b65acda83063cbc69f6bba1522558bfc +- name: github.com/bugsnag/bugsnag-go + version: b1d153021fcd90ca3f080db36bec96dc690fb274 + subpackages: + - errors +- name: github.com/bugsnag/osext + version: 0dd3f918b21bec95ace9dc86c7e70266cfc5c702 +- name: github.com/bugsnag/panicwrap + version: e2c28503fcd0675329da73bf48b33404db873782 +- name: github.com/BurntSushi/toml + version: 99064174e013895bbd9b025c31100bd1d9b590ca +- name: github.com/BurntSushi/xgb + version: 27f122750802c950b2c869a5b63dafcf590ced95 + repo: https://github.com/BurntSushi/xgb +- name: github.com/cbroglie/mapstructure + version: 25325b46b67d1c3eb7d58bad37d34d89a31cf9ec +- name: github.com/ClusterHQ/flocker-go + version: 1c0a791b33bdc01d062b376612aa04e27eed7eb3 +- name: github.com/cockroachdb/cmux + version: b64f5908f4945f4b11ed4a0a9d3cc1e23350866d + repo: https://github.com/cockroachdb/cmux +- name: github.com/codegangsta/cli + version: 942282e931e8286aa802a30b01fa7e16befb50f3 +- name: github.com/codegangsta/negroni + version: 8d75e11374a1928608c906fe745b538483e7aeb2 +- name: github.com/coreos/etcd + version: 8b320e7c550067b1dfb37bd1682e8067023e0751 + subpackages: + - alarm + - auth + - auth/authpb + - client + - clientv3 + - compactor + - discovery + - error + - etcdserver + - etcdserver/api + - etcdserver/api/v2http + - etcdserver/api/v2http/httptypes + - etcdserver/api/v3rpc + - etcdserver/api/v3rpc/rpctypes + - etcdserver/auth + - etcdserver/etcdserverpb + - etcdserver/membership + - etcdserver/stats + - integration + - lease + - lease/leasehttp + - lease/leasepb + - pkg/adt + - pkg/contention + - pkg/crc + - pkg/fileutil + - pkg/httputil + - pkg/idutil + - pkg/ioutil + - pkg/logutil + - pkg/netutil + - pkg/pathutil + - pkg/pbutil + - pkg/runtime + - pkg/schedule + - pkg/testutil + - pkg/tlsutil + - pkg/transport + - pkg/types + - pkg/wait + - raft + - raft/raftpb + - rafthttp + - snap + - snap/snappb + - storage + - storage/backend + - storage/storagepb + - store + - version + - wal + - wal/walpb +- name: github.com/coreos/gexpect + version: ca42424d18c76d0d51a4cccd830d11878e9e5c17 +- name: github.com/coreos/go-etcd + version: 003851be7bb0694fe3cc457a49529a19388ee7cf + repo: https://github.com/coreos/go-etcd +- name: github.com/coreos/go-iptables + version: 83dfad0f13fd7310fb3c1cb8563248d8d604b95b + subpackages: + - iptables - name: github.com/coreos/go-oidc version: 5cf2aa52da8c574d3aa4458f471ad6ae2240fe6b subpackages: @@ -25,6 +161,10 @@ imports: - key - oauth2 - oidc +- name: github.com/coreos/go-semver + version: d043ae190b3202550d026daf009359bb5d761672 + subpackages: + - semver - name: github.com/coreos/go-systemd version: 4484981625c1a6a2ecb40a390fcb6a9bcfee76e3 subpackages: @@ -34,6 +174,14 @@ imports: - journal - unit - util +- name: github.com/coreos/go-tspi + version: 03955c59fff97f9e38e7e32c68ac4db21a2aea2b + subpackages: + - tpmclient + - tspiconst + - verification +- name: github.com/coreos/ioprogress + version: e7fc03058804de5488baed8df5b89f3924b9ec9a - name: github.com/coreos/pkg version: 7f080b6c11ac2d2347c3cd7521e810207ea1a041 subpackages: @@ -42,35 +190,122 @@ imports: - health - httputil - timeutil +- name: github.com/coreos/rkt + version: 14437382a98e5ebeb6cafb57cff445370e3f7d56 + subpackages: + - api/v1alpha +- name: github.com/cpuguy83/go-md2man + version: 71acacd42f85e5e82f70a55327789582a5200a90 +- name: github.com/cznic/b + version: e2e747ce049fb910cff6b1fd7ad8faf3900939d5 +- name: github.com/cznic/bufs + version: 3dcccbd7064a1689f9c093a988ea11ac00e21f51 +- name: github.com/cznic/exp + version: 36265f1914ea00990ff0b73f72350edf9b1850df + subpackages: + - lldb +- name: github.com/cznic/fileutil + version: 21ae57c9dce724a15e88bd9cd46d5668f3e880a5 +- name: github.com/cznic/mathutil + version: 250d0b9d3304c5ea0c4cfc7d9efc7ee528b81f3b +- name: github.com/cznic/ql + version: 77545ff365ff426dfab06c46d70fa77600cd55af + subpackages: + - driver +- name: github.com/cznic/sortutil + version: d4401851b4c370f979b842fa1e45e0b3b718b391 +- name: github.com/cznic/strutil + version: 97bc31f80ac4c9fa9c5dc5fea74c383858988ea2 +- name: github.com/cznic/zappy + version: 47331054e4f96186e3ff772877c0443909368a45 +- name: github.com/d2g/dhcp4 + version: f0e4d29ff0231dce36e250b2ed9ff08412584bca +- name: github.com/d2g/dhcp4client + version: bed07e1bc5b85f69c6f0fd73393aa35ec68ed892 - name: github.com/davecgh/go-spew version: 5215b55f46b2b919f50a1df0eaa5886afe4e3b3d subpackages: - spew +- name: github.com/daviddengcn/go-colortext + version: 511bcaf42ccd42c38aba7427b6673277bf19e2a1 +- name: github.com/denverdino/aliyungo + version: 6ffb587da9da6d029d0ce517b85fecc82172d502 + subpackages: + - common + - oss + - util +- name: github.com/dgrijalva/jwt-go + version: 5ca80149b9d3f8b863af0e2bb6742e608603bd99 - name: github.com/docker/distribution version: cd27f179f2c10c5d300e6d09025b538c475b0d51 subpackages: - digest - reference -- name: github.com/docker/docker - version: 0f5c9d301b9b1cca66b3ea0f9dec3b5317d3686d - subpackages: - - pkg/jsonmessage - - pkg/mount - - pkg/stdcopy - - pkg/symlink - - pkg/term - - pkg/term/winconsole - - pkg/timeutils - - pkg/units +- name: github.com/docker/engine-api + version: dea108d3aa0c67d7162a3fd8aa65f38a430019fd + subpackages: + - client + - client/transport + - client/transport/cancellable + - types + - types/blkiodev + - types/container + - types/filters + - types/network + - types/reference + - types/registry + - types/strslice + - types/time + - types/versions - name: github.com/docker/go-units version: 0bbddae09c5a5419a8c6dcdd7ff90da3d450393b +- name: github.com/docker/goamz + version: f0a21f5b2e12f83a505ecf79b633bb2035cf6f85 + subpackages: + - aws + - s3 +- name: github.com/docker/libtrust + version: fa567046d9b14f6aa788882a950d69651d230b21 +- name: github.com/dustin/go-humanize + version: c20a8bde38c8f5ba06f6600edf473705c96829d1 +- name: github.com/elazarl/go-bindata-assetfs + version: 3dcc96556217539f50599357fb481ac0dc7439b9 +- name: github.com/elazarl/goproxy + version: 07b16b6e30fcac0ad8c0435548e743bcf2ca7e92 - name: github.com/emicklei/go-restful version: 7c47e2558a0bbbaba9ecab06bc6681e73028a28a subpackages: - log - swagger +- name: github.com/evanphx/json-patch + version: 465937c80b3c07a7c7ad20cc934898646a91c1de +- name: github.com/feyeleanor/raw + version: 724aedf6e1a5d8971aafec384b6bde3d5608fba4 +- name: github.com/feyeleanor/sets + version: 6c54cb57ea406ff6354256a4847e37298194478f +- name: github.com/feyeleanor/slices + version: bb44bb2e4817fe71ba7082d351fd582e7d40e3ea +- name: github.com/fsnotify/fsnotify + version: f12c6236fe7b5cf6bcf30e5935d08cb079d78334 + repo: https://github.com/fsnotify/fsnotify +- name: github.com/garyburd/redigo + version: b8dc90050f24c1a73a52f107f3f575be67b21b7c + subpackages: + - internal + - redis +- name: github.com/getsentry/raven-go + version: c630fd2e8badfb3b9522c3d3a8d645506517d384 + repo: https://github.com/getsentry/raven-go - name: github.com/ghodss/yaml version: 73d445a93680fa1a78ae23a5839bad48f32ba1ee +- name: github.com/go-ini/ini + version: 2e44421e256d82ebbf3d4d4fcabe8930b905eff3 +- name: github.com/go-ole/go-ole + version: 572eabb84c424e76a0d39d31510dd7dfd62f70b2 + subpackages: + - oleutil +- name: github.com/godbus/dbus + version: c7fdd8b5cd55e87b4e1f4e372cdb1db61dd6c66f - name: github.com/gogo/protobuf version: 82d16f734d6d871204a3feb1a73cb220cc92574c subpackages: @@ -100,81 +335,172 @@ imports: - vanity/command - name: github.com/golang/glog version: 44145f04b68cf362d9c4df2182967c2275eaefed +- name: github.com/golang/groupcache + version: 604ed5785183e59ae2789449d89e73f3a2a77987 + subpackages: + - lru +- name: github.com/golang/mock + version: bd3c8e81be01eef76d4b503f5e687d2d1354d2d9 + subpackages: + - gomock - name: github.com/golang/protobuf - version: b982704f8bb716bb608144408cff30e15fbde841 + version: f0a097ddac24fb00e07d2ac17f8671423f3ea47c subpackages: - proto -- name: github.com/google/cadvisor - version: 4dbefc9b671b81257973a33211fb12370c1a526e - subpackages: - - api - - cache/memory - - collector - - container - - container/common - - container/docker - - container/libcontainer - - container/raw - - container/rkt - - container/systemd - - devicemapper - - events - - fs - - healthz - - http - - http/mux - - info/v1 - - info/v1/test - - info/v2 - - machine - - manager - - manager/watcher - - manager/watcher/raw - - manager/watcher/rkt - - metrics - - pages - - pages/static - - storage - - summary - - utils - - utils/cloudinfo - - utils/cpuload - - utils/cpuload/netlink - - utils/docker - - utils/oomparser - - utils/sysfs - - utils/sysinfo - - utils/tail - - validate - - version + - ptypes/any + - ptypes/timestamp +- name: github.com/google/btree + version: cc6329d4279e3f025a53a83c397d2339b5705c45 - name: github.com/google/gofuzz version: bbcb9da2d746f8bdbd6a936686a0a6067ada0ec5 - name: github.com/gorilla/context version: 215affda49addc4c8ef7e2534915df2c8c35c6cd +- name: github.com/gorilla/handlers + version: 60c7bfde3e33c201519a200a4507a158cc03a17b - name: github.com/gorilla/mux - version: cf79e51a62d8219d52060dfc1b4e810414ba2d15 + version: 8096f47503459bcc74d1f4c487b7e6e42e5746b5 +- name: github.com/gosuri/uitable + version: 36ee7e946282a3fb1cfecd476ddc9b35d8847e42 +- name: github.com/hashicorp/errwrap + version: 7554cd9344cec97297fa6649b055a8c98c2a1e55 +- name: github.com/hashicorp/go-msgpack + version: fa3f63826f7c23912c15263591e65d54d080b458 + subpackages: + - codec +- name: github.com/hashicorp/golang-lru + version: a0d98a5f288019575c6d1f4bb1573fef2d1fcdc4 + subpackages: + - simplelru +- name: github.com/hashicorp/hcl + version: 9edfb078e1d0f5f827728907994937ef9bfb06a1 + repo: https://github.com/hashicorp/hcl +- name: github.com/hashicorp/raft + version: 057b893fd996696719e98b6c44649ea14968c811 +- name: github.com/hashicorp/raft-boltdb + version: d1e82c1ec3f15ee991f7cc7ffd5b67ff6f5bbaee +- name: github.com/hawkular/hawkular-client-go + version: 1d46ce7e1eca635f372357a8ccbf1fa7cc28b7d2 + subpackages: + - metrics +- name: github.com/hydrogen18/stoppableListener + version: 2eebd04b47ff393f4bc50b7fcacf8f048994c6fd +- name: github.com/imdario/mergo + version: 6633656539c1639d9d78127b7d47c622b5d7b6dc +- name: github.com/inconshreveable/go-vhost + version: 06d84117953b22058c096b49a429ebd4f3d3d97b + repo: https://github.com/inconshreveable/go-vhost +- name: github.com/inconshreveable/mousetrap + version: 76626ae9c91c4f2a10f34cad8ce83ea42c93bb75 +- name: github.com/jmespath/go-jmespath + version: 3433f3ea46d9f8019119e7dd41274e112a2359a9 - name: github.com/jonboulle/clockwork version: 3f831b65b61282ba6bece21b91beea2edc4c887a - name: github.com/juju/ansiterm - version: e2a7a542a5eb387f493298469a8a3de37dcbc043 - subpackages: - - tabwriter + version: c4ce98b1cd3195b2d6a3069a2445bee608de04a2 + repo: https://github.com/juju/ansiterm - name: github.com/juju/loggo version: 3b7ece48644d35850f4ced4c2cbc2cf8413f58e0 - name: github.com/juju/ratelimit version: 77ed1c8a01217656d2080ad51981f6e99adaa177 +- name: github.com/julienschmidt/httprouter + version: d8ff598a019f2c7bad0980917a588193cf26666e + repo: https://github.com/julienschmidt/httprouter +- name: github.com/kardianos/osext + version: 8fef92e41e22a70e700a96b29f066cda30ea24ef +- name: github.com/kballard/go-shellquote + version: e5c918b80c17694cbc49aab32a759f9a40067f5d - name: github.com/kelseyhightower/envconfig - version: 49e88841e458491c935563aac0eb7e871c3375f4 + version: 1e291572dcb9ba673cdcdb15e0f422702415ebaf +- name: github.com/klauspost/compress + version: 08fe86a420401e830c24114bdf9f1ba91331407a +- name: github.com/klauspost/cpuid + version: 09cded8978dc9e80714c4d85b0322337b0a1e5e0 +- name: github.com/klauspost/crc32 + version: 19b0b332c9e4516a6370a0456e6182c3b5036720 +- name: github.com/kr/fs + version: 2788f0dbd16903de03cb8186e5c7d97b69ad387b + repo: https://github.com/kr/fs +- name: github.com/kr/pty + version: f7ee69f31298ecbe5d2b349c711e2547a617d398 - name: github.com/lunixbochs/vtclean version: 4fbf7632a2c6d3fbdb9931439bdbbeded02cbe36 + repo: https://github.com/lunixbochs/vtclean +- name: github.com/magiconair/properties + version: 61b492c03cf472e0c6419be5899b8e0dc28b1b88 + repo: https://github.com/magiconair/properties +- name: github.com/Masterminds/semver + version: 808ed7761c233af2de3f9729a041d68c62527f3a +- name: github.com/Masterminds/sprig + version: 89eb6984259918bffe1ddff9647b9ed06061e688 - name: github.com/mattn/go-colorable version: ed8eb9e318d7a84ce5915b495b7d35e0cfe7b5a8 - name: github.com/mattn/go-isatty version: 66b8e73f3f5cda9f96b69efd03dd3d7fc4a5cdb8 +- name: github.com/mattn/go-runewidth + version: d6bea18f789704b5f83375793155289da36a3c7f + repo: https://github.com/mattn/go-runewidth - name: github.com/matttproud/golang_protobuf_extensions version: fc2b8d3a73c4867e51861bbdd5ae3c1f0869dd6a subpackages: - pbutil +- name: github.com/mesos/mesos-go + version: 45c8b08e9af666add36a6f93ff8c1c75812367b0 + subpackages: + - auth + - auth/callback + - auth/sasl + - auth/sasl/mech + - auth/sasl/mech/crammd5 + - detector + - detector/zoo + - executor + - mesosproto + - mesosproto/scheduler + - mesosutil + - mesosutil/process + - messenger + - messenger/sessionid + - scheduler + - upid +- name: github.com/Microsoft/go-winio + version: 8f9387ea7efabb228a981b9c381142be7667967f +- name: github.com/miekg/dns + version: c2b278e70f35902fd68b54b69238cd10bb1b7451 +- name: github.com/mistifyio/go-zfs + version: 1b4ae6fb4e77b095934d4430860ff202060169f8 +- name: github.com/mitchellh/mapstructure + version: 740c764bc6149d3f1806231418adb9f52c11bcbf + repo: https://github.com/mitchellh/mapstructure +- name: github.com/mjibson/appstats + version: 0542d5f0e87ea3a8fa4174322b9532f5d04f9fa8 + repo: https://github.com/mjibson/appstats +- name: github.com/mvdan/xurls + version: 1b768d7c393abd8e8dda1458385a57becd4b2d4e +- name: github.com/mxk/go-flowrate + version: cca7078d478f8520f85629ad7c68962d31ed7682 + subpackages: + - flowrate +- name: github.com/ncw/swift + version: c54732e87b0b283d1baf0a18db689d0aea460ba3 + subpackages: + - swifttest +- name: github.com/olekukonko/tablewriter + version: daf2955e742cf123959884fdff4685aa79b63135 + repo: https://github.com/olekukonko/tablewriter +- name: github.com/onsi/gomega + version: 7ce781ea776b2fd506491011353bded2e40c8467 + subpackages: + - format + - internal/assertion + - internal/asyncassertion + - internal/oraclematcher + - internal/testingtsupport + - matchers + - matchers/support/goraph/bipartitegraph + - matchers/support/goraph/edge + - matchers/support/goraph/node + - matchers/support/goraph/util + - types - name: github.com/opencontainers/runc version: 7ca2aa4873aea7cb4265b1726acb24b90d8726c6 subpackages: @@ -193,8 +519,28 @@ imports: - libcontainer/system - libcontainer/user - libcontainer/utils +- name: github.com/opencontainers/specs + version: a7b50925d8996923d99e1c50750131e20b743067 - name: github.com/pborman/uuid - version: c55201b036063326c5b1b89ccfe45a184973d073 + version: ca53cad383cad2479bbba7f7a1a05797ec1386e4 +- name: github.com/pelletier/go-buffruneio + version: df1e16fde7fc330a0ca68167c23bf7ed6ac31d6d + repo: https://github.com/pelletier/go-buffruneio +- name: github.com/pelletier/go-toml + version: 5a62685873ef617233ab5f1b825a6e4a758e16cf + repo: https://github.com/pelletier/go-toml +- name: github.com/peterbourgon/diskv + version: 937c5a91d7fb1477fa6d2bb71410b2ae7a0f2143 +- name: github.com/pkg/errors + version: 17b591df37844cde689f4d5813e5cea0927d8dd2 + repo: https://github.com/pkg/errors +- name: github.com/pkg/sftp + version: a71e8f580e3b622ebff585309160b1cc549ef4d2 + repo: https://github.com/pkg/sftp +- name: github.com/pmezard/go-difflib + version: d8ed2627bdf02c080bf22230dbb337003b7aba2d + subpackages: + - difflib - name: github.com/prometheus/client_golang version: 3b78d7a77f51ccbc364d4bc170920153022cfd08 subpackages: @@ -211,146 +557,284 @@ imports: - model - name: github.com/prometheus/procfs version: 490cc6eb5fa45bf8a8b7b73c8bc82a8160e8531d +- name: github.com/rackspace/gophercloud + version: 934dbf81977c67c521c75492dc1f55ca74dc5b04 + subpackages: + - openstack + - openstack/blockstorage/v1/volumes + - openstack/compute/v2/extensions/bootfromvolume + - openstack/compute/v2/extensions/diskconfig + - openstack/compute/v2/extensions/volumeattach + - openstack/compute/v2/flavors + - openstack/compute/v2/images + - openstack/compute/v2/servers + - openstack/identity/v2/tenants + - openstack/identity/v2/tokens + - openstack/identity/v3/tokens + - openstack/networking/v2/extensions/layer3/floatingips + - openstack/networking/v2/extensions/lbaas/members + - openstack/networking/v2/extensions/lbaas/monitors + - openstack/networking/v2/extensions/lbaas/pools + - openstack/networking/v2/extensions/lbaas/vips + - openstack/networking/v2/extensions/lbaas_v2/listeners + - openstack/networking/v2/extensions/lbaas_v2/loadbalancers + - openstack/networking/v2/extensions/lbaas_v2/monitors + - openstack/networking/v2/extensions/lbaas_v2/pools + - openstack/networking/v2/ports + - openstack/utils + - pagination + - rackspace + - rackspace/blockstorage/v1/volumes + - rackspace/compute/v2/servers + - rackspace/compute/v2/volumeattach + - rackspace/identity/v2/tokens + - testhelper + - testhelper/client +- name: github.com/rcrowley/go-metrics + version: bdb33529eca3e55eac7328e07c57012a797af602 + repo: https://github.com/rcrowley/go-metrics +- name: github.com/remyoudompheng/bigfft + version: a8e77ddfb93284b9d58881f597c820a2875af336 + repo: https://github.com/remyoudompheng/bigfft +- name: github.com/revel/config + version: 75f5ee659b5338d1ce823536fe9b9e50b4de01b4 + repo: https://github.com/revel/config +- name: github.com/revel/revel + version: 8d01a054f6bca9430b3e497fe7b4f20ec8f054ce + repo: https://github.com/revel/revel +- name: github.com/robfig/cron + version: 0f39cf7ebc65a602f45692f9894bd6a193faf8fa +- name: github.com/robfig/go-cache + version: 9fc39e0dbf62c034ec4e45e6120fc69433a3ec51 + repo: https://github.com/robfig/go-cache +- name: github.com/robfig/pathtree + version: 41257a1839e945fce74afd070e02bab2ea2c776a + repo: https://github.com/robfig/pathtree +- name: github.com/russross/blackfriday + version: 77efab57b2f74dd3f9051c79752b2e8995c8b789 +- name: github.com/samuel/go-zookeeper + version: 177002e16a0061912f02377e2dd8951a8b3551bc + subpackages: + - zk +- name: github.com/seccomp/libseccomp-golang + version: 1b506fc7c24eec5a3693cdcbed40d9c226cfc6a1 +- name: github.com/shirou/gopsutil + version: 9ef341037b1bcadeeee79e77da4f6baf63de4feb + subpackages: + - cpu + - host + - internal/common + - mem + - net + - process +- name: github.com/shirou/w32 + version: 3c9377fc6748f222729a8270fe2775d149a249ad +- name: github.com/shurcooL/sanitized_anchor_name + version: 10ef21a441db47d8b13ebcc5fd2310f636973c77 +- name: github.com/Sirupsen/logrus + version: 51fe59aca108dc5680109e7b2051cbdcfa5a253c +- name: github.com/skynetservices/skydns + version: f7b6fb74bcfab300b4e7e0e27b1fe6c0ed555f78 + subpackages: + - cache + - metrics + - msg + - server + - singleflight +- name: github.com/spacejam/loghisto + version: 9d1d8c1fd2a4ac852bf2e312f2379f553345fda7 + repo: https://github.com/spacejam/loghisto +- name: github.com/spf13/afero + version: 20500e2abd0d1f4564a499e83d11d6c73cd58c27 + repo: https://github.com/spf13/afero +- name: github.com/spf13/cast + version: e31f36ffc91a2ba9ddb72a4b6a607ff9b3d3cb63 + repo: https://github.com/spf13/cast +- name: github.com/spf13/cobra + version: 4c05eb1145f16d0e6bb4a3e1b6d769f4713cb41f +- name: github.com/spf13/jwalterweatherman + version: 33c24e77fb80341fe7130ee7c594256ff08ccc46 + repo: https://github.com/spf13/jwalterweatherman - name: github.com/spf13/pflag - version: 08b1a584251b5b62f458943640fc8ebd4d50aaa5 + version: 367864438f1b1a3c7db4da06a2f55b144e6784e0 +- name: github.com/spf13/viper + version: 16990631d4aa7e38f73dbbbf37fa13e67c648531 + repo: https://github.com/spf13/viper +- name: github.com/StackExchange/wmi + version: f3e2bae1e0cb5aef83e319133eabfee30013a4a5 +- name: github.com/stathat/go + version: 74669b9f388d9d788c97399a0824adbfee78400e + repo: https://github.com/stathat/go +- name: github.com/stevvooe/resumable + version: 51ad44105773cafcbe91927f70ac68e1bf78f8b4 + subpackages: + - sha256 + - sha512 +- name: github.com/stretchr/objx + version: 1a9d0bb9f541897e62256577b352fdbc1fb4fd94 +- name: github.com/stretchr/testify + version: e3a8ff8ce36581f87a15341206f205b1da467059 + subpackages: + - assert + - mock + - require +- name: github.com/syndtr/gocapability + version: 2c00daeb6c3b45114c80ac44119e7b8801fdd852 + subpackages: + - capability +- name: github.com/technosophos/moniker + version: 9f956786b91d9786ca11aa5be6104542fa911546 +- name: github.com/tobi/airbrake-go + version: a3cdd910a3ffef88a20fbecc10363a520ad61a0a + repo: https://github.com/tobi/airbrake-go - name: github.com/ugorji/go version: f4485b318aadd133842532f841dc205a8e339d74 +- name: github.com/urfave/cli + version: 33bb4c121333cd8eb7d8cc8d74474f4ec156f371 + repo: https://github.com/urfave/cli +- name: github.com/vishvananda/netlink + version: 1e2e08e8a2dcdacaae3f14ac44c5cfa31361f270 subpackages: - - codec - - codec/codecgen + - nl +- name: github.com/vmware/govmomi + version: 9051bd6b44125d9472e0c148b5965692ab283d4a + subpackages: + - find + - list + - object + - property + - session + - task + - vim25 + - vim25/debug + - vim25/methods + - vim25/mo + - vim25/progress + - vim25/soap + - vim25/types + - vim25/xml +- name: github.com/xiang90/probing + version: 6a0cc1ae81b4cc11db5e491e030e4b98fba79c19 +- name: github.com/xordataexchange/crypt + version: 749e360c8f236773f28fc6d3ddfce4a470795227 + repo: https://github.com/xordataexchange/crypt +- name: github.com/xyproto/simpleredis + version: 5292687f5379e01054407da44d7c4590a61fd3de +- name: github.com/yvasiyarov/go-metrics + version: 57bccd1ccd43f94bb17fdd8bf3007059b802f85e +- name: github.com/yvasiyarov/gorelic + version: a9bba5b9ab508a086f9a12b8c51fab68478e2128 +- name: github.com/yvasiyarov/newrelic_platform_go + version: b21fdbd4370f3717f3bbd2bf41c223bc273068e6 +- name: go4.org + version: 03efcb870d84809319ea509714dd6d19a1498483 + subpackages: + - errorutil +- name: golang.org/x/crypto + version: c84e1f8e3a7e322d497cd16c0e8a13c7e127baf3 +- name: golang.org/x/exp + version: 292a51b8d262487dab23a588950e8052d63d9113 + subpackages: + - inotify +- name: golang.org/x/image + version: 112f99633204ef0d08331ea410378b44a4bdde9e + repo: https://golang.org/x/image +- name: golang.org/x/mobile + version: 7573efae754bbd3a44d66cff513c65fa5a62e408 + repo: https://golang.org/x/mobile - name: golang.org/x/net - version: 62685c2d7ca23c807425dca88b11a3e2323dab41 + version: fb93926129b8ec0056f2f458b1f519654814edf0 subpackages: - context - - http2 - - http2/hpack - - context/ctxhttp - name: golang.org/x/oauth2 version: b5adcc2dcdf009d0391547edc6ecbaff889f5bb9 - subpackages: - - google - - internal - - jws - - jwt - name: golang.org/x/sys version: 833a04a10549a95dc34458c195cbad61bbb6cb4d + repo: https://golang.org/x/sys +- name: golang.org/x/text + version: ceefd2213ed29504fff30155163c8f59827734f3 +- name: golang.org/x/tools + version: 27e692e6ec36d8f48be794f32553e1400c70dbf2 + subpackages: + - go/vcs +- name: google.golang.org/api + version: 4300f6b0c8a7f09e521dd0af2cee27e28846e037 subpackages: - - unix + - cloudmonitoring/v2beta2 + - compute/v1 + - container/v1 + - dns/v1 + - gensupport + - googleapi + - googleapi/internal/uritemplates - name: google.golang.org/appengine version: 12d5545dc1cfa6047a286d5e853841b6471f4c19 subpackages: - - urlfetch - internal - - internal/urlfetch - internal/app_identity - - internal/modules - internal/base - internal/datastore - internal/log + - internal/modules - internal/remote_api -- name: google.golang.org/cloud - version: eb47ba841d53d93506cfbfbc03927daf9cc48f88 +- name: google.golang.org/grpc + version: dec33edc378cf4971a2741cfd86ed70a644d6ba3 +- name: gopkg.in/check.v1 + version: 64131543e7896d5bcc6bd5a76287eb75ea96c673 +- name: gopkg.in/cheggaaa/pb.v1 + version: 9453b2db37f4d8bc63751daca63bbe7049eb5e74 + repo: https://gopkg.in/cheggaaa/pb.v1 +- name: gopkg.in/fsnotify.v1 + version: a8a77c9133d2d6fd8334f3260d06f60e8d80a5fb + repo: https://gopkg.in/fsnotify.v1 +- name: gopkg.in/gcfg.v1 + version: 083575c3955c85df16fe9590cceab64d03f5eb6e subpackages: - - compute/metadata - - internal + - scanner + - token + - types - name: gopkg.in/inf.v0 version: 3887ee99ecf07df5b447e9b00d9c0b2adaa9f3e4 +- name: gopkg.in/natefinch/lumberjack.v2 + version: 20b71e5b60d756d3d2f80def009790325acc2b23 +- name: gopkg.in/urfave/cli.v1 + version: a14d7d367bc02b1f57d88de97926727f2d936387 + repo: https://gopkg.in/urfave/cli.v1 +- name: gopkg.in/vmihailenco/msgpack.v2 + version: 38f3708c44f1402aeb2f137dd9a9dfb12bae2204 + repo: https://gopkg.in/vmihailenco/msgpack.v2 +- name: gopkg.in/yaml.v1 + version: 9f9df34309c04878acc86042b16630b0f696e1de + repo: https://gopkg.in/yaml.v1 - name: gopkg.in/yaml.v2 version: a83829b6f1293c91addabc89d0571c246397bbf4 +- name: k8s.io/helm + version: 1119fdd54d9f810e0d09deefc3084f7743b26657 - name: k8s.io/kubernetes - version: 9bafa3400a77c14ee50782bb05f9efc5c91b3185 + version: 0eaf484c0ac0007d66bf0c454cdfa02e7acafb51 subpackages: - - pkg/client/unversioned - pkg/api + - pkg/api/meta + - pkg/api/error - pkg/api/unversioned + - pkg/apimachinery/registered - pkg/client/restclient - - pkg/watch + - pkg/client/unversioned + - pkg/apis/batch + - pkg/client/unversioned/clientcmd + - pkg/client/unversioned/fake + - pkg/client/unversioned/portforward + - pkg/client/unversioned/remotecommand + - pkg/kubectl + - pkg/kubectl/cmd/util + - pkg/kubectl/resource - pkg/labels - - pkg/api/meta - - pkg/api/meta/metatypes - - pkg/api/resource - - pkg/auth/user - - pkg/conversion - - pkg/fields - pkg/runtime - - pkg/runtime/serializer - - pkg/types - - pkg/util - - pkg/util/intstr - - pkg/util/rand - - pkg/util/sets - - pkg/api/errors - - pkg/api/install - - pkg/apimachinery/registered - - pkg/apis/apps - - pkg/apis/apps/install - - pkg/apis/authentication.k8s.io/install - - pkg/apis/authorization/install - - pkg/apis/autoscaling - - pkg/apis/autoscaling/install - - pkg/apis/batch - - pkg/apis/batch/install - - pkg/apis/batch/v2alpha1 - - pkg/apis/componentconfig/install - - pkg/apis/extensions - - pkg/apis/extensions/install - - pkg/apis/policy - - pkg/apis/policy/install - - pkg/apis/rbac - - pkg/apis/rbac/install - - pkg/client/typed/discovery - - pkg/util/net - - pkg/util/wait - - pkg/version - - plugin/pkg/client/auth - - pkg/api/v1 - - pkg/api/validation - - pkg/client/metrics - - pkg/client/transport - - pkg/client/unversioned/clientcmd/api - - pkg/runtime/serializer/streaming - - pkg/util/crypto - - pkg/util/flowcontrol - - pkg/watch/versioned - - pkg/util/runtime - - pkg/util/validation - - pkg/util/errors - - third_party/forked/reflect - - pkg/conversion/queryparams - - pkg/util/json - - pkg/runtime/serializer/json - - pkg/runtime/serializer/protobuf - - pkg/runtime/serializer/recognizer - - pkg/runtime/serializer/versioning - - pkg/util/validation/field - - pkg/apimachinery - - pkg/apis/apps/v1alpha1 - - pkg/apis/authentication.k8s.io - - pkg/apis/authentication.k8s.io/v1beta1 - - pkg/apis/authorization - - pkg/apis/authorization/v1beta1 - - pkg/apis/autoscaling/v1 - - pkg/apis/batch/v1 - - pkg/apis/componentconfig - - pkg/apis/componentconfig/v1alpha1 - - pkg/apis/extensions/v1beta1 - - pkg/apis/policy/v1alpha1 - - pkg/apis/rbac/v1alpha1 - - plugin/pkg/client/auth/gcp - - plugin/pkg/client/auth/oidc - - pkg/util/parsers - - pkg/api/endpoints - - pkg/api/pod - - pkg/api/service - - pkg/api/unversioned/validation - - pkg/api/util - - pkg/capabilities + - pkg/watch + - pkg/util/strategicpatch - pkg/util/yaml - - pkg/util/integer - - pkg/util/framer - - pkg/kubelet/qos - - pkg/master/ports - - pkg/util/hash - - pkg/util/net/sets - - pkg/kubelet/qos/util +- name: speter.net/go/exp/math/dec/inf + version: 3887ee99ecf07df5b447e9b00d9c0b2adaa9f3e4 + repo: https://github.com/go-inf/inf.git + vcs: git devImports: [] diff --git a/glide.yaml b/glide.yaml index 85bcabd..bfbbc12 100644 --- a/glide.yaml +++ b/glide.yaml @@ -1,12 +1,30 @@ package: github.com/deis/steward +ignore: + - camlistore.org + - code.google.com + - github.com/docker/docker + - github.com/camlistore/camlistore + - labix.org/v2/mgo + - github.com/influxdb/influxdb + - github.com/aws/aws-sdk-go + - github.com/armon/go-metrics + - github.com/armon/consul-api + - github.com/Azure/azure-sdk-for-go + - google.golang.org/cloud + - cloud.google.com/go + - github.com/camlistore/go4 + - github.com/google/cadvisor + - github.com/onsi/ginkgo + - github.com/docker/spdystream + - github.com/docker/go-connections + - code.google.com/p/go-charset + - k8s.io/heapster import: -- package: k8s.io/kubernetes - version: 1.3.2 - subpackages: - - pkg/client/unversioned - package: github.com/arschles/assert - package: github.com/kelseyhightower/envconfig - package: github.com/juju/loggo - package: github.com/gorilla/mux - package: github.com/arschles/testsrv - package: github.com/pborman/uuid +- package: k8s.io/helm + version: v2.0.0-alpha.3 diff --git a/k8s/claim/event_actions.go b/k8s/claim/event_actions.go index d7d0847..026f990 100644 --- a/k8s/claim/event_actions.go +++ b/k8s/claim/event_actions.go @@ -85,6 +85,8 @@ func processProvision( claimCh chan<- claimUpdate, ) { + logger.Debugf("processProvision for event %s", evt.claim.Claim.ToMap()) + claim := *evt.claim.Claim svc, err := getService(claim, catalogLookup) @@ -106,13 +108,14 @@ func processProvision( spaceGUID := uuid.New() instanceID := uuid.New() claim.InstanceID = instanceID - if _, err := lifecycler.Provision(instanceID, &mode.ProvisionRequest{ + provisionResp, err := lifecycler.Provision(instanceID, &mode.ProvisionRequest{ OrganizationGUID: orgGUID, PlanID: svc.Plan.ID, ServiceID: svc.Info.ID, SpaceGUID: spaceGUID, Parameters: mode.JSONObject(map[string]string{}), - }); err != nil { + }) + if err != nil { select { case claimCh <- newErrClaimUpdate(claim, err): case <-ctx.Done(): @@ -121,6 +124,7 @@ func processProvision( } claim.Status = mode.StatusProvisioned.String() + claim.Extra = provisionResp.Extra select { case claimCh <- newClaimUpdate(claim): case <-ctx.Done(): @@ -137,6 +141,8 @@ func processBind( claimCh chan<- claimUpdate, ) { + logger.Debugf("processBind for event %s", evt.claim.Claim.ToMap()) + claim := *evt.claim.Claim claimWrapper := *evt.claim if _, err := getService(claim, catalogLookup); err != nil { @@ -210,6 +216,8 @@ func processUnbind( claimCh chan<- claimUpdate, ) { + logger.Debugf("processUnbind for event %s", evt.claim.Claim.ToMap()) + claimWrapper := evt.claim claim := *evt.claim.Claim if _, err := getService(claim, catalogLookup); err != nil { @@ -281,6 +289,8 @@ func processDeprovision( claimCh chan<- claimUpdate, ) { + logger.Debugf("processDeprovision for event %s", evt.claim.Claim.ToMap()) + claim := *evt.claim.Claim if _, err := getService(claim, catalogLookup); err != nil { select { @@ -307,10 +317,12 @@ func processDeprovision( } // deprovision - if _, err := lifecycler.Deprovision(instanceID, &mode.DeprovisionRequest{ - ServiceID: claim.ServiceID, - PlanID: claim.PlanID, - }); err != nil { + deprovisionReq := &mode.DeprovisionRequest{ + ServiceID: claim.ServiceID, + PlanID: claim.PlanID, + Parameters: evt.claim.Claim.Extra, + } + if _, err := lifecycler.Deprovision(instanceID, deprovisionReq); err != nil { select { case claimCh <- newErrClaimUpdate(claim, err): case <-ctx.Done(): diff --git a/k8s/claim/event_actions_test.go b/k8s/claim/event_actions_test.go index ea0ec67..8d02aa0 100644 --- a/k8s/claim/event_actions_test.go +++ b/k8s/claim/event_actions_test.go @@ -110,7 +110,13 @@ func TestProcessProvisionServiceFound(t *testing.T) { ch := make(chan claimUpdate) cancelCtx, cancelFn := context.WithCancel(ctx) defer cancelFn() - provisioner := &fake.Provisioner{} + provisioner := &fake.Provisioner{ + Resp: &mode.ProvisionResponse{ + Extra: mode.JSONObject(map[string]string{ + uuid.New(): uuid.New(), + }), + }, + } lifecycler := &mode.Lifecycler{ Provisioner: provisioner, } @@ -134,6 +140,7 @@ func TestProcessProvisionServiceFound(t *testing.T) { assert.True(t, claimUpdate.stop, "stop boolean in claim update") assert.NotNil(t, claimUpdate.newClaim, "new claim") assert.Equal(t, claimUpdate.newClaim.Status, mode.StatusProvisioned.String(), "status") + assert.Equal(t, claimUpdate.newClaim.Extra, provisioner.Resp.Extra, "extra") assert.Equal(t, len(provisioner.Provisioned), 1, "number of provision calls") provCall := provisioner.Provisioned[0] assert.Equal(t, provCall.Req.ServiceID, evt.claim.Claim.ServiceID, "service ID") diff --git a/k8s/claim/loop.go b/k8s/claim/loop.go index 2a56c63..3d50363 100644 --- a/k8s/claim/loop.go +++ b/k8s/claim/loop.go @@ -90,7 +90,6 @@ func receiveEvent( lookup k8s.ServiceCatalogLookup, lifecycler *mode.Lifecycler, ) { - nextAction, err := evt.nextAction() if isNoNextActionErr(err) { logger.Debugf("received event that has no next action (%s), skipping", err) @@ -112,7 +111,7 @@ func receiveEvent( case claimUpdate := <-claimUpdateCh: // stop watching the processor if it failed if claimUpdate.err != nil { - logger.Errorf("error in claim processing (%s)", err) + logger.Errorf("error in claim processing (%s)", claimUpdate.err) return } diff --git a/k8s/claim/loops.go b/k8s/claim/loops.go index 285cf3c..46592ed 100644 --- a/k8s/claim/loops.go +++ b/k8s/claim/loops.go @@ -21,6 +21,7 @@ func StartControlLoops( errCh chan<- error, ) { for _, ns := range namespaces { + logger.Debugf("starting claims control loop for namespace %s", ns) go func(ns string) { evtIface := evtNamespacer.Interactor(ns) if err := StartControlLoop(ctx, evtIface, cmNamespacer, lookup, lifecycler); err != nil { diff --git a/main.go b/main.go index a1d64ed..d5da826 100644 --- a/main.go +++ b/main.go @@ -2,6 +2,7 @@ package main import ( "context" + "net/http" "os" modeutils "github.com/deis/steward/mode/utils" @@ -30,10 +31,11 @@ func main() { errCh := make(chan error) rootCtx := context.Background() + httpCl := http.DefaultClient ctx, cancelFn := context.WithCancel(rootCtx) defer cancelFn() - if err := modeutils.Run(ctx, cfg.Mode, errCh, cfg.WatchNamespaces); err != nil { + if err := modeutils.Run(ctx, httpCl, cfg.Mode, errCh, cfg.WatchNamespaces); err != nil { logger.Criticalf("Error starting %s mode: %s", cfg.Mode, err) exitWithCode(cancelFn, 1) } diff --git a/manifests/steward-template.yaml b/manifests/steward-template-cf.yaml similarity index 93% rename from manifests/steward-template.yaml rename to manifests/steward-template-cf.yaml index e3f5863..d19f2ee 100644 --- a/manifests/steward-template.yaml +++ b/manifests/steward-template-cf.yaml @@ -17,6 +17,10 @@ spec: image: #steward_image# imagePullPolicy: Always env: + - name: LOG_LEVEL + value: trace + - name: STEWARD_MODE + value: cf - name: CF_BROKER_SCHEME value: "#cf_broker_scheme#" - name: CF_BROKER_HOSTNAME diff --git a/manifests/steward-template-helm.yaml b/manifests/steward-template-helm.yaml new file mode 100644 index 0000000..5e89914 --- /dev/null +++ b/manifests/steward-template-helm.yaml @@ -0,0 +1,77 @@ +apiVersion: extensions/v1beta1 +kind: Deployment +metadata: + name: #helm_name#-steward + namespace: steward + labels: + app: #helm-name#-steward +spec: + replicas: 1 + template: + metadata: + labels: + app: #helm_name#-steward + spec: + containers: + - name: #helm_name#-steward + image: #steward_image# + imagePullPolicy: Always + env: + - name: STEWARD_MODE + value: helm + - name: LOG_LEVEL + value: trace + - name: HELM_TILLER_IP + value: "#helm_tiller_ip#" + - name: HELM_TILLER_PORT + value: "#helm_tiller_port#" + - name: HELM_CHART_URL + value: "#helm_chart_url" + - name: HELM_CHART_INSTALL_NAMESPACE + value: "#helm_chart_install_namespace#" + - name: HELM_PROVISION_BEHAVIOR + value: "#helm_provision_behavior#" + - name: HELM_SERVICE_ID + value: "#helm_service_id#" + - name: HELM_SERVICE_NAME + value: "#helm_service_name#" + - name: HELM_SERVICE_DESCRIPTION + value: "#helm_service_description#" + - name: HELM_PLAN_ID + value: "#helm_plan_id#" + - name: HELM_PLAN_NAME + value: "#helm_plan_name#" + - name: HELM_PLAN_DESCRIPTION + value: "#helm_plan_description#" + livenessProbe: + httpGet: + path: /healthz + port: 8080 + failureThreshold: 3 + initialDelaySeconds: 5 + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 2 + readinessProbe: + httpGet: + path: /healthz + port: 8080 + failureThreshold: 1 + initialDelaySeconds: 5 + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 2 +--- +apiVersion: v1 +kind: Service +metadata: + name: #helm-steward + namespace: steward + labels: + app: #helm-steward +spec: + ports: + - port: 80 + targetPort: 8080 + selector: + app: #helm-steward diff --git a/manifests/tiller-service.yaml b/manifests/tiller-service.yaml new file mode 100644 index 0000000..a9444d5 --- /dev/null +++ b/manifests/tiller-service.yaml @@ -0,0 +1,14 @@ +apiVersion: v1 +kind: Service +metadata: + name: tiller + namespace: kube-system +spec: + type: ClusterIP + selector: + app: helm + name: tiller + ports: + - name: tiller + port: 44134 + targetPort: tiller diff --git a/mode/deprovision_request.go b/mode/deprovision_request.go index c979b64..39cf20e 100644 --- a/mode/deprovision_request.go +++ b/mode/deprovision_request.go @@ -2,6 +2,7 @@ package mode // DeprovisionRequest represents a request to do a service deprovision operation. This struct is JSON-compatible with the request body detailed at https://docs.cloudfoundry.org/services/api.html#deprovisioning type DeprovisionRequest struct { - ServiceID string `json:"service_id"` - PlanID string `json:"plan_id"` + ServiceID string `json:"service_id"` + PlanID string `json:"plan_id"` + Parameters JSONObject `json:parameters,omitempty` } diff --git a/mode/fake/provisioner.go b/mode/fake/provisioner.go index 00ad76d..235b69d 100644 --- a/mode/fake/provisioner.go +++ b/mode/fake/provisioner.go @@ -12,10 +12,12 @@ type ProvisionCall struct { // Provisioner is a fake implementation of (github.com/deis/steward/mode).Provisioner, suitable for usage in unit tests type Provisioner struct { Provisioned []ProvisionCall + Resp *mode.ProvisionResponse + Err error } -// Provision is the Provisioner interface implementation. It packages the function parameters into a ProvisionCall, stores them in p.Provisioned, and returns nil, nil. This function is not concurrency safe +// Provision is the Provisioner interface implementation. It packages the function parameters into a ProvisionCall, stores them in p.Provisioned, and returns p.Resp, p.Err. This function is not concurrency safe func (p *Provisioner) Provision(instanceID string, req *mode.ProvisionRequest) (*mode.ProvisionResponse, error) { p.Provisioned = append(p.Provisioned, ProvisionCall{InstanceID: instanceID, Req: req}) - return nil, nil + return p.Resp, p.Err } diff --git a/mode/helm/binder.go b/mode/helm/binder.go new file mode 100644 index 0000000..faff850 --- /dev/null +++ b/mode/helm/binder.go @@ -0,0 +1,54 @@ +package helm + +import ( + "fmt" + + "github.com/deis/steward/mode" + "k8s.io/helm/pkg/proto/hapi/chart" + "k8s.io/kubernetes/pkg/api" + kcl "k8s.io/kubernetes/pkg/client/unversioned" +) + +const ( + chartTmpDirPrefix = "steward-bind-chart-dl" + tmpChartName = "tmpchart" +) + +type binder struct { + cmInfos []cmNamespaceAndName + cmNamespacer kcl.ConfigMapsNamespacer +} + +func dataFieldKey(cm *api.ConfigMap, key string) string { + return fmt.Sprintf("%s-%s-%s", cm.Namespace, cm.Name, key) +} + +func (b binder) Bind(instanceID, bindingID string, bindRequest *mode.BindRequest) (*mode.BindResponse, error) { + resp := &mode.BindResponse{ + Creds: mode.JSONObject(map[string]string{}), + } + + // use b.cmInfo to try and find all the listed ConfigMaps in k8s. use the data from each ConfigMap to fill in the bind response's Data field + if err := rangeConfigMaps(b.cmNamespacer, b.cmInfos, func(cm *api.ConfigMap) error { + for key, val := range cm.Data { + resp.Creds[dataFieldKey(cm, key)] = val + } + return nil + }); err != nil { + logger.Errorf("ranging over helm chart credential config maps (%s)", err) + return nil, err + } + return resp, nil +} + +// NewBinder returns a Tiller-backed mode.Binder +func NewBinder(chart *chart.Chart, cmNamespacer kcl.ConfigMapsNamespacer) (mode.Binder, error) { + cmInfos, err := getStewardConfigMapInfo(chart.Values) + if err != nil { + logger.Errorf("getting steward config map info (%s)", err) + return nil, err + } + logger.Debugf("got config map infos for helm chart %s", cmInfos) + // parse the values file for steward-specific config map info + return binder{cmInfos: cmInfos, cmNamespacer: cmNamespacer}, nil +} diff --git a/mode/helm/cataloger.go b/mode/helm/cataloger.go new file mode 100644 index 0000000..34126bc --- /dev/null +++ b/mode/helm/cataloger.go @@ -0,0 +1,33 @@ +package helm + +import ( + "github.com/deis/steward/mode" +) + +type cataloger struct { + svc *mode.Service +} + +func (c cataloger) List() ([]*mode.Service, error) { + return []*mode.Service{c.svc}, nil +} + +// NewCataloger creates a new Tiller-backed mode.Cataloger +func NewCataloger(serviceID, serviceName, serviceDescription, planID, planName, planDescription string) mode.Cataloger { + return cataloger{ + svc: &mode.Service{ + ServiceInfo: mode.ServiceInfo{ + ID: serviceID, + Name: serviceName, + Description: serviceDescription, + }, + Plans: []mode.ServicePlan{ + mode.ServicePlan{ + ID: planID, + Name: planName, + Description: planDescription, + }, + }, + }, + } +} diff --git a/mode/helm/chart_util.go b/mode/helm/chart_util.go new file mode 100644 index 0000000..9fd1f40 --- /dev/null +++ b/mode/helm/chart_util.go @@ -0,0 +1,52 @@ +package helm + +import ( + "context" + "io" + "io/ioutil" + "net/http" + "os" + "path/filepath" + + "github.com/deis/steward/web/ctxhttp" + "k8s.io/helm/pkg/chartutil" + "k8s.io/helm/pkg/proto/hapi/chart" +) + +// GetChart downloads the chart at chartURL to a directory, parses it into a *chart.Chart and returns it along with the root directory of the directory the chart was downloaded to. Returns a non-nil error if the parsing failed. It's the caller's responsibility to delete the chart directory when done with it. +func GetChart(ctx context.Context, httpCl *http.Client, chartURL string) (*chart.Chart, string, error) { + logger.Debugf("downloading chart from %s", chartURL) + resp, err := ctxhttp.Get(ctx, httpCl, chartURL) + if err != nil { + logger.Errorf("downloading chart from %s (%s)", chartURL, err) + return nil, "", err + } + defer resp.Body.Close() + tmpDir, err := ioutil.TempDir("", chartTmpDirPrefix) + if err != nil { + return nil, "", err + } + + fullChartPath := filepath.Join(tmpDir, tmpChartName) + fd, err := os.Create(fullChartPath) + if err != nil { + return nil, "", err + } + defer func() { + if err := fd.Close(); err != nil { + logger.Errorf("closing file descriptor for chart at %s (%s)", fullChartPath, err) + } + }() + logger.Debugf("copying chart to %s", fullChartPath) + if _, err := io.Copy(fd, resp.Body); err != nil { + logger.Errorf("copying chart contents to %s (%s)", fullChartPath, err) + return nil, "", err + } + logger.Debugf("loading chart from %s on disk", fullChartPath) + chart, err := chartutil.Load(fullChartPath) + if err != nil { + logger.Errorf("loading chart from %s on disk (%s)", fullChartPath, err) + return nil, "", err + } + return chart, tmpDir, nil +} diff --git a/mode/helm/common.go b/mode/helm/common.go new file mode 100644 index 0000000..4b30f03 --- /dev/null +++ b/mode/helm/common.go @@ -0,0 +1,13 @@ +package helm + +import ( + "github.com/juju/loggo" +) + +const ( + releaseNameKey = "release-name" +) + +var ( + logger = loggo.GetLogger("mode.helm") +) diff --git a/mode/helm/deprovisioner.go b/mode/helm/deprovisioner.go new file mode 100644 index 0000000..4134c4a --- /dev/null +++ b/mode/helm/deprovisioner.go @@ -0,0 +1,57 @@ +package helm + +import ( + "fmt" + + "github.com/deis/steward/mode" + "k8s.io/helm/pkg/proto/hapi/chart" +) + +const ( + deprovisionedNoopOperation = "deprovisioned-noop" + deprovisionedActiveOperation = "deprovisioned-active" +) + +type errMissingReleaseName struct { + params mode.JSONObject +} + +func (e errMissingReleaseName) Error() string { + return fmt.Sprintf("missing release name in parameters %s", e.params) +} + +type deprovisioner struct { + chart *chart.Chart + provBehavior ProvisionBehavior + deleter ReleaseDeleter +} + +func (d deprovisioner) Deprovision(instanceID string, dreq *mode.DeprovisionRequest) (*mode.DeprovisionResponse, error) { + if d.provBehavior == ProvisionBehaviorNoop { + return &mode.DeprovisionResponse{ + Operation: deprovisionedNoopOperation, + }, nil + } + releaseName, ok := dreq.Parameters[releaseNameKey] + if !ok { + logger.Errorf("finding the release name key") + return nil, errMissingReleaseName{params: dreq.Parameters} + } + if _, err := d.deleter.Delete(releaseName); err != nil { + logger.Errorf("deleting the helm chart (%s)", err) + return nil, err + } + + return &mode.DeprovisionResponse{ + Operation: deprovisionedActiveOperation, + }, nil +} + +// NewDeprovisioner returns a new Tiller-backed mode.Deprovisioner +func NewDeprovisioner(chart *chart.Chart, provBehavior ProvisionBehavior, deleter ReleaseDeleter) mode.Deprovisioner { + return deprovisioner{ + chart: chart, + provBehavior: provBehavior, + deleter: deleter, + } +} diff --git a/mode/helm/doc.go b/mode/helm/doc.go new file mode 100644 index 0000000..967fc7d --- /dev/null +++ b/mode/helm/doc.go @@ -0,0 +1,2 @@ +// Package helm contains the Helm / Tiller mode implementations and config. See https://github.com/kubernetes/helm for more information on Helm and Tiller +package helm diff --git a/mode/helm/helm_config_maps.go b/mode/helm/helm_config_maps.go new file mode 100644 index 0000000..f6c1c14 --- /dev/null +++ b/mode/helm/helm_config_maps.go @@ -0,0 +1,24 @@ +package helm + +import ( + "k8s.io/kubernetes/pkg/api" + kcl "k8s.io/kubernetes/pkg/client/unversioned" +) + +// look up the ConfigMap using cmNamespacer for each item in cmInfos. return any errors getting config map or calling fn +func rangeConfigMaps( + cmNamespacer kcl.ConfigMapsNamespacer, + cmInfos []cmNamespaceAndName, + fn func(*api.ConfigMap) error) error { + for _, cmInfo := range cmInfos { + cm, err := cmNamespacer.ConfigMaps(cmInfo.Namespace).Get(cmInfo.Name) + if err != nil { + logger.Debugf("no such ConfigMap %s/%s", cmInfo.Namespace, cmInfo.Name) + return err + } + if err := fn(cm); err != nil { + return err + } + } + return nil +} diff --git a/mode/helm/lifecycler.go b/mode/helm/lifecycler.go new file mode 100644 index 0000000..e8483df --- /dev/null +++ b/mode/helm/lifecycler.go @@ -0,0 +1,32 @@ +package helm + +import ( + "context" + + "github.com/deis/steward/mode" + "k8s.io/helm/pkg/proto/hapi/chart" + kcl "k8s.io/kubernetes/pkg/client/unversioned" +) + +// NewLifecycler creates a new mode.Lifecycler that's backed by a Tiller instance accessible by iface +func NewLifecycler( + ctx context.Context, + chart *chart.Chart, + installNS string, + provBehavior ProvisionBehavior, + creatorDeleter ReleaseCreatorDeleter, + cmNamespacer kcl.ConfigMapsNamespacer, +) (*mode.Lifecycler, error) { + + binder, err := NewBinder(chart, cmNamespacer) + if err != nil { + return nil, err + } + unbinder, err := NewUnbinder(chart, cmNamespacer) + return &mode.Lifecycler{ + Provisioner: NewProvisioner(chart, installNS, provBehavior, creatorDeleter), + Binder: binder, + Unbinder: unbinder, + Deprovisioner: NewDeprovisioner(chart, provBehavior, creatorDeleter), + }, nil +} diff --git a/mode/helm/provision_behavior.go b/mode/helm/provision_behavior.go new file mode 100644 index 0000000..850076b --- /dev/null +++ b/mode/helm/provision_behavior.go @@ -0,0 +1,39 @@ +package helm + +import ( + "fmt" +) + +type errUnknownProvisionBehavior struct { + behavior string +} + +func (e errUnknownProvisionBehavior) Error() string { + return fmt.Sprintf("unknown provision behavior %s", e.behavior) +} + +const ( + // ProvisionBehaviorNoop indicates that steward should 'helm install' a new instance of the chart on startup, and that provision and deprovisoin should do nothing + ProvisionBehaviorNoop ProvisionBehavior = "noop" + // ProvisionBehaviorActive indicates that steward should 'helm install' a new instance of the chart on every provision operation (and helm uninstall on each deprovision operation) + ProvisionBehaviorActive ProvisionBehavior = "active" +) + +// ProvisionBehavior is the indication for what steward should do in helm mode when a provision comes in. It implements fmt.Stringer +type ProvisionBehavior string + +// ProvisionBehaviorFromString returns the ProvisionBehavior that corresponds to s. If s is an invalid provision behavior, returns an empty string and a non-nil error +func ProvisionBehaviorFromString(s string) (ProvisionBehavior, error) { + switch s { + case ProvisionBehaviorActive.String(): + return ProvisionBehaviorActive, nil + case ProvisionBehaviorNoop.String(): + return ProvisionBehaviorNoop, nil + default: + return "", errUnknownProvisionBehavior{behavior: s} + } +} + +func (p ProvisionBehavior) String() string { + return string(p) +} diff --git a/mode/helm/provisioner.go b/mode/helm/provisioner.go new file mode 100644 index 0000000..4851afc --- /dev/null +++ b/mode/helm/provisioner.go @@ -0,0 +1,55 @@ +package helm + +import ( + "github.com/deis/steward/mode" + "k8s.io/helm/pkg/proto/hapi/chart" +) + +const ( + provisionedNoopOperation = "provisioned-noop" + provisionedActiveOperation = "provisioned-active" +) + +type provisioner struct { + chart *chart.Chart + targetNS string + provBehavior ProvisionBehavior + creator ReleaseCreator +} + +func (p provisioner) Provision(instanceID string, req *mode.ProvisionRequest) (*mode.ProvisionResponse, error) { + if p.provBehavior == ProvisionBehaviorNoop { + return &mode.ProvisionResponse{ + Operation: provisionedNoopOperation, + }, nil + } + + createResp, err := p.creator.Create(p.chart, p.targetNS) + if err != nil { + logger.Errorf("creating the helm chart (%s)", err) + return nil, err + } + + resp := &mode.ProvisionResponse{ + Operation: provisionedActiveOperation, + Extra: mode.JSONObject(map[string]string{ + releaseNameKey: createResp.Release.Name, + }), + } + return resp, nil +} + +// NewProvisioner returns a new Tiller-backed mode.Provisioner +func NewProvisioner( + chart *chart.Chart, + targetNS string, + provBehavior ProvisionBehavior, + creator ReleaseCreator, +) mode.Provisioner { + return provisioner{ + chart: chart, + targetNS: targetNS, + provBehavior: provBehavior, + creator: creator, + } +} diff --git a/mode/helm/release_creator_deleter.go b/mode/helm/release_creator_deleter.go new file mode 100644 index 0000000..5d587a9 --- /dev/null +++ b/mode/helm/release_creator_deleter.go @@ -0,0 +1,22 @@ +package helm + +import ( + "k8s.io/helm/pkg/proto/hapi/chart" + rls "k8s.io/helm/pkg/proto/hapi/services" +) + +// ReleaseCreator is the interface for creating a helm release. It's intended for use in function params for easy mocking +type ReleaseCreator interface { + Create(*chart.Chart, string) (*rls.InstallReleaseResponse, error) +} + +// ReleaseDeleter is the interface for deleting a helm release. It's intended for use in function params for easy mocking +type ReleaseDeleter interface { + Delete(string) (*rls.UninstallReleaseResponse, error) +} + +// ReleaseCreatorDeleter is the concrete composition of a ReleaseCreator and ReleaseDeleter +type ReleaseCreatorDeleter interface { + ReleaseCreator + ReleaseDeleter +} diff --git a/mode/helm/steward_values.go b/mode/helm/steward_values.go new file mode 100644 index 0000000..ef8391c --- /dev/null +++ b/mode/helm/steward_values.go @@ -0,0 +1,29 @@ +package helm + +import ( + "fmt" + + "github.com/ghodss/yaml" + "k8s.io/helm/pkg/proto/hapi/chart" +) + +type cmNamespaceAndName struct { + Name string `json:"name"` + Namespace string `json:"namespace"` +} + +func (c cmNamespaceAndName) String() string { + return fmt.Sprintf("namespace=%s,name=%s", c.Namespace, c.Name) +} + +type stewardValues struct { + ConfigMaps []cmNamespaceAndName `json:"stewardConfigMaps"` +} + +func getStewardConfigMapInfo(chart *chart.Config) ([]cmNamespaceAndName, error) { + var ret stewardValues + if err := yaml.Unmarshal([]byte(chart.Raw), &ret); err != nil { + return nil, err + } + return ret.ConfigMaps, nil +} diff --git a/mode/helm/steward_values_test.go b/mode/helm/steward_values_test.go new file mode 100644 index 0000000..1494d71 --- /dev/null +++ b/mode/helm/steward_values_test.go @@ -0,0 +1,9 @@ +package helm + +import ( + "testing" +) + +func TestGetStewardConfigMapInfo(t *testing.T) { + t.Skip("TODO") +} diff --git a/mode/helm/tiller_release_creator_deleter.go b/mode/helm/tiller_release_creator_deleter.go new file mode 100644 index 0000000..4913978 --- /dev/null +++ b/mode/helm/tiller_release_creator_deleter.go @@ -0,0 +1,51 @@ +package helm + +import ( + "golang.org/x/net/context" + "google.golang.org/grpc" + "k8s.io/helm/pkg/proto/hapi/chart" + rls "k8s.io/helm/pkg/proto/hapi/services" +) + +type tillerRCD struct { + tillerHost string +} + +func (t tillerRCD) Create(ch *chart.Chart, installNS string) (*rls.InstallReleaseResponse, error) { + c, err := grpc.Dial(t.tillerHost, grpc.WithInsecure()) + if err != nil { + return nil, err + } + defer c.Close() + rlsCl := rls.NewReleaseServiceClient(c) + ctx := context.Background() + req := &rls.InstallReleaseRequest{ + Chart: ch, + Namespace: installNS, + ReuseName: true, + DisableHooks: false, + } + logger.Debugf("installing release for chart %s", ch.Metadata.Name) + return rlsCl.InstallRelease(ctx, req) +} + +func (t tillerRCD) Delete(relName string) (*rls.UninstallReleaseResponse, error) { + c, err := grpc.Dial(t.tillerHost, grpc.WithInsecure()) + if err != nil { + return nil, err + } + defer c.Close() + rlsCl := rls.NewReleaseServiceClient(c) + ctx := context.Background() + req := &rls.UninstallReleaseRequest{ + Name: relName, + DisableHooks: false, + } + logger.Debugf("uninstalling release %s", relName) + return rlsCl.UninstallRelease(ctx, req) +} + +// NewTillerReleaseCreatorDeleter returns a new ReleaseCreatorDeleter implemented with a tiller backend +func NewTillerReleaseCreatorDeleter(tillerHost string) ReleaseCreatorDeleter { + return tillerRCD{tillerHost: tillerHost} +} diff --git a/mode/helm/unbinder.go b/mode/helm/unbinder.go new file mode 100644 index 0000000..eedb4d9 --- /dev/null +++ b/mode/helm/unbinder.go @@ -0,0 +1,36 @@ +package helm + +import ( + "github.com/deis/steward/mode" + "k8s.io/helm/pkg/proto/hapi/chart" + "k8s.io/kubernetes/pkg/api" + kcl "k8s.io/kubernetes/pkg/client/unversioned" +) + +type unbinder struct { + cmInfos []cmNamespaceAndName + cmNamespacer kcl.ConfigMapsNamespacer +} + +func (u unbinder) Unbind(instanceID, bindingID string, unbindRequest *mode.UnbindRequest) error { + // iterate and delete all ConfigMaps represented in cmInfos + if err := rangeConfigMaps(u.cmNamespacer, u.cmInfos, func(cm *api.ConfigMap) error { + return u.cmNamespacer.ConfigMaps(cm.Namespace).Delete(cm.Name) + }); err != nil { + logger.Errorf("ranging over helm chart credential config maps (%s)", err) + return err + } + return nil +} + +// NewUnbinder returns a Tiller-backed mode.Unbinder +func NewUnbinder(chart *chart.Chart, cmNamespacer kcl.ConfigMapsNamespacer) (mode.Unbinder, error) { + cmInfos, err := getStewardConfigMapInfo(chart.Values) + if err != nil { + return nil, err + } + return unbinder{ + cmInfos: cmInfos, + cmNamespacer: cmNamespacer, + }, nil +} diff --git a/mode/json_object.go b/mode/json_object.go index 3b3627a..87ba38b 100644 --- a/mode/json_object.go +++ b/mode/json_object.go @@ -3,12 +3,22 @@ package mode import ( "encoding/base64" "errors" + "fmt" + "strings" ) var ( errMissing = errors.New("key is missing") ) +type errMalformedKV struct { + kv []string +} + +func (e errMalformedKV) Error() string { + return fmt.Sprintf("malformed JSONObject-encoded key/value pair %s", e.kv) +} + // JSONObject is a convenience wrapper around a Go type that represents a JSON object type JSONObject map[string]string @@ -28,3 +38,32 @@ func (j JSONObject) Base64EncodedVals() JSONObject { } return JSONObject(newMap) } + +// MarshalText is the encoding.TextMarshaler implementation +func (j JSONObject) EncodeToString() string { + slc := make([]string, len(j)) + i := 0 + for key, val := range j { + slc[i] = fmt.Sprintf("%s=%s", key, val) + i++ + } + return strings.Join(slc, ",") +} + +// JSONObjectFromString decodes a string into a JSONObject. Returns a non-nil error if the string was not a valid JSONObject +func JSONObjectFromString(str string) (JSONObject, error) { + if len(str) == 0 { + return JSONObject(map[string]string{}), nil + } + mp := map[string]string{} + spl := strings.Split(str, ",") + for _, s := range spl { + kv := strings.Split(s, "=") + if len(kv) != 2 { + return JSONObject(map[string]string{}), errMalformedKV{kv: kv} + } + key, val := kv[0], kv[1] + mp[key] = val + } + return JSONObject(mp), nil +} diff --git a/mode/json_object_test.go b/mode/json_object_test.go index ae63d3a..7da029d 100644 --- a/mode/json_object_test.go +++ b/mode/json_object_test.go @@ -33,3 +33,25 @@ func TestJSONObjectBase64EncodedVals(t *testing.T) { assert.Equal(t, decodedMap[k], v, "value of key "+k) } } + +func TestJSONObjectToFromStringRoundTrip(t *testing.T) { + t.Run("full JSONObject", func(t *testing.T) { + jso := JSONObject(map[string]string{ + "key1": "val1", + "key2": "val2", + "key3": "val3", + uuid.New(): uuid.New(), + }) + jsoStr := jso.EncodeToString() + jsoDecoded, err := JSONObjectFromString(jsoStr) + assert.NoErr(t, err) + assert.Equal(t, len(jsoDecoded), len(jso), "decoded json object length") + }) + t.Run("empty JSONObject", func(t *testing.T) { + jso := JSONObject(map[string]string{}) + jsoStr := jso.EncodeToString() + jsoDecoded, err := JSONObjectFromString(jsoStr) + assert.NoErr(t, err) + assert.Equal(t, len(jsoDecoded), len(jso), "decoded json object length") + }) +} diff --git a/mode/provision_response.go b/mode/provision_response.go index 1f5c442..a815888 100644 --- a/mode/provision_response.go +++ b/mode/provision_response.go @@ -2,5 +2,6 @@ package mode // ProvisionResponse represents a response to a provisioning request. It is marked with JSON struct tags so that it can be encoded to, and decoded from the CloudFoundry provisioning response body format. See https://docs.cloudfoundry.org/services/api.html#provisioning for more details type ProvisionResponse struct { - Operation string `json:"operation"` + Operation string `json:"operation"` + Extra JSONObject `json:"extra,omitempty"` } diff --git a/mode/service_plan_claim.go b/mode/service_plan_claim.go index 58cc296..d0190c1 100644 --- a/mode/service_plan_claim.go +++ b/mode/service_plan_claim.go @@ -40,6 +40,7 @@ const ( targetNameMapKey = "target-name" instanceIDMapKey = "instance-id" bindIDMapKey = "bind-id" + extraMapKey = "extra" // ActionProvision is the action indicating that a service should be provision ActionProvision Action = "provision" @@ -84,15 +85,16 @@ func (e errServicePlanClaimMapMissingKey) Error() string { // ServicePlanClaim is the json-encodable struct that represents a service plan claim. See https://github.com/deis/steward/blob/master/DATA_STRUCTURES.md#serviceplanclaim for more detail. This struct implements fmt.Stringer type ServicePlanClaim struct { - TargetName string `json:"target-name"` - ServiceID string `json:"service-id"` - PlanID string `json:"plan-id"` - ClaimID string `json:"claim-id"` - Action string `json:"action"` - Status string `json:"status"` - StatusDescription string `json:"status-description"` - InstanceID string `json:"instance-id"` - BindID string `json:"bind-id"` + TargetName string `json:"target-name"` + ServiceID string `json:"service-id"` + PlanID string `json:"plan-id"` + ClaimID string `json:"claim-id"` + Action string `json:"action"` + Status string `json:"status"` + StatusDescription string `json:"status-description"` + InstanceID string `json:"instance-id"` + BindID string `json:"bind-id"` + Extra JSONObject `json:"extra"` } // ServicePlanClaimFromMap attempts to convert m to a ServicePlanClaim. If the map was malformed or missing any keys, returns nil and an appropriate error @@ -122,6 +124,11 @@ func ServicePlanClaimFromMap(m map[string]string) (*ServicePlanClaim, error) { statusDescription := m[statusDescriptionMapKey] instanceID := m[instanceIDMapKey] bindID := m[bindIDMapKey] + extraStr := m[extraMapKey] + extra, err := JSONObjectFromString(extraStr) + if err != nil { + return nil, err + } return &ServicePlanClaim{ TargetName: targetName, @@ -133,6 +140,7 @@ func ServicePlanClaimFromMap(m map[string]string) (*ServicePlanClaim, error) { StatusDescription: statusDescription, InstanceID: instanceID, BindID: bindID, + Extra: extra, }, nil } @@ -148,6 +156,7 @@ func (s ServicePlanClaim) ToMap() map[string]string { statusDescriptionMapKey: s.StatusDescription, instanceIDMapKey: s.InstanceID, bindIDMapKey: s.BindID, + extraMapKey: s.Extra.EncodeToString(), } } diff --git a/mode/utils/helm_config.go b/mode/utils/helm_config.go new file mode 100644 index 0000000..6148715 --- /dev/null +++ b/mode/utils/helm_config.go @@ -0,0 +1,29 @@ +package utils + +import ( + "github.com/deis/steward/config" +) + +// Config is the envconfig-compatible struct for a backing Tiller server +type helmConfig struct { + TillerIP string `envconfig:"HELM_TILLER_IP" required:"true"` + TillerPort int `envconfig:"HELM_TILLER_PORT" required:"true"` + ChartURL string `envconfig:"HELM_CHART_URL" required:"true"` + ChartInstallNS string `envconfig:"HELM_CHART_INSTALL_NAMESPACE" required:"true"` + ProvisionBehavior string `envconfig:"HELM_PROVISION_BEHAVIOR" required:"true"` + ServiceID string `envconfig:"HELM_SERVICE_ID" required:"true"` + ServiceName string `envconfig:"HELM_SERVICE_NAME" required:"true"` + ServiceDescription string `envconfig:"HELM_SERVICE_DESCRIPTION" required:"true"` + PlanID string `envconfig:"HELM_PLAN_ID" required:"true"` + PlanName string `envconfig:"HELM_PLAN_NAME" required:"true"` + PlanDescription string `envconfig:"HELM_PLAN_DESCRIPTION" required:"true"` +} + +// GetConfig gets the configuration for helm mode +func getHelmConfig() (*helmConfig, error) { + ret := new(helmConfig) + if err := config.Load(ret); err != nil { + return nil, err + } + return ret, nil +} diff --git a/mode/utils/helm_mode.go b/mode/utils/helm_mode.go new file mode 100644 index 0000000..fc02a63 --- /dev/null +++ b/mode/utils/helm_mode.go @@ -0,0 +1,55 @@ +package utils + +import ( + "context" + "fmt" + "net/http" + + "github.com/deis/steward/mode" + "github.com/deis/steward/mode/helm" + kcl "k8s.io/kubernetes/pkg/client/unversioned" +) + +func getHelmModeComponents( + ctx context.Context, + httpCl *http.Client, + cmNamespacer kcl.ConfigMapsNamespacer, +) (mode.Cataloger, *mode.Lifecycler, error) { + helmCfg, err := getHelmConfig() + if err != nil { + logger.Errorf("getting helm config (%s)", err) + return nil, nil, errGettingBrokerConfig{Original: err} + } + logger.Infof("starting in Helm mode with Tiller backend at %s:%d", helmCfg.TillerIP, helmCfg.TillerPort) + + provBehavior, err := helm.ProvisionBehaviorFromString(helmCfg.ProvisionBehavior) + if err != nil { + logger.Errorf("parsing provision behavior from '%s' (%s)", helmCfg.ProvisionBehavior, err) + return nil, nil, err + } + + chart, _, err := helm.GetChart(ctx, httpCl, helmCfg.ChartURL) + if err != nil { + logger.Errorf("getting chart from %s (%s)", helmCfg.ChartURL, err) + return nil, nil, err + } + + tillerHost := fmt.Sprintf("%s:%d", helmCfg.TillerIP, helmCfg.TillerPort) + creatorDeleter := helm.NewTillerReleaseCreatorDeleter(tillerHost) + + cataloger := helm.NewCataloger( + helmCfg.ServiceID, + helmCfg.ServiceName, + helmCfg.ServiceDescription, + helmCfg.PlanID, + helmCfg.PlanName, + helmCfg.PlanDescription, + ) + lifecycler, err := helm.NewLifecycler(ctx, chart, helmCfg.ChartInstallNS, provBehavior, creatorDeleter, cmNamespacer) + if err != nil { + logger.Errorf("creating a new helm mode lifecycler (%s)", err) + return nil, nil, err + } + + return cataloger, lifecycler, nil +} diff --git a/mode/utils/mode_runner.go b/mode/utils/mode_runner.go index 2868588..b576ae8 100644 --- a/mode/utils/mode_runner.go +++ b/mode/utils/mode_runner.go @@ -3,6 +3,7 @@ package utils import ( "context" "fmt" + "net/http" "github.com/deis/steward/k8s" "github.com/deis/steward/k8s/claim" @@ -12,12 +13,23 @@ import ( ) const ( - cfMode = "cf" + cfMode = "cf" + helmMode = "helm" ) // Run publishes the underlying broker's service offerings to the catalog, then starts Steward's // control loop in the specified mode. -func Run(ctx context.Context, modeStr string, errCh chan<- error, namespaces []string) error { +func Run( + ctx context.Context, + httpCl *http.Client, + modeStr string, + errCh chan<- error, namespaces []string) error { + + k8sClient, err := kcl.NewInCluster() + if err != nil { + return errGettingK8sClient{Original: err} + } + var cataloger mode.Cataloger var lifecycler *mode.Lifecycler // Get the right implementations of mode.Cataloger and mode.Lifecycler @@ -28,17 +40,18 @@ func Run(ctx context.Context, modeStr string, errCh chan<- error, namespaces []s if err != nil { return err } + case helmMode: + var err error + cataloger, lifecycler, err = getHelmModeComponents(ctx, httpCl, k8sClient) + if err != nil { + return err + } default: return errUnrecognizedMode{mode: modeStr} } // Everything else does not vary by mode... - k8sClient, err := kcl.NewInCluster() - if err != nil { - return errGettingK8sClient{Original: err} - } - catalogInteractor := k8s.NewK8sServiceCatalogInteractor(k8sClient.RESTClient) published, err := publishCatalog(cataloger, catalogInteractor) if err != nil { @@ -53,7 +66,6 @@ func Run(ctx context.Context, modeStr string, errCh chan<- error, namespaces []s } logger.Infof("created service catalog lookup with %d items", lookup.Len()) claim.StartControlLoops(ctx, evtNamespacer, k8sClient, *lookup, lifecycler, namespaces, errCh) - claim.StartControlLoops(ctx, evtNamespacer, k8sClient, *lookup, lifecycler, namespaces, errCh) return nil } diff --git a/web/ctxhttp/ctxhttp.go b/web/ctxhttp/ctxhttp.go new file mode 100644 index 0000000..94cb44d --- /dev/null +++ b/web/ctxhttp/ctxhttp.go @@ -0,0 +1,73 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build go1.7 + +// Package ctxhttp provides helper functions for performing context-aware HTTP requests. This package was copied from the golang.org/x/net/context/ctxhttp package, and modified to use the standard library's "context" package +package ctxhttp + +import ( + "context" + "io" + "net/http" + "net/url" + "strings" +) + +// Do sends an HTTP request with the provided http.Client and returns +// an HTTP response. +// +// If the client is nil, http.DefaultClient is used. +// +// The provided ctx must be non-nil. If it is canceled or times out, +// ctx.Err() will be returned. +func Do(ctx context.Context, client *http.Client, req *http.Request) (*http.Response, error) { + if client == nil { + client = http.DefaultClient + } + resp, err := client.Do(req.WithContext(ctx)) + // If we got an error, and the context has been canceled, + // the context's error is probably more useful. + if err != nil { + select { + case <-ctx.Done(): + err = ctx.Err() + default: + } + } + return resp, err +} + +// Get issues a GET request via the Do function. +func Get(ctx context.Context, client *http.Client, url string) (*http.Response, error) { + req, err := http.NewRequest("GET", url, nil) + if err != nil { + return nil, err + } + return Do(ctx, client, req) +} + +// Head issues a HEAD request via the Do function. +func Head(ctx context.Context, client *http.Client, url string) (*http.Response, error) { + req, err := http.NewRequest("HEAD", url, nil) + if err != nil { + return nil, err + } + return Do(ctx, client, req) +} + +// Post issues a POST request via the Do function. +func Post(ctx context.Context, client *http.Client, url string, bodyType string, body io.Reader) (*http.Response, error) { + req, err := http.NewRequest("POST", url, body) + if err != nil { + return nil, err + } + req.Header.Set("Content-Type", bodyType) + return Do(ctx, client, req) +} + +// PostForm issues a POST request via the Do function. +func PostForm(ctx context.Context, client *http.Client, url string, data url.Values) (*http.Response, error) { + return Post(ctx, client, url, "application/x-www-form-urlencoded", strings.NewReader(data.Encode())) +}