26 Commits

Author SHA1 Message Date
Joachim Hill-Grannec
fed0a769f2 Update container maintainer 2022-08-22 09:58:59 -06:00
Joachim Hill-Grannec
48473bd9d8 Merge pull request #110 from mongodb-forks/contribute_historymax
Add `history_max` setting
2022-08-22 09:54:03 -06:00
Colin Hoglund
64045fadfb add comment 2022-08-16 11:44:57 -07:00
Colin Hoglund
c5a7551da8 unexport 2022-08-16 11:43:37 -07:00
Colin Hoglund
eeccfdd143 fix test 2022-08-16 11:42:02 -07:00
Colin Hoglund
8d450bbf7d DEVOPS-2496 add max history setting (#12)
* add history_max setting

* update docs

* use suite for env test
2022-08-16 11:37:13 -07:00
Joachim Hill-Grannec
a566ea0cf7 Merge pull request #108 from hobbypunk90/master
Update to helm 3.8.1
2022-07-08 00:48:40 -06:00
Marcel Hoppe
24f10ce1b9 Update to helm 3.8.1 2022-04-04 11:51:54 +02:00
Joachim Hill-Grannec
7c033a8b0a Merge pull request #104 from georgekaz/add-skip-crds
add skip-crds flag support
2021-07-21 08:10:35 -06:00
George Kaz
e482a144c1 add skip-crds flag support
Signed-off-by: George Kaz <egeorgekaz@gmail.com>
2021-07-20 19:26:00 +01:00
Joachim Hill-Grannec
8dba329407 Merge pull request #101 from jeessy2/master
fix: Add a new parameter "create_namespace"
2020-08-28 12:09:17 -07:00
Joachim Hill-Grannec
4462307983 Merge branch 'master' into master 2020-08-26 10:15:24 -07:00
Erin Call
cfc59a46ef Merge pull request #102 from minhdanh/skip-kube-init
Support skipping kubeconfig creation
2020-08-25 22:20:05 -07:00
minhdanh
5b4e3ab2ea Rename kube_init_skip to skip_kubeconfig 2020-08-25 09:59:06 +07:00
minhdanh
0e58dde591 Support skipping kubeconfig creation 2020-08-24 13:03:36 +07:00
jie
1809d7b133 upgrade helm to 3.2.4, kubernetes needs 1.8+ 2020-08-19 13:36:30 +08:00
jie
70b2a2d0b4 Add a parameter "create_namespace" 2020-08-19 11:11:11 +08:00
Joachim Hill-Grannec
03b066bfd7 Merge pull request #94 from pelotech/no-binary
Remove the built binary
2020-05-22 17:24:15 -07:00
Erin Call
fe0c874815 Merge branch 'master' into no-binary 2020-05-22 17:17:18 -07:00
Erin Call
91d4506b37 Ignore build/* [#93]
I definitely thought this rule was already in effect--I had a
`build/.gitignore` that contained just `*`. Unfortunately, that makes
git ignore the .gitignore file itself, so it was never added to the
repo...
2020-05-22 17:15:24 -07:00
Joachim Hill-Grannec
0ab0a7fee4 Merge pull request #96 from pelotech/explain-setting-aliases
Document the purpose of settingAliases
2020-05-22 17:04:28 -07:00
Erin Call
0b17d929a7 Document the purpose of settingAliases [#95] 2020-05-22 15:35:43 -07:00
Erin Call
3fea592040 Remove the built binary [#93]
It was never supposed to be part of the repo, and currently accounts for
at least half of the .git directory's size. We need to nip it in the bud
before `git clone` times become unmanageable!
2020-05-22 14:43:33 -07:00
Joachim Hill-Grannec
d7a9eccb13 Merge pull request #86 from obukhov/add-dependecies-action
Add dependecies action
2020-04-07 14:06:48 -06:00
Alex Obukhov
9e3a424a36 Add configuration option to docs 2020-04-05 14:30:44 +02:00
Alex Obukhov
774bbb74db Add dependencies_action configuration option 2020-04-05 14:22:46 +02:00
12 changed files with 411 additions and 99 deletions

3
.gitignore vendored
View File

@@ -4,6 +4,7 @@
*.dll
*.so
*.dylib
*.swp
.idea
@@ -17,3 +18,5 @@
# vendor/
.env
.secrets
build/*

View File

@@ -1,5 +1,5 @@
FROM alpine/helm:3.1.1
MAINTAINER Erin Call <erin@liffft.com>
FROM alpine/helm:3.8.1
MAINTAINER Joachim Hill-Grannec <joachim@pelo.tech>
COPY build/drone-helm /bin/drone-helm
COPY assets/kubeconfig.tpl /root/.kube/config.tpl

View File

@@ -31,22 +31,27 @@ Installations are triggered when the `mode` setting is "upgrade." They can also
|------------------------|----------------|----------|------------------------|---------|
| chart | string | yes | | The chart to use for this installation. |
| release | string | yes | | The release name for helm to use. |
| kube_api_server | string | yes | api_server | API endpoint for the Kubernetes cluster. |
| kube_token | string | yes | kubernetes_token | Token for authenticating to Kubernetes. |
| kube_service_account | string | | service_account | Service account for authenticating to Kubernetes. Default is `helm`. |
| kube_certificate | string | | kubernetes_certificate | Base64 encoded TLS certificate used by the Kubernetes cluster's certificate authority. |
| skip_kubeconfig | boolean | | | Whether to skip kubeconfig file creation. |
| kube_api_server | string | yes | api_server | API endpoint for the Kubernetes cluster. This is ignored if `skip_kubeconfig` is `true`. |
| kube_token | string | yes | kubernetes_token | Token for authenticating to Kubernetes. This is ignored if `skip_kubeconfig` is `true`. |
| kube_service_account | string | | service_account | Service account for authenticating to Kubernetes. Default is `helm`. This is ignored if `skip_kubeconfig` is `true`. |
| kube_certificate | string | | kubernetes_certificate | Base64 encoded TLS certificate used by the Kubernetes cluster's certificate authority. This is ignored if `skip_kubeconfig` is `true`. |
| chart_version | string | | | Specific chart version to install. |
| dry_run | boolean | | | Pass `--dry-run` to `helm upgrade`. |
| dependencies_action | string | | | Calls `helm dependency build` OR `helm dependency update` before running the main command. Possible values: `build`, `update`. |
| wait_for_upgrade | boolean | | wait | Wait until kubernetes resources are in a ready state before marking the installation successful. |
| timeout | duration | | | Timeout for any *individual* Kubernetes operation. The installation's full runtime may exceed this duration. |
| force_upgrade | boolean | | force | Pass `--force` to `helm upgrade`. |
| atomic_upgrade | boolean | | | Pass `--atomic` to `helm upgrade`. |
| cleanup_failed_upgrade | boolean | | | Pass `--cleanup-on-fail` to `helm upgrade`. |
| history_max | int | | | Pass `--history-max` 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`. |
| values_files | list\<string\> | | | Values to use as `--values` arguments to `helm upgrade`. |
| reuse_values | boolean | | | Reuse the values from a previous release. |
| skip_tls_verify | boolean | | | Connect to the Kubernetes cluster without checking for a valid TLS certificate. Not recommended in production. |
| skip_tls_verify | boolean | | | Connect to the Kubernetes cluster without checking for a valid TLS certificate. Not recommended in production. This is ignored if `skip_kubeconfig` is `true`. |
| create_namespace | boolean | | | Pass --create-namespace to `helm upgrade`. |
| skip_crds | boolean | | | Pass --skip-crds to `helm upgrade`. |
## Uninstallation
@@ -55,14 +60,15 @@ Uninstallations are triggered when the `mode` setting is "uninstall" or "delete.
| Param name | Type | Required | Alias | Purpose |
|------------------------|----------|----------|------------------------|---------|
| release | string | yes | | The release name for helm to use. |
| kube_api_server | string | yes | api_server | API endpoint for the Kubernetes cluster. |
| kube_token | string | yes | kubernetes_token | Token for authenticating to Kubernetes. |
| kube_service_account | string | | service_account | Service account for authenticating to Kubernetes. Default is `helm`. |
| kube_certificate | string | | kubernetes_certificate | Base64 encoded TLS certificate used by the Kubernetes cluster's certificate authority. |
| skip_kubeconfig | boolean | | | Whether to skip kubeconfig file creation. |
| kube_api_server | string | yes | api_server | API endpoint for the Kubernetes cluster. This is ignored if `skip_kubeconfig` is `true`. |
| kube_token | string | yes | kubernetes_token | Token for authenticating to Kubernetes. This is ignored if `skip_kubeconfig` is `true`. |
| kube_service_account | string | | service_account | Service account for authenticating to Kubernetes. Default is `helm`. This is ignored if `skip_kubeconfig` is `true`. |
| kube_certificate | string | | kubernetes_certificate | Base64 encoded TLS certificate used by the Kubernetes cluster's certificate authority. This is ignored if `skip_kubeconfig` is `true`. |
| keep_history | boolean | | | Pass `--keep-history` to `helm uninstall`, to retain the release history. |
| 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. |
| skip_tls_verify | boolean | | | Connect to the Kubernetes cluster without checking for a valid TLS certificate. Not recommended in production. |
| skip_tls_verify | boolean | | | Connect to the Kubernetes cluster without checking for a valid TLS certificate. Not recommended in production. This is ignored if `skip_kubeconfig` is `true`. |
| chart | string | | | Required when the global `update_dependencies` parameter is true. No effect otherwise. |
### Where to put settings

View File

@@ -10,6 +10,10 @@ import (
"github.com/kelseyhightower/envconfig"
)
const (
defaultHistoryMax = 10
)
var (
justNumbers = regexp.MustCompile(`^\d+$`)
deprecatedVars = []string{"PURGE", "RECREATE_PODS", "TILLER_NS", "UPGRADE", "CANARY_IMAGE", "CLIENT_ONLY", "STABLE_REPO_URL"}
@@ -23,7 +27,8 @@ type Config struct {
// Configuration for drone-helm itself
Command string `envconfig:"mode"` // Helm command to run
DroneEvent string `envconfig:"drone_build_event"` // Drone event that invoked this plugin.
UpdateDependencies bool `split_words:"true"` // Call `helm dependency update` before the main command
UpdateDependencies bool `split_words:"true"` // [Deprecated] Call `helm dependency update` before the main command (deprecated, use dependencies_action: update instead)
DependenciesAction string `split_words:"true"` // Call `helm dependency build` or `helm dependency update` before the main command
AddRepos []string `split_words:"true"` // Call `helm repo add` before the main command
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)
@@ -32,7 +37,9 @@ type Config struct {
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
Namespace string `` // Kubernetes namespace for all helm commands
CreateNamespace bool `split_words:"true"` // Pass --create-namespace to `helm upgrade`
KubeToken string `split_words:"true"` // Kubernetes authentication token to put in .kube/config
SkipKubeconfig bool `envconfig:"skip_kubeconfig"` // Skip kubeconfig creation
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)
APIServer string `envconfig:"kube_api_server"` // The Kubernetes cluster's API endpoint
@@ -42,6 +49,7 @@ type Config struct {
Wait bool `envconfig:"wait_for_upgrade"` // Pass --wait to applicable helm commands
ReuseValues bool `split_words:"true"` // Pass --reuse-values to `helm upgrade`
KeepHistory bool `split_words:"true"` // Pass --keep-history to `helm uninstall`
HistoryMax int `split_words:"true"` // Pass --history-max option
Timeout string `` // Argument to pass to --timeout in applicable helm commands
Chart string `` // Chart argument to use in applicable helm commands
Release string `` // Release argument to use in applicable helm commands
@@ -49,6 +57,7 @@ type Config struct {
AtomicUpgrade bool `split_words:"true"` // Pass --atomic to `helm upgrade`
CleanupOnFail bool `envconfig:"cleanup_failed_upgrade"` // Pass --cleanup-on-fail to `helm upgrade`
LintStrictly bool `split_words:"true"` // Pass --strict to `helm lint`
SkipCrds bool `split_words:"true"` // Pass --skip-crds to `helm upgrade`
Stdout io.Writer `ignored:"true"`
Stderr io.Writer `ignored:"true"`
@@ -75,6 +84,9 @@ func NewConfig(stdout, stderr io.Writer) (*Config, error) {
KubeToken: aliases.KubeToken,
Certificate: aliases.Certificate,
// set to same default as helm CLI
HistoryMax: defaultHistoryMax,
Stdout: stdout,
Stderr: stderr,
}
@@ -86,6 +98,12 @@ func NewConfig(stdout, stderr io.Writer) (*Config, error) {
return nil, err
}
if cfg.SkipKubeconfig {
if cfg.KubeToken != "" || cfg.Certificate != "" || cfg.APIServer != "" || cfg.ServiceAccount != "" || cfg.SkipTLSVerify {
fmt.Fprintf(cfg.Stderr, "Warning: skip_kubeconfig is set. The following kubeconfig-related settings will be ignored: kube_config, kube_certificate, kube_api_server, kube_service_account, skip_tls_verify.")
}
}
if justNumbers.MatchString(cfg.Timeout) {
cfg.Timeout = fmt.Sprintf("%ss", cfg.Timeout)
}
@@ -143,6 +161,10 @@ func (cfg *Config) deprecationWarn() {
}
}
// settingAliases provides alternate environment variable names for certain settings, either because
// they were renamed during drone-helm3's lifetime or for backward-compatibility with the original
// drone-helm. Most config options don't need to be included here; adding them to the main Config
// struct is sufficient.
type settingAliases struct {
Command string `envconfig:"helm_command"`
AddRepos []string `envconfig:"helm_repos"`

View File

@@ -217,6 +217,15 @@ func (suite *ConfigTestSuite) TestValuesSecretsWithDebugLogging() {
suite.Contains(stderr.String(), `$SECRET_WATER not present in environment, replaced with ""`)
}
func (suite *ConfigTestSuite) TestHistoryMax() {
conf := NewTestConfig(suite.T())
suite.Assert().Equal(10, conf.HistoryMax)
suite.setenv("PLUGIN_HISTORY_MAX", "0")
conf = NewTestConfig(suite.T())
suite.Assert().Equal(0, conf.HistoryMax)
}
func (suite *ConfigTestSuite) setenv(key, val string) {
orig, ok := os.LookupEnv(key)
if ok {

16
internal/env/testing.go vendored Normal file
View File

@@ -0,0 +1,16 @@
package env
import (
"os"
"testing"
"github.com/stretchr/testify/require"
)
// NewTestConfig is a helper for setting up a Config object and error checking
func NewTestConfig(t *testing.T) *Config {
conf, err := NewConfig(os.Stdout, os.Stderr)
require.NoError(t, err)
return conf
}

View File

@@ -1,6 +1,7 @@
package helm
import (
"errors"
"fmt"
"github.com/pelotech/drone-helm3/internal/env"
"github.com/pelotech/drone-helm3/internal/run"
@@ -30,6 +31,10 @@ func NewPlan(cfg env.Config) (*Plan, error) {
cfg: cfg,
}
if cfg.UpdateDependencies && cfg.DependenciesAction != "" {
return nil, errors.New("update_dependencies is deprecated and cannot be provided together with dependencies_action")
}
p.steps = (*determineSteps(cfg))(cfg)
for i, step := range p.steps {
@@ -87,13 +92,21 @@ func (p *Plan) Execute() error {
var upgrade = func(cfg env.Config) []Step {
var steps []Step
steps = append(steps, run.NewInitKube(cfg, kubeConfigTemplate, kubeConfigFile))
if !cfg.SkipKubeconfig {
steps = append(steps, run.NewInitKube(cfg, kubeConfigTemplate, kubeConfigFile))
}
for _, repo := range cfg.AddRepos {
steps = append(steps, run.NewAddRepo(cfg, repo))
}
if cfg.DependenciesAction != "" {
steps = append(steps, run.NewDepAction(cfg))
}
if cfg.UpdateDependencies {
steps = append(steps, run.NewDepUpdate(cfg))
}
steps = append(steps, run.NewUpgrade(cfg))
return steps
@@ -101,7 +114,9 @@ var upgrade = func(cfg env.Config) []Step {
var uninstall = func(cfg env.Config) []Step {
var steps []Step
steps = append(steps, run.NewInitKube(cfg, kubeConfigTemplate, kubeConfigFile))
if !cfg.SkipKubeconfig {
steps = append(steps, run.NewInitKube(cfg, kubeConfigTemplate, kubeConfigFile))
}
if cfg.UpdateDependencies {
steps = append(steps, run.NewDepUpdate(cfg))
}

View File

@@ -122,6 +122,12 @@ func (suite *PlanTestSuite) TestUpgrade() {
suite.IsType(&run.Upgrade{}, steps[1])
}
func (suite *PlanTestSuite) TestUpgradeWithSkipKubeconfig() {
steps := upgrade(env.Config{SkipKubeconfig: true})
suite.Require().Equal(1, len(steps), "upgrade should return 1 step")
suite.IsType(&run.Upgrade{}, steps[0])
}
func (suite *PlanTestSuite) TestUpgradeWithUpdateDependencies() {
cfg := env.Config{
UpdateDependencies: true,

59
internal/run/depaction.go Normal file
View File

@@ -0,0 +1,59 @@
package run
import (
"errors"
"fmt"
"github.com/pelotech/drone-helm3/internal/env"
)
const (
actionBuild = "build"
actionUpdate = "update"
)
// DepAction is an execution step that calls `helm dependency update` or `helm dependency build` when executed.
type DepAction struct {
*config
chart string
cmd cmd
action string
}
// NewDepAction creates a DepAction using fields from the given Config. No validation is performed at this time.
func NewDepAction(cfg env.Config) *DepAction {
return &DepAction{
config: newConfig(cfg),
chart: cfg.Chart,
action: cfg.DependenciesAction,
}
}
// Execute executes the `helm upgrade` command.
func (d *DepAction) Execute() error {
return d.cmd.Run()
}
// Prepare gets the DepAction ready to execute.
func (d *DepAction) Prepare() error {
if d.chart == "" {
return fmt.Errorf("chart is required")
}
args := d.globalFlags()
if d.action != actionBuild && d.action != actionUpdate {
return errors.New("unknown dependency_action: " + d.action)
}
args = append(args, "dependency", d.action, d.chart)
d.cmd = command(helmBin, args...)
d.cmd.Stdout(d.stdout)
d.cmd.Stderr(d.stderr)
if d.debug {
fmt.Fprintf(d.stderr, "Generated command: '%s'\n", d.cmd.String())
}
return nil
}

View File

@@ -0,0 +1,131 @@
package run
import (
"errors"
"github.com/golang/mock/gomock"
"github.com/pelotech/drone-helm3/internal/env"
"github.com/stretchr/testify/suite"
"strings"
"testing"
)
type DepActionTestSuite struct {
suite.Suite
ctrl *gomock.Controller
mockCmd *Mockcmd
originalCommand func(string, ...string) cmd
}
func (suite *DepActionTestSuite) BeforeTest(_, _ string) {
suite.ctrl = gomock.NewController(suite.T())
suite.mockCmd = NewMockcmd(suite.ctrl)
suite.originalCommand = command
command = func(path string, args ...string) cmd { return suite.mockCmd }
}
func (suite *DepActionTestSuite) AfterTest(_, _ string) {
command = suite.originalCommand
}
func TestDepActionTestSuite(t *testing.T) {
suite.Run(t, new(DepActionTestSuite))
}
func (suite *DepActionTestSuite) TestNewDepAction() {
cfg := env.Config{
Chart: "scatterplot",
}
d := NewDepAction(cfg)
suite.Equal("scatterplot", d.chart)
}
func (suite *DepActionTestSuite) TestPrepareAndExecuteBuild() {
defer suite.ctrl.Finish()
stdout := strings.Builder{}
stderr := strings.Builder{}
cfg := env.Config{
Chart: "your_top_songs_2019",
Stdout: &stdout,
Stderr: &stderr,
DependenciesAction: "build",
}
command = func(path string, args ...string) cmd {
suite.Equal(helmBin, path)
suite.Equal([]string{"dependency", "build", "your_top_songs_2019"}, args)
return suite.mockCmd
}
suite.mockCmd.EXPECT().
Stdout(&stdout)
suite.mockCmd.EXPECT().
Stderr(&stderr)
suite.mockCmd.EXPECT().
Run().
Times(1)
d := NewDepAction(cfg)
suite.Require().NoError(d.Prepare())
suite.NoError(d.Execute())
}
func (suite *DepActionTestSuite) TestPrepareAndExecuteUpdate() {
defer suite.ctrl.Finish()
stdout := strings.Builder{}
stderr := strings.Builder{}
cfg := env.Config{
Chart: "your_top_songs_2019",
Stdout: &stdout,
Stderr: &stderr,
DependenciesAction: "update",
}
command = func(path string, args ...string) cmd {
suite.Equal(helmBin, path)
suite.Equal([]string{"dependency", "update", "your_top_songs_2019"}, args)
return suite.mockCmd
}
suite.mockCmd.EXPECT().
Stdout(&stdout)
suite.mockCmd.EXPECT().
Stderr(&stderr)
suite.mockCmd.EXPECT().
Run().
Times(1)
d := NewDepAction(cfg)
suite.Require().NoError(d.Prepare())
suite.NoError(d.Execute())
}
func (suite *DepActionTestSuite) TestPrepareAndExecuteUnknown() {
defer suite.ctrl.Finish()
stdout := strings.Builder{}
stderr := strings.Builder{}
cfg := env.Config{
Chart: "your_top_songs_2019",
Stdout: &stdout,
Stderr: &stderr,
DependenciesAction: "downgrade",
}
d := NewDepAction(cfg)
suite.Require().Equal(errors.New("unknown dependency_action: downgrade"), d.Prepare())
}
func (suite *DepActionTestSuite) TestPrepareChartRequired() {
d := NewDepAction(env.Config{})
suite.mockCmd.EXPECT().Stdout(gomock.Any()).AnyTimes()
suite.mockCmd.EXPECT().Stderr(gomock.Any()).AnyTimes()
err := d.Prepare()
suite.EqualError(err, "chart is required")
}

View File

@@ -2,6 +2,7 @@ package run
import (
"fmt"
"github.com/pelotech/drone-helm3/internal/env"
)
@@ -11,18 +12,21 @@ type Upgrade struct {
chart string
release string
chartVersion string
dryRun bool
wait bool
values string
stringValues string
valuesFiles []string
reuseValues bool
timeout string
force bool
atomic bool
cleanupOnFail bool
certs *repoCerts
chartVersion string
dryRun bool
wait bool
values string
stringValues string
valuesFiles []string
reuseValues bool
timeout string
force bool
atomic bool
cleanupOnFail bool
historyMax int
certs *repoCerts
createNamespace bool
skipCrds bool
cmd cmd
}
@@ -30,21 +34,24 @@ type Upgrade struct {
// 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),
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,
historyMax: cfg.HistoryMax,
certs: newRepoCerts(cfg),
createNamespace: cfg.CreateNamespace,
skipCrds: cfg.SkipCrds,
}
}
@@ -95,11 +102,20 @@ func (u *Upgrade) Prepare() error {
if u.stringValues != "" {
args = append(args, "--set-string", u.stringValues)
}
if u.createNamespace {
args = append(args, "--create-namespace")
}
if u.skipCrds {
args = append(args, "--skip-crds")
}
for _, vFile := range u.valuesFiles {
args = append(args, "--values", vFile)
}
args = append(args, u.certs.flags()...)
// always set --history-max since it defaults to non-zero value
args = append(args, fmt.Sprintf("--history-max=%d", u.historyMax))
args = append(args, u.release, u.chart)
u.cmd = command(helmBin, args...)
u.cmd.Stdout(u.stdout)

View File

@@ -2,11 +2,12 @@ package run
import (
"fmt"
"strings"
"testing"
"github.com/golang/mock/gomock"
"github.com/pelotech/drone-helm3/internal/env"
"github.com/stretchr/testify/suite"
"strings"
"testing"
)
type UpgradeTestSuite struct {
@@ -33,23 +34,23 @@ func TestUpgradeTestSuite(t *testing.T) {
}
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,
}
cfg := env.NewTestConfig(suite.T())
cfg.ChartVersion = "seventeen"
cfg.DryRun = true
cfg.Wait = true
cfg.Values = "steadfastness,forthrightness"
cfg.StringValues = "tensile_strength,flexibility"
cfg.ValuesFiles = []string{"/root/price_inventory.yml"}
cfg.ReuseValues = true
cfg.Timeout = "go sit in the corner"
cfg.Chart = "billboard_top_100"
cfg.Release = "post_malone_circles"
cfg.Force = true
cfg.AtomicUpgrade = true
cfg.CleanupOnFail = true
up := NewUpgrade(*cfg)
up := NewUpgrade(cfg)
suite.Equal(cfg.Chart, up.chart)
suite.Equal(cfg.Release, up.release)
suite.Equal(cfg.ChartVersion, up.chartVersion)
@@ -70,15 +71,15 @@ func (suite *UpgradeTestSuite) TestNewUpgrade() {
func (suite *UpgradeTestSuite) TestPrepareAndExecute() {
defer suite.ctrl.Finish()
cfg := env.Config{
Chart: "at40",
Release: "jonas_brothers_only_human",
}
u := NewUpgrade(cfg)
cfg := env.NewTestConfig(suite.T())
cfg.Chart = "at40"
cfg.Release = "jonas_brothers_only_human"
u := NewUpgrade(*cfg)
command = func(path string, args ...string) cmd {
suite.Equal(helmBin, path)
suite.Equal([]string{"upgrade", "--install", "jonas_brothers_only_human", "at40"}, args)
suite.Equal([]string{"upgrade", "--install", "--history-max=10", "jonas_brothers_only_human", "at40"}, args)
return suite.mockCmd
}
@@ -99,16 +100,16 @@ func (suite *UpgradeTestSuite) TestPrepareAndExecute() {
func (suite *UpgradeTestSuite) TestPrepareNamespaceFlag() {
defer suite.ctrl.Finish()
cfg := env.Config{
Namespace: "melt",
Chart: "at40",
Release: "shaed_trampoline",
}
u := NewUpgrade(cfg)
cfg := env.NewTestConfig(suite.T())
cfg.Namespace = "melt"
cfg.Chart = "at40"
cfg.Release = "shaed_trampoline"
u := NewUpgrade(*cfg)
command = func(path string, args ...string) cmd {
suite.Equal(helmBin, path)
suite.Equal([]string{"--namespace", "melt", "upgrade", "--install", "shaed_trampoline", "at40"}, args)
suite.Equal([]string{"--namespace", "melt", "upgrade", "--install", "--history-max=10", "shaed_trampoline", "at40"}, args)
return suite.mockCmd
}
@@ -123,22 +124,22 @@ func (suite *UpgradeTestSuite) TestPrepareNamespaceFlag() {
func (suite *UpgradeTestSuite) TestPrepareWithUpgradeFlags() {
defer suite.ctrl.Finish()
cfg := env.Config{
Chart: "hot_ac",
Release: "maroon_5_memories",
ChartVersion: "radio_edit",
DryRun: true,
Wait: true,
Values: "age=35",
StringValues: "height=5ft10in",
ValuesFiles: []string{"/usr/local/stats", "/usr/local/grades"},
ReuseValues: true,
Timeout: "sit_in_the_corner",
Force: true,
AtomicUpgrade: true,
CleanupOnFail: true,
}
u := NewUpgrade(cfg)
cfg := env.NewTestConfig(suite.T())
cfg.Chart = "hot_ac"
cfg.Release = "maroon_5_memories"
cfg.ChartVersion = "radio_edit"
cfg.DryRun = true
cfg.Wait = true
cfg.Values = "age=35"
cfg.StringValues = "height=5ft10in"
cfg.ValuesFiles = []string{"/usr/local/stats", "/usr/local/grades"}
cfg.ReuseValues = true
cfg.Timeout = "sit_in_the_corner"
cfg.Force = true
cfg.AtomicUpgrade = true
cfg.CleanupOnFail = true
u := NewUpgrade(*cfg)
// inject a ca cert filename so repoCerts won't create any files that we'd have to clean up
u.certs.caCertFilename = "local_ca.cert"
@@ -158,6 +159,7 @@ func (suite *UpgradeTestSuite) TestPrepareWithUpgradeFlags() {
"--values", "/usr/local/stats",
"--values", "/usr/local/grades",
"--ca-file", "local_ca.cert",
"--history-max=10",
"maroon_5_memories", "hot_ac"}, args)
return suite.mockCmd
@@ -191,14 +193,15 @@ func (suite *UpgradeTestSuite) TestRequiresChartAndRelease() {
func (suite *UpgradeTestSuite) TestPrepareDebugFlag() {
stdout := strings.Builder{}
stderr := strings.Builder{}
cfg := env.Config{
Chart: "at40",
Release: "lewis_capaldi_someone_you_loved",
Debug: true,
Stdout: &stdout,
Stderr: &stderr,
}
u := NewUpgrade(cfg)
cfg := env.NewTestConfig(suite.T())
cfg.Chart = "at40"
cfg.Release = "lewis_capaldi_someone_you_loved"
cfg.Debug = true
cfg.Stdout = &stdout
cfg.Stderr = &stderr
u := NewUpgrade(*cfg)
command = func(path string, args ...string) cmd {
suite.mockCmd.EXPECT().
@@ -215,8 +218,34 @@ func (suite *UpgradeTestSuite) TestPrepareDebugFlag() {
u.Prepare()
want := fmt.Sprintf("Generated command: '%s --debug upgrade "+
"--install lewis_capaldi_someone_you_loved at40'\n", helmBin)
want := fmt.Sprintf(
"Generated command: '%s --debug upgrade --install --history-max=10 lewis_capaldi_someone_you_loved at40'\n",
helmBin,
)
suite.Equal(want, stderr.String())
suite.Equal("", stdout.String())
}
func (suite *UpgradeTestSuite) TestPrepareSkipCrdsFlag() {
defer suite.ctrl.Finish()
cfg := env.NewTestConfig(suite.T())
cfg.Chart = "at40"
cfg.Release = "cabbages_smell_great"
cfg.SkipCrds = true
u := NewUpgrade(*cfg)
command = func(path string, args ...string) cmd {
suite.Equal(helmBin, path)
suite.Equal([]string{"upgrade", "--install", "--skip-crds", "--history-max=10", "cabbages_smell_great", "at40"}, args)
return suite.mockCmd
}
suite.mockCmd.EXPECT().Stdout(gomock.Any())
suite.mockCmd.EXPECT().Stderr(gomock.Any())
err := u.Prepare()
suite.Require().Nil(err)
}