Compare commits

...
97 Commits
Author SHA1 Message Date
KUOKA Yusuke eff2a7bf84 Fix repo update timing (#1287)
Fixes #1283
2020-05-30 18:01:14 +09:00
KUOKA Yusuke 3284df2752 Bump variantdev/chartify to fix kustomize support (#1280)
Fixes #1279
2020-05-29 10:08:09 +09:00
KUOKA Yusuke e607bae5ff Fix ignored chart version on patched release (#1276)
See https://sweetops.slack.com/archives/CE5NGCB9Q/p1590607083301900?thread_ts=1590237129.290300&cid=CE5NGCB9Q
2020-05-28 20:52:56 +09:00
KUOKA Yusuke 930f91a70d Merge pull request #1273 from roboll/fix-chartify
fix: Follow-up fixes for #1172
2020-05-27 22:15:41 +09:00
Yusuke Kuoka 1cfce32e0d Add documentation for releases[].{strategicMergePatches,jsonPatches} 2020-05-27 22:15:12 +09:00
Yusuke Kuoka 29fdb57add fix: Support for kustomization as chart
A kustomization has not been properly chartified when a kustomization is specified on `releases[].chart` but there were no `releases[].strategicMergePatches` and `jsonPatches` specified.
This fixes that.
2020-05-27 22:07:52 +09:00
Yusuke Kuoka 68432b1848 fix: Do not skip passing values files when adhocDependencies/jsonPatches/jsonPatches exist
This is a follow-up for #1172
2020-05-27 21:13:05 +09:00
KUOKA Yusuke 16288dfa7d feat: GA of Kustomize and K8s manifests support (#1172)
This is the GA version of the helm-x integration #673 developed last year.

You get all the following benefits without an extra helm plugin:

- Ability to add ad-hoc chart dependencies/aliases, without forking the chart (Fixes #876 )
- Ability to patch resulting K8s resources before installing the helm chart
- Ability to install a kustomization as a chart (Requires `kustomize` binary to be available in `$PATH`
- Ability to install a directory of K8s manifests as a chart
- etc.
2020-05-27 11:42:43 +09:00
Brian Choy 969de1ebc9 Fix typo in writing-helmfiles.md example (#1263) 2020-05-19 10:04:38 +09:00
Danny Shemesh ceb108a24f Fixed part splitting for CLRF separated files (#1265)
Fixes https://github.com/roboll/helmfile/issues/1142

desired_state_file_loader.go
- Will now normalize the content before splitting it to parts

context:
Me & and a fellow dev have tried to figure out why helmfile didn't fill in certain values on his machine; 
turns out, he'd mistakenly checked out our project w/ CRLF line endings, which had caused part splitting to not work (as it's hard coded to look for '\n').

The following was acted on as a single part, causing values from the bases not to be available in the next yaml part:

```
bases:\r\n
  - base.yaml\r\n
---\r\n

releases:
  - name: external-secrets-crd
    ... some templated yaml ...
```

I've thought about regex-ing it out instead of replace-all, but benchmarks had shown that a plain replace is faster.

I've also considered splitting by "\n---" instead of "\n---", but that would break if the dashes were to continue with some other text.
2020-05-19 10:04:12 +09:00
hacker65536 576a07f497 fix: typo in README (#1254) 2020-05-12 09:42:07 +09:00
Craig Furman eb8ab54865 Optionally error when subhelmfiles do not exist (#1245)
This prevents typos in helmfiles from silently preventing operations
from running. The default behavior is to print a warning, to preserve
backwards compatibility.
2020-05-12 09:41:39 +09:00
Yusuke Kuoka 9e4bc6e7fc chore: Add retries for downloading kubectl in docker builds 2020-05-09 14:31:52 +09:00
James F McMahon ef2d8853c2 README: Add note about environment variable types (#1251)
Fixes https://github.com/roboll/helmfile/issues/1248
2020-05-09 14:12:52 +09:00
machine424andayoub mrini 66f34580c3 feat: Add global --debug flag for passing --debug to helm (#1236)
Add a global flag --debug that sets the logging level to debug and passes the global flag --debug to Helm through the implementation of subcommands’ --args.

Resolves #1104

Co-authored-by: ayoub mrini <amrini@wiremind.fr>
2020-05-09 14:11:54 +09:00
Guillaume Perrin 6d5f8c71cb Allow skipping chart-related operations when installed: false (#1233)
Resolves #1232

CHANGES:

* Disable repo update when installed is false

When install is false, we do not need to update the repositories and get
the chart.

Signed-off-by: Guillaume Perrin <guillaume28.perrin@gmail.com>
2020-05-09 14:03:56 +09:00
刘相轩 23be9af2db Show executed command when errors occurred (#1252) 2020-05-09 13:35:31 +09:00
Cédric de Saint Martin 1e9e228a9a Dockerfile: Upgrade to Helm v3.2.1. (#1253) 2020-05-09 13:34:46 +09:00
Rick StokkingreefandRick Stokkingreef c72cab80b5 Bump variantdev/vals for Vault AppRole support (#1239)
Co-authored-by: Rick Stokkingreef <rick.stokkingreef@takeaway.com>
2020-05-02 09:40:16 +09:00
Victor Noël 923bd54db0 Use namespace for release unicity with helm3 (#1213) (#1235)
This is for #1213 so that the releases are considered scoped to their namespace and not to tiller namespace.

Fixes #1213
2020-05-02 09:35:51 +09:00
nejtr0nandAlexey Shishkin a0e30c5193 Correct ordering between releases and sub-helmfiles on destroy/delete (#1234)
Fixes #1192

Co-authored-by: Alexey Shishkin <a.shishkin@directheroes.com>
2020-05-01 09:06:13 +09:00
Cédric Lamalle daf8c2f12b Upgrade helm to v3.2.0 (#1231)
* Upgrade helm to v3.2.0
2020-04-29 15:00:49 +09:00
Craig Dunford d71ac15748 Enhance createNamespace error handling (#1227)
- An error is raised if createNamespace is set explicitly and the
  helm version being used is not 3.2+
2020-04-27 08:59:44 +09:00
Craig Dunford eeb61e6174 Support for createNamespace (#1226)
- createNamespace is a new attribute that can be added to helmDefaults
  or an individual release to enforce the creation of a release namespace
  during sync if the namespace does not exist. This leverages helm's
  (3.2+) --create-namespace flag for the install/upgrade command. If
  running helm < 3.2, the createNamespace attribute has no effect.

Resolves #891
Resolves #1140
2020-04-26 10:41:40 +09:00
Marcin Kaciuba b1190508b2 feat(template): added secret template function (#1221)
* feat(tmpl): added fetchSecretValue template function

This adds a tmpl `fetchSecretValue` and `expandSecretRefs` function by:
- Adding:
    - `expandSecretRefs` function in tmpl package that uses vals
    package to fetch secrets
    - `fetchSecretValue` function in tmpl package like below but for
    single string value
    - gomock for tests purpose
- Changing:
    - move init of vals package to function (so the same instance can be used for template values and rendering the whole template)

* doc(secret): added doc how to use new tmpl methods

Added example usage of `fetchSecretValue` and `expandSecretRefs`
2020-04-25 21:10:02 +09:00
Ash Caire 3a19a394ab Add condition release attribute (#1190) 2020-04-23 08:43:34 +09:00
Jesse Glick dcb19f942b Typo: kustommize (#1224) 2020-04-23 08:38:33 +09:00
Eric Bailey 458385d971 Amend examples which broke lock files in README.md (#1225)
Prior to this change, these two examples suggested enclosing a release spec in
an if expression, which breaks the lock file when switching environments.

This change parameterizes `installed` instead of the entire release, thus
preserving the dependency locking functionality.
2020-04-23 08:38:01 +09:00
chenrui b89fba1cc2 Bump golang to v1.14.2 (#1132)
* Bump golang to v1.14.2

* Use gotest.tools/v3
2020-04-21 09:06:29 +09:00
KUOKA Yusuke 6b6b86a0b5 Bump variantdev/vals to v0.4.0 (#1217)
For GCP Secrets Manager support
2020-04-18 23:22:23 +09:00
Rene Hernandez e0a793b7c5 New output flag for list command (#1215)
* New output flag for list command

Support output as json
Add new formatters file to handle extrac formatting to its own concern
New config interface to support list command specification

* Fix usage message

* Add error handling for formatters
2020-04-18 21:11:12 +09:00
Danny Shemesh 71bb7354e7 Fix: populate .Values regardless of prestate success (#1202)
This commit proposes a potential solution for
https://github.com/roboll/helmfile/issues/1201

The gist is that, if prestate rendering fails, for any reason,
we do not populate the .Values in the second pass renderer.

I think that what have been expected in this case is to populate the
.Values irregardless.

pkg/app/two_pass_renderer.go
- Migrated to use finalEnv.GetMergedValues()

pkg/environment/environment.go
- Introduced GetMergedValues, which merges the environment's defaults
and current values, and then casts the keys to string;
This was previously defined in HelmState.Values() - however, as this
method is only concerned with the environment, I think it's more
appropriate for it to sit here.

pkg/state/state_exec_tmpl.go
- Extracted out HelmState.Values() to environment.go, see above
2020-04-17 09:18:01 +09:00
KUOKA Yusuke 3a34d28d7c fix: helmDefaults.timeout and releases[].timeout not working for helmfile test (#1211)
Fixes #1191
2020-04-17 09:08:21 +09:00
Douglas Hellinger f39d7928bf Put tiller on path for use in helm-tiller plugin (#1198)
Put tiller binary on the path in helm v2.X container image so that helm-tiller plugin will use it instead of duplicating the download of helm tarball and performing version check.
2020-04-14 09:18:18 +09:00
Vadim Bauer e0811dffa2 Update helmfile.yaml example in README (#1189)
- Adds default comments into helmfile.yaml example

- Fixes some typos

Signed-off-by: Vadim Bauer <vb@8gears.com>
2020-04-10 08:27:09 +09:00
RaymondKYLiuandRaymond Liu 7d11f5dedc feat: add tmpl function required (#1188)
Co-authored-by: Raymond Liu (RD-TW) <raymond_liu@trend.com.tw>
2020-04-10 08:23:42 +09:00
KUOKA Yusuke 870cc03c70 feat: helmfile diff --detailed-exitcode should also detect deletions (#1186)
Resolves #499
Resolves #1072
2020-04-10 08:22:33 +09:00
KUOKA Yusuke 98886df5d2 fix: .Values is missing keys with zero values (#1185)
Ref #1184
2020-04-10 08:21:23 +09:00
RaymondKYLiuandRaymond Liu 71635caace feat: add option --include-tests for diff and apply command (#1179)
Co-authored-by: Raymond Liu (RD-TW) <raymond_liu@trend.com.tw>
2020-04-05 17:43:54 +09:00
KUOKA Yusuke 486be0970d Bump vals to 0.3.0 (#1175)
vals v0.3.0 introduces the terraform output source that can be accessed by `ref+tfstate://path/to/tfstatefile/type.resourcename.prop` syntax. Please see the updated README section of vals for more info.
2020-04-04 19:50:01 +09:00
KUOKA Yusuke 9d7d2de6f5 Fix misleading helmfile diff output (#1174)
Fixes #749
2020-04-04 17:39:20 +09:00
KUOKA Yusuke 04c963d050 feat: Complete helmfile diff output when diff exists (#1173)
Fixes #874
2020-04-04 16:24:01 +09:00
KUOKA Yusuke a2c62c9e73 feat: Emit clear error message instead of panic on empty chart name (#1171)
Resolves #999
2020-04-04 15:32:22 +09:00
KUOKA Yusuke 1654ce4c18 Add regression tests for environment values merge (#1170)
This is a follow-up for #1169 and it also relates to #1168
2020-04-04 14:27:29 +09:00
KUOKA Yusuke 25aae679b1 Merge pull request #1169 from elodani/master
fix: empty map merge was not working after mergo bump
2020-04-04 14:00:41 +09:00
Daniel Elo d404356230 feat: fix empty map merge after mergo bump
since mergo had been bumped to 0.3.9 an environment value like:
```
affinity: {}
```
was not merged properly (not merged at all) instead it threw an error
that it cannot find the key "affinity" in the environment values even
though it was outputted in debug output as read in properly.
2020-04-03 15:01:27 +02:00
KUOKA Yusuke 51ecd12360 Fix panic while loading environment secrets (#1164)
This fixes a regression in #1160
2020-03-30 20:13:40 +09:00
KUOKA Yusuke 6643a41ea3 fix: merge environment values by ovewriting with empty values (#1162)
Fixes #1154
2020-03-29 20:47:23 +09:00
KUOKA Yusuke 35e5454994 Change the helmfile list header "INSTALLED" -> "ENABLED" (#1161)
Resolves #1076
2020-03-29 19:11:58 +09:00
KUOKA Yusuke 69feadc360 feat: helmBinary in helmfile.yaml (#1160)
* feat: `helmBinary` in helmfile.yaml

Resolves #1083

* Add regression test for `helmfile destroy`
2020-03-29 17:51:07 +09:00
KUOKA Yusuke f676c61425 feat: Better exec error reporting (#1159)
Enhances Helmfile to print more helpful message on error while calling `exec` template function.

Helmfile has been printing error messages like the below:

```
in ./helmfile.yaml: error during helmfile.yaml.part.0 parsing: template: stringTemplate:5:8: executing "stringTemplate" at <exec "./exectest.sha" (list)>: error calling exec: exit status 1
```

Adding captured stdout and stderr, with some indentation to make it readable, it now produces the following message on missing executable:

```
$ make build && ./helmfile build
go build
in ./helmfile.yaml: error during helmfile.yaml.part.0 parsing: template: stringTemplate:5:8: executing "stringTemplate" at <exec "./exectest.sha" (list)>: error calling exec: fork/exec ./exectest.sha: no such file or directory

COMMAND:
  ./exectest.sha

ERROR:
  fork/exec ./exectest.sha: no such file or directory
```

On non-zero exit status without output:

```
$ make build && ./helmfile build
go build
in ./helmfile.yaml: error during helmfile.yaml.part.0 parsing: template: stringTemplate:5:8: executing "stringTemplate" at <exec "./exectest.sh" (list)>: error calling exec: exit status 1

COMMAND:
  ./exectest.sh

ERROR:
  exit status 1
```

On non-zero exit status with output:

```
$ make build && ./helmfile build
go build
in ./helmfile.yaml: error during helmfile.yaml.part.0 parsing: template: stringTemplate:5:8: executing "stringTemplate" at <exec "./exectest.sh" (list)>: error calling exec: exit status 2

COMMAND:
  ./exectest.sh

ERROR:
  exit status 2

COMBINED OUTPUT:
  out1
  err1
```

Resolves #1158
2020-03-29 10:49:02 +09:00
KUOKA Yusuke 20a39e9412 Bump mergo to 0.3.8 (#1153)
Fixes #1150
2020-03-20 18:56:43 +09:00
KUOKA Yusuke 048c79ad34 Bump variantdev/vals to 0.2.0 (#1152)
For support for the advanced AWS profile usage in retrieving AWS SSM params/secrets https://github.com/variantdev/vals/issues/19
2020-03-20 16:00:21 +09:00
KUOKA Yusuke 994e4b66fc fix: exec template func should not throw away stdout when stdin is non empty (#1151)
Fixes #1149
2020-03-20 12:23:34 +09:00
KUOKA Yusuke 499f6ec6e3 Add Sportradar to USERS.md (#1133) 2020-03-05 08:09:17 +09:00
KUOKA Yusuke bf22502a2f feat: add helmfile template --validate (#1135)
This adds the ability for helmfile to call `helm template --validate` introduced in helm 3.

fixes #1105
2020-03-05 08:07:02 +09:00
Eric Bailey 7c80a859fa Fix awkward dependency error message (#1130) 2020-03-02 08:23:03 +09:00
Gleidson Nascimento 00c4422a64 Add validation for empty repository names (#1128)
Resolves #1112
2020-02-28 19:42:15 +09:00
KUOKA Yusuke af44965949 feat: helmfile --log-level=debug apply --retain-values-files (#1127)
`--retain-values-files` prevents temporary values files that were passed to Helm commands run by Helmfile for debugging purpose.

With that, you can manually rerun helm commands that were logged when `--log-level=debug` is enabled.

Resolves ##1117
2020-02-28 19:39:01 +09:00
KUOKA Yusuke 0186254e79 feat: --detailed-exitcode for helmfile apply (#1120)
Resolves #1113
2020-02-26 21:09:05 +09:00
jfreeland 8acbbc596d new here, fixing very minor typo (#1121) 2020-02-26 10:33:35 +09:00
KUOKA Yusuke f1bdb65777 fix: delete/destroy ordering within directory (#1119)
Fixes #979
2020-02-25 09:46:01 +09:00
Mark Henneman 322ed4d1d8 fix explanation about secrets in .Environment.Values (#1116) 2020-02-24 17:00:11 +09:00
刘相轩 c4e6160220 Remove unneeded env HELMFILE_HELM3 since #963 (#1109) 2020-02-21 00:12:26 +09:00
KUOKA Yusuke 10f2a576c5 fix: helmfile -n ns statuses should set ns for helm 3 (#1108)
This fixes the issue reported in Slack https://sweetops.slack.com/archives/CE5NGCB9Q/p1582088381195000
2020-02-20 22:30:02 +09:00
chenrui 2579a876a6 Upgrade deps (#1100)
* Upgrade helm3 to v3.1.0

* Upgrade helm2 to v2.16.1

* Use golang v1.13.7
2020-02-17 08:43:14 +09:00
Alex Jones 6b3277ce71 Added AMEX to USERS.md (#1101) 2020-02-17 08:41:41 +09:00
Anton Trifonov 27425bc4d2 Fix status command for helm 3 (#1098) 2020-02-13 08:40:20 +09:00
Emil 05add478c1 Add option to suppress diff on apply (#1092)
* Add option to suppress diff on apply

Add --supress-diff option on apply. Usable for fresh installs when a
lot of output is produces by diff.

Resolves #458

* fix tests for suppress-diff
2020-02-05 21:29:55 +09:00
abdennour 757e748e3f Feature/autobuild docker image for each release thru dockerhub auto builds (#886)
* feat: build hook for dockerhub autobuilds

* release new image

* Create pre_build

* Update push
2020-02-03 08:45:51 +09:00
chenrui e6f1db128c Upgrade alpine to 3.11 (#1085) 2020-02-03 08:43:25 +09:00
Daniel Poelzleithner 5fb037c8e3 Fix crash on missing chart (#1087)
When chart is not set, an exception without any hints occure due
normalizeChart tries to access the first character of a 0 length string.
Properly inform user of missing chart.
2020-02-03 08:42:54 +09:00
Guillaume Lecerf 5abbdea059 Fix "Merging Arrays in Layers" doc example (#1084) 2020-02-03 08:42:16 +09:00
Christoph Hösler 9fab6851f0 fix test command for helm 3 (#1080) 2020-02-03 08:41:38 +09:00
Matt L c8ba676e19 Update writing-helmfile.md (#1073)
nitpick spelling
2020-02-03 08:39:40 +09:00
a-hat fc75f25293 do not pass --api-versions to "helm diff" (#1061) 2020-01-08 08:48:29 +09:00
KUOKA Yusuke b044820607 fix: helmfile destroy does not delete helm releases in status : PENDING_INSTALL (#995)
Fixes #539
2019-12-27 09:33:28 +09:00
KUOKA Yusuke 0e1aeb43ce Fix deps error for helm 3.0.2 (#1056)
Fixes #1052
2019-12-27 09:15:07 +09:00
Andrew Nazarov 88adc56551 Upgrade to the latest Helm 3 release (#1049) 2019-12-27 08:31:10 +09:00
a-hat 9cf6b59cd8 feat: Option to pass apiVersions to helm diff and helm template (#1046)
This makes it possible to pass the API Capabilities to helmfile when executing a task that does not render against an actual cluster (diff, template, apply).

Resolves #1014
2019-12-27 08:30:39 +09:00
Jake Hill fb7cc60afe Upgrade to latest stable Helm 3 (#1024)
Signed-off-by: Jake Hill <jake@naphta.uk>
2019-12-17 10:38:02 +09:00
Yujun Zhang 90c7b04dbf Fix invalid link in README.md (#1026) 2019-12-17 08:29:13 +09:00
刘相轩 ac534a897b Add historyMax (#1022)
Resloves #868
2019-12-17 08:27:42 +09:00
Maria Kotlyarevskaya 314557c824 Fix typo in README (#1032) 2019-12-17 08:25:11 +09:00
eddycharly 4b2d6946be Add indexed key support to --state-values-set (#1042)
Adds support for indexed key arguments and escaped dot arguments to `--state-values-set`.
Things like `--state-values-set config\.yml.something=abc` or `--state-values-set config.something[0]=abc` can be passed in the command line.

Resolves #899
2019-12-17 07:31:42 +09:00
Jake Hill 03898b7a98 Fix Helm test having run subcommand with helm3 (#1023)
It looks like during the beta phase the testing functionality was tweaked / changed back during the beta phase but has since been returned back to how it worked originally.

RE: https://github.com/helm/helm/commit/a6f4bc1bc0fc721aa501f7b8667928b2ec4f0016
Signed-off-by: Jake Hill <jake@naphta.uk>
2019-12-11 09:20:41 +09:00
刘相轩 4b1b19f8a6 feat: Persist Helmfile version in helmfile.lock for version compatibility check (#1016)
Reslove #698
2019-12-11 09:19:18 +09:00
KUOKA Yusuke 07c42474cc Do not fail due to missing env in base helmfile (#1009)
When helmfile is run with `--environment NAME` and there was a base hemlfile that misses `environments`, helmfile had been trying to load env values for NAME and failing.

A base helmfile is allowed to reference values from within itself, but that's optional. In other words, a base helmfile that misses the env is okay as long as it doesn't self-reference env values.

So, this change allows missing env and env values while loading base helmfile. After loading, a base helmfile can fail due to referencing missing env values, but that's okay.

Fixes #1008
2019-12-01 11:37:56 +09:00
KUOKA Yusuke 27c098c17c Fix capitalization of cleanup-on-fail config key (#1005)
It is how `cleanupOnFail` to be consistent with other keys.

Ref #969
2019-11-29 09:29:26 +09:00
ldealmei e745a2602b Fix typos (#1003) 2019-11-29 08:55:13 +09:00
Harshal Shah 04379cee80 Add the cleanup-on-fail flag to upgrade command (#969)
* Adding the cleanup-on-fail flag to upgrade command

* Adding tests and making the flag optional

* Updating readme with new flag details

* go fmt
2019-11-29 08:47:19 +09:00
KUOKA Yusuke 25599eae89 Fix ref url in values key to actually work (#998)
Sorry it was not working at all :(

See https://github.com/roboll/helmfile/issues/990#issuecomment-557756343 for how this is supposed to work.

Fixes #990
2019-11-23 11:36:44 +09:00
KUOKA Yusuke 8ea144e31f Validate on recursion (#993)
Fixes #983
2019-11-21 22:40:53 +09:00
KUOKA Yusuke f508b9091e Fix regression in the order of processed releases when concurrency is 1 (#992)
Fixes #988
2019-11-21 22:29:05 +09:00
KUOKA Yusuke 7ec92c0520 Bump variantdev/vals (#991)
For the new feature that allows you to merge-in YAML/JSON objects stored in a secret store.

Resolves #990
2019-11-21 22:10:21 +09:00
刘相轩 06a3b558e0 Disable tillerless for helm3 (#987) 2019-11-21 21:53:38 +09:00
KUOKA Yusuke 8a2311d033 Bump variantdev/vals (#985)
To accomodate the fix for https://github.com/variantdev/vals/issues/13
2019-11-20 08:32:01 +09:00
73 changed files with 5744 additions and 1100 deletions
+6 -6
View File
@@ -4,7 +4,7 @@ jobs:
build:
docker:
- image: circleci/golang:1.13.4
- image: circleci/golang:1.14.2
environment:
GOPATH: /go
working_directory: /home/circleci/workspace/helmfile
@@ -26,7 +26,7 @@ jobs:
- .
image:
docker:
- image: circleci/golang:1.13.4
- image: circleci/golang:1.14.2
working_directory: /home/circleci/workspace/helmfile
steps:
- checkout
@@ -43,7 +43,7 @@ jobs:
image_helm3:
docker:
- image: circleci/golang:1.13.4
- image: circleci/golang:1.14.2
working_directory: /home/circleci/workspace/helmfile
steps:
- checkout
@@ -62,7 +62,7 @@ jobs:
environment:
GOPATH: /go
docker:
- image: circleci/golang:1.13.4
- image: circleci/golang:1.14.2
working_directory: /home/circleci/workspace/helmfile
steps:
- checkout
@@ -91,7 +91,7 @@ jobs:
- run:
name: Install helm
environment:
HELM_VERSION: v2.15.2
HELM_VERSION: v2.16.1
command: |
HELM_FILENAME="helm-${HELM_VERSION}-linux-amd64.tar.gz"
curl -Lo ${HELM_FILENAME} "https://kubernetes-helm.storage.googleapis.com/${HELM_FILENAME}"
@@ -169,7 +169,7 @@ jobs:
release:
docker:
- image: circleci/golang:1.13.4
- image: circleci/golang:1.14.2
working_directory: /home/circleci/workspace/helmfile
steps:
- checkout
+6 -5
View File
@@ -1,4 +1,4 @@
FROM golang:1.13.4-alpine3.10 as builder
FROM golang:1.14.2-alpine3.11 as builder
RUN apk add --no-cache make git
WORKDIR /workspace/helmfile
@@ -7,19 +7,20 @@ RUN make static-linux
# -----------------------------------------------------------------------------
FROM alpine:3.10
FROM alpine:3.11
RUN apk add --no-cache ca-certificates git bash curl jq
ARG HELM_VERSION=v2.15.2
ARG HELM_VERSION=v2.16.1
ARG HELM_LOCATION="https://kubernetes-helm.storage.googleapis.com"
ARG HELM_FILENAME="helm-${HELM_VERSION}-linux-amd64.tar.gz"
ARG HELM_SHA256="a9d2db920bd4b3d824729bbe1ff3fa57ad27760487581af6e5d3156d1b3c2511"
ARG HELM_SHA256="7eebaaa2da4734242bbcdced62cc32ba8c7164a18792c8acdf16c77abffce202"
RUN wget ${HELM_LOCATION}/${HELM_FILENAME} && \
echo Verifying ${HELM_FILENAME}... && \
sha256sum ${HELM_FILENAME} | grep -q "${HELM_SHA256}" && \
echo Extracting ${HELM_FILENAME}... && \
tar zxvf ${HELM_FILENAME} && mv /linux-amd64/helm /usr/local/bin/ && \
mv /linux-amd64/tiller /usr/local/bin/ && \
rm ${HELM_FILENAME} && rm -r /linux-amd64
# using the install documentation found at https://kubernetes.io/docs/tasks/tools/install-kubectl/
@@ -27,7 +28,7 @@ RUN wget ${HELM_LOCATION}/${HELM_FILENAME} && \
# we should be able to install using apk add.
ENV KUBECTL_VERSION="v1.14.5"
ENV KUBECTL_SHA256="26681319de56820a8467c9407e9203d5b15fb010ffc75ac5b99c9945ad0bd28c"
RUN curl -LO "https://storage.googleapis.com/kubernetes-release/release/${KUBECTL_VERSION}/bin/linux/amd64/kubectl" && \
RUN curl --retry 3 -LO "https://storage.googleapis.com/kubernetes-release/release/${KUBECTL_VERSION}/bin/linux/amd64/kubectl" && \
sha256sum kubectl | grep ${KUBECTL_SHA256} && \
chmod +x kubectl && \
mv kubectl /usr/local/bin/kubectl
+5 -7
View File
@@ -1,4 +1,4 @@
FROM golang:1.13.4-alpine3.10 as builder
FROM golang:1.14.2-alpine3.11 as builder
RUN apk add --no-cache make git
WORKDIR /workspace/helmfile
@@ -7,14 +7,14 @@ RUN make static-linux
# -----------------------------------------------------------------------------
FROM alpine:3.10
FROM alpine:3.11
RUN apk add --no-cache ca-certificates git bash curl jq
ARG HELM_VERSION=v3.0.0
ARG HELM_VERSION=v3.2.1
ARG HELM_LOCATION="https://get.helm.sh"
ARG HELM_FILENAME="helm-${HELM_VERSION}-linux-amd64.tar.gz"
ARG HELM_SHA256="10e1fdcca263062b1d7b2cb93a924be1ef3dd6c381263d8151dd1a20a3d8c0dc"
ARG HELM_SHA256="018f9908cb950701a5d59e757653a790c66d8eda288625dbb185354ca6f41f6b"
RUN wget ${HELM_LOCATION}/${HELM_FILENAME} && \
echo Verifying ${HELM_FILENAME}... && \
sha256sum ${HELM_FILENAME} | grep -q "${HELM_SHA256}" && \
@@ -27,7 +27,7 @@ RUN wget ${HELM_LOCATION}/${HELM_FILENAME} && \
# we should be able to install using apk add.
ENV KUBECTL_VERSION="v1.14.5"
ENV KUBECTL_SHA256="26681319de56820a8467c9407e9203d5b15fb010ffc75ac5b99c9945ad0bd28c"
RUN curl -LO "https://storage.googleapis.com/kubernetes-release/release/${KUBECTL_VERSION}/bin/linux/amd64/kubectl" && \
RUN curl --retry 3 -LO "https://storage.googleapis.com/kubernetes-release/release/${KUBECTL_VERSION}/bin/linux/amd64/kubectl" && \
sha256sum kubectl | grep ${KUBECTL_SHA256} && \
chmod +x kubectl && \
mv kubectl /usr/local/bin/kubectl
@@ -39,6 +39,4 @@ RUN helm plugin install https://github.com/databus23/helm-diff --version v3.0.0-
COPY --from=builder /workspace/helmfile/dist/helmfile_linux_amd64 /usr/local/bin/helmfile
ENV HELMFILE_HELM3="1"
CMD ["/usr/local/bin/helmfile"]
+3 -3
View File
@@ -26,15 +26,15 @@ integration:
.PHONY: integration
cross:
env CGO_ENABLED=0 gox -os '!openbsd !freebsd !netbsd' -arch '!arm !mips !mipsle !mips64 !mips64le !s390x' -output "dist/{{.Dir}}_{{.OS}}_{{.Arch}}" -ldflags '-X main.Version=${TAG}' ${TARGETS}
env CGO_ENABLED=0 gox -os '!openbsd !freebsd !netbsd' -arch '!arm !mips !mipsle !mips64 !mips64le !s390x' -output "dist/{{.Dir}}_{{.OS}}_{{.Arch}}" -ldflags '-X github.com/roboll/helmfile/pkg/app/version.Version=${TAG}' ${TARGETS}
.PHONY: cross
static-linux:
env CGO_ENABLED=0 GOOS=linux GOARCH=amd64 GOFLAGS=-mod=vendor go build -o "dist/helmfile_linux_amd64" -ldflags '-X main.Version=${TAG}' ${TARGETS}
env CGO_ENABLED=0 GOOS=linux GOARCH=amd64 GOFLAGS=-mod=vendor go build -o "dist/helmfile_linux_amd64" -ldflags '-X github.com/roboll/helmfile/pkg/app/version.Version=${TAG}' ${TARGETS}
.PHONY: static-linux
install:
env CGO_ENABLED=0 go install -ldflags '-X main.Version=${TAG}' ${TARGETS}
env CGO_ENABLED=0 go install -ldflags '-X github.com/roboll/helmfile/pkg/app/version.Version=${TAG}' ${TARGETS}
.PHONY: install
clean:
+64 -31
View File
@@ -37,7 +37,7 @@ To avoid upgrades for each iteration of `helm`, the `helmfile` executable delega
**CAUTION**: This documentation is for the development version of Helmfile. If you are looking for the documentation for any of releases, please switch to the corresponding release tag like [v0.92.1](https://github.com/roboll/helmfile/tree/v0.92.1).
The default helmfile is `helmfile.yaml`:
The default name for a helmfile is `helmfile.yaml`:
```yaml
# Chart repositories used from within this state file
@@ -69,21 +69,28 @@ repositories:
# context: kube-context # this directive is deprecated, please consider using helmDefaults.kubeContext
#default values to set for args along with dedicated keys that can be set by contributers, cli args take precedence over these
# Default values to set for args along with dedicated keys that can be set by contributors, cli args take precedence over these.
# In other words, unset values results in no flags passed to helm.
# See the helm usage (helm SUBCOMMAND -h) for more info on default values when those flags aren't provided.
helmDefaults:
tillerNamespace: tiller-namespace #dedicated default key for tiller-namespace
tillerless: false #dedicated default key for tillerless
kubeContext: kube-context #dedicated default key for kube-context (--kube-context)
# additional and global args passed to helm
cleanupOnFail: false #dedicated default key for helm flag --cleanup-on-fail
# additional and global args passed to helm (default "")
args:
- "--set k=v"
# defaults for verify, wait, force, timeout and recreatePods under releases[]
# verify the chart before upgrading (only works with packaged charts not directories) (default false)
verify: true
# wait for k8s resources via --wait. (default false)
wait: true
# time in seconds to wait for any individual Kubernetes operation (like Jobs for hooks, and waits on pod/pvc/svc/deployment readiness) (default 300)
timeout: 600
# performs pods restart for the resource if applicable (default false)
recreatePods: true
force: true
# enable TLS for request to Tiller
# forces resource update through delete/recreate if needed (default false)
force: false
# enable TLS for request to Tiller (default false)
tls: true
# path to TLS CA certificate file (default "$HELM_HOME/ca.pem")
tlsCACert: "path/to/ca.pem"
@@ -91,6 +98,11 @@ helmDefaults:
tlsCert: "path/to/cert.pem"
# path to TLS key file (default "$HELM_HOME/key.pem")
tlsKey: "path/to/key.pem"
# limit the maximum number of revisions saved per release. Use 0 for no limit. (default 10)
historyMax: 10
# when using helm 3.2+, automatically create release namespaces if they do not exist (default true)
createNamespace: true
# The desired states of Helm releases.
#
@@ -99,10 +111,12 @@ releases:
# Published chart example
- name: vault # name of this release
namespace: vault # target namespace
labels: # Arbitrary key value pairs for filtering releases
createNamespace: true # helm 3.2+ automatically create release namespace (default true)
labels: # Arbitrary key value pairs for filtering releases
foo: bar
chart: roboll/vault-secret-manager # the chart being installed to create this release, referenced by `repository/chart` syntax
version: ~1.24.1 # the semver of the chart. range constraint is supported
condition: vault.enabled # The values lookup key for filtering releases. Corresponds to the boolean value of `vault.enabled`, where `vault` is an arbitrary value
missingFileHandler: Warn # set to either "Error" or "Warn". "Error" instructs helmfile to fail when unable to find a values or secrets file. When "Warn", it prints the file and continues.
# Values files used for rendering the chart
values:
@@ -143,25 +157,23 @@ releases:
# will attempt to decrypt it using helm-secrets plugin
secrets:
- vault_secret.yaml
# verify the chart before upgrading (only works with packaged charts not directories)
# Override helmDefaults options for verify, wait, timeout, recreatePods and force.
verify: true
# wait for k8s resources via --wait. Defaults to `false`
wait: true
# time in seconds to wait for any individual Kubernetes operation (like Jobs for hooks, and waits on pod/pvc/svc/deployment readiness) (default 300)
timeout: 60
# performs pods restart for the resource if applicable
recreatePods: true
# forces resource update through delete/recreate if needed
force: true
# set `false` to uninstall on sync
force: false
# set `false` to uninstall this release on sync. (default true)
installed: true
# restores previous state in case of failed release
# restores previous state in case of failed release (default false)
atomic: true
# name of the tiller namespace
# when true, cleans up any new resources created during a failed release (default false)
cleanupOnFail: false
# name of the tiller namespace (default "")
tillerNamespace: vault
# if true, will use the helm-tiller plugin
# if true, will use the helm-tiller plugin (default false)
tillerless: false
# enable TLS for request to Tiller
# enable TLS for request to Tiller (default false)
tls: true
# path to TLS CA certificate file (default "$HELM_HOME/ca.pem")
tlsCACert: "path/to/ca.pem"
@@ -172,7 +184,10 @@ releases:
# --kube-context to be passed to helm commands
# CAUTION: this doesn't work as expected for `tilerless: true`.
# See https://github.com/roboll/helmfile/issues/642
# (default "", which means the standard kubeconfig, either ~/kubeconfig or the file pointed by $KUBECONFIG environment variable)
kubeContext: kube-context
# limit the maximum number of revisions saved per release. Use 0 for no limit (default 10)
historyMax: 10
# Local chart example
- name: grafana # name of this release
@@ -209,6 +224,9 @@ helmfiles:
# The nested-state file is locally checked-out along with the remote directory containing it.
# Therefore all the local paths in the file are resolved relative to the file
path: git::https://github.com/cloudposse/helmfiles.git@releases/kiam.yaml?ref=0.40.0
# If set to "Error", return an error when a subhelmfile points to a
# non-existent path. The default behavior is to print a warning and continue.
missingFileHandler: Error
#
# Advanced Configuration: Environments
@@ -237,7 +255,10 @@ environments:
values:
- environment/production/values.yaml
- myChartVer: 1.0.0
## `secrets.yaml` is decrypted by `helm-secrets` and available via `{{ .Environment.Secrets.KEY }}`
# disable vault release processing
- vault:
enabled: false
## `secrets.yaml` is decrypted by `helm-secrets` and available via `{{ .Environment.Values.KEY }}`
secrets:
- environment/production/secrets.yaml
# Instructs helmfile to fail when unable to find a environment values file listed under `environments.NAME.values`.
@@ -259,6 +280,18 @@ bases:
- environments.yaml
- defaults.yaml
- templates.yaml
#
# Advanced Configuration: API Capabilities
#
# 'helmfile template' renders releases locally without querying an actual cluster,
# and in this case `.Capabilities.APIVersions` cannot be populated.
# When a chart queries for a specific CRD, this can lead to unexpected results.
#
# Configure a fixed list of api versions to pass to 'helm template' via the --api-versions flag:
apiVersions:
- example/v1
```
## Templating
@@ -297,6 +330,10 @@ releases:
value: {{ env "SCHEME" | default "https" }}
```
### Note
If you wish to treat your enviroment variables as strings always, even if they are boolean or numeric values you can use `{{ env "ENV_NAME" | quote }}` or `"{{ env "ENV_NAME" }}"`. These approaches also work with `requiredEnv`.
## installation
- download one of [releases](https://github.com/roboll/helmfile/releases) or
@@ -331,7 +368,7 @@ Congratulations! You now have your first Prometheus deployment running inside yo
Iterate on the `helmfile.yaml` by referencing:
- [Configuration syntax](#configuration-syntax)
- [Configuration](#configuration)
- [CLI reference](#cli-reference).
- [Helmfile Best Practices Guide](https://github.com/roboll/helmfile/blob/master/docs/writing-helmfile.md)
@@ -501,7 +538,7 @@ In addition to built-in ones, the following custom template functions are availa
You can reference a template of values file in your `helmfile.yaml` like below:
```yaml
releases
releases:
- name: myapp
chart: mychart
values:
@@ -596,11 +633,9 @@ environments:
production:
releases:
{{ if eq .Environment.Name "production" }}
- name: newrelic-agent
installed: {{ eq .Environment.Name "production" | toYaml }}
# snip
{{ end }}
- name: myapp
# snip
```
@@ -666,12 +701,10 @@ releases:
- name: myapp-{{ .Values.releaseName }} # release name will be one of `dev` or `prod` depending on selected environment
values:
- values.yaml.gotmpl
{{ if eq (.Values.releaseName "prod" ) }}
# this release would be installed only if selected environment is `production`
- name: production-specific-release
# this release would be installed only if selected environment is `production`
installed: {{ eq .Values.releaseName "prod" | toYaml }}
...
{{ end }}
```
### Note
@@ -732,7 +765,7 @@ or in the `releases` entries.
`needs` controls the order of the installation/deletion of the release:
```yaml
relesaes:
releases:
- name: somerelease
needs:
- [TILLER_NAMESPACE/][NAMESPACE/]anotherelease
@@ -767,7 +800,7 @@ For the following example, `helmfile [sync|apply]` installs releases in this ord
Note that all the releases in a same group is installed concurrently. That is, myapp1 and myapp2 are installed concurrently.
On `helmdile [delete|destroy]`, deleations happen in the reverse order.
On `helmfile [delete|destroy]`, deletions happen in the reverse order.
That is, `myapp1` and `myapp2` are deleted first, then `servicemesh`, and finally `logging`.
@@ -957,7 +990,7 @@ like rollback, history, and so on? This section is for you!
The combination of `hooks` and [helmify-kustomize](https://gist.github.com/mumoshu/f9d0bd98e0eb77f636f79fc2fb130690)
enables you to integrate [kustomize](https://github.com/kubernetes-sigs/kustomize) into Helmfile.
That is, you can use `kustommize` to build a local helm chart from a kustomize overlay.
That is, you can use `kustomize` to build a local helm chart from a kustomize overlay.
Let's assume you have a kustomize project named `foo-kustomize` like this:
+2
View File
@@ -18,4 +18,6 @@ information to this file.
| [uniqkey](https://uniqkey.eu/) | production | [Wiki Page](https://ocd-scm.github.io/ocd-meta/) | Copenhagen, Denmark | April 2019 |
| [bitsofinfo](https://github.com/bitsofinfo/helmfile-deploy) | production | Used with [helmfile-deploy](https://github.com/bitsofinfo/helmfile-deploy) to manage releases for dozens of apps | USA | July 2019 |
| [kloeckner-i](https://www.kloeckner-i.com/) | production | We are deploying our standard tools via helmfile to all our clusters. | Berlin, Germany | September 2019 |
| [American Express](https://www.americanexpress.com) | proof-of-concept | Orchestration of both internal cluster workloads and local developer environments. | London, GB | January 2020 |
| [Sportradar](https://www.sportradar.com) | production | Since mid-2019, we've been deploying our core infrastructure and several application stacks with Helmfile. | St. Gallen, Switzerland | March 2020 |
<!-- TABLE_END -->
+137
View File
@@ -0,0 +1,137 @@
## Advanced Features
- [Import Configuration Parameters into Helmfile](#import-configuration-parameters-into-helmfile)
### Import Configuration Parameters into Helmfile
Helmfile integrates [vals]() to import configuration parameters from following backends:
- AWS SSM Parameter Store
- AWS SecretsManager
- Vault
- SOPS
See [Vals "Suported Backends"](https://github.com/variantdev/vals#suported-backends) for the full list of available backends.
This feature was implemented in https://github.com/roboll/helmfile/pull/906.
If you're curious how it's designed and how it works, please consult the pull request.
### Deploy Kustomizations with Helmfile
You can deploy [kustomize](https://github.com/kubernetes-sigs/kustomize) "kustomization"s with Helmfile.
Most of Kustomize operations that is usually done with `kustomize edit` can be done declaratively via Helm values.yaml files.
Under the hood, Helmfile transforms the kustomization into a local chart in a temporary directory so that it can be `helm upgrade --install`ed.
The transformation is done by generating (1)a temporary kustomization from various options and (2)temporary chart from the temporary kustomization.
An example pseudo code for the transformation logic can be written as:
```console
$ TMPCHART=/tmp/sometmpdir
$ mkdir -p ${TMPCHART}/templates
$ somehow_generate_chart_yaml ${TMPCHART}/Chart.yaml
$ TMPKUSTOMIZATION=/tmp/sometmpdir2
$ somehow_generate_temp_kustomization_yaml ${TMPKUSTOMIZATION}/kustomization.yaml
$ kustomize build ${TMPKUSTOMIZATION}/kustomization.yaml > ${TMPCHART}/templates/all.yaml
```
Let's say you have a `helmfile.yaml` that looks like the below:
```yaml
releases:
- name: myapp
chart: mykustomization
values:
- values.yaml
```
Helmfile firstly generates a temporary `kustomization.yaml` that looks like:
```yaml
bases:
- $(ABS_PATH_TO_HELMFILE_YAML}/mykustomization
```
Followed by the below steps:
- Running `kustomize edit set image $IMAGE` for every `$IMAGE` generated from your values.yaml
- Running `kustomize edit set nameprefix $NAMEPREFIX` with the nameprefix specified in your values.yaml
- Running `kustomize edit set namesuffix $NAMESUFFIX` with the namesuffix specified in your values.yaml
- Running `kustomize edit set namespace $NS` with the namespace specified in your values.yaml
A `values.yaml` file for kustomization would look like the below:
```yaml
images:
# kustomize edit set image mysql=eu.gcr.io/my-project/mysql@canary
- name: mysql
newName: eu.gcr.io/my-project/mysql
newTag: canary
# kustomize edit set image myapp=my-registry/my-app@sha256:24a0c4b4a4c0eb97a1aabb8e29f18e917d05abfe1b7a7c07857230879ce7d3d3
- name: myapp
digest: sha256:24a0c4b4a4c0eb97a1aabb8e29f18e917d05abfe1b7a7c07857230879ce7d3d3
newName: my-registry/my-app
# kustomize edit set nameprefix foo-
namePrefix: foo-
# kustomize edit set namesuffix -bar
nameSuffix: -bar
# kustomize edit set namespace myapp
namespace: myapp
```
At this point, Helmfile can generate a complete kustomization from the base kustomization you specified in `releases[].chart` of your helmfile.yaml and `values.yaml`,
which can be included in the temporary chart.
After all, Helmfile just installs the temporary chart like standard charts, which allows you to manage everything with Helmfile regardless of each app is declared using a Helm chart or a kustomization.
Please also see [test/advanced/helmfile.yaml](https://github.com/roboll/helmfile/tree/master/test/advanced/helmfile.yaml) for an example of kustomization support and more.
## Adhoc Customization of Helm charts
You can add/update any Kubernetes resource field rendered from a Helm chart by specifying `releases[].strategicMergePatches`:
```
repositories:
- name: incubator
url: https://kubernetes-charts-incubator.storage.googleapis.com
releases:
- name: raw1
chart: incubator/raw
values:
- resources:
- apiVersion: v1
kind: ConfigMap
metadata:
name: raw1
namespace: default
data:
foo: FOO
strategicMergePatches:
- apiVersion: v1
kind: ConfigMap
metadata:
name: raw1
namespace: default
data:
bar: BAR
```
Running `helmfile template` on the above example results in a ConfigMap called `raw` whose `data` is:
```yaml
foo: FOO
bar: BAR
```
Please note that the second `data` field `bar` is coming from the strategic-merge patch defined in the above helmfile.yaml.
There's also `releases[].jsonPatches` that works similarly to `strategicMergePatches` but has additional capability to remove fields.
Please also see [test/advanced/helmfile.yaml](https://github.com/roboll/helmfile/tree/master/test/advanced/helmfile.yaml) for an example of patching support and more.
+58
View File
@@ -0,0 +1,58 @@
# Secrets
helmfile can handle secrets using [helm-secrets](https://github.com/zendesk/helm-secrets) plugin or using remote secrets storage
(everything that package [vals](https://github.com/variantdev/vals) can handle vault, AWS SSM etc)
This section will describe the second use case.
# Remote secrets
This paragraph will describe how to use remote secrets storage (vault, SSM etc) in helmfile
## Fetching single key
To fetch single key from remote secret storage you can use `fetchSecretValue` template function example below
```yaml
# helmfile.yaml
repositories:
- name: stable
url: https://kubernetes-charts.storage.googleapis.com
environments:
default:
values:
- service:
password: ref+vault://svc/#pass
login: ref+vault://svc/#login
releases:
- name: service
namespace: default
labels:
cluster: services
secrets: vault
chart: stable/svc
version: 0.1.0
values:
- service:
login: {{ .Values.service.login | fetchSecretValue }} # this will resolve ref+vault://svc/#pass and fetch secret from vault
password: {{ .Values.service.password | fetchSecretValue | quote }}
# - values/service.yaml.gotmpl # alternatively
```
## Fetching multiple keys
Alternatively you can use `expandSecretRefs` to fetch a map of secrets
```yaml
# values/service.yaml.gotmpl
service:
{{ .Values.service | expandSecretRefs | toYaml | nindent 2 }}
```
This will produce
```yaml
# values/service.yaml
service:
login: svc-login # fetched from vault
password: pass
```
+7 -7
View File
@@ -32,7 +32,7 @@ Now, you get `1` when there is no `eventApi.replicas` defined in environment val
## Release Template / Conventional Directory Structure
Introducing helmfile into a large-scale project that involes dozens of releases often results in a lot of repetitions in `helmfile.yaml` files.
Introducing helmfile into a large-scale project that involves dozens of releases often results in a lot of repetitions in `helmfile.yaml` files.
The example below shows repetitions in `namespace`, `chart`, `values`, and `secrets`:
@@ -146,7 +146,7 @@ bases:
- environments.yaml
releases:
- name: metricbaet
- name: metricbeat
chart: stable/metricbeat
- name: myapp
chart: mychart
@@ -173,7 +173,7 @@ environments:
releases:
- name: myapp
chart: mychart
- name: metricbaet
- name: metricbeat
chart: stable/metricbeat
```
@@ -186,7 +186,7 @@ environments:
production:
releases:
- name: metricbaet
- name: metricbeat
chart: stable/metricbeat
- name: myapp
chart: mychart
@@ -204,7 +204,7 @@ Helmfile doesn't merge arrays across layers. That is, the below example doesn't
```yaml
releases:
- name: metricbaet
- name: metricbeat
chart: stable/metricbeat
---
releases:
@@ -216,7 +216,7 @@ Helmfile overrides the `releases` array with the latest layer so the resulting s
```yaml
releases:
# metricbeat release disappeared! but that's how helmfiel works
# metricbeat release disappeared! but that's how helmfile works
- name: myapp
chart: mychart
```
@@ -228,7 +228,7 @@ A work-around is to treat the state file as a go template and use `readFile` tem
```yaml
templates:
metricbeat: &metricbeat
- name: metricbeat
name: metricbeat
chart: stable/metricbeat
```
+9 -18
View File
@@ -1,47 +1,38 @@
module github.com/roboll/helmfile
go 1.13
go 1.14
require (
cloud.google.com/go v0.47.0 // indirect
github.com/Azure/azure-sdk-for-go v35.0.0+incompatible // indirect
github.com/Masterminds/goutils v1.1.0 // indirect
github.com/Masterminds/semver v1.4.2
github.com/Masterminds/sprig v2.22.0+incompatible
github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a
github.com/aws/aws-sdk-go v1.25.22 // indirect
github.com/go-test/deep v1.0.3
github.com/golang/groupcache v0.0.0-20191027212112-611e8accdfc9 // indirect
github.com/google/go-cmp v0.3.1
github.com/golang/mock v1.4.3
github.com/google/go-cmp v0.4.0
github.com/gosuri/uitable v0.0.3
github.com/hashicorp/go-getter v1.3.0
github.com/hashicorp/go-retryablehttp v0.6.3 // indirect
github.com/hashicorp/go-version v1.2.0
github.com/howeyc/gopass v0.0.0-20190910152052-7cb4b85ec19c // indirect
github.com/huandu/xstrings v1.2.0 // indirect
github.com/imdario/mergo v0.3.6
github.com/imdario/mergo v0.3.9
github.com/konsorten/go-windows-terminal-sequences v1.0.2 // indirect
github.com/mattn/go-colorable v0.1.4 // indirect
github.com/mattn/go-isatty v0.0.10 // indirect
github.com/pierrec/lz4 v2.3.0+incompatible // indirect
github.com/r3labs/diff v0.0.0-20190801153147-a71de73c46ad
github.com/tatsushid/go-prettytable v0.0.0-20141013043238-ed2d14c29939
github.com/urfave/cli v1.20.0
github.com/variantdev/chartify v0.3.3
github.com/variantdev/dag v0.0.0-20191028002400-bb0b3c785363
github.com/variantdev/vals v0.0.0-20191117001227-ba4c7a29875a
github.com/variantdev/vals v0.4.1-0.20200501114609-9cebe482281c
go.mozilla.org/sops v0.0.0-20190912205235-14a22d7a7060 // indirect
go.opencensus.io v0.22.1 // indirect
go.uber.org/multierr v1.1.0
go.uber.org/zap v1.9.1
golang.org/x/crypto v0.0.0-20191029031824-8986dd9e96cf // indirect
golang.org/x/net v0.0.0-20191028085509-fe3aa8a45271 // indirect
golang.org/x/sys v0.0.0-20191029155521-f43be2a4598c // indirect
golang.org/x/time v0.0.0-20191024005414-555d28b269f0 // indirect
google.golang.org/api v0.13.0 // indirect
google.golang.org/appengine v1.6.5 // indirect
google.golang.org/genproto v0.0.0-20191028173616-919d9bdd9fe6 // indirect
google.golang.org/grpc v1.24.0 // indirect
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a
gopkg.in/square/go-jose.v2 v2.4.0 // indirect
gopkg.in/yaml.v2 v2.2.4
gopkg.in/yaml.v3 v3.0.0-20191026110619-0b21df46bc1d // indirect
gotest.tools v2.2.0+incompatible
gotest.tools/v3 v3.0.3-0.20200410202438-4e4a41b7851a
)
+224 -105
View File
@@ -15,14 +15,30 @@ cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTj
cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
cloud.google.com/go v0.47.0 h1:1JUtpcY9E7+eTospEwWS2QXP3DEn7poB3E2j0jN74mM=
cloud.google.com/go v0.47.0/go.mod h1:5p3Ky/7f3N10VBkhuR5LFtddroTiMyjZV/Kj5qOQFxU=
cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To=
cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4=
cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M=
cloud.google.com/go v0.56.0 h1:WRz29PgAsVEyPSDHyk+0fpEkwEFyfhHn+JbksT6gIL4=
cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk=
cloud.google.com/go/bigquery v1.0.1 h1:hL+ycaJpVE9M7nLoiXb/Pn10ENE2u+oddxbD8uu0ZVU=
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
cloud.google.com/go/bigquery v1.4.0 h1:xE3CPsOgttP4ACBePh79zTKALtXwn/Edhcr16R5hMWU=
cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
cloud.google.com/go/datastore v1.0.0 h1:Kt+gOPPp2LEPWp8CSfxhsM8ik9CcyE/gYu+0r+RnZvM=
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
cloud.google.com/go/datastore v1.1.0 h1:/May9ojXjRkPBNVrq+oWLqmWCkr4OU5uRY29bu0mRyQ=
cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
cloud.google.com/go/pubsub v1.0.1 h1:W9tAK3E57P75u0XLLR82LZyw8VpAnhmyTOxW9qzmyj8=
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
cloud.google.com/go/pubsub v1.2.0 h1:Lpy6hKgdcl7a3WGSfJIFmxmcdjSpP6OmBEfcOv1Y680=
cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=
cloud.google.com/go/storage v1.0.0 h1:VV2nUM3wwLLGh9lSABFgZMjInyUbJeaRSE64WuAIQ+4=
cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos=
cloud.google.com/go/storage v1.6.0 h1:UDpwYIwla4jHGzZJaEJYx1tOejbgSoNqsAfHAUYe2r8=
cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
code.cloudfoundry.org/gofileutils v0.0.0-20170111115228-4d0c80011a0f h1:UrKzEwTgeiff9vxdrfdqxibzpWjxLnuXDI5m6z3GJAk=
code.cloudfoundry.org/gofileutils v0.0.0-20170111115228-4d0c80011a0f/go.mod h1:sk5LnIjB/nIEU7yP5sDQExVm62wu0pBh3yrElngUisI=
contrib.go.opencensus.io/exporter/ocagent v0.4.12/go.mod h1:450APlNTSR6FrvC3CTRqYosuDstRB9un7SOx2k/9ckA=
@@ -111,8 +127,15 @@ github.com/SAP/go-hdb v0.14.1/go.mod h1:7fdQLVC2lER3urZLjZCm0AuMQfApof92n3aylBPE
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/abdullin/seq v0.0.0-20160510034733-d5467c17e7af/go.mod h1:5Jv4cbFiHJMsVxt52+i0Ha45fjshj6wxYr1r19tB9bw=
github.com/alecthomas/go-thrift v0.0.0-20170109061633-7914173639b2/go.mod h1:CxCgO+NdpMdi9SsTlGbc0W+/UNxO3I0AabOEJZ3w61w=
github.com/alecthomas/kong v0.2.1/go.mod h1:+inYUSluD+p4L8KdviBSgzcqEjUQOfC5fQDRFuc36lI=
github.com/alecthomas/participle v0.4.2-0.20191220090139-9fbceec1d131 h1:iPgE4wTIM/fgSreWdpxnKXxaGOgGwfPqc2aVPq2BFSU=
github.com/alecthomas/participle v0.4.2-0.20191220090139-9fbceec1d131/go.mod h1:T8u4bQOSMwrkTWOSyt8/jSFPEnRtd0FKFMjVfYBlqPs=
github.com/alecthomas/repr v0.0.0-20181024024818-d37bc2a10ba1/go.mod h1:xTS7Pm1pD1mvyM075QCDSRqH6qRLXylzS24ZTpRiSzQ=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho=
github.com/aliyun/alibaba-cloud-sdk-go v0.0.0-20190412020505-60e2075261b6/go.mod h1:T9M45xf79ahXVelWoOBmH0y4aC1t5kXO5BxwyakgIGA=
github.com/aliyun/alibaba-cloud-sdk-go v0.0.0-20190620160927-9418d7b0cd0f h1:oRD16bhpKNAanfcDDVU+J0NXqsgHIvGbbe/sy+r6Rs0=
github.com/aliyun/alibaba-cloud-sdk-go v0.0.0-20190620160927-9418d7b0cd0f/go.mod h1:myCDvQSzCW+wB1WAlocEru4wMGJxy+vlxHdhegi1CDQ=
@@ -142,6 +165,8 @@ github.com/aws/aws-sdk-go v1.25.19 h1:sp3xP91qIAVhWufyn9qM6Zhhn6kX06WJQcmhRj7QTX
github.com/aws/aws-sdk-go v1.25.19/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
github.com/aws/aws-sdk-go v1.25.22 h1:DXcA0jjMnGt2awoWM2qwCu+ouGDB5FYnGxCVrRweE/0=
github.com/aws/aws-sdk-go v1.25.22/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
github.com/aws/aws-sdk-go v1.29.34 h1:yrzwfDaZFe9oT4AmQeNNunSQA7c0m2chz0B43+bJ1ok=
github.com/aws/aws-sdk-go v1.29.34/go.mod h1:1KvfttTE3SPKMpo8g2c6jL3ZKfXtFvKscTgahTma5Xg=
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 h1:xJ4a3vCFaGF/jqvzLMYoU8P317H5OQ+Via4RmuPwCS0=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
@@ -162,16 +187,21 @@ github.com/briankassouf/jose v0.9.2-0.20180619214549-d2569464773f/go.mod h1:HQhV
github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4=
github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
github.com/census-instrumentation/opencensus-proto v0.2.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/centrify/cloud-golang-sdk v0.0.0-20190214225812-119110094d0f h1:gJzxrodnNd/CtPXjO3WYiakyNzHg3rtAi7rO74ejHYU=
github.com/centrify/cloud-golang-sdk v0.0.0-20190214225812-119110094d0f/go.mod h1:C0rtzmGXgN78pYR0tGJFhtHgkbAs0lIbHwkB81VxDQE=
github.com/cheggaaa/pb v1.0.27/go.mod h1:pQciLPpbU0oxA0h+VJYYLxO+XeDQb5pZijXscXHm81s=
github.com/chrismalek/oktasdk-go v0.0.0-20181212195951-3430665dfaa0 h1:CWU8piLyqoi9qXEUwzOh5KFKGgmSU5ZhktJyYcq6ryQ=
github.com/chrismalek/oktasdk-go v0.0.0-20181212195951-3430665dfaa0/go.mod h1:5d8DqS60xkj9k3aXfL3+mXBH0DPYO0FQjcKosxl+b/Q=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
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/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag=
github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cloudfoundry-community/go-cfclient v0.0.0-20190201205600-f136f9222381 h1:rdRS5BT13Iae9ssvcslol66gfOOXjaLYwqerEn/cl9s=
github.com/cloudfoundry-community/go-cfclient v0.0.0-20190201205600-f136f9222381/go.mod h1:e5+USP2j8Le2M0Jo3qKPFnNhuo1wueU4nWHCXBOfQ14=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ=
github.com/cockroachdb/cockroach-go v0.0.0-20181001143604-e0a95dfd547c/go.mod h1:XGLbWH/ujMcbPbhZq52Nv6UrCghb1yGn//133kEsvDk=
github.com/codegangsta/inject v0.0.0-20150114235600-33e0aa1cb7c0 h1:sDMmm+q/3+BukdIpxwO365v/Rbspp2Nt5XntgQRXq8Q=
@@ -210,9 +240,17 @@ github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFP
github.com/elazarl/go-bindata-assetfs v1.0.0 h1:G/bYguwHIzWq9ZoyUQqrjTmJbbYn3j3CKKpKinvZLFk=
github.com/elazarl/go-bindata-assetfs v1.0.0/go.mod h1:v+YaWX3bdea5J/mo8dSETolEo7R71Vk1u8bnjau5yw4=
github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc=
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/evanphx/json-patch v0.0.0-20190203023257-5858425f7550/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
github.com/fastly/go-utils v0.0.0-20180712184237-d95a45783239 h1:Ghm4eQYC0nEPnSJdVkTrXpu9KtoVCSo1hg7mtI7G9KU=
github.com/fastly/go-utils v0.0.0-20180712184237-d95a45783239/go.mod h1:Gdwt2ce0yfBxPvZrHkprdPPTTS3N5rwmLE8T22KBXlw=
github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/fatih/color v1.9.0 h1:8xPHl4/q1VyqGIPif1F+1V3Y3lSmrq01EabUW3CoW5s=
github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU=
github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo=
github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
@@ -220,6 +258,8 @@ github.com/frankban/quicktest v1.4.1 h1:Wv2VwvNn73pAdFIVUQRXYDFp31lXKbqblIXo/Q5G
github.com/frankban/quicktest v1.4.1/go.mod h1:36zfPVQyHxymz4cH7wlDmVwDrJuljRB60qkgn7rorfQ=
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/fujiwara/tfstate-lookup v0.0.4 h1:NGH9y+U4RDO3ooMYjK+fq1iYAkrBfCNZbZwX4HqQnBo=
github.com/fujiwara/tfstate-lookup v0.0.4/go.mod h1:kluvnw25uJ8bszZldMoewWxylt7QFvuzTGN+Rr911qs=
github.com/fullsailor/pkcs7 v0.0.0-20190404230743-d7302db945fa h1:RDBNVkRviHZtvDvId8XSGPu3rmpmSe+wKRcEWNgsfWU=
github.com/fullsailor/pkcs7 v0.0.0-20190404230743-d7302db945fa/go.mod h1:KnogPXtdwXqoenmZCw6S+25EAm2MkxbG0deNDu4cbSA=
github.com/gammazero/deque v0.0.0-20190130191400-2afb3858e9c7 h1:D2LrfOPgGHQprIxmsTpxtzhpmF66HoM6rXSmcqaX7h8=
@@ -232,14 +272,19 @@ github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aev
github.com/go-errors/errors v1.0.1 h1:LUHzmkK3GUKUrL/1gfBUxAHzcev3apQlezX/+O7ma6w=
github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q=
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-ldap/ldap v3.0.2+incompatible h1:kD5HQcAzlQ7yrhfn+h+MSABeAy/jAJhvIJ/QDllP44g=
github.com/go-ldap/ldap v3.0.2+incompatible/go.mod h1:qfd9rJvER9Q0/D/Sqn1DfHRoBp40uXYvFoEVrNEPqRc=
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas=
github.com/go-martini/martini v0.0.0-20170121215854-22fa46961aab h1:xveKWz2iaueeTaUgdetzel+U7exyigDYBryyVfV/rZk=
github.com/go-martini/martini v0.0.0-20170121215854-22fa46961aab/go.mod h1:/P9AEU963A2AYjv4d1V5eVL1CQbEJq6aCNHDDjibzu8=
github.com/go-sql-driver/mysql v1.4.1 h1:g24URVg0OFbNUTx9qqY1IRZ9D9z3iPyi5zKhQZpNwpA=
github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs=
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/go-test/deep v1.0.1/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA=
github.com/go-test/deep v1.0.2-0.20181118220953-042da051cf31/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA=
@@ -263,16 +308,22 @@ github.com/golang/groupcache v0.0.0-20191025150517-4a4ac3fbac33 h1:0VG7K4qDn8U/d
github.com/golang/groupcache v0.0.0-20191025150517-4a4ac3fbac33/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20191027212112-611e8accdfc9 h1:uHTyIjqVhYRhLbJ8nIiOJHkEZZ+5YoOsAbD3sk82NiE=
github.com/golang/groupcache v0.0.0-20191027212112-611e8accdfc9/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e h1:1r7pUrabqp18hOBcwBwiTsbnFeTZHV9eER/QT5JVZxY=
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM=
github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
github.com/golang/mock v1.4.3 h1:GV+pQPG/EUUbkh47niozDcADz6go/dUwhVzdUQHIVRw=
github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
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/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/golang/protobuf v1.3.5 h1:F768QJ1E9tib+q5Sc8MkdJi1RxLTbRcTf8LJV56aRls=
github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=
github.com/golang/snappy v0.0.0-20170215233205-553a64147049/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4=
@@ -282,10 +333,10 @@ github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Z
github.com/google/btree v1.0.0 h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1 h1:Xye71clBPdm5HgqGwUkwhbynsUJZhDbS20FvLhQ2izg=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-github v17.0.0+incompatible h1:N0LgJ1j65A7kfXrZnUDaYCs/Sf4rEjNlfyDHW9dolSY=
github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ=
github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk=
@@ -296,15 +347,15 @@ github.com/google/martian v2.1.0+incompatible h1:/CP5g8u/VJHijgedC/Legn3BAbAaWPg
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/shlex v0.0.0-20181106134648-c34317bd91bf/go.mod h1:RpwtwJQFrIEPstU94h88MWPXP2ektJZ8cZ0YntAmXiE=
github.com/google/uuid v0.0.0-20161128191214-064e2069ce9c h1:jWtZjFEUE/Bz0IeIhqCnyZ3HG6KRXSntXe4SjtuTH7c=
github.com/google/uuid v0.0.0-20161128191214-064e2069ce9c/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
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/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 h1:sjZBwGj9Jlw33ImPtvFviGYvseOtDM7hkSKB7+Tv3SM=
@@ -340,7 +391,6 @@ github.com/hashicorp/consul/sdk v0.1.1 h1:LnuDWGNsoajlhGyHJvuWW6FVqRl8JOTPqS6CPT
github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/go-cleanhttp v0.5.0 h1:wvCrVc9TjDls6+YGAF2hAifE1E5U1+b4tH6KdvN3Gig=
github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
github.com/hashicorp/go-cleanhttp v0.5.1 h1:dH3aiDG9Jvb5r5+bYHsikaOUIpcM0xvgMXVoDkXMzJM=
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
@@ -369,10 +419,7 @@ github.com/hashicorp/go-plugin v1.0.1/go.mod h1:++UyYGoz3o5w9ZzAdZxtQKrWWP+iqPBn
github.com/hashicorp/go-raftchunking v0.6.2 h1:imj6CVkwXj6VzgXZQvzS+fSrkbFCzlJ2t00F3PacnuU=
github.com/hashicorp/go-raftchunking v0.6.2/go.mod h1:cGlg3JtDy7qy6c/3Bu660Mic1JF+7lWqIwCFSb08fX0=
github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs=
github.com/hashicorp/go-retryablehttp v0.5.4 h1:1BZvpawXoJCWX6pNtow9+rpEj+3itIlutiqnntI6jOE=
github.com/hashicorp/go-retryablehttp v0.5.4/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs=
github.com/hashicorp/go-retryablehttp v0.6.2 h1:bHM2aVXwBtBJWxHtkSrWuI4umABCUczs52eiUS9nSiw=
github.com/hashicorp/go-retryablehttp v0.6.2/go.mod h1:gEx6HMUGxYYhJScX7W1Il64m6cc2C1mDaW3NQ9sY1FY=
github.com/hashicorp/go-retryablehttp v0.6.3 h1:tuulM+WnToeqa05z83YLmKabZxrySOmJAd4mJ+s2Nfg=
github.com/hashicorp/go-retryablehttp v0.6.3/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY=
github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=
@@ -387,13 +434,11 @@ github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdv
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/go-version v1.2.0 h1:3vNe/fWF5CBgRIguda1meWhsZHy3m8gCJ5wx+dIzX/E=
github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+dAcgU=
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.3 h1:YPkqC67at8FYaadspW/6uE0COsBxS2656RLEr8Bppgk=
github.com/hashicorp/golang-lru v0.5.3/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
@@ -447,13 +492,11 @@ github.com/hashicorp/vault-plugin-secrets-gcpkms v0.5.2-0.20190814210149-315cdbf
github.com/hashicorp/vault-plugin-secrets-kv v0.5.2-0.20190814210155-e060c2a001a8 h1:8nZOMqGQQiuWNld162nxUvM4/7EW4NOO9gpyp7LCC84=
github.com/hashicorp/vault-plugin-secrets-kv v0.5.2-0.20190814210155-e060c2a001a8/go.mod h1:5ksi71TrWxz7ZRo0MIlsry2lYDlZQyLalN4cF8a4vnk=
github.com/hashicorp/vault/api v1.0.1/go.mod h1:AV/+M5VPDpB90arloVX0rVDUIHkONiwz5Uza9HRtpUE=
github.com/hashicorp/vault/api v1.0.4 h1:j08Or/wryXT4AcHj1oCbMd7IijXcKzYUGw59LGu9onU=
github.com/hashicorp/vault/api v1.0.4/go.mod h1:gDcqh3WGcR1cpF5AJz/B1UFheUEneMoIospckxBxk6Q=
github.com/hashicorp/vault/api v1.0.5-0.20190814205728-e9c5cd8aca98/go.mod h1:t4IAg1Is4bLUtTq8cGgeUh0I8oDRBXPk2bM1Jvg/nWA=
github.com/hashicorp/vault/api v1.0.5-0.20190909201928-35325e2c3262 h1:En9ph/axtiRQX8mt4+fVBOgNfEmndq5c+K0fUBviFTQ=
github.com/hashicorp/vault/api v1.0.5-0.20190909201928-35325e2c3262/go.mod h1:LGTA4eiQKhPGTBgi6fCuAT5n0S3CJBHa7cpUotrLxjw=
github.com/hashicorp/vault/sdk v0.1.8/go.mod h1:tHZfc6St71twLizWNHvnnbiGFo1aq0eD2jGPLtP8kAU=
github.com/hashicorp/vault/sdk v0.1.13 h1:mOEPeOhT7jl0J4AMl1E705+BcmeRs1VmKNb9F0sMLy8=
github.com/hashicorp/vault/sdk v0.1.13/go.mod h1:B+hVj7TpuQY1Y/GPbCpffmgd+tSEwvhkWnjtSYCaS2M=
github.com/hashicorp/vault/sdk v0.1.14-0.20190814205504-1cad00d1133b/go.mod h1:B+hVj7TpuQY1Y/GPbCpffmgd+tSEwvhkWnjtSYCaS2M=
github.com/hashicorp/vault/sdk v0.1.14-0.20190909201848-e0fbf9b652e2 h1:b65cSyZqljnCPzzsUXvR4P0eXypo1xahQyG809+IySk=
@@ -461,7 +504,7 @@ github.com/hashicorp/vault/sdk v0.1.14-0.20190909201848-e0fbf9b652e2/go.mod h1:B
github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM=
github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d h1:kJCB4vdITiW1eC1vq2e6IsrXKrZit1bv/TDYFGMp4BQ=
github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM=
github.com/howeyc/gopass v0.0.0-20170109162249-bf9dde6d0d2c h1:kQWxfPIHVLbgLzphqk3QUflDy9QdksZR4ygR807bpy0=
github.com/hokaccha/go-prettyjson v0.0.0-20190818114111-108c894c2c0e/go.mod h1:pFlLw2CfqZiIBOx6BuCeRLCrfxBJipTY0nIOF/VbGcI=
github.com/howeyc/gopass v0.0.0-20170109162249-bf9dde6d0d2c/go.mod h1:lADxMC39cJJqL93Duh1xhAs4I2Zs8mKS89XWXFGp9cs=
github.com/howeyc/gopass v0.0.0-20190910152052-7cb4b85ec19c h1:aY2hhxLhjEAbfXOx2nRJxCXezC6CO2V/yN+OCr1srtk=
github.com/howeyc/gopass v0.0.0-20190910152052-7cb4b85ec19c/go.mod h1:lADxMC39cJJqL93Duh1xhAs4I2Zs8mKS89XWXFGp9cs=
@@ -469,11 +512,16 @@ github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/huandu/xstrings v1.2.0 h1:yPeWdRnmynF7p+lLYz0H2tthW9lqhMJrQV/U7yy4wX0=
github.com/huandu/xstrings v1.2.0/go.mod h1:DvyZB1rfVYsBIigL8HwpZgxHwXozlTgGqn63UyNX5k4=
github.com/imdario/mergo v0.3.6 h1:xTNEAn+kxVO7dTZGu0CegyqKZmoWFI0rF8UxjlB2d28=
github.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/imdario/mergo v0.3.9 h1:UauaLniWCFHWd+Jp9oCEkTBj8VO/9DKg3PV3VCNMDIg=
github.com/imdario/mergo v0.3.9/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/influxdata/influxdb v0.0.0-20190411212539-d24b7ba8c4c4 h1:3K3KcD4S6/Y2hevi70EzUTNKOS3cryQyhUnkjE6Tz0w=
github.com/influxdata/influxdb v0.0.0-20190411212539-d24b7ba8c4c4/go.mod h1:qZna6X/4elxqT3yI9iZYdZrWWdeFOOprn86kgg4+IzY=
github.com/itchyny/astgen-go v0.0.0-20200116103543-aaa595cf980e/go.mod h1:9Gyr9nZoENI+woes+xm+BFhmvYmAp6bPtXD866pQH9g=
github.com/itchyny/go-flags v1.5.0/go.mod h1:lenkYuCobuxLBAd/HGFE4LRoW8D3B6iXRQfWYJ+MNbA=
github.com/itchyny/gojq v0.9.0 h1:i8KSE5ehGU3PSnQ716raWVJIuIRpTdtJ2U8X+RY3+7s=
github.com/itchyny/gojq v0.9.0/go.mod h1:gzGMMdm17KzrO9WNNtxP7F+U52KlLeoQeFCbLW9vgrg=
github.com/jackc/fake v0.0.0-20150926172116-812a484cc733/go.mod h1:WrMFNQdiFJ80sQsxDoMokWK1W5TQtxBFNpzWTD84ibQ=
github.com/jackc/pgx v3.3.0+incompatible/go.mod h1:0ZGrqGqkRlliWnWB4zKnWtjbSWbGkVEFm4TeybAXq+I=
github.com/jeffchao/backoff v0.0.0-20140404060208-9d7fd7aa17f2 h1:mex1izRBCD+7WjieGgRdy7e651vD/lvB1bD9vNE/3K4=
@@ -481,8 +529,9 @@ github.com/jeffchao/backoff v0.0.0-20140404060208-9d7fd7aa17f2/go.mod h1:xkfESuH
github.com/jefferai/isbadcipher v0.0.0-20190226160619-51d2077c035f/go.mod h1:3J2qVK16Lq8V+wfiL2lPeDZ7UWMxk5LemerHa1p6N00=
github.com/jefferai/jsonx v1.0.0 h1:Xoz0ZbmkpBvED5W9W1B5B/zc3Oiq7oXqiW7iRV3B6EI=
github.com/jefferai/jsonx v1.0.0/go.mod h1:OGmqmi2tTeI/PS+qQfBDToLHHJIy/RMp24fPo8vFvoQ=
github.com/jehiah/go-strftime v0.0.0-20171201141054-1d33003b3869 h1:IPJ3dvxmJ4uczJe5YQdrYB16oTJlGSC/OyZDqUk9xX4=
github.com/jehiah/go-strftime v0.0.0-20171201141054-1d33003b3869/go.mod h1:cJ6Cj7dQo+O6GJNiMx+Pa94qKj+TG8ONdKHgMNIyyag=
github.com/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1/go.mod h1:E0B/fFc00Y+Rasa88328GlI/XbtyysCtTHZS8h7IrBU=
github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8 h1:12VvqtR6Aowv3l/EQUlocDHW2Cp4G9WJVH7uyH8QFJE=
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=
@@ -492,8 +541,9 @@ github.com/json-iterator/go v0.0.0-20180701071628-ab8a2e0c74be/go.mod h1:+SdeFBv
github.com/json-iterator/go v1.1.5/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/json-iterator/go v1.1.6 h1:MrUvLMLTMxbqFJ9kzlvat/rYZqZnW3u4wkLzWTaFwKs=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024 h1:rBMNdlhTLzJjJSDIjNEXX1Pz3Hmwmz91v+zycvx9PJc=
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
github.com/jstemmer/go-junit-report v0.9.1 h1:6QPYqodiu3GuPL+7mfx+NwDdp2eTkp9IfEUpgAwUN0o=
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
github.com/jtolds/gls v4.2.1+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
@@ -502,7 +552,6 @@ github.com/keybase/go-crypto v0.0.0-20190403132359-d65b6b94177f h1:Gsc9mVHLRqBjM
github.com/keybase/go-crypto v0.0.0-20190403132359-d65b6b94177f/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/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.2 h1:DB17ag19krx9CFsz4o3enTrPXyIXCl+2iCXH/aMAp9s=
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
@@ -514,23 +563,27 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/lestrrat-go/envload v0.0.0-20180220234015-a3eb8ddeffcc h1:RKf14vYWi2ttpEmkA4aQ3j4u9dStX2t4M8UM6qqNsG8=
github.com/lestrrat-go/envload v0.0.0-20180220234015-a3eb8ddeffcc/go.mod h1:kopuH9ugFRkIXf3YoqHKyrJ9YfUFsckUU9S7B+XP+is=
github.com/lestrrat-go/strftime v1.0.1 h1:o7qz5pmLzPDLyGW4lG6JvTKPUfTFXwe+vOamIYWtnVU=
github.com/lestrrat-go/strftime v1.0.1/go.mod h1:E1nN3pCbtMSu1yjSVeyuRFVm/U0xoR76fd03sz+Qz4g=
github.com/lib/pq v1.2.0 h1:LXpIM/LZ5xGFhOpXAQUIMM1HdyqzVYM13zNdjCEEcA0=
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/martini-contrib/render v0.0.0-20150707142108-ec18f8345a11 h1:YFh+sjyJTMQSYjKwM4dFKhJPJC/wfo98tPUc17HdoYw=
github.com/martini-contrib/render v0.0.0-20150707142108-ec18f8345a11/go.mod h1:Ah2dBMoxZEqk118as2T4u4fjfXarE0pPnMJaArZQZsI=
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.4 h1:snbPLB8fVfU9iwbbo30TPtbLRzwWu6aJS6Xh4eaaviA=
github.com/mattn/go-colorable v0.1.4/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.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.10 h1:qxFzApOv4WsAL965uUPIsXzAKCZxN2p9UqdhFS4ZW10=
github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84=
github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE=
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
github.com/mattn/go-runewidth v0.0.4 h1:2BvfKmzob6Bmd4YsL0zygOqfdFnK7GR4QL06Do4/p7Y=
github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
github.com/mattn/go-runewidth v0.0.8 h1:3tS41NlGYSmhhe/8fhGRzc+z3AYCw1Fe1WAyLuujKs0=
github.com/mattn/go-runewidth v0.0.8/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/matttproud/golang_protobuf_extensions v1.0.0/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
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=
@@ -542,7 +595,6 @@ github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3N
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
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 h1:vKb8ShqSby24Yrqr/yDYkuFz8d0WUjys40rvnGC8aR0=
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=
@@ -595,6 +647,12 @@ github.com/oracle/oci-go-sdk v7.0.0+incompatible h1:oj5ESjXwwkFRdhZSnPlShvLWYdt/
github.com/oracle/oci-go-sdk v7.0.0+incompatible/go.mod h1:VQb79nF8Z2cwLkLS35ukwStZIg5F66tcBccjip/j888=
github.com/ory/dockertest v3.3.4+incompatible h1:VrpM6Gqg7CrPm3bL4Wm1skO+zFWLbh7/Xb5kGEbJRh8=
github.com/ory/dockertest v3.3.4+incompatible/go.mod h1:1vX4m9wsvi00u5bseYwXaSnhNrne+V0E6LAcBILJdPs=
github.com/otiai10/copy v1.1.1 h1:PH7IFlRQ6Fv9vYmuXbDRLdgTHoP1w483kPNUP2bskpo=
github.com/otiai10/copy v1.1.1/go.mod h1:rrF5dJ5F0t/EWSYODDu4j9/vEeYHMkc8jt0zJChqQWw=
github.com/otiai10/curr v0.0.0-20150429015615-9b4961190c95/go.mod h1:9qAhocn7zKJG+0mI8eUu6xqkFDYS2kb2saOteoSB3cE=
github.com/otiai10/curr v1.0.0/go.mod h1:LskTG5wDwr8Rs+nNQ+1LlxRjAtTZZjtJW4rMXl6j4vs=
github.com/otiai10/mint v1.3.0/go.mod h1:F5AjcsTsWUqX+Na9fpHb52P8pcRX2CI6A3ctIT91xUo=
github.com/otiai10/mint v1.3.1/go.mod h1:/yxELlJQ0ufhjUwhshSj+wFjZ78CnZ48/1wtmBH1OTc=
github.com/oxtoacart/bpool v0.0.0-20150712133111-4e1c5567d7c2 h1:CXwSGu/LYmbjEab5aMCs5usQRVBGThelUKBNnoSOuso=
github.com/oxtoacart/bpool v0.0.0-20150712133111-4e1c5567d7c2/go.mod h1:L3UMQOThbttwfYRNFOWLLVXMhk5Lkio4GGOtw5UrxS0=
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
@@ -602,15 +660,16 @@ github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0Mw
github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc=
github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
github.com/pierrec/lz4 v2.0.5+incompatible h1:2xWsjqPFWcplujydGg4WmhC/6fZqK42wMM8aXeqhl0I=
github.com/pbnjay/strptime v0.0.0-20140226051138-5c05b0d668c9 h1:4lfz0keanz7/gAlvJ7lAe9zmE08HXxifBZJC0AdeGKo=
github.com/pbnjay/strptime v0.0.0-20140226051138-5c05b0d668c9/go.mod h1:6Hr+C/olSdkdL3z68MlyXWzwhvwmwN7KuUFXGb3PoOk=
github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
github.com/pierrec/lz4 v2.2.6+incompatible h1:6aCX4/YZ9v8q69hTyiR7dNLnTA3fgtKHVVW5BCd5Znw=
github.com/pierrec/lz4 v2.2.6+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
github.com/pierrec/lz4 v2.3.0+incompatible h1:CZzRn4Ut9GbUkHlQ7jqBXeZQV41ZSKWFc302ZU6lUTk=
github.com/pierrec/lz4 v2.3.0+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
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=
@@ -626,8 +685,9 @@ github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829 h1:D+Ci
github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs=
github.com/prometheus/client_model v0.0.0-20170216185247-6f3806018612/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f h1:BVwpUVJDADN2ufcGik7W992pyps0wZ888b/y9GXcLTU=
github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4 h1:gQz4mCbXsO+nc9n1hCxHcGA3Zx3Eo+UHZoInFGUIXNM=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/common v0.0.0-20180518154759-7600349dcfe1/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
github.com/prometheus/common v0.0.0-20181126121408-4724e9255275/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
@@ -654,7 +714,6 @@ github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww=
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 h1:nn5Wsu0esKSJiIVhscUtVbo7ada43DJhG55ua/hjS5I=
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ=
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4=
github.com/shurcooL/component v0.0.0-20170202220835-f88ec8f54cc4/go.mod h1:XhFIlyj5a1fBNx5aJTbKoIq0mNaPvOagO+HjB3EtxrY=
@@ -695,68 +754,83 @@ github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4k
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/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
github.com/spf13/pflag v1.0.1 h1:aCvUg6QPl3ibpQUxyLkrEkCHtPqYJL4x9AuhqVqFis4=
github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg=
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94 h1:0ngsPmuP6XIjiFRNFYlvKwSr5zff2v+uPHaffZ6/M4k=
github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.2.0 h1:Hbg2NidpLE8veEBkEZTL3CvlkUIVzuU9jDplZO54c48=
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
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/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.5.0 h1:DMOzIV76tmoDNE9pX6RSN0aDtCYeCg5VueieJaAo1uw=
github.com/stretchr/testify v1.5.0/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA=
github.com/tatsushid/go-prettytable v0.0.0-20141013043238-ed2d14c29939 h1:BhIUXV2ySTLrKgh/Hnts+QTQlIbWtomXt3LMdzME0A0=
github.com/tatsushid/go-prettytable v0.0.0-20141013043238-ed2d14c29939/go.mod h1:omGxs4/6hNjxPKUTjmaNkPzehSnNJOJN6pMEbrlYIT4=
github.com/tebeka/strftime v0.1.3 h1:5HQXOqWKYRFfNyBMNVc9z5+QzuBtIXy03psIhtdJYto=
github.com/tebeka/strftime v0.1.3/go.mod h1:7wJm3dZlpr4l/oVK0t1HYIc4rMzQ2XJlOMIUJUJH6XQ=
github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM=
github.com/ugorji/go v1.1.2/go.mod h1:hnLbHMwcvSihnDhEfx2/BzKp2xb0Y+ErdfYcrs9tkJQ=
github.com/ugorji/go/codec v0.0.0-20190204201341-e444a5086c43/go.mod h1:iT03XoTwV7xq/+UGwKO3UbC1nNNlopQiY61beSdrtOA=
github.com/ulikunitz/xz v0.5.5 h1:pFrO0lVpTBXLpYw+pnLj6TbvHuyjXMfjGeCwSqCVwok=
github.com/ulikunitz/xz v0.5.5/go.mod h1:2bypXElzHzzJZwzH67Y6wb67pO62Rzfn7BSiF4ABRW8=
github.com/urfave/cli v0.0.0-20160620154522-6011f165dc28 h1:Yf7/DcGM+61oLBGXQV2Q+ztEGBcOe3EUnbKSOn4fQuE=
github.com/urfave/cli v0.0.0-20160620154522-6011f165dc28/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
github.com/urfave/cli v1.20.0 h1:fDqGv3UG/4jbVl/QkFwEdddtEDjh/5Ov6X+0B/3bPaw=
github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
github.com/variantdev/chartify v0.0.0-20200330123007-ddc79388188c h1:PiAOZUdKtf8tIfps1fLaEsP9BCTT8hR9FawTfAuLY5o=
github.com/variantdev/chartify v0.0.0-20200330123007-ddc79388188c/go.mod h1:DzFQ1XeNBpbLdbze39ai3auS4b+Xo/KzRluEY/LyqlM=
github.com/variantdev/chartify v0.1.0 h1:RHo/di1tXvydrJwwbHoWduvG3r9nU0oj7RvuxmQLCW0=
github.com/variantdev/chartify v0.1.0/go.mod h1:DzFQ1XeNBpbLdbze39ai3auS4b+Xo/KzRluEY/LyqlM=
github.com/variantdev/chartify v0.1.1 h1:4K+6XotrJ/U5BoQc29BTOWpNGnzsCBEDahApiOoPURc=
github.com/variantdev/chartify v0.1.1/go.mod h1:DzFQ1XeNBpbLdbze39ai3auS4b+Xo/KzRluEY/LyqlM=
github.com/variantdev/chartify v0.1.2 h1:BAaZawFXJfcgk6HVM3o+/Q9Xzyv1rkPd9m+YeWM7+dg=
github.com/variantdev/chartify v0.1.2/go.mod h1:DzFQ1XeNBpbLdbze39ai3auS4b+Xo/KzRluEY/LyqlM=
github.com/variantdev/chartify v0.1.3 h1:3WEaT1ZYLaivcVdlE2poxBCw2KxNEaOzHB9k4rj5zXk=
github.com/variantdev/chartify v0.1.3/go.mod h1:DzFQ1XeNBpbLdbze39ai3auS4b+Xo/KzRluEY/LyqlM=
github.com/variantdev/chartify v0.2.0 h1:oaG/o+juw1C5PTAbRlsxUTgGYPmIneoqnYI7KQyVJJU=
github.com/variantdev/chartify v0.2.0/go.mod h1:0tw+4doFHsNnhttYx7I9Pv/dsZ82BD4UuTV9saBOcfw=
github.com/variantdev/chartify v0.3.0 h1:vm9cY+Amj44g7scz/FVpa3s13XqzFvTJrdM6iufiKEM=
github.com/variantdev/chartify v0.3.0/go.mod h1:0tw+4doFHsNnhttYx7I9Pv/dsZ82BD4UuTV9saBOcfw=
github.com/variantdev/chartify v0.3.1 h1:w5zdqUcRGYLLJIrpZm1FykiDdILo+M/nJNIA+RyUI7c=
github.com/variantdev/chartify v0.3.1/go.mod h1:0tw+4doFHsNnhttYx7I9Pv/dsZ82BD4UuTV9saBOcfw=
github.com/variantdev/chartify v0.3.2 h1:EjwlY/ArgkOhsvbMG+xyeBnsWRV6eaxfHfJj9st8PvI=
github.com/variantdev/chartify v0.3.2/go.mod h1:0tw+4doFHsNnhttYx7I9Pv/dsZ82BD4UuTV9saBOcfw=
github.com/variantdev/chartify v0.3.3 h1:qKi5Rw4A9TfZ2oedQIop4rcHfiToraakWIdn1rZJyLo=
github.com/variantdev/chartify v0.3.3/go.mod h1:0tw+4doFHsNnhttYx7I9Pv/dsZ82BD4UuTV9saBOcfw=
github.com/variantdev/dag v0.0.0-20191028002400-bb0b3c785363 h1:KrfQBEUn+wEOQ/6UIfoqRDvn+Q/wZridQ7t0G1vQqKE=
github.com/variantdev/dag v0.0.0-20191028002400-bb0b3c785363/go.mod h1:pH1TQsNSLj2uxMo9NNl9zdGy01Wtn+/2MT96BrKmVyE=
github.com/variantdev/vals v0.0.0-20191025124021-e86de6f8cd7d h1:od9EUts72ELxOSrqJxtbxAU/c30XCaA+xnq0jDWMQrA=
github.com/variantdev/vals v0.0.0-20191025124021-e86de6f8cd7d/go.mod h1:7Gb8avEj7D9rMIqn4Nn9RBKXo/0rZ7Z0e+3bmHIs420=
github.com/variantdev/vals v0.0.0-20191026125821-5d18b16cf30a h1:zrV+XXPXniLy9ZVHUN7DLVVKnMjActASxwsMKIohfLA=
github.com/variantdev/vals v0.0.0-20191026125821-5d18b16cf30a/go.mod h1:8CW8eonQlIJgAjF1fLfrkaBe16fGjwGf2PX52/Vualw=
github.com/variantdev/vals v0.0.0-20191030045026-1fa6af1b5299 h1:91g7EEeE6dHdubLkS013s9vcZScrGDk/RqZEQwSkn4w=
github.com/variantdev/vals v0.0.0-20191030045026-1fa6af1b5299/go.mod h1:8CW8eonQlIJgAjF1fLfrkaBe16fGjwGf2PX52/Vualw=
github.com/variantdev/vals v0.0.0-20191117001227-ba4c7a29875a h1:Ce3KCr4PAC1qdEykv5iMnDrUjhN6pWDkdSTw/Lx11ts=
github.com/variantdev/vals v0.0.0-20191117001227-ba4c7a29875a/go.mod h1:8CW8eonQlIJgAjF1fLfrkaBe16fGjwGf2PX52/Vualw=
github.com/variantdev/vals v0.4.0 h1:O1O7/sWhlvozcY2DjZBzlE1notxwVo6UBT1+w7HsO/k=
github.com/variantdev/vals v0.4.0/go.mod h1:28iJwAamsfHevGLXO7QKmVWmwa79xM06sRjw8eGY73U=
github.com/variantdev/vals v0.4.1-0.20200501114609-9cebe482281c h1:ODJGhYOTMQJ1s+aeVzFsoJc/2MnseLhMdgB+/mykXe0=
github.com/variantdev/vals v0.4.1-0.20200501114609-9cebe482281c/go.mod h1:IfLBErvsyYBQU0Jobuzfmft46K0MG0sLO6xz9o9KZdY=
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
go.etcd.io/bbolt v1.3.2 h1:Z/90sZLPOeCy2PwprqkFa25PdkusRzaj9P8zm/KNyvk=
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
go.etcd.io/etcd v0.0.0-20190412021913-f29b1ada1971/go.mod h1:KSGwdbiFchh5KIC9My2+ZVl5/3ANcwohw50dpPwa2cw=
go.mozilla.org/gopgagent v0.0.0-20170926210634-4d7ea76ff71a h1:N7VD+PwpJME2ZfQT8+ejxwA4Ow10IkGbU0MGf94ll8k=
go.mozilla.org/gopgagent v0.0.0-20170926210634-4d7ea76ff71a/go.mod h1:YDKUvO0b//78PaaEro6CAPH6NqohCmL2Cwju5XI2HoE=
go.mozilla.org/sops v0.0.0-20190611200209-e9e1e87723c8 h1:RGVnXInLdDvAWF8mT1gAl4M/6g4GyIRB0ltFPV7PtT4=
go.mozilla.org/sops v0.0.0-20190611200209-e9e1e87723c8/go.mod h1:njv+SYMHy9urU/V330aYWmWAP6EwAfN0WaRafSBgwfs=
go.mozilla.org/sops v0.0.0-20190912205235-14a22d7a7060 h1:KnOZgR0z89tCwGympfPkpzXdlCmhksUP5ddQ+Iqo1FU=
go.mozilla.org/sops v0.0.0-20190912205235-14a22d7a7060/go.mod h1:StzH0aTTiss59GzhB1wiC9H5dqUqTTDZYfeurUrmJD8=
go.opencensus.io v0.18.0 h1:Mk5rgZcggtbvtAun5aJzAtjKKN/t0R3jJPlWILlv938=
go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA=
go.opencensus.io v0.19.1/go.mod h1:gug0GbSHa8Pafr0d2urOSgoXHZ6x/RUlaiT0d9pqb4A=
go.opencensus.io v0.19.2/go.mod h1:NO/8qkisMZLZ1FCsKNqtJPwc8/TaclWyY0B6wcYNg9M=
go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
go.opencensus.io v0.21.0 h1:mU6zScU4U1YAFPHEHYk+3JC4SY7JxgkqS10ZOSyksNg=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
go.opencensus.io v0.22.1 h1:8dP3SGL7MPB94crU3bEPplMPe83FI4EouesJUeFHv50=
go.opencensus.io v0.22.1/go.mod h1:Ap50jQcDJrx6rB6VgeeFPtuPIf3wMRvRfrfYDO6+BmA=
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.3 h1:8sGtKOrtQqkN1bp2AtX+misvLIlOmsEsNd+9NIcPEm8=
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.uber.org/atomic v1.3.2 h1:2Oa65PReHzfn29GpvgsYwloV9AVFHPDk8tYxt2c2tr4=
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/multierr v1.1.0 h1:HoEmRHQPVSqub6w2z2d2EOVs2fjyFRGyofhKuyDq0QI=
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
go.uber.org/zap v1.8.0 h1:r6Za1Rii8+EGOYRDLvpooNOF6kP3iyDnkpzbw67gCQ8=
go.uber.org/zap v1.8.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
go.uber.org/zap v1.9.1 h1:XCJQEf3W6eZaVwhRBof6ImoYGJSITeKWsyeh3HFu/5o=
go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
go4.org v0.0.0-20180809161055-417644f6feb5/go.mod h1:MkTOUMDaeVYJUOUsaDXIhWPZYa1yOyC1qaOBpL57BhE=
@@ -764,17 +838,14 @@ golang.org/x/build v0.0.0-20190111050920-041ab4dc3f9d/go.mod h1:OWs+y06UdEOHN4y+
golang.org/x/crypto v0.0.0-20180608092829-8ac0e0d97ce4/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16 h1:y6ce7gCWtnH+m3dCjzQ1PCuwl28DDIc3VNnvY29DlIA=
golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190418165655-df01cb2cc480 h1:O5YqonU5IWby+w98jVUG9h7zlCWCcH4RHyPVReBmhzk=
golang.org/x/crypto v0.0.0-20190418165655-df01cb2cc480/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190513172903-22d7a77e9e5f/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550 h1:ObdrDkeb4kJdCP557AjRjq69pTHfNouLtWZG7j9rPN8=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191029031824-8986dd9e96cf h1:fnPsqIDRbCSgumaMCRpoIoF2s4qxv0xSSS0BVZUE/ss=
golang.org/x/crypto v0.0.0-20191029031824-8986dd9e96cf/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
@@ -782,8 +853,12 @@ golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=
golang.org/x/exp v0.0.0-20191002040644-a1355ae1e2c3 h1:n9HxLrNxWWtEb1cA950nuEEj3QnKbtsCJ6KjcgisNUs=
golang.org/x/exp v0.0.0-20191002040644-a1355ae1e2c3/go.mod h1:NOZ3BPKG0ec/BKJQgnvsSFpcKLM5xXVWnvZS97DWHgE=
golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
@@ -794,19 +869,25 @@ golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTk
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190930215403-16217165b5de h1:5hukYrvBGR8/eNkX5mdUezrA6JiaEZDtJb9Ei+1LlBs=
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=
golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/lint v0.0.0-20200302205851-738671d3881b h1:Wh+f8QHJXR411sJR8/vRBTZ7YapZaRvUcLFFJhusH0k=
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.2.0 h1:KU7oHjnv3XNWfa5COkzUifxZmxp1TyI7ImMXqFxLwvQ=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/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-20181023162649-9b4f9f5ad519/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 h1:gOpx8G595UYyvj8UK4+OFyY4rx037g3fmfhe5SasG3U=
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -819,33 +900,36 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn
golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859 h1:R/3boaszxrf1GEUWTVDzSKVwLmSJpwZ1yqXm8j0v2QI=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20191021144547-ec77196f6094 h1:5O4U9trLjNpuhpynaDsqwCk+Tw6seqJz1EbqbnzHrc8=
golang.org/x/net v0.0.0-20191021144547-ec77196f6094/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20191028085509-fe3aa8a45271 h1:N66aaryRB3Ax92gH0v3hp1QYZ3zWWCCUR/j8Ifh45Ss=
golang.org/x/net v0.0.0-20191028085509-fe3aa8a45271/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e h1:3G+cUijn7XD+S4eJFddp53Pv7+slrESplyjG25HgL+k=
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
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 h1:uESlIz09WIHT2I+pasSXcpLYqYK8wHcdCetU3VuMBJE=
golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190130055435-99b60b757ec1/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190319182350-c85d3e98c914/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
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/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d h1:TzXSXBo42m9gQenoE3b9BGiEpg5IG2JkU5FkPIawgtw=
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/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 h1:Bl/8QSvNqXvPGPGXa2z5xUTmV7VDcZyvRZ+QQXkXTZQ=
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/sync v0.0.0-20190911185100-cd5d95a43a6e h1:vcxGaoTs7kV8m5Np9uUNQin4BrLOthgV7252N8V+FwY=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a h1:WXEvlFVvvGxCJLG6REjsT03iWnKLEWinaScsxF2Vm2o=
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
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=
@@ -855,7 +939,6 @@ golang.org/x/sys v0.0.0-20181029174526-d69651ed3497/go.mod h1:STP8DvDyc/dI5b8T5h
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-20181213200352-4d1cda033e06 h1:0oC8rFnE+74kEmuHZ46F6KHsMr5Gx2gUQPuNz28iQZM=
golang.org/x/sys v0.0.0-20181213200352-4d1cda033e06/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181218192612-074acd46bca6/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190129075346-302c3dd5f1cc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -866,27 +949,34 @@ golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b h1:ag/x1USPSsqHud38I9BAC88qdNLDHHtQ4mlgQIZPPNA=
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190514135907-3a4b5fb9f71f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190523142557-0e01d883c5c5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037 h1:YyJpGZS1sBuBCzLAR1VEpK193GlqGZbnPFnPV/5Rsb4=
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191029155521-f43be2a4598c h1:S/FtSvpNLtFBgjTqcKsRpsa6aVsI6iztaz1bQd9BJwE=
golang.org/x/sys v0.0.0-20191029155521-f43be2a4598c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200331124033-c3d80250170d h1:nc5K6ox/4lTFbMVSL9WRR81ixkcwXThoiF6yf+R9scA=
golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2 h1:z99zHgr7hKfrUcX/KsoJk5FJfjTceCKIp96+biqP4To=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20181227161524-e6919f6577db/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
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/time v0.0.0-20191024005414-555d28b269f0 h1:/5xXl8Y5W96D+TtHSlonuFqGHIWVuyCkGJLwGh9JJFs=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
@@ -906,19 +996,34 @@ golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBn
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190624222133-a101b041ded4/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20190927191325-030b2cf1153e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191010171213-8abd42400456/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191026034945-b2104f82a97d h1:QFO0Wgcqcp8nI9hbisKDTBsmfwrvLswk2T73QDZZgVo=
golang.org/x/tools v0.0.0-20191026034945-b2104f82a97d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4 h1:kDtqNkeBrZb8B+atrj50B5XLHpzXXqcCdZPP/ApQ5NY=
golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
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.0.0-20181220000619-583d854617af/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
google.golang.org/api v0.1.0 h1:K6z2u68e86TPdSdefXdzvXgR1zEMa+459vBSfWYAZkI=
google.golang.org/api v0.1.0/go.mod h1:UGEZY7KEX120AnNLIHFMKIo4obdJhkp2tPbaPlQx13Y=
google.golang.org/api v0.2.0/go.mod h1:IfRCZScioGtypHNTlz3gFk67J8uePVW7uDTBzXuIkhU=
google.golang.org/api v0.3.0/go.mod h1:IuvZyQh8jgscv8qWfQ4ABd8m7hEudgBFM/EdhA3BnXw=
@@ -929,19 +1034,19 @@ google.golang.org/api v0.5.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEt
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
google.golang.org/api v0.10.0 h1:7tmAxx3oKE98VMZ+SBZzvYYWRQ9HODBxmC8mXUsraSQ=
google.golang.org/api v0.10.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
google.golang.org/api v0.11.0 h1:n/qM3q0/rV2F0pox7o0CvNhlPvZAo7pLbef122cbLJ0=
google.golang.org/api v0.11.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
google.golang.org/api v0.13.0 h1:Q3Ui3V3/CVinFWFiW39Iw0kMuVrRzYX0wN6OPFp0lTA=
google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.20.0 h1:jz2KixHX7EcCPiQrySzPdnYT7DbINAypCqKZ1Z7GM40=
google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.21.0 h1:zS+Q/CJJnVlXpXQVIz+lH0ZT2lBuT2ac7XD8Y/3w6hY=
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 h1:FBSsiFRMz3LBeXIomRnVzrQwSDj4ibvcRexLG0LZGQk=
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.0 h1:Tfd7cKwKbFRsI8RMAD3oqqw7JPFRrvFlOsfbgVkjOOw=
google.golang.org/appengine v1.6.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
google.golang.org/appengine v1.6.5 h1:tycE03LOZYQNhDpS27tcQdAzLCVMaj7QT2SXxebnpCM=
@@ -952,43 +1057,50 @@ google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoA
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-20181219182458-5a97ab628bfb/go.mod h1:7Ep/1NZk928CDR8SjdVbjWNpdIf6nzjE3BTgJDr2Atg=
google.golang.org/genproto v0.0.0-20190201180003-4b09977fb922 h1:mBVYJnbrXLA/ZCBTCe7PtEgAUP+1bg92qTaFoPHdz+8=
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/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190404172233-64821d5d2107/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 h1:nfPFGzJkUDX6uBmpN/pSw7MbOAWegH5QDQuoXFHedLg=
google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190508193815-b515fa19cec8/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190513181449-d00d292a067c/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s=
google.golang.org/genproto v0.0.0-20190716160619-c506a9f90610/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64 h1:iKtrH9Y8mcbADOP0YFaEMth7OfuHY9xHOwNj4znpM1A=
google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
google.golang.org/genproto v0.0.0-20191009194640-548a555dbc03 h1:4HYDjxeNXAOTv3o1N2tjo8UUSlhQgAD52FVkwxnWgM8=
google.golang.org/genproto v0.0.0-20191009194640-548a555dbc03/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20191028173616-919d9bdd9fe6 h1:UXl+Zk3jqqcbEVV7ace5lrt4YdA4tXiz3f/KbmD29Vo=
google.golang.org/genproto v0.0.0-20191028173616-919d9bdd9fe6/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA=
google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200403120447-c50568487044 h1:112OPFAnKD+XtZhdffFnz17dEbiCTCKfnNQCAWmwFzA=
google.golang.org/genproto v0.0.0-20200403120447-c50568487044/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
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 h1:TRJYBgMclJvGYn2rIMjj+h9KtMt5r1Ij7ODVRIZkwhk=
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.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
google.golang.org/grpc v1.22.0 h1:J0UbZOIrCAl+fpTOf8YLs4dJo8L/owV4LYVtAXQoPkw=
google.golang.org/grpc v1.22.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.22.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.24.0 h1:vb/1TCsVn3DcJlQ0Gs1yB1pKI6Do2/QNwxdKqmc/b0s=
google.golang.org/grpc v1.24.0/go.mod h1:XDChyiUovWa60DnaeDeZmSW86xtLtjtZbwvSiRnRtcA=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.28.0 h1:bO/TA4OxCOummhSf10siHuG7vJOiwh7SpRpFZDkOgl4=
google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60=
gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d h1:TxyelI5cVkbREznMhfzycHdkp5cLA7DpE+GKjSslYhM=
gopkg.in/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d/go.mod h1:cuepJuh7vyXfUyUwEgHQXw849cJrilpS5NeIjOWESAw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
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=
@@ -1009,7 +1121,6 @@ gopkg.in/mgo.v2 v2.0.0-20180705113604-9856a29383ce/go.mod h1:yeKp02qBN3iKW1OzL3M
gopkg.in/ory-am/dockertest.v3 v3.3.4/go.mod h1:s9mmoLkaGeAh97qygnNj4xWkiN7e1SKekYC6CovU+ek=
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
gopkg.in/square/go-jose.v2 v2.3.0/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
gopkg.in/square/go-jose.v2 v2.3.1 h1:SK5KegNXmKmqE342YYN2qPHEnUYeoMiXXl1poUlI+o4=
gopkg.in/square/go-jose.v2 v2.3.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
gopkg.in/square/go-jose.v2 v2.4.0 h1:0kXPskUMGAXXWJlP05ktEMOV0vmzFQUWw6d+aZJQU8A=
gopkg.in/square/go-jose.v2 v2.4.0/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
@@ -1017,9 +1128,7 @@ gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkep
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/urfave/cli.v1 v1.20.0/go.mod h1:vuBzUtMdQeixQj8LVd+/98pzhxNGQoyuPBlsXHOQNO0=
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
gopkg.in/yaml.v2 v2.2.1 h1:mUhvW9EsL+naU5Q3cakzfE91YhliOondGd6ZrsDBHQE=
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.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
@@ -1027,8 +1136,13 @@ gopkg.in/yaml.v3 v3.0.0-20190709130402-674ba3eaed22 h1:0efs3hwEZhFKsCoP8l6dDB1AZ
gopkg.in/yaml.v3 v3.0.0-20190709130402-674ba3eaed22/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20191026110619-0b21df46bc1d h1:LCPbGQ34PMrwad11aMZ+dbz5SAsq/0ySjRwQ8I9Qwd8=
gopkg.in/yaml.v3 v3.0.0-20191026110619-0b21df46bc1d/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20200121175148-a6ecf24a6d71/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo=
gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
gotest.tools/v3 v3.0.3-0.20200410202438-4e4a41b7851a h1:W6NA2qLDtfcDtS8xAbmFY1PtPHM0oVlNzcGlZH7vL+w=
gotest.tools/v3 v3.0.3-0.20200410202438-4e4a41b7851a/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8=
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-20180920025451-e3ad64cb4ed3/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
@@ -1036,18 +1150,23 @@ honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWh
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=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.1-2019.2.3 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM=
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
honnef.co/go/tools v0.0.1-2020.1.3 h1:sXmLre5bzIR6ypkjXCDI3jHPssRhc8KD/Ome589sc3U=
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
k8s.io/api v0.0.0-20190409092523-d687e77c8ae9 h1:c9UEl5z8gk1DGh/g3snETZ+a52YeR9VdbX/3BQ4PHas=
k8s.io/api v0.0.0-20190409092523-d687e77c8ae9/go.mod h1:FQEUn50aaytlU65qqBn/w+5ugllHwrBzKm7DzbnXdzE=
k8s.io/apimachinery v0.0.0-20190409092423-760d1845f48b h1:fVkKJL9FIpA8LSJyHVM00MP45q1WJ7+af77vcxmQP4g=
k8s.io/apimachinery v0.0.0-20190409092423-760d1845f48b/go.mod h1:FW86P8YXVLsbuplGMZeb20J3jYHscrDqw4jELaFJvRU=
k8s.io/klog v0.0.0-20190306015804-8e90cee79f82 h1:SHucoAy7lRb+w5oC/hbXyZg+zX+Wftn6hD4tGzHCVqA=
k8s.io/klog v0.0.0-20190306015804-8e90cee79f82/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk=
k8s.io/klog v1.0.0 h1:Pt+yjF5aB1xDSVbau4VsWe+dQNzA0qv1LlXdC2dF6Q8=
k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I=
k8s.io/kube-openapi v0.0.0-20190228160746-b3a7cee44a30/go.mod h1:BXM9ceUBTj2QnfH2MK1odQs778ajze1RxcmP6S8RVVc=
layeh.com/radius v0.0.0-20190322222518-890bc1058917 h1:BDXFaFzUt5EIqe/4wrTc4AcYZWP6iC6Ult+jQWLh5eU=
layeh.com/radius v0.0.0-20190322222518-890bc1058917/go.mod h1:fywZKyu//X7iRzaxLgPWsvc0L26IUpVvE/aeIL2JtIQ=
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
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=
+6
View File
@@ -0,0 +1,6 @@
#!/bin/bash
echo "=> Building container"
docker build \
--tag "$IMAGE_NAME" \
--file Dockerfile .
+9
View File
@@ -0,0 +1,9 @@
#!/bin/bash
go()
{
docker run --rm -v "$(pwd):/workspace" -w /workspace golang:latest go $@;
}
go get -u github.com/tcnksm/ghr github.com/mitchellh/gox
go mod vendor
+11
View File
@@ -0,0 +1,11 @@
#!/bin/bash
echo "=> Fetch unshallow origin"
git fetch --unshallow origin || true
echo "=> Tag image:"
docker tag "${IMAGE_NAME}" "${DOCKER_REPO}:$(git describe --tags --abbrev=0 HEAD^)"
docker tag "${IMAGE_NAME}" "${DOCKER_REPO}:$(git describe --tags --abbrev=0 HEAD)"
echo "=> Push images"
docker push "${DOCKER_REPO}"
+78 -17
View File
@@ -5,31 +5,27 @@ import (
"os"
"strings"
"github.com/roboll/helmfile/pkg/app/version"
"github.com/roboll/helmfile/pkg/app"
"github.com/roboll/helmfile/pkg/helmexec"
"github.com/roboll/helmfile/pkg/maputil"
"github.com/roboll/helmfile/pkg/state"
"github.com/urfave/cli"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
)
var Version string
var logger *zap.SugaredLogger
func configureLogging(c *cli.Context) error {
// Valid levels:
// https://github.com/uber-go/zap/blob/7e7e266a8dbce911a49554b945538c5b950196b8/zapcore/level.go#L126
logLevel := c.GlobalString("log-level")
if c.GlobalBool("quiet") {
if c.GlobalBool("debug") {
logLevel = "debug"
} else if c.GlobalBool("quiet") {
logLevel = "warn"
}
var level zapcore.Level
err := level.Set(logLevel)
if err != nil {
return err
}
logger = helmexec.NewLogger(os.Stderr, logLevel)
if c.App.Metadata == nil {
// Auto-initialised in 1.19.0
@@ -45,12 +41,12 @@ func main() {
cliApp := cli.NewApp()
cliApp.Name = "helmfile"
cliApp.Usage = ""
cliApp.Version = Version
cliApp.Version = version.Version
cliApp.Flags = []cli.Flag{
cli.StringFlag{
Name: "helm-binary, b",
Usage: "path to helm binary",
Value: "helm",
Value: app.DefaultHelmBinary,
},
cli.StringFlag{
Name: "file, f",
@@ -76,6 +72,10 @@ func main() {
Name: "kube-context",
Usage: "Set kubectl context. Uses current context by default",
},
cli.BoolFlag{
Name: "debug",
Usage: "Enable verbose output for Helm and set log-level to debug, this disables --quiet/-q effect",
},
cli.BoolFlag{
Name: "no-color",
Usage: "Output without color",
@@ -191,6 +191,10 @@ func main() {
Name: "detailed-exitcode",
Usage: "return a non-zero exit code when there are changes",
},
cli.BoolFlag{
Name: "include-tests",
Usage: "enable the diffing of the helm test hooks",
},
cli.BoolFlag{
Name: "suppress-secrets",
Usage: "suppress secrets in the output. highly recommended to specify on CI/CD use-cases",
@@ -236,6 +240,10 @@ func main() {
Value: 0,
Usage: "maximum number of concurrent downloads of release charts",
},
cli.BoolFlag{
Name: "validate",
Usage: "validate your manifests against the Kubernetes cluster you are currently pointing at",
},
cli.BoolFlag{
Name: "skip-deps",
Usage: "skip running `helm repo update` and `helm dependency build`",
@@ -329,15 +337,31 @@ func main() {
Value: 0,
Usage: "output NUM lines of context around changes",
},
cli.BoolFlag{
Name: "detailed-exitcode",
Usage: "return a non-zero exit code 2 instead of 0 when there were changes detected AND the changes are synced successfully",
},
cli.StringFlag{
Name: "args",
Value: "",
Usage: "pass args to helm exec",
},
cli.BoolFlag{
Name: "retain-values-files",
Usage: "Stop cleaning up values files passed to Helm. Together with --log-level=debug, you can manually rerun helm commands as Helmfile did for debugging purpose",
},
cli.BoolFlag{
Name: "include-tests",
Usage: "enable the diffing of the helm test hooks",
},
cli.BoolFlag{
Name: "suppress-secrets",
Usage: "suppress secrets in the diff output. highly recommended to specify on CI/CD use-cases",
},
cli.BoolFlag{
Name: "suppress-diff",
Usage: "suppress diff in the output. Usable in new installs",
},
cli.BoolFlag{
Name: "skip-deps",
Usage: "skip running `helm repo update` and `helm dependency build`",
@@ -447,7 +471,13 @@ func main() {
{
Name: "list",
Usage: "list releases defined in state file",
Flags: []cli.Flag{},
Flags: []cli.Flag{
cli.StringFlag{
Name: "output",
Value: "",
Usage: "output releases list as a json string",
},
},
Action: action(func(run *app.App, c configImpl) error {
return run.ListReleases(c)
}),
@@ -484,10 +514,10 @@ func NewUrfaveCliConfigImpl(c *cli.Context) (configImpl, error) {
ops := strings.Split(optsSet[i], ",")
for j := range ops {
op := strings.SplitN(ops[j], "=", 2)
k := strings.Split(op[0], ".")
k := maputil.ParseKey(op[0])
v := op[1]
set = maputil.Set(set, k, v)
maputil.Set(set, k, v)
}
}
conf.set = set
@@ -509,13 +539,23 @@ func (c configImpl) Values() []string {
}
func (c configImpl) Args() string {
return c.c.String("args")
args := c.c.String("args")
enableHelmDebug := c.c.GlobalBool("debug")
if enableHelmDebug {
args = fmt.Sprintf("%s %s", args, "--debug")
}
return args
}
func (c configImpl) OutputDir() string {
return c.c.String("output-dir")
}
func (c configImpl) Validate() bool {
return c.c.Bool("validate")
}
func (c configImpl) Concurrency() int {
return c.c.Int("concurrency")
}
@@ -534,10 +574,22 @@ func (c configImpl) DetailedExitcode() bool {
return c.c.Bool("detailed-exitcode")
}
func (c configImpl) RetainValuesFiles() bool {
return c.c.Bool("retain-values-files")
}
func (c configImpl) IncludeTests() bool {
return c.c.Bool("include-tests")
}
func (c configImpl) SuppressSecrets() bool {
return c.c.Bool("suppress-secrets")
}
func (c configImpl) SuppressDiff() bool {
return c.c.Bool("suppress-diff")
}
// DeleteConfig
func (c configImpl) Purge() bool {
@@ -551,9 +603,18 @@ func (c configImpl) Cleanup() bool {
}
func (c configImpl) Timeout() int {
if !c.c.IsSet("timeout") {
return state.EmptyTimeout
}
return c.c.Int("timeout")
}
// ListConfig
func (c configImpl) Output() string {
return c.c.String("output")
}
// GlobalConfig
func (c configImpl) HelmBinary() string {
@@ -617,11 +678,11 @@ func action(do func(*app.App, configImpl) error) func(*cli.Context) error {
a := app.New(conf)
a.ErrorHandler = func(err error) error {
if err := do(a, conf); err != nil {
return toCliError(implCtx, err)
}
return do(a, conf)
return nil
}
}
+493 -278
View File
File diff suppressed because it is too large Load Diff
+756 -317
View File
File diff suppressed because it is too large Load Diff
+15
View File
@@ -40,11 +40,18 @@ type ApplyConfigProvider interface {
Set() []string
SkipDeps() bool
IncludeTests() bool
SuppressSecrets() bool
SuppressDiff() bool
DetailedExitcode() bool
NoColor() bool
Context() int
RetainValuesFiles() bool
concurrencyConfig
interactive
loggingConfig
@@ -68,7 +75,10 @@ type DiffConfigProvider interface {
Set() []string
SkipDeps() bool
IncludeTests() bool
SuppressSecrets() bool
SuppressDiff() bool
DetailedExitcode() bool
NoColor() bool
@@ -119,6 +129,7 @@ type TemplateConfigProvider interface {
Values() []string
Set() []string
Validate() bool
SkipDeps() bool
OutputDir() string
@@ -145,3 +156,7 @@ type loggingConfig interface {
type interactive interface {
Interactive() bool
}
type ListConfigProvider interface {
Output() string
}
+3 -3
View File
@@ -1,9 +1,9 @@
package app
import (
"gotest.tools/assert"
is "gotest.tools/assert/cmp"
"gotest.tools/env"
"gotest.tools/v3/assert"
is "gotest.tools/v3/assert/cmp"
"gotest.tools/v3/env"
"os"
"testing"
)
+9
View File
@@ -17,6 +17,15 @@ func NewContext() Context {
func (ctx Context) SyncReposOnce(st *state.HelmState, helm state.RepoUpdater) []error {
var errs []error
hasInstalled := false
for _, release := range st.Releases {
hasInstalled = hasInstalled || release.Installed == nil || *release.Installed
}
if !hasInstalled {
return errs
}
allUpdated := true
for _, r := range st.Repositories {
_, exists := ctx.updatedRepos[r.Name]
+35 -10
View File
@@ -4,20 +4,24 @@ import (
"bytes"
"errors"
"fmt"
"github.com/roboll/helmfile/pkg/helmexec"
"path/filepath"
"sort"
"github.com/imdario/mergo"
"github.com/roboll/helmfile/pkg/environment"
"github.com/roboll/helmfile/pkg/helmexec"
"github.com/roboll/helmfile/pkg/state"
"github.com/variantdev/vals"
"go.uber.org/zap"
)
const (
DefaultHelmBinary = state.DefaultHelmBinary
)
type desiredStateLoader struct {
KubeContext string
Reverse bool
overrideKubeContext string
overrideHelmBinary string
env string
namespace string
@@ -26,9 +30,9 @@ type desiredStateLoader struct {
fileExists func(string) (bool, error)
abs func(string) (string, error)
glob func(string) ([]string, error)
getHelm func(*state.HelmState) helmexec.Interface
logger *zap.SugaredLogger
helm helmexec.Interface
valsRuntime vals.Evaluator
}
@@ -60,7 +64,7 @@ func (ld *desiredStateLoader) Load(f string, opts LoadOpts) (*state.HelmState, e
return nil, err
}
if ld.Reverse {
if opts.Reverse {
rev := func(i, j int) bool {
return j < i
}
@@ -68,11 +72,11 @@ func (ld *desiredStateLoader) Load(f string, opts LoadOpts) (*state.HelmState, e
sort.Slice(st.Helmfiles, rev)
}
if ld.KubeContext != "" {
if ld.overrideKubeContext != "" {
if st.HelmDefaults.KubeContext != "" {
return nil, errors.New("err: Cannot use option --kube-context and set attribute helmDefaults.kubeContext.")
}
st.HelmDefaults.KubeContext = ld.KubeContext
st.HelmDefaults.KubeContext = ld.overrideKubeContext
}
if ld.namespace != "" {
@@ -126,11 +130,24 @@ func (ld *desiredStateLoader) loadFileWithOverrides(inheritedEnv, overrodeEnv *e
)
}
return self, err
if err != nil {
return nil, err
}
for i, h := range self.Helmfiles {
if h.Path == f {
return nil, fmt.Errorf("%s contains a recursion into the same sub-helmfile at helmfiles[%d]", f, i)
}
if h.Path == "." {
return nil, fmt.Errorf("%s contains a recursion into the the directory containing this helmfile at helmfiles[%d]", f, i)
}
}
return self, nil
}
func (a *desiredStateLoader) underlying() *state.StateCreator {
c := state.NewCreator(a.logger, a.readFile, a.fileExists, a.abs, a.glob, a.helm, a.valsRuntime)
c := state.NewCreator(a.logger, a.readFile, a.fileExists, a.abs, a.glob, a.valsRuntime, a.getHelm, a.overrideHelmBinary)
c.LoadFile = a.loadFile
return c
}
@@ -156,7 +173,9 @@ func (a *desiredStateLoader) load(yaml []byte, baseDir, file string, evaluateBas
}
func (ld *desiredStateLoader) renderAndLoad(env, overrodeEnv *environment.Environment, baseDir, filename string, content []byte, evaluateBases bool) (*state.HelmState, error) {
parts := bytes.Split(content, []byte("\n---\n"))
// Allows part-splitting to work with CLRF-ed content
normalizedContent := bytes.ReplaceAll(content, []byte("\r\n"), []byte("\n"))
parts := bytes.Split(normalizedContent, []byte("\n---\n"))
var finalState *state.HelmState
@@ -190,6 +209,12 @@ func (ld *desiredStateLoader) renderAndLoad(env, overrodeEnv *environment.Enviro
return nil, err
}
for i, r := range currentState.Releases {
if r.Chart == "" {
return nil, fmt.Errorf("error during %s parsing: encountered empty chart while reading release %q at index %d", id, r.Name, i)
}
}
if finalState == nil {
finalState = currentState
} else {
+453
View File
@@ -0,0 +1,453 @@
package app
import (
"bufio"
"bytes"
"github.com/roboll/helmfile/pkg/exectest"
"github.com/roboll/helmfile/pkg/helmexec"
"github.com/roboll/helmfile/pkg/testhelper"
"github.com/variantdev/vals"
"go.uber.org/zap"
"io"
"path/filepath"
"sync"
"testing"
)
type destroyConfig struct {
args string
concurrency int
interactive bool
logger *zap.SugaredLogger
}
func (d destroyConfig) Args() string {
return d.args
}
func (d destroyConfig) Interactive() bool {
return d.interactive
}
func (d destroyConfig) Logger() *zap.SugaredLogger {
return d.logger
}
func (d destroyConfig) Concurrency() int {
return d.concurrency
}
func TestDestroy(t *testing.T) {
testcases := []struct {
name string
loc string
ns string
concurrency int
error string
files map[string]string
selectors []string
lists map[exectest.ListKey]string
diffs map[exectest.DiffKey]error
upgraded []exectest.Release
deleted []exectest.Release
log string
}{
//
// complex test cases for smoke testing
//
{
name: "smoke",
loc: location(),
files: map[string]string{
"/path/to/helmfile.yaml": `
releases:
- name: database
chart: charts/mysql
needs:
- logging
- name: frontend-v1
chart: charts/frontend
installed: false
needs:
- servicemesh
- logging
- backend-v1
- name: frontend-v2
chart: charts/frontend
needs:
- servicemesh
- logging
- backend-v2
- name: frontend-v3
chart: charts/frontend
needs:
- servicemesh
- logging
- backend-v2
- name: backend-v1
chart: charts/backend
installed: false
needs:
- servicemesh
- logging
- database
- anotherbackend
- name: backend-v2
chart: charts/backend
needs:
- servicemesh
- logging
- database
- anotherbackend
- name: anotherbackend
chart: charts/anotherbackend
needs:
- servicemesh
- logging
- database
- name: servicemesh
chart: charts/istio
needs:
- logging
- name: logging
chart: charts/fluent-bit
- name: front-proxy
chart: stable/envoy
`,
},
diffs: map[exectest.DiffKey]error{},
lists: map[exectest.ListKey]string{
exectest.ListKey{Filter: "^frontend-v1$", Flags: "--kube-contextdefault--deployed--failed--pending"}: `NAME REVISION UPDATED STATUS CHART APP VERSION NAMESPACE
`,
exectest.ListKey{Filter: "^frontend-v2$", Flags: "--kube-contextdefault--deployed--failed--pending"}: `NAME REVISION UPDATED STATUS CHART APP VERSION NAMESPACE
frontend-v2 4 Fri Nov 1 08:40:07 2019 DEPLOYED frontend-3.1.0 3.1.0 default
`,
exectest.ListKey{Filter: "^frontend-v3$", Flags: "--kube-contextdefault--deployed--failed--pending"}: `NAME REVISION UPDATED STATUS CHART APP VERSION NAMESPACE
frontend-v3 4 Fri Nov 1 08:40:07 2019 DEPLOYED frontend-3.1.0 3.1.0 default
`,
exectest.ListKey{Filter: "^backend-v1$", Flags: "--kube-contextdefault--deployed--failed--pending"}: `NAME REVISION UPDATED STATUS CHART APP VERSION NAMESPACE
`,
exectest.ListKey{Filter: "^backend-v2$", Flags: "--kube-contextdefault--deployed--failed--pending"}: `NAME REVISION UPDATED STATUS CHART APP VERSION NAMESPACE
backend-v2 4 Fri Nov 1 08:40:07 2019 DEPLOYED backend-3.1.0 3.1.0 default
`,
exectest.ListKey{Filter: "^logging$", Flags: "--kube-contextdefault--deployed--failed--pending"}: `NAME REVISION UPDATED STATUS CHART APP VERSION NAMESPACE
logging 4 Fri Nov 1 08:40:07 2019 DEPLOYED fluent-bit-3.1.0 3.1.0 default
`,
exectest.ListKey{Filter: "^front-proxy$", Flags: "--kube-contextdefault--deployed--failed--pending"}: `NAME REVISION UPDATED STATUS CHART APP VERSION NAMESPACE
front-proxy 4 Fri Nov 1 08:40:07 2019 DEPLOYED envoy-3.1.0 3.1.0 default
`,
exectest.ListKey{Filter: "^servicemesh$", Flags: "--kube-contextdefault--deployed--failed--pending"}: `NAME REVISION UPDATED STATUS CHART APP VERSION NAMESPACE
servicemesh 4 Fri Nov 1 08:40:07 2019 DEPLOYED istio-3.1.0 3.1.0 default
`,
exectest.ListKey{Filter: "^database$", Flags: "--kube-contextdefault--deployed--failed--pending"}: `NAME REVISION UPDATED STATUS CHART APP VERSION NAMESPACE
database 4 Fri Nov 1 08:40:07 2019 DEPLOYED mysql-3.1.0 3.1.0 default
`,
exectest.ListKey{Filter: "^anotherbackend$", Flags: "--kube-contextdefault--deployed--failed--pending"}: `NAME REVISION UPDATED STATUS CHART APP VERSION NAMESPACE
anotherbackend 4 Fri Nov 1 08:40:07 2019 DEPLOYED anotherbackend-3.1.0 3.1.0 default
`,
},
// Disable concurrency to avoid in-deterministic result
concurrency: 1,
upgraded: []exectest.Release{},
deleted: []exectest.Release{
{Name: "frontend-v3", Flags: []string{}},
{Name: "frontend-v2", Flags: []string{}},
{Name: "frontend-v1", Flags: []string{}},
{Name: "backend-v2", Flags: []string{}},
{Name: "backend-v1", Flags: []string{}},
{Name: "anotherbackend", Flags: []string{}},
{Name: "database", Flags: []string{}},
{Name: "servicemesh", Flags: []string{}},
{Name: "front-proxy", Flags: []string{}},
{Name: "logging", Flags: []string{}},
},
log: `processing file "helmfile.yaml" in directory "."
first-pass rendering starting for "helmfile.yaml.part.0": inherited=&{default map[] map[]}, overrode=<nil>
first-pass uses: &{default map[] map[]}
first-pass rendering output of "helmfile.yaml.part.0":
0:
1: releases:
2: - name: database
3: chart: charts/mysql
4: needs:
5: - logging
6: - name: frontend-v1
7: chart: charts/frontend
8: installed: false
9: needs:
10: - servicemesh
11: - logging
12: - backend-v1
13: - name: frontend-v2
14: chart: charts/frontend
15: needs:
16: - servicemesh
17: - logging
18: - backend-v2
19: - name: frontend-v3
20: chart: charts/frontend
21: needs:
22: - servicemesh
23: - logging
24: - backend-v2
25: - name: backend-v1
26: chart: charts/backend
27: installed: false
28: needs:
29: - servicemesh
30: - logging
31: - database
32: - anotherbackend
33: - name: backend-v2
34: chart: charts/backend
35: needs:
36: - servicemesh
37: - logging
38: - database
39: - anotherbackend
40: - name: anotherbackend
41: chart: charts/anotherbackend
42: needs:
43: - servicemesh
44: - logging
45: - database
46: - name: servicemesh
47: chart: charts/istio
48: needs:
49: - logging
50: - name: logging
51: chart: charts/fluent-bit
52: - name: front-proxy
53: chart: stable/envoy
54:
first-pass produced: &{default map[] map[]}
first-pass rendering result of "helmfile.yaml.part.0": {default map[] map[]}
vals:
map[]
defaultVals:[]
second-pass rendering result of "helmfile.yaml.part.0":
0:
1: releases:
2: - name: database
3: chart: charts/mysql
4: needs:
5: - logging
6: - name: frontend-v1
7: chart: charts/frontend
8: installed: false
9: needs:
10: - servicemesh
11: - logging
12: - backend-v1
13: - name: frontend-v2
14: chart: charts/frontend
15: needs:
16: - servicemesh
17: - logging
18: - backend-v2
19: - name: frontend-v3
20: chart: charts/frontend
21: needs:
22: - servicemesh
23: - logging
24: - backend-v2
25: - name: backend-v1
26: chart: charts/backend
27: installed: false
28: needs:
29: - servicemesh
30: - logging
31: - database
32: - anotherbackend
33: - name: backend-v2
34: chart: charts/backend
35: needs:
36: - servicemesh
37: - logging
38: - database
39: - anotherbackend
40: - name: anotherbackend
41: chart: charts/anotherbackend
42: needs:
43: - servicemesh
44: - logging
45: - database
46: - name: servicemesh
47: chart: charts/istio
48: needs:
49: - logging
50: - name: logging
51: chart: charts/fluent-bit
52: - name: front-proxy
53: chart: stable/envoy
54:
merged environment: &{default map[] map[]}
10 release(s) found in helmfile.yaml
processing 5 groups of releases in this order:
GROUP RELEASES
1 frontend-v3, frontend-v2, frontend-v1
2 backend-v2, backend-v1
3 anotherbackend
4 database, servicemesh
5 front-proxy, logging
processing releases in group 1/5: frontend-v3, frontend-v2, frontend-v1
release "frontend-v3" processed
release "frontend-v2" processed
release "frontend-v1" processed
processing releases in group 2/5: backend-v2, backend-v1
release "backend-v2" processed
release "backend-v1" processed
processing releases in group 3/5: anotherbackend
release "anotherbackend" processed
processing releases in group 4/5: database, servicemesh
release "database" processed
release "servicemesh" processed
processing releases in group 5/5: front-proxy, logging
release "front-proxy" processed
release "logging" processed
DELETED RELEASES:
NAME
frontend-v3
frontend-v2
frontend-v1
backend-v2
backend-v1
anotherbackend
database
servicemesh
front-proxy
logging
`,
},
}
for i := range testcases {
tc := testcases[i]
t.Run(tc.name, func(t *testing.T) {
wantUpgrades := tc.upgraded
wantDeletes := tc.deleted
var helm = &exectest.Helm{
FailOnUnexpectedList: true,
FailOnUnexpectedDiff: true,
Lists: tc.lists,
Diffs: tc.diffs,
DiffMutex: &sync.Mutex{},
ChartsMutex: &sync.Mutex{},
ReleasesMutex: &sync.Mutex{},
}
bs := &bytes.Buffer{}
func() {
logReader, logWriter := io.Pipe()
logFlushed := &sync.WaitGroup{}
// Ensure all the log is consumed into `bs` by calling `logWriter.Close()` followed by `logFlushed.Wait()`
logFlushed.Add(1)
go func() {
scanner := bufio.NewScanner(logReader)
for scanner.Scan() {
bs.Write(scanner.Bytes())
bs.WriteString("\n")
}
logFlushed.Done()
}()
defer func() {
// This is here to avoid data-trace on bytes buffer `bs` to capture logs
if err := logWriter.Close(); err != nil {
panic(err)
}
logFlushed.Wait()
}()
logger := helmexec.NewLogger(logWriter, "debug")
valsRuntime, err := vals.New(vals.Options{CacheSize: 32})
if err != nil {
t.Errorf("unexpected error creating vals runtime: %v", err)
}
app := appWithFs(&App{
OverrideHelmBinary: DefaultHelmBinary,
glob: filepath.Glob,
abs: filepath.Abs,
OverrideKubeContext: "default",
Env: "default",
Logger: logger,
helms: map[helmKey]helmexec.Interface{
createHelmKey("helm", "default"): helm,
},
valsRuntime: valsRuntime,
}, tc.files)
if tc.ns != "" {
app.Namespace = tc.ns
}
if tc.selectors != nil {
app.Selectors = tc.selectors
}
destroyErr := app.Destroy(destroyConfig{
// if we check log output, concurrency must be 1. otherwise the test becomes non-deterministic.
concurrency: tc.concurrency,
logger: logger,
})
if tc.error == "" && destroyErr != nil {
t.Fatalf("unexpected error for data defined at %s: %v", tc.loc, destroyErr)
} else if tc.error != "" && destroyErr == nil {
t.Fatalf("expected error did not occur for data defined at %s", tc.loc)
} else if tc.error != "" && destroyErr != nil && tc.error != destroyErr.Error() {
t.Fatalf("invalid error: expected %q, got %q", tc.error, destroyErr.Error())
}
if len(wantUpgrades) > len(helm.Releases) {
t.Fatalf("insufficient number of upgrades: got %d, want %d", len(helm.Releases), len(wantUpgrades))
}
for relIdx := range wantUpgrades {
if wantUpgrades[relIdx].Name != helm.Releases[relIdx].Name {
t.Errorf("releases[%d].name: got %q, want %q", relIdx, helm.Releases[relIdx].Name, wantUpgrades[relIdx].Name)
}
for flagIdx := range wantUpgrades[relIdx].Flags {
if wantUpgrades[relIdx].Flags[flagIdx] != helm.Releases[relIdx].Flags[flagIdx] {
t.Errorf("releaes[%d].flags[%d]: got %v, want %v", relIdx, flagIdx, helm.Releases[relIdx].Flags[flagIdx], wantUpgrades[relIdx].Flags[flagIdx])
}
}
}
if len(wantDeletes) > len(helm.Deleted) {
t.Fatalf("insufficient number of deletes: got %d, want %d", len(helm.Deleted), len(wantDeletes))
}
for relIdx := range wantDeletes {
if wantDeletes[relIdx].Name != helm.Deleted[relIdx].Name {
t.Errorf("releases[%d].name: got %q, want %q", relIdx, helm.Deleted[relIdx].Name, wantDeletes[relIdx].Name)
}
for flagIdx := range wantDeletes[relIdx].Flags {
if wantDeletes[relIdx].Flags[flagIdx] != helm.Deleted[relIdx].Flags[flagIdx] {
t.Errorf("releaes[%d].flags[%d]: got %v, want %v", relIdx, flagIdx, helm.Deleted[relIdx].Flags[flagIdx], wantDeletes[relIdx].Flags[flagIdx])
}
}
}
}()
if tc.log != "" {
actual := bs.String()
diff, exists := testhelper.Diff(tc.log, actual, 3)
if exists {
t.Errorf("unexpected log for data defined %s:\nDIFF\n%s\nEOD", tc.loc, diff)
}
}
})
}
}
+1292
View File
File diff suppressed because it is too large Load Diff
+33
View File
@@ -0,0 +1,33 @@
package app
import (
"encoding/json"
"fmt"
"github.com/gosuri/uitable"
)
func FormatAsTable(releases []*HelmRelease) error {
table := uitable.New()
table.AddRow("NAME", "NAMESPACE", "ENABLED", "LABELS")
for _, r := range releases {
table.AddRow(r.Name, r.Namespace, fmt.Sprintf("%t", r.Enabled), r.Labels)
}
fmt.Println(table.String())
return nil
}
func FormatAsJson(releases []*HelmRelease) error {
output, err := json.Marshal(releases)
if err != nil {
return fmt.Errorf("error generating json: %v", err)
}
fmt.Println(string(output))
return nil
}
+4
View File
@@ -9,8 +9,12 @@ type LoadOpts struct {
Selectors []string
Environment state.SubhelmfileEnvironmentSpec
RetainValuesFiles bool
// CalleePath is the absolute path to the file being loaded
CalleePath string
Reverse bool
}
func (o LoadOpts) DeepCopy() LoadOpts {
+103
View File
@@ -0,0 +1,103 @@
package app
import "github.com/roboll/helmfile/pkg/helmexec"
type noCallHelmExec struct {
}
type versionOnlyHelmExec struct {
*noCallHelmExec
isHelm3 bool
}
func (helm *versionOnlyHelmExec) IsHelm3() bool {
return helm.isHelm3
}
func (helm *noCallHelmExec) doPanic() {
panic("unexpected call to helm")
}
func (helm *noCallHelmExec) TemplateRelease(name, chart string, flags ...string) error {
helm.doPanic()
return nil
}
func (helm *noCallHelmExec) UpdateDeps(chart string) error {
helm.doPanic()
return nil
}
func (helm *noCallHelmExec) BuildDeps(name, chart string) error {
helm.doPanic()
return nil
}
func (helm *noCallHelmExec) SetExtraArgs(args ...string) {
helm.doPanic()
return
}
func (helm *noCallHelmExec) SetHelmBinary(bin string) {
helm.doPanic()
return
}
func (helm *noCallHelmExec) AddRepo(name, repository, cafile, certfile, keyfile, username, password string) error {
helm.doPanic()
return nil
}
func (helm *noCallHelmExec) UpdateRepo() error {
helm.doPanic()
return nil
}
func (helm *noCallHelmExec) SyncRelease(context helmexec.HelmContext, name, chart string, flags ...string) error {
helm.doPanic()
return nil
}
func (helm *noCallHelmExec) DiffRelease(context helmexec.HelmContext, name, chart string, suppressDiff bool, flags ...string) error {
helm.doPanic()
return nil
}
func (helm *noCallHelmExec) ReleaseStatus(context helmexec.HelmContext, release string, flags ...string) error {
helm.doPanic()
return nil
}
func (helm *noCallHelmExec) DeleteRelease(context helmexec.HelmContext, name string, flags ...string) error {
helm.doPanic()
return nil
}
func (helm *noCallHelmExec) List(context helmexec.HelmContext, filter string, flags ...string) (string, error) {
helm.doPanic()
return "", nil
}
func (helm *noCallHelmExec) DecryptSecret(context helmexec.HelmContext, name string, flags ...string) (string, error) {
helm.doPanic()
return "", nil
}
func (helm *noCallHelmExec) TestRelease(context helmexec.HelmContext, name string, flags ...string) error {
helm.doPanic()
return nil
}
func (helm *noCallHelmExec) Fetch(chart string, flags ...string) error {
helm.doPanic()
return nil
}
func (helm *noCallHelmExec) Lint(name, chart string, flags ...string) error {
helm.doPanic()
return nil
}
func (helm *noCallHelmExec) IsHelm3() bool {
helm.doPanic()
return false
}
func (helm *noCallHelmExec) GetVersion() helmexec.Version {
helm.doPanic()
return helmexec.Version{}
}
func (helm *noCallHelmExec) IsVersionAtLeast(major int, minor int) bool {
helm.doPanic()
return false
}
+172 -18
View File
@@ -1,9 +1,14 @@
package app
import (
"fmt"
"github.com/roboll/helmfile/pkg/argparser"
"github.com/roboll/helmfile/pkg/helmexec"
"github.com/roboll/helmfile/pkg/state"
"io/ioutil"
"os"
"sort"
"strings"
)
type Run struct {
@@ -11,6 +16,8 @@ type Run struct {
helm helmexec.Interface
ctx Context
ReleaseToChart map[string]string
Ask func(string) bool
}
@@ -25,15 +32,55 @@ func (r *Run) askForConfirmation(msg string) bool {
return AskForConfirmation(msg)
}
func (r *Run) Deps(c DepsConfigProvider) []error {
r.helm.SetExtraArgs(argparser.GetArgs(c.Args(), r.state)...)
if !c.SkipRepos() {
if errs := r.ctx.SyncReposOnce(r.state, r.helm); errs != nil && len(errs) > 0 {
func (r *Run) withReposAndPreparedCharts(forceDownload bool, skipRepos bool, f func()) []error {
if !skipRepos {
ctx := r.ctx
if errs := ctx.SyncReposOnce(r.state, r.helm); errs != nil && len(errs) > 0 {
return errs
}
}
if err := r.withPreparedCharts(forceDownload, f); err != nil {
return []error{err}
}
return nil
}
func (r *Run) withPreparedCharts(forceDownload bool, f func()) error {
if r.ReleaseToChart != nil {
panic("Run.PrepareCharts can be called only once")
}
// Create tmp directory and bail immediately if it fails
dir, err := ioutil.TempDir("", "")
if err != nil {
return err
}
defer os.RemoveAll(dir)
releaseToChart, errs := state.PrepareCharts(r.helm, r.state, dir, 2, "template", forceDownload)
if len(errs) > 0 {
return fmt.Errorf("%v", errs)
}
for i := range r.state.Releases {
rel := &r.state.Releases[i]
rel.Chart = releaseToChart[rel.Name]
}
r.ReleaseToChart = releaseToChart
f()
return nil
}
func (r *Run) Deps(c DepsConfigProvider) []error {
r.helm.SetExtraArgs(argparser.GetArgs(c.Args(), r.state)...)
return r.state.UpdateDeps(r.helm)
}
@@ -61,21 +108,32 @@ func (r *Run) Status(c StatusesConfigProvider) []error {
return r.state.ReleaseStatuses(r.helm, workers)
}
func (r *Run) Diff(c DiffConfigProvider) []error {
func (r *Run) Diff(c DiffConfigProvider) (*string, bool, bool, []error) {
st := r.state
helm := r.helm
ctx := r.ctx
allReleases := st.GetReleasesWithOverrides()
toDiff, err := st.GetSelectedReleasesWithOverrides()
if err != nil {
return nil, false, false, []error{err}
}
if len(toDiff) == 0 {
return nil, false, false, nil
}
// Do build deps and prepare only on selected releases so that we won't waste time
// on running various helm commands on unnecessary releases
st.Releases = toDiff
if !c.SkipDeps() {
if errs := ctx.SyncReposOnce(st, helm); errs != nil && len(errs) > 0 {
return errs
}
if errs := st.BuildDeps(helm); errs != nil && len(errs) > 0 {
return errs
return nil, false, false, errs
}
}
if errs := st.PrepareReleases(helm, "diff"); errs != nil && len(errs) > 0 {
return errs
return nil, false, false, errs
}
r.helm.SetExtraArgs(argparser.GetArgs(c.Args(), r.state)...)
@@ -85,8 +143,28 @@ func (r *Run) Diff(c DiffConfigProvider) []error {
NoColor: c.NoColor(),
Set: c.Set(),
}
_, errs := st.DiffReleases(helm, c.Values(), c.Concurrency(), c.DetailedExitcode(), c.SuppressSecrets(), true, opts)
return errs
// Validate all releases for missing `needs` targets
st.Releases = allReleases
if _, err := st.PlanReleases(false); err != nil {
return nil, false, false, []error{err}
}
// Diff only targeted releases
st.Releases = toDiff
filtered := &Run{
state: st,
helm: r.helm,
ctx: r.ctx,
Ask: r.Ask,
}
infoMsg, updated, deleted, errs := filtered.diff(true, c.DetailedExitcode(), c, opts)
return infoMsg, true, len(deleted) > 0 || len(updated) > 0, errs
}
func (r *Run) Test(c TestConfigProvider) []error {
@@ -102,15 +180,11 @@ func (r *Run) Test(c TestConfigProvider) []error {
func (r *Run) Lint(c LintConfigProvider) []error {
st := r.state
helm := r.helm
ctx := r.ctx
values := c.Values()
args := argparser.GetArgs(c.Args(), st)
workers := c.Concurrency()
if !c.SkipDeps() {
if errs := ctx.SyncReposOnce(st, helm); errs != nil && len(errs) > 0 {
return errs
}
if errs := st.BuildDeps(helm); errs != nil && len(errs) > 0 {
return errs
}
@@ -123,3 +197,83 @@ func (r *Run) Lint(c LintConfigProvider) []error {
}
return st.LintReleases(helm, values, args, workers, opts)
}
func (run *Run) diff(triggerCleanupEvent bool, detailedExitCode bool, c DiffConfigProvider, diffOpts *state.DiffOpts) (*string, map[string]state.ReleaseSpec, map[string]state.ReleaseSpec, []error) {
st := run.state
helm := run.helm
var changedReleases []state.ReleaseSpec
var deletingReleases []state.ReleaseSpec
var planningErrs []error
// TODO Better way to detect diff on only filtered releases
{
changedReleases, planningErrs = st.DiffReleases(helm, c.Values(), c.Concurrency(), detailedExitCode, c.IncludeTests(), c.SuppressSecrets(), c.SuppressDiff(), triggerCleanupEvent, diffOpts)
var err error
deletingReleases, err = st.DetectReleasesToBeDeletedForSync(helm, st.Releases)
if err != nil {
planningErrs = append(planningErrs, err)
}
}
fatalErrs := []error{}
for _, e := range planningErrs {
switch err := e.(type) {
case *state.ReleaseError:
if err.Code != 2 {
fatalErrs = append(fatalErrs, e)
}
default:
fatalErrs = append(fatalErrs, e)
}
}
if len(fatalErrs) > 0 {
return nil, nil, nil, fatalErrs
}
releasesToBeDeleted := map[string]state.ReleaseSpec{}
for _, r := range deletingReleases {
id := state.ReleaseToID(&r)
releasesToBeDeleted[id] = r
}
releasesToBeUpdated := map[string]state.ReleaseSpec{}
for _, r := range changedReleases {
id := state.ReleaseToID(&r)
// If `helm-diff` detected changes but it is not being `helm delete`ed, we should run `helm upgrade`
if _, ok := releasesToBeDeleted[id]; !ok {
releasesToBeUpdated[id] = r
}
}
// sync only when there are changes
if len(releasesToBeUpdated) == 0 && len(releasesToBeDeleted) == 0 {
var msg *string
if c.DetailedExitcode() {
// TODO better way to get the logger
m := "No affected releases"
msg = &m
}
return msg, nil, nil, nil
}
names := []string{}
for _, r := range releasesToBeUpdated {
names = append(names, fmt.Sprintf(" %s (%s) UPDATED", r.Name, r.Chart))
}
for _, r := range releasesToBeDeleted {
names = append(names, fmt.Sprintf(" %s (%s) DELETED", r.Name, r.Chart))
}
// Make the output deterministic for testing purpose
sort.Strings(names)
infoMsg := fmt.Sprintf(`Affected releases are:
%s
`, strings.Join(names, "\n"))
return &infoMsg, releasesToBeUpdated, releasesToBeDeleted, nil
}
+5 -7
View File
@@ -116,15 +116,13 @@ func (r *desiredStateLoader) twoPassRenderTemplateToYaml(inherited, overrode *en
r.logger.Debugf("first-pass rendering result of \"%s\": %v", filename, *finalEnv)
}
vals := map[string]interface{}{}
vals, err := finalEnv.GetMergedValues()
if err != nil {
return nil, err
}
if prestate != nil {
prestate.Env = *finalEnv
vals, err = prestate.Values()
if err != nil {
return nil, err
}
}
if prestate != nil {
r.logger.Debugf("vals:\n%v\ndefaultVals:%v", vals, prestate.DefaultValues)
}
+3
View File
@@ -0,0 +1,3 @@
package version
var Version string
+20 -1
View File
@@ -58,9 +58,28 @@ func (e *Environment) Merge(other *Environment) (*Environment, error) {
}
copy := e.DeepCopy()
if other != nil {
if err := mergo.Merge(&copy, other, mergo.WithOverride); err != nil {
if err := mergo.Merge(&copy, other, mergo.WithOverride, mergo.WithOverwriteWithEmptyValue); err != nil {
return nil, err
}
}
return &copy, nil
}
func (e *Environment) GetMergedValues() (map[string]interface{}, error) {
vals := map[string]interface{}{}
if err := mergo.Merge(&vals, e.Defaults, mergo.WithOverride, mergo.WithOverwriteWithEmptyValue); err != nil {
return nil, err
}
if err := mergo.Merge(&vals, e.Values, mergo.WithOverride, mergo.WithOverwriteWithEmptyValue); err != nil {
return nil, err
}
vals, err := maputil.CastKeysToStrings(vals)
if err != nil {
return nil, err
}
return vals, nil
}
+98
View File
@@ -0,0 +1,98 @@
package environment
import (
"github.com/google/go-cmp/cmp"
"testing"
)
// See https://github.com/roboll/helmfile/issues/1150
func TestMerge_OverwriteNilValue_Issue1150(t *testing.T) {
dst := &Environment{
Name: "dst",
Values: map[string]interface{}{
"components": map[string]interface{}{
"etcd-operator": nil,
},
},
Defaults: nil,
}
src := &Environment{
Name: "src",
Values: map[string]interface{}{
"components": map[string]interface{}{
"etcd-operator": map[string]interface{}{
"version": "0.10.3",
},
},
},
Defaults: nil,
}
merged, err := dst.Merge(src)
if err != nil {
t.Fatal(err)
}
actual := merged.Values
expected := map[string]interface{}{
"components": map[string]interface{}{
"etcd-operator": map[string]interface{}{
"version": "0.10.3",
},
},
}
if diff := cmp.Diff(expected, actual); diff != "" {
t.Errorf(diff)
}
}
// See https://github.com/roboll/helmfile/issues/1154
func TestMerge_OverwriteWithNilValue_Issue1154(t *testing.T) {
dst := &Environment{
Name: "dst",
Values: map[string]interface{}{
"components": map[string]interface{}{
"etcd-operator": map[string]interface{}{
"version": "0.10.0",
},
},
},
Defaults: nil,
}
src := &Environment{
Name: "src",
Values: map[string]interface{}{
"components": map[string]interface{}{
"etcd-operator": map[string]interface{}{
"version": "0.10.3",
},
"prometheus": nil,
},
},
Defaults: nil,
}
merged, err := dst.Merge(src)
if err != nil {
t.Fatal(err)
}
actual := merged.Values
expected := map[string]interface{}{
"components": map[string]interface{}{
"etcd-operator": map[string]interface{}{
"version": "0.10.3",
},
"prometheus": nil,
},
}
if diff := cmp.Diff(expected, actual); diff != "" {
t.Errorf(diff)
}
}
+18 -1
View File
@@ -30,6 +30,7 @@ type Helm struct {
Diffed []Release
FailOnUnexpectedDiff bool
FailOnUnexpectedList bool
Version *helmexec.Version
UpdateDepsCallbacks map[string]func(string) error
@@ -100,7 +101,7 @@ func (helm *Helm) SyncRelease(context helmexec.HelmContext, name, chart string,
return nil
}
func (helm *Helm) DiffRelease(context helmexec.HelmContext, name, chart string, flags ...string) error {
func (helm *Helm) DiffRelease(context helmexec.HelmContext, name, chart string, suppressDiff bool, flags ...string) error {
if helm.DiffMutex != nil {
helm.DiffMutex.Lock()
}
@@ -161,6 +162,22 @@ func (helm *Helm) IsHelm3() bool {
return false
}
func (helm *Helm) GetVersion() helmexec.Version {
if helm.Version != nil {
return *helm.Version
}
return helmexec.Version{}
}
func (helm *Helm) IsVersionAtLeast(major int, minor int) bool {
if helm.Version == nil {
return false
}
return helm.Version.Major >= major && minor >= helm.Version.Minor
}
func (helm *Helm) sync(m *sync.Mutex, f func()) {
if m != nil {
m.Lock()
+5 -4
View File
@@ -8,15 +8,16 @@ import (
type HelmContext struct {
Tillerless bool
TillerNamespace string
HistoryMax int
WorkerIndex int
}
func (context *HelmContext) GetTillerlessArgs(helmBinary string) []string {
if context.Tillerless {
func (context *HelmContext) GetTillerlessArgs(helm *execer) []string {
if context.Tillerless && !helm.IsHelm3() {
if context.TillerNamespace != "" {
return []string{"tiller", "run", context.TillerNamespace, "--", helmBinary}
return []string{"tiller", "run", context.TillerNamespace, "--", helm.helmBinary}
} else {
return []string{"tiller", "run", "--", helmBinary}
return []string{"tiller", "run", "--", helm.helmBinary}
}
} else {
return []string{}
+80 -26
View File
@@ -6,6 +6,8 @@ import (
"io/ioutil"
"os"
"path/filepath"
"regexp"
"strconv"
"strings"
"sync"
@@ -20,7 +22,7 @@ type decryptedSecret struct {
type execer struct {
helmBinary string
isHelm3 bool
version Version
runner Runner
logger *zap.SugaredLogger
kubeContext string
@@ -46,31 +48,67 @@ func NewLogger(writer io.Writer, logLevel string) *zap.SugaredLogger {
return zap.New(core).Sugar()
}
func detectHelm3(helmBinary string, logger *zap.SugaredLogger, runner Runner) bool {
// Support explicit opt-in via environment variable
if os.Getenv("HELMFILE_HELM3") != "" {
return true
}
func getHelmVersion(helmBinary string, logger *zap.SugaredLogger, runner Runner) Version {
// Autodetect from `helm verison`
bytes, err := runner.Execute(helmBinary, []string{"version", "--client", "--short"}, nil)
if err != nil {
panic(err)
}
return strings.HasPrefix(string(bytes), "v3.")
if bytes == nil || len(bytes) == 0 {
return Version{}
}
re := regexp.MustCompile("v(?P<major>\\d+)\\.(?P<minor>\\d+)\\.(?P<patch>\\d+)")
matches := re.FindStringSubmatch(string(bytes))
result := make(map[string]string)
for i, name := range re.SubexpNames() {
result[name] = matches[i]
}
major, err := strconv.Atoi(result["major"])
if err != nil {
panic(err)
}
minor, err := strconv.Atoi(result["minor"])
if err != nil {
panic(err)
}
patch, err := strconv.Atoi(result["patch"])
if err != nil {
panic(err)
}
// Support explicit helm3 opt-in via environment variable
if os.Getenv("HELMFILE_HELM3") != "" && major < 3 {
return Version{
Major: 3,
Minor: 0,
Patch: 0,
}
}
return Version{
Major: major,
Minor: minor,
Patch: patch,
}
}
// New for running helm commands
func New(helmBinary string, logger *zap.SugaredLogger, kubeContext string, runner Runner) *execer {
return &execer{
helmBinary: helmBinary,
isHelm3: detectHelm3(helmBinary, logger, runner),
version: getHelmVersion(helmBinary, logger, runner),
logger: logger,
kubeContext: kubeContext,
runner: runner,
decryptedSecrets: make(map[string]*decryptedSecret),
}
}
func (helm *execer) SetExtraArgs(args ...string) {
@@ -83,6 +121,10 @@ func (helm *execer) SetHelmBinary(bin string) {
func (helm *execer) AddRepo(name, repository, cafile, certfile, keyfile, username, password string) error {
var args []string
if name == "" && repository != "" {
helm.logger.Infof("empty field name\n")
return fmt.Errorf("empty field name")
}
args = append(args, "repo", "add", name, repository)
if certfile != "" && keyfile != "" {
args = append(args, "--cert-file", certfile, "--key-file", keyfile)
@@ -122,8 +164,15 @@ func (helm *execer) UpdateDeps(chart string) error {
func (helm *execer) SyncRelease(context HelmContext, name, chart string, flags ...string) error {
helm.logger.Infof("Upgrading release=%v, chart=%v", name, chart)
preArgs := context.GetTillerlessArgs(helm.helmBinary)
preArgs := context.GetTillerlessArgs(helm)
env := context.getTillerlessEnv()
if helm.IsHelm3() {
flags = append(flags, "--history-max", strconv.Itoa(context.HistoryMax))
} else {
env["HELM_TILLER_HISTORY_MAX"] = strconv.Itoa(context.HistoryMax)
}
out, err := helm.exec(append(append(preArgs, "upgrade", "--install", "--reset-values", name, chart), flags...), env)
helm.write(out)
return err
@@ -131,7 +180,7 @@ func (helm *execer) SyncRelease(context HelmContext, name, chart string, flags .
func (helm *execer) ReleaseStatus(context HelmContext, name string, flags ...string) error {
helm.logger.Infof("Getting status %v", name)
preArgs := context.GetTillerlessArgs(helm.helmBinary)
preArgs := context.GetTillerlessArgs(helm)
env := context.getTillerlessEnv()
out, err := helm.exec(append(append(preArgs, "status", name), flags...), env)
helm.write(out)
@@ -140,7 +189,7 @@ func (helm *execer) ReleaseStatus(context HelmContext, name string, flags ...str
func (helm *execer) List(context HelmContext, filter string, flags ...string) (string, error) {
helm.logger.Infof("Listing releases matching %v", filter)
preArgs := context.GetTillerlessArgs(helm.helmBinary)
preArgs := context.GetTillerlessArgs(helm)
env := context.getTillerlessEnv()
var args []string
if helm.IsHelm3() {
@@ -187,7 +236,7 @@ func (helm *execer) DecryptSecret(context HelmContext, name string, flags ...str
helm.decryptedSecretMutex.Unlock()
helm.logger.Infof("Decrypting secret %v", absPath)
preArgs := context.GetTillerlessArgs(helm.helmBinary)
preArgs := context.GetTillerlessArgs(helm)
env := context.getTillerlessEnv()
out, err := helm.exec(append(append(preArgs, "secrets", "dec", absPath), flags...), env)
helm.info(out)
@@ -247,9 +296,9 @@ func (helm *execer) TemplateRelease(name string, chart string, flags ...string)
return err
}
func (helm *execer) DiffRelease(context HelmContext, name, chart string, flags ...string) error {
func (helm *execer) DiffRelease(context HelmContext, name, chart string, suppressDiff bool, flags ...string) error {
helm.logger.Infof("Comparing release=%v, chart=%v", name, chart)
preArgs := context.GetTillerlessArgs(helm.helmBinary)
preArgs := context.GetTillerlessArgs(helm)
env := context.getTillerlessEnv()
out, err := helm.exec(append(append(preArgs, "diff", "upgrade", "--reset-values", "--allow-unreleased", name, chart), flags...), env)
// Do our best to write STDOUT only when diff existed
@@ -265,11 +314,13 @@ func (helm *execer) DiffRelease(context HelmContext, name, chart string, flags .
switch e := err.(type) {
case ExitError:
if e.ExitStatus() == 2 {
helm.write(out)
if !(suppressDiff) {
helm.write(out)
}
return err
}
}
} else {
} else if !(suppressDiff) {
helm.write(out)
}
return err
@@ -291,7 +342,7 @@ func (helm *execer) Fetch(chart string, flags ...string) error {
func (helm *execer) DeleteRelease(context HelmContext, name string, flags ...string) error {
helm.logger.Infof("Deleting %v", name)
preArgs := context.GetTillerlessArgs(helm.helmBinary)
preArgs := context.GetTillerlessArgs(helm)
env := context.getTillerlessEnv()
out, err := helm.exec(append(append(preArgs, "delete", name), flags...), env)
helm.write(out)
@@ -300,14 +351,9 @@ func (helm *execer) DeleteRelease(context HelmContext, name string, flags ...str
func (helm *execer) TestRelease(context HelmContext, name string, flags ...string) error {
helm.logger.Infof("Testing %v", name)
preArgs := context.GetTillerlessArgs(helm.helmBinary)
preArgs := context.GetTillerlessArgs(helm)
env := context.getTillerlessEnv()
var args []string
if helm.IsHelm3() {
args = []string{"test", "run", name}
} else {
args = []string{"test", name}
}
args := []string{"test", name}
out, err := helm.exec(append(append(preArgs, args...), flags...), env)
helm.write(out)
return err
@@ -341,5 +387,13 @@ func (helm *execer) write(out []byte) {
}
func (helm *execer) IsHelm3() bool {
return helm.isHelm3
return helm.version.Major == 3
}
func (helm *execer) GetVersion() Version {
return helm.version
}
func (helm *execer) IsVersionAtLeast(major int, minor int) bool {
return helm.version.Major >= major && helm.version.Minor >= minor
}
+53 -3
View File
@@ -109,6 +109,15 @@ exec: helm repo add myRepo https://repo.example.com/ --kube-context dev:
expected = `Adding repo myRepo https://repo.example.com/
exec: helm repo add myRepo https://repo.example.com/ --username example_user --password example_password --kube-context dev
exec: helm repo add myRepo https://repo.example.com/ --username example_user --password example_password --kube-context dev:
`
if buffer.String() != expected {
t.Errorf("helmexec.AddRepo()\nactual = %v\nexpect = %v", buffer.String(), expected)
}
buffer.Reset()
helm.AddRepo("", "https://repo.example.com/", "", "", "", "", "")
expected = `empty field name
`
if buffer.String() != expected {
t.Errorf("helmexec.AddRepo()\nactual = %v\nexpect = %v", buffer.String(), expected)
@@ -246,7 +255,7 @@ func Test_DiffRelease(t *testing.T) {
var buffer bytes.Buffer
logger := NewLogger(&buffer, "debug")
helm := MockExecer(logger, "dev")
helm.DiffRelease(HelmContext{}, "release", "chart", "--timeout 10", "--wait")
helm.DiffRelease(HelmContext{}, "release", "chart", false, "--timeout 10", "--wait")
expected := `Comparing release=release, chart=chart
exec: helm diff upgrade --reset-values --allow-unreleased release chart --timeout 10 --wait --kube-context dev
exec: helm diff upgrade --reset-values --allow-unreleased release chart --timeout 10 --wait --kube-context dev:
@@ -256,7 +265,7 @@ exec: helm diff upgrade --reset-values --allow-unreleased release chart --timeou
}
buffer.Reset()
helm.DiffRelease(HelmContext{}, "release", "chart")
helm.DiffRelease(HelmContext{}, "release", "chart", false)
expected = `Comparing release=release, chart=chart
exec: helm diff upgrade --reset-values --allow-unreleased release chart --kube-context dev
exec: helm diff upgrade --reset-values --allow-unreleased release chart --kube-context dev:
@@ -270,7 +279,7 @@ func Test_DiffReleaseTillerless(t *testing.T) {
var buffer bytes.Buffer
logger := NewLogger(&buffer, "debug")
helm := MockExecer(logger, "dev")
helm.DiffRelease(HelmContext{Tillerless: true}, "release", "chart", "--timeout 10", "--wait")
helm.DiffRelease(HelmContext{Tillerless: true}, "release", "chart", false, "--timeout 10", "--wait")
expected := `Comparing release=release, chart=chart
exec: helm tiller run -- helm diff upgrade --reset-values --allow-unreleased release chart --timeout 10 --wait --kube-context dev
exec: helm tiller run -- helm diff upgrade --reset-values --allow-unreleased release chart --timeout 10 --wait --kube-context dev:
@@ -519,4 +528,45 @@ func Test_IsHelm3(t *testing.T) {
if !helm.IsHelm3() {
t.Error("helmexec.IsHelm3() - Failed to detect Helm 3")
}
os.Setenv("HELMFILE_HELM3", "1")
helm2Runner = mockRunner{output: []byte("Client: v2.16.0+ge13bc94\n")}
helm = New("helm", NewLogger(os.Stdout, "info"), "dev", &helm2Runner)
if !helm.IsHelm3() {
t.Error("helmexec.IsHelm3() - Helm3 not detected when HELMFILE_HELM3 is set")
}
os.Setenv("HELMFILE_HELM3", "")
}
func Test_GetVersion(t *testing.T) {
helm2Runner := mockRunner{output: []byte("Client: v2.16.1+ge13bc94\n")}
helm := New("helm", NewLogger(os.Stdout, "info"), "dev", &helm2Runner)
ver := helm.GetVersion()
if ver.Major != 2 || ver.Minor != 16 || ver.Patch != 1 {
t.Error(fmt.Sprintf("helmexec.GetVersion - did not detect correct Helm2 version; it was: %+v", ver))
}
helm3Runner := mockRunner{output: []byte("v3.2.4+ge29ce2a\n")}
helm = New("helm", NewLogger(os.Stdout, "info"), "dev", &helm3Runner)
ver = helm.GetVersion()
if ver.Major != 3 || ver.Minor != 2 || ver.Patch != 4 {
t.Error(fmt.Sprintf("helmexec.GetVersion - did not detect correct Helm3 version; it was: %+v", ver))
}
}
func Test_IsVersionAtLeast(t *testing.T) {
helm2Runner := mockRunner{output: []byte("Client: v2.16.1+ge13bc94\n")}
helm := New("helm", NewLogger(os.Stdout, "info"), "dev", &helm2Runner)
if !helm.IsVersionAtLeast(2, 1) {
t.Error("helmexec.IsVersionAtLeast - 2.16.1 not atleast 2.1")
}
if helm.IsVersionAtLeast(2, 19) {
t.Error("helmexec.IsVersionAtLeast - 2.16.1 is atleast 2.19")
}
if helm.IsVersionAtLeast(3, 2) {
t.Error("helmexec.IsVersionAtLeast - 2.16.1 is atleast 3.2")
}
}
+1 -2
View File
@@ -2,13 +2,12 @@ package helmexec
import (
"fmt"
"path/filepath"
"strings"
)
func newExitError(helmCmdPath string, exitStatus int, errorMessage string) ExitError {
return ExitError{
Message: fmt.Sprintf("%s exited with status %d:\n%s", filepath.Base(helmCmdPath), exitStatus, indent(strings.TrimSpace(errorMessage))),
Message: fmt.Sprintf("the following cmd exited with status %d:\n%s\n\n%s", exitStatus, indent(strings.TrimSpace(helmCmdPath)), indent(strings.TrimSpace(errorMessage))),
Code: exitStatus,
}
}
+10 -1
View File
@@ -1,5 +1,12 @@
package helmexec
// Version represents the version of helm
type Version struct {
Major int
Minor int
Patch int
}
// Interface for executing helm commands
type Interface interface {
SetExtraArgs(args ...string)
@@ -10,7 +17,7 @@ type Interface interface {
BuildDeps(name, chart string) error
UpdateDeps(chart string) error
SyncRelease(context HelmContext, name, chart string, flags ...string) error
DiffRelease(context HelmContext, name, chart string, flags ...string) error
DiffRelease(context HelmContext, name, chart string, suppressDiff bool, flags ...string) error
TemplateRelease(name, chart string, flags ...string) error
Fetch(chart string, flags ...string) error
Lint(name, chart string, flags ...string) error
@@ -20,6 +27,8 @@ type Interface interface {
List(context HelmContext, filter string, flags ...string) (string, error)
DecryptSecret(context HelmContext, name string, flags ...string) (string, error)
IsHelm3() bool
GetVersion() Version
IsVersionAtLeast(major int, minor int) bool
}
type DependencyUpdater interface {
+2 -1
View File
@@ -64,7 +64,8 @@ func combinedOutput(c *exec.Cmd, logger *zap.SugaredLogger) ([]byte, error) {
// so that helmfile could return its own exit code accordingly
waitStatus := ee.Sys().(syscall.WaitStatus)
exitStatus := waitStatus.ExitStatus()
err = newExitError(c.Path, exitStatus, string(e))
cmd := fmt.Sprintf("%s %s", c.Path, strings.Join(c.Args, " "))
err = newExitError(cmd, exitStatus, string(e))
default:
panic(fmt.Sprintf("unexpected error: %v", err))
}
+123 -23
View File
@@ -1,6 +1,9 @@
package maputil
import "fmt"
import (
"fmt"
"strings"
)
func CastKeysToStrings(s interface{}) (map[string]interface{}, error) {
new := map[string]interface{}{}
@@ -60,32 +63,129 @@ func recursivelyStringifyMapKey(v interface{}) (interface{}, error) {
return casted_v, nil
}
func Set(m map[string]interface{}, key []string, value string) map[string]interface{} {
type arg interface {
getMap(map[string]interface{}) map[string]interface{}
set(map[string]interface{}, string)
}
type keyArg struct {
key string
}
func (a keyArg) getMap(m map[string]interface{}) map[string]interface{} {
_, ok := m[a.key]
if !ok {
m[a.key] = map[string]interface{}{}
}
switch t := m[a.key].(type) {
case map[string]interface{}:
return t
default:
panic(fmt.Errorf("unexpected type: %v(%T)", t, t))
}
}
func (a keyArg) set(m map[string]interface{}, value string) {
m[a.key] = value
}
type indexedKeyArg struct {
key string
index int
}
func (a indexedKeyArg) getArray(m map[string]interface{}) []interface{} {
_, ok := m[a.key]
if !ok {
m[a.key] = make([]interface{}, a.index+1)
}
switch t := m[a.key].(type) {
case []interface{}:
if len(t) <= a.index {
t2 := make([]interface{}, a.index+1)
copy(t, t2)
t = t2
}
return t
default:
panic(fmt.Errorf("unexpected type: %v(%T)", t, t))
}
}
func (a indexedKeyArg) getMap(m map[string]interface{}) map[string]interface{} {
t := a.getArray(m)
if t[a.index] == nil {
t[a.index] = map[string]interface{}{}
}
switch t := t[a.index].(type) {
case map[string]interface{}:
return t
default:
panic(fmt.Errorf("unexpected type: %v(%T)", t, t))
}
}
func (a indexedKeyArg) set(m map[string]interface{}, value string) {
t := a.getArray(m)
t[a.index] = value
m[a.key] = t
}
func getCursor(key string) arg {
key = strings.ReplaceAll(key, "[", " ")
key = strings.ReplaceAll(key, "]", " ")
k := key
idx := 0
n, err := fmt.Sscanf(key, "%s %d", &k, &idx)
if n == 2 && err == nil {
return indexedKeyArg{
key: k,
index: idx,
}
}
return keyArg{
key: key,
}
}
func ParseKey(key string) []string {
r := []string{}
part := ""
escaped := false
for _, rune := range key {
split := false
switch {
case escaped == false && rune == '\\':
escaped = true
continue
case rune == '.':
split = escaped == false
}
escaped = false
if split {
r = append(r, part)
part = ""
} else {
part = part + string(rune)
}
}
if len(part) > 0 {
r = append(r, part)
}
return r
}
func Set(m map[string]interface{}, key []string, value string) {
if len(key) == 0 {
panic(fmt.Errorf("bug: unexpected length of key: %d", len(key)))
}
k := key[0]
if len(key) == 1 {
m[k] = value
return m
for len(key) > 1 {
m, key = getCursor(key[0]).getMap(m), key[1:]
}
remain := key[1:]
nested, ok := m[k]
if !ok {
nested = map[string]interface{}{}
}
switch t := nested.(type) {
case map[string]interface{}:
nested = Set(t, remain, value)
default:
panic(fmt.Errorf("unexpected type: %v(%T)", t, t))
}
m[k] = nested
return m
getCursor(key[0]).set(m, value)
}
+69
View File
@@ -59,3 +59,72 @@ func TestMapUtil_IFKeys(t *testing.T) {
t.Errorf("unexpected c: expected=C, got=%s", c)
}
}
func TestMapUtil_KeyArg(t *testing.T) {
m := map[string]interface{}{}
key := []string{"a", "b", "c"}
Set(m, key, "C")
c := (((m["a"].(map[string]interface{}))["b"]).(map[string]interface{}))["c"]
if c != "C" {
t.Errorf("unexpected c: expected=C, got=%s", c)
}
}
func TestMapUtil_IndexedKeyArg(t *testing.T) {
m := map[string]interface{}{}
key := []string{"a", "b[0]", "c"}
Set(m, key, "C")
c := (((m["a"].(map[string]interface{}))["b"].([]interface{}))[0].(map[string]interface{}))["c"]
if c != "C" {
t.Errorf("unexpected c: expected=C, got=%s", c)
}
}
type parseKeyTc struct {
key string
result map[int]string
}
func TestMapUtil_ParseKey(t *testing.T) {
tcs := []parseKeyTc{
{
key: `a.b.c`,
result: map[int]string{
0: "a",
1: "b",
2: "c",
},
},
{
key: `a\.b.c`,
result: map[int]string{
0: "a.b",
1: "c",
},
},
{
key: `a\.b\.c`,
result: map[int]string{
0: "a.b.c",
},
},
}
for _, tc := range tcs {
parts := ParseKey(tc.key)
for index, value := range tc.result {
if parts[index] != value {
t.Errorf("unexpected key part[%d]: expected=%s, got=%s", index, value, parts[index])
}
}
}
}
+23
View File
@@ -0,0 +1,23 @@
package plugins
import (
"github.com/variantdev/vals"
"sync"
)
const (
// cache size for improving performance of ref+.* secrets rendering
valsCacheSize = 512
)
var instance *vals.Runtime
var once sync.Once
func ValsInstance() (*vals.Runtime, error) {
var err error
once.Do(func() {
instance, err = vals.New(vals.Options{CacheSize: valsCacheSize})
})
return instance, err
}
+17
View File
@@ -0,0 +1,17 @@
package plugins
import "testing"
func TestValsInstance(t *testing.T) {
i, err := ValsInstance()
if err != nil {
t.Errorf("unexpected error: %v", err)
}
i2, _ := ValsInstance()
if i != i2 {
t.Error("Instances should be equal")
}
}
+25 -1
View File
@@ -3,7 +3,9 @@ package state
import (
"fmt"
"github.com/Masterminds/semver"
goversion "github.com/hashicorp/go-version"
"github.com/r3labs/diff"
"github.com/roboll/helmfile/pkg/app/version"
"github.com/roboll/helmfile/pkg/helmexec"
"go.uber.org/zap"
"gopkg.in/yaml.v2"
@@ -50,6 +52,7 @@ type ChartRequirements struct {
}
type ChartLockedRequirements struct {
Version string `yaml:"version"`
ResolvedDependencies []ResolvedChartDependency `yaml:"dependencies"`
Digest string `yaml:"digest"`
Generated string `yaml:"generated"`
@@ -288,7 +291,7 @@ func (m *chartDependencyManager) Update(shell helmexec.DependencyUpdater, wd str
func (m *chartDependencyManager) updateHelm3(shell helmexec.DependencyUpdater, wd string, unresolved *UnresolvedDependencies) (*ResolvedDependencies, error) {
// Generate `Chart.yaml` of the temporary local chart
chartMetaContent := fmt.Sprintf("name: %s\nversion: 1.0.0\n", m.Name)
chartMetaContent := fmt.Sprintf("name: %s\nversion: 1.0.0\napiVersion: v2\n", m.Name)
// Generate `requirements.yaml` of the temporary local chart from the helmfile state
reqsContent, err := yaml.Marshal(unresolved.ToChartRequirements())
@@ -374,6 +377,8 @@ func (m *chartDependencyManager) doUpdate(chartLockFile string, unresolved *Unre
}
}
lockedReqs.Version = version.Version
updatedLockFileContent, err = yaml.Marshal(lockedReqs)
if err != nil {
@@ -404,6 +409,25 @@ func (m *chartDependencyManager) Resolve(unresolved *UnresolvedDependencies) (*R
return nil, false, err
}
// Make sure go run main.go works and compatible with old lock files.
if version.Version != "" && lockedReqs.Version != "" {
lockedVersion, err := goversion.NewVersion(lockedReqs.Version)
if err != nil {
return nil, false, err
}
currentVersion, err := goversion.NewVersion(version.Version)
if err != nil {
return nil, false, err
}
if currentVersion.LessThan(lockedVersion) {
return nil, false, fmt.Errorf("the lockfile was created by Helmfile %s, which is newer than current %s; Please upgrade to Helmfile %s or greater", lockedVersion.Original(), currentVersion.Original(), lockedVersion.Original())
}
}
resolved := &ResolvedDependencies{deps: map[string][]ResolvedChartDependency{}}
for _, d := range lockedReqs.ResolvedDependencies {
if err := resolved.add(d); err != nil {
+36 -21
View File
@@ -4,18 +4,22 @@ import (
"bytes"
"errors"
"fmt"
"github.com/roboll/helmfile/pkg/helmexec"
"io"
"os"
"github.com/imdario/mergo"
"github.com/roboll/helmfile/pkg/environment"
"github.com/roboll/helmfile/pkg/helmexec"
"github.com/roboll/helmfile/pkg/maputil"
"github.com/variantdev/vals"
"go.uber.org/zap"
"gopkg.in/yaml.v2"
)
const (
DefaultHelmBinary = "helm"
)
type StateLoadError struct {
msg string
Cause error
@@ -39,15 +43,18 @@ type StateCreator struct {
fileExists func(string) (bool, error)
abs func(string) (string, error)
glob func(string) ([]string, error)
helm helmexec.Interface
valsRuntime vals.Evaluator
Strict bool
LoadFile func(inheritedEnv *environment.Environment, baseDir, file string, evaluateBases bool) (*HelmState, error)
getHelm func(*HelmState) helmexec.Interface
overrideHelmBinary string
}
func NewCreator(logger *zap.SugaredLogger, readFile func(string) ([]byte, error), fileExists func(string) (bool, error), abs func(string) (string, error), glob func(string) ([]string, error), helm helmexec.Interface, valsRuntime vals.Evaluator) *StateCreator {
func NewCreator(logger *zap.SugaredLogger, readFile func(string) ([]byte, error), fileExists func(string) (bool, error), abs func(string) (string, error), glob func(string) ([]string, error), valsRuntime vals.Evaluator, getHelm func(*HelmState) helmexec.Interface, overrideHelmBinary string) *StateCreator {
return &StateCreator{
logger: logger,
readFile: readFile,
@@ -55,8 +62,10 @@ func NewCreator(logger *zap.SugaredLogger, readFile func(string) ([]byte, error)
abs: abs,
glob: glob,
Strict: true,
helm: helm,
valsRuntime: valsRuntime,
getHelm: getHelm,
overrideHelmBinary: overrideHelmBinary,
}
}
@@ -66,7 +75,6 @@ func (c *StateCreator) Parse(content []byte, baseDir, file string) (*HelmState,
state.FilePath = file
state.basePath = baseDir
state.helm = c.helm
decoder := yaml.NewDecoder(bytes.NewReader(content))
if !c.Strict {
@@ -104,6 +112,13 @@ func (c *StateCreator) Parse(content []byte, baseDir, file string) (*HelmState,
state.HelmDefaults.KubeContext = state.DeprecatedContext
}
if c.overrideHelmBinary != "" && c.overrideHelmBinary != DefaultHelmBinary {
state.DefaultHelmBinary = c.overrideHelmBinary
} else if state.DefaultHelmBinary == "" {
// Let `helmfile --helm-binary ""` not break this helmfile run
state.DefaultHelmBinary = DefaultHelmBinary
}
state.logger = c.logger
state.readFile = c.readFile
@@ -116,10 +131,10 @@ func (c *StateCreator) Parse(content []byte, baseDir, file string) (*HelmState,
}
// LoadEnvValues loads environment values files relative to the `baseDir`
func (c *StateCreator) LoadEnvValues(target *HelmState, env string, ctxEnv *environment.Environment) (*HelmState, error) {
func (c *StateCreator) LoadEnvValues(target *HelmState, env string, ctxEnv *environment.Environment, failOnMissingEnv bool) (*HelmState, error) {
state := *target
e, err := state.loadEnvValues(env, ctxEnv, c.readFile, c.glob)
e, err := c.loadEnvValues(&state, env, failOnMissingEnv, ctxEnv, c.readFile, c.glob)
if err != nil {
return nil, &StateLoadError{fmt.Sprintf("failed to read %s", state.FilePath), err}
}
@@ -135,6 +150,7 @@ func (c *StateCreator) LoadEnvValues(target *HelmState, env string, ctxEnv *envi
}
// Parses YAML into HelmState, while loading environment values files relative to the `baseDir`
// evaluateBases=true means that this is NOT a base helmfile
func (c *StateCreator) ParseAndLoad(content []byte, baseDir, file string, envName string, evaluateBases bool, envValues *environment.Environment) (*HelmState, error) {
state, err := c.Parse(content, baseDir, file)
if err != nil {
@@ -145,14 +161,14 @@ func (c *StateCreator) ParseAndLoad(content []byte, baseDir, file string, envNam
if len(state.Bases) > 0 {
return nil, errors.New("nested `base` helmfile is unsupported. please submit a feature request if you need this!")
}
} else {
state, err = c.loadBases(envValues, state, baseDir)
if err != nil {
return nil, err
}
}
state, err = c.loadBases(envValues, state, baseDir)
if err != nil {
return nil, err
}
state, err = c.LoadEnvValues(state, envName, envValues)
state, err = c.LoadEnvValues(state, envName, envValues, evaluateBases)
if err != nil {
return nil, err
}
@@ -182,7 +198,7 @@ func (c *StateCreator) loadBases(envValues *environment.Environment, st *HelmSta
return layers[0], nil
}
func (st *HelmState) loadEnvValues(name string, ctxEnv *environment.Environment, readFile func(string) ([]byte, error), glob func(string) ([]string, error)) (*environment.Environment, error) {
func (c *StateCreator) loadEnvValues(st *HelmState, name string, failOnMissingEnv bool, ctxEnv *environment.Environment, readFile func(string) ([]byte, error), glob func(string) ([]string, error)) (*environment.Environment, error) {
envVals := map[string]interface{}{}
envSpec, ok := st.Environments[name]
if ok {
@@ -206,11 +222,11 @@ func (st *HelmState) loadEnvValues(name string, ctxEnv *environment.Environment,
envSecretFiles = append(envSecretFiles, resolved...)
}
if err = st.scatterGatherEnvSecretFiles(envSecretFiles, envVals, readFile); err != nil {
if err = c.scatterGatherEnvSecretFiles(st, envSecretFiles, envVals, readFile); err != nil {
return nil, err
}
}
} else if ctxEnv == nil && name != DefaultEnv {
} else if ctxEnv == nil && name != DefaultEnv && failOnMissingEnv {
return nil, &UndefinedEnvError{msg: fmt.Sprintf("environment \"%s\" is not defined", name)}
}
@@ -219,7 +235,7 @@ func (st *HelmState) loadEnvValues(name string, ctxEnv *environment.Environment,
if ctxEnv != nil {
intEnv := *ctxEnv
if err := mergo.Merge(&intEnv, newEnv, mergo.WithOverride); err != nil {
if err := mergo.Merge(&intEnv, newEnv, mergo.WithOverride, mergo.WithOverwriteWithEmptyValue); err != nil {
return nil, fmt.Errorf("error while merging environment values for \"%s\": %v", name, err)
}
@@ -229,7 +245,7 @@ func (st *HelmState) loadEnvValues(name string, ctxEnv *environment.Environment,
return newEnv, nil
}
func (st *HelmState) scatterGatherEnvSecretFiles(envSecretFiles []string, envVals map[string]interface{}, readFile func(string) ([]byte, error)) error {
func (c *StateCreator) scatterGatherEnvSecretFiles(st *HelmState, envSecretFiles []string, envVals map[string]interface{}, readFile func(string) ([]byte, error)) error {
var errs []error
inputs := envSecretFiles
@@ -243,7 +259,6 @@ func (st *HelmState) scatterGatherEnvSecretFiles(envSecretFiles []string, envVal
secrets := make(chan string, inputsSize)
results := make(chan secretResult, inputsSize)
helm := st.helm
st.scatterGather(0, inputsSize,
func() {
@@ -256,7 +271,7 @@ func (st *HelmState) scatterGatherEnvSecretFiles(envSecretFiles []string, envVal
for path := range secrets {
release := &ReleaseSpec{}
flags := st.appendConnectionFlags([]string{}, release)
decFile, err := helm.DecryptSecret(st.createHelmContext(release, 0), path, flags...)
decFile, err := c.getHelm(st).DecryptSecret(st.createHelmContext(release, 0), path, flags...)
if err != nil {
results <- secretResult{nil, err, path}
continue
@@ -288,7 +303,7 @@ func (st *HelmState) scatterGatherEnvSecretFiles(envSecretFiles []string, envVal
if result.err != nil {
errs = append(errs, result.err)
} else {
if err := mergo.Merge(&envVals, &result.result, mergo.WithOverride); err != nil {
if err := mergo.Merge(&envVals, &result.result, mergo.WithOverride, mergo.WithOverwriteWithEmptyValue); err != nil {
errs = append(errs, fmt.Errorf("failed to load environment secrets file \"%s\": %v", result.path, err))
}
}
+5 -5
View File
@@ -9,8 +9,8 @@ import (
"github.com/roboll/helmfile/pkg/testhelper"
"go.uber.org/zap"
. "gotest.tools/assert"
"gotest.tools/assert/cmp"
. "gotest.tools/v3/assert"
"gotest.tools/v3/assert/cmp"
)
func createFromYaml(content []byte, file string, env string, logger *zap.SugaredLogger) (*HelmState, error) {
@@ -20,7 +20,7 @@ func createFromYaml(content []byte, file string, env string, logger *zap.Sugared
abs: filepath.Abs,
Strict: true,
}
return c.ParseAndLoad(content, filepath.Dir(file), file, env, false, nil)
return c.ParseAndLoad(content, filepath.Dir(file), file, env, true, nil)
}
func TestReadFromYaml(t *testing.T) {
@@ -46,7 +46,7 @@ func TestReadFromYaml(t *testing.T) {
}
}
func TestReadFromYaml_InexistentEnv(t *testing.T) {
func TestReadFromYaml_NonexistentEnv(t *testing.T) {
yamlFile := "example/path/to/yaml/file"
yamlContent := []byte(`releases:
- name: myrelease
@@ -108,7 +108,7 @@ bar: {{ readFile "bar.txt" }}
})
testFs.Cwd = "/example/path/to"
state, err := NewCreator(logger, testFs.ReadFile, testFs.FileExists, testFs.Abs, testFs.Glob, nil, nil).ParseAndLoad(yamlContent, filepath.Dir(yamlFile), yamlFile, "production", false, nil)
state, err := NewCreator(logger, testFs.ReadFile, testFs.FileExists, testFs.Abs, testFs.Glob, nil, nil, "").ParseAndLoad(yamlContent, filepath.Dir(yamlFile), yamlFile, "production", true, nil)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
+1 -1
View File
@@ -73,7 +73,7 @@ func (ld *EnvironmentValuesLoader) LoadEnvironmentValues(missingFileHandler *str
if err != nil {
return nil, err
}
if err := mergo.Merge(&result, &vals, mergo.WithOverride); err != nil {
if err := mergo.Merge(&result, &vals, mergo.WithOverride, mergo.WithOverwriteWithEmptyValue); err != nil {
return nil, fmt.Errorf("failed to merge %v: %v", m, err)
}
}
+116
View File
@@ -0,0 +1,116 @@
package state
import (
"github.com/google/go-cmp/cmp"
"go.uber.org/zap"
"io/ioutil"
"path/filepath"
"testing"
)
func newLoader() *EnvironmentValuesLoader {
log, err := zap.NewDevelopment(zap.AddStacktrace(zap.DebugLevel))
if err != nil {
panic(err)
}
sugar := log.Sugar()
storage := &Storage{
FilePath: "./helmfile.yaml",
basePath: ".",
glob: filepath.Glob,
logger: sugar,
}
return NewEnvironmentValuesLoader(storage, ioutil.ReadFile, sugar)
}
// See https://github.com/roboll/helmfile/pull/1169
func TestEnvValsLoad_SingleValuesFile(t *testing.T) {
l := newLoader()
actual, err := l.LoadEnvironmentValues(nil, []interface{}{"testdata/values.5.yaml"})
if err != nil {
t.Fatal(err)
}
expected := map[string]interface{}{
"affinity": map[string]interface{}{},
}
if diff := cmp.Diff(expected, actual); diff != "" {
t.Errorf(diff)
}
}
// See https://github.com/roboll/helmfile/issues/1150
func TestEnvValsLoad_OverwriteNilValue_Issue1150(t *testing.T) {
l := newLoader()
actual, err := l.LoadEnvironmentValues(nil, []interface{}{"testdata/values.1.yaml", "testdata/values.2.yaml"})
if err != nil {
t.Fatal(err)
}
expected := map[string]interface{}{
"components": map[string]interface{}{
"etcd-operator": map[string]interface{}{
"version": "0.10.3",
},
},
}
if diff := cmp.Diff(expected, actual); diff != "" {
t.Errorf(diff)
}
}
// See https://github.com/roboll/helmfile/issues/1154
func TestEnvValsLoad_OverwriteWithNilValue_Issue1154(t *testing.T) {
l := newLoader()
actual, err := l.LoadEnvironmentValues(nil, []interface{}{"testdata/values.3.yaml", "testdata/values.4.yaml"})
if err != nil {
t.Fatal(err)
}
expected := map[string]interface{}{
"components": map[string]interface{}{
"etcd-operator": map[string]interface{}{
"version": "0.10.3",
},
"prometheus": nil,
},
}
if diff := cmp.Diff(expected, actual); diff != "" {
t.Errorf(diff)
}
}
// See https://github.com/roboll/helmfile/issues/1168
func TestEnvValsLoad_OverwriteEmptyValue_Issue1168(t *testing.T) {
l := newLoader()
actual, err := l.LoadEnvironmentValues(nil, []interface{}{"testdata/issues/1168/addons.yaml", "testdata/issues/1168/addons2.yaml"})
if err != nil {
t.Fatal(err)
}
expected := map[string]interface{}{
"addons": map[string]interface{}{
"mychart": map[string]interface{}{
"skip": false,
"name": "mychart",
"namespace": "kube-system",
"chart": "stable/mychart",
"version": "1.0.0",
},
},
}
if diff := cmp.Diff(expected, actual); diff != "" {
t.Errorf(diff)
}
}
+61 -9
View File
@@ -1,5 +1,13 @@
package state
import (
"github.com/roboll/helmfile/pkg/helmexec"
"github.com/variantdev/chartify"
"os"
"path/filepath"
"strings"
)
type Dependency struct {
Chart string `yaml:"chart"`
Version string `yaml:"version"`
@@ -7,49 +15,93 @@ type Dependency struct {
}
func (st *HelmState) appendHelmXFlags(flags []string, release *ReleaseSpec) ([]string, error) {
for _, adopt := range release.Adopt {
flags = append(flags, "--adopt", adopt)
}
return flags, nil
}
func (st *HelmState) PrepareChartify(helm helmexec.Interface, release *ReleaseSpec, workerIndex int) (bool, *chartify.ChartifyOpts, error) {
var opts chartify.ChartifyOpts
var shouldRun bool
opts.EnableKustomizeAlphaPlugins = true
opts.ChartVersion = release.Version
dir := filepath.Join(st.basePath, release.Chart)
if stat, _ := os.Stat(dir); stat != nil && stat.IsDir() {
if exists, err := st.fileExists(filepath.Join(dir, "Chart.yaml")); err == nil && !exists {
shouldRun = true
}
}
for _, d := range release.Dependencies {
var dep string
if d.Alias != "" {
dep += d.Alias + "="
} else {
a := strings.Split(d.Chart, "/")
chart := a[len(a)-1]
dep += chart + "="
}
dep += d.Chart
if d.Version != "" {
dep += ":" + d.Version
}
flags = append(flags, "--dependency", dep)
}
for _, adopt := range release.Adopt {
flags = append(flags, "--adopt", adopt)
opts.AdhocChartDependencies = append(opts.AdhocChartDependencies, dep)
shouldRun = true
}
jsonPatches := release.JSONPatches
if len(jsonPatches) > 0 {
generatedFiles, err := st.generateTemporaryValuesFiles(jsonPatches, release.MissingFileHandler)
if err != nil {
return nil, err
return false, nil, err
}
for _, f := range generatedFiles {
flags = append(flags, "--json-patch", f)
opts.JsonPatches = append(opts.JsonPatches, f)
}
release.generatedValues = append(release.generatedValues, generatedFiles...)
shouldRun = true
}
strategicMergePatches := release.StrategicMergePatches
if len(strategicMergePatches) > 0 {
generatedFiles, err := st.generateTemporaryValuesFiles(strategicMergePatches, release.MissingFileHandler)
if err != nil {
return nil, err
return false, nil, err
}
for _, f := range generatedFiles {
flags = append(flags, "--strategic-merge-patch", f)
opts.StrategicMergePatches = append(opts.StrategicMergePatches, f)
}
release.generatedValues = append(release.generatedValues, generatedFiles...)
shouldRun = true
}
return flags, nil
if shouldRun {
generatedFiles, err := st.generateValuesFiles(helm, release, workerIndex)
if err != nil {
return false, nil, err
}
opts.ValuesFiles = generatedFiles
}
return shouldRun, &opts, nil
}
+13 -3
View File
@@ -13,9 +13,19 @@ type ReleaseError struct {
}
func (e *ReleaseError) Error() string {
return fmt.Sprintf("failed processing release %s: %v", e.Name, e.err.Error())
return e.err.Error()
}
func newReleaseError(release *ReleaseSpec, err error) *ReleaseError {
return &ReleaseError{release, err, ReleaseErrorCodeFailure}
func NewReleaseError(release *ReleaseSpec, err error, code int) *ReleaseError {
return &ReleaseError{
ReleaseSpec: release,
err: err,
Code: code,
}
}
func newReleaseFailedError(release *ReleaseSpec, err error) *ReleaseError {
wrappedErr := fmt.Errorf("failed processing release %s: %v", release.Name, err.Error())
return NewReleaseError(release, wrappedErr, ReleaseErrorCodeFailure)
}
+275 -87
View File
@@ -5,6 +5,7 @@ import (
"encoding/hex"
"errors"
"fmt"
"github.com/variantdev/chartify"
"io"
"io/ioutil"
"os"
@@ -28,11 +29,20 @@ import (
"gopkg.in/yaml.v2"
)
const (
// EmptyTimeout represents the `--timeout` value passed to helm commands not being specified via helmfile flags.
// This is used by an interim solution to make the urfave/cli command report to the helmfile internal about that the
// --timeout flag is missingl
EmptyTimeout = -1
)
// HelmState structure for the helmfile
type HelmState struct {
basePath string
FilePath string
DefaultHelmBinary string `yaml:"helmBinary,omitempty"`
// DefaultValues is the default values to be overrode by environment values and command-line overrides
DefaultValues []interface{} `yaml:"values,omitempty"`
@@ -47,6 +57,7 @@ type HelmState struct {
Repositories []RepositorySpec `yaml:"repositories,omitempty"`
Releases []ReleaseSpec `yaml:"releases,omitempty"`
Selectors []string `yaml:"-"`
ApiVersions []string `yaml:"apiVersions,omitempty"`
Templates map[string]TemplateSpec `yaml:"templates"`
@@ -62,8 +73,12 @@ type HelmState struct {
tempDir func(string, string) (string, error)
runner helmexec.Runner
helm helmexec.Interface
valsRuntime vals.Evaluator
// If set to "Error", return an error when a subhelmfile points to a
// non-existent path. The default behavior is to print a warning. Note the
// differing default compared to other MissingFileHandlers.
MissingFileHandler string `yaml:"missingFileHandler"`
}
// SubHelmfileSpec defines the subhelmfile path and options
@@ -101,6 +116,12 @@ type HelmSpec struct {
Force bool `yaml:"force"`
// Atomic, when set to true, restore previous state in case of a failed install/upgrade attempt
Atomic bool `yaml:"atomic"`
// CleanupOnFail, when set to true, the --cleanup-on-fail helm flag is passed to the upgrade command
CleanupOnFail bool `yaml:"cleanupOnFail,omitempty"`
// HistoryMax, limit the maximum number of revisions saved per release. Use 0 for no limit (default 10)
HistoryMax *int `yaml:"historyMax,omitempty"`
// CreateNamespace, when set to true (default), --create-namespace is passed to helm3 on install/upgrade (ignored for helm2)
CreateNamespace *bool `yaml:"createNamespace,omitempty"`
TLS bool `yaml:"tls"`
TLSCACert string `yaml:"tlsCACert,omitempty"`
@@ -139,6 +160,14 @@ type ReleaseSpec struct {
Installed *bool `yaml:"installed,omitempty"`
// Atomic, when set to true, restore previous state in case of a failed install/upgrade attempt
Atomic *bool `yaml:"atomic,omitempty"`
// CleanupOnFail, when set to true, the --cleanup-on-fail helm flag is passed to the upgrade command
CleanupOnFail *bool `yaml:"cleanupOnFail,omitempty"`
// HistoryMax, limit the maximum number of revisions saved per release. Use 0 for no limit (default 10)
HistoryMax *int `yaml:"historyMax,omitempty"`
// Condition, when set, evaluate the mapping specified in this string to a boolean which decides whether or not to process the release
Condition string `yaml:"condition,omitempty"`
// CreateNamespace, when set to true (default), --create-namespace is passed to helm3 on install (ignored for helm2)
CreateNamespace *bool `yaml:"createNamespace,omitempty"`
// MissingFileHandler is set to either "Error" or "Warn". "Error" instructs helmfile to fail when unable to find a values or secrets file. When "Warn", it prints the file and continues.
// The default value for MissingFileHandler is "Error".
@@ -320,7 +349,7 @@ func (st *HelmState) prepareSyncReleases(helm helmexec.Interface, additionalValu
flags, flagsErr := st.flagsForUpgrade(helm, release, workerIndex)
mut.Unlock()
if flagsErr != nil {
results <- syncPrepareResult{errors: []*ReleaseError{newReleaseError(release, flagsErr)}}
results <- syncPrepareResult{errors: []*ReleaseError{newReleaseFailedError(release, flagsErr)}}
continue
}
@@ -328,14 +357,14 @@ func (st *HelmState) prepareSyncReleases(helm helmexec.Interface, additionalValu
for _, value := range additionalValues {
valfile, err := filepath.Abs(value)
if err != nil {
errs = append(errs, newReleaseError(release, err))
errs = append(errs, newReleaseFailedError(release, err))
}
ok, err := st.fileExists(valfile)
if err != nil {
errs = append(errs, newReleaseError(release, err))
errs = append(errs, newReleaseFailedError(release, err))
} else if !ok {
errs = append(errs, newReleaseError(release, fmt.Errorf("file does not exist: %s", valfile)))
errs = append(errs, newReleaseFailedError(release, fmt.Errorf("file does not exist: %s", valfile)))
}
flags = append(flags, "--values", valfile)
}
@@ -474,7 +503,7 @@ func (st *HelmState) DeleteReleasesForSync(affectedReleases *AffectedReleases, h
context := st.createHelmContext(release, workerIndex)
if _, err := st.triggerPresyncEvent(release, "sync"); err != nil {
relErr = newReleaseError(release, err)
relErr = newReleaseFailedError(release, err)
} else {
var args []string
if helm.IsHelm3() {
@@ -489,7 +518,7 @@ func (st *HelmState) DeleteReleasesForSync(affectedReleases *AffectedReleases, h
m.Lock()
if err := helm.DeleteRelease(context, release.Name, deletionFlags...); err != nil {
affectedReleases.Failed = append(affectedReleases.Failed, release)
relErr = newReleaseError(release, err)
relErr = newReleaseFailedError(release, err)
} else {
affectedReleases.Deleted = append(affectedReleases.Deleted, release)
}
@@ -570,11 +599,11 @@ func (st *HelmState) SyncReleases(affectedReleases *AffectedReleases, helm helme
context := st.createHelmContext(release, workerIndex)
if _, err := st.triggerPresyncEvent(release, "sync"); err != nil {
relErr = newReleaseError(release, err)
relErr = newReleaseFailedError(release, err)
} else if !release.Desired() {
installed, err := st.isReleaseInstalled(context, helm, *release)
if err != nil {
relErr = newReleaseError(release, err)
relErr = newReleaseFailedError(release, err)
} else if installed {
var args []string
if helm.IsHelm3() {
@@ -586,7 +615,7 @@ func (st *HelmState) SyncReleases(affectedReleases *AffectedReleases, helm helme
m.Lock()
if err := helm.DeleteRelease(context, release.Name, deletionFlags...); err != nil {
affectedReleases.Failed = append(affectedReleases.Failed, release)
relErr = newReleaseError(release, err)
relErr = newReleaseFailedError(release, err)
} else {
affectedReleases.Deleted = append(affectedReleases.Deleted, release)
}
@@ -596,7 +625,7 @@ func (st *HelmState) SyncReleases(affectedReleases *AffectedReleases, helm helme
m.Lock()
affectedReleases.Failed = append(affectedReleases.Failed, release)
m.Unlock()
relErr = newReleaseError(release, err)
relErr = newReleaseFailedError(release, err)
} else {
m.Lock()
affectedReleases.Upgraded = append(affectedReleases.Upgraded, release)
@@ -649,6 +678,7 @@ func (st *HelmState) listReleases(context helmexec.HelmContext, helm helmexec.In
if helm.IsHelm3() && release.Namespace != "" {
flags = append(flags, "--namespace", release.Namespace)
}
flags = append(flags, "--deployed", "--failed", "--pending")
return helm.List(context, "^"+release.Name+"$", flags...)
}
@@ -670,18 +700,37 @@ func (st *HelmState) getDeployedVersion(context helmexec.HelmContext, helm helme
}
}
// downloadCharts will download and untar charts for Lint and Template
func (st *HelmState) downloadCharts(helm helmexec.Interface, dir string, concurrency int, helmfileCommand string) (map[string]string, []error) {
// PrepareCharts creates temporary directories of charts.
//
// Each resulting "chart" can be one of the followings:
//
// (1) local chart
// (2) temporary local chart generated from kustomization or manifests
// (3) remote chart
//
// When running `helmfile lint` or `helmfile template`, PrepareCharts will download and untar charts for linting and templating.
//
// Otheriwse, if a chart is not a helm chart, it will call "chartify" to turn it into a chart.
//
// If exists, it will also patch resources by json patches, strategic-merge patches, and injectors.
func PrepareCharts(helm helmexec.Interface, st *HelmState, dir string, concurrency int, helmfileCommand string, forceDownload bool) (map[string]string, []error) {
temp := make(map[string]string, len(st.Releases))
type downloadResults struct {
releaseName string
chartPath string
err error
}
errs := []error{}
jobQueue := make(chan *ReleaseSpec, len(st.Releases))
results := make(chan *downloadResults, len(st.Releases))
var helm3 bool
if helm != nil {
helm3 = helm.IsHelm3()
}
st.scatterGather(
concurrency,
len(st.Releases),
@@ -691,10 +740,32 @@ func (st *HelmState) downloadCharts(helm helmexec.Interface, dir string, concurr
}
close(jobQueue)
},
func(_ int) {
func(workerIndex int) {
for release := range jobQueue {
chartPath := ""
if pathExists(normalizeChart(st.basePath, release.Chart)) {
var chartPath string
shouldChartify, opts, err := st.PrepareChartify(helm, release, workerIndex)
if err != nil {
results <- &downloadResults{err: err}
return
}
if shouldChartify {
c := chartify.New(
chartify.HelmBin(st.DefaultHelmBinary),
chartify.UseHelm3(helm3),
)
out, err := c.Chartify(release.Name, release.Chart, chartify.WithChartifyOpts(opts))
if err != nil {
errs = append(errs, err)
} else {
// TODO Chartify
chartPath = out
}
} else if !forceDownload {
chartPath = release.Chart
} else if pathExists(normalizeChart(st.basePath, release.Chart)) {
chartPath = normalizeChart(st.basePath, release.Chart)
} else {
fetchFlags := []string{}
@@ -722,12 +793,19 @@ func (st *HelmState) downloadCharts(helm helmexec.Interface, dir string, concurr
chartPath = filepath.Dir(fullChartPath)
}
}
results <- &downloadResults{release.Name, chartPath}
results <- &downloadResults{releaseName: release.Name, chartPath: chartPath}
}
},
func() {
for i := 0; i < len(st.Releases); i++ {
downloadRes := <-results
if downloadRes.err != nil {
errs = append(errs, downloadRes.err)
return
}
temp[downloadRes.releaseName] = downloadRes.chartPath
}
},
@@ -750,7 +828,7 @@ func (o *TemplateOpts) Apply(opts *TemplateOpts) {
}
// TemplateReleases wrapper for executing helm template on the releases
func (st *HelmState) TemplateReleases(helm helmexec.Interface, outputDir string, additionalValues []string, args []string, workerLimit int, opt ...TemplateOpt) []error {
func (st *HelmState) TemplateReleases(helm helmexec.Interface, outputDir string, additionalValues []string, args []string, workerLimit int, validate bool, opt ...TemplateOpt) []error {
opts := &TemplateOpts{}
for _, o := range opt {
o.Apply(opts)
@@ -760,20 +838,6 @@ func (st *HelmState) TemplateReleases(helm helmexec.Interface, outputDir string,
helm.SetExtraArgs()
errs := []error{}
// Create tmp directory and bail immediately if it fails
dir, err := ioutil.TempDir("", "")
if err != nil {
errs = append(errs, err)
return errs
}
defer os.RemoveAll(dir)
temp, errs := st.downloadCharts(helm, dir, workerLimit, "template")
if errs != nil {
errs = append(errs, err)
return errs
}
if len(args) > 0 {
helm.SetExtraArgs(args...)
@@ -822,8 +886,12 @@ func (st *HelmState) TemplateReleases(helm helmexec.Interface, outputDir string,
os.Mkdir(releaseOutputDir, 0755)
}
if validate {
flags = append(flags, "--validate")
}
if len(errs) == 0 {
if err := helm.TemplateRelease(release.Name, temp[release.Name], flags...); err != nil {
if err := helm.TemplateRelease(release.Name, release.Chart, flags...); err != nil {
errs = append(errs, err)
}
}
@@ -861,19 +929,6 @@ func (st *HelmState) LintReleases(helm helmexec.Interface, additionalValues []st
helm.SetExtraArgs()
errs := []error{}
// Create tmp directory and bail immediately if it fails
dir, err := ioutil.TempDir("", "")
if err != nil {
errs = append(errs, err)
return errs
}
defer os.RemoveAll(dir)
temp, errs := st.downloadCharts(helm, dir, workerLimit, "lint")
if errs != nil {
errs = append(errs, err)
return errs
}
if len(args) > 0 {
helm.SetExtraArgs(args...)
@@ -909,7 +964,7 @@ func (st *HelmState) LintReleases(helm helmexec.Interface, additionalValues []st
}
if len(errs) == 0 {
if err := helm.Lint(release.Name, temp[release.Name], flags...); err != nil {
if err := helm.Lint(release.Name, release.Chart, flags...); err != nil {
errs = append(errs, err)
}
}
@@ -936,7 +991,7 @@ type diffPrepareResult struct {
errors []*ReleaseError
}
func (st *HelmState) prepareDiffReleases(helm helmexec.Interface, additionalValues []string, concurrency int, detailedExitCode, suppressSecrets bool, opt ...DiffOpt) ([]diffPrepareResult, []error) {
func (st *HelmState) prepareDiffReleases(helm helmexec.Interface, additionalValues []string, concurrency int, detailedExitCode, includeTests, suppressSecrets bool, opt ...DiffOpt) ([]diffPrepareResult, []error) {
opts := &DiffOpts{}
for _, o := range opt {
o.Apply(opts)
@@ -947,6 +1002,9 @@ func (st *HelmState) prepareDiffReleases(helm helmexec.Interface, additionalValu
if !st.Releases[i].Desired() {
continue
}
if st.Releases[i].Installed != nil && !*(st.Releases[i].Installed) {
continue
}
releases = append(releases, &st.Releases[i])
}
@@ -999,6 +1057,10 @@ func (st *HelmState) prepareDiffReleases(helm helmexec.Interface, additionalValu
flags = append(flags, "--detailed-exitcode")
}
if includeTests {
flags = append(flags, "--include-tests")
}
if suppressSecrets {
flags = append(flags, "--suppress-secrets")
}
@@ -1020,7 +1082,7 @@ func (st *HelmState) prepareDiffReleases(helm helmexec.Interface, additionalValu
if len(errs) > 0 {
rsErrs := make([]*ReleaseError, len(errs))
for i, e := range errs {
rsErrs[i] = newReleaseError(release, e)
rsErrs[i] = newReleaseFailedError(release, e)
}
results <- diffPrepareResult{errors: rsErrs}
} else {
@@ -1054,11 +1116,19 @@ func (st *HelmState) createHelmContext(spec *ReleaseSpec, workerIndex int) helme
if spec.Tillerless != nil {
tillerless = *spec.Tillerless
}
historyMax := 10
if st.HelmDefaults.HistoryMax != nil {
historyMax = *st.HelmDefaults.HistoryMax
}
if spec.HistoryMax != nil {
historyMax = *spec.HistoryMax
}
return helmexec.HelmContext{
Tillerless: tillerless,
TillerNamespace: namespace,
WorkerIndex: workerIndex,
HistoryMax: historyMax,
}
}
@@ -1076,13 +1146,13 @@ type DiffOpt interface{ Apply(*DiffOpts) }
// DiffReleases wrapper for executing helm diff on the releases
// It returns releases that had any changes
func (st *HelmState) DiffReleases(helm helmexec.Interface, additionalValues []string, workerLimit int, detailedExitCode, suppressSecrets bool, triggerCleanupEvents bool, opt ...DiffOpt) ([]ReleaseSpec, []error) {
func (st *HelmState) DiffReleases(helm helmexec.Interface, additionalValues []string, workerLimit int, detailedExitCode, includeTests, suppressSecrets, suppressDiff, triggerCleanupEvents bool, opt ...DiffOpt) ([]ReleaseSpec, []error) {
opts := &DiffOpts{}
for _, o := range opt {
o.Apply(opts)
}
preps, prepErrs := st.prepareDiffReleases(helm, additionalValues, workerLimit, detailedExitCode, suppressSecrets, opts)
preps, prepErrs := st.prepareDiffReleases(helm, additionalValues, workerLimit, detailedExitCode, includeTests, suppressSecrets, opts)
if len(prepErrs) > 0 {
return []ReleaseSpec{}, prepErrs
}
@@ -1106,7 +1176,7 @@ func (st *HelmState) DiffReleases(helm helmexec.Interface, additionalValues []st
for prep := range jobQueue {
flags := prep.flags
release := prep.release
if err := helm.DiffRelease(st.createHelmContext(release, workerIndex), release.Name, normalizeChart(st.basePath, release.Chart), flags...); err != nil {
if err := helm.DiffRelease(st.createHelmContext(release, workerIndex), release.Name, normalizeChart(st.basePath, release.Chart), suppressDiff, flags...); err != nil {
switch e := err.(type) {
case helmexec.ExitError:
// Propagate any non-zero exit status from the external command like `helm` that is failed under the hood
@@ -1148,7 +1218,12 @@ func (st *HelmState) ReleaseStatuses(helm helmexec.Interface, workerLimit int) [
return nil
}
st.ApplyOverrides(&release)
flags := []string{}
if helm.IsHelm3() && release.Namespace != "" {
flags = append(flags, "--namespace", release.Namespace)
}
flags = st.appendConnectionFlags(flags, &release)
return helm.ReleaseStatus(st.createHelmContext(&release, workerIndex), release.Name, flags...)
@@ -1188,14 +1263,23 @@ func (st *HelmState) TestReleases(helm helmexec.Interface, cleanup bool, timeout
}
flags := []string{}
if cleanup {
if helm.IsHelm3() && release.Namespace != "" {
flags = append(flags, "--namespace", release.Namespace)
}
if cleanup && !helm.IsHelm3() {
flags = append(flags, "--cleanup")
}
duration := strconv.Itoa(timeout)
if helm.IsHelm3() {
duration += "s"
if timeout == EmptyTimeout {
flags = append(flags, st.timeoutFlags(helm, &release)...)
} else {
duration := strconv.Itoa(timeout)
if helm.IsHelm3() {
duration += "s"
}
flags = append(flags, "--timeout", duration)
}
flags = append(flags, "--timeout", duration)
flags = st.appendConnectionFlags(flags, &release)
return helm.TestRelease(st.createHelmContext(&release, workerIndex), release.Name, flags...)
@@ -1234,14 +1318,18 @@ func (st *HelmState) GetReleasesWithOverrides() []ReleaseSpec {
}
func (st *HelmState) SelectReleasesWithOverrides() ([]Release, error) {
rs, err := markFilteredReleases(st.GetReleasesWithOverrides(), st.Selectors)
values, err := st.Values()
if err != nil {
return nil, err
}
rs, err := markExcludedReleases(st.GetReleasesWithOverrides(), st.Selectors, values)
if err != nil {
return nil, err
}
return rs, nil
}
func markFilteredReleases(releases []ReleaseSpec, selectors []string) ([]Release, error) {
func markExcludedReleases(releases []ReleaseSpec, selectors []string, values map[string]interface{}) ([]Release, error) {
var filteredReleases []Release
filters := []ReleaseFilter{}
for _, label := range selectors {
@@ -1261,19 +1349,29 @@ func markFilteredReleases(releases []ReleaseSpec, selectors []string) ([]Release
// Strip off just the last portion for the name stable/newrelic would give newrelic
chartSplit := strings.Split(r.Chart, "/")
r.Labels["chart"] = chartSplit[len(chartSplit)-1]
var matched bool
var filterMatch bool
for _, f := range filters {
if r.Labels == nil {
r.Labels = map[string]string{}
}
if f.Match(r) {
matched = true
filterMatch = true
break
}
}
var conditionMatch bool
if len(r.Condition) > 0 {
conditionSplit := strings.Split(r.Condition, ".")
if len(conditionSplit) != 2 {
return nil, fmt.Errorf("Condition value must be in the form 'foo.enabled' where 'foo' can be modified as necessary")
}
if values[conditionSplit[0]].(map[string]interface{})["enabled"] == true {
conditionMatch = true
}
}
res := Release{
ReleaseSpec: r,
Filtered: len(filters) > 0 && !matched,
Filtered: (len(filters) > 0 && !filterMatch) || (len(r.Condition) > 0 && !conditionMatch),
}
filteredReleases = append(filteredReleases, res)
}
@@ -1292,6 +1390,15 @@ func (st *HelmState) GetSelectedReleasesWithOverrides() ([]ReleaseSpec, error) {
releases = append(releases, r.ReleaseSpec)
}
}
var extra string
if len(st.Selectors) > 0 {
extra = " matching " + strings.Join(st.Selectors, ",")
}
st.logger.Debugf("%d release(s)%s found in %s\n", len(releases), extra, st.FilePath)
return releases, nil
}
@@ -1302,7 +1409,6 @@ func (st *HelmState) FilterReleases() error {
return err
}
st.Releases = releases
st.logger.Debugf("%d release(s) matching %s found in %s\n", len(releases), strings.Join(st.Selectors, ","), st.FilePath)
return nil
}
@@ -1312,8 +1418,12 @@ func (st *HelmState) PrepareReleases(helm helmexec.Interface, helmfileCommand st
for i := range st.Releases {
release := st.Releases[i]
if release.Installed != nil && !*release.Installed {
continue
}
if _, err := st.triggerPrepareEvent(&release, helmfileCommand); err != nil {
errs = append(errs, newReleaseError(&release, err))
errs = append(errs, newReleaseFailedError(&release, err))
continue
}
}
@@ -1403,6 +1513,15 @@ func (st *HelmState) BuildDeps(helm helmexec.Interface) []error {
errs := []error{}
for _, release := range st.Releases {
if len(release.Chart) == 0 {
errs = append(errs, errors.New("chart is required for: "+release.Name))
continue
}
if release.Installed != nil && !*release.Installed {
continue
}
if isLocalChart(release.Chart) {
if err := helm.BuildDeps(release.Name, normalizeChart(st.basePath, release.Chart)); err != nil {
errs = append(errs, err)
@@ -1504,6 +1623,24 @@ func (st *HelmState) connectionFlags(release *ReleaseSpec) []string {
return flags
}
func (st *HelmState) timeoutFlags(helm helmexec.Interface, release *ReleaseSpec) []string {
var flags []string
timeout := st.HelmDefaults.Timeout
if release.Timeout != nil {
timeout = *release.Timeout
}
if timeout != 0 {
duration := strconv.Itoa(timeout)
if helm.IsHelm3() {
duration += "s"
}
flags = append(flags, "--timeout", duration)
}
return flags
}
func (st *HelmState) flagsForUpgrade(helm helmexec.Interface, release *ReleaseSpec, workerIndex int) ([]string, error) {
flags := []string{}
if release.Version != "" {
@@ -1522,17 +1659,7 @@ func (st *HelmState) flagsForUpgrade(helm helmexec.Interface, release *ReleaseSp
flags = append(flags, "--wait")
}
timeout := st.HelmDefaults.Timeout
if release.Timeout != nil {
timeout = *release.Timeout
}
if timeout != 0 {
duration := strconv.Itoa(timeout)
if helm.IsHelm3() {
duration += "s"
}
flags = append(flags, "--timeout", duration)
}
flags = append(flags, st.timeoutFlags(helm, release)...)
if release.Force != nil && *release.Force || release.Force == nil && st.HelmDefaults.Force {
flags = append(flags, "--force")
@@ -1546,6 +1673,20 @@ func (st *HelmState) flagsForUpgrade(helm helmexec.Interface, release *ReleaseSp
flags = append(flags, "--atomic")
}
if release.CleanupOnFail != nil && *release.CleanupOnFail || release.CleanupOnFail == nil && st.HelmDefaults.CleanupOnFail {
flags = append(flags, "--cleanup-on-fail")
}
if release.CreateNamespace != nil && *release.CreateNamespace ||
release.CreateNamespace == nil && (st.HelmDefaults.CreateNamespace == nil || *st.HelmDefaults.CreateNamespace) {
if helm.IsVersionAtLeast(3, 2) {
flags = append(flags, "--create-namespace")
} else if release.CreateNamespace != nil || st.HelmDefaults.CreateNamespace != nil {
// createNamespace was set explicitly, but not running supported version of helm - error
return nil, fmt.Errorf("releases[].createNamespace requires Helm 3.2.0 or greater")
}
}
flags = st.appendConnectionFlags(flags, release)
var err error
@@ -1570,6 +1711,8 @@ func (st *HelmState) flagsForTemplate(helm helmexec.Interface, release *ReleaseS
return nil, err
}
flags = st.appendApiVersionsFlags(flags)
common, err := st.namespaceAndValuesFlags(helm, release, workerIndex)
if err != nil {
return nil, err
@@ -1602,6 +1745,13 @@ func (st *HelmState) flagsForDiff(helm helmexec.Interface, release *ReleaseSpec,
return append(flags, common...), nil
}
func (st *HelmState) appendApiVersionsFlags(flags []string) []string {
for _, a := range st.ApiVersions {
flags = append(flags, "--api-versions", a)
}
return flags
}
func (st *HelmState) isDevelopment(release *ReleaseSpec) bool {
result := st.HelmDefaults.Devel
if release.Devel != nil {
@@ -1678,6 +1828,11 @@ func (st *HelmState) ExpandedHelmfiles() ([]SubHelmfileSpec, error) {
return nil, err
}
if len(matches) == 0 {
err := fmt.Errorf("no matches for path: %s", hf.Path)
if st.MissingFileHandler == "Error" {
return nil, err
}
fmt.Println(err)
continue
}
for _, match := range matches {
@@ -1744,12 +1899,7 @@ func (st *HelmState) generateTemporaryValuesFiles(values []interface{}, missingF
return generatedFiles, nil
}
func (st *HelmState) namespaceAndValuesFlags(helm helmexec.Interface, release *ReleaseSpec, workerIndex int) ([]string, error) {
flags := []string{}
if release.Namespace != "" {
flags = append(flags, "--namespace", release.Namespace)
}
func (st *HelmState) generateVanillaValuesFiles(release *ReleaseSpec) ([]string, error) {
values := []interface{}{}
for _, v := range release.Values {
switch typedValue := v.(type) {
@@ -1776,12 +1926,14 @@ func (st *HelmState) namespaceAndValuesFlags(helm helmexec.Interface, release *R
return nil, err
}
for _, f := range generatedFiles {
flags = append(flags, "--values", f)
}
release.generatedValues = append(release.generatedValues, generatedFiles...)
return generatedFiles, nil
}
func (st *HelmState) generateSecretValuesFiles(helm helmexec.Interface, release *ReleaseSpec, workerIndex int) ([]string, error) {
var generatedFiles []string
for _, value := range release.Secrets {
paths, skip, err := st.storage().resolveFile(release.MissingFileHandler, "secrets", release.ValuesPathPrefix+value)
if err != nil {
@@ -1802,9 +1954,45 @@ func (st *HelmState) namespaceAndValuesFlags(helm helmexec.Interface, release *R
return nil, err
}
release.generatedValues = append(release.generatedValues, valfile)
flags = append(flags, "--values", valfile)
generatedFiles = append(generatedFiles, valfile)
}
release.generatedValues = append(release.generatedValues, generatedFiles...)
return generatedFiles, nil
}
func (st *HelmState) generateValuesFiles(helm helmexec.Interface, release *ReleaseSpec, workerIndex int) ([]string, error) {
valuesFiles, err := st.generateVanillaValuesFiles(release)
if err != nil {
return nil, err
}
secretValuesFiles, err := st.generateSecretValuesFiles(helm, release, workerIndex)
if err != nil {
return nil, err
}
files := append(valuesFiles, secretValuesFiles...)
return files, nil
}
func (st *HelmState) namespaceAndValuesFlags(helm helmexec.Interface, release *ReleaseSpec, workerIndex int) ([]string, error) {
flags := []string{}
if release.Namespace != "" {
flags = append(flags, "--namespace", release.Namespace)
}
generatedFiles, err := st.generateValuesFiles(helm, release, workerIndex)
if err != nil {
return nil, err
}
for _, f := range generatedFiles {
flags = append(flags, "--values", f)
}
if len(release.SetValues) > 0 {
for _, set := range release.SetValues {
if set.Value != "" {
+1 -17
View File
@@ -4,28 +4,12 @@ import (
"fmt"
"reflect"
"github.com/imdario/mergo"
"github.com/roboll/helmfile/pkg/maputil"
"github.com/roboll/helmfile/pkg/tmpl"
"gopkg.in/yaml.v2"
)
func (st *HelmState) Values() (map[string]interface{}, error) {
vals := map[string]interface{}{}
if err := mergo.Merge(&vals, st.Env.Defaults, mergo.WithOverride); err != nil {
return nil, err
}
if err := mergo.Merge(&vals, st.Env.Values, mergo.WithOverride); err != nil {
return nil, err
}
vals, err := maputil.CastKeysToStrings(vals)
if err != nil {
return nil, err
}
return vals, nil
return st.Env.GetMergedValues()
}
func (st *HelmState) mustLoadVals() map[string]interface{} {
+13 -12
View File
@@ -38,9 +38,7 @@ func (st *HelmState) scatterGather(concurrency int, items int, produceInputs fun
for w := 1; w <= concurrency; w++ {
go func(id int) {
st.logger.Debugf("worker %d/%d started", id, concurrency)
receiveInputsAndProduceIntermediates(id)
st.logger.Debugf("worker %d/%d finished", id, concurrency)
waitGroup.Done()
}(w)
}
@@ -78,21 +76,16 @@ func (st *HelmState) iterateOnReleases(helm helmexec.Interface, concurrency int,
func(id int) {
for release := range releases {
err := do(release, id)
st.logger.Debugf("sending result for release: %s\n", release.Name)
st.logger.Debugf("release %q processed", release.Name)
results <- result{release: release, err: err}
st.logger.Debugf("sent result for release: %s\n", release.Name)
}
},
func() {
for i := range inputs {
st.logger.Debugf("receiving result %d", i)
for range inputs {
r := <-results
if r.err != nil {
errs = append(errs, fmt.Errorf("release \"%s\" failed: %v", r.release.Name, r.err))
} else {
st.logger.Debugf("received result for release \"%s\"", r.release.Name)
}
st.logger.Debugf("received result for %d", i)
}
},
)
@@ -139,13 +132,15 @@ func SortedReleaseGroups(releases []Release, reverse bool) ([][]Release, error)
func GroupReleasesByDependency(releases []Release) ([][]Release, error) {
idToReleases := map[string][]Release{}
idToIndex := map[string]int{}
d := dag.New()
for _, r := range releases {
for i, r := range releases {
id := ReleaseToID(&r.ReleaseSpec)
idToReleases[id] = append(idToReleases[id], r)
idToIndex[id] = i
// Only compute dependencies from non-filtered releases
if !r.Filtered {
@@ -158,7 +153,7 @@ func GroupReleasesByDependency(releases []Release) ([][]Release, error) {
for _, n := range r.Needs {
if _, ok := idToReleases[n]; !ok {
id := ReleaseToID(&r.ReleaseSpec)
return nil, fmt.Errorf("%q has dependency to inexistent release %q", id, n)
return nil, fmt.Errorf("%q depends on nonexistent release %q", id, n)
}
}
}
@@ -182,7 +177,13 @@ func GroupReleasesByDependency(releases []Release) ([][]Release, error) {
}
// Make the helmfile behavior deterministic for reproducibility and ease of testing
sort.Strings(idsInGroup)
// We try to keep the order of definitions to keep backward-compatibility
// See https://github.com/roboll/helmfile/issues/988
sort.Slice(idsInGroup, func(i, j int) bool {
ii := idToIndex[idsInGroup[i]]
ij := idToIndex[idsInGroup[j]]
return ii < ij
})
for _, id := range idsInGroup {
releases, ok := idToReleases[id]
+182 -7
View File
@@ -163,9 +163,11 @@ func TestHelmState_flagsForUpgrade(t *testing.T) {
tests := []struct {
name string
version *helmexec.Version
defaults HelmSpec
release *ReleaseSpec
want []string
wantErr string
}{
{
name: "no-options",
@@ -448,6 +450,58 @@ func TestHelmState_flagsForUpgrade(t *testing.T) {
"--namespace", "test-namespace",
},
},
{
name: "cleanup-on-fail",
defaults: HelmSpec{
CleanupOnFail: false,
},
release: &ReleaseSpec{
Chart: "test/chart",
Version: "0.1",
CleanupOnFail: &enable,
Name: "test-charts",
Namespace: "test-namespace",
},
want: []string{
"--version", "0.1",
"--cleanup-on-fail",
"--namespace", "test-namespace",
},
},
{
name: "cleanup-on-fail-override-default",
defaults: HelmSpec{
CleanupOnFail: true,
},
release: &ReleaseSpec{
Chart: "test/chart",
Version: "0.1",
CleanupOnFail: &disable,
Name: "test-charts",
Namespace: "test-namespace",
},
want: []string{
"--version", "0.1",
"--namespace", "test-namespace",
},
},
{
name: "cleanup-on-fail-from-default",
defaults: HelmSpec{
CleanupOnFail: true,
},
release: &ReleaseSpec{
Chart: "test/chart",
Version: "0.1",
Name: "test-charts",
Namespace: "test-namespace",
},
want: []string{
"--version", "0.1",
"--cleanup-on-fail",
"--namespace", "test-namespace",
},
},
{
name: "tiller",
defaults: HelmSpec{},
@@ -521,6 +575,121 @@ func TestHelmState_flagsForUpgrade(t *testing.T) {
"--tls-ca-cert", "ca.pem",
},
},
{
name: "create-namespace-default-helm3.2",
defaults: HelmSpec{
Verify: false,
},
version: &helmexec.Version{
Major: 3,
Minor: 2,
Patch: 0,
},
release: &ReleaseSpec{
Chart: "test/chart",
Version: "0.1",
Verify: &disable,
Name: "test-charts",
Namespace: "test-namespace",
},
want: []string{
"--version", "0.1",
"--create-namespace",
"--namespace", "test-namespace",
},
},
{
name: "create-namespace-disabled-helm3.2",
defaults: HelmSpec{
Verify: false,
CreateNamespace: &disable,
},
version: &helmexec.Version{
Major: 3,
Minor: 2,
Patch: 0,
},
release: &ReleaseSpec{
Chart: "test/chart",
Version: "0.1",
Verify: &disable,
Name: "test-charts",
Namespace: "test-namespace",
},
want: []string{
"--version", "0.1",
"--namespace", "test-namespace",
},
},
{
name: "create-namespace-release-override-enabled-helm3.2",
defaults: HelmSpec{
Verify: false,
CreateNamespace: &disable,
},
version: &helmexec.Version{
Major: 3,
Minor: 2,
Patch: 0,
},
release: &ReleaseSpec{
Chart: "test/chart",
Version: "0.1",
Verify: &disable,
Name: "test-charts",
Namespace: "test-namespace",
CreateNamespace: &enable,
},
want: []string{
"--version", "0.1",
"--create-namespace",
"--namespace", "test-namespace",
},
},
{
name: "create-namespace-release-override-disabled-helm3.2",
defaults: HelmSpec{
Verify: false,
CreateNamespace: &enable,
},
version: &helmexec.Version{
Major: 3,
Minor: 2,
Patch: 0,
},
release: &ReleaseSpec{
Chart: "test/chart",
Version: "0.1",
Verify: &disable,
Name: "test-charts",
Namespace: "test-namespace",
CreateNamespace: &disable,
},
want: []string{
"--version", "0.1",
"--namespace", "test-namespace",
},
},
{
name: "create-namespace-unsupported",
defaults: HelmSpec{
Verify: false,
CreateNamespace: &enable,
},
version: &helmexec.Version{
Major: 2,
Minor: 16,
Patch: 0,
},
release: &ReleaseSpec{
Chart: "test/chart",
Version: "0.1",
Verify: &disable,
Name: "test-charts",
Namespace: "test-namespace",
},
wantErr: "releases[].createNamespace requires Helm 3.2.0 or greater",
},
}
for i := range tests {
tt := tests[i]
@@ -532,10 +701,16 @@ func TestHelmState_flagsForUpgrade(t *testing.T) {
HelmDefaults: tt.defaults,
valsRuntime: valsRuntime,
}
helm := helmexec.New("helm", logger, "default", &mockRunner{})
helm := &exectest.Helm{
Version: tt.version,
}
args, err := state.flagsForUpgrade(helm, tt.release, 0)
if err != nil {
t.Errorf("unexpected error flagsForUpgade: %v", err)
if err != nil && tt.wantErr == "" {
t.Errorf("unexpected error flagsForUpgrade: %v", err)
}
if tt.wantErr != "" && (err == nil || err.Error() != tt.wantErr) {
t.Errorf("expected error '%v'; got '%v'", err, tt.wantErr)
}
if !reflect.DeepEqual(args, tt.want) {
t.Errorf("flagsForUpgrade returned = %v, want %v", args, tt.want)
@@ -1116,7 +1291,7 @@ func TestHelmState_SyncReleasesAffectedRealeases(t *testing.T) {
//simulate the release is already installed
for i, release := range tt.releases {
if tt.installed != nil && tt.installed[i] {
helm.Lists[exectest.ListKey{Filter: "^" + release.Name + "$"}] = release.Name
helm.Lists[exectest.ListKey{Filter: "^" + release.Name + "$", Flags: "--deployed--failed--pending"}] = release.Name
}
}
@@ -1216,7 +1391,7 @@ func TestGetDeployedVersion(t *testing.T) {
Lists: map[exectest.ListKey]string{},
}
//simulate the helm.list call result
helm.Lists[exectest.ListKey{Filter: "^" + tt.release.Name + "$"}] = tt.listResult
helm.Lists[exectest.ListKey{Filter: "^" + tt.release.Name + "$", Flags: "--deployed--failed--pending"}] = tt.listResult
affectedReleases := AffectedReleases{}
state.SyncReleases(&affectedReleases, helm, []string{}, 1)
@@ -1339,7 +1514,7 @@ func TestHelmState_DiffReleases(t *testing.T) {
logger: logger,
valsRuntime: valsRuntime,
}
_, errs := state.DiffReleases(tt.helm, []string{}, 1, false, false, false)
_, errs := state.DiffReleases(tt.helm, []string{}, 1, false, false, false, false, false)
if errs != nil && len(errs) > 0 {
t.Errorf("unexpected error: %v", errs)
}
@@ -1504,7 +1679,7 @@ func TestHelmState_DiffReleasesCleanup(t *testing.T) {
`,
})
state = injectFs(state, testfs)
if _, errs := state.DiffReleases(tt.helm, []string{}, 1, false, false, false); errs != nil && len(errs) > 0 {
if _, errs := state.DiffReleases(tt.helm, []string{}, 1, false, false, false, false, false); errs != nil && len(errs) > 0 {
t.Errorf("unexpected errors: %v", errs)
}
+7
View File
@@ -0,0 +1,7 @@
addons:
mychart:
skip: true
name: mychart
namespace: kube-system
chart: stable/mychart
version: 1.0.0
+3
View File
@@ -0,0 +1,3 @@
addons:
mychart:
skip: false
+2
View File
@@ -0,0 +1,2 @@
components:
etcd-operator:
+3
View File
@@ -0,0 +1,3 @@
components:
etcd-operator:
version: 0.10.3
+3
View File
@@ -0,0 +1,3 @@
components:
etcd-operator:
version: 0.10.0
+4
View File
@@ -0,0 +1,4 @@
components:
etcd-operator:
version: 0.10.3
prometheus:
+1
View File
@@ -0,0 +1 @@
affinity: {}
+90 -39
View File
@@ -2,6 +2,7 @@ package tmpl
import (
"fmt"
"golang.org/x/sync/errgroup"
"gopkg.in/yaml.v2"
"io"
"os"
@@ -16,15 +17,18 @@ type Values = map[string]interface{}
func (c *Context) createFuncMap() template.FuncMap {
funcMap := template.FuncMap{
"exec": c.Exec,
"readFile": c.ReadFile,
"toYaml": ToYaml,
"fromYaml": FromYaml,
"setValueAtPath": SetValueAtPath,
"requiredEnv": RequiredEnv,
"get": get,
"getOrNil": getOrNil,
"tpl": c.Tpl,
"exec": c.Exec,
"readFile": c.ReadFile,
"toYaml": ToYaml,
"fromYaml": FromYaml,
"setValueAtPath": SetValueAtPath,
"requiredEnv": RequiredEnv,
"get": get,
"getOrNil": getOrNil,
"tpl": c.Tpl,
"required": Required,
"fetchSecretValue": fetchSecretValue,
"expandSecretRefs": fetchSecretValues,
}
if c.preRender {
// disable potential side-effect template calls
@@ -58,60 +62,95 @@ func (c *Context) Exec(command string, args []interface{}, inputs ...string) (st
cmd := exec.Command(command, strArgs...)
cmd.Dir = c.basePath
writeErrs := make(chan error)
cmdErrs := make(chan error)
cmdOuts := make(chan []byte)
g := errgroup.Group{}
if len(input) > 0 {
stdin, err := cmd.StdinPipe()
if err != nil {
return "", err
}
go func(input string, stdin io.WriteCloser) {
g.Go(func() error {
defer stdin.Close()
defer close(writeErrs)
size := len(input)
var n int
var err error
i := 0
for {
n, err = io.WriteString(stdin, input[i:])
n, err := io.WriteString(stdin, input[i:])
if err != nil {
writeErrs <- fmt.Errorf("failed while writing %d bytes to stdin of \"%s\": %v", len(input), command, err)
break
return fmt.Errorf("failed while writing %d bytes to stdin of \"%s\": %v", len(input), command, err)
}
i += n
if n == size {
break
if i == size {
return nil
}
}
}(input, stdin)
})
}
go func() {
defer close(cmdOuts)
defer close(cmdErrs)
var bytes []byte
bytes, err := cmd.Output()
g.Go(func() error {
// We use CombinedOutput to produce helpful error messages
// See https://github.com/roboll/helmfile/issues/1158
bs, err := cmd.CombinedOutput()
if err != nil {
cmdErrs <- fmt.Errorf("exec cmd=%s args=[%s] failed: %v", command, strings.Join(strArgs, ", "), err)
} else {
cmdOuts <- bytes
}
}()
args := strings.Join(strArgs, ", ")
shownCmd := []string{command}
if len(args) > 0 {
shownCmd = append(shownCmd, args)
}
for {
select {
case bytes := <-cmdOuts:
return string(bytes), nil
case err := <-cmdErrs:
return "", err
case err := <-writeErrs:
return "", err
var out string
out += fmt.Sprintf("\n\nCOMMAND:\n%s", Indent(strings.Join(shownCmd, " "), " "))
out += fmt.Sprintf("\n\nERROR:\n%s", Indent(err.Error(), " "))
if len(bs) > 0 {
out += fmt.Sprintf("\n\nCOMBINED OUTPUT:\n%s", Indent(string(bs), " "))
}
return fmt.Errorf("%v%s", err, out)
}
bytes = bs
return nil
})
if err := g.Wait(); err != nil {
return "", err
}
return string(bytes), nil
}
// indents a block of text with an indent string
func Indent(text, indent string) string {
var b strings.Builder
b.Grow(len(text) * 2)
lines := strings.Split(text, "\n")
last := len(lines) - 1
for i, j := range lines {
if i > 0 && i < last && j != "" {
b.WriteString("\n")
}
if j != "" {
b.WriteString(indent + j)
}
}
return b.String()
}
func (c *Context) ReadFile(filename string) (string, error) {
@@ -206,3 +245,15 @@ func RequiredEnv(name string) (string, error) {
return "", fmt.Errorf("required env var `%s` is not set", name)
}
func Required(warn string, val interface{}) (interface{}, error) {
if val == nil {
return nil, fmt.Errorf(warn)
} else if _, ok := val.(string); ok {
if val == "" {
return nil, fmt.Errorf(warn)
}
}
return val, nil
}
+44
View File
@@ -133,3 +133,47 @@ func TestTpl(t *testing.T) {
t.Errorf("unexpected result: expected=%v, actual=%v", expected, actual)
}
}
func TestRequired(t *testing.T) {
type args struct {
warn string
val interface{}
}
tests := []struct {
name string
args args
want interface{}
wantErr bool
}{
{
name: "required val is nil",
args: args{warn: "This value is required", val: nil},
want: nil,
wantErr: true,
},
{
name: "required val is empty string",
args: args{warn: "This value is required", val: ""},
want: nil,
wantErr: true,
},
{
name: "required val is existed",
args: args{warn: "This value is required", val: "foo"},
want: "foo",
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := Required(tt.args.warn, tt.args.val)
if (err != nil) != tt.wantErr {
t.Errorf("Required() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("Required() got = %v, want %v", got, tt.want)
}
})
}
}
+2 -2
View File
@@ -6,7 +6,7 @@ import (
"text/template"
)
func (c *Context) stringTemplate() *template.Template {
func (c *Context) newTemplate() *template.Template {
funcMap := sprig.TxtFuncMap()
for name, f := range c.createFuncMap() {
funcMap[name] = f
@@ -21,7 +21,7 @@ func (c *Context) stringTemplate() *template.Template {
}
func (c *Context) RenderTemplateToBuffer(s string, data ...interface{}) (*bytes.Buffer, error) {
var t, parseErr = c.stringTemplate().Parse(s)
var t, parseErr = c.newTemplate().Parse(s)
if parseErr != nil {
return nil, parseErr
}
+78
View File
@@ -250,3 +250,81 @@ func Test_renderTemplateToString(t *testing.T) {
})
}
}
func TestRenderTemplate_Required(t *testing.T) {
tests := []struct {
name string
s string
data map[string]interface{}
want string
wantErr bool
}{
{
name: ".foo is existed",
s: `{{ required ".foo.bar is required" .foo }}`,
data: map[string]interface{}{
"foo": "bar",
},
want: "bar",
wantErr: false,
},
{
name: ".foo.bar is existed",
s: `{{ required "foo.bar is required" .foo.bar }}`,
data: map[string]interface{}{
"foo": map[string]interface{}{
"bar": "FOO_BAR",
},
},
want: "FOO_BAR",
wantErr: false,
},
{
name: ".foo.bar is existed but value is nil",
s: `{{ required "foo.bar is required" .foo.bar }}`,
data: map[string]interface{}{
"foo": map[string]interface{}{
"bar": nil,
},
},
wantErr: true,
},
{
name: ".foo.bar is existed but value is empty string",
s: `{{ required "foo.bar is required" .foo.bar }}`,
data: map[string]interface{}{
"foo": map[string]interface{}{
"bar": "",
},
},
wantErr: true,
},
{
name: ".foo is nil",
s: `{{ required "foo is required" .foo }}`,
data: map[string]interface{}{
"foo": nil,
},
wantErr: true,
},
{
name: ".foo is a empty string",
s: `{{ required "foo is required" .foo }}`,
data: map[string]interface{}{
"foo": "",
},
wantErr: true,
},
}
for _, tt := range tests {
got, err := renderTemplateToString(tt.s, tt.data)
if (err != nil) != tt.wantErr {
t.Errorf("renderTemplateToString() for %s error = %v, wantErr %v", tt.name, err, tt.wantErr)
return
}
if got != tt.want {
t.Errorf("renderTemplateToString() for %s = %v, want %v", tt.name, got, tt.want)
}
}
}
+63
View File
@@ -0,0 +1,63 @@
package tmpl
import (
"errors"
"fmt"
"github.com/roboll/helmfile/pkg/plugins"
"github.com/variantdev/vals"
"sync"
)
//to generate mock run mockgen -source=expand_secret_ref.go -destination=expand_secrets_mock.go -package=tmpl
type valClient interface {
Eval(template map[string]interface{}) (map[string]interface{}, error)
}
var once sync.Once
var secretsClient valClient
func fetchSecretValue(path string) (string, error) {
tmpMap := make(map[string]interface{})
tmpMap["key"] = path
resultMap, err := fetchSecretValues(tmpMap)
if err != nil {
return "", err
}
rendered, ok := resultMap["key"]
if !ok {
return "", errors.New(fmt.Sprintf("unexpected error occurred, %v doesn't have 'key' key", resultMap))
}
result, ok := rendered.(string)
if !ok {
return "", errors.New(fmt.Sprintf("expected %v to be string", rendered))
}
return result, nil
}
func fetchSecretValues(values map[string]interface{}) (map[string]interface{}, error) {
var err error
// below lines are for tests
once.Do(func() {
var valRuntime *vals.Runtime
if secretsClient == nil {
valRuntime, err = plugins.ValsInstance()
if err != nil {
return
}
secretsClient = valRuntime
}
})
if secretsClient == nil {
return nil, err
}
resultMap, err := secretsClient.Eval(values)
if err != nil {
return nil, err
}
return resultMap, nil
}
+78
View File
@@ -0,0 +1,78 @@
package tmpl
import (
"errors"
"github.com/golang/mock/gomock"
"gotest.tools/assert"
"testing"
)
func Test_fetchSecretValue(t *testing.T) {
controller := gomock.NewController(t)
defer controller.Finish()
c := NewMockvalClient(controller)
secretsClient = c
secretPath := "ref+vault://key/#path"
expectArg := make(map[string]interface{})
expectArg["key"] = secretPath
valsResult := make(map[string]interface{})
valsResult["key"] = "key_value"
c.EXPECT().Eval(expectArg).Return(valsResult, nil)
result, err := fetchSecretValue(secretPath)
assert.NilError(t, err)
assert.Equal(t, result, "key_value")
}
func Test_fetchSecretValue_error(t *testing.T) {
controller := gomock.NewController(t)
defer controller.Finish()
c := NewMockvalClient(controller)
secretsClient = c
secretPath := "ref+vault://key/#path"
expectArg := make(map[string]interface{})
expectArg["key"] = secretPath
expectedErr := errors.New("some error")
c.EXPECT().Eval(expectArg).Return(nil, expectedErr)
result, err := fetchSecretValue(secretPath)
assert.Equal(t, err, expectedErr)
assert.Equal(t, result, "")
}
func Test_fetchSecretValue_no_key(t *testing.T) {
controller := gomock.NewController(t)
defer controller.Finish()
c := NewMockvalClient(controller)
secretsClient = c
secretPath := "ref+vault://key/#path"
expectArg := make(map[string]interface{})
expectArg["key"] = secretPath
valsResult := make(map[string]interface{})
c.EXPECT().Eval(expectArg).Return(valsResult, nil)
result, err := fetchSecretValue(secretPath)
assert.Error(t, err, "unexpected error occurred, map[] doesn't have 'key' key")
assert.Equal(t, result, "")
}
func Test_fetchSecretValue_invalid_type(t *testing.T) {
controller := gomock.NewController(t)
defer controller.Finish()
c := NewMockvalClient(controller)
secretsClient = c
secretPath := "ref+vault://key/#path"
expectArg := make(map[string]interface{})
expectArg["key"] = secretPath
valsResult := make(map[string]interface{})
valsResult["key"] = 10
c.EXPECT().Eval(expectArg).Return(valsResult, nil)
result, err := fetchSecretValue(secretPath)
assert.Error(t, err, "expected 10 to be string")
assert.Equal(t, result, "")
}
+45
View File
@@ -0,0 +1,45 @@
// Code generated by MockGen. DO NOT EDIT.
// Source: expand_secret_ref.go
package tmpl
import (
gomock "github.com/golang/mock/gomock"
reflect "reflect"
)
// MockvalClient is a mock of valClient interface
type MockvalClient struct {
ctrl *gomock.Controller
recorder *MockvalClientMockRecorder
}
// MockvalClientMockRecorder is the mock recorder for MockvalClient
type MockvalClientMockRecorder struct {
mock *MockvalClient
}
// NewMockvalClient creates a new mock instance
func NewMockvalClient(ctrl *gomock.Controller) *MockvalClient {
mock := &MockvalClient{ctrl: ctrl}
mock.recorder = &MockvalClientMockRecorder{mock}
return mock
}
// EXPECT returns an object that allows the caller to indicate expected use
func (_m *MockvalClient) EXPECT() *MockvalClientMockRecorder {
return _m.recorder
}
// Eval mocks base method
func (_m *MockvalClient) Eval(template map[string]interface{}) (map[string]interface{}, error) {
ret := _m.ctrl.Call(_m, "Eval", template)
ret0, _ := ret[0].(map[string]interface{})
ret1, _ := ret[1].(error)
return ret0, ret1
}
// Eval indicates an expected call of Eval
func (_mr *MockvalClientMockRecorder) Eval(arg0 interface{}) *gomock.Call {
return _mr.mock.ctrl.RecordCallWithMethodType(_mr.mock, "Eval", reflect.TypeOf((*MockvalClient)(nil).Eval), arg0)
}
+50
View File
@@ -0,0 +1,50 @@
repositories:
- name: incubator
url: https://kubernetes-charts-incubator.storage.googleapis.com
releases:
- name: kustomapp
chart: ./kustomapp
values:
- namePrefix: kustomapp-
- name: raw1
chart: incubator/raw
version: 0.1.0
values:
- resources:
- apiVersion: v1
kind: ConfigMap
metadata:
name: raw1
namespace: default
data:
foo: FOO
strategicMergePatches:
- apiVersion: v1
kind: ConfigMap
metadata:
name: raw1
namespace: default
data:
bar: BAR
- name: raw2
chart: incubator/raw
values:
- resources:
- apiVersion: v1
kind: ConfigMap
metadata:
name: raw2
namespace: default
data:
foo: FOO
jsonPatches:
- target:
version: v1
kind: ConfigMap
name: raw2
namespace: default
patch:
- op: replace
path: /data/baz
value: "BAZ"
+7
View File
@@ -0,0 +1,7 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: kustomapp
namespace: default
data:
kustomappfoo: FOO
@@ -0,0 +1,2 @@
resources:
- all.yaml
+7
View File
@@ -0,0 +1,7 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: kustomapp2
namespace: default
data:
kustomappfoo: FOO
@@ -0,0 +1,2 @@
resources:
- all.yaml
+5 -2
View File
@@ -77,6 +77,9 @@ ${helmfile} -f ${dir}/happypath.yaml template
code=$?
[ ${code} -eq 0 ] || fail "unexpected exit code returned by helmfile template: ${code}"
info "Applying ${dir}/happypath.yaml"
bash -c "${helmfile} -f ${dir}/happypath.yaml apply --detailed-exitcode; code="'$?'"; echo Code: "'$code'"; [ "'${code}'" -eq 2 ]" || fail "unexpected exit code returned by helmfile apply"
info "Syncing ${dir}/happypath.yaml"
${helmfile} -f ${dir}/happypath.yaml sync
wait_deploy_ready httpbin-httpbin
@@ -84,9 +87,9 @@ retry 5 "curl --fail $(minikube service --url --namespace=${test_ns} httpbin-htt
[ ${retry_result} -eq 0 ] || fail "httpbin failed to return 200 OK"
info "Applying ${dir}/happypath.yaml"
${helmfile} -f ${dir}/happypath.yaml apply
${helmfile} -f ${dir}/happypath.yaml apply --detailed-exitcode
code=$?
[ ${code} -eq 0 ] || fail "unexpected exit code returned by helmfile apply: ${code}"
[ ${code} -eq 0 ] || fail "unexpected exit code returned by helmfile apply: want 0, got ${code}"
info "Locking dependencies"
${helmfile} -f ${dir}/happypath.yaml deps