Compare commits
23 Commits
alias-sett
...
v0.14.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a4834dd4f7 | ||
|
|
dbcef2699e | ||
|
|
22aa1df894 | ||
|
|
8f7b481934 | ||
|
|
e843b26759 | ||
|
|
713dcd8317 | ||
|
|
18313eeb5c | ||
|
|
ee6d8d1724 | ||
|
|
fcddc6e077 | ||
|
|
c4b11795e3 | ||
|
|
ffa636ce47 | ||
|
|
c38537ac32 | ||
|
|
1f7b6bb389 | ||
|
|
79532e7635 | ||
|
|
a21848484b | ||
|
|
d8ddb79ef4 | ||
|
|
231138563c | ||
|
|
88bb8085b0 | ||
|
|
21b9d32329 | ||
|
|
588c7cb9f7 | ||
|
|
16117eea2f | ||
|
|
8a9cf23ab9 | ||
|
|
3d1a2227da |
@@ -5,11 +5,12 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
|
|
||||||
_ "github.com/joho/godotenv/autoload"
|
_ "github.com/joho/godotenv/autoload"
|
||||||
|
"github.com/pelotech/drone-helm3/internal/env"
|
||||||
"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 := env.NewConfig(os.Stdout, os.Stderr)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Fprintf(os.Stderr, "%s\n", err.Error())
|
fmt.Fprintf(os.Stderr, "%s\n", err.Error())
|
||||||
|
|||||||
@@ -1,13 +1,15 @@
|
|||||||
# Parameter reference
|
# Parameter reference
|
||||||
|
|
||||||
## Global
|
## Global
|
||||||
| Param name | Type | Purpose |
|
| Param name | Type | Alias | Purpose |
|
||||||
|---------------------|-----------------|---------|
|
|---------------------|-----------------|--------------|---------|
|
||||||
| mode | string | 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\> | 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/`. |
|
||||||
| namespace | string | Kubernetes namespace to use for this operation. |
|
| repo_certificate | string | | Base64 encoded TLS certificate for a chart repository. |
|
||||||
| 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. |
|
| repo_ca_certificate | string | | Base64 encoded TLS certificate for a chart repository certificate authority. |
|
||||||
|
| 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. |
|
||||||
|
|
||||||
## Linting
|
## Linting
|
||||||
|
|
||||||
@@ -25,43 +27,43 @@ Linting is only triggered when the `mode` setting is "lint".
|
|||||||
|
|
||||||
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.
|
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 | Alias | 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. |
|
||||||
| kube_api_server | string | yes | API endpoint for the Kubernetes cluster. |
|
| kube_api_server | string | yes | api_server | API endpoint for the Kubernetes cluster. |
|
||||||
| kube_token | string | yes | Token for authenticating to Kubernetes. |
|
| kube_token | string | yes | kubernetes_token | Token for authenticating to Kubernetes. |
|
||||||
| kube_service_account | string | | Service account for authenticating to Kubernetes. Default is `helm`. |
|
| kube_service_account | string | | service_account | Service account for authenticating to Kubernetes. Default is `helm`. |
|
||||||
| kube_certificate | string | | Base64 encoded TLS certificate used by the Kubernetes cluster's certificate authority. |
|
| kube_certificate | string | | kubernetes_certificate | 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_for_upgrade | boolean | | 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 | | Pass `--force` to `helm upgrade`. |
|
| force_upgrade | boolean | | force | Pass `--force` to `helm upgrade`. |
|
||||||
| atomic_upgrade | boolean | | Pass `--atomic` to `helm upgrade`. |
|
| atomic_upgrade | boolean | | | Pass `--atomic` to `helm upgrade`. |
|
||||||
| cleanup_failed_upgrade | boolean | | Pass `--cleanup-on-fail` 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`. |
|
||||||
| 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. |
|
||||||
|
|
||||||
## Uninstallation
|
## Uninstallation
|
||||||
|
|
||||||
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.
|
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 | 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 endpoint for the Kubernetes cluster. |
|
| kube_api_server | string | yes | api_server | API endpoint for the Kubernetes cluster. |
|
||||||
| kube_token | string | yes | Token for authenticating to Kubernetes. |
|
| kube_token | string | yes | kubernetes_token | Token for authenticating to Kubernetes. |
|
||||||
| kube_service_account | string | | Service account for authenticating to Kubernetes. Default is `helm`. |
|
| kube_service_account | string | | service_account | Service account for authenticating to Kubernetes. Default is `helm`. |
|
||||||
| kube_certificate | string | | Base64 encoded TLS certificate used by the Kubernetes cluster's certificate authority. |
|
| kube_certificate | string | | kubernetes_certificate | 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. |
|
| 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. |
|
||||||
| 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
|
||||||
|
|
||||||
@@ -92,3 +94,38 @@ 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" ]
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Interpolating secrets into the `values` and `string_values` 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
|
||||||
|
|
||||||
|
Some settings have alternate names, for backward-compatibility with drone-helm. We recommend using the canonical name unless you require the backward-compatible form.
|
||||||
|
|
||||||
|
| Canonical name | Alias |
|
||||||
|
|----------------------|-------|
|
||||||
|
| mode | helm_command |
|
||||||
|
| add_repos | helm_repos |
|
||||||
|
| kube_api_server | api_server |
|
||||||
|
| kube_service_account | service_account |
|
||||||
|
| kube_token | kubernetes_token |
|
||||||
|
| kube_certificate | kubernetes_certificate |
|
||||||
|
| wait_for_upgrade | wait |
|
||||||
|
| force_upgrade | force |
|
||||||
|
|||||||
33
internal/helm/config.go → internal/env/config.go
vendored
33
internal/helm/config.go → internal/env/config.go
vendored
@@ -1,4 +1,4 @@
|
|||||||
package helm
|
package env
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
@@ -21,16 +21,18 @@ var (
|
|||||||
type Config struct {
|
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"` // Call `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
|
||||||
|
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
|
||||||
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
|
||||||
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
|
||||||
ServiceAccount string `envconfig:"kube_service_account"` // Account to use for connecting to the Kubernetes cluster
|
ServiceAccount string `envconfig:"kube_service_account"` // Account to use for connecting to the Kubernetes cluster
|
||||||
@@ -44,7 +46,7 @@ type Config struct {
|
|||||||
Release string `` // Release argument to use in applicable helm commands
|
Release string `` // Release argument to use in applicable helm commands
|
||||||
Force bool `envconfig:"force_upgrade"` // Pass --force to applicable helm commands
|
Force bool `envconfig:"force_upgrade"` // Pass --force to applicable helm commands
|
||||||
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`
|
||||||
|
|
||||||
Stdout io.Writer `ignored:"true"`
|
Stdout io.Writer `ignored:"true"`
|
||||||
@@ -87,6 +89,8 @@ func NewConfig(stdout, stderr io.Writer) (*Config, error) {
|
|||||||
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()
|
||||||
}
|
}
|
||||||
@@ -96,6 +100,27 @@ 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)
|
||||||
|
}
|
||||||
|
|
||||||
func (cfg Config) logDebug() {
|
func (cfg Config) logDebug() {
|
||||||
if cfg.KubeToken != "" {
|
if cfg.KubeToken != "" {
|
||||||
cfg.KubeToken = "(redacted)"
|
cfg.KubeToken = "(redacted)"
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package helm
|
package env
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
@@ -183,6 +183,37 @@ 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}")
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
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 {
|
||||||
@@ -6,7 +6,6 @@ package helm
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
gomock "github.com/golang/mock/gomock"
|
gomock "github.com/golang/mock/gomock"
|
||||||
run "github.com/pelotech/drone-helm3/internal/run"
|
|
||||||
reflect "reflect"
|
reflect "reflect"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -34,29 +33,29 @@ func (m *MockStep) EXPECT() *MockStepMockRecorder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Prepare mocks base method
|
// Prepare mocks base method
|
||||||
func (m *MockStep) Prepare(arg0 run.Config) error {
|
func (m *MockStep) Prepare() error {
|
||||||
m.ctrl.T.Helper()
|
m.ctrl.T.Helper()
|
||||||
ret := m.ctrl.Call(m, "Prepare", arg0)
|
ret := m.ctrl.Call(m, "Prepare")
|
||||||
ret0, _ := ret[0].(error)
|
ret0, _ := ret[0].(error)
|
||||||
return ret0
|
return ret0
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prepare indicates an expected call of Prepare
|
// Prepare indicates an expected call of Prepare
|
||||||
func (mr *MockStepMockRecorder) Prepare(arg0 interface{}) *gomock.Call {
|
func (mr *MockStepMockRecorder) Prepare() *gomock.Call {
|
||||||
mr.mock.ctrl.T.Helper()
|
mr.mock.ctrl.T.Helper()
|
||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Prepare", reflect.TypeOf((*MockStep)(nil).Prepare), arg0)
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Prepare", reflect.TypeOf((*MockStep)(nil).Prepare))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Execute mocks base method
|
// Execute mocks base method
|
||||||
func (m *MockStep) Execute(arg0 run.Config) error {
|
func (m *MockStep) Execute() error {
|
||||||
m.ctrl.T.Helper()
|
m.ctrl.T.Helper()
|
||||||
ret := m.ctrl.Call(m, "Execute", arg0)
|
ret := m.ctrl.Call(m, "Execute")
|
||||||
ret0, _ := ret[0].(error)
|
ret0, _ := ret[0].(error)
|
||||||
return ret0
|
return ret0
|
||||||
}
|
}
|
||||||
|
|
||||||
// Execute indicates an expected call of Execute
|
// Execute indicates an expected call of Execute
|
||||||
func (mr *MockStepMockRecorder) Execute(arg0 interface{}) *gomock.Call {
|
func (mr *MockStepMockRecorder) Execute() *gomock.Call {
|
||||||
mr.mock.ctrl.T.Helper()
|
mr.mock.ctrl.T.Helper()
|
||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Execute", reflect.TypeOf((*MockStep)(nil).Execute), arg0)
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Execute", reflect.TypeOf((*MockStep)(nil).Execute))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package helm
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/pelotech/drone-helm3/internal/env"
|
||||||
"github.com/pelotech/drone-helm3/internal/run"
|
"github.com/pelotech/drone-helm3/internal/run"
|
||||||
"os"
|
"os"
|
||||||
)
|
)
|
||||||
@@ -13,27 +14,20 @@ const (
|
|||||||
|
|
||||||
// A Step is one step in the plan.
|
// A Step is one step in the plan.
|
||||||
type Step interface {
|
type Step interface {
|
||||||
Prepare(run.Config) error
|
Prepare() error
|
||||||
Execute(run.Config) error
|
Execute() error
|
||||||
}
|
}
|
||||||
|
|
||||||
// A Plan is a series of steps to perform.
|
// A Plan is a series of steps to perform.
|
||||||
type Plan struct {
|
type Plan struct {
|
||||||
steps []Step
|
steps []Step
|
||||||
cfg Config
|
cfg env.Config
|
||||||
runCfg run.Config
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewPlan makes a plan for running a helm operation.
|
// NewPlan makes a plan for running a helm operation.
|
||||||
func NewPlan(cfg Config) (*Plan, error) {
|
func NewPlan(cfg env.Config) (*Plan, error) {
|
||||||
p := Plan{
|
p := Plan{
|
||||||
cfg: cfg,
|
cfg: cfg,
|
||||||
runCfg: run.Config{
|
|
||||||
Debug: cfg.Debug,
|
|
||||||
Namespace: cfg.Namespace,
|
|
||||||
Stdout: cfg.Stdout,
|
|
||||||
Stderr: cfg.Stderr,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
p.steps = (*determineSteps(cfg))(cfg)
|
p.steps = (*determineSteps(cfg))(cfg)
|
||||||
@@ -43,7 +37,7 @@ func NewPlan(cfg Config) (*Plan, error) {
|
|||||||
fmt.Fprintf(os.Stderr, "calling %T.Prepare (step %d)\n", step, i)
|
fmt.Fprintf(os.Stderr, "calling %T.Prepare (step %d)\n", step, i)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := step.Prepare(p.runCfg); err != nil {
|
if err := step.Prepare(); err != nil {
|
||||||
err = fmt.Errorf("while preparing %T step: %w", step, err)
|
err = fmt.Errorf("while preparing %T step: %w", step, err)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -54,7 +48,7 @@ func NewPlan(cfg Config) (*Plan, error) {
|
|||||||
|
|
||||||
// determineSteps is primarily for the tests' convenience: it allows testing the "which stuff should
|
// determineSteps is primarily for the tests' convenience: it allows testing the "which stuff should
|
||||||
// we do" logic without building a config that meets all the steps' requirements.
|
// we do" logic without building a config that meets all the steps' requirements.
|
||||||
func determineSteps(cfg Config) *func(Config) []Step {
|
func determineSteps(cfg env.Config) *func(env.Config) []Step {
|
||||||
switch cfg.Command {
|
switch cfg.Command {
|
||||||
case "upgrade":
|
case "upgrade":
|
||||||
return &upgrade
|
return &upgrade
|
||||||
@@ -83,7 +77,7 @@ func (p *Plan) Execute() error {
|
|||||||
fmt.Fprintf(p.cfg.Stderr, "calling %T.Execute (step %d)\n", step, i)
|
fmt.Fprintf(p.cfg.Stderr, "calling %T.Execute (step %d)\n", step, i)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := step.Execute(p.runCfg); err != nil {
|
if err := step.Execute(); err != nil {
|
||||||
return fmt.Errorf("while executing %T step: %w", step, err)
|
return fmt.Errorf("while executing %T step: %w", step, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -91,97 +85,43 @@ func (p *Plan) Execute() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var upgrade = func(cfg Config) []Step {
|
var upgrade = func(cfg env.Config) []Step {
|
||||||
steps := initKube(cfg)
|
var steps []Step
|
||||||
steps = append(steps, addRepos(cfg)...)
|
steps = append(steps, run.NewInitKube(cfg, kubeConfigTemplate, kubeConfigFile))
|
||||||
if cfg.UpdateDependencies {
|
|
||||||
steps = append(steps, depUpdate(cfg)...)
|
|
||||||
}
|
|
||||||
steps = append(steps, &run.Upgrade{
|
|
||||||
Chart: cfg.Chart,
|
|
||||||
Release: cfg.Release,
|
|
||||||
ChartVersion: cfg.ChartVersion,
|
|
||||||
DryRun: cfg.DryRun,
|
|
||||||
Wait: cfg.Wait,
|
|
||||||
Values: cfg.Values,
|
|
||||||
StringValues: cfg.StringValues,
|
|
||||||
ValuesFiles: cfg.ValuesFiles,
|
|
||||||
ReuseValues: cfg.ReuseValues,
|
|
||||||
Timeout: cfg.Timeout,
|
|
||||||
Force: cfg.Force,
|
|
||||||
Atomic: cfg.AtomicUpgrade,
|
|
||||||
CleanupOnFail: cfg.CleanupOnFail,
|
|
||||||
})
|
|
||||||
|
|
||||||
return steps
|
|
||||||
}
|
|
||||||
|
|
||||||
var uninstall = func(cfg Config) []Step {
|
|
||||||
steps := initKube(cfg)
|
|
||||||
if cfg.UpdateDependencies {
|
|
||||||
steps = append(steps, depUpdate(cfg)...)
|
|
||||||
}
|
|
||||||
steps = append(steps, &run.Uninstall{
|
|
||||||
Release: cfg.Release,
|
|
||||||
DryRun: cfg.DryRun,
|
|
||||||
KeepHistory: cfg.KeepHistory,
|
|
||||||
})
|
|
||||||
|
|
||||||
return steps
|
|
||||||
}
|
|
||||||
|
|
||||||
var lint = func(cfg Config) []Step {
|
|
||||||
steps := addRepos(cfg)
|
|
||||||
if cfg.UpdateDependencies {
|
|
||||||
steps = append(steps, depUpdate(cfg)...)
|
|
||||||
}
|
|
||||||
steps = append(steps, &run.Lint{
|
|
||||||
Chart: cfg.Chart,
|
|
||||||
Values: cfg.Values,
|
|
||||||
StringValues: cfg.StringValues,
|
|
||||||
ValuesFiles: cfg.ValuesFiles,
|
|
||||||
Strict: cfg.LintStrictly,
|
|
||||||
})
|
|
||||||
|
|
||||||
return steps
|
|
||||||
}
|
|
||||||
|
|
||||||
var help = func(cfg Config) []Step {
|
|
||||||
help := &run.Help{
|
|
||||||
HelmCommand: cfg.Command,
|
|
||||||
}
|
|
||||||
return []Step{help}
|
|
||||||
}
|
|
||||||
|
|
||||||
func initKube(cfg Config) []Step {
|
|
||||||
return []Step{
|
|
||||||
&run.InitKube{
|
|
||||||
SkipTLSVerify: cfg.SkipTLSVerify,
|
|
||||||
Certificate: cfg.Certificate,
|
|
||||||
APIServer: cfg.APIServer,
|
|
||||||
ServiceAccount: cfg.ServiceAccount,
|
|
||||||
Token: cfg.KubeToken,
|
|
||||||
TemplateFile: kubeConfigTemplate,
|
|
||||||
ConfigFile: kubeConfigFile,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func addRepos(cfg Config) []Step {
|
|
||||||
steps := make([]Step, 0)
|
|
||||||
for _, repo := range cfg.AddRepos {
|
for _, repo := range cfg.AddRepos {
|
||||||
steps = append(steps, &run.AddRepo{
|
steps = append(steps, run.NewAddRepo(cfg, repo))
|
||||||
Repo: repo,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
if cfg.UpdateDependencies {
|
||||||
|
steps = append(steps, run.NewDepUpdate(cfg))
|
||||||
|
}
|
||||||
|
steps = append(steps, run.NewUpgrade(cfg))
|
||||||
|
|
||||||
return steps
|
return steps
|
||||||
}
|
}
|
||||||
|
|
||||||
func depUpdate(cfg Config) []Step {
|
var uninstall = func(cfg env.Config) []Step {
|
||||||
return []Step{
|
var steps []Step
|
||||||
&run.DepUpdate{
|
steps = append(steps, run.NewInitKube(cfg, kubeConfigTemplate, kubeConfigFile))
|
||||||
Chart: cfg.Chart,
|
if cfg.UpdateDependencies {
|
||||||
},
|
steps = append(steps, run.NewDepUpdate(cfg))
|
||||||
}
|
}
|
||||||
|
steps = append(steps, run.NewUninstall(cfg))
|
||||||
|
|
||||||
|
return steps
|
||||||
|
}
|
||||||
|
|
||||||
|
var lint = func(cfg env.Config) []Step {
|
||||||
|
var steps []Step
|
||||||
|
for _, repo := range cfg.AddRepos {
|
||||||
|
steps = append(steps, run.NewAddRepo(cfg, repo))
|
||||||
|
}
|
||||||
|
if cfg.UpdateDependencies {
|
||||||
|
steps = append(steps, run.NewDepUpdate(cfg))
|
||||||
|
}
|
||||||
|
steps = append(steps, run.NewLint(cfg))
|
||||||
|
return steps
|
||||||
|
}
|
||||||
|
|
||||||
|
var help = func(cfg env.Config) []Step {
|
||||||
|
return []Step{run.NewHelp(cfg)}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/pelotech/drone-helm3/internal/env"
|
||||||
"github.com/pelotech/drone-helm3/internal/run"
|
"github.com/pelotech/drone-helm3/internal/run"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -25,14 +26,14 @@ func (suite *PlanTestSuite) TestNewPlan() {
|
|||||||
stepTwo := NewMockStep(ctrl)
|
stepTwo := NewMockStep(ctrl)
|
||||||
|
|
||||||
origHelp := help
|
origHelp := help
|
||||||
help = func(cfg Config) []Step {
|
help = func(cfg env.Config) []Step {
|
||||||
return []Step{stepOne, stepTwo}
|
return []Step{stepOne, stepTwo}
|
||||||
}
|
}
|
||||||
defer func() { help = origHelp }()
|
defer func() { help = origHelp }()
|
||||||
|
|
||||||
stdout := strings.Builder{}
|
stdout := strings.Builder{}
|
||||||
stderr := strings.Builder{}
|
stderr := strings.Builder{}
|
||||||
cfg := Config{
|
cfg := env.Config{
|
||||||
Command: "help",
|
Command: "help",
|
||||||
Debug: false,
|
Debug: false,
|
||||||
Namespace: "outer",
|
Namespace: "outer",
|
||||||
@@ -40,22 +41,14 @@ func (suite *PlanTestSuite) TestNewPlan() {
|
|||||||
Stderr: &stderr,
|
Stderr: &stderr,
|
||||||
}
|
}
|
||||||
|
|
||||||
runCfg := run.Config{
|
|
||||||
Debug: false,
|
|
||||||
Namespace: "outer",
|
|
||||||
Stdout: &stdout,
|
|
||||||
Stderr: &stderr,
|
|
||||||
}
|
|
||||||
|
|
||||||
stepOne.EXPECT().
|
stepOne.EXPECT().
|
||||||
Prepare(runCfg)
|
Prepare()
|
||||||
stepTwo.EXPECT().
|
stepTwo.EXPECT().
|
||||||
Prepare(runCfg)
|
Prepare()
|
||||||
|
|
||||||
plan, err := NewPlan(cfg)
|
plan, err := NewPlan(cfg)
|
||||||
suite.Require().Nil(err)
|
suite.Require().Nil(err)
|
||||||
suite.Equal(cfg, plan.cfg)
|
suite.Equal(cfg, plan.cfg)
|
||||||
suite.Equal(runCfg, plan.runCfg)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (suite *PlanTestSuite) TestNewPlanAbortsOnError() {
|
func (suite *PlanTestSuite) TestNewPlanAbortsOnError() {
|
||||||
@@ -65,17 +58,17 @@ func (suite *PlanTestSuite) TestNewPlanAbortsOnError() {
|
|||||||
stepTwo := NewMockStep(ctrl)
|
stepTwo := NewMockStep(ctrl)
|
||||||
|
|
||||||
origHelp := help
|
origHelp := help
|
||||||
help = func(cfg Config) []Step {
|
help = func(cfg env.Config) []Step {
|
||||||
return []Step{stepOne, stepTwo}
|
return []Step{stepOne, stepTwo}
|
||||||
}
|
}
|
||||||
defer func() { help = origHelp }()
|
defer func() { help = origHelp }()
|
||||||
|
|
||||||
cfg := Config{
|
cfg := env.Config{
|
||||||
Command: "help",
|
Command: "help",
|
||||||
}
|
}
|
||||||
|
|
||||||
stepOne.EXPECT().
|
stepOne.EXPECT().
|
||||||
Prepare(gomock.Any()).
|
Prepare().
|
||||||
Return(fmt.Errorf("I'm starry Dave, aye, cat blew that"))
|
Return(fmt.Errorf("I'm starry Dave, aye, cat blew that"))
|
||||||
|
|
||||||
_, err := NewPlan(cfg)
|
_, err := NewPlan(cfg)
|
||||||
@@ -89,18 +82,15 @@ func (suite *PlanTestSuite) TestExecute() {
|
|||||||
stepOne := NewMockStep(ctrl)
|
stepOne := NewMockStep(ctrl)
|
||||||
stepTwo := NewMockStep(ctrl)
|
stepTwo := NewMockStep(ctrl)
|
||||||
|
|
||||||
runCfg := run.Config{}
|
|
||||||
|
|
||||||
plan := Plan{
|
plan := Plan{
|
||||||
steps: []Step{stepOne, stepTwo},
|
steps: []Step{stepOne, stepTwo},
|
||||||
runCfg: runCfg,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
stepOne.EXPECT().
|
stepOne.EXPECT().
|
||||||
Execute(runCfg).
|
Execute().
|
||||||
Times(1)
|
Times(1)
|
||||||
stepTwo.EXPECT().
|
stepTwo.EXPECT().
|
||||||
Execute(runCfg).
|
Execute().
|
||||||
Times(1)
|
Times(1)
|
||||||
|
|
||||||
suite.NoError(plan.Execute())
|
suite.NoError(plan.Execute())
|
||||||
@@ -112,15 +102,12 @@ func (suite *PlanTestSuite) TestExecuteAbortsOnError() {
|
|||||||
stepOne := NewMockStep(ctrl)
|
stepOne := NewMockStep(ctrl)
|
||||||
stepTwo := NewMockStep(ctrl)
|
stepTwo := NewMockStep(ctrl)
|
||||||
|
|
||||||
runCfg := run.Config{}
|
|
||||||
|
|
||||||
plan := Plan{
|
plan := Plan{
|
||||||
steps: []Step{stepOne, stepTwo},
|
steps: []Step{stepOne, stepTwo},
|
||||||
runCfg: runCfg,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
stepOne.EXPECT().
|
stepOne.EXPECT().
|
||||||
Execute(runCfg).
|
Execute().
|
||||||
Times(1).
|
Times(1).
|
||||||
Return(fmt.Errorf("oh, he'll gnaw"))
|
Return(fmt.Errorf("oh, he'll gnaw"))
|
||||||
|
|
||||||
@@ -129,50 +116,14 @@ func (suite *PlanTestSuite) TestExecuteAbortsOnError() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (suite *PlanTestSuite) TestUpgrade() {
|
func (suite *PlanTestSuite) TestUpgrade() {
|
||||||
cfg := Config{
|
steps := upgrade(env.Config{})
|
||||||
ChartVersion: "seventeen",
|
|
||||||
DryRun: true,
|
|
||||||
Wait: true,
|
|
||||||
Values: "steadfastness,forthrightness",
|
|
||||||
StringValues: "tensile_strength,flexibility",
|
|
||||||
ValuesFiles: []string{"/root/price_inventory.yml"},
|
|
||||||
ReuseValues: true,
|
|
||||||
Timeout: "go sit in the corner",
|
|
||||||
Chart: "billboard_top_100",
|
|
||||||
Release: "post_malone_circles",
|
|
||||||
Force: true,
|
|
||||||
AtomicUpgrade: true,
|
|
||||||
CleanupOnFail: true,
|
|
||||||
}
|
|
||||||
|
|
||||||
steps := upgrade(cfg)
|
|
||||||
suite.Require().Equal(2, len(steps), "upgrade should return 2 steps")
|
suite.Require().Equal(2, len(steps), "upgrade should return 2 steps")
|
||||||
suite.Require().IsType(&run.InitKube{}, steps[0])
|
suite.IsType(&run.InitKube{}, steps[0])
|
||||||
|
suite.IsType(&run.Upgrade{}, steps[1])
|
||||||
suite.Require().IsType(&run.Upgrade{}, steps[1])
|
|
||||||
upgrade, _ := steps[1].(*run.Upgrade)
|
|
||||||
|
|
||||||
expected := &run.Upgrade{
|
|
||||||
Chart: cfg.Chart,
|
|
||||||
Release: cfg.Release,
|
|
||||||
ChartVersion: cfg.ChartVersion,
|
|
||||||
DryRun: true,
|
|
||||||
Wait: cfg.Wait,
|
|
||||||
Values: "steadfastness,forthrightness",
|
|
||||||
StringValues: "tensile_strength,flexibility",
|
|
||||||
ValuesFiles: []string{"/root/price_inventory.yml"},
|
|
||||||
ReuseValues: cfg.ReuseValues,
|
|
||||||
Timeout: cfg.Timeout,
|
|
||||||
Force: cfg.Force,
|
|
||||||
Atomic: true,
|
|
||||||
CleanupOnFail: true,
|
|
||||||
}
|
|
||||||
|
|
||||||
suite.Equal(expected, upgrade)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (suite *PlanTestSuite) TestUpgradeWithUpdateDependencies() {
|
func (suite *PlanTestSuite) TestUpgradeWithUpdateDependencies() {
|
||||||
cfg := Config{
|
cfg := env.Config{
|
||||||
UpdateDependencies: true,
|
UpdateDependencies: true,
|
||||||
}
|
}
|
||||||
steps := upgrade(cfg)
|
steps := upgrade(cfg)
|
||||||
@@ -182,7 +133,7 @@ func (suite *PlanTestSuite) TestUpgradeWithUpdateDependencies() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (suite *PlanTestSuite) TestUpgradeWithAddRepos() {
|
func (suite *PlanTestSuite) TestUpgradeWithAddRepos() {
|
||||||
cfg := Config{
|
cfg := env.Config{
|
||||||
AddRepos: []string{
|
AddRepos: []string{
|
||||||
"machine=https://github.com/harold_finch/themachine",
|
"machine=https://github.com/harold_finch/themachine",
|
||||||
},
|
},
|
||||||
@@ -193,47 +144,15 @@ func (suite *PlanTestSuite) TestUpgradeWithAddRepos() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (suite *PlanTestSuite) TestUninstall() {
|
func (suite *PlanTestSuite) TestUninstall() {
|
||||||
cfg := Config{
|
steps := uninstall(env.Config{})
|
||||||
KubeToken: "b2YgbXkgYWZmZWN0aW9u",
|
|
||||||
SkipTLSVerify: true,
|
|
||||||
Certificate: "cHJvY2xhaW1zIHdvbmRlcmZ1bCBmcmllbmRzaGlw",
|
|
||||||
APIServer: "98.765.43.21",
|
|
||||||
ServiceAccount: "greathelm",
|
|
||||||
DryRun: true,
|
|
||||||
Timeout: "think about what you did",
|
|
||||||
Release: "jetta_id_love_to_change_the_world",
|
|
||||||
KeepHistory: true,
|
|
||||||
}
|
|
||||||
|
|
||||||
steps := uninstall(cfg)
|
|
||||||
suite.Require().Equal(2, len(steps), "uninstall should return 2 steps")
|
suite.Require().Equal(2, len(steps), "uninstall should return 2 steps")
|
||||||
|
|
||||||
suite.Require().IsType(&run.InitKube{}, steps[0])
|
suite.IsType(&run.InitKube{}, steps[0])
|
||||||
init, _ := steps[0].(*run.InitKube)
|
suite.IsType(&run.Uninstall{}, steps[1])
|
||||||
var expected Step = &run.InitKube{
|
|
||||||
SkipTLSVerify: true,
|
|
||||||
Certificate: "cHJvY2xhaW1zIHdvbmRlcmZ1bCBmcmllbmRzaGlw",
|
|
||||||
APIServer: "98.765.43.21",
|
|
||||||
ServiceAccount: "greathelm",
|
|
||||||
Token: "b2YgbXkgYWZmZWN0aW9u",
|
|
||||||
TemplateFile: kubeConfigTemplate,
|
|
||||||
ConfigFile: kubeConfigFile,
|
|
||||||
}
|
|
||||||
|
|
||||||
suite.Equal(expected, init)
|
|
||||||
|
|
||||||
suite.Require().IsType(&run.Uninstall{}, steps[1])
|
|
||||||
actual, _ := steps[1].(*run.Uninstall)
|
|
||||||
expected = &run.Uninstall{
|
|
||||||
Release: "jetta_id_love_to_change_the_world",
|
|
||||||
DryRun: true,
|
|
||||||
KeepHistory: true,
|
|
||||||
}
|
|
||||||
suite.Equal(expected, actual)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (suite *PlanTestSuite) TestUninstallWithUpdateDependencies() {
|
func (suite *PlanTestSuite) TestUninstallWithUpdateDependencies() {
|
||||||
cfg := Config{
|
cfg := env.Config{
|
||||||
UpdateDependencies: true,
|
UpdateDependencies: true,
|
||||||
}
|
}
|
||||||
steps := uninstall(cfg)
|
steps := uninstall(cfg)
|
||||||
@@ -242,91 +161,14 @@ func (suite *PlanTestSuite) TestUninstallWithUpdateDependencies() {
|
|||||||
suite.IsType(&run.DepUpdate{}, steps[1])
|
suite.IsType(&run.DepUpdate{}, steps[1])
|
||||||
}
|
}
|
||||||
|
|
||||||
func (suite *PlanTestSuite) TestInitKube() {
|
|
||||||
cfg := Config{
|
|
||||||
KubeToken: "cXVlZXIgY2hhcmFjdGVyCg==",
|
|
||||||
SkipTLSVerify: true,
|
|
||||||
Certificate: "b2Ygd29rZW5lc3MK",
|
|
||||||
APIServer: "123.456.78.9",
|
|
||||||
ServiceAccount: "helmet",
|
|
||||||
}
|
|
||||||
|
|
||||||
steps := initKube(cfg)
|
|
||||||
suite.Require().Equal(1, len(steps), "initKube should return one step")
|
|
||||||
suite.Require().IsType(&run.InitKube{}, steps[0])
|
|
||||||
init, _ := steps[0].(*run.InitKube)
|
|
||||||
|
|
||||||
expected := &run.InitKube{
|
|
||||||
SkipTLSVerify: true,
|
|
||||||
Certificate: "b2Ygd29rZW5lc3MK",
|
|
||||||
APIServer: "123.456.78.9",
|
|
||||||
ServiceAccount: "helmet",
|
|
||||||
Token: "cXVlZXIgY2hhcmFjdGVyCg==",
|
|
||||||
TemplateFile: kubeConfigTemplate,
|
|
||||||
ConfigFile: kubeConfigFile,
|
|
||||||
}
|
|
||||||
suite.Equal(expected, init)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (suite *PlanTestSuite) TestDepUpdate() {
|
|
||||||
cfg := Config{
|
|
||||||
UpdateDependencies: true,
|
|
||||||
Chart: "scatterplot",
|
|
||||||
}
|
|
||||||
|
|
||||||
steps := depUpdate(cfg)
|
|
||||||
suite.Require().Equal(1, len(steps), "depUpdate should return one step")
|
|
||||||
suite.Require().IsType(&run.DepUpdate{}, steps[0])
|
|
||||||
update, _ := steps[0].(*run.DepUpdate)
|
|
||||||
|
|
||||||
expected := &run.DepUpdate{
|
|
||||||
Chart: "scatterplot",
|
|
||||||
}
|
|
||||||
suite.Equal(expected, update)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (suite *PlanTestSuite) TestAddRepos() {
|
|
||||||
cfg := Config{
|
|
||||||
AddRepos: []string{
|
|
||||||
"first=https://add.repos/one",
|
|
||||||
"second=https://add.repos/two",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
steps := addRepos(cfg)
|
|
||||||
suite.Require().Equal(2, len(steps), "addRepos should add one step per repo")
|
|
||||||
suite.Require().IsType(&run.AddRepo{}, steps[0])
|
|
||||||
suite.Require().IsType(&run.AddRepo{}, steps[1])
|
|
||||||
first := steps[0].(*run.AddRepo)
|
|
||||||
second := steps[1].(*run.AddRepo)
|
|
||||||
|
|
||||||
suite.Equal(first.Repo, "first=https://add.repos/one")
|
|
||||||
suite.Equal(second.Repo, "second=https://add.repos/two")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (suite *PlanTestSuite) TestLint() {
|
func (suite *PlanTestSuite) TestLint() {
|
||||||
cfg := Config{
|
steps := lint(env.Config{})
|
||||||
Chart: "./flow",
|
suite.Require().Equal(1, len(steps))
|
||||||
Values: "steadfastness,forthrightness",
|
suite.IsType(&run.Lint{}, steps[0])
|
||||||
StringValues: "tensile_strength,flexibility",
|
|
||||||
ValuesFiles: []string{"/root/price_inventory.yml"},
|
|
||||||
LintStrictly: true,
|
|
||||||
}
|
|
||||||
|
|
||||||
steps := lint(cfg)
|
|
||||||
suite.Equal(1, len(steps))
|
|
||||||
|
|
||||||
want := &run.Lint{
|
|
||||||
Chart: "./flow",
|
|
||||||
Values: "steadfastness,forthrightness",
|
|
||||||
StringValues: "tensile_strength,flexibility",
|
|
||||||
ValuesFiles: []string{"/root/price_inventory.yml"},
|
|
||||||
Strict: true,
|
|
||||||
}
|
|
||||||
suite.Equal(want, steps[0])
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (suite *PlanTestSuite) TestLintWithUpdateDependencies() {
|
func (suite *PlanTestSuite) TestLintWithUpdateDependencies() {
|
||||||
cfg := Config{
|
cfg := env.Config{
|
||||||
UpdateDependencies: true,
|
UpdateDependencies: true,
|
||||||
}
|
}
|
||||||
steps := lint(cfg)
|
steps := lint(cfg)
|
||||||
@@ -335,7 +177,7 @@ func (suite *PlanTestSuite) TestLintWithUpdateDependencies() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (suite *PlanTestSuite) TestLintWithAddRepos() {
|
func (suite *PlanTestSuite) TestLintWithAddRepos() {
|
||||||
cfg := Config{
|
cfg := env.Config{
|
||||||
AddRepos: []string{"friendczar=https://github.com/logan_pierce/friendczar"},
|
AddRepos: []string{"friendczar=https://github.com/logan_pierce/friendczar"},
|
||||||
}
|
}
|
||||||
steps := lint(cfg)
|
steps := lint(cfg)
|
||||||
@@ -344,7 +186,7 @@ func (suite *PlanTestSuite) TestLintWithAddRepos() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (suite *PlanTestSuite) TestDeterminePlanUpgradeCommand() {
|
func (suite *PlanTestSuite) TestDeterminePlanUpgradeCommand() {
|
||||||
cfg := Config{
|
cfg := env.Config{
|
||||||
Command: "upgrade",
|
Command: "upgrade",
|
||||||
}
|
}
|
||||||
stepsMaker := determineSteps(cfg)
|
stepsMaker := determineSteps(cfg)
|
||||||
@@ -352,7 +194,7 @@ func (suite *PlanTestSuite) TestDeterminePlanUpgradeCommand() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (suite *PlanTestSuite) TestDeterminePlanUpgradeFromDroneEvent() {
|
func (suite *PlanTestSuite) TestDeterminePlanUpgradeFromDroneEvent() {
|
||||||
cfg := Config{}
|
cfg := env.Config{}
|
||||||
|
|
||||||
upgradeEvents := []string{"push", "tag", "deployment", "pull_request", "promote", "rollback"}
|
upgradeEvents := []string{"push", "tag", "deployment", "pull_request", "promote", "rollback"}
|
||||||
for _, event := range upgradeEvents {
|
for _, event := range upgradeEvents {
|
||||||
@@ -363,7 +205,7 @@ func (suite *PlanTestSuite) TestDeterminePlanUpgradeFromDroneEvent() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (suite *PlanTestSuite) TestDeterminePlanUninstallCommand() {
|
func (suite *PlanTestSuite) TestDeterminePlanUninstallCommand() {
|
||||||
cfg := Config{
|
cfg := env.Config{
|
||||||
Command: "uninstall",
|
Command: "uninstall",
|
||||||
}
|
}
|
||||||
stepsMaker := determineSteps(cfg)
|
stepsMaker := determineSteps(cfg)
|
||||||
@@ -372,7 +214,7 @@ func (suite *PlanTestSuite) TestDeterminePlanUninstallCommand() {
|
|||||||
|
|
||||||
// helm_command = delete is provided as an alias for backward-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 := env.Config{
|
||||||
Command: "delete",
|
Command: "delete",
|
||||||
}
|
}
|
||||||
stepsMaker := determineSteps(cfg)
|
stepsMaker := determineSteps(cfg)
|
||||||
@@ -380,7 +222,7 @@ func (suite *PlanTestSuite) TestDeterminePlanDeleteCommand() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (suite *PlanTestSuite) TestDeterminePlanDeleteFromDroneEvent() {
|
func (suite *PlanTestSuite) TestDeterminePlanDeleteFromDroneEvent() {
|
||||||
cfg := Config{
|
cfg := env.Config{
|
||||||
DroneEvent: "delete",
|
DroneEvent: "delete",
|
||||||
}
|
}
|
||||||
stepsMaker := determineSteps(cfg)
|
stepsMaker := determineSteps(cfg)
|
||||||
@@ -388,7 +230,7 @@ func (suite *PlanTestSuite) TestDeterminePlanDeleteFromDroneEvent() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (suite *PlanTestSuite) TestDeterminePlanLintCommand() {
|
func (suite *PlanTestSuite) TestDeterminePlanLintCommand() {
|
||||||
cfg := Config{
|
cfg := env.Config{
|
||||||
Command: "lint",
|
Command: "lint",
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -397,7 +239,7 @@ func (suite *PlanTestSuite) TestDeterminePlanLintCommand() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (suite *PlanTestSuite) TestDeterminePlanHelpCommand() {
|
func (suite *PlanTestSuite) TestDeterminePlanHelpCommand() {
|
||||||
cfg := Config{
|
cfg := env.Config{
|
||||||
Command: "help",
|
Command: "help",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,50 +2,60 @@ package run
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/pelotech/drone-helm3/internal/env"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
// 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 {
|
||||||
Repo string
|
*config
|
||||||
|
repo string
|
||||||
|
certs *repoCerts
|
||||||
cmd cmd
|
cmd cmd
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewAddRepo creates an AddRepo for the given repo-spec. No validation is performed at this time.
|
||||||
|
func NewAddRepo(cfg env.Config, repo string) *AddRepo {
|
||||||
|
return &AddRepo{
|
||||||
|
config: newConfig(cfg),
|
||||||
|
repo: repo,
|
||||||
|
certs: newRepoCerts(cfg),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Execute executes the `helm repo add` command.
|
// Execute executes the `helm repo add` command.
|
||||||
func (a *AddRepo) Execute(_ Config) error {
|
func (a *AddRepo) Execute() error {
|
||||||
return a.cmd.Run()
|
return a.cmd.Run()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prepare gets the AddRepo ready to execute.
|
// Prepare gets the AddRepo ready to execute.
|
||||||
func (a *AddRepo) Prepare(cfg Config) error {
|
func (a *AddRepo) Prepare() error {
|
||||||
if a.Repo == "" {
|
if a.repo == "" {
|
||||||
return fmt.Errorf("repo is required")
|
return fmt.Errorf("repo is required")
|
||||||
}
|
}
|
||||||
split := strings.SplitN(a.Repo, "=", 2)
|
split := strings.SplitN(a.repo, "=", 2)
|
||||||
if len(split) != 2 {
|
if len(split) != 2 {
|
||||||
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 := make([]string, 0)
|
args := a.globalFlags()
|
||||||
|
args = append(args, "repo", "add")
|
||||||
if cfg.Namespace != "" {
|
args = append(args, a.certs.flags()...)
|
||||||
args = append(args, "--namespace", cfg.Namespace)
|
args = append(args, name, url)
|
||||||
}
|
|
||||||
if cfg.Debug {
|
|
||||||
args = append(args, "--debug")
|
|
||||||
}
|
|
||||||
|
|
||||||
args = append(args, "repo", "add", name, url)
|
|
||||||
|
|
||||||
a.cmd = command(helmBin, args...)
|
a.cmd = command(helmBin, args...)
|
||||||
a.cmd.Stdout(cfg.Stdout)
|
a.cmd.Stdout(a.stdout)
|
||||||
a.cmd.Stderr(cfg.Stderr)
|
a.cmd.Stderr(a.stderr)
|
||||||
|
|
||||||
if cfg.Debug {
|
if a.debug {
|
||||||
fmt.Fprintf(cfg.Stderr, "Generated command: '%s'\n", a.cmd.String())
|
fmt.Fprintf(a.stderr, "Generated command: '%s'\n", a.cmd.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
package run
|
package run
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"github.com/golang/mock/gomock"
|
"github.com/golang/mock/gomock"
|
||||||
|
"github.com/pelotech/drone-helm3/internal/env"
|
||||||
"github.com/stretchr/testify/suite"
|
"github.com/stretchr/testify/suite"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
@@ -38,16 +38,22 @@ func TestAddRepoTestSuite(t *testing.T) {
|
|||||||
suite.Run(t, new(AddRepoTestSuite))
|
suite.Run(t, new(AddRepoTestSuite))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (suite *AddRepoTestSuite) TestNewAddRepo() {
|
||||||
|
repo := NewAddRepo(env.Config{}, "picompress=https://github.com/caleb_phipps/picompress")
|
||||||
|
suite.Require().NotNil(repo)
|
||||||
|
suite.Equal("picompress=https://github.com/caleb_phipps/picompress", repo.repo)
|
||||||
|
suite.NotNil(repo.config)
|
||||||
|
suite.NotNil(repo.certs)
|
||||||
|
}
|
||||||
|
|
||||||
func (suite *AddRepoTestSuite) TestPrepareAndExecute() {
|
func (suite *AddRepoTestSuite) TestPrepareAndExecute() {
|
||||||
stdout := strings.Builder{}
|
stdout := strings.Builder{}
|
||||||
stderr := strings.Builder{}
|
stderr := strings.Builder{}
|
||||||
cfg := Config{
|
cfg := env.Config{
|
||||||
Stdout: &stdout,
|
Stdout: &stdout,
|
||||||
Stderr: &stderr,
|
Stderr: &stderr,
|
||||||
}
|
}
|
||||||
a := AddRepo{
|
a := NewAddRepo(cfg, "edeath=https://github.com/n_marks/e-death")
|
||||||
Repo: "edeath=https://github.com/n_marks/e-death",
|
|
||||||
}
|
|
||||||
|
|
||||||
suite.mockCmd.EXPECT().
|
suite.mockCmd.EXPECT().
|
||||||
Stdout(&stdout).
|
Stdout(&stdout).
|
||||||
@@ -56,7 +62,7 @@ func (suite *AddRepoTestSuite) TestPrepareAndExecute() {
|
|||||||
Stderr(&stderr).
|
Stderr(&stderr).
|
||||||
Times(1)
|
Times(1)
|
||||||
|
|
||||||
suite.Require().NoError(a.Prepare(cfg))
|
suite.Require().NoError(a.Prepare())
|
||||||
suite.Equal(helmBin, suite.commandPath)
|
suite.Equal(helmBin, suite.commandPath)
|
||||||
suite.Equal([]string{"repo", "add", "edeath", "https://github.com/n_marks/e-death"}, suite.commandArgs)
|
suite.Equal([]string{"repo", "add", "edeath", "https://github.com/n_marks/e-death"}, suite.commandArgs)
|
||||||
|
|
||||||
@@ -64,7 +70,7 @@ func (suite *AddRepoTestSuite) TestPrepareAndExecute() {
|
|||||||
Run().
|
Run().
|
||||||
Times(1)
|
Times(1)
|
||||||
|
|
||||||
suite.Require().NoError(a.Execute(cfg))
|
suite.Require().NoError(a.Execute())
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -72,70 +78,35 @@ func (suite *AddRepoTestSuite) TestPrepareRepoIsRequired() {
|
|||||||
// These aren't really expected, but allowing them gives clearer test-failure messages
|
// These aren't really expected, but allowing them gives clearer test-failure messages
|
||||||
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 := Config{}
|
a := NewAddRepo(env.Config{}, "")
|
||||||
a := AddRepo{}
|
|
||||||
|
|
||||||
err := a.Prepare(cfg)
|
err := a.Prepare()
|
||||||
suite.EqualError(err, "repo is required")
|
suite.EqualError(err, "repo is required")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (suite *AddRepoTestSuite) TestPrepareMalformedRepo() {
|
func (suite *AddRepoTestSuite) TestPrepareMalformedRepo() {
|
||||||
a := AddRepo{
|
a := NewAddRepo(env.Config{}, "dwim")
|
||||||
Repo: "dwim",
|
err := a.Prepare()
|
||||||
}
|
|
||||||
err := a.Prepare(Config{})
|
|
||||||
suite.EqualError(err, "bad repo spec 'dwim'")
|
suite.EqualError(err, "bad repo spec 'dwim'")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (suite *AddRepoTestSuite) TestPrepareWithEqualSignInURL() {
|
func (suite *AddRepoTestSuite) TestPrepareWithEqualSignInURL() {
|
||||||
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()
|
||||||
a := AddRepo{
|
a := NewAddRepo(env.Config{}, "samaritan=https://github.com/arthur_claypool/samaritan?version=2.1")
|
||||||
Repo: "samaritan=https://github.com/arthur_claypool/samaritan?version=2.1",
|
suite.NoError(a.Prepare())
|
||||||
}
|
|
||||||
suite.NoError(a.Prepare(Config{}))
|
|
||||||
suite.Contains(suite.commandArgs, "https://github.com/arthur_claypool/samaritan?version=2.1")
|
suite.Contains(suite.commandArgs, "https://github.com/arthur_claypool/samaritan?version=2.1")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (suite *AddRepoTestSuite) TestNamespaceFlag() {
|
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 := Config{
|
cfg := env.Config{}
|
||||||
Namespace: "alliteration",
|
a := NewAddRepo(cfg, "machine=https://github.com/harold_finch/themachine")
|
||||||
}
|
|
||||||
a := AddRepo{
|
|
||||||
Repo: "edeath=https://github.com/theater_guy/e-death",
|
|
||||||
}
|
|
||||||
|
|
||||||
suite.NoError(a.Prepare(cfg))
|
// inject a ca cert filename so repoCerts won't create any files that we'd have to clean up
|
||||||
suite.Equal(suite.commandPath, helmBin)
|
a.certs.caCertFilename = "./helm/reporepo.cert"
|
||||||
suite.Equal(suite.commandArgs, []string{"--namespace", "alliteration",
|
suite.NoError(a.Prepare())
|
||||||
"repo", "add", "edeath", "https://github.com/theater_guy/e-death"})
|
suite.Equal([]string{"repo", "add", "--ca-file", "./helm/reporepo.cert",
|
||||||
}
|
"machine", "https://github.com/harold_finch/themachine"}, suite.commandArgs)
|
||||||
|
|
||||||
func (suite *AddRepoTestSuite) TestDebugFlag() {
|
|
||||||
suite.mockCmd.EXPECT().Stdout(gomock.Any()).AnyTimes()
|
|
||||||
suite.mockCmd.EXPECT().Stderr(gomock.Any()).AnyTimes()
|
|
||||||
|
|
||||||
stderr := strings.Builder{}
|
|
||||||
|
|
||||||
command = func(path string, args ...string) cmd {
|
|
||||||
suite.mockCmd.EXPECT().
|
|
||||||
String().
|
|
||||||
Return(fmt.Sprintf("%s %s", path, strings.Join(args, " ")))
|
|
||||||
|
|
||||||
return suite.mockCmd
|
|
||||||
}
|
|
||||||
|
|
||||||
cfg := Config{
|
|
||||||
Debug: true,
|
|
||||||
Stderr: &stderr,
|
|
||||||
}
|
|
||||||
a := AddRepo{
|
|
||||||
Repo: "edeath=https://github.com/the_bug/e-death",
|
|
||||||
}
|
|
||||||
|
|
||||||
suite.Require().NoError(a.Prepare(cfg))
|
|
||||||
suite.Equal(fmt.Sprintf("Generated command: '%s --debug "+
|
|
||||||
"repo add edeath https://github.com/the_bug/e-death'\n", helmBin), stderr.String())
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,33 @@
|
|||||||
package run
|
package run
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/pelotech/drone-helm3/internal/env"
|
||||||
"io"
|
"io"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Config contains configuration applicable to all helm commands
|
type config struct {
|
||||||
type Config struct {
|
debug bool
|
||||||
Debug bool
|
namespace string
|
||||||
Namespace string
|
stdout io.Writer
|
||||||
Stdout io.Writer
|
stderr io.Writer
|
||||||
Stderr io.Writer
|
}
|
||||||
|
|
||||||
|
func newConfig(cfg env.Config) *config {
|
||||||
|
return &config{
|
||||||
|
debug: cfg.Debug,
|
||||||
|
namespace: cfg.Namespace,
|
||||||
|
stdout: cfg.Stdout,
|
||||||
|
stderr: cfg.Stderr,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cfg *config) globalFlags() []string {
|
||||||
|
flags := []string{}
|
||||||
|
if cfg.debug {
|
||||||
|
flags = append(flags, "--debug")
|
||||||
|
}
|
||||||
|
if cfg.namespace != "" {
|
||||||
|
flags = append(flags, "--namespace", cfg.namespace)
|
||||||
|
}
|
||||||
|
return flags
|
||||||
}
|
}
|
||||||
|
|||||||
48
internal/run/config_test.go
Normal file
48
internal/run/config_test.go
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
package run
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/pelotech/drone-helm3/internal/env"
|
||||||
|
"github.com/stretchr/testify/suite"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ConfigTestSuite struct {
|
||||||
|
suite.Suite
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConfigTestSuite(t *testing.T) {
|
||||||
|
suite.Run(t, new(ConfigTestSuite))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (suite *ConfigTestSuite) TestNewConfig() {
|
||||||
|
stdout := &strings.Builder{}
|
||||||
|
stderr := &strings.Builder{}
|
||||||
|
envCfg := env.Config{
|
||||||
|
Namespace: "private",
|
||||||
|
Debug: true,
|
||||||
|
Stdout: stdout,
|
||||||
|
Stderr: stderr,
|
||||||
|
}
|
||||||
|
cfg := newConfig(envCfg)
|
||||||
|
suite.Require().NotNil(cfg)
|
||||||
|
suite.Equal(&config{
|
||||||
|
namespace: "private",
|
||||||
|
debug: true,
|
||||||
|
stdout: stdout,
|
||||||
|
stderr: stderr,
|
||||||
|
}, cfg)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (suite *ConfigTestSuite) TestGlobalFlags() {
|
||||||
|
cfg := config{
|
||||||
|
debug: true,
|
||||||
|
namespace: "public",
|
||||||
|
}
|
||||||
|
flags := cfg.globalFlags()
|
||||||
|
suite.Equal([]string{"--debug", "--namespace", "public"}, flags)
|
||||||
|
|
||||||
|
cfg = config{}
|
||||||
|
flags = cfg.globalFlags()
|
||||||
|
suite.Equal([]string{}, flags)
|
||||||
|
}
|
||||||
@@ -2,42 +2,44 @@ package run
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/pelotech/drone-helm3/internal/env"
|
||||||
)
|
)
|
||||||
|
|
||||||
// DepUpdate is an execution step that calls `helm dependency update` when executed.
|
// DepUpdate is an execution step that calls `helm dependency update` when executed.
|
||||||
type DepUpdate struct {
|
type DepUpdate struct {
|
||||||
Chart string
|
*config
|
||||||
|
chart string
|
||||||
cmd cmd
|
cmd cmd
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewDepUpdate creates a DepUpdate using fields from the given Config. No validation is performed at this time.
|
||||||
|
func NewDepUpdate(cfg env.Config) *DepUpdate {
|
||||||
|
return &DepUpdate{
|
||||||
|
config: newConfig(cfg),
|
||||||
|
chart: cfg.Chart,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Execute executes the `helm upgrade` command.
|
// Execute executes the `helm upgrade` command.
|
||||||
func (d *DepUpdate) Execute(_ Config) error {
|
func (d *DepUpdate) Execute() error {
|
||||||
return d.cmd.Run()
|
return d.cmd.Run()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prepare gets the DepUpdate ready to execute.
|
// Prepare gets the DepUpdate ready to execute.
|
||||||
func (d *DepUpdate) Prepare(cfg Config) error {
|
func (d *DepUpdate) Prepare() error {
|
||||||
if d.Chart == "" {
|
if d.chart == "" {
|
||||||
return fmt.Errorf("chart is required")
|
return fmt.Errorf("chart is required")
|
||||||
}
|
}
|
||||||
|
|
||||||
args := make([]string, 0)
|
args := d.globalFlags()
|
||||||
|
args = append(args, "dependency", "update", d.chart)
|
||||||
if cfg.Namespace != "" {
|
|
||||||
args = append(args, "--namespace", cfg.Namespace)
|
|
||||||
}
|
|
||||||
if cfg.Debug {
|
|
||||||
args = append(args, "--debug")
|
|
||||||
}
|
|
||||||
|
|
||||||
args = append(args, "dependency", "update", d.Chart)
|
|
||||||
|
|
||||||
d.cmd = command(helmBin, args...)
|
d.cmd = command(helmBin, args...)
|
||||||
d.cmd.Stdout(cfg.Stdout)
|
d.cmd.Stdout(d.stdout)
|
||||||
d.cmd.Stderr(cfg.Stderr)
|
d.cmd.Stderr(d.stderr)
|
||||||
|
|
||||||
if cfg.Debug {
|
if d.debug {
|
||||||
fmt.Fprintf(cfg.Stderr, "Generated command: '%s'\n", d.cmd.String())
|
fmt.Fprintf(d.stderr, "Generated command: '%s'\n", d.cmd.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
package run
|
package run
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"github.com/golang/mock/gomock"
|
"github.com/golang/mock/gomock"
|
||||||
|
"github.com/pelotech/drone-helm3/internal/env"
|
||||||
"github.com/stretchr/testify/suite"
|
"github.com/stretchr/testify/suite"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
@@ -31,12 +31,21 @@ func TestDepUpdateTestSuite(t *testing.T) {
|
|||||||
suite.Run(t, new(DepUpdateTestSuite))
|
suite.Run(t, new(DepUpdateTestSuite))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (suite *DepUpdateTestSuite) TestNewDepUpdate() {
|
||||||
|
cfg := env.Config{
|
||||||
|
Chart: "scatterplot",
|
||||||
|
}
|
||||||
|
d := NewDepUpdate(cfg)
|
||||||
|
suite.Equal("scatterplot", d.chart)
|
||||||
|
}
|
||||||
|
|
||||||
func (suite *DepUpdateTestSuite) TestPrepareAndExecute() {
|
func (suite *DepUpdateTestSuite) TestPrepareAndExecute() {
|
||||||
defer suite.ctrl.Finish()
|
defer suite.ctrl.Finish()
|
||||||
|
|
||||||
stdout := strings.Builder{}
|
stdout := strings.Builder{}
|
||||||
stderr := strings.Builder{}
|
stderr := strings.Builder{}
|
||||||
cfg := Config{
|
cfg := env.Config{
|
||||||
|
Chart: "your_top_songs_2019",
|
||||||
Stdout: &stdout,
|
Stdout: &stdout,
|
||||||
Stderr: &stderr,
|
Stderr: &stderr,
|
||||||
}
|
}
|
||||||
@@ -55,74 +64,18 @@ func (suite *DepUpdateTestSuite) TestPrepareAndExecute() {
|
|||||||
Run().
|
Run().
|
||||||
Times(1)
|
Times(1)
|
||||||
|
|
||||||
d := DepUpdate{
|
d := NewDepUpdate(cfg)
|
||||||
Chart: "your_top_songs_2019",
|
|
||||||
}
|
|
||||||
|
|
||||||
suite.Require().NoError(d.Prepare(cfg))
|
suite.Require().NoError(d.Prepare())
|
||||||
suite.NoError(d.Execute(cfg))
|
suite.NoError(d.Execute())
|
||||||
}
|
|
||||||
|
|
||||||
func (suite *DepUpdateTestSuite) TestPrepareNamespaceFlag() {
|
|
||||||
defer suite.ctrl.Finish()
|
|
||||||
|
|
||||||
cfg := Config{
|
|
||||||
Namespace: "spotify",
|
|
||||||
}
|
|
||||||
|
|
||||||
command = func(path string, args ...string) cmd {
|
|
||||||
suite.Equal([]string{"--namespace", "spotify", "dependency", "update", "your_top_songs_2019"}, args)
|
|
||||||
|
|
||||||
return suite.mockCmd
|
|
||||||
}
|
|
||||||
suite.mockCmd.EXPECT().Stdout(gomock.Any()).AnyTimes()
|
|
||||||
suite.mockCmd.EXPECT().Stderr(gomock.Any()).AnyTimes()
|
|
||||||
|
|
||||||
d := DepUpdate{
|
|
||||||
Chart: "your_top_songs_2019",
|
|
||||||
}
|
|
||||||
|
|
||||||
suite.Require().NoError(d.Prepare(cfg))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (suite *DepUpdateTestSuite) TestPrepareDebugFlag() {
|
|
||||||
defer suite.ctrl.Finish()
|
|
||||||
|
|
||||||
stdout := strings.Builder{}
|
|
||||||
stderr := strings.Builder{}
|
|
||||||
cfg := Config{
|
|
||||||
Debug: true,
|
|
||||||
Stdout: &stdout,
|
|
||||||
Stderr: &stderr,
|
|
||||||
}
|
|
||||||
|
|
||||||
command = func(path string, args ...string) cmd {
|
|
||||||
suite.mockCmd.EXPECT().
|
|
||||||
String().
|
|
||||||
Return(fmt.Sprintf("%s %s", path, strings.Join(args, " ")))
|
|
||||||
|
|
||||||
return suite.mockCmd
|
|
||||||
}
|
|
||||||
suite.mockCmd.EXPECT().Stdout(gomock.Any()).AnyTimes()
|
|
||||||
suite.mockCmd.EXPECT().Stderr(gomock.Any()).AnyTimes()
|
|
||||||
|
|
||||||
d := DepUpdate{
|
|
||||||
Chart: "your_top_songs_2019",
|
|
||||||
}
|
|
||||||
|
|
||||||
suite.Require().NoError(d.Prepare(cfg))
|
|
||||||
|
|
||||||
want := fmt.Sprintf("Generated command: '%s --debug dependency update your_top_songs_2019'\n", helmBin)
|
|
||||||
suite.Equal(want, stderr.String())
|
|
||||||
suite.Equal("", stdout.String())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (suite *DepUpdateTestSuite) TestPrepareChartRequired() {
|
func (suite *DepUpdateTestSuite) TestPrepareChartRequired() {
|
||||||
d := DepUpdate{}
|
d := NewDepUpdate(env.Config{})
|
||||||
|
|
||||||
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()
|
||||||
|
|
||||||
err := d.Prepare(Config{})
|
err := d.Prepare()
|
||||||
suite.EqualError(err, "chart is required")
|
suite.EqualError(err, "chart is required")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,39 +2,47 @@ package run
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/pelotech/drone-helm3/internal/env"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Help is a step in a helm Plan that calls `helm help`.
|
// Help is a step in a helm Plan that calls `helm help`.
|
||||||
type Help struct {
|
type Help struct {
|
||||||
HelmCommand string
|
*config
|
||||||
|
helmCommand string
|
||||||
cmd cmd
|
cmd cmd
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewHelp creates a Help using fields from the given Config. No validation is performed at this time.
|
||||||
|
func NewHelp(cfg env.Config) *Help {
|
||||||
|
return &Help{
|
||||||
|
config: newConfig(cfg),
|
||||||
|
helmCommand: cfg.Command,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Execute executes the `helm help` command.
|
// Execute executes the `helm help` command.
|
||||||
func (h *Help) Execute(cfg Config) error {
|
func (h *Help) Execute() error {
|
||||||
if err := h.cmd.Run(); err != nil {
|
if err := h.cmd.Run(); err != nil {
|
||||||
return fmt.Errorf("while running '%s': %w", h.cmd.String(), err)
|
return fmt.Errorf("while running '%s': %w", h.cmd.String(), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if h.HelmCommand == "help" {
|
if h.helmCommand == "help" {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return fmt.Errorf("unknown command '%s'", h.HelmCommand)
|
return fmt.Errorf("unknown command '%s'", h.helmCommand)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prepare gets the Help ready to execute.
|
// Prepare gets the Help ready to execute.
|
||||||
func (h *Help) Prepare(cfg Config) error {
|
func (h *Help) Prepare() error {
|
||||||
args := []string{"help"}
|
args := h.globalFlags()
|
||||||
if cfg.Debug {
|
args = append(args, "help")
|
||||||
args = append([]string{"--debug"}, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
h.cmd = command(helmBin, args...)
|
h.cmd = command(helmBin, args...)
|
||||||
h.cmd.Stdout(cfg.Stdout)
|
h.cmd.Stdout(h.stdout)
|
||||||
h.cmd.Stderr(cfg.Stderr)
|
h.cmd.Stderr(h.stderr)
|
||||||
|
|
||||||
if cfg.Debug {
|
if h.debug {
|
||||||
fmt.Fprintf(cfg.Stderr, "Generated command: '%s'\n", h.cmd.String())
|
fmt.Fprintf(h.stderr, "Generated command: '%s'\n", h.cmd.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
package run
|
package run
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"github.com/golang/mock/gomock"
|
"github.com/golang/mock/gomock"
|
||||||
|
"github.com/pelotech/drone-helm3/internal/env"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/suite"
|
"github.com/stretchr/testify/suite"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -17,6 +17,15 @@ func TestHelpTestSuite(t *testing.T) {
|
|||||||
suite.Run(t, new(HelpTestSuite))
|
suite.Run(t, new(HelpTestSuite))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (suite *HelpTestSuite) TestNewHelp() {
|
||||||
|
cfg := env.Config{
|
||||||
|
Command: "everybody dance NOW!!",
|
||||||
|
}
|
||||||
|
help := NewHelp(cfg)
|
||||||
|
suite.Require().NotNil(help)
|
||||||
|
suite.Equal("everybody dance NOW!!", help.helmCommand)
|
||||||
|
}
|
||||||
|
|
||||||
func (suite *HelpTestSuite) TestPrepare() {
|
func (suite *HelpTestSuite) TestPrepare() {
|
||||||
ctrl := gomock.NewController(suite.T())
|
ctrl := gomock.NewController(suite.T())
|
||||||
defer ctrl.Finish()
|
defer ctrl.Finish()
|
||||||
@@ -39,13 +48,13 @@ func (suite *HelpTestSuite) TestPrepare() {
|
|||||||
mCmd.EXPECT().
|
mCmd.EXPECT().
|
||||||
Stderr(&stderr)
|
Stderr(&stderr)
|
||||||
|
|
||||||
cfg := Config{
|
cfg := env.Config{
|
||||||
Stdout: &stdout,
|
Stdout: &stdout,
|
||||||
Stderr: &stderr,
|
Stderr: &stderr,
|
||||||
}
|
}
|
||||||
|
|
||||||
h := Help{}
|
h := NewHelp(cfg)
|
||||||
err := h.Prepare(cfg)
|
err := h.Prepare()
|
||||||
suite.NoError(err)
|
suite.NoError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -53,41 +62,15 @@ func (suite *HelpTestSuite) TestExecute() {
|
|||||||
ctrl := gomock.NewController(suite.T())
|
ctrl := gomock.NewController(suite.T())
|
||||||
defer ctrl.Finish()
|
defer ctrl.Finish()
|
||||||
mCmd := NewMockcmd(ctrl)
|
mCmd := NewMockcmd(ctrl)
|
||||||
originalCommand := command
|
|
||||||
command = func(_ string, _ ...string) cmd {
|
|
||||||
return mCmd
|
|
||||||
}
|
|
||||||
defer func() { command = originalCommand }()
|
|
||||||
|
|
||||||
mCmd.EXPECT().
|
mCmd.EXPECT().
|
||||||
Run().
|
Run().
|
||||||
Times(2)
|
Times(2)
|
||||||
|
|
||||||
cfg := Config{}
|
help := NewHelp(env.Config{Command: "help"})
|
||||||
help := Help{
|
help.cmd = mCmd
|
||||||
HelmCommand: "help",
|
suite.NoError(help.Execute())
|
||||||
cmd: mCmd,
|
|
||||||
}
|
help.helmCommand = "get down on friday"
|
||||||
suite.NoError(help.Execute(cfg))
|
suite.EqualError(help.Execute(), "unknown command 'get down on friday'")
|
||||||
|
|
||||||
help.HelmCommand = "get down on friday"
|
|
||||||
suite.EqualError(help.Execute(cfg), "unknown command 'get down on friday'")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (suite *HelpTestSuite) TestPrepareDebugFlag() {
|
|
||||||
help := Help{}
|
|
||||||
|
|
||||||
stdout := strings.Builder{}
|
|
||||||
stderr := strings.Builder{}
|
|
||||||
cfg := Config{
|
|
||||||
Debug: true,
|
|
||||||
Stdout: &stdout,
|
|
||||||
Stderr: &stderr,
|
|
||||||
}
|
|
||||||
|
|
||||||
help.Prepare(cfg)
|
|
||||||
|
|
||||||
want := fmt.Sprintf("Generated command: '%s --debug help'\n", helmBin)
|
|
||||||
suite.Equal(want, stderr.String())
|
|
||||||
suite.Equal("", stdout.String())
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package run
|
|||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/pelotech/drone-helm3/internal/env"
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"text/template"
|
"text/template"
|
||||||
@@ -10,14 +11,9 @@ import (
|
|||||||
|
|
||||||
// InitKube is a step in a helm Plan that initializes the kubernetes config file.
|
// InitKube is a step in a helm Plan that initializes the kubernetes config file.
|
||||||
type InitKube struct {
|
type InitKube struct {
|
||||||
SkipTLSVerify bool
|
*config
|
||||||
Certificate string
|
templateFilename string
|
||||||
APIServer string
|
configFilename string
|
||||||
ServiceAccount string
|
|
||||||
Token string
|
|
||||||
TemplateFile string
|
|
||||||
ConfigFile string
|
|
||||||
|
|
||||||
template *template.Template
|
template *template.Template
|
||||||
configFile io.WriteCloser
|
configFile io.WriteCloser
|
||||||
values kubeValues
|
values kubeValues
|
||||||
@@ -32,58 +28,66 @@ type kubeValues struct {
|
|||||||
Token string
|
Token string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewInitKube creates a InitKube using the given Config and filepaths. No validation is performed at this time.
|
||||||
|
func NewInitKube(cfg env.Config, templateFile, configFile string) *InitKube {
|
||||||
|
return &InitKube{
|
||||||
|
config: newConfig(cfg),
|
||||||
|
values: kubeValues{
|
||||||
|
SkipTLSVerify: cfg.SkipTLSVerify,
|
||||||
|
Certificate: cfg.Certificate,
|
||||||
|
APIServer: cfg.APIServer,
|
||||||
|
Namespace: cfg.Namespace,
|
||||||
|
ServiceAccount: cfg.ServiceAccount,
|
||||||
|
Token: cfg.KubeToken,
|
||||||
|
},
|
||||||
|
templateFilename: templateFile,
|
||||||
|
configFilename: configFile,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Execute generates a kubernetes config file from drone-helm3's template.
|
// Execute generates a kubernetes config file from drone-helm3's template.
|
||||||
func (i *InitKube) Execute(cfg Config) error {
|
func (i *InitKube) Execute() error {
|
||||||
if cfg.Debug {
|
if i.debug {
|
||||||
fmt.Fprintf(cfg.Stderr, "writing kubeconfig file to %s\n", i.ConfigFile)
|
fmt.Fprintf(i.stderr, "writing kubeconfig file to %s\n", i.configFilename)
|
||||||
}
|
}
|
||||||
defer i.configFile.Close()
|
defer i.configFile.Close()
|
||||||
return i.template.Execute(i.configFile, i.values)
|
return i.template.Execute(i.configFile, i.values)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prepare ensures all required configuration is present and that the config file is writable.
|
// Prepare ensures all required configuration is present and that the config file is writable.
|
||||||
func (i *InitKube) Prepare(cfg Config) error {
|
func (i *InitKube) Prepare() error {
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
if i.APIServer == "" {
|
if i.values.APIServer == "" {
|
||||||
return errors.New("an API Server is needed to deploy")
|
return errors.New("an API Server is needed to deploy")
|
||||||
}
|
}
|
||||||
if i.Token == "" {
|
if i.values.Token == "" {
|
||||||
return errors.New("token is needed to deploy")
|
return errors.New("token is needed to deploy")
|
||||||
}
|
}
|
||||||
|
|
||||||
if i.ServiceAccount == "" {
|
if i.values.ServiceAccount == "" {
|
||||||
i.ServiceAccount = "helm"
|
i.values.ServiceAccount = "helm"
|
||||||
}
|
}
|
||||||
|
|
||||||
if cfg.Debug {
|
if i.debug {
|
||||||
fmt.Fprintf(cfg.Stderr, "loading kubeconfig template from %s\n", i.TemplateFile)
|
fmt.Fprintf(i.stderr, "loading kubeconfig template from %s\n", i.templateFilename)
|
||||||
}
|
}
|
||||||
i.template, err = template.ParseFiles(i.TemplateFile)
|
i.template, err = template.ParseFiles(i.templateFilename)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("could not load kubeconfig template: %w", err)
|
return fmt.Errorf("could not load kubeconfig template: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
i.values = kubeValues{
|
if i.debug {
|
||||||
SkipTLSVerify: i.SkipTLSVerify,
|
if _, err := os.Stat(i.configFilename); err != nil {
|
||||||
Certificate: i.Certificate,
|
|
||||||
APIServer: i.APIServer,
|
|
||||||
ServiceAccount: i.ServiceAccount,
|
|
||||||
Token: i.Token,
|
|
||||||
Namespace: cfg.Namespace,
|
|
||||||
}
|
|
||||||
|
|
||||||
if cfg.Debug {
|
|
||||||
if _, err := os.Stat(i.ConfigFile); err != nil {
|
|
||||||
// non-nil err here isn't an actual error state; the kubeconfig just doesn't exist
|
// non-nil err here isn't an actual error state; the kubeconfig just doesn't exist
|
||||||
fmt.Fprint(cfg.Stderr, "creating ")
|
fmt.Fprint(i.stderr, "creating ")
|
||||||
} else {
|
} else {
|
||||||
fmt.Fprint(cfg.Stderr, "truncating ")
|
fmt.Fprint(i.stderr, "truncating ")
|
||||||
}
|
}
|
||||||
fmt.Fprintf(cfg.Stderr, "kubeconfig file at %s\n", i.ConfigFile)
|
fmt.Fprintf(i.stderr, "kubeconfig file at %s\n", i.configFilename)
|
||||||
}
|
}
|
||||||
|
|
||||||
i.configFile, err = os.Create(i.ConfigFile)
|
i.configFile, err = os.Create(i.configFilename)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("could not open kubeconfig file for writing: %w", err)
|
return fmt.Errorf("could not open kubeconfig file for writing: %w", err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,13 @@
|
|||||||
package run
|
package run
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/pelotech/drone-helm3/internal/env"
|
||||||
"github.com/stretchr/testify/suite"
|
"github.com/stretchr/testify/suite"
|
||||||
yaml "gopkg.in/yaml.v2"
|
yaml "gopkg.in/yaml.v2"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
"text/template"
|
"text/template"
|
||||||
)
|
)
|
||||||
@@ -17,6 +20,30 @@ func TestInitKubeTestSuite(t *testing.T) {
|
|||||||
suite.Run(t, new(InitKubeTestSuite))
|
suite.Run(t, new(InitKubeTestSuite))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (suite *InitKubeTestSuite) TestNewInitKube() {
|
||||||
|
cfg := env.Config{
|
||||||
|
SkipTLSVerify: true,
|
||||||
|
Certificate: "cHJvY2xhaW1zIHdvbmRlcmZ1bCBmcmllbmRzaGlw",
|
||||||
|
APIServer: "98.765.43.21",
|
||||||
|
ServiceAccount: "greathelm",
|
||||||
|
KubeToken: "b2YgbXkgYWZmZWN0aW9u",
|
||||||
|
Stderr: &strings.Builder{},
|
||||||
|
Debug: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
init := NewInitKube(cfg, "conf.tpl", "conf.yml")
|
||||||
|
suite.Equal(kubeValues{
|
||||||
|
SkipTLSVerify: true,
|
||||||
|
Certificate: "cHJvY2xhaW1zIHdvbmRlcmZ1bCBmcmllbmRzaGlw",
|
||||||
|
APIServer: "98.765.43.21",
|
||||||
|
ServiceAccount: "greathelm",
|
||||||
|
Token: "b2YgbXkgYWZmZWN0aW9u",
|
||||||
|
}, init.values)
|
||||||
|
suite.Equal("conf.tpl", init.templateFilename)
|
||||||
|
suite.Equal("conf.yml", init.configFilename)
|
||||||
|
suite.NotNil(init.config)
|
||||||
|
}
|
||||||
|
|
||||||
func (suite *InitKubeTestSuite) TestPrepareExecute() {
|
func (suite *InitKubeTestSuite) TestPrepareExecute() {
|
||||||
templateFile, err := tempfile("kubeconfig********.yml.tpl", `
|
templateFile, err := tempfile("kubeconfig********.yml.tpl", `
|
||||||
certificate: {{ .Certificate }}
|
certificate: {{ .Certificate }}
|
||||||
@@ -29,23 +56,20 @@ namespace: {{ .Namespace }}
|
|||||||
defer os.Remove(configFile.Name())
|
defer os.Remove(configFile.Name())
|
||||||
suite.Require().Nil(err)
|
suite.Require().Nil(err)
|
||||||
|
|
||||||
init := InitKube{
|
cfg := env.Config{
|
||||||
APIServer: "Sysadmin",
|
APIServer: "Sysadmin",
|
||||||
Certificate: "CCNA",
|
Certificate: "CCNA",
|
||||||
Token: "Aspire virtual currency",
|
KubeToken: "Aspire virtual currency",
|
||||||
TemplateFile: templateFile.Name(),
|
|
||||||
ConfigFile: configFile.Name(),
|
|
||||||
}
|
|
||||||
cfg := Config{
|
|
||||||
Namespace: "Cisco",
|
Namespace: "Cisco",
|
||||||
}
|
}
|
||||||
err = init.Prepare(cfg)
|
init := NewInitKube(cfg, templateFile.Name(), configFile.Name())
|
||||||
|
err = init.Prepare()
|
||||||
suite.Require().Nil(err)
|
suite.Require().Nil(err)
|
||||||
|
|
||||||
suite.IsType(&template.Template{}, init.template)
|
suite.IsType(&template.Template{}, init.template)
|
||||||
suite.NotNil(init.configFile)
|
suite.NotNil(init.configFile)
|
||||||
|
|
||||||
err = init.Execute(cfg)
|
err = init.Execute()
|
||||||
suite.Require().Nil(err)
|
suite.Require().Nil(err)
|
||||||
|
|
||||||
conf, err := ioutil.ReadFile(configFile.Name())
|
conf, err := ioutil.ReadFile(configFile.Name())
|
||||||
@@ -63,19 +87,16 @@ func (suite *InitKubeTestSuite) TestExecuteGeneratesConfig() {
|
|||||||
defer os.Remove(configFile.Name())
|
defer os.Remove(configFile.Name())
|
||||||
suite.Require().NoError(err)
|
suite.Require().NoError(err)
|
||||||
|
|
||||||
cfg := Config{
|
cfg := env.Config{
|
||||||
Namespace: "marshmallow",
|
|
||||||
}
|
|
||||||
init := InitKube{
|
|
||||||
ConfigFile: configFile.Name(),
|
|
||||||
TemplateFile: "../../assets/kubeconfig.tpl", // the actual kubeconfig template
|
|
||||||
APIServer: "https://kube.cluster/peanut",
|
APIServer: "https://kube.cluster/peanut",
|
||||||
ServiceAccount: "chef",
|
ServiceAccount: "chef",
|
||||||
Token: "eWVhaCB3ZSB0b2tpbic=",
|
KubeToken: "eWVhaCB3ZSB0b2tpbic=",
|
||||||
Certificate: "d293LCB5b3UgYXJlIHNvIGNvb2wgZm9yIHNtb2tpbmcgd2VlZCDwn5mE",
|
Certificate: "d293LCB5b3UgYXJlIHNvIGNvb2wgZm9yIHNtb2tpbmcgd2VlZCDwn5mE",
|
||||||
|
Namespace: "marshmallow",
|
||||||
}
|
}
|
||||||
suite.Require().NoError(init.Prepare(cfg))
|
init := NewInitKube(cfg, "../../assets/kubeconfig.tpl", configFile.Name()) // the actual kubeconfig template
|
||||||
suite.Require().NoError(init.Execute(cfg))
|
suite.Require().NoError(init.Prepare())
|
||||||
|
suite.Require().NoError(init.Execute())
|
||||||
|
|
||||||
contents, err := ioutil.ReadFile(configFile.Name())
|
contents, err := ioutil.ReadFile(configFile.Name())
|
||||||
suite.Require().NoError(err)
|
suite.Require().NoError(err)
|
||||||
@@ -98,11 +119,11 @@ func (suite *InitKubeTestSuite) TestExecuteGeneratesConfig() {
|
|||||||
suite.NoError(yaml.UnmarshalStrict(contents, &conf))
|
suite.NoError(yaml.UnmarshalStrict(contents, &conf))
|
||||||
|
|
||||||
// test the other branch of the certificate/SkipTLSVerify conditional
|
// test the other branch of the certificate/SkipTLSVerify conditional
|
||||||
init.SkipTLSVerify = true
|
init.values.SkipTLSVerify = true
|
||||||
init.Certificate = ""
|
init.values.Certificate = ""
|
||||||
|
|
||||||
suite.Require().NoError(init.Prepare(cfg))
|
suite.Require().NoError(init.Prepare())
|
||||||
suite.Require().NoError(init.Execute(cfg))
|
suite.Require().NoError(init.Execute())
|
||||||
contents, err = ioutil.ReadFile(configFile.Name())
|
contents, err = ioutil.ReadFile(configFile.Name())
|
||||||
suite.Require().NoError(err)
|
suite.Require().NoError(err)
|
||||||
suite.Contains(string(contents), "insecure-skip-tls-verify: true")
|
suite.Contains(string(contents), "insecure-skip-tls-verify: true")
|
||||||
@@ -116,25 +137,25 @@ func (suite *InitKubeTestSuite) TestPrepareParseError() {
|
|||||||
defer os.Remove(templateFile.Name())
|
defer os.Remove(templateFile.Name())
|
||||||
suite.Require().Nil(err)
|
suite.Require().Nil(err)
|
||||||
|
|
||||||
init := InitKube{
|
cfg := env.Config{
|
||||||
APIServer: "Sysadmin",
|
APIServer: "Sysadmin",
|
||||||
Certificate: "CCNA",
|
Certificate: "CCNA",
|
||||||
Token: "Aspire virtual currency",
|
KubeToken: "Aspire virtual currency",
|
||||||
TemplateFile: templateFile.Name(),
|
|
||||||
}
|
}
|
||||||
err = init.Prepare(Config{})
|
init := NewInitKube(cfg, templateFile.Name(), "")
|
||||||
|
err = init.Prepare()
|
||||||
suite.Error(err)
|
suite.Error(err)
|
||||||
suite.Regexp("could not load kubeconfig .* function .* not defined", err)
|
suite.Regexp("could not load kubeconfig .* function .* not defined", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (suite *InitKubeTestSuite) TestPrepareNonexistentTemplateFile() {
|
func (suite *InitKubeTestSuite) TestPrepareNonexistentTemplateFile() {
|
||||||
init := InitKube{
|
cfg := env.Config{
|
||||||
APIServer: "Sysadmin",
|
APIServer: "Sysadmin",
|
||||||
Certificate: "CCNA",
|
Certificate: "CCNA",
|
||||||
Token: "Aspire virtual currency",
|
KubeToken: "Aspire virtual currency",
|
||||||
TemplateFile: "/usr/foreign/exclude/kubeprofig.tpl",
|
|
||||||
}
|
}
|
||||||
err := init.Prepare(Config{})
|
init := NewInitKube(cfg, "/usr/foreign/exclude/kubeprofig.tpl", "")
|
||||||
|
err := init.Prepare()
|
||||||
suite.Error(err)
|
suite.Error(err)
|
||||||
suite.Regexp("could not load kubeconfig .* no such file or directory", err)
|
suite.Regexp("could not load kubeconfig .* no such file or directory", err)
|
||||||
}
|
}
|
||||||
@@ -143,16 +164,14 @@ func (suite *InitKubeTestSuite) TestPrepareCannotOpenDestinationFile() {
|
|||||||
templateFile, err := tempfile("kubeconfig********.yml.tpl", "hurgity burgity")
|
templateFile, err := tempfile("kubeconfig********.yml.tpl", "hurgity burgity")
|
||||||
defer os.Remove(templateFile.Name())
|
defer os.Remove(templateFile.Name())
|
||||||
suite.Require().Nil(err)
|
suite.Require().Nil(err)
|
||||||
init := InitKube{
|
cfg := env.Config{
|
||||||
APIServer: "Sysadmin",
|
APIServer: "Sysadmin",
|
||||||
Certificate: "CCNA",
|
Certificate: "CCNA",
|
||||||
Token: "Aspire virtual currency",
|
KubeToken: "Aspire virtual currency",
|
||||||
TemplateFile: templateFile.Name(),
|
|
||||||
ConfigFile: "/usr/foreign/exclude/kubeprofig",
|
|
||||||
}
|
}
|
||||||
|
init := NewInitKube(cfg, templateFile.Name(), "/usr/foreign/exclude/kubeprofig")
|
||||||
|
|
||||||
cfg := Config{}
|
err = init.Prepare()
|
||||||
err = init.Prepare(cfg)
|
|
||||||
suite.Error(err)
|
suite.Error(err)
|
||||||
suite.Regexp("could not open .* for writing: .* no such file or directory", err)
|
suite.Regexp("could not open .* for writing: .* no such file or directory", err)
|
||||||
}
|
}
|
||||||
@@ -167,24 +186,21 @@ func (suite *InitKubeTestSuite) TestPrepareRequiredConfig() {
|
|||||||
suite.Require().Nil(err)
|
suite.Require().Nil(err)
|
||||||
|
|
||||||
// initial config with all required fields present
|
// initial config with all required fields present
|
||||||
init := InitKube{
|
cfg := env.Config{
|
||||||
APIServer: "Sysadmin",
|
APIServer: "Sysadmin",
|
||||||
Certificate: "CCNA",
|
Certificate: "CCNA",
|
||||||
Token: "Aspire virtual currency",
|
KubeToken: "Aspire virtual currency",
|
||||||
TemplateFile: templateFile.Name(),
|
|
||||||
ConfigFile: configFile.Name(),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
cfg := Config{}
|
init := NewInitKube(cfg, templateFile.Name(), configFile.Name())
|
||||||
|
suite.NoError(init.Prepare()) // consistency check; we should be starting in a happy state
|
||||||
|
|
||||||
suite.NoError(init.Prepare(cfg)) // consistency check; we should be starting in a happy state
|
init.values.APIServer = ""
|
||||||
|
suite.Error(init.Prepare(), "APIServer should be required.")
|
||||||
|
|
||||||
init.APIServer = ""
|
init.values.APIServer = "Sysadmin"
|
||||||
suite.Error(init.Prepare(cfg), "APIServer should be required.")
|
init.values.Token = ""
|
||||||
|
suite.Error(init.Prepare(), "Token should be required.")
|
||||||
init.APIServer = "Sysadmin"
|
|
||||||
init.Token = ""
|
|
||||||
suite.Error(init.Prepare(cfg), "Token should be required.")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (suite *InitKubeTestSuite) TestPrepareDefaultsServiceAccount() {
|
func (suite *InitKubeTestSuite) TestPrepareDefaultsServiceAccount() {
|
||||||
@@ -196,18 +212,43 @@ func (suite *InitKubeTestSuite) TestPrepareDefaultsServiceAccount() {
|
|||||||
defer os.Remove(configFile.Name())
|
defer os.Remove(configFile.Name())
|
||||||
suite.Require().Nil(err)
|
suite.Require().Nil(err)
|
||||||
|
|
||||||
init := InitKube{
|
cfg := env.Config{
|
||||||
APIServer: "Sysadmin",
|
APIServer: "Sysadmin",
|
||||||
Certificate: "CCNA",
|
Certificate: "CCNA",
|
||||||
Token: "Aspire virtual currency",
|
KubeToken: "Aspire virtual currency",
|
||||||
TemplateFile: templateFile.Name(),
|
}
|
||||||
ConfigFile: configFile.Name(),
|
init := NewInitKube(cfg, templateFile.Name(), configFile.Name())
|
||||||
|
|
||||||
|
init.Prepare()
|
||||||
|
suite.Equal("helm", init.values.ServiceAccount)
|
||||||
}
|
}
|
||||||
|
|
||||||
cfg := Config{}
|
func (suite *InitKubeTestSuite) TestDebugOutput() {
|
||||||
|
templateFile, err := tempfile("kubeconfig********.yml.tpl", "hurgity burgity")
|
||||||
|
defer os.Remove(templateFile.Name())
|
||||||
|
suite.Require().Nil(err)
|
||||||
|
|
||||||
init.Prepare(cfg)
|
configFile, err := tempfile("kubeconfig********.yml", "")
|
||||||
suite.Equal("helm", init.ServiceAccount)
|
defer os.Remove(configFile.Name())
|
||||||
|
suite.Require().Nil(err)
|
||||||
|
|
||||||
|
stdout := &strings.Builder{}
|
||||||
|
stderr := &strings.Builder{}
|
||||||
|
cfg := env.Config{
|
||||||
|
APIServer: "http://my.kube.server/",
|
||||||
|
KubeToken: "QSBzaW5nbGUgcm9zZQ==",
|
||||||
|
Debug: true,
|
||||||
|
Stdout: stdout,
|
||||||
|
Stderr: stderr,
|
||||||
|
}
|
||||||
|
init := NewInitKube(cfg, templateFile.Name(), configFile.Name())
|
||||||
|
suite.NoError(init.Prepare())
|
||||||
|
|
||||||
|
suite.Contains(stderr.String(), fmt.Sprintf("loading kubeconfig template from %s\n", templateFile.Name()))
|
||||||
|
suite.Contains(stderr.String(), fmt.Sprintf("truncating kubeconfig file at %s\n", configFile.Name()))
|
||||||
|
|
||||||
|
suite.NoError(init.Execute())
|
||||||
|
suite.Contains(stderr.String(), fmt.Sprintf("writing kubeconfig file to %s\n", configFile.Name()))
|
||||||
}
|
}
|
||||||
|
|
||||||
func tempfile(name, contents string) (*os.File, error) {
|
func tempfile(name, contents string) (*os.File, error) {
|
||||||
|
|||||||
@@ -2,61 +2,67 @@ package run
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/pelotech/drone-helm3/internal/env"
|
||||||
)
|
)
|
||||||
|
|
||||||
// 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
|
*config
|
||||||
Values string
|
chart string
|
||||||
StringValues string
|
values string
|
||||||
ValuesFiles []string
|
stringValues string
|
||||||
Strict bool
|
valuesFiles []string
|
||||||
|
strict bool
|
||||||
cmd cmd
|
cmd cmd
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewLint creates a Lint using fields from the given Config. No validation is performed at this time.
|
||||||
|
func NewLint(cfg env.Config) *Lint {
|
||||||
|
return &Lint{
|
||||||
|
config: newConfig(cfg),
|
||||||
|
chart: cfg.Chart,
|
||||||
|
values: cfg.Values,
|
||||||
|
stringValues: cfg.StringValues,
|
||||||
|
valuesFiles: cfg.ValuesFiles,
|
||||||
|
strict: cfg.LintStrictly,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Execute executes the `helm lint` command.
|
// Execute executes the `helm lint` command.
|
||||||
func (l *Lint) Execute(_ Config) error {
|
func (l *Lint) Execute() error {
|
||||||
return l.cmd.Run()
|
return l.cmd.Run()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prepare gets the Lint ready to execute.
|
// Prepare gets the Lint ready to execute.
|
||||||
func (l *Lint) Prepare(cfg Config) error {
|
func (l *Lint) Prepare() error {
|
||||||
if l.Chart == "" {
|
if l.chart == "" {
|
||||||
return fmt.Errorf("chart is required")
|
return fmt.Errorf("chart is required")
|
||||||
}
|
}
|
||||||
|
|
||||||
args := make([]string, 0)
|
args := l.globalFlags()
|
||||||
|
|
||||||
if cfg.Namespace != "" {
|
|
||||||
args = append(args, "--namespace", cfg.Namespace)
|
|
||||||
}
|
|
||||||
if cfg.Debug {
|
|
||||||
args = append(args, "--debug")
|
|
||||||
}
|
|
||||||
|
|
||||||
args = append(args, "lint")
|
args = append(args, "lint")
|
||||||
|
|
||||||
if l.Values != "" {
|
if l.values != "" {
|
||||||
args = append(args, "--set", l.Values)
|
args = append(args, "--set", l.values)
|
||||||
}
|
}
|
||||||
if l.StringValues != "" {
|
if l.stringValues != "" {
|
||||||
args = append(args, "--set-string", l.StringValues)
|
args = append(args, "--set-string", l.stringValues)
|
||||||
}
|
}
|
||||||
for _, vFile := range l.ValuesFiles {
|
for _, vFile := range l.valuesFiles {
|
||||||
args = append(args, "--values", vFile)
|
args = append(args, "--values", vFile)
|
||||||
}
|
}
|
||||||
if l.Strict {
|
if l.strict {
|
||||||
args = append(args, "--strict")
|
args = append(args, "--strict")
|
||||||
}
|
}
|
||||||
|
|
||||||
args = append(args, l.Chart)
|
args = append(args, l.chart)
|
||||||
|
|
||||||
l.cmd = command(helmBin, args...)
|
l.cmd = command(helmBin, args...)
|
||||||
l.cmd.Stdout(cfg.Stdout)
|
l.cmd.Stdout(l.stdout)
|
||||||
l.cmd.Stderr(cfg.Stderr)
|
l.cmd.Stderr(l.stderr)
|
||||||
|
|
||||||
if cfg.Debug {
|
if l.debug {
|
||||||
fmt.Fprintf(cfg.Stderr, "Generated command: '%s'\n", l.cmd.String())
|
fmt.Fprintf(l.stderr, "Generated command: '%s'\n", l.cmd.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
package run
|
package run
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"github.com/golang/mock/gomock"
|
"github.com/golang/mock/gomock"
|
||||||
|
"github.com/pelotech/drone-helm3/internal/env"
|
||||||
"github.com/stretchr/testify/suite"
|
"github.com/stretchr/testify/suite"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
@@ -31,19 +31,36 @@ func TestLintTestSuite(t *testing.T) {
|
|||||||
suite.Run(t, new(LintTestSuite))
|
suite.Run(t, new(LintTestSuite))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (suite *LintTestSuite) TestNewLint() {
|
||||||
|
cfg := env.Config{
|
||||||
|
Chart: "./flow",
|
||||||
|
Values: "steadfastness,forthrightness",
|
||||||
|
StringValues: "tensile_strength,flexibility",
|
||||||
|
ValuesFiles: []string{"/root/price_inventory.yml"},
|
||||||
|
LintStrictly: true,
|
||||||
|
}
|
||||||
|
lint := NewLint(cfg)
|
||||||
|
suite.Require().NotNil(lint)
|
||||||
|
suite.Equal("./flow", lint.chart)
|
||||||
|
suite.Equal("steadfastness,forthrightness", lint.values)
|
||||||
|
suite.Equal("tensile_strength,flexibility", lint.stringValues)
|
||||||
|
suite.Equal([]string{"/root/price_inventory.yml"}, lint.valuesFiles)
|
||||||
|
suite.Equal(true, lint.strict)
|
||||||
|
suite.NotNil(lint.config)
|
||||||
|
}
|
||||||
|
|
||||||
func (suite *LintTestSuite) TestPrepareAndExecute() {
|
func (suite *LintTestSuite) TestPrepareAndExecute() {
|
||||||
defer suite.ctrl.Finish()
|
defer suite.ctrl.Finish()
|
||||||
|
|
||||||
stdout := strings.Builder{}
|
stdout := strings.Builder{}
|
||||||
stderr := strings.Builder{}
|
stderr := strings.Builder{}
|
||||||
|
|
||||||
l := Lint{
|
cfg := env.Config{
|
||||||
Chart: "./epic/mychart",
|
Chart: "./epic/mychart",
|
||||||
}
|
|
||||||
cfg := Config{
|
|
||||||
Stdout: &stdout,
|
Stdout: &stdout,
|
||||||
Stderr: &stderr,
|
Stderr: &stderr,
|
||||||
}
|
}
|
||||||
|
l := NewLint(cfg)
|
||||||
|
|
||||||
command = func(path string, args ...string) cmd {
|
command = func(path string, args ...string) cmd {
|
||||||
suite.Equal(helmBin, path)
|
suite.Equal(helmBin, path)
|
||||||
@@ -52,6 +69,7 @@ func (suite *LintTestSuite) TestPrepareAndExecute() {
|
|||||||
return suite.mockCmd
|
return suite.mockCmd
|
||||||
}
|
}
|
||||||
|
|
||||||
|
suite.mockCmd.EXPECT().String().AnyTimes()
|
||||||
suite.mockCmd.EXPECT().
|
suite.mockCmd.EXPECT().
|
||||||
Stdout(&stdout)
|
Stdout(&stdout)
|
||||||
suite.mockCmd.EXPECT().
|
suite.mockCmd.EXPECT().
|
||||||
@@ -60,9 +78,9 @@ func (suite *LintTestSuite) TestPrepareAndExecute() {
|
|||||||
Run().
|
Run().
|
||||||
Times(1)
|
Times(1)
|
||||||
|
|
||||||
err := l.Prepare(cfg)
|
err := l.Prepare()
|
||||||
suite.Require().Nil(err)
|
suite.Require().Nil(err)
|
||||||
l.Execute(cfg)
|
l.Execute()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (suite *LintTestSuite) TestPrepareRequiresChart() {
|
func (suite *LintTestSuite) TestPrepareRequiresChart() {
|
||||||
@@ -70,25 +88,22 @@ func (suite *LintTestSuite) TestPrepareRequiresChart() {
|
|||||||
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 := Config{}
|
l := NewLint(env.Config{})
|
||||||
l := Lint{}
|
err := l.Prepare()
|
||||||
|
|
||||||
err := l.Prepare(cfg)
|
|
||||||
suite.EqualError(err, "chart is required", "Chart should be mandatory")
|
suite.EqualError(err, "chart is required", "Chart should be mandatory")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (suite *LintTestSuite) TestPrepareWithLintFlags() {
|
func (suite *LintTestSuite) TestPrepareWithLintFlags() {
|
||||||
defer suite.ctrl.Finish()
|
defer suite.ctrl.Finish()
|
||||||
|
|
||||||
cfg := Config{}
|
cfg := env.Config{
|
||||||
|
|
||||||
l := Lint{
|
|
||||||
Chart: "./uk/top_40",
|
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,
|
LintStrictly: true,
|
||||||
}
|
}
|
||||||
|
l := NewLint(cfg)
|
||||||
|
|
||||||
command = func(path string, args ...string) cmd {
|
command = func(path string, args ...string) cmd {
|
||||||
suite.Equal(helmBin, path)
|
suite.Equal(helmBin, path)
|
||||||
@@ -105,66 +120,8 @@ func (suite *LintTestSuite) TestPrepareWithLintFlags() {
|
|||||||
|
|
||||||
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()
|
||||||
|
suite.mockCmd.EXPECT().String().AnyTimes()
|
||||||
|
|
||||||
err := l.Prepare(cfg)
|
err := l.Prepare()
|
||||||
suite.Require().Nil(err)
|
suite.Require().Nil(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (suite *LintTestSuite) TestPrepareWithDebugFlag() {
|
|
||||||
defer suite.ctrl.Finish()
|
|
||||||
|
|
||||||
stderr := strings.Builder{}
|
|
||||||
|
|
||||||
cfg := Config{
|
|
||||||
Debug: true,
|
|
||||||
Stderr: &stderr,
|
|
||||||
}
|
|
||||||
|
|
||||||
l := Lint{
|
|
||||||
Chart: "./scotland/top_40",
|
|
||||||
}
|
|
||||||
|
|
||||||
command = func(path string, args ...string) cmd {
|
|
||||||
suite.mockCmd.EXPECT().
|
|
||||||
String().
|
|
||||||
Return(fmt.Sprintf("%s %s", path, strings.Join(args, " ")))
|
|
||||||
|
|
||||||
return suite.mockCmd
|
|
||||||
}
|
|
||||||
|
|
||||||
suite.mockCmd.EXPECT().Stdout(gomock.Any())
|
|
||||||
suite.mockCmd.EXPECT().Stderr(&stderr)
|
|
||||||
|
|
||||||
err := l.Prepare(cfg)
|
|
||||||
suite.Require().Nil(err)
|
|
||||||
|
|
||||||
want := fmt.Sprintf("Generated command: '%s --debug lint ./scotland/top_40'\n", helmBin)
|
|
||||||
suite.Equal(want, stderr.String())
|
|
||||||
}
|
|
||||||
|
|
||||||
func (suite *LintTestSuite) TestPrepareWithNamespaceFlag() {
|
|
||||||
defer suite.ctrl.Finish()
|
|
||||||
|
|
||||||
cfg := Config{
|
|
||||||
Namespace: "table-service",
|
|
||||||
}
|
|
||||||
|
|
||||||
l := Lint{
|
|
||||||
Chart: "./wales/top_40",
|
|
||||||
}
|
|
||||||
|
|
||||||
actual := []string{}
|
|
||||||
command = func(path string, args ...string) cmd {
|
|
||||||
actual = args
|
|
||||||
return suite.mockCmd
|
|
||||||
}
|
|
||||||
|
|
||||||
suite.mockCmd.EXPECT().Stdout(gomock.Any()).AnyTimes()
|
|
||||||
suite.mockCmd.EXPECT().Stderr(gomock.Any()).AnyTimes()
|
|
||||||
|
|
||||||
err := l.Prepare(cfg)
|
|
||||||
suite.Require().Nil(err)
|
|
||||||
|
|
||||||
expected := []string{"--namespace", "table-service", "lint", "./wales/top_40"}
|
|
||||||
suite.Equal(expected, actual)
|
|
||||||
}
|
|
||||||
|
|||||||
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))
|
||||||
|
}
|
||||||
@@ -2,53 +2,57 @@ package run
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/pelotech/drone-helm3/internal/env"
|
||||||
)
|
)
|
||||||
|
|
||||||
// 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
|
*config
|
||||||
DryRun bool
|
release string
|
||||||
KeepHistory bool
|
dryRun bool
|
||||||
|
keepHistory bool
|
||||||
cmd cmd
|
cmd cmd
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewUninstall creates an Uninstall using fields from the given Config. No validation is performed at this time.
|
||||||
|
func NewUninstall(cfg env.Config) *Uninstall {
|
||||||
|
return &Uninstall{
|
||||||
|
config: newConfig(cfg),
|
||||||
|
release: cfg.Release,
|
||||||
|
dryRun: cfg.DryRun,
|
||||||
|
keepHistory: cfg.KeepHistory,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Execute executes the `helm uninstall` command.
|
// Execute executes the `helm uninstall` command.
|
||||||
func (u *Uninstall) Execute(_ Config) error {
|
func (u *Uninstall) Execute() error {
|
||||||
return u.cmd.Run()
|
return u.cmd.Run()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prepare gets the Uninstall ready to execute.
|
// Prepare gets the Uninstall ready to execute.
|
||||||
func (u *Uninstall) Prepare(cfg Config) error {
|
func (u *Uninstall) Prepare() error {
|
||||||
if u.Release == "" {
|
if u.release == "" {
|
||||||
return fmt.Errorf("release is required")
|
return fmt.Errorf("release is required")
|
||||||
}
|
}
|
||||||
|
|
||||||
args := make([]string, 0)
|
args := u.globalFlags()
|
||||||
|
|
||||||
if cfg.Namespace != "" {
|
|
||||||
args = append(args, "--namespace", cfg.Namespace)
|
|
||||||
}
|
|
||||||
if cfg.Debug {
|
|
||||||
args = append(args, "--debug")
|
|
||||||
}
|
|
||||||
|
|
||||||
args = append(args, "uninstall")
|
args = append(args, "uninstall")
|
||||||
|
|
||||||
if u.DryRun {
|
if u.dryRun {
|
||||||
args = append(args, "--dry-run")
|
args = append(args, "--dry-run")
|
||||||
}
|
}
|
||||||
if u.KeepHistory {
|
if u.keepHistory {
|
||||||
args = append(args, "--keep-history")
|
args = append(args, "--keep-history")
|
||||||
}
|
}
|
||||||
|
|
||||||
args = append(args, u.Release)
|
args = append(args, u.release)
|
||||||
|
|
||||||
u.cmd = command(helmBin, args...)
|
u.cmd = command(helmBin, args...)
|
||||||
u.cmd.Stdout(cfg.Stdout)
|
u.cmd.Stdout(u.stdout)
|
||||||
u.cmd.Stderr(cfg.Stderr)
|
u.cmd.Stderr(u.stderr)
|
||||||
|
|
||||||
if cfg.Debug {
|
if u.debug {
|
||||||
fmt.Fprintf(cfg.Stderr, "Generated command: '%s'\n", u.cmd.String())
|
fmt.Fprintf(u.stderr, "Generated command: '%s'\n", u.cmd.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@@ -1,10 +1,9 @@
|
|||||||
package run
|
package run
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"github.com/golang/mock/gomock"
|
"github.com/golang/mock/gomock"
|
||||||
|
"github.com/pelotech/drone-helm3/internal/env"
|
||||||
"github.com/stretchr/testify/suite"
|
"github.com/stretchr/testify/suite"
|
||||||
"strings"
|
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -35,12 +34,26 @@ func TestUninstallTestSuite(t *testing.T) {
|
|||||||
suite.Run(t, new(UninstallTestSuite))
|
suite.Run(t, new(UninstallTestSuite))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (suite *UninstallTestSuite) TestNewUninstall() {
|
||||||
|
cfg := env.Config{
|
||||||
|
DryRun: true,
|
||||||
|
Release: "jetta_id_love_to_change_the_world",
|
||||||
|
KeepHistory: true,
|
||||||
|
}
|
||||||
|
u := NewUninstall(cfg)
|
||||||
|
suite.Equal("jetta_id_love_to_change_the_world", u.release)
|
||||||
|
suite.Equal(true, u.dryRun)
|
||||||
|
suite.Equal(true, u.keepHistory)
|
||||||
|
suite.NotNil(u.config)
|
||||||
|
}
|
||||||
|
|
||||||
func (suite *UninstallTestSuite) TestPrepareAndExecute() {
|
func (suite *UninstallTestSuite) TestPrepareAndExecute() {
|
||||||
defer suite.ctrl.Finish()
|
defer suite.ctrl.Finish()
|
||||||
|
|
||||||
u := Uninstall{
|
cfg := env.Config{
|
||||||
Release: "zayde_wølf_king",
|
Release: "zayde_wølf_king",
|
||||||
}
|
}
|
||||||
|
u := NewUninstall(cfg)
|
||||||
|
|
||||||
actual := []string{}
|
actual := []string{}
|
||||||
command = func(path string, args ...string) cmd {
|
command = func(path string, args ...string) cmd {
|
||||||
@@ -58,92 +71,49 @@ func (suite *UninstallTestSuite) TestPrepareAndExecute() {
|
|||||||
Run().
|
Run().
|
||||||
Times(1)
|
Times(1)
|
||||||
|
|
||||||
cfg := Config{}
|
suite.NoError(u.Prepare())
|
||||||
suite.NoError(u.Prepare(cfg))
|
|
||||||
expected := []string{"uninstall", "zayde_wølf_king"}
|
expected := []string{"uninstall", "zayde_wølf_king"}
|
||||||
suite.Equal(expected, actual)
|
suite.Equal(expected, actual)
|
||||||
|
|
||||||
u.Execute(cfg)
|
u.Execute()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (suite *UninstallTestSuite) TestPrepareDryRunFlag() {
|
func (suite *UninstallTestSuite) TestPrepareDryRunFlag() {
|
||||||
u := Uninstall{
|
cfg := env.Config{
|
||||||
Release: "firefox_ak_wildfire",
|
Release: "firefox_ak_wildfire",
|
||||||
DryRun: true,
|
DryRun: true,
|
||||||
}
|
}
|
||||||
cfg := Config{}
|
u := NewUninstall(cfg)
|
||||||
|
|
||||||
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()
|
||||||
|
|
||||||
suite.NoError(u.Prepare(cfg))
|
suite.NoError(u.Prepare())
|
||||||
expected := []string{"uninstall", "--dry-run", "firefox_ak_wildfire"}
|
expected := []string{"uninstall", "--dry-run", "firefox_ak_wildfire"}
|
||||||
suite.Equal(expected, suite.actualArgs)
|
suite.Equal(expected, suite.actualArgs)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (suite *UninstallTestSuite) TestPrepareKeepHistoryFlag() {
|
func (suite *UninstallTestSuite) TestPrepareKeepHistoryFlag() {
|
||||||
u := Uninstall{
|
cfg := env.Config{
|
||||||
Release: "perturbator_sentient",
|
Release: "perturbator_sentient",
|
||||||
KeepHistory: true,
|
KeepHistory: true,
|
||||||
}
|
}
|
||||||
cfg := Config{}
|
u := NewUninstall(cfg)
|
||||||
|
|
||||||
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()
|
||||||
|
|
||||||
suite.NoError(u.Prepare(cfg))
|
suite.NoError(u.Prepare())
|
||||||
expected := []string{"uninstall", "--keep-history", "perturbator_sentient"}
|
expected := []string{"uninstall", "--keep-history", "perturbator_sentient"}
|
||||||
suite.Equal(expected, suite.actualArgs)
|
suite.Equal(expected, suite.actualArgs)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (suite *UninstallTestSuite) TestPrepareNamespaceFlag() {
|
|
||||||
u := Uninstall{
|
|
||||||
Release: "carly_simon_run_away_with_me",
|
|
||||||
}
|
|
||||||
cfg := Config{
|
|
||||||
Namespace: "emotion",
|
|
||||||
}
|
|
||||||
|
|
||||||
suite.mockCmd.EXPECT().Stdout(gomock.Any()).AnyTimes()
|
|
||||||
suite.mockCmd.EXPECT().Stderr(gomock.Any()).AnyTimes()
|
|
||||||
|
|
||||||
suite.NoError(u.Prepare(cfg))
|
|
||||||
expected := []string{"--namespace", "emotion", "uninstall", "carly_simon_run_away_with_me"}
|
|
||||||
suite.Equal(expected, suite.actualArgs)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (suite *UninstallTestSuite) TestPrepareDebugFlag() {
|
|
||||||
u := Uninstall{
|
|
||||||
Release: "just_a_band_huff_and_puff",
|
|
||||||
}
|
|
||||||
stderr := strings.Builder{}
|
|
||||||
cfg := Config{
|
|
||||||
Debug: true,
|
|
||||||
Stderr: &stderr,
|
|
||||||
}
|
|
||||||
|
|
||||||
command = func(path string, args ...string) cmd {
|
|
||||||
suite.mockCmd.EXPECT().
|
|
||||||
String().
|
|
||||||
Return(fmt.Sprintf("%s %s", path, strings.Join(args, " ")))
|
|
||||||
|
|
||||||
return suite.mockCmd
|
|
||||||
}
|
|
||||||
|
|
||||||
suite.mockCmd.EXPECT().Stdout(gomock.Any()).AnyTimes()
|
|
||||||
suite.mockCmd.EXPECT().Stderr(&stderr).AnyTimes()
|
|
||||||
|
|
||||||
suite.NoError(u.Prepare(cfg))
|
|
||||||
suite.Equal(fmt.Sprintf("Generated command: '%s --debug "+
|
|
||||||
"uninstall just_a_band_huff_and_puff'\n", helmBin), stderr.String())
|
|
||||||
}
|
|
||||||
|
|
||||||
func (suite *UninstallTestSuite) TestPrepareRequiresRelease() {
|
func (suite *UninstallTestSuite) TestPrepareRequiresRelease() {
|
||||||
// These aren't really expected, but allowing them gives clearer test-failure messages
|
// These aren't really expected, but allowing them gives clearer test-failure messages
|
||||||
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()
|
||||||
|
|
||||||
u := Uninstall{}
|
u := NewUninstall(env.Config{})
|
||||||
err := u.Prepare(Config{})
|
err := u.Prepare()
|
||||||
suite.EqualError(err, "release is required", "Uninstall.Release should be mandatory")
|
suite.EqualError(err, "release is required", "Uninstall.Release should be mandatory")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,94 +2,111 @@ package run
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/pelotech/drone-helm3/internal/env"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Upgrade is an execution step that calls `helm upgrade` when executed.
|
// Upgrade is an execution step that calls `helm upgrade` when executed.
|
||||||
type Upgrade struct {
|
type Upgrade struct {
|
||||||
Chart string
|
*config
|
||||||
Release string
|
chart 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
|
||||||
|
certs *repoCerts
|
||||||
|
|
||||||
cmd cmd
|
cmd cmd
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewUpgrade creates an Upgrade using fields from the given Config. No validation is performed at this time.
|
||||||
|
func NewUpgrade(cfg env.Config) *Upgrade {
|
||||||
|
return &Upgrade{
|
||||||
|
config: newConfig(cfg),
|
||||||
|
chart: cfg.Chart,
|
||||||
|
release: cfg.Release,
|
||||||
|
chartVersion: cfg.ChartVersion,
|
||||||
|
dryRun: cfg.DryRun,
|
||||||
|
wait: cfg.Wait,
|
||||||
|
values: cfg.Values,
|
||||||
|
stringValues: cfg.StringValues,
|
||||||
|
valuesFiles: cfg.ValuesFiles,
|
||||||
|
reuseValues: cfg.ReuseValues,
|
||||||
|
timeout: cfg.Timeout,
|
||||||
|
force: cfg.Force,
|
||||||
|
atomic: cfg.AtomicUpgrade,
|
||||||
|
cleanupOnFail: cfg.CleanupOnFail,
|
||||||
|
certs: newRepoCerts(cfg),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Execute executes the `helm upgrade` command.
|
// Execute executes the `helm upgrade` command.
|
||||||
func (u *Upgrade) Execute(_ Config) error {
|
func (u *Upgrade) Execute() error {
|
||||||
return u.cmd.Run()
|
return u.cmd.Run()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prepare gets the Upgrade ready to execute.
|
// Prepare gets the Upgrade ready to execute.
|
||||||
func (u *Upgrade) Prepare(cfg Config) error {
|
func (u *Upgrade) Prepare() error {
|
||||||
if u.Chart == "" {
|
if u.chart == "" {
|
||||||
return fmt.Errorf("chart is required")
|
return fmt.Errorf("chart is required")
|
||||||
}
|
}
|
||||||
if u.Release == "" {
|
if u.release == "" {
|
||||||
return fmt.Errorf("release is required")
|
return fmt.Errorf("release is required")
|
||||||
}
|
}
|
||||||
|
|
||||||
args := make([]string, 0)
|
args := u.globalFlags()
|
||||||
|
|
||||||
if cfg.Namespace != "" {
|
|
||||||
args = append(args, "--namespace", cfg.Namespace)
|
|
||||||
}
|
|
||||||
if cfg.Debug {
|
|
||||||
args = append(args, "--debug")
|
|
||||||
}
|
|
||||||
|
|
||||||
args = append(args, "upgrade", "--install")
|
args = append(args, "upgrade", "--install")
|
||||||
|
|
||||||
if u.ChartVersion != "" {
|
if u.chartVersion != "" {
|
||||||
args = append(args, "--version", u.ChartVersion)
|
args = append(args, "--version", u.chartVersion)
|
||||||
}
|
}
|
||||||
if u.DryRun {
|
if u.dryRun {
|
||||||
args = append(args, "--dry-run")
|
args = append(args, "--dry-run")
|
||||||
}
|
}
|
||||||
if u.Wait {
|
if u.wait {
|
||||||
args = append(args, "--wait")
|
args = append(args, "--wait")
|
||||||
}
|
}
|
||||||
if u.ReuseValues {
|
if u.reuseValues {
|
||||||
args = append(args, "--reuse-values")
|
args = append(args, "--reuse-values")
|
||||||
}
|
}
|
||||||
if u.Timeout != "" {
|
if u.timeout != "" {
|
||||||
args = append(args, "--timeout", u.Timeout)
|
args = append(args, "--timeout", u.timeout)
|
||||||
}
|
}
|
||||||
if u.Force {
|
if u.force {
|
||||||
args = append(args, "--force")
|
args = append(args, "--force")
|
||||||
}
|
}
|
||||||
if u.Atomic {
|
if u.atomic {
|
||||||
args = append(args, "--atomic")
|
args = append(args, "--atomic")
|
||||||
}
|
}
|
||||||
if u.CleanupOnFail {
|
if u.cleanupOnFail {
|
||||||
args = append(args, "--cleanup-on-fail")
|
args = append(args, "--cleanup-on-fail")
|
||||||
}
|
}
|
||||||
if u.Values != "" {
|
if u.values != "" {
|
||||||
args = append(args, "--set", u.Values)
|
args = append(args, "--set", u.values)
|
||||||
}
|
}
|
||||||
if u.StringValues != "" {
|
if u.stringValues != "" {
|
||||||
args = append(args, "--set-string", u.StringValues)
|
args = append(args, "--set-string", u.stringValues)
|
||||||
}
|
}
|
||||||
for _, vFile := range u.ValuesFiles {
|
for _, vFile := range u.valuesFiles {
|
||||||
args = append(args, "--values", vFile)
|
args = append(args, "--values", vFile)
|
||||||
}
|
}
|
||||||
|
args = append(args, u.certs.flags()...)
|
||||||
|
|
||||||
args = append(args, u.Release, u.Chart)
|
args = append(args, u.release, u.chart)
|
||||||
u.cmd = command(helmBin, args...)
|
u.cmd = command(helmBin, args...)
|
||||||
u.cmd.Stdout(cfg.Stdout)
|
u.cmd.Stdout(u.stdout)
|
||||||
u.cmd.Stderr(cfg.Stderr)
|
u.cmd.Stderr(u.stderr)
|
||||||
|
|
||||||
if cfg.Debug {
|
if u.debug {
|
||||||
fmt.Fprintf(cfg.Stderr, "Generated command: '%s'\n", u.cmd.String())
|
fmt.Fprintf(u.stderr, "Generated command: '%s'\n", u.cmd.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package run
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/golang/mock/gomock"
|
"github.com/golang/mock/gomock"
|
||||||
|
"github.com/pelotech/drone-helm3/internal/env"
|
||||||
"github.com/stretchr/testify/suite"
|
"github.com/stretchr/testify/suite"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
@@ -31,13 +32,49 @@ func TestUpgradeTestSuite(t *testing.T) {
|
|||||||
suite.Run(t, new(UpgradeTestSuite))
|
suite.Run(t, new(UpgradeTestSuite))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (suite *UpgradeTestSuite) TestNewUpgrade() {
|
||||||
|
cfg := env.Config{
|
||||||
|
ChartVersion: "seventeen",
|
||||||
|
DryRun: true,
|
||||||
|
Wait: true,
|
||||||
|
Values: "steadfastness,forthrightness",
|
||||||
|
StringValues: "tensile_strength,flexibility",
|
||||||
|
ValuesFiles: []string{"/root/price_inventory.yml"},
|
||||||
|
ReuseValues: true,
|
||||||
|
Timeout: "go sit in the corner",
|
||||||
|
Chart: "billboard_top_100",
|
||||||
|
Release: "post_malone_circles",
|
||||||
|
Force: true,
|
||||||
|
AtomicUpgrade: true,
|
||||||
|
CleanupOnFail: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
up := NewUpgrade(cfg)
|
||||||
|
suite.Equal(cfg.Chart, up.chart)
|
||||||
|
suite.Equal(cfg.Release, up.release)
|
||||||
|
suite.Equal(cfg.ChartVersion, up.chartVersion)
|
||||||
|
suite.Equal(true, up.dryRun)
|
||||||
|
suite.Equal(cfg.Wait, up.wait)
|
||||||
|
suite.Equal("steadfastness,forthrightness", up.values)
|
||||||
|
suite.Equal("tensile_strength,flexibility", up.stringValues)
|
||||||
|
suite.Equal([]string{"/root/price_inventory.yml"}, up.valuesFiles)
|
||||||
|
suite.Equal(cfg.ReuseValues, up.reuseValues)
|
||||||
|
suite.Equal(cfg.Timeout, up.timeout)
|
||||||
|
suite.Equal(cfg.Force, up.force)
|
||||||
|
suite.Equal(true, up.atomic)
|
||||||
|
suite.Equal(true, up.cleanupOnFail)
|
||||||
|
suite.NotNil(up.config)
|
||||||
|
suite.NotNil(up.certs)
|
||||||
|
}
|
||||||
|
|
||||||
func (suite *UpgradeTestSuite) TestPrepareAndExecute() {
|
func (suite *UpgradeTestSuite) TestPrepareAndExecute() {
|
||||||
defer suite.ctrl.Finish()
|
defer suite.ctrl.Finish()
|
||||||
|
|
||||||
u := Upgrade{
|
cfg := env.Config{
|
||||||
Chart: "at40",
|
Chart: "at40",
|
||||||
Release: "jonas_brothers_only_human",
|
Release: "jonas_brothers_only_human",
|
||||||
}
|
}
|
||||||
|
u := NewUpgrade(cfg)
|
||||||
|
|
||||||
command = func(path string, args ...string) cmd {
|
command = func(path string, args ...string) cmd {
|
||||||
suite.Equal(helmBin, path)
|
suite.Equal(helmBin, path)
|
||||||
@@ -54,19 +91,20 @@ func (suite *UpgradeTestSuite) TestPrepareAndExecute() {
|
|||||||
Run().
|
Run().
|
||||||
Times(1)
|
Times(1)
|
||||||
|
|
||||||
cfg := Config{}
|
err := u.Prepare()
|
||||||
err := u.Prepare(cfg)
|
|
||||||
suite.Require().Nil(err)
|
suite.Require().Nil(err)
|
||||||
u.Execute(cfg)
|
u.Execute()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (suite *UpgradeTestSuite) TestPrepareNamespaceFlag() {
|
func (suite *UpgradeTestSuite) TestPrepareNamespaceFlag() {
|
||||||
defer suite.ctrl.Finish()
|
defer suite.ctrl.Finish()
|
||||||
|
|
||||||
u := Upgrade{
|
cfg := env.Config{
|
||||||
|
Namespace: "melt",
|
||||||
Chart: "at40",
|
Chart: "at40",
|
||||||
Release: "shaed_trampoline",
|
Release: "shaed_trampoline",
|
||||||
}
|
}
|
||||||
|
u := NewUpgrade(cfg)
|
||||||
|
|
||||||
command = func(path string, args ...string) cmd {
|
command = func(path string, args ...string) cmd {
|
||||||
suite.Equal(helmBin, path)
|
suite.Equal(helmBin, path)
|
||||||
@@ -78,17 +116,14 @@ func (suite *UpgradeTestSuite) TestPrepareNamespaceFlag() {
|
|||||||
suite.mockCmd.EXPECT().Stdout(gomock.Any())
|
suite.mockCmd.EXPECT().Stdout(gomock.Any())
|
||||||
suite.mockCmd.EXPECT().Stderr(gomock.Any())
|
suite.mockCmd.EXPECT().Stderr(gomock.Any())
|
||||||
|
|
||||||
cfg := Config{
|
err := u.Prepare()
|
||||||
Namespace: "melt",
|
|
||||||
}
|
|
||||||
err := u.Prepare(cfg)
|
|
||||||
suite.Require().Nil(err)
|
suite.Require().Nil(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (suite *UpgradeTestSuite) TestPrepareWithUpgradeFlags() {
|
func (suite *UpgradeTestSuite) TestPrepareWithUpgradeFlags() {
|
||||||
defer suite.ctrl.Finish()
|
defer suite.ctrl.Finish()
|
||||||
|
|
||||||
u := Upgrade{
|
cfg := env.Config{
|
||||||
Chart: "hot_ac",
|
Chart: "hot_ac",
|
||||||
Release: "maroon_5_memories",
|
Release: "maroon_5_memories",
|
||||||
ChartVersion: "radio_edit",
|
ChartVersion: "radio_edit",
|
||||||
@@ -100,11 +135,12 @@ func (suite *UpgradeTestSuite) TestPrepareWithUpgradeFlags() {
|
|||||||
ReuseValues: true,
|
ReuseValues: true,
|
||||||
Timeout: "sit_in_the_corner",
|
Timeout: "sit_in_the_corner",
|
||||||
Force: true,
|
Force: true,
|
||||||
Atomic: true,
|
AtomicUpgrade: true,
|
||||||
CleanupOnFail: true,
|
CleanupOnFail: true,
|
||||||
}
|
}
|
||||||
|
u := NewUpgrade(cfg)
|
||||||
cfg := Config{}
|
// 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)
|
||||||
@@ -121,6 +157,7 @@ func (suite *UpgradeTestSuite) TestPrepareWithUpgradeFlags() {
|
|||||||
"--set-string", "height=5ft10in",
|
"--set-string", "height=5ft10in",
|
||||||
"--values", "/usr/local/stats",
|
"--values", "/usr/local/stats",
|
||||||
"--values", "/usr/local/grades",
|
"--values", "/usr/local/grades",
|
||||||
|
"--ca-file", "local_ca.cert",
|
||||||
"maroon_5_memories", "hot_ac"}, args)
|
"maroon_5_memories", "hot_ac"}, args)
|
||||||
|
|
||||||
return suite.mockCmd
|
return suite.mockCmd
|
||||||
@@ -129,7 +166,7 @@ func (suite *UpgradeTestSuite) TestPrepareWithUpgradeFlags() {
|
|||||||
suite.mockCmd.EXPECT().Stdout(gomock.Any())
|
suite.mockCmd.EXPECT().Stdout(gomock.Any())
|
||||||
suite.mockCmd.EXPECT().Stderr(gomock.Any())
|
suite.mockCmd.EXPECT().Stderr(gomock.Any())
|
||||||
|
|
||||||
err := u.Prepare(cfg)
|
err := u.Prepare()
|
||||||
suite.Require().Nil(err)
|
suite.Require().Nil(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -138,34 +175,30 @@ func (suite *UpgradeTestSuite) TestRequiresChartAndRelease() {
|
|||||||
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()
|
||||||
|
|
||||||
u := Upgrade{
|
u := NewUpgrade(env.Config{})
|
||||||
Release: "seth_everman_unskippable_cutscene",
|
u.release = "seth_everman_unskippable_cutscene"
|
||||||
}
|
|
||||||
|
|
||||||
err := u.Prepare(Config{})
|
err := u.Prepare()
|
||||||
suite.EqualError(err, "chart is required", "Chart should be mandatory")
|
suite.EqualError(err, "chart is required", "Chart should be mandatory")
|
||||||
|
|
||||||
u = Upgrade{
|
u.release = ""
|
||||||
Chart: "billboard_top_zero",
|
u.chart = "billboard_top_zero"
|
||||||
}
|
|
||||||
|
|
||||||
err = u.Prepare(Config{})
|
err = u.Prepare()
|
||||||
suite.EqualError(err, "release is required", "Release should be mandatory")
|
suite.EqualError(err, "release is required", "Release should be mandatory")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (suite *UpgradeTestSuite) TestPrepareDebugFlag() {
|
func (suite *UpgradeTestSuite) TestPrepareDebugFlag() {
|
||||||
u := Upgrade{
|
|
||||||
Chart: "at40",
|
|
||||||
Release: "lewis_capaldi_someone_you_loved",
|
|
||||||
}
|
|
||||||
|
|
||||||
stdout := strings.Builder{}
|
stdout := strings.Builder{}
|
||||||
stderr := strings.Builder{}
|
stderr := strings.Builder{}
|
||||||
cfg := Config{
|
cfg := env.Config{
|
||||||
|
Chart: "at40",
|
||||||
|
Release: "lewis_capaldi_someone_you_loved",
|
||||||
Debug: true,
|
Debug: true,
|
||||||
Stdout: &stdout,
|
Stdout: &stdout,
|
||||||
Stderr: &stderr,
|
Stderr: &stderr,
|
||||||
}
|
}
|
||||||
|
u := NewUpgrade(cfg)
|
||||||
|
|
||||||
command = func(path string, args ...string) cmd {
|
command = func(path string, args ...string) cmd {
|
||||||
suite.mockCmd.EXPECT().
|
suite.mockCmd.EXPECT().
|
||||||
@@ -180,7 +213,7 @@ func (suite *UpgradeTestSuite) TestPrepareDebugFlag() {
|
|||||||
suite.mockCmd.EXPECT().
|
suite.mockCmd.EXPECT().
|
||||||
Stderr(&stderr)
|
Stderr(&stderr)
|
||||||
|
|
||||||
u.Prepare(cfg)
|
u.Prepare()
|
||||||
|
|
||||||
want := fmt.Sprintf("Generated command: '%s --debug upgrade "+
|
want := fmt.Sprintf("Generated command: '%s --debug upgrade "+
|
||||||
"--install lewis_capaldi_someone_you_loved at40'\n", helmBin)
|
"--install lewis_capaldi_someone_you_loved at40'\n", helmBin)
|
||||||
|
|||||||
Reference in New Issue
Block a user