Compare commits
33 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7c033a8b0a | ||
|
|
e482a144c1 | ||
|
|
8dba329407 | ||
|
|
4462307983 | ||
|
|
cfc59a46ef | ||
|
|
5b4e3ab2ea | ||
|
|
0e58dde591 | ||
|
|
1809d7b133 | ||
|
|
70b2a2d0b4 | ||
|
|
03b066bfd7 | ||
|
|
fe0c874815 | ||
|
|
91d4506b37 | ||
|
|
0ab0a7fee4 | ||
|
|
0b17d929a7 | ||
|
|
3fea592040 | ||
|
|
d7a9eccb13 | ||
|
|
9e3a424a36 | ||
|
|
774bbb74db | ||
|
|
e0157d9bc2 | ||
|
|
1f2da68bbb | ||
|
|
7e2f982af7 | ||
|
|
0502d76c63 | ||
|
|
591b084970 | ||
|
|
f24a8e44ca | ||
|
|
8cb8a5d95d | ||
|
|
a4834dd4f7 | ||
|
|
8b6a8fdd4b | ||
|
|
dbcef2699e | ||
|
|
22aa1df894 | ||
|
|
8f7b481934 | ||
|
|
e843b26759 | ||
|
|
713dcd8317 | ||
|
|
18313eeb5c |
3
.github/ISSUE_TEMPLATE/bug_report.md
vendored
3
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@@ -7,6 +7,9 @@ assignees: ''
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
**My drone-helm3 and drone versions:**
|
||||||
|
<!-- e.g. drone-helm3 0.9.0, drone 1.6.0-->
|
||||||
|
|
||||||
**What I tried to do:**
|
**What I tried to do:**
|
||||||
<!-- e.g. run a helm installation -->
|
<!-- e.g. run a helm installation -->
|
||||||
|
|
||||||
|
|||||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -4,6 +4,7 @@
|
|||||||
*.dll
|
*.dll
|
||||||
*.so
|
*.so
|
||||||
*.dylib
|
*.dylib
|
||||||
|
*.swp
|
||||||
|
|
||||||
.idea
|
.idea
|
||||||
|
|
||||||
@@ -17,3 +18,5 @@
|
|||||||
# vendor/
|
# vendor/
|
||||||
.env
|
.env
|
||||||
.secrets
|
.secrets
|
||||||
|
|
||||||
|
build/*
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
FROM alpine/helm:3.0.2
|
FROM alpine/helm:3.6.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
|
||||||
|
|||||||
@@ -6,7 +6,8 @@
|
|||||||
| mode | string | helm_command | Indicates the operation to perform. Recommended, but not required. Valid options are `upgrade`, `uninstall`, `lint`, and `help`. |
|
| mode | string | helm_command | 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.|
|
||||||
| add_repos | list\<string\> | helm_repos | 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\> | helm_repos | Calls `helm repo add $repo` before running the main command. Each string should be formatted as `repo_name=https://repo.url/`. |
|
||||||
| repo_ca_file | string | | TLS certificate for a chart repository certificate authority. |
|
| repo_certificate | string | | Base64 encoded TLS certificate for a chart repository. |
|
||||||
|
| repo_ca_certificate | string | | Base64 encoded TLS certificate for a chart repository certificate authority. |
|
||||||
| namespace | string | | Kubernetes namespace to use for this operation. |
|
| namespace | string | | Kubernetes namespace to use for this operation. |
|
||||||
| 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. |
|
||||||
|
|
||||||
@@ -30,12 +31,14 @@ Installations are triggered when the `mode` setting is "upgrade." They can also
|
|||||||
|------------------------|----------------|----------|------------------------|---------|
|
|------------------------|----------------|----------|------------------------|---------|
|
||||||
| 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. |
|
||||||
| kube_api_server | string | yes | api_server | API endpoint for the Kubernetes cluster. |
|
| skip_kubeconfig | boolean | | | Whether to skip kubeconfig file creation. |
|
||||||
| kube_token | string | yes | kubernetes_token | Token for authenticating to Kubernetes. |
|
| kube_api_server | string | yes | api_server | API endpoint for the Kubernetes cluster. This is ignored if `skip_kubeconfig` is `true`. |
|
||||||
| kube_service_account | string | | service_account | Service account for authenticating to Kubernetes. Default is `helm`. |
|
| kube_token | string | yes | kubernetes_token | Token for authenticating to Kubernetes. This is ignored if `skip_kubeconfig` is `true`. |
|
||||||
| kube_certificate | string | | kubernetes_certificate | Base64 encoded TLS certificate used by the Kubernetes cluster's certificate authority. |
|
| kube_service_account | string | | service_account | Service account for authenticating to Kubernetes. Default is `helm`. This is ignored if `skip_kubeconfig` is `true`. |
|
||||||
|
| kube_certificate | string | | kubernetes_certificate | Base64 encoded TLS certificate used by the Kubernetes cluster's certificate authority. This is ignored if `skip_kubeconfig` is `true`. |
|
||||||
| 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`. |
|
||||||
|
| dependencies_action | string | | | Calls `helm dependency build` OR `helm dependency update` before running the main command. Possible values: `build`, `update`. |
|
||||||
| wait_for_upgrade | boolean | | wait | Wait until kubernetes resources are in a ready state before marking the installation successful. |
|
| wait_for_upgrade | boolean | | wait | 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_upgrade | boolean | | force | Pass `--force` to `helm upgrade`. |
|
| force_upgrade | boolean | | force | Pass `--force` to `helm upgrade`. |
|
||||||
@@ -45,7 +48,9 @@ Installations are triggered when the `mode` setting is "upgrade." They can also
|
|||||||
| 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`. |
|
||||||
| reuse_values | boolean | | | Reuse the values from a previous release. |
|
| reuse_values | boolean | | | Reuse the values from a previous release. |
|
||||||
| 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. This is ignored if `skip_kubeconfig` is `true`. |
|
||||||
|
| create_namespace | boolean | | | Pass --create-namespace to `helm upgrade`. |
|
||||||
|
| skip_crds | boolean | | | Pass --skip-crds to `helm upgrade`. |
|
||||||
|
|
||||||
## Uninstallation
|
## Uninstallation
|
||||||
|
|
||||||
@@ -54,14 +59,15 @@ Uninstallations are triggered when the `mode` setting is "uninstall" or "delete.
|
|||||||
| Param name | Type | Required | Alias | Purpose |
|
| Param name | Type | Required | Alias | Purpose |
|
||||||
|------------------------|----------|----------|------------------------|---------|
|
|------------------------|----------|----------|------------------------|---------|
|
||||||
| release | string | yes | | The release name for helm to use. |
|
| release | string | yes | | The release name for helm to use. |
|
||||||
| kube_api_server | string | yes | api_server | API endpoint for the Kubernetes cluster. |
|
| skip_kubeconfig | boolean | | | Whether to skip kubeconfig file creation. |
|
||||||
| kube_token | string | yes | kubernetes_token | Token for authenticating to Kubernetes. |
|
| kube_api_server | string | yes | api_server | API endpoint for the Kubernetes cluster. This is ignored if `skip_kubeconfig` is `true`. |
|
||||||
| kube_service_account | string | | service_account | Service account for authenticating to Kubernetes. Default is `helm`. |
|
| kube_token | string | yes | kubernetes_token | Token for authenticating to Kubernetes. This is ignored if `skip_kubeconfig` is `true`. |
|
||||||
| kube_certificate | string | | kubernetes_certificate | Base64 encoded TLS certificate used by the Kubernetes cluster's certificate authority. |
|
| kube_service_account | string | | service_account | Service account for authenticating to Kubernetes. Default is `helm`. This is ignored if `skip_kubeconfig` is `true`. |
|
||||||
|
| kube_certificate | string | | kubernetes_certificate | Base64 encoded TLS certificate used by the Kubernetes cluster's certificate authority. This is ignored if `skip_kubeconfig` is `true`. |
|
||||||
| keep_history | boolean | | | Pass `--keep-history` to `helm uninstall`, to retain the release history. |
|
| 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. This is ignored if `skip_kubeconfig` is `true`. |
|
||||||
| chart | string | | | Required when the global `update_dependencies` parameter is true. No effect otherwise. |
|
| chart | string | | | Required when the global `update_dependencies` parameter is true. No effect otherwise. |
|
||||||
|
|
||||||
### Where to put settings
|
### Where to put settings
|
||||||
@@ -94,6 +100,26 @@ values_files: [ "./over_9,000.yml" ]
|
|||||||
values_files: [ "./over_9", "000.yml" ]
|
values_files: [ "./over_9", "000.yml" ]
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Interpolating secrets into the `values`, `string_values` and `add_repos` settings
|
||||||
|
|
||||||
|
If you want to send secrets to your charts, you can use syntax similar to shell variable interpolation--either `$VARNAME` or `$${VARNAME}`. The double dollar-sign is necessary when using curly brackets; using curly brackets with a single dollar-sign will trigger Drone's string substitution (which can't use arbitrary environment variables). If an environment variable is not set, it will be treated as if it were set to the empty string.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
environment:
|
||||||
|
DB_PASSWORD:
|
||||||
|
from_secret: db_password
|
||||||
|
SESSION_KEY:
|
||||||
|
from_secret: session_key
|
||||||
|
settings:
|
||||||
|
values:
|
||||||
|
- db_password=$DB_PASSWORD # db_password will be set to the contents of the db_password secret
|
||||||
|
- db_pass=$DB_PASS # db_pass will be set to "" since $DB_PASS is not set
|
||||||
|
- session_key=$${SESSION_KEY} # session_key will be set to the contents of the session_key secret
|
||||||
|
- sess_key=${SESSION_KEY} # sess_key will be set to "" by Drone's variable substitution
|
||||||
|
```
|
||||||
|
|
||||||
|
Variables intended for interpolation must be set in the `environment` section, not `settings`.
|
||||||
|
|
||||||
### Backward-compatibility aliases
|
### Backward-compatibility aliases
|
||||||
|
|
||||||
Some settings have alternate names, for backward-compatibility with drone-helm. We recommend using the canonical name unless you require the backward-compatible form.
|
Some settings have alternate names, for backward-compatibility with drone-helm. We recommend using the canonical name unless you require the backward-compatible form.
|
||||||
|
|||||||
49
internal/env/config.go
vendored
49
internal/env/config.go
vendored
@@ -2,11 +2,12 @@ package env
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/kelseyhightower/envconfig"
|
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/kelseyhightower/envconfig"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -22,15 +23,19 @@ type Config struct {
|
|||||||
// Configuration for drone-helm itself
|
// Configuration for drone-helm itself
|
||||||
Command string `envconfig:"mode"` // Helm command to run
|
Command string `envconfig:"mode"` // 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"` // [Deprecated] Call `helm dependency update` before the main command (deprecated, use dependencies_action: update instead)
|
||||||
|
DependenciesAction string `split_words:"true"` // Call `helm dependency build` or `helm dependency update` before the main command
|
||||||
AddRepos []string `split_words:"true"` // Call `helm repo add` before the main command
|
AddRepos []string `split_words:"true"` // Call `helm repo add` before the main command
|
||||||
RepoCAFile string `envconfig:"repo_ca_file"` // CA certificate for `helm repo add`
|
RepoCertificate string `envconfig:"repo_certificate"` // The Helm chart repository's self-signed certificate (must be base64-encoded)
|
||||||
|
RepoCACertificate string `envconfig:"repo_ca_certificate"` // The Helm chart repository CA's self-signed certificate (must be base64-encoded)
|
||||||
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
|
||||||
ValuesFiles []string `split_words:"true"` // 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
|
||||||
|
CreateNamespace bool `split_words:"true"` // Pass --create-namespace to `helm upgrade`
|
||||||
KubeToken string `split_words:"true"` // Kubernetes authentication token to put in .kube/config
|
KubeToken string `split_words:"true"` // Kubernetes authentication token to put in .kube/config
|
||||||
|
SkipKubeconfig bool `envconfig:"skip_kubeconfig"` // Skip kubeconfig creation
|
||||||
SkipTLSVerify bool `envconfig:"skip_tls_verify"` // Put insecure-skip-tls-verify in .kube/config
|
SkipTLSVerify bool `envconfig:"skip_tls_verify"` // Put insecure-skip-tls-verify in .kube/config
|
||||||
Certificate string `envconfig:"kube_certificate"` // The Kubernetes cluster CA's self-signed certificate (must be base64-encoded)
|
Certificate string `envconfig:"kube_certificate"` // The Kubernetes cluster CA's self-signed certificate (must be base64-encoded)
|
||||||
APIServer string `envconfig:"kube_api_server"` // The Kubernetes cluster's API endpoint
|
APIServer string `envconfig:"kube_api_server"` // The Kubernetes cluster's API endpoint
|
||||||
@@ -47,6 +52,7 @@ type Config struct {
|
|||||||
AtomicUpgrade bool `split_words:"true"` // Pass --atomic to `helm upgrade`
|
AtomicUpgrade bool `split_words:"true"` // Pass --atomic to `helm upgrade`
|
||||||
CleanupOnFail bool `envconfig:"cleanup_failed_upgrade"` // Pass --cleanup-on-fail 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`
|
LintStrictly bool `split_words:"true"` // Pass --strict to `helm lint`
|
||||||
|
SkipCrds bool `split_words:"true"` // Pass --skip-crds to `helm upgrade`
|
||||||
|
|
||||||
Stdout io.Writer `ignored:"true"`
|
Stdout io.Writer `ignored:"true"`
|
||||||
Stderr io.Writer `ignored:"true"`
|
Stderr io.Writer `ignored:"true"`
|
||||||
@@ -84,10 +90,18 @@ func NewConfig(stdout, stderr io.Writer) (*Config, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if cfg.SkipKubeconfig {
|
||||||
|
if cfg.KubeToken != "" || cfg.Certificate != "" || cfg.APIServer != "" || cfg.ServiceAccount != "" || cfg.SkipTLSVerify {
|
||||||
|
fmt.Fprintf(cfg.Stderr, "Warning: skip_kubeconfig is set. The following kubeconfig-related settings will be ignored: kube_config, kube_certificate, kube_api_server, kube_service_account, skip_tls_verify.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if justNumbers.MatchString(cfg.Timeout) {
|
if justNumbers.MatchString(cfg.Timeout) {
|
||||||
cfg.Timeout = fmt.Sprintf("%ss", cfg.Timeout)
|
cfg.Timeout = fmt.Sprintf("%ss", cfg.Timeout)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cfg.loadValuesSecrets()
|
||||||
|
|
||||||
if cfg.Debug && cfg.Stderr != nil {
|
if cfg.Debug && cfg.Stderr != nil {
|
||||||
cfg.logDebug()
|
cfg.logDebug()
|
||||||
}
|
}
|
||||||
@@ -97,6 +111,31 @@ func NewConfig(stdout, stderr io.Writer) (*Config, error) {
|
|||||||
return &cfg, nil
|
return &cfg, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (cfg *Config) loadValuesSecrets() {
|
||||||
|
findVar := regexp.MustCompile(`\$\{?(\w+)\}?`)
|
||||||
|
|
||||||
|
replacer := func(varName string) string {
|
||||||
|
sigils := regexp.MustCompile(`[${}]`)
|
||||||
|
varName = sigils.ReplaceAllString(varName, "")
|
||||||
|
|
||||||
|
if value, ok := os.LookupEnv(varName); ok {
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
|
||||||
|
if cfg.Debug {
|
||||||
|
fmt.Fprintf(cfg.Stderr, "$%s not present in environment, replaced with \"\"\n", varName)
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
cfg.Values = findVar.ReplaceAllStringFunc(cfg.Values, replacer)
|
||||||
|
cfg.StringValues = findVar.ReplaceAllStringFunc(cfg.StringValues, replacer)
|
||||||
|
|
||||||
|
for i := 0; i < len(cfg.AddRepos); i++ {
|
||||||
|
cfg.AddRepos[i] = findVar.ReplaceAllStringFunc(cfg.AddRepos[i], replacer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (cfg Config) logDebug() {
|
func (cfg Config) logDebug() {
|
||||||
if cfg.KubeToken != "" {
|
if cfg.KubeToken != "" {
|
||||||
cfg.KubeToken = "(redacted)"
|
cfg.KubeToken = "(redacted)"
|
||||||
@@ -114,6 +153,10 @@ func (cfg *Config) deprecationWarn() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// settingAliases provides alternate environment variable names for certain settings, either because
|
||||||
|
// they were renamed during drone-helm3's lifetime or for backward-compatibility with the original
|
||||||
|
// drone-helm. Most config options don't need to be included here; adding them to the main Config
|
||||||
|
// struct is sufficient.
|
||||||
type settingAliases struct {
|
type settingAliases struct {
|
||||||
Command string `envconfig:"helm_command"`
|
Command string `envconfig:"helm_command"`
|
||||||
AddRepos []string `envconfig:"helm_repos"`
|
AddRepos []string `envconfig:"helm_repos"`
|
||||||
|
|||||||
36
internal/env/config_test.go
vendored
36
internal/env/config_test.go
vendored
@@ -2,10 +2,11 @@ package env
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/stretchr/testify/suite"
|
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/suite"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ConfigTestSuite struct {
|
type ConfigTestSuite struct {
|
||||||
@@ -183,6 +184,39 @@ func (suite *ConfigTestSuite) TestLogDebugCensorsKubeToken() {
|
|||||||
suite.Equal(kubeToken, cfg.KubeToken) // The actual config value should be left unchanged
|
suite.Equal(kubeToken, cfg.KubeToken) // The actual config value should be left unchanged
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (suite *ConfigTestSuite) TestNewConfigWithValuesSecrets() {
|
||||||
|
suite.unsetenv("VALUES")
|
||||||
|
suite.unsetenv("STRING_VALUES")
|
||||||
|
suite.unsetenv("SECRET_WATER")
|
||||||
|
suite.setenv("SECRET_FIRE", "Eru_Ilúvatar")
|
||||||
|
suite.setenv("SECRET_RINGS", "1")
|
||||||
|
suite.setenv("PLUGIN_VALUES", "fire=$SECRET_FIRE,water=${SECRET_WATER}")
|
||||||
|
suite.setenv("PLUGIN_STRING_VALUES", "rings=${SECRET_RINGS}")
|
||||||
|
suite.setenv("PLUGIN_ADD_REPOS", "testrepo=https://user:${SECRET_FIRE}@testrepo.test")
|
||||||
|
|
||||||
|
cfg, err := NewConfig(&strings.Builder{}, &strings.Builder{})
|
||||||
|
suite.Require().NoError(err)
|
||||||
|
|
||||||
|
suite.Equal("fire=Eru_Ilúvatar,water=", cfg.Values)
|
||||||
|
suite.Equal("rings=1", cfg.StringValues)
|
||||||
|
suite.Equal(fmt.Sprintf("testrepo=https://user:%s@testrepo.test", os.Getenv("SECRET_FIRE")), cfg.AddRepos[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
func (suite *ConfigTestSuite) TestValuesSecretsWithDebugLogging() {
|
||||||
|
suite.unsetenv("VALUES")
|
||||||
|
suite.unsetenv("SECRET_WATER")
|
||||||
|
suite.setenv("SECRET_FIRE", "Eru_Ilúvatar")
|
||||||
|
suite.setenv("PLUGIN_DEBUG", "true")
|
||||||
|
suite.setenv("PLUGIN_STRING_VALUES", "fire=$SECRET_FIRE")
|
||||||
|
suite.setenv("PLUGIN_VALUES", "fire=$SECRET_FIRE,water=$SECRET_WATER")
|
||||||
|
stderr := strings.Builder{}
|
||||||
|
_, err := NewConfig(&strings.Builder{}, &stderr)
|
||||||
|
suite.Require().NoError(err)
|
||||||
|
|
||||||
|
suite.Contains(stderr.String(), "Values:fire=Eru_Ilúvatar,water=")
|
||||||
|
suite.Contains(stderr.String(), `$SECRET_WATER not present in environment, replaced with ""`)
|
||||||
|
}
|
||||||
|
|
||||||
func (suite *ConfigTestSuite) setenv(key, val string) {
|
func (suite *ConfigTestSuite) setenv(key, val string) {
|
||||||
orig, ok := os.LookupEnv(key)
|
orig, ok := os.LookupEnv(key)
|
||||||
if ok {
|
if ok {
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package helm
|
package helm
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/pelotech/drone-helm3/internal/env"
|
"github.com/pelotech/drone-helm3/internal/env"
|
||||||
"github.com/pelotech/drone-helm3/internal/run"
|
"github.com/pelotech/drone-helm3/internal/run"
|
||||||
@@ -30,6 +31,10 @@ func NewPlan(cfg env.Config) (*Plan, error) {
|
|||||||
cfg: cfg,
|
cfg: cfg,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if cfg.UpdateDependencies && cfg.DependenciesAction != "" {
|
||||||
|
return nil, errors.New("update_dependencies is deprecated and cannot be provided together with dependencies_action")
|
||||||
|
}
|
||||||
|
|
||||||
p.steps = (*determineSteps(cfg))(cfg)
|
p.steps = (*determineSteps(cfg))(cfg)
|
||||||
|
|
||||||
for i, step := range p.steps {
|
for i, step := range p.steps {
|
||||||
@@ -87,13 +92,21 @@ func (p *Plan) Execute() error {
|
|||||||
|
|
||||||
var upgrade = func(cfg env.Config) []Step {
|
var upgrade = func(cfg env.Config) []Step {
|
||||||
var steps []Step
|
var steps []Step
|
||||||
steps = append(steps, run.NewInitKube(cfg, kubeConfigTemplate, kubeConfigFile))
|
if !cfg.SkipKubeconfig {
|
||||||
|
steps = append(steps, run.NewInitKube(cfg, kubeConfigTemplate, kubeConfigFile))
|
||||||
|
}
|
||||||
for _, repo := range cfg.AddRepos {
|
for _, repo := range cfg.AddRepos {
|
||||||
steps = append(steps, run.NewAddRepo(cfg, repo))
|
steps = append(steps, run.NewAddRepo(cfg, repo))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if cfg.DependenciesAction != "" {
|
||||||
|
steps = append(steps, run.NewDepAction(cfg))
|
||||||
|
}
|
||||||
|
|
||||||
if cfg.UpdateDependencies {
|
if cfg.UpdateDependencies {
|
||||||
steps = append(steps, run.NewDepUpdate(cfg))
|
steps = append(steps, run.NewDepUpdate(cfg))
|
||||||
}
|
}
|
||||||
|
|
||||||
steps = append(steps, run.NewUpgrade(cfg))
|
steps = append(steps, run.NewUpgrade(cfg))
|
||||||
|
|
||||||
return steps
|
return steps
|
||||||
@@ -101,7 +114,9 @@ var upgrade = func(cfg env.Config) []Step {
|
|||||||
|
|
||||||
var uninstall = func(cfg env.Config) []Step {
|
var uninstall = func(cfg env.Config) []Step {
|
||||||
var steps []Step
|
var steps []Step
|
||||||
steps = append(steps, run.NewInitKube(cfg, kubeConfigTemplate, kubeConfigFile))
|
if !cfg.SkipKubeconfig {
|
||||||
|
steps = append(steps, run.NewInitKube(cfg, kubeConfigTemplate, kubeConfigFile))
|
||||||
|
}
|
||||||
if cfg.UpdateDependencies {
|
if cfg.UpdateDependencies {
|
||||||
steps = append(steps, run.NewDepUpdate(cfg))
|
steps = append(steps, run.NewDepUpdate(cfg))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -122,6 +122,12 @@ func (suite *PlanTestSuite) TestUpgrade() {
|
|||||||
suite.IsType(&run.Upgrade{}, steps[1])
|
suite.IsType(&run.Upgrade{}, steps[1])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (suite *PlanTestSuite) TestUpgradeWithSkipKubeconfig() {
|
||||||
|
steps := upgrade(env.Config{SkipKubeconfig: true})
|
||||||
|
suite.Require().Equal(1, len(steps), "upgrade should return 1 step")
|
||||||
|
suite.IsType(&run.Upgrade{}, steps[0])
|
||||||
|
}
|
||||||
|
|
||||||
func (suite *PlanTestSuite) TestUpgradeWithUpdateDependencies() {
|
func (suite *PlanTestSuite) TestUpgradeWithUpdateDependencies() {
|
||||||
cfg := env.Config{
|
cfg := env.Config{
|
||||||
UpdateDependencies: true,
|
UpdateDependencies: true,
|
||||||
|
|||||||
@@ -9,9 +9,9 @@ import (
|
|||||||
// AddRepo is an execution step that calls `helm repo add` when executed.
|
// AddRepo is an execution step that calls `helm repo add` when executed.
|
||||||
type AddRepo struct {
|
type AddRepo struct {
|
||||||
*config
|
*config
|
||||||
repo string
|
repo string
|
||||||
caFile string
|
certs *repoCerts
|
||||||
cmd cmd
|
cmd cmd
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewAddRepo creates an AddRepo for the given repo-spec. No validation is performed at this time.
|
// NewAddRepo creates an AddRepo for the given repo-spec. No validation is performed at this time.
|
||||||
@@ -19,7 +19,7 @@ func NewAddRepo(cfg env.Config, repo string) *AddRepo {
|
|||||||
return &AddRepo{
|
return &AddRepo{
|
||||||
config: newConfig(cfg),
|
config: newConfig(cfg),
|
||||||
repo: repo,
|
repo: repo,
|
||||||
caFile: cfg.RepoCAFile,
|
certs: newRepoCerts(cfg),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -38,14 +38,16 @@ func (a *AddRepo) Prepare() error {
|
|||||||
return fmt.Errorf("bad repo spec '%s'", a.repo)
|
return fmt.Errorf("bad repo spec '%s'", a.repo)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err := a.certs.write(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
name := split[0]
|
name := split[0]
|
||||||
url := split[1]
|
url := split[1]
|
||||||
|
|
||||||
args := a.globalFlags()
|
args := a.globalFlags()
|
||||||
args = append(args, "repo", "add")
|
args = append(args, "repo", "add")
|
||||||
if a.caFile != "" {
|
args = append(args, a.certs.flags()...)
|
||||||
args = append(args, "--ca-file", a.caFile)
|
|
||||||
}
|
|
||||||
args = append(args, name, url)
|
args = append(args, name, url)
|
||||||
|
|
||||||
a.cmd = command(helmBin, args...)
|
a.cmd = command(helmBin, args...)
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ func (suite *AddRepoTestSuite) TestNewAddRepo() {
|
|||||||
suite.Require().NotNil(repo)
|
suite.Require().NotNil(repo)
|
||||||
suite.Equal("picompress=https://github.com/caleb_phipps/picompress", repo.repo)
|
suite.Equal("picompress=https://github.com/caleb_phipps/picompress", repo.repo)
|
||||||
suite.NotNil(repo.config)
|
suite.NotNil(repo.config)
|
||||||
|
suite.NotNil(repo.certs)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (suite *AddRepoTestSuite) TestPrepareAndExecute() {
|
func (suite *AddRepoTestSuite) TestPrepareAndExecute() {
|
||||||
@@ -100,10 +101,11 @@ func (suite *AddRepoTestSuite) TestPrepareWithEqualSignInURL() {
|
|||||||
func (suite *AddRepoTestSuite) TestRepoAddFlags() {
|
func (suite *AddRepoTestSuite) TestRepoAddFlags() {
|
||||||
suite.mockCmd.EXPECT().Stdout(gomock.Any()).AnyTimes()
|
suite.mockCmd.EXPECT().Stdout(gomock.Any()).AnyTimes()
|
||||||
suite.mockCmd.EXPECT().Stderr(gomock.Any()).AnyTimes()
|
suite.mockCmd.EXPECT().Stderr(gomock.Any()).AnyTimes()
|
||||||
cfg := env.Config{
|
cfg := env.Config{}
|
||||||
RepoCAFile: "./helm/reporepo.cert",
|
|
||||||
}
|
|
||||||
a := NewAddRepo(cfg, "machine=https://github.com/harold_finch/themachine")
|
a := NewAddRepo(cfg, "machine=https://github.com/harold_finch/themachine")
|
||||||
|
|
||||||
|
// inject a ca cert filename so repoCerts won't create any files that we'd have to clean up
|
||||||
|
a.certs.caCertFilename = "./helm/reporepo.cert"
|
||||||
suite.NoError(a.Prepare())
|
suite.NoError(a.Prepare())
|
||||||
suite.Equal([]string{"repo", "add", "--ca-file", "./helm/reporepo.cert",
|
suite.Equal([]string{"repo", "add", "--ca-file", "./helm/reporepo.cert",
|
||||||
"machine", "https://github.com/harold_finch/themachine"}, suite.commandArgs)
|
"machine", "https://github.com/harold_finch/themachine"}, suite.commandArgs)
|
||||||
|
|||||||
59
internal/run/depaction.go
Normal file
59
internal/run/depaction.go
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
package run
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"github.com/pelotech/drone-helm3/internal/env"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
actionBuild = "build"
|
||||||
|
actionUpdate = "update"
|
||||||
|
)
|
||||||
|
|
||||||
|
// DepAction is an execution step that calls `helm dependency update` or `helm dependency build` when executed.
|
||||||
|
type DepAction struct {
|
||||||
|
*config
|
||||||
|
chart string
|
||||||
|
cmd cmd
|
||||||
|
action string
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewDepAction creates a DepAction using fields from the given Config. No validation is performed at this time.
|
||||||
|
func NewDepAction(cfg env.Config) *DepAction {
|
||||||
|
return &DepAction{
|
||||||
|
config: newConfig(cfg),
|
||||||
|
chart: cfg.Chart,
|
||||||
|
action: cfg.DependenciesAction,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Execute executes the `helm upgrade` command.
|
||||||
|
func (d *DepAction) Execute() error {
|
||||||
|
return d.cmd.Run()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prepare gets the DepAction ready to execute.
|
||||||
|
func (d *DepAction) Prepare() error {
|
||||||
|
if d.chart == "" {
|
||||||
|
return fmt.Errorf("chart is required")
|
||||||
|
}
|
||||||
|
|
||||||
|
args := d.globalFlags()
|
||||||
|
|
||||||
|
if d.action != actionBuild && d.action != actionUpdate {
|
||||||
|
return errors.New("unknown dependency_action: " + d.action)
|
||||||
|
}
|
||||||
|
|
||||||
|
args = append(args, "dependency", d.action, d.chart)
|
||||||
|
|
||||||
|
d.cmd = command(helmBin, args...)
|
||||||
|
d.cmd.Stdout(d.stdout)
|
||||||
|
d.cmd.Stderr(d.stderr)
|
||||||
|
|
||||||
|
if d.debug {
|
||||||
|
fmt.Fprintf(d.stderr, "Generated command: '%s'\n", d.cmd.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
131
internal/run/depaction_test.go
Normal file
131
internal/run/depaction_test.go
Normal file
@@ -0,0 +1,131 @@
|
|||||||
|
package run
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"github.com/golang/mock/gomock"
|
||||||
|
"github.com/pelotech/drone-helm3/internal/env"
|
||||||
|
"github.com/stretchr/testify/suite"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
type DepActionTestSuite struct {
|
||||||
|
suite.Suite
|
||||||
|
ctrl *gomock.Controller
|
||||||
|
mockCmd *Mockcmd
|
||||||
|
originalCommand func(string, ...string) cmd
|
||||||
|
}
|
||||||
|
|
||||||
|
func (suite *DepActionTestSuite) BeforeTest(_, _ string) {
|
||||||
|
suite.ctrl = gomock.NewController(suite.T())
|
||||||
|
suite.mockCmd = NewMockcmd(suite.ctrl)
|
||||||
|
|
||||||
|
suite.originalCommand = command
|
||||||
|
command = func(path string, args ...string) cmd { return suite.mockCmd }
|
||||||
|
}
|
||||||
|
|
||||||
|
func (suite *DepActionTestSuite) AfterTest(_, _ string) {
|
||||||
|
command = suite.originalCommand
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDepActionTestSuite(t *testing.T) {
|
||||||
|
suite.Run(t, new(DepActionTestSuite))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (suite *DepActionTestSuite) TestNewDepAction() {
|
||||||
|
cfg := env.Config{
|
||||||
|
Chart: "scatterplot",
|
||||||
|
}
|
||||||
|
d := NewDepAction(cfg)
|
||||||
|
suite.Equal("scatterplot", d.chart)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (suite *DepActionTestSuite) TestPrepareAndExecuteBuild() {
|
||||||
|
defer suite.ctrl.Finish()
|
||||||
|
|
||||||
|
stdout := strings.Builder{}
|
||||||
|
stderr := strings.Builder{}
|
||||||
|
cfg := env.Config{
|
||||||
|
Chart: "your_top_songs_2019",
|
||||||
|
Stdout: &stdout,
|
||||||
|
Stderr: &stderr,
|
||||||
|
DependenciesAction: "build",
|
||||||
|
}
|
||||||
|
|
||||||
|
command = func(path string, args ...string) cmd {
|
||||||
|
suite.Equal(helmBin, path)
|
||||||
|
suite.Equal([]string{"dependency", "build", "your_top_songs_2019"}, args)
|
||||||
|
|
||||||
|
return suite.mockCmd
|
||||||
|
}
|
||||||
|
suite.mockCmd.EXPECT().
|
||||||
|
Stdout(&stdout)
|
||||||
|
suite.mockCmd.EXPECT().
|
||||||
|
Stderr(&stderr)
|
||||||
|
suite.mockCmd.EXPECT().
|
||||||
|
Run().
|
||||||
|
Times(1)
|
||||||
|
|
||||||
|
d := NewDepAction(cfg)
|
||||||
|
|
||||||
|
suite.Require().NoError(d.Prepare())
|
||||||
|
suite.NoError(d.Execute())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (suite *DepActionTestSuite) TestPrepareAndExecuteUpdate() {
|
||||||
|
defer suite.ctrl.Finish()
|
||||||
|
|
||||||
|
stdout := strings.Builder{}
|
||||||
|
stderr := strings.Builder{}
|
||||||
|
cfg := env.Config{
|
||||||
|
Chart: "your_top_songs_2019",
|
||||||
|
Stdout: &stdout,
|
||||||
|
Stderr: &stderr,
|
||||||
|
DependenciesAction: "update",
|
||||||
|
}
|
||||||
|
|
||||||
|
command = func(path string, args ...string) cmd {
|
||||||
|
suite.Equal(helmBin, path)
|
||||||
|
suite.Equal([]string{"dependency", "update", "your_top_songs_2019"}, args)
|
||||||
|
|
||||||
|
return suite.mockCmd
|
||||||
|
}
|
||||||
|
suite.mockCmd.EXPECT().
|
||||||
|
Stdout(&stdout)
|
||||||
|
suite.mockCmd.EXPECT().
|
||||||
|
Stderr(&stderr)
|
||||||
|
suite.mockCmd.EXPECT().
|
||||||
|
Run().
|
||||||
|
Times(1)
|
||||||
|
|
||||||
|
d := NewDepAction(cfg)
|
||||||
|
|
||||||
|
suite.Require().NoError(d.Prepare())
|
||||||
|
suite.NoError(d.Execute())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (suite *DepActionTestSuite) TestPrepareAndExecuteUnknown() {
|
||||||
|
defer suite.ctrl.Finish()
|
||||||
|
|
||||||
|
stdout := strings.Builder{}
|
||||||
|
stderr := strings.Builder{}
|
||||||
|
cfg := env.Config{
|
||||||
|
Chart: "your_top_songs_2019",
|
||||||
|
Stdout: &stdout,
|
||||||
|
Stderr: &stderr,
|
||||||
|
DependenciesAction: "downgrade",
|
||||||
|
}
|
||||||
|
|
||||||
|
d := NewDepAction(cfg)
|
||||||
|
suite.Require().Equal(errors.New("unknown dependency_action: downgrade"), d.Prepare())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (suite *DepActionTestSuite) TestPrepareChartRequired() {
|
||||||
|
d := NewDepAction(env.Config{})
|
||||||
|
|
||||||
|
suite.mockCmd.EXPECT().Stdout(gomock.Any()).AnyTimes()
|
||||||
|
suite.mockCmd.EXPECT().Stderr(gomock.Any()).AnyTimes()
|
||||||
|
|
||||||
|
err := d.Prepare()
|
||||||
|
suite.EqualError(err, "chart is required")
|
||||||
|
}
|
||||||
77
internal/run/repocerts.go
Normal file
77
internal/run/repocerts.go
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
package run
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/base64"
|
||||||
|
"fmt"
|
||||||
|
"github.com/pelotech/drone-helm3/internal/env"
|
||||||
|
"io/ioutil"
|
||||||
|
)
|
||||||
|
|
||||||
|
type repoCerts struct {
|
||||||
|
*config
|
||||||
|
cert string
|
||||||
|
certFilename string
|
||||||
|
caCert string
|
||||||
|
caCertFilename string
|
||||||
|
}
|
||||||
|
|
||||||
|
func newRepoCerts(cfg env.Config) *repoCerts {
|
||||||
|
return &repoCerts{
|
||||||
|
config: newConfig(cfg),
|
||||||
|
cert: cfg.RepoCertificate,
|
||||||
|
caCert: cfg.RepoCACertificate,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rc *repoCerts) write() error {
|
||||||
|
if rc.cert != "" {
|
||||||
|
file, err := ioutil.TempFile("", "repo********.cert")
|
||||||
|
defer file.Close()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to create certificate file: %w", err)
|
||||||
|
}
|
||||||
|
rc.certFilename = file.Name()
|
||||||
|
rawCert, err := base64.StdEncoding.DecodeString(rc.cert)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to base64-decode certificate string: %w", err)
|
||||||
|
}
|
||||||
|
if rc.debug {
|
||||||
|
fmt.Fprintf(rc.stderr, "writing repo certificate to %s\n", rc.certFilename)
|
||||||
|
}
|
||||||
|
if _, err := file.Write(rawCert); err != nil {
|
||||||
|
return fmt.Errorf("failed to write certificate file: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if rc.caCert != "" {
|
||||||
|
file, err := ioutil.TempFile("", "repo********.ca.cert")
|
||||||
|
defer file.Close()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to create CA certificate file: %w", err)
|
||||||
|
}
|
||||||
|
rc.caCertFilename = file.Name()
|
||||||
|
rawCert, err := base64.StdEncoding.DecodeString(rc.caCert)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to base64-decode CA certificate string: %w", err)
|
||||||
|
}
|
||||||
|
if rc.debug {
|
||||||
|
fmt.Fprintf(rc.stderr, "writing repo ca certificate to %s\n", rc.caCertFilename)
|
||||||
|
}
|
||||||
|
if _, err := file.Write(rawCert); err != nil {
|
||||||
|
return fmt.Errorf("failed to write CA certificate file: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rc *repoCerts) flags() []string {
|
||||||
|
flags := make([]string, 0)
|
||||||
|
if rc.certFilename != "" {
|
||||||
|
flags = append(flags, "--cert-file", rc.certFilename)
|
||||||
|
}
|
||||||
|
if rc.caCertFilename != "" {
|
||||||
|
flags = append(flags, "--ca-file", rc.caCertFilename)
|
||||||
|
}
|
||||||
|
|
||||||
|
return flags
|
||||||
|
}
|
||||||
80
internal/run/repocerts_test.go
Normal file
80
internal/run/repocerts_test.go
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
package run
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/pelotech/drone-helm3/internal/env"
|
||||||
|
"github.com/stretchr/testify/suite"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
type RepoCertsTestSuite struct {
|
||||||
|
suite.Suite
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRepoCertsTestSuite(t *testing.T) {
|
||||||
|
suite.Run(t, new(RepoCertsTestSuite))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (suite *RepoCertsTestSuite) TestNewRepoCerts() {
|
||||||
|
cfg := env.Config{
|
||||||
|
RepoCertificate: "bGljZW5zZWQgYnkgdGhlIFN0YXRlIG9mIE9yZWdvbiB0byBwZXJmb3JtIHJlcG9zc2Vzc2lvbnM=",
|
||||||
|
RepoCACertificate: "T3JlZ29uIFN0YXRlIExpY2Vuc3VyZSBib2FyZA==",
|
||||||
|
}
|
||||||
|
rc := newRepoCerts(cfg)
|
||||||
|
suite.Require().NotNil(rc)
|
||||||
|
suite.Equal("bGljZW5zZWQgYnkgdGhlIFN0YXRlIG9mIE9yZWdvbiB0byBwZXJmb3JtIHJlcG9zc2Vzc2lvbnM=", rc.cert)
|
||||||
|
suite.Equal("T3JlZ29uIFN0YXRlIExpY2Vuc3VyZSBib2FyZA==", rc.caCert)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (suite *RepoCertsTestSuite) TestWrite() {
|
||||||
|
cfg := env.Config{
|
||||||
|
RepoCertificate: "bGljZW5zZWQgYnkgdGhlIFN0YXRlIG9mIE9yZWdvbiB0byBwZXJmb3JtIHJlcG9zc2Vzc2lvbnM=",
|
||||||
|
RepoCACertificate: "T3JlZ29uIFN0YXRlIExpY2Vuc3VyZSBib2FyZA==",
|
||||||
|
}
|
||||||
|
rc := newRepoCerts(cfg)
|
||||||
|
suite.Require().NotNil(rc)
|
||||||
|
|
||||||
|
suite.NoError(rc.write())
|
||||||
|
defer os.Remove(rc.certFilename)
|
||||||
|
defer os.Remove(rc.caCertFilename)
|
||||||
|
suite.NotEqual("", rc.certFilename)
|
||||||
|
suite.NotEqual("", rc.caCertFilename)
|
||||||
|
|
||||||
|
cert, err := ioutil.ReadFile(rc.certFilename)
|
||||||
|
suite.Require().NoError(err)
|
||||||
|
caCert, err := ioutil.ReadFile(rc.caCertFilename)
|
||||||
|
suite.Require().NoError(err)
|
||||||
|
suite.Equal("licensed by the State of Oregon to perform repossessions", string(cert))
|
||||||
|
suite.Equal("Oregon State Licensure board", string(caCert))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (suite *RepoCertsTestSuite) TestFlags() {
|
||||||
|
rc := newRepoCerts(env.Config{})
|
||||||
|
suite.Equal([]string{}, rc.flags())
|
||||||
|
rc.certFilename = "hurgityburgity"
|
||||||
|
suite.Equal([]string{"--cert-file", "hurgityburgity"}, rc.flags())
|
||||||
|
rc.caCertFilename = "honglydongly"
|
||||||
|
suite.Equal([]string{"--cert-file", "hurgityburgity", "--ca-file", "honglydongly"}, rc.flags())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (suite *RepoCertsTestSuite) TestDebug() {
|
||||||
|
stderr := strings.Builder{}
|
||||||
|
cfg := env.Config{
|
||||||
|
RepoCertificate: "bGljZW5zZWQgYnkgdGhlIFN0YXRlIG9mIE9yZWdvbiB0byBwZXJmb3JtIHJlcG9zc2Vzc2lvbnM=",
|
||||||
|
RepoCACertificate: "T3JlZ29uIFN0YXRlIExpY2Vuc3VyZSBib2FyZA==",
|
||||||
|
Stderr: &stderr,
|
||||||
|
Debug: true,
|
||||||
|
}
|
||||||
|
rc := newRepoCerts(cfg)
|
||||||
|
suite.Require().NotNil(rc)
|
||||||
|
|
||||||
|
suite.NoError(rc.write())
|
||||||
|
defer os.Remove(rc.certFilename)
|
||||||
|
defer os.Remove(rc.caCertFilename)
|
||||||
|
|
||||||
|
suite.Contains(stderr.String(), fmt.Sprintf("writing repo certificate to %s", rc.certFilename))
|
||||||
|
suite.Contains(stderr.String(), fmt.Sprintf("writing repo ca certificate to %s", rc.caCertFilename))
|
||||||
|
}
|
||||||
@@ -11,18 +11,20 @@ type Upgrade struct {
|
|||||||
chart string
|
chart string
|
||||||
release string
|
release string
|
||||||
|
|
||||||
chartVersion string
|
chartVersion string
|
||||||
dryRun bool
|
dryRun bool
|
||||||
wait bool
|
wait bool
|
||||||
values string
|
values string
|
||||||
stringValues string
|
stringValues string
|
||||||
valuesFiles []string
|
valuesFiles []string
|
||||||
reuseValues bool
|
reuseValues bool
|
||||||
timeout string
|
timeout string
|
||||||
force bool
|
force bool
|
||||||
atomic bool
|
atomic bool
|
||||||
cleanupOnFail bool
|
cleanupOnFail bool
|
||||||
caFile string
|
certs *repoCerts
|
||||||
|
createNamespace bool
|
||||||
|
skipCrds bool
|
||||||
|
|
||||||
cmd cmd
|
cmd cmd
|
||||||
}
|
}
|
||||||
@@ -30,21 +32,23 @@ type Upgrade struct {
|
|||||||
// NewUpgrade creates an Upgrade using fields from the given Config. No validation is performed at this time.
|
// NewUpgrade creates an Upgrade using fields from the given Config. No validation is performed at this time.
|
||||||
func NewUpgrade(cfg env.Config) *Upgrade {
|
func NewUpgrade(cfg env.Config) *Upgrade {
|
||||||
return &Upgrade{
|
return &Upgrade{
|
||||||
config: newConfig(cfg),
|
config: newConfig(cfg),
|
||||||
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,
|
||||||
values: cfg.Values,
|
values: cfg.Values,
|
||||||
stringValues: cfg.StringValues,
|
stringValues: cfg.StringValues,
|
||||||
valuesFiles: cfg.ValuesFiles,
|
valuesFiles: cfg.ValuesFiles,
|
||||||
reuseValues: cfg.ReuseValues,
|
reuseValues: cfg.ReuseValues,
|
||||||
timeout: cfg.Timeout,
|
timeout: cfg.Timeout,
|
||||||
force: cfg.Force,
|
force: cfg.Force,
|
||||||
atomic: cfg.AtomicUpgrade,
|
atomic: cfg.AtomicUpgrade,
|
||||||
cleanupOnFail: cfg.CleanupOnFail,
|
cleanupOnFail: cfg.CleanupOnFail,
|
||||||
caFile: cfg.RepoCAFile,
|
certs: newRepoCerts(cfg),
|
||||||
|
createNamespace: cfg.CreateNamespace,
|
||||||
|
skipCrds: cfg.SkipCrds,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -95,12 +99,16 @@ func (u *Upgrade) Prepare() error {
|
|||||||
if u.stringValues != "" {
|
if u.stringValues != "" {
|
||||||
args = append(args, "--set-string", u.stringValues)
|
args = append(args, "--set-string", u.stringValues)
|
||||||
}
|
}
|
||||||
|
if u.createNamespace {
|
||||||
|
args = append(args, "--create-namespace")
|
||||||
|
}
|
||||||
|
if u.skipCrds {
|
||||||
|
args = append(args, "--skip-crds")
|
||||||
|
}
|
||||||
for _, vFile := range u.valuesFiles {
|
for _, vFile := range u.valuesFiles {
|
||||||
args = append(args, "--values", vFile)
|
args = append(args, "--values", vFile)
|
||||||
}
|
}
|
||||||
if u.caFile != "" {
|
args = append(args, u.certs.flags()...)
|
||||||
args = append(args, "--ca-file", u.caFile)
|
|
||||||
}
|
|
||||||
|
|
||||||
args = append(args, u.release, u.chart)
|
args = append(args, u.release, u.chart)
|
||||||
u.cmd = command(helmBin, args...)
|
u.cmd = command(helmBin, args...)
|
||||||
|
|||||||
@@ -64,6 +64,7 @@ func (suite *UpgradeTestSuite) TestNewUpgrade() {
|
|||||||
suite.Equal(true, up.atomic)
|
suite.Equal(true, up.atomic)
|
||||||
suite.Equal(true, up.cleanupOnFail)
|
suite.Equal(true, up.cleanupOnFail)
|
||||||
suite.NotNil(up.config)
|
suite.NotNil(up.config)
|
||||||
|
suite.NotNil(up.certs)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (suite *UpgradeTestSuite) TestPrepareAndExecute() {
|
func (suite *UpgradeTestSuite) TestPrepareAndExecute() {
|
||||||
@@ -136,9 +137,10 @@ func (suite *UpgradeTestSuite) TestPrepareWithUpgradeFlags() {
|
|||||||
Force: true,
|
Force: true,
|
||||||
AtomicUpgrade: true,
|
AtomicUpgrade: true,
|
||||||
CleanupOnFail: true,
|
CleanupOnFail: true,
|
||||||
RepoCAFile: "local_ca.cert",
|
|
||||||
}
|
}
|
||||||
u := NewUpgrade(cfg)
|
u := NewUpgrade(cfg)
|
||||||
|
// inject a ca cert filename so repoCerts won't create any files that we'd have to clean up
|
||||||
|
u.certs.caCertFilename = "local_ca.cert"
|
||||||
|
|
||||||
command = func(path string, args ...string) cmd {
|
command = func(path string, args ...string) cmd {
|
||||||
suite.Equal(helmBin, path)
|
suite.Equal(helmBin, path)
|
||||||
@@ -218,3 +220,27 @@ func (suite *UpgradeTestSuite) TestPrepareDebugFlag() {
|
|||||||
suite.Equal(want, stderr.String())
|
suite.Equal(want, stderr.String())
|
||||||
suite.Equal("", stdout.String())
|
suite.Equal("", stdout.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (suite *UpgradeTestSuite) TestPrepareSkipCrdsFlag() {
|
||||||
|
defer suite.ctrl.Finish()
|
||||||
|
|
||||||
|
cfg := env.Config{
|
||||||
|
Chart: "at40",
|
||||||
|
Release: "cabbages_smell_great",
|
||||||
|
SkipCrds: true,
|
||||||
|
}
|
||||||
|
u := NewUpgrade(cfg)
|
||||||
|
|
||||||
|
command = func(path string, args ...string) cmd {
|
||||||
|
suite.Equal(helmBin, path)
|
||||||
|
suite.Equal([]string{"upgrade", "--install", "--skip-crds", "cabbages_smell_great", "at40"}, args)
|
||||||
|
|
||||||
|
return suite.mockCmd
|
||||||
|
}
|
||||||
|
|
||||||
|
suite.mockCmd.EXPECT().Stdout(gomock.Any())
|
||||||
|
suite.mockCmd.EXPECT().Stderr(gomock.Any())
|
||||||
|
|
||||||
|
err := u.Prepare()
|
||||||
|
suite.Require().Nil(err)
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user