61 Commits

Author SHA1 Message Date
Erin Call
7892c9c0a6 Get env vars from urfave/cli instead of envconfig [#66] 2020-01-09 09:37:32 -08:00
Erin Call
b4b1f7882d Verify that every config field gets populated [#66]
I'm about to radically alter the way NewConfig populates its fields, so
I want assurance that it's still working correctly afterward.
2020-01-08 14:19:54 -08:00
Erin Call
a826f66425 Test settings aliases without the plugin_ prefix [#66] 2020-01-08 12:37:34 -08:00
Erin Call
71421fbaa5 Fix godotenv dependency in go.mod
go.mod got an update when I ran the tests. It should've happened in
51058470e; I'm not sure why it was never updated.
2020-01-08 11:08:39 -08:00
Erin Call
cfd8e33995 Use "backward-compatible", not "backwardS"
While writing docs in the previous commit, I noticed that we'd been
inconsistent in the naming scheme. Wikipedia's back-compat article
redirects from "backwards" to "backward", so I figure that's a
reasonable source of authority for which form to use.
2020-01-08 11:04:30 -08:00
Erin Call
a5342d7bac Default to the canonical setting names [#66]
The goal with these changes was to give users a clearer, more readable
interface, so we should present that interface up front and only
document the aliases as a backward-compatibility option.

I've renamed the envconfig tags to reflect the switch, but I left the
actual field names the same. I think they're sufficiently meaningful
inside the code, and leaving them unchanged avoids making a bunch of
churn in the rest of the code.
2020-01-08 11:02:29 -08:00
Erin Call
6aa1d79d56 Stabilize the logic for setting-alias conflicts [#66]
This includes a refactor to the way aliases are processed. I had been
thinking in terms of locking down the aliases names pretty tightly, in
order to provide an error if there are conflicts. After discussion with
@josmo, though, it seems like we can do it the same way we do for
"PLUGIN_"/non-prefixed variables, i.e. quietly override them.
2020-01-08 09:35:14 -08:00
Erin Call
1d1117ba49 Use "kube" in setting aliases [#66]
Nobody likes typing "kubernetes"! Writing out that whole word without
typos is the third hard problem in computer science.
2020-01-07 15:40:58 -08:00
Erin Call
04de280821 Rough draft of aliased settings [#66] 2020-01-07 15:25:54 -08:00
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 732 additions and 308 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,12 +1,16 @@
# 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
* Deploy your service * Deploy your service
* Delete your service * Delete your service
The plugin is inpsired by [drone-helm](https://github.com/ipedrazas/drone-helm), which fills the same role for Helm 2. It provides a comparable feature-set and the configuration settings are backwards-compatible. The plugin is inpsired by [drone-helm](https://github.com/ipedrazas/drone-helm), which fills the same role for Helm 2. It provides a comparable feature-set and the configuration settings are backward-compatible.
## Example configuration ## Example configuration
@@ -19,7 +23,7 @@ steps:
- name: lint - name: lint
image: pelotech/drone-helm3 image: pelotech/drone-helm3
settings: settings:
helm_command: lint mode: lint
chart: ./ chart: ./
``` ```
@@ -30,12 +34,12 @@ steps:
- name: deploy - name: deploy
image: pelotech/drone-helm3 image: pelotech/drone-helm3
settings: settings:
helm_command: upgrade mode: upgrade
chart: ./ chart: ./
release: my-project release: my-project
environment: environment:
API_SERVER: https://my.kubernetes.installation/clusters/a-1234 KUBE_API_SERVER: https://my.kubernetes.installation/clusters/a-1234
KUBERNETES_TOKEN: KUBE_TOKEN:
from_secret: kubernetes_token from_secret: kubernetes_token
``` ```
@@ -46,20 +50,23 @@ steps:
- name: uninstall - name: uninstall
image: pelotech/drone-helm3 image: pelotech/drone-helm3
settings: settings:
helm_command: uninstall mode: uninstall
release: my-project release: my-project
environment: environment:
API_SERVER: https://my.kubernetes.installation/clusters/a-1234 KUBE_API_SERVER: https://my.kubernetes.installation/clusters/a-1234
KUBERNETES_TOKEN: KUBE_TOKEN:
from_secret: kubernetes_token from_secret: kubernetes_token
``` ```
## Upgrading from drone-helm ## Upgrading from drone-helm
drone-helm3 is largely backwards-compatible with drone-helm. There are some known differences: drone-helm3 is largely backward-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`
@@ -67,5 +74,22 @@ drone-helm3 is largely backwards-compatible with drone-helm. There are some know
* `canary_image` * `canary_image`
* `client_only` * `client_only`
* `stable_repo_url` * `stable_repo_url`
* Several settings have been renamed, to clarify their purpose and provide a more consistent naming scheme. For backward-compatibility, the old names are still available as aliases. If the old and new names are both present, the updated form takes priority. Conflicting settings will make your `.drone.yml` harder to understand, so we recommend updating to the new names:
* `helm_command` is now `mode`
° `helm_repos` is now `add_repos`
* `api_server` is now `kube_api_server`
* `service_account` is now `kube_service_account`
* `kubernetes_token` is now `kube_token`
* `kubernetes_certificate` is now `kube_certificate`
* `wait` is now `wait_for_upgrade`
* `force` is now `force_upgrade`
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,11 +4,12 @@ import (
"fmt" "fmt"
"os" "os"
_ "github.com/joho/godotenv/autoload"
"github.com/pelotech/drone-helm3/internal/helm" "github.com/pelotech/drone-helm3/internal/helm"
) )
func main() { func main() {
cfg, err := helm.NewConfig(os.Stdout, os.Stderr) cfg, err := helm.NewConfig(os.Stdout, os.Stderr, os.Args...)
if err != nil { if err != nil {
fmt.Fprintf(os.Stderr, "%s\n", err.Error()) fmt.Fprintf(os.Stderr, "%s\n", err.Error())

View File

@@ -3,16 +3,15 @@
## Global ## Global
| Param name | Type | Purpose | | Param name | Type | Purpose |
|---------------------|-----------------|---------| |---------------------|-----------------|---------|
| helm_command | string | Indicates the operation to perform. Recommended, but not required. Valid options are `upgrade`, `uninstall`, `lint`, and `help`. | | mode | string | Indicates the operation to perform. Recommended, but not required. Valid options are `upgrade`, `uninstall`, `lint`, and `help`. |
| 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/`. | | add_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
Linting is only triggered when the `helm_command` setting is "lint". Linting is only triggered when the `mode` setting is "lint".
| Param name | Type | Required | Purpose | | Param name | Type | Required | Purpose |
|---------------|----------------|----------|---------| |---------------|----------------|----------|---------|
@@ -20,24 +19,27 @@ 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
Installations are triggered when the `helm_command` setting is "upgrade." They can also be triggered when the build was triggered by a `push`, `tag`, `deployment`, `pull_request`, `promote`, or `rollback` Drone event. Installations are triggered when the `mode` setting is "upgrade." They can also be triggered when the build was triggered by a `push`, `tag`, `deployment`, `pull_request`, `promote`, or `rollback` Drone event.
| Param name | Type | Required | Purpose | | Param name | Type | Required | Purpose |
|------------------------|----------------|----------|---------| |------------------------|----------------|----------|---------|
| chart | string | yes | The chart to use for this installation. | | chart | string | yes | The chart to use for this installation. |
| release | string | yes | The release name for helm to use. | | release | string | yes | The release name for helm to use. |
| api_server | string | yes | API endpoint for the Kubernetes cluster. | | kube_api_server | string | yes | API endpoint for the Kubernetes cluster. |
| kubernetes_token | string | yes | Token for authenticating to Kubernetes. | | kube_token | string | yes | Token for authenticating to Kubernetes. |
| service_account | string | | Service account for authenticating to Kubernetes. Default is `helm`. | | kube_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. | | kube_certificate | string | | Base64 encoded TLS certificate used by the Kubernetes cluster's certificate authority. |
| chart_version | string | | Specific chart version to install. | | chart_version | string | | Specific chart version to install. |
| dry_run | boolean | | Pass `--dry-run` to `helm upgrade`. | | dry_run | boolean | | Pass `--dry-run` to `helm upgrade`. |
| wait | boolean | | Wait until kubernetes resources are in a ready state before marking the installation successful. | | wait_for_upgrade | 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_upgrade | 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`. |
@@ -46,15 +48,16 @@ Installations are triggered when the `helm_command` setting is "upgrade." They c
## Uninstallation ## Uninstallation
Uninstallations are triggered when the `helm_command` setting is "uninstall" or "delete." They can also be triggered when the build was triggered by a `delete` Drone event. Uninstallations are triggered when the `mode` setting is "uninstall" or "delete." They can also be triggered when the build was triggered by a `delete` Drone event.
| Param name | Type | Required | Purpose | | Param name | Type | Required | Purpose |
|------------------------|----------|----------|---------| |------------------------|----------|----------|---------|
| release | string | yes | The release name for helm to use. | | release | string | yes | The release name for helm to use. |
| api_server | string | yes | API endpoint for the Kubernetes cluster. | | kube_api_server | string | yes | API endpoint for the Kubernetes cluster. |
| kubernetes_token | string | yes | Token for authenticating to Kubernetes. | | kube_token | string | yes | Token for authenticating to Kubernetes. |
| service_account | string | | Service account for authenticating to Kubernetes. Default is `helm`. | | kube_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. | | kube_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
```

5
go.mod
View File

@@ -4,9 +4,8 @@ go 1.13
require ( require (
github.com/golang/mock v1.3.1 github.com/golang/mock v1.3.1
github.com/kelseyhightower/envconfig v1.4.0 github.com/joho/godotenv v1.3.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 github.com/urfave/cli/v2 v2.1.1
golang.org/x/tools v0.0.0-20191209225234-22774f7dae43 // indirect
gopkg.in/yaml.v2 v2.2.2 gopkg.in/yaml.v2 v2.2.2
) )

23
go.sum
View File

@@ -1,31 +1,30 @@
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d h1:U+s90UTSYgptZMwQh2aRr3LuazLJIa+Pg3Kc1ylSYVY=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= 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/kelseyhightower/envconfig v1.4.0 h1:Im6hONhd3pLkfDFsbRgu68RDNkGF1r3dvMUtDTo2cv8= github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc=
github.com/kelseyhightower/envconfig v1.4.0/go.mod h1:cccZRl6mQpaq41TPp5QxidR+Sa3axMbJDNb//FQX6Gg= github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 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/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/urfave/cli/v2 v2.1.1 h1:Qt8FeAtxE/vfdrLmR3rxR6JRE0RoVmbXu8+6kZtYU4k=
github.com/urfave/cli/v2 v2.1.1/go.mod h1:SE9GqnLQmjVa0iPEY0f1w3ygNIYcIJ0OKPMoW2caLfQ=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f h1:J5lckAjkw6qYlOZNj90mLYNTEKDvWeuc1yieZ8qUzUE=
golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/tools v0.0.0-20190425150028-36563e24a262 h1:qsl9y/CJx34tuA7QCPNp86JNJe4spst6Ff8MjvPUdPg= golang.org/x/tools v0.0.0-20190425150028-36563e24a262 h1:qsl9y/CJx34tuA7QCPNp86JNJe4spst6Ff8MjvPUdPg=
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f h1:kDxGY2VmgABOe55qheT/TFqUMtcTHnomIPS1iv3G4Ms=
golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191206204035-259af5ff87bd h1:Zc7EU2PqpsNeIfOoVA7hvQX4cS3YDJEs5KlfatT3hLo=
golang.org/x/tools v0.0.0-20191206204035-259af5ff87bd/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191209225234-22774f7dae43 h1:NfPq5mgc5ArFgVLCpeS4z07IoxSAqVfV/gQ5vxdgaxI=
golang.org/x/tools v0.0.0-20191209225234-22774f7dae43/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= 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 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=

View File

@@ -2,67 +2,208 @@ package helm
import ( import (
"fmt" "fmt"
"github.com/kelseyhightower/envconfig" "github.com/urfave/cli/v2"
"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 // Helm command to run
DroneEvent string `envconfig:"DRONE_BUILD_EVENT"` // Drone event that invoked this plugin. DroneEvent string // Drone event that invoked this plugin.
UpdateDependencies bool `split_words:"true"` // Call `helm dependency update` before the main command UpdateDependencies bool // Call `helm dependency update` before the main command
AddRepos []string `envconfig:"HELM_REPOS"` // Call `helm repo add` before the main command AddRepos []string // 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 // 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 ValuesFiles []string // Arguments to pass to --values in applicable helm commands
ValuesFiles []string `split_words:"true"` // Arguments to pass to --values in applicable helm commands Namespace string // Kubernetes namespace for all helm commands
Namespace string `` // Kubernetes namespace for all helm commands KubeToken string // Kubernetes authentication token to put in .kube/config
KubeToken string `envconfig:"KUBERNETES_TOKEN"` // Kubernetes authentication token to put in .kube/config SkipTLSVerify bool // Put insecure-skip-tls-verify in .kube/config
SkipTLSVerify bool `envconfig:"SKIP_TLS_VERIFY"` // Put insecure-skip-tls-verify in .kube/config Certificate string // The Kubernetes cluster CA's self-signed certificate (must be base64-encoded)
Certificate string `envconfig:"KUBERNETES_CERTIFICATE"` // The Kubernetes cluster CA's self-signed certificate (must be base64-encoded) APIServer string // The Kubernetes cluster's API endpoint
APIServer string `envconfig:"API_SERVER"` // The Kubernetes cluster's API endpoint ServiceAccount string // Account to use for connecting to the Kubernetes cluster
ServiceAccount string `split_words:"true"` // Account to use for connecting to the Kubernetes cluster ChartVersion string // Specific chart version to use in `helm upgrade`
ChartVersion string `split_words:"true"` // Specific chart version to use in `helm upgrade` DryRun bool // 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 // Pass --reuse-values to `helm upgrade`
ReuseValues bool `split_words:"true"` // Pass --reuse-values to `helm upgrade` KeepHistory bool // 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 // Pass --atomic to `helm upgrade`
CleanupOnFail bool // Pass --cleanup-on-fail to `helm upgrade`
LintStrictly bool // Pass --strict to `helm lint`
Stdout io.Writer `ignored:"true"` Stdout io.Writer
Stderr io.Writer `ignored:"true"` Stderr io.Writer
} }
// NewConfig creates a Config and reads environment variables into it, accounting for several possible formats. // NewConfig creates a Config and reads environment variables into it, accounting for several possible formats.
func NewConfig(stdout, stderr io.Writer) (*Config, error) { func NewConfig(stdout, stderr io.Writer, argv ...string) (*Config, error) {
cfg := Config{ cfg := Config{
Stdout: stdout, Stdout: stdout,
Stderr: stderr, Stderr: stderr,
} }
if err := envconfig.Process("plugin", &cfg); err != nil { // cli doesn't support Destination for string slices, so we'll use bare
// strings as an intermediate value and split them on commas ourselves.
var addRepos, valuesFiles string
app := &cli.App{
Name: "drone-helm3",
Action: func(*cli.Context) error { return nil },
Flags: []cli.Flag{
&cli.StringFlag{
Name: "mode",
Destination: &cfg.Command,
EnvVars: []string{"MODE", "PLUGIN_MODE", "HELM_COMMAND", "PLUGIN_HELM_COMMAND"},
},
&cli.StringFlag{
Name: "drone-event",
Destination: &cfg.DroneEvent,
EnvVars: []string{"DRONE_BUILD_EVENT"},
},
&cli.BoolFlag{
Name: "update-dependencies",
Destination: &cfg.UpdateDependencies,
EnvVars: []string{"UPDATE_DEPENDENCIES", "PLUGIN_UPDATE_DEPENDENCIES"},
},
&cli.StringFlag{
Name: "add-repos",
Destination: &addRepos,
EnvVars: []string{"ADD_REPOS", "PLUGIN_ADD_REPOS", "HELM_REPOS", "PLUGIN_HELM_REPOS"},
},
&cli.BoolFlag{
Name: "debug",
Destination: &cfg.Debug,
EnvVars: []string{"DEBUG", "PLUGIN_DEBUG"},
},
&cli.StringFlag{
Name: "values",
Destination: &cfg.Values,
EnvVars: []string{"VALUES", "PLUGIN_VALUES"},
},
&cli.StringFlag{
Name: "string-values",
Destination: &cfg.StringValues,
EnvVars: []string{"STRING_VALUES", "PLUGIN_STRING_VALUES"},
},
&cli.StringFlag{
Name: "values-files",
Destination: &valuesFiles,
EnvVars: []string{"VALUES_FILES", "PLUGIN_VALUES_FILES"},
},
&cli.StringFlag{
Name: "namespace",
Destination: &cfg.Namespace,
EnvVars: []string{"NAMESPACE", "PLUGIN_NAMESPACE"},
},
&cli.StringFlag{
Name: "kube-token",
Destination: &cfg.KubeToken,
EnvVars: []string{"KUBE_TOKEN", "PLUGIN_KUBE_TOKEN", "KUBERNETES_TOKEN", "PLUGIN_KUBERNETES_TOKEN"},
},
&cli.BoolFlag{
Name: "skip-tls-verify",
Destination: &cfg.SkipTLSVerify,
EnvVars: []string{"SKIP_TLS_VERIFY", "PLUGIN_SKIP_TLS_VERIFY"},
},
&cli.StringFlag{
Name: "kube-certificate",
Destination: &cfg.Certificate,
EnvVars: []string{"KUBE_CERTIFICATE", "PLUGIN_KUBE_CERTIFICATE", "KUBERNETES_CERTIFICATE", "PLUGIN_KUBERNETES_CERTIFICATE"},
},
&cli.StringFlag{
Name: "kube-api-server",
Destination: &cfg.APIServer,
EnvVars: []string{"KUBE_API_SERVER", "PLUGIN_KUBE_API_SERVER", "API_SERVER", "PLUGIN_API_SERVER"},
},
&cli.StringFlag{
Name: "service-account",
Destination: &cfg.ServiceAccount,
EnvVars: []string{"KUBE_SERVICE_ACCOUNT", "PLUGIN_KUBE_SERVICE_ACCOUNT", "SERVICE_ACCOUNT", "PLUGIN_SERVICE_ACCOUNT"},
},
&cli.StringFlag{
Name: "chart-version",
Destination: &cfg.ChartVersion,
EnvVars: []string{"CHART_VERSION", "PLUGIN_CHART_VERSION"},
},
&cli.BoolFlag{
Name: "dry-run",
Destination: &cfg.DryRun,
EnvVars: []string{"DRY_RUN", "PLUGIN_DRY_RUN"},
},
&cli.BoolFlag{
Name: "wait-for-upgrade",
Destination: &cfg.Wait,
EnvVars: []string{"WAIT_FOR_UPGRADE", "PLUGIN_WAIT_FOR_UPGRADE", "WAIT", "PLUGIN_WAIT"},
},
&cli.BoolFlag{
Name: "reuse-values",
Destination: &cfg.ReuseValues,
EnvVars: []string{"REUSE_VALUES", "PLUGIN_REUSE_VALUES"},
},
&cli.BoolFlag{
Name: "keep-history",
Destination: &cfg.KeepHistory,
EnvVars: []string{"KEEP_HISTORY", "PLUGIN_KEEP_HISTORY"},
},
&cli.StringFlag{
Name: "timeout",
Destination: &cfg.Timeout,
EnvVars: []string{"TIMEOUT", "PLUGIN_TIMEOUT"},
},
&cli.StringFlag{
Name: "chart",
Destination: &cfg.Chart,
EnvVars: []string{"CHART", "PLUGIN_CHART"},
},
&cli.StringFlag{
Name: "release",
Destination: &cfg.Release,
EnvVars: []string{"RELEASE", "PLUGIN_RELEASE"},
},
&cli.BoolFlag{
Name: "force-upgrade",
Destination: &cfg.Force,
EnvVars: []string{"FORCE_UPGRADE", "PLUGIN_FORCE_UPGRADE", "FORCE", "PLUGIN_FORCE"},
},
&cli.BoolFlag{
Name: "atomic-upgrade",
Destination: &cfg.AtomicUpgrade,
EnvVars: []string{"ATOMIC_UPGRADE", "PLUGIN_ATOMIC_UPGRADE"},
},
&cli.BoolFlag{
Name: "cleanup-failed-upgrade",
Destination: &cfg.CleanupOnFail,
EnvVars: []string{"CLEANUP_FAILED_UPGRADE", "PLUGIN_CLEANUP_FAILED_UPGRADE"},
},
&cli.BoolFlag{
Name: "lint-strictly",
Destination: &cfg.LintStrictly,
EnvVars: []string{"LINT_STRICTLY", "PLUGIN_LINT_STRICTLY"},
},
},
}
if err := app.Run(argv); err != nil {
return nil, err return nil, err
} }
if addRepos != "" {
prefix := cfg.Prefix cfg.AddRepos = strings.Split(addRepos, ",")
if err := envconfig.Process("", &cfg); err != nil {
return nil, err
} }
if valuesFiles != "" {
if prefix != "" { cfg.ValuesFiles = strings.Split(valuesFiles, ",")
if err := envconfig.Process(cfg.Prefix, &cfg); err != nil {
return nil, err
}
} }
if justNumbers.MatchString(cfg.Timeout) { if justNumbers.MatchString(cfg.Timeout) {
@@ -73,6 +214,8 @@ func NewConfig(stdout, stderr io.Writer) (*Config, error) {
cfg.logDebug() cfg.logDebug()
} }
cfg.deprecationWarn()
return &cfg, nil return &cfg, nil
} }
@@ -82,3 +225,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,117 +20,339 @@ func TestConfigTestSuite(t *testing.T) {
} }
func (suite *ConfigTestSuite) TestNewConfigWithPluginPrefix() { func (suite *ConfigTestSuite) TestNewConfigWithPluginPrefix() {
suite.unsetenv("PLUGIN_PREFIX") stdout := strings.Builder{}
suite.unsetenv("HELM_COMMAND") stderr := strings.Builder{}
suite.unsetenv("UPDATE_DEPENDENCIES") for _, varname := range []string{
suite.unsetenv("DEBUG") "MODE",
"DRONE_BUILD_EVENT",
"HELM_COMMAND",
"PLUGIN_HELM_COMMAND",
"UPDATE_DEPENDENCIES",
"ADD_REPOS",
"HELM_REPOS",
"PLUGIN_HELM_REPOS",
"DEBUG",
"VALUES",
"STRING_VALUES",
"VALUES_FILES",
"NAMESPACE",
"KUBE_TOKEN",
"KUBERNETES_TOKEN",
"PLUGIN_KUBERNETES_TOKEN",
"SKIP_TLS_VERIFY",
"KUBE_CERTIFICATE",
"KUBERNETES_CERTIFICATE",
"PLUGIN_KUBERNETES_CERTIFICATE",
"KUBE_API_SERVER",
"API_SERVER",
"PLUGIN_API_SERVER",
"KUBE_SERVICE_ACCOUNT",
"SERVICE_ACCOUNT",
"PLUGIN_SERVICE_ACCOUNT",
"CHART_VERSION",
"DRY_RUN",
"WAIT_FOR_UPGRADE",
"WAIT",
"PLUGIN_WAIT",
"REUSE_VALUES",
"KEEP_HISTORY",
"TIMEOUT",
"CHART",
"RELEASE",
"FORCE",
"FORCE_UPGRADE",
"PLUGIN_FORCE_UPGRADE",
"ATOMIC_UPGRADE",
"CLEANUP_FAILED_UPGRADE",
"LINT_STRICTLY",
} {
suite.unsetenv(varname)
}
suite.setenv("PLUGIN_HELM_COMMAND", "execute order 66") suite.setenv("PLUGIN_MODE", "upgrade")
suite.setenv("PLUGIN_UPDATE_DEPENDENCIES", "true") suite.setenv("PLUGIN_UPDATE_DEPENDENCIES", "true")
suite.setenv("PLUGIN_ADD_REPOS", "foo=http://bar,goo=http://baz")
suite.setenv("PLUGIN_DEBUG", "true") suite.setenv("PLUGIN_DEBUG", "true")
suite.setenv("PLUGIN_VALUES", "dog=husky")
suite.setenv("PLUGIN_STRING_VALUES", "version=1.0")
suite.setenv("PLUGIN_VALUES_FILES", "underrides.yml,overrides.yml")
suite.setenv("PLUGIN_NAMESPACE", "myapp")
suite.setenv("PLUGIN_KUBE_TOKEN", "cGxlYXNlIHNpciwgbGV0IG1lIGlu")
suite.setenv("PLUGIN_SKIP_TLS_VERIFY", "true")
suite.setenv("PLUGIN_KUBE_CERTIFICATE", "SSBhbSB0b3RhbGx5IHRoZSBzZXJ2ZXIgeW91IHdhbnQ=")
suite.setenv("PLUGIN_KUBE_API_SERVER", "http://my.kube/cluster")
suite.setenv("PLUGIN_KUBE_SERVICE_ACCOUNT", "deploybot")
suite.setenv("PLUGIN_CHART_VERSION", "six")
suite.setenv("PLUGIN_DRY_RUN", "true")
suite.setenv("PLUGIN_WAIT_FOR_UPGRADE", "true")
suite.setenv("PLUGIN_REUSE_VALUES", "true")
suite.setenv("PLUGIN_KEEP_HISTORY", "true")
suite.setenv("PLUGIN_TIMEOUT", "5m20s")
suite.setenv("PLUGIN_CHART", "./helm/myapp/")
suite.setenv("PLUGIN_RELEASE", "my_app")
suite.setenv("PLUGIN_FORCE_UPGRADE", "true")
suite.setenv("PLUGIN_ATOMIC_UPGRADE", "true")
suite.setenv("PLUGIN_CLEANUP_FAILED_UPGRADE", "true")
suite.setenv("PLUGIN_LINT_STRICTLY", "true")
cfg, err := NewConfig(&strings.Builder{}, &strings.Builder{}) cfg, err := NewConfig(&stdout, &stderr, "test")
suite.Require().NoError(err) suite.Require().NoError(err)
suite.Equal("execute order 66", cfg.Command) want := Config{
suite.True(cfg.UpdateDependencies) Command: "upgrade",
suite.True(cfg.Debug) DroneEvent: "",
UpdateDependencies: true,
AddRepos: []string{"foo=http://bar", "goo=http://baz"},
Debug: true,
Values: "dog=husky",
StringValues: "version=1.0",
ValuesFiles: []string{"underrides.yml", "overrides.yml"},
Namespace: "myapp",
KubeToken: "cGxlYXNlIHNpciwgbGV0IG1lIGlu",
SkipTLSVerify: true,
Certificate: "SSBhbSB0b3RhbGx5IHRoZSBzZXJ2ZXIgeW91IHdhbnQ=",
APIServer: "http://my.kube/cluster",
ServiceAccount: "deploybot",
ChartVersion: "six",
DryRun: true,
Wait: true,
ReuseValues: true,
KeepHistory: true,
Timeout: "5m20s",
Chart: "./helm/myapp/",
Release: "my_app",
Force: true,
AtomicUpgrade: true,
CleanupOnFail: true,
LintStrictly: true,
Stdout: &stdout,
Stderr: &stderr,
}
suite.Equal(&want, cfg)
} }
func (suite *ConfigTestSuite) TestNewConfigWithNoPrefix() { func (suite *ConfigTestSuite) TestNewConfigWithNoPrefix() {
suite.unsetenv("PLUGIN_PREFIX") stdout := strings.Builder{}
suite.unsetenv("PLUGIN_HELM_COMMAND") stderr := strings.Builder{}
suite.unsetenv("PLUGIN_UPDATE_DEPENDENCIES") for _, varname := range []string{
suite.unsetenv("PLUGIN_DEBUG") "PLUGIN_MODE",
"PLUGIN_HELM_COMMAND",
"HELM_COMMAND",
"PLUGIN_UPDATE_DEPENDENCIES",
"PLUGIN_ADD_REPOS",
"PLUGIN_HELM_REPOS",
"HELM_REPOS",
"PLUGIN_DEBUG",
"PLUGIN_VALUES",
"PLUGIN_STRING_VALUES",
"PLUGIN_VALUES_FILES",
"PLUGIN_NAMESPACE",
"PLUGIN_KUBE_TOKEN",
"PLUGIN_KUBERNETES_TOKEN",
"KUBERNETES_TOKEN",
"PLUGIN_SKIP_TLS_VERIFY",
"PLUGIN_KUBE_CERTIFICATE",
"PLUGIN_KUBERNETES_CERTIFICATE",
"KUBERNETES_CERTIFICATE",
"PLUGIN_KUBE_API_SERVER",
"PLUGIN_API_SERVER",
"API_SERVER",
"PLUGIN_KUBE_SERVICE_ACCOUNT",
"PLUGIN_SERVICE_ACCOUNT",
"SERVICE_ACCOUNT",
"PLUGIN_CHART_VERSION",
"PLUGIN_DRY_RUN",
"PLUGIN_WAIT_FOR_UPGRADE",
"PLUGIN_WAIT",
"WAIT",
"PLUGIN_REUSE_VALUES",
"PLUGIN_KEEP_HISTORY",
"PLUGIN_TIMEOUT",
"PLUGIN_CHART",
"PLUGIN_RELEASE",
"PLUGIN_FORCE",
"PLUGIN_FORCE_UPGRADE",
"FORCE_UPGRADE",
"PLUGIN_ATOMIC_UPGRADE",
"PLUGIN_CLEANUP_FAILED_UPGRADE",
"PLUGIN_LINT_STRICTLY",
} {
suite.unsetenv(varname)
}
suite.setenv("HELM_COMMAND", "execute order 66") suite.setenv("MODE", "upgrade")
suite.setenv("DRONE_BUILD_EVENT", "tag")
suite.setenv("UPDATE_DEPENDENCIES", "true") suite.setenv("UPDATE_DEPENDENCIES", "true")
suite.setenv("ADD_REPOS", "foo=http://bar,goo=http://baz")
suite.setenv("DEBUG", "true") suite.setenv("DEBUG", "true")
suite.setenv("VALUES", "dog=husky")
suite.setenv("STRING_VALUES", "version=1.0")
suite.setenv("VALUES_FILES", "underrides.yml,overrides.yml")
suite.setenv("NAMESPACE", "myapp")
suite.setenv("KUBE_TOKEN", "cGxlYXNlIHNpciwgbGV0IG1lIGlu")
suite.setenv("SKIP_TLS_VERIFY", "true")
suite.setenv("KUBE_CERTIFICATE", "SSBhbSB0b3RhbGx5IHRoZSBzZXJ2ZXIgeW91IHdhbnQ=")
suite.setenv("KUBE_API_SERVER", "http://my.kube/cluster")
suite.setenv("KUBE_SERVICE_ACCOUNT", "deploybot")
suite.setenv("CHART_VERSION", "six")
suite.setenv("DRY_RUN", "true")
suite.setenv("WAIT_FOR_UPGRADE", "true")
suite.setenv("REUSE_VALUES", "true")
suite.setenv("KEEP_HISTORY", "true")
suite.setenv("TIMEOUT", "5m20s")
suite.setenv("CHART", "./helm/myapp/")
suite.setenv("RELEASE", "my_app")
suite.setenv("FORCE_UPGRADE", "true")
suite.setenv("ATOMIC_UPGRADE", "true")
suite.setenv("CLEANUP_FAILED_UPGRADE", "true")
suite.setenv("LINT_STRICTLY", "true")
cfg, err := NewConfig(&strings.Builder{}, &strings.Builder{}) cfg, err := NewConfig(&stdout, &stderr, "test")
suite.Require().NoError(err) suite.Require().NoError(err)
suite.Equal("execute order 66", cfg.Command) want := Config{
suite.True(cfg.UpdateDependencies) Command: "upgrade",
suite.True(cfg.Debug) DroneEvent: "tag",
} UpdateDependencies: true,
AddRepos: []string{"foo=http://bar", "goo=http://baz"},
Debug: true,
Values: "dog=husky",
StringValues: "version=1.0",
ValuesFiles: []string{"underrides.yml", "overrides.yml"},
Namespace: "myapp",
KubeToken: "cGxlYXNlIHNpciwgbGV0IG1lIGlu",
SkipTLSVerify: true,
Certificate: "SSBhbSB0b3RhbGx5IHRoZSBzZXJ2ZXIgeW91IHdhbnQ=",
APIServer: "http://my.kube/cluster",
ServiceAccount: "deploybot",
ChartVersion: "six",
DryRun: true,
Wait: true,
ReuseValues: true,
KeepHistory: true,
Timeout: "5m20s",
Chart: "./helm/myapp/",
Release: "my_app",
Force: true,
AtomicUpgrade: true,
CleanupOnFail: true,
LintStrictly: true,
Stdout: &stdout,
Stderr: &stderr,
}
func (suite *ConfigTestSuite) TestNewConfigWithConfigurablePrefix() { suite.Equal(&want, cfg)
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_MODE", "iambic")
suite.setenv("HELM_COMMAND", "defend the jedi") // values from the `environment` block override those from `settings` suite.setenv("MODE", "haiku") // values from the `environment` block override those from `settings`
suite.setenv("PLUGIN_PREFIX", "prod") cfg, err := NewConfig(&strings.Builder{}, &strings.Builder{}, "test")
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{})
suite.Require().NoError(err) suite.Require().NoError(err)
suite.Equal("defend the jedi", cfg.Command) suite.Equal("haiku", cfg.Command)
suite.Equal("2m30s", cfg.Timeout)
} }
func (suite *ConfigTestSuite) TestNewConfigInfersNumbersAreSeconds() { func (suite *ConfigTestSuite) TestNewConfigInfersNumbersAreSeconds() {
suite.setenv("PLUGIN_TIMEOUT", "42") suite.setenv("PLUGIN_TIMEOUT", "42")
cfg, err := NewConfig(&strings.Builder{}, &strings.Builder{}) cfg, err := NewConfig(&strings.Builder{}, &strings.Builder{}, "test")
suite.Require().NoError(err) suite.Require().NoError(err)
suite.Equal("42s", cfg.Timeout) suite.Equal("42s", cfg.Timeout)
} }
func (suite *ConfigTestSuite) TestNewConfigWithAliases() {
for _, varname := range []string{
"MODE",
"ADD_REPOS",
"KUBE_API_SERVER",
"KUBE_SERVICE_ACCOUNT",
"WAIT_FOR_UPGRADE",
"FORCE_UPGRADE",
"KUBE_TOKEN",
"KUBE_CERTIFICATE",
} {
suite.unsetenv(varname)
suite.unsetenv("PLUGIN_" + varname)
}
suite.setenv("PLUGIN_HELM_COMMAND", "beware the jabberwock")
suite.setenv("PLUGIN_HELM_REPOS", "chortle=http://calloo.callay/frabjous/day")
suite.setenv("PLUGIN_API_SERVER", "http://tumtum.tree")
suite.setenv("PLUGIN_SERVICE_ACCOUNT", "tulgey")
suite.setenv("PLUGIN_WAIT", "true")
suite.setenv("PLUGIN_FORCE", "true")
suite.setenv("PLUGIN_KUBERNETES_TOKEN", "Y29tZSB0byBteSBhcm1z")
suite.setenv("PLUGIN_KUBERNETES_CERTIFICATE", "d2l0aCBpdHMgaGVhZA==")
cfg, err := NewConfig(&strings.Builder{}, &strings.Builder{}, "test")
suite.Require().NoError(err)
suite.Equal("beware the jabberwock", cfg.Command)
suite.Equal([]string{"chortle=http://calloo.callay/frabjous/day"}, cfg.AddRepos)
suite.Equal("http://tumtum.tree", cfg.APIServer)
suite.Equal("tulgey", cfg.ServiceAccount)
suite.True(cfg.Wait, "Wait should be aliased")
suite.True(cfg.Force, "Force should be aliased")
suite.Equal("Y29tZSB0byBteSBhcm1z", cfg.KubeToken, "KubeToken should be aliased")
suite.Equal("d2l0aCBpdHMgaGVhZA==", cfg.Certificate, "Certificate should be aliased")
}
func (suite *ConfigTestSuite) TestAliasedSettingWithoutPluginPrefix() {
suite.unsetenv("FORCE_UPGRADE")
suite.unsetenv("PLUGIN_FORCE_UPGRADE")
suite.unsetenv("PLUGIN_FORCE")
suite.setenv("FORCE", "true")
cfg, err := NewConfig(&strings.Builder{}, &strings.Builder{}, "test")
suite.Require().NoError(err)
suite.True(cfg.Force)
}
func (suite *ConfigTestSuite) TestNewConfigWithAliasConflicts() {
suite.unsetenv("FORCE_UPGRADE")
suite.setenv("PLUGIN_FORCE", "true")
suite.setenv("PLUGIN_FORCE_UPGRADE", "false") // should override even when set to the zero value
cfg, err := NewConfig(&strings.Builder{}, &strings.Builder{}, "test")
suite.NoError(err)
suite.False(cfg.Force, "official names should override alias names")
}
func (suite *ConfigTestSuite) TestNewConfigSetsWriters() { func (suite *ConfigTestSuite) TestNewConfigSetsWriters() {
stdout := &strings.Builder{} stdout := &strings.Builder{}
stderr := &strings.Builder{} stderr := &strings.Builder{}
cfg, err := NewConfig(stdout, stderr) cfg, err := NewConfig(stdout, stderr, "test")
suite.Require().NoError(err) suite.Require().NoError(err)
suite.Equal(stdout, cfg.Stdout) suite.Equal(stdout, cfg.Stdout)
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, "test")
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("MODE", "upgrade")
stderr := strings.Builder{} stderr := strings.Builder{}
stdout := strings.Builder{} stdout := strings.Builder{}
_, err := NewConfig(&stdout, &stderr) _, err := NewConfig(&stdout, &stderr, "test")
suite.Require().NoError(err) suite.Require().NoError(err)
suite.Equal("", stdout.String()) suite.Equal("", stdout.String())

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])
} }
@@ -356,7 +370,7 @@ func (suite *PlanTestSuite) TestDeterminePlanUninstallCommand() {
suite.Same(&uninstall, stepsMaker) suite.Same(&uninstall, stepsMaker)
} }
// helm_command = delete is provided as an alias for backwards-compatibility with drone-helm // helm_command = delete is provided as an alias for backward-compatibility with drone-helm
func (suite *PlanTestSuite) TestDeterminePlanDeleteCommand() { func (suite *PlanTestSuite) TestDeterminePlanDeleteCommand() {
cfg := Config{ cfg := Config{
Command: "delete", Command: "delete",

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",