52 Commits

Author SHA1 Message Date
Erin Call
7cfe20db1f Merge pull request #69 from pelotech/cleanup-on-fail
Pass --cleanup-on-fail when so instructed
2020-01-07 13:18:31 -08:00
Erin Call
7fc8212451 Merge branch 'master' into cleanup-on-fail 2020-01-07 13:17:58 -08:00
Erin Call
7cb7ecec90 Merge pull request #70 from pelotech/external-param-reference
Mention the external docs in the PR template
2020-01-07 13:17:46 -08:00
Erin Call
55017eed0f Mention the external docs in the PR template [#63]
I just opened a PR with a config change and forgot about the plugin-
index docs...must be time to get around to this issue.
2020-01-07 13:04:21 -08:00
Erin Call
b6ba856c31 Pass CleanupOnFail to the Upgrade Step [#65]
I don't love the mismatch between the helm.Config field (CleanupOnFail)
and the setting name (cleanup_failed_upgrade). I do think the setting
name should contain "upgrade" since it's specific to the upgrade command,
but if I make the config field CleanupFailedUpgrade, it becomes the new
longest field name, and gofmt creates a bunch of churn. Is that a good
enough reason...?
2020-01-07 12:56:51 -08:00
Erin Call
8c6c6fbfa5 CleanupOnFail option for the upgrade Step [#65] 2020-01-07 12:53:55 -08:00
Erin Call
e071d23fef Merge pull request #68 from pelotech/ATOMICS
Atomics
2020-01-07 12:34:32 -08:00
Erin Call
c8b4ad4c46 Pass an atomic_upgrade setting to the Upgrade step [#64] 2020-01-07 12:21:55 -08:00
Erin Call
971e3f17cb Atomic option for the Upgrade step [#64] 2020-01-07 12:17:54 -08:00
Erin Call
ee3dc9ff0e Merge pull request #56 from pelotech/dotenv
Load .env files on startup
2020-01-02 13:51:57 -08:00
Erin Call
7ecfe70e3e Merge branch 'master' into dotenv 2020-01-02 13:30:19 -08:00
Erin Call
d5bd083bf5 Merge pull request #59 from pelotech/keep-history
Pass --keep-history when so instructed
2020-01-02 13:29:43 -08:00
Erin Call
8d8bcf78a7 Merge branch 'master' into keep-history 2020-01-02 13:26:53 -08:00
Joachim Hill-Grannec
222261d931 Merge branch 'master' into dotenv 2020-01-02 15:32:40 -05:00
Joachim Hill-Grannec
e694d93445 Merge pull request #57 from pelotech/documentation-template
Simple issue template for doc requests
2020-01-02 15:32:24 -05:00
Joachim Hill-Grannec
eaac6dd643 Merge pull request #62 from pelotech/values-arent-global
Put step-specific config in those steps
2020-01-02 15:32:05 -05:00
Joachim Hill-Grannec
c569059b87 Merge branch 'master' into dotenv 2020-01-02 15:30:06 -05:00
Erin Call
45428a2e25 Merge branch 'master' into keep-history 2020-01-02 12:29:32 -08:00
Erin Call
7b816ea257 Merge branch 'master' into values-arent-global 2020-01-02 12:29:15 -08:00
Erin Call
3c44be059e Merge pull request #60 from pelotech/lint-strictly
Pass --strict to `helm lint` when so instructed
2020-01-02 12:27:00 -08:00
Erin Call
11ffdc7210 Merge branch 'master' into lint-strictly 2020-01-02 11:44:09 -08:00
Erin Call
b4a13d9971 Merge pull request #58 from pelotech/conflict-resolution-doc
Docs for settings/environment conflict resolution
2020-01-02 11:43:57 -08:00
Erin Call
4330728215 Put step-specific config in those steps [#61]
This is just something that's been bugging me for a while--they're
specific to Lint and Upgrade, so that's where they belong.
2020-01-02 11:38:41 -08:00
Erin Call
3ae13d4b3c Pass --strict to helm lint when so instructed [#28] 2020-01-02 11:25:13 -08:00
Erin Call
17724e7015 Pass --keep-history when so instructed [#24] 2020-01-02 10:58:58 -08:00
Erin Call
04a2c48ece Docs for settings/environment conflict resolution [#45] 2020-01-02 09:23:02 -08:00
Erin Call
997a33fc0d Simple issue template for doc requests [#46] 2020-01-02 09:12:51 -08:00
Erin Call
280c8577a4 Merge branch 'master' into dotenv 2020-01-02 09:08:25 -08:00
Erin Call
51058470e5 Load .env files on startup [#6] 2020-01-02 09:05:26 -08:00
Joachim Hill-Grannec
7d750f097d Merge pull request #40 from pelotech/no-eks-support
Mention that EKS isn't currently supported [#5]
2020-01-01 16:47:18 -05:00
Erin Call
9c91a415b0 Merge branch 'master' into no-eks-support 2020-01-01 09:09:01 -08:00
Joachim Hill-Grannec
9e8de53f8f Merge pull request #52 from josmo/contributions
Directions on how to setup custom build of images till PR gets merged
2019-12-31 18:08:31 -05:00
Joachim Hill-Grannec
2893e09651 Merge branch 'master' into contributions 2019-12-31 18:07:55 -05:00
Joachim Hill-Grannec
69a15a8d48 Merge pull request #51 from pelotech/deprecation-warnings
Emit warnings about deprecated settings
2019-12-31 18:07:32 -05:00
Joachim Hill-Grannec
27827a2940 Merge branch 'master' into deprecation-warnings 2019-12-31 18:03:52 -05:00
Joachim Hill-Grannec
eb2d280d59 Merge pull request #55 from josmo/pin-image
pin version of docker image to alpine/helm:3.0.2
2019-12-31 18:03:32 -05:00
Joachim Hill-Grannec
3bf73acb42 Merge branch 'master' into deprecation-warnings 2019-12-31 18:03:00 -05:00
Joachim Hill-Grannec
997f49bb8d Merge branch 'master' into pin-image 2019-12-31 18:01:42 -05:00
Erin Call
86984bcecd Merge pull request #50 from pelotech/no-prefix
Remove support for the prefix setting [#48]
2019-12-31 15:00:52 -08:00
Joachim Hill-Grannec
c4329833f8 Merge branch 'master' into no-prefix 2019-12-31 18:00:00 -05:00
Joachim Hill-Grannec
3fa2d71559 pin version of docker image to alpine/helm:3.0.2 2019-12-31 17:57:32 -05:00
Joachim Hill-Grannec
f919966cf0 Merge pull request #54 from josmo/migration-link
link to migrate the deployments in the cluster
2019-12-31 17:56:21 -05:00
Joachim Hill-Grannec
fed4de2ed9 Update README.md
Co-Authored-By: Erin Call <hello@erincall.com>
2019-12-31 17:54:46 -05:00
Joachim Hill-Grannec
e641102770 link to migrate the deployments in the cluster 2019-12-31 17:34:53 -05:00
Joachim Hill-Grannec
51db551995 Merge pull request #53 from josmo/badges
Adding basic badges
2019-12-31 17:27:59 -05:00
Joachim Hill-Grannec
2389268fa2 Adding basic badges 2019-12-31 17:24:12 -05:00
Joachim Hill-Grannec
fe7ee09350 add link to contributing 2019-12-31 14:48:21 -05:00
Joachim Hill-Grannec
680989754a Directions on how to setup custom build of images till PR gets merged 2019-12-31 14:19:56 -05:00
Erin Call
7cd46bb8b1 Emit warnings about deprecated settings [#10]
These aren't an error case--the plugin will work just fine--but users
should be aware they (the settings) aren't being respected.
2019-12-31 10:03:53 -08:00
Erin Call
ab7abb699a Remove support for the prefix setting [#48]
The setting isn't necessary with modern versions of Drone, and it
creates a lot of edge-cases. The use-case doesn't justify the added
complexity.
2019-12-31 09:28:42 -08:00
Erin Call
8e5a32ff85 Merge branch 'master' into no-eks-support 2019-12-30 13:45:21 -08:00
Erin Call
12c8e08d6f Mention that EKS isn't currently supported [#5]
We'd like to support it eventually, but the current state of affairs
doesn't justify the effort.

Also removed some vestigial code that was copy-pasta from the kubeconfig
in drone-helm.
2019-12-27 10:49:22 -08:00
21 changed files with 246 additions and 215 deletions

11
.github/ISSUE_TEMPLATE/documentation.md vendored Normal file
View File

@@ -0,0 +1,11 @@
---
name: Documentation
about: Docs you'd like to see, or questions about existing docs
title: ''
labels: documentation
assignees: ''
---
**What needs explanation:**
<!-- e.g. "what happens when ____", "how do I ___", etc. -->

View File

@@ -3,6 +3,8 @@
Pre-merge checklist: Pre-merge checklist:
* [ ] Code changes have tests * [ ] Code changes have tests
* [ ] Any changes to the config are documented in `docs/parameter_reference.md` * [ ] Any config changes are documented:
* [ ] Any new _required_ config is documented in `README.md` * If the change touches _required_ config, there's a corresponding update to `README.md`
* There's a corresponding update to `docs/parameter_reference.md`
* There's a pull request to update [the parameter reference in drone-plugin-index](https://github.com/drone/drone-plugin-index/blob/master/content/pelotech/drone-helm3/index.md)
* [ ] Any large changes have been verified by running a Drone job * [ ] Any large changes have been verified by running a Drone job

2
.gitignore vendored
View File

@@ -5,6 +5,8 @@
*.so *.so
*.dylib *.dylib
.idea
# Test binary, built with `go test -c` # Test binary, built with `go test -c`
*.test *.test

View File

@@ -1,4 +1,4 @@
FROM alpine/helm FROM alpine/helm:3.0.2
MAINTAINER Erin Call <erin@liffft.com> MAINTAINER Erin Call <erin@liffft.com>
COPY build/drone-helm /bin/drone-helm COPY build/drone-helm /bin/drone-helm

View File

@@ -1,5 +1,9 @@
# Drone plugin for Helm 3 # Drone plugin for Helm 3
[![Build Status](https://cloud.drone.io/api/badges/pelotech/drone-helm3/status.svg)](https://cloud.drone.io/pelotech/drone-helm3)
[![Go Report](https://goreportcard.com/badge/github.com/pelotech/drone-helm3)](https://goreportcard.com/report/github.com/pelotech/drone-helm3)
[![](https://images.microbadger.com/badges/image/pelotech/drone-helm3.svg)](https://microbadger.com/images/pelotech/drone-helm3 "Get your own image badge on microbadger.com")
This plugin provides an interface between [Drone](https://drone.io/) and [Helm 3](https://github.com/kubernetes/helm): This plugin provides an interface between [Drone](https://drone.io/) and [Helm 3](https://github.com/kubernetes/helm):
* Lint your charts * Lint your charts
@@ -58,8 +62,11 @@ steps:
drone-helm3 is largely backwards-compatible with drone-helm. There are some known differences: drone-helm3 is largely backwards-compatible with drone-helm. There are some known differences:
* `prefix` must be supplied via the `settings` block, not `environment`. * You'll need to migrate the deployments in the cluster [helm-v2-to-helm-v3](https://helm.sh/blog/migrate-from-helm-v2-to-helm-v3/).
* Several settings no longer have any effect: * EKS is not supported. See [#5](https://github.com/pelotech/drone-helm3/issues/5) for more information.
* The `prefix` setting is no longer supported. If you were relying on the `prefix` setting with `secrets: [...]`, you'll need to switch to the `from_secret` syntax.
* During uninstallations, the release history is purged by default. Use `keep_history: true` to return to the old behavior.
* Several settings no longer have any effect. The plugin will produce warnings if any of these are present:
* `purge` -- this is the default behavior in Helm 3 * `purge` -- this is the default behavior in Helm 3
* `recreate_pods` * `recreate_pods`
* `tiller_ns` * `tiller_ns`
@@ -69,3 +76,11 @@ drone-helm3 is largely backwards-compatible with drone-helm. There are some know
* `stable_repo_url` * `stable_repo_url`
Since helm 3 does not require Tiller, we also recommend switching to a service account with less-expansive permissions. Since helm 3 does not require Tiller, we also recommend switching to a service account with less-expansive permissions.
### [Contributing](docs/contributing.md)
This repo is setup in a way that if you enable a personal drone server to build your fork it will
build and publish your image (makes it easier to test PRs and use the image till the contributions get merged)
* Build local ```DRONE_REPO_OWNER=josmo DRONE_REPO_NAME=drone-ecs drone exec```
* on your server (or cloud.drone.io) just make sure you have DOCKER_USERNAME, DOCKER_PASSWORD, and PLUGIN_REPO set as secrets

View File

@@ -24,16 +24,4 @@ users:
user: user:
{{- if .Token }} {{- if .Token }}
token: {{ .Token }} token: {{ .Token }}
{{- else if .EKSCluster }}
exec:
apiVersion: client.authentication.k8s.io/v1alpha1
command: aws-iam-authenticator
args:
- "token"
- "-i"
- "{{ .EKSCluster }}"
{{- if .EKSRoleARN }}
- "-r"
- "{{ .EKSRoleARN }}"
{{- end }}
{{- end }} {{- end }}

View File

@@ -4,6 +4,7 @@ import (
"fmt" "fmt"
"os" "os"
_ "github.com/joho/godotenv/autoload"
"github.com/pelotech/drone-helm3/internal/helm" "github.com/pelotech/drone-helm3/internal/helm"
) )

View File

@@ -7,7 +7,6 @@
| update_dependencies | boolean | Calls `helm dependency update` before running the main command.| | update_dependencies | boolean | Calls `helm dependency update` before running the main command.|
| helm_repos | list\<string\> | Calls `helm repo add $repo` before running the main command. Each string should be formatted as `repo_name=https://repo.url/`. | | helm_repos | list\<string\> | Calls `helm repo add $repo` before running the main command. Each string should be formatted as `repo_name=https://repo.url/`. |
| namespace | string | Kubernetes namespace to use for this operation. | | namespace | string | Kubernetes namespace to use for this operation. |
| prefix | string | Expect environment variables to be prefixed with the given string. For more details, see "Using the prefix setting" below. |
| debug | boolean | Generate debug output within drone-helm3 and pass `--debug` to all helm commands. Use with care, since the debug output may include secrets. | | debug | boolean | Generate debug output within drone-helm3 and pass `--debug` to all helm commands. Use with care, since the debug output may include secrets. |
## Linting ## Linting
@@ -20,6 +19,7 @@ Linting is only triggered when the `helm_command` setting is "lint".
| values | list\<string\> | | Chart values to use as the `--set` argument to `helm lint`. | | values | list\<string\> | | Chart values to use as the `--set` argument to `helm lint`. |
| string_values | list\<string\> | | Chart values to use as the `--set-string` argument to `helm lint`. | | string_values | list\<string\> | | Chart values to use as the `--set-string` argument to `helm lint`. |
| values_files | list\<string\> | | Values to use as `--values` arguments to `helm lint`. | | values_files | list\<string\> | | Values to use as `--values` arguments to `helm lint`. |
| lint_strictly | boolean | | Pass `--strict` to `helm lint`, to turn warnings into errors. |
## Installation ## Installation
@@ -38,6 +38,8 @@ Installations are triggered when the `helm_command` setting is "upgrade." They c
| wait | boolean | | Wait until kubernetes resources are in a ready state before marking the installation successful. | | wait | boolean | | Wait until kubernetes resources are in a ready state before marking the installation successful. |
| timeout | duration | | Timeout for any *individual* Kubernetes operation. The installation's full runtime may exceed this duration. | | timeout | duration | | Timeout for any *individual* Kubernetes operation. The installation's full runtime may exceed this duration. |
| force | boolean | | Pass `--force` to `helm upgrade`. | | force | boolean | | Pass `--force` to `helm upgrade`. |
| atomic_upgrade | boolean | | Pass `--atomic` to `helm upgrade`. |
| cleanup_failed_upgrade | boolean | | Pass `--cleanup-on-fail` to `helm upgrade`. |
| values | list\<string\> | | Chart values to use as the `--set` argument to `helm upgrade`. | | values | list\<string\> | | Chart values to use as the `--set` argument to `helm upgrade`. |
| string_values | list\<string\> | | Chart values to use as the `--set-string` argument to `helm upgrade`. | | string_values | list\<string\> | | Chart values to use as the `--set-string` argument to `helm upgrade`. |
| values_files | list\<string\> | | Values to use as `--values` arguments to `helm upgrade`. | | values_files | list\<string\> | | Values to use as `--values` arguments to `helm upgrade`. |
@@ -55,6 +57,7 @@ Uninstallations are triggered when the `helm_command` setting is "uninstall" or
| kubernetes_token | string | yes | Token for authenticating to Kubernetes. | | kubernetes_token | string | yes | Token for authenticating to Kubernetes. |
| service_account | string | | Service account for authenticating to Kubernetes. Default is `helm`. | | service_account | string | | Service account for authenticating to Kubernetes. Default is `helm`. |
| kubernetes_certificate | string | | Base64 encoded TLS certificate used by the Kubernetes cluster's certificate authority. | | kubernetes_certificate | string | | Base64 encoded TLS certificate used by the Kubernetes cluster's certificate authority. |
| keep_history | boolean | | Pass `--keep-history` to `helm uninstall`, to retain the release history. |
| dry_run | boolean | | Pass `--dry-run` to `helm uninstall`. | | dry_run | boolean | | Pass `--dry-run` to `helm uninstall`. |
| timeout | duration | | Timeout for any *individual* Kubernetes operation. The uninstallation's full runtime may exceed this duration. | | timeout | duration | | Timeout for any *individual* Kubernetes operation. The uninstallation's full runtime may exceed this duration. |
| skip_tls_verify | boolean | | Connect to the Kubernetes cluster without checking for a valid TLS certificate. Not recommended in production. | | skip_tls_verify | boolean | | Connect to the Kubernetes cluster without checking for a valid TLS certificate. Not recommended in production. |
@@ -62,7 +65,9 @@ Uninstallations are triggered when the `helm_command` setting is "uninstall" or
### Where to put settings ### Where to put settings
Any setting (with the exception of `prefix`; [see below](#user-content-using-the-prefix-setting)), can go in either the `settings` or `environment` section. Any setting can go in either the `settings` or `environment` section. If a setting exists in _both_ sections, the version in `environment` will override the version in `settings`.
We recommend putting all drone-helm3 configuration in the `settings` block and limiting the `environment` block to variables that are used when building your charts.
### Formatting non-string values ### Formatting non-string values
@@ -87,45 +92,3 @@ Note that **list members must not contain commas**. Both of the following are eq
values_files: [ "./over_9,000.yml" ] values_files: [ "./over_9,000.yml" ]
values_files: [ "./over_9", "000.yml" ] values_files: [ "./over_9", "000.yml" ]
``` ```
### Using the `prefix` setting
Because the prefix setting is meta-configuration, it has some inherent edge-cases. Here is what it does in the cases we've thought of:
Unlike the other settings, it must be declared in the `settings` block, not `environment`:
```yaml
settings:
prefix: helm # drone-helm3 will look for environment variables called HELM_VARNAME
environment:
prefix: armet # no effect
```
It does not apply to configuration in the `settings` block, only in `environment`:
```yaml
settings:
prefix: helm
helm_timeout: 5m # no effect
environment:
helm_timeout: 2m # timeout will be 2 minutes
```
If the environment contains a variable in non-prefixed form, it will still be applied:
```yaml
settings:
prefix: helm
environment:
timeout: 2m # timeout will be 2 minutes
```
If the environment contains both the prefixed and non-prefixed forms, drone-helm3 will use the prefixed form:
```yaml
settings:
prefix: helm
environment:
timeout: 5m # overridden
helm_timeout: 2m # timeout will be 2 minutes
```

1
go.mod
View File

@@ -4,6 +4,7 @@ go 1.13
require ( require (
github.com/golang/mock v1.3.1 github.com/golang/mock v1.3.1
github.com/joho/godotenv v1.3.0 // indirect
github.com/kelseyhightower/envconfig v1.4.0 github.com/kelseyhightower/envconfig v1.4.0
github.com/stretchr/testify v1.4.0 github.com/stretchr/testify v1.4.0
golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f // indirect golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f // indirect

2
go.sum
View File

@@ -2,6 +2,8 @@ github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/golang/mock v1.3.1 h1:qGJ6qTW+x6xX/my+8YUVl4WNpX9B7+/l2tRsHGZ7f2s= github.com/golang/mock v1.3.1 h1:qGJ6qTW+x6xX/my+8YUVl4WNpX9B7+/l2tRsHGZ7f2s=
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc=
github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
github.com/kelseyhightower/envconfig v1.4.0 h1:Im6hONhd3pLkfDFsbRgu68RDNkGF1r3dvMUtDTo2cv8= github.com/kelseyhightower/envconfig v1.4.0 h1:Im6hONhd3pLkfDFsbRgu68RDNkGF1r3dvMUtDTo2cv8=
github.com/kelseyhightower/envconfig v1.4.0/go.mod h1:cccZRl6mQpaq41TPp5QxidR+Sa3axMbJDNb//FQX6Gg= github.com/kelseyhightower/envconfig v1.4.0/go.mod h1:cccZRl6mQpaq41TPp5QxidR+Sa3axMbJDNb//FQX6Gg=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=

View File

@@ -4,22 +4,26 @@ import (
"fmt" "fmt"
"github.com/kelseyhightower/envconfig" "github.com/kelseyhightower/envconfig"
"io" "io"
"os"
"regexp" "regexp"
"strings"
) )
var justNumbers = regexp.MustCompile(`^\d+$`) var (
justNumbers = regexp.MustCompile(`^\d+$`)
deprecatedVars = []string{"PURGE", "RECREATE_PODS", "TILLER_NS", "UPGRADE", "CANARY_IMAGE", "CLIENT_ONLY", "STABLE_REPO_URL"}
)
// The Config struct captures the `settings` and `environment` blocks in the application's drone // The Config struct captures the `settings` and `environment` blocks in the application's drone
// config. Configuration in drone's `settings` block arrives as uppercase env vars matching the // config. Configuration in drone's `settings` block arrives as uppercase env vars matching the
// config key, prefixed with `PLUGIN_`. Config from the `environment` block is uppercased, but does // config key, prefixed with `PLUGIN_`. Config from the `environment` block is uppercased, but does
// not have the `PLUGIN_` prefix. It may, however, be prefixed with the value in `$PLUGIN_PREFIX`. // not have the `PLUGIN_` prefix.
type Config struct { type Config struct {
// Configuration for drone-helm itself // Configuration for drone-helm itself
Command string `envconfig:"HELM_COMMAND"` // Helm command to run Command string `envconfig:"HELM_COMMAND"` // Helm command to run
DroneEvent string `envconfig:"DRONE_BUILD_EVENT"` // Drone event that invoked this plugin. DroneEvent string `envconfig:"DRONE_BUILD_EVENT"` // Drone event that invoked this plugin.
UpdateDependencies bool `split_words:"true"` // Call `helm dependency update` before the main command UpdateDependencies bool `split_words:"true"` // Call `helm dependency update` before the main command
AddRepos []string `envconfig:"HELM_REPOS"` // Call `helm repo add` before the main command AddRepos []string `envconfig:"HELM_REPOS"` // Call `helm repo add` before the main command
Prefix string `` // Prefix to use when looking up secret env vars
Debug bool `` // Generate debug output and pass --debug to all helm commands Debug bool `` // Generate debug output and pass --debug to all helm commands
Values string `` // Argument to pass to --set in applicable helm commands Values string `` // Argument to pass to --set in applicable helm commands
StringValues string `split_words:"true"` // Argument to pass to --set-string in applicable helm commands StringValues string `split_words:"true"` // Argument to pass to --set-string in applicable helm commands
@@ -34,10 +38,14 @@ type Config struct {
DryRun bool `split_words:"true"` // Pass --dry-run to applicable helm commands DryRun bool `split_words:"true"` // Pass --dry-run to applicable helm commands
Wait bool `` // Pass --wait to applicable helm commands Wait bool `` // Pass --wait to applicable helm commands
ReuseValues bool `split_words:"true"` // Pass --reuse-values to `helm upgrade` ReuseValues bool `split_words:"true"` // Pass --reuse-values to `helm upgrade`
KeepHistory bool `split_words:"true"` // Pass --keep-history to `helm uninstall`
Timeout string `` // Argument to pass to --timeout in applicable helm commands Timeout string `` // Argument to pass to --timeout in applicable helm commands
Chart string `` // Chart argument to use in applicable helm commands Chart string `` // Chart argument to use in applicable helm commands
Release string `` // Release argument to use in applicable helm commands Release string `` // Release argument to use in applicable helm commands
Force bool `` // Pass --force to applicable helm commands Force bool `` // Pass --force to applicable helm commands
AtomicUpgrade bool `split_words:"true"` // Pass --atomic to `helm upgrade`
CleanupOnFail bool `envconfig:"CLEANUP_FAILED_UPGRADE"` // Pass --cleanup-on-fail to `helm upgrade`
LintStrictly bool `split_words:"true"` // Pass --strict to `helm lint`
Stdout io.Writer `ignored:"true"` Stdout io.Writer `ignored:"true"`
Stderr io.Writer `ignored:"true"` Stderr io.Writer `ignored:"true"`
@@ -53,18 +61,10 @@ func NewConfig(stdout, stderr io.Writer) (*Config, error) {
return nil, err return nil, err
} }
prefix := cfg.Prefix
if err := envconfig.Process("", &cfg); err != nil { if err := envconfig.Process("", &cfg); err != nil {
return nil, err return nil, err
} }
if prefix != "" {
if err := envconfig.Process(cfg.Prefix, &cfg); err != nil {
return nil, err
}
}
if justNumbers.MatchString(cfg.Timeout) { if justNumbers.MatchString(cfg.Timeout) {
cfg.Timeout = fmt.Sprintf("%ss", cfg.Timeout) cfg.Timeout = fmt.Sprintf("%ss", cfg.Timeout)
} }
@@ -73,6 +73,8 @@ func NewConfig(stdout, stderr io.Writer) (*Config, error) {
cfg.logDebug() cfg.logDebug()
} }
cfg.deprecationWarn()
return &cfg, nil return &cfg, nil
} }
@@ -82,3 +84,13 @@ func (cfg Config) logDebug() {
} }
fmt.Fprintf(cfg.Stderr, "Generated config: %+v\n", cfg) fmt.Fprintf(cfg.Stderr, "Generated config: %+v\n", cfg)
} }
func (cfg *Config) deprecationWarn() {
for _, varname := range deprecatedVars {
_, barePresent := os.LookupEnv(varname)
_, prefixedPresent := os.LookupEnv("PLUGIN_" + varname)
if barePresent || prefixedPresent {
fmt.Fprintf(cfg.Stderr, "Warning: ignoring deprecated '%s' setting\n", strings.ToLower(varname))
}
}
}

View File

@@ -1,6 +1,7 @@
package helm package helm
import ( import (
"fmt"
"github.com/stretchr/testify/suite" "github.com/stretchr/testify/suite"
"os" "os"
"strings" "strings"
@@ -19,7 +20,6 @@ func TestConfigTestSuite(t *testing.T) {
} }
func (suite *ConfigTestSuite) TestNewConfigWithPluginPrefix() { func (suite *ConfigTestSuite) TestNewConfigWithPluginPrefix() {
suite.unsetenv("PLUGIN_PREFIX")
suite.unsetenv("HELM_COMMAND") suite.unsetenv("HELM_COMMAND")
suite.unsetenv("UPDATE_DEPENDENCIES") suite.unsetenv("UPDATE_DEPENDENCIES")
suite.unsetenv("DEBUG") suite.unsetenv("DEBUG")
@@ -37,7 +37,6 @@ func (suite *ConfigTestSuite) TestNewConfigWithPluginPrefix() {
} }
func (suite *ConfigTestSuite) TestNewConfigWithNoPrefix() { func (suite *ConfigTestSuite) TestNewConfigWithNoPrefix() {
suite.unsetenv("PLUGIN_PREFIX")
suite.unsetenv("PLUGIN_HELM_COMMAND") suite.unsetenv("PLUGIN_HELM_COMMAND")
suite.unsetenv("PLUGIN_UPDATE_DEPENDENCIES") suite.unsetenv("PLUGIN_UPDATE_DEPENDENCIES")
suite.unsetenv("PLUGIN_DEBUG") suite.unsetenv("PLUGIN_DEBUG")
@@ -54,56 +53,14 @@ func (suite *ConfigTestSuite) TestNewConfigWithNoPrefix() {
suite.True(cfg.Debug) suite.True(cfg.Debug)
} }
func (suite *ConfigTestSuite) TestNewConfigWithConfigurablePrefix() {
suite.unsetenv("API_SERVER")
suite.unsetenv("PLUGIN_API_SERVER")
suite.setenv("PLUGIN_PREFIX", "prix_fixe")
suite.setenv("PRIX_FIXE_API_SERVER", "your waiter this evening")
cfg, err := NewConfig(&strings.Builder{}, &strings.Builder{})
suite.Require().NoError(err)
suite.Equal("prix_fixe", cfg.Prefix)
suite.Equal("your waiter this evening", cfg.APIServer)
}
func (suite *ConfigTestSuite) TestPrefixSettingDoesNotAffectPluginPrefix() {
suite.setenv("PLUGIN_PREFIX", "IXFREP")
suite.setenv("PLUGIN_HELM_COMMAND", "wake me up")
suite.setenv("IXFREP_PLUGIN_HELM_COMMAND", "send me to sleep inside")
cfg, err := NewConfig(&strings.Builder{}, &strings.Builder{})
suite.Require().NoError(err)
suite.Equal("wake me up", cfg.Command)
}
func (suite *ConfigTestSuite) TestPrefixSettingMustHavePluginPrefix() {
suite.unsetenv("PLUGIN_PREFIX")
suite.setenv("PREFIX", "refpix")
suite.setenv("HELM_COMMAND", "gimme more")
suite.setenv("REFPIX_HELM_COMMAND", "gimme less")
cfg, err := NewConfig(&strings.Builder{}, &strings.Builder{})
suite.Require().NoError(err)
suite.Equal("gimme more", cfg.Command)
}
func (suite *ConfigTestSuite) TestNewConfigWithConflictingVariables() { func (suite *ConfigTestSuite) TestNewConfigWithConflictingVariables() {
suite.setenv("PLUGIN_HELM_COMMAND", "execute order 66") suite.setenv("PLUGIN_HELM_COMMAND", "execute order 66")
suite.setenv("HELM_COMMAND", "defend the jedi") // values from the `environment` block override those from `settings` suite.setenv("HELM_COMMAND", "defend the jedi") // values from the `environment` block override those from `settings`
suite.setenv("PLUGIN_PREFIX", "prod")
suite.setenv("TIMEOUT", "5m0s")
suite.setenv("PROD_TIMEOUT", "2m30s") // values from prefixed env vars override those from non-prefixed ones
cfg, err := NewConfig(&strings.Builder{}, &strings.Builder{}) cfg, err := NewConfig(&strings.Builder{}, &strings.Builder{})
suite.Require().NoError(err) suite.Require().NoError(err)
suite.Equal("defend the jedi", cfg.Command) suite.Equal("defend the jedi", cfg.Command)
suite.Equal("2m30s", cfg.Timeout)
} }
func (suite *ConfigTestSuite) TestNewConfigInfersNumbersAreSeconds() { func (suite *ConfigTestSuite) TestNewConfigInfersNumbersAreSeconds() {
@@ -123,6 +80,24 @@ func (suite *ConfigTestSuite) TestNewConfigSetsWriters() {
suite.Equal(stderr, cfg.Stderr) suite.Equal(stderr, cfg.Stderr)
} }
func (suite *ConfigTestSuite) TestDeprecatedSettingWarnings() {
for _, varname := range deprecatedVars {
suite.setenv(varname, "deprecoat") // environment-block entries should cause warnings
}
suite.unsetenv("PURGE")
suite.setenv("PLUGIN_PURGE", "true") // settings-block entries should cause warnings
suite.setenv("UPGRADE", "") // entries should cause warnings even when set to empty string
stderr := &strings.Builder{}
_, err := NewConfig(&strings.Builder{}, stderr)
suite.NoError(err)
for _, varname := range deprecatedVars {
suite.Contains(stderr.String(), fmt.Sprintf("Warning: ignoring deprecated '%s' setting\n", strings.ToLower(varname)))
}
}
func (suite *ConfigTestSuite) TestLogDebug() { func (suite *ConfigTestSuite) TestLogDebug() {
suite.setenv("DEBUG", "true") suite.setenv("DEBUG", "true")
suite.setenv("HELM_COMMAND", "upgrade") suite.setenv("HELM_COMMAND", "upgrade")

View File

@@ -29,13 +29,10 @@ func NewPlan(cfg Config) (*Plan, error) {
p := Plan{ p := Plan{
cfg: cfg, cfg: cfg,
runCfg: run.Config{ runCfg: run.Config{
Debug: cfg.Debug, Debug: cfg.Debug,
Values: cfg.Values, Namespace: cfg.Namespace,
StringValues: cfg.StringValues, Stdout: cfg.Stdout,
ValuesFiles: cfg.ValuesFiles, Stderr: cfg.Stderr,
Namespace: cfg.Namespace,
Stdout: cfg.Stdout,
Stderr: cfg.Stderr,
}, },
} }
@@ -101,14 +98,19 @@ var upgrade = func(cfg Config) []Step {
steps = append(steps, depUpdate(cfg)...) steps = append(steps, depUpdate(cfg)...)
} }
steps = append(steps, &run.Upgrade{ steps = append(steps, &run.Upgrade{
Chart: cfg.Chart, Chart: cfg.Chart,
Release: cfg.Release, Release: cfg.Release,
ChartVersion: cfg.ChartVersion, ChartVersion: cfg.ChartVersion,
DryRun: cfg.DryRun, DryRun: cfg.DryRun,
Wait: cfg.Wait, Wait: cfg.Wait,
ReuseValues: cfg.ReuseValues, Values: cfg.Values,
Timeout: cfg.Timeout, StringValues: cfg.StringValues,
Force: cfg.Force, ValuesFiles: cfg.ValuesFiles,
ReuseValues: cfg.ReuseValues,
Timeout: cfg.Timeout,
Force: cfg.Force,
Atomic: cfg.AtomicUpgrade,
CleanupOnFail: cfg.CleanupOnFail,
}) })
return steps return steps
@@ -120,8 +122,9 @@ var uninstall = func(cfg Config) []Step {
steps = append(steps, depUpdate(cfg)...) steps = append(steps, depUpdate(cfg)...)
} }
steps = append(steps, &run.Uninstall{ steps = append(steps, &run.Uninstall{
Release: cfg.Release, Release: cfg.Release,
DryRun: cfg.DryRun, DryRun: cfg.DryRun,
KeepHistory: cfg.KeepHistory,
}) })
return steps return steps
@@ -133,7 +136,11 @@ var lint = func(cfg Config) []Step {
steps = append(steps, depUpdate(cfg)...) steps = append(steps, depUpdate(cfg)...)
} }
steps = append(steps, &run.Lint{ steps = append(steps, &run.Lint{
Chart: cfg.Chart, Chart: cfg.Chart,
Values: cfg.Values,
StringValues: cfg.StringValues,
ValuesFiles: cfg.ValuesFiles,
Strict: cfg.LintStrictly,
}) })
return steps return steps

View File

@@ -33,24 +33,18 @@ func (suite *PlanTestSuite) TestNewPlan() {
stdout := strings.Builder{} stdout := strings.Builder{}
stderr := strings.Builder{} stderr := strings.Builder{}
cfg := Config{ cfg := Config{
Command: "help", Command: "help",
Debug: false, Debug: false,
Values: "steadfastness,forthrightness", Namespace: "outer",
StringValues: "tensile_strength,flexibility", Stdout: &stdout,
ValuesFiles: []string{"/root/price_inventory.yml"}, Stderr: &stderr,
Namespace: "outer",
Stdout: &stdout,
Stderr: &stderr,
} }
runCfg := run.Config{ runCfg := run.Config{
Debug: false, Debug: false,
Values: "steadfastness,forthrightness", Namespace: "outer",
StringValues: "tensile_strength,flexibility", Stdout: &stdout,
ValuesFiles: []string{"/root/price_inventory.yml"}, Stderr: &stderr,
Namespace: "outer",
Stdout: &stdout,
Stderr: &stderr,
} }
stepOne.EXPECT(). stepOne.EXPECT().
@@ -136,14 +130,19 @@ func (suite *PlanTestSuite) TestExecuteAbortsOnError() {
func (suite *PlanTestSuite) TestUpgrade() { func (suite *PlanTestSuite) TestUpgrade() {
cfg := Config{ cfg := Config{
ChartVersion: "seventeen", ChartVersion: "seventeen",
DryRun: true, DryRun: true,
Wait: true, Wait: true,
ReuseValues: true, Values: "steadfastness,forthrightness",
Timeout: "go sit in the corner", StringValues: "tensile_strength,flexibility",
Chart: "billboard_top_100", ValuesFiles: []string{"/root/price_inventory.yml"},
Release: "post_malone_circles", ReuseValues: true,
Force: true, Timeout: "go sit in the corner",
Chart: "billboard_top_100",
Release: "post_malone_circles",
Force: true,
AtomicUpgrade: true,
CleanupOnFail: true,
} }
steps := upgrade(cfg) steps := upgrade(cfg)
@@ -154,14 +153,19 @@ func (suite *PlanTestSuite) TestUpgrade() {
upgrade, _ := steps[1].(*run.Upgrade) upgrade, _ := steps[1].(*run.Upgrade)
expected := &run.Upgrade{ expected := &run.Upgrade{
Chart: cfg.Chart, Chart: cfg.Chart,
Release: cfg.Release, Release: cfg.Release,
ChartVersion: cfg.ChartVersion, ChartVersion: cfg.ChartVersion,
DryRun: true, DryRun: true,
Wait: cfg.Wait, Wait: cfg.Wait,
ReuseValues: cfg.ReuseValues, Values: "steadfastness,forthrightness",
Timeout: cfg.Timeout, StringValues: "tensile_strength,flexibility",
Force: cfg.Force, ValuesFiles: []string{"/root/price_inventory.yml"},
ReuseValues: cfg.ReuseValues,
Timeout: cfg.Timeout,
Force: cfg.Force,
Atomic: true,
CleanupOnFail: true,
} }
suite.Equal(expected, upgrade) suite.Equal(expected, upgrade)
@@ -198,6 +202,7 @@ func (suite *PlanTestSuite) TestUninstall() {
DryRun: true, DryRun: true,
Timeout: "think about what you did", Timeout: "think about what you did",
Release: "jetta_id_love_to_change_the_world", Release: "jetta_id_love_to_change_the_world",
KeepHistory: true,
} }
steps := uninstall(cfg) steps := uninstall(cfg)
@@ -220,8 +225,9 @@ func (suite *PlanTestSuite) TestUninstall() {
suite.Require().IsType(&run.Uninstall{}, steps[1]) suite.Require().IsType(&run.Uninstall{}, steps[1])
actual, _ := steps[1].(*run.Uninstall) actual, _ := steps[1].(*run.Uninstall)
expected = &run.Uninstall{ expected = &run.Uninstall{
Release: "jetta_id_love_to_change_the_world", Release: "jetta_id_love_to_change_the_world",
DryRun: true, DryRun: true,
KeepHistory: true,
} }
suite.Equal(expected, actual) suite.Equal(expected, actual)
} }
@@ -299,14 +305,22 @@ func (suite *PlanTestSuite) TestAddRepos() {
func (suite *PlanTestSuite) TestLint() { func (suite *PlanTestSuite) TestLint() {
cfg := Config{ cfg := Config{
Chart: "./flow", Chart: "./flow",
Values: "steadfastness,forthrightness",
StringValues: "tensile_strength,flexibility",
ValuesFiles: []string{"/root/price_inventory.yml"},
LintStrictly: true,
} }
steps := lint(cfg) steps := lint(cfg)
suite.Equal(1, len(steps)) suite.Equal(1, len(steps))
want := &run.Lint{ want := &run.Lint{
Chart: "./flow", Chart: "./flow",
Values: "steadfastness,forthrightness",
StringValues: "tensile_strength,flexibility",
ValuesFiles: []string{"/root/price_inventory.yml"},
Strict: true,
} }
suite.Equal(want, steps[0]) suite.Equal(want, steps[0])
} }

View File

@@ -6,11 +6,8 @@ import (
// Config contains configuration applicable to all helm commands // Config contains configuration applicable to all helm commands
type Config struct { type Config struct {
Debug bool Debug bool
Values string Namespace string
StringValues string Stdout io.Writer
ValuesFiles []string Stderr io.Writer
Namespace string
Stdout io.Writer
Stderr io.Writer
} }

View File

@@ -6,8 +6,12 @@ import (
// Lint is an execution step that calls `helm lint` when executed. // Lint is an execution step that calls `helm lint` when executed.
type Lint struct { type Lint struct {
Chart string Chart string
cmd cmd Values string
StringValues string
ValuesFiles []string
Strict bool
cmd cmd
} }
// Execute executes the `helm lint` command. // Execute executes the `helm lint` command.
@@ -32,15 +36,18 @@ func (l *Lint) Prepare(cfg Config) error {
args = append(args, "lint") args = append(args, "lint")
if cfg.Values != "" { if l.Values != "" {
args = append(args, "--set", cfg.Values) args = append(args, "--set", l.Values)
} }
if cfg.StringValues != "" { if l.StringValues != "" {
args = append(args, "--set-string", cfg.StringValues) args = append(args, "--set-string", l.StringValues)
} }
for _, vFile := range cfg.ValuesFiles { for _, vFile := range l.ValuesFiles {
args = append(args, "--values", vFile) args = append(args, "--values", vFile)
} }
if l.Strict {
args = append(args, "--strict")
}
args = append(args, l.Chart) args = append(args, l.Chart)

View File

@@ -80,14 +80,14 @@ func (suite *LintTestSuite) TestPrepareRequiresChart() {
func (suite *LintTestSuite) TestPrepareWithLintFlags() { func (suite *LintTestSuite) TestPrepareWithLintFlags() {
defer suite.ctrl.Finish() defer suite.ctrl.Finish()
cfg := Config{ cfg := Config{}
l := Lint{
Chart: "./uk/top_40",
Values: "width=5", Values: "width=5",
StringValues: "version=2.0", StringValues: "version=2.0",
ValuesFiles: []string{"/usr/local/underrides", "/usr/local/overrides"}, ValuesFiles: []string{"/usr/local/underrides", "/usr/local/overrides"},
} Strict: true,
l := Lint{
Chart: "./uk/top_40",
} }
command = func(path string, args ...string) cmd { command = func(path string, args ...string) cmd {
@@ -97,6 +97,7 @@ func (suite *LintTestSuite) TestPrepareWithLintFlags() {
"--set-string", "version=2.0", "--set-string", "version=2.0",
"--values", "/usr/local/underrides", "--values", "/usr/local/underrides",
"--values", "/usr/local/overrides", "--values", "/usr/local/overrides",
"--strict",
"./uk/top_40"}, args) "./uk/top_40"}, args)
return suite.mockCmd return suite.mockCmd

View File

@@ -6,9 +6,10 @@ import (
// Uninstall is an execution step that calls `helm uninstall` when executed. // Uninstall is an execution step that calls `helm uninstall` when executed.
type Uninstall struct { type Uninstall struct {
Release string Release string
DryRun bool DryRun bool
cmd cmd KeepHistory bool
cmd cmd
} }
// Execute executes the `helm uninstall` command. // Execute executes the `helm uninstall` command.
@@ -36,6 +37,9 @@ func (u *Uninstall) Prepare(cfg Config) error {
if u.DryRun { if u.DryRun {
args = append(args, "--dry-run") args = append(args, "--dry-run")
} }
if u.KeepHistory {
args = append(args, "--keep-history")
}
args = append(args, u.Release) args = append(args, u.Release)

View File

@@ -81,6 +81,21 @@ func (suite *UninstallTestSuite) TestPrepareDryRunFlag() {
suite.Equal(expected, suite.actualArgs) suite.Equal(expected, suite.actualArgs)
} }
func (suite *UninstallTestSuite) TestPrepareKeepHistoryFlag() {
u := Uninstall{
Release: "perturbator_sentient",
KeepHistory: true,
}
cfg := Config{}
suite.mockCmd.EXPECT().Stdout(gomock.Any()).AnyTimes()
suite.mockCmd.EXPECT().Stderr(gomock.Any()).AnyTimes()
suite.NoError(u.Prepare(cfg))
expected := []string{"uninstall", "--keep-history", "perturbator_sentient"}
suite.Equal(expected, suite.actualArgs)
}
func (suite *UninstallTestSuite) TestPrepareNamespaceFlag() { func (suite *UninstallTestSuite) TestPrepareNamespaceFlag() {
u := Uninstall{ u := Uninstall{
Release: "carly_simon_run_away_with_me", Release: "carly_simon_run_away_with_me",

View File

@@ -9,12 +9,17 @@ type Upgrade struct {
Chart string Chart string
Release string Release string
ChartVersion string ChartVersion string
DryRun bool DryRun bool
Wait bool Wait bool
ReuseValues bool Values string
Timeout string StringValues string
Force bool ValuesFiles []string
ReuseValues bool
Timeout string
Force bool
Atomic bool
CleanupOnFail bool
cmd cmd cmd cmd
} }
@@ -62,13 +67,19 @@ func (u *Upgrade) Prepare(cfg Config) error {
if u.Force { if u.Force {
args = append(args, "--force") args = append(args, "--force")
} }
if cfg.Values != "" { if u.Atomic {
args = append(args, "--set", cfg.Values) args = append(args, "--atomic")
} }
if cfg.StringValues != "" { if u.CleanupOnFail {
args = append(args, "--set-string", cfg.StringValues) args = append(args, "--cleanup-on-fail")
} }
for _, vFile := range cfg.ValuesFiles { if u.Values != "" {
args = append(args, "--set", u.Values)
}
if u.StringValues != "" {
args = append(args, "--set-string", u.StringValues)
}
for _, vFile := range u.ValuesFiles {
args = append(args, "--values", vFile) args = append(args, "--values", vFile)
} }

View File

@@ -89,21 +89,22 @@ func (suite *UpgradeTestSuite) TestPrepareWithUpgradeFlags() {
defer suite.ctrl.Finish() defer suite.ctrl.Finish()
u := Upgrade{ u := Upgrade{
Chart: "hot_ac", Chart: "hot_ac",
Release: "maroon_5_memories", Release: "maroon_5_memories",
ChartVersion: "radio_edit", //-version ChartVersion: "radio_edit",
DryRun: true, //-run DryRun: true,
Wait: true, //-wait Wait: true,
ReuseValues: true, //-values Values: "age=35",
Timeout: "sit_in_the_corner", //-timeout StringValues: "height=5ft10in",
Force: true, //-force ValuesFiles: []string{"/usr/local/stats", "/usr/local/grades"},
ReuseValues: true,
Timeout: "sit_in_the_corner",
Force: true,
Atomic: true,
CleanupOnFail: true,
} }
cfg := Config{ cfg := Config{}
Values: "age=35",
StringValues: "height=5ft10in",
ValuesFiles: []string{"/usr/local/stats", "/usr/local/grades"},
}
command = func(path string, args ...string) cmd { command = func(path string, args ...string) cmd {
suite.Equal(helmBin, path) suite.Equal(helmBin, path)
@@ -114,6 +115,8 @@ func (suite *UpgradeTestSuite) TestPrepareWithUpgradeFlags() {
"--reuse-values", "--reuse-values",
"--timeout", "sit_in_the_corner", "--timeout", "sit_in_the_corner",
"--force", "--force",
"--atomic",
"--cleanup-on-fail",
"--set", "age=35", "--set", "age=35",
"--set-string", "height=5ft10in", "--set-string", "height=5ft10in",
"--values", "/usr/local/stats", "--values", "/usr/local/stats",