Compare commits
48 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c017a87c4c | |||
| f21e5bede0 | |||
| 4896617402 | |||
| 5b9ebfb2d8 | |||
| 628f9d3ef6 | |||
| 5a95211c2f | |||
| b926404924 | |||
| 53104347c2 | |||
| 123c331889 | |||
| 9ce383cf2c | |||
| 06bf1b863c | |||
| e656306216 | |||
| 82ed33b180 | |||
| e4a1fa89fa | |||
| 66e8afd9a4 | |||
| b1e18abf75 | |||
| 5a725fd5c6 | |||
| cda07939db | |||
|
|
7533ed95ad | ||
|
|
98bb4e4ba4 | ||
|
|
e07eb1399b | ||
|
|
9a9987eeb2 | ||
|
|
fed0a769f2 | ||
|
|
48473bd9d8 | ||
|
|
64045fadfb | ||
|
|
c5a7551da8 | ||
|
|
eeccfdd143 | ||
|
|
8d450bbf7d | ||
|
|
a566ea0cf7 | ||
|
|
24f10ce1b9 | ||
|
|
7c033a8b0a | ||
|
|
e482a144c1 | ||
|
|
8dba329407 | ||
|
|
4462307983 | ||
|
|
cfc59a46ef | ||
|
|
5b4e3ab2ea | ||
|
|
0e58dde591 | ||
|
|
1809d7b133 | ||
|
|
70b2a2d0b4 | ||
|
|
03b066bfd7 | ||
|
|
fe0c874815 | ||
|
|
91d4506b37 | ||
|
|
0ab0a7fee4 | ||
|
|
0b17d929a7 | ||
|
|
3fea592040 | ||
|
|
d7a9eccb13 | ||
|
|
9e3a424a36 | ||
|
|
774bbb74db |
52
.drone.yml
52
.drone.yml
@@ -1,32 +1,36 @@
|
||||
---
|
||||
kind: pipeline
|
||||
type: docker
|
||||
name: default
|
||||
type: kubernetes
|
||||
name: build
|
||||
|
||||
trigger:
|
||||
branch:
|
||||
- master
|
||||
|
||||
clone:
|
||||
# disable: true
|
||||
depth: 1
|
||||
|
||||
image_pull_secrets:
|
||||
- dockerconfig
|
||||
|
||||
steps:
|
||||
- name: test
|
||||
image: golang:1.13
|
||||
commands:
|
||||
- go test ./cmd/... ./internal/...
|
||||
- go vet ./cmd/... ./internal/...
|
||||
- name: lint
|
||||
image: cytopia/golint
|
||||
commands:
|
||||
- golint -set_exit_status ./cmd/... ./internal/...
|
||||
- name: build
|
||||
image: golang:1.13
|
||||
commands:
|
||||
- go build -o build/drone-helm cmd/drone-helm/main.go
|
||||
- name: publish_linux_amd64
|
||||
image: plugins/docker
|
||||
image: harbor.1sept.ru/drone/drone-kaniko
|
||||
settings:
|
||||
auto_tag: true
|
||||
registry: harbor.1sept.ru
|
||||
repo: drone/drone-helm3
|
||||
cache: false
|
||||
tags:
|
||||
- git-${DRONE_COMMIT_SHA:0:7}
|
||||
- latest
|
||||
username:
|
||||
from_secret: docker_username
|
||||
from_secret: harbor-username
|
||||
password:
|
||||
from_secret: docker_password
|
||||
repo:
|
||||
from_secret: plugin_repo
|
||||
dockerfile: Dockerfile
|
||||
when:
|
||||
event: [ tag, push ]
|
||||
from_secret: harbor-password
|
||||
|
||||
---
|
||||
kind: signature
|
||||
hmac: ab275b41f1621072152d6e85b5f94289be8dda38debd2a177e0f666960491c6e
|
||||
|
||||
...
|
||||
|
||||
4
.gitignore
vendored
4
.gitignore
vendored
@@ -4,6 +4,7 @@
|
||||
*.dll
|
||||
*.so
|
||||
*.dylib
|
||||
*.swp
|
||||
|
||||
.idea
|
||||
|
||||
@@ -17,3 +18,6 @@
|
||||
# vendor/
|
||||
.env
|
||||
.secrets
|
||||
|
||||
build/*
|
||||
**/.DS_Store
|
||||
|
||||
21
Dockerfile
21
Dockerfile
@@ -1,10 +1,19 @@
|
||||
FROM alpine/helm:3.1.1
|
||||
MAINTAINER Erin Call <erin@liffft.com>
|
||||
FROM golang:1.24-alpine3.21 AS builder
|
||||
|
||||
COPY build/drone-helm /bin/drone-helm
|
||||
COPY assets/kubeconfig.tpl /root/.kube/config.tpl
|
||||
ENV GO111MODULE=on
|
||||
WORKDIR /app
|
||||
|
||||
LABEL description="Helm 3 plugin for Drone 3"
|
||||
LABEL base="alpine/helm"
|
||||
COPY --link go.mod .
|
||||
COPY --link go.sum .
|
||||
RUN go mod download
|
||||
COPY --link . .
|
||||
|
||||
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o /go/bin/drone-helm ./cmd/drone-helm
|
||||
|
||||
# --- Copy the cli to an image with helm already installed ---
|
||||
FROM alpine/helm:3.18.5
|
||||
|
||||
COPY --link --from=builder /go/bin/drone-helm /bin/drone-helm
|
||||
COPY --link ./assets/kubeconfig.tpl /root/.kube/config.tpl
|
||||
|
||||
ENTRYPOINT [ "/bin/drone-helm" ]
|
||||
9
Makefile
Normal file
9
Makefile
Normal file
@@ -0,0 +1,9 @@
|
||||
help:
|
||||
@echo "make check - Check Dockerfile syntax"
|
||||
@echo "make drone-sign - Sign Drone config file"
|
||||
|
||||
check:
|
||||
docker build --check .
|
||||
|
||||
drone-sign: ## Envs DRONE_SERVER & DRONE_TOKEN needed
|
||||
drone sign --save 1sept/drone-helm3
|
||||
@@ -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
|
||||
|
||||
24
internal/env/config.go
vendored
24
internal/env/config.go
vendored
@@ -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"`
|
||||
|
||||
9
internal/env/config_test.go
vendored
9
internal/env/config_test.go
vendored
@@ -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
16
internal/env/testing.go
vendored
Normal 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
|
||||
}
|
||||
@@ -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
|
||||
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
|
||||
if !cfg.SkipKubeconfig {
|
||||
steps = append(steps, run.NewInitKube(cfg, kubeConfigTemplate, kubeConfigFile))
|
||||
}
|
||||
if cfg.UpdateDependencies {
|
||||
steps = append(steps, run.NewDepUpdate(cfg))
|
||||
}
|
||||
|
||||
@@ -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
59
internal/run/depaction.go
Normal file
@@ -0,0 +1,59 @@
|
||||
package run
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/pelotech/drone-helm3/internal/env"
|
||||
)
|
||||
|
||||
const (
|
||||
actionBuild = "build"
|
||||
actionUpdate = "update"
|
||||
)
|
||||
|
||||
// DepAction is an execution step that calls `helm dependency update` or `helm dependency build` when executed.
|
||||
type DepAction struct {
|
||||
*config
|
||||
chart string
|
||||
cmd cmd
|
||||
action string
|
||||
}
|
||||
|
||||
// NewDepAction creates a DepAction using fields from the given Config. No validation is performed at this time.
|
||||
func NewDepAction(cfg env.Config) *DepAction {
|
||||
return &DepAction{
|
||||
config: newConfig(cfg),
|
||||
chart: cfg.Chart,
|
||||
action: cfg.DependenciesAction,
|
||||
}
|
||||
}
|
||||
|
||||
// Execute executes the `helm upgrade` command.
|
||||
func (d *DepAction) Execute() error {
|
||||
return d.cmd.Run()
|
||||
}
|
||||
|
||||
// Prepare gets the DepAction ready to execute.
|
||||
func (d *DepAction) Prepare() error {
|
||||
if d.chart == "" {
|
||||
return fmt.Errorf("chart is required")
|
||||
}
|
||||
|
||||
args := d.globalFlags()
|
||||
|
||||
if d.action != actionBuild && d.action != actionUpdate {
|
||||
return errors.New("unknown dependency_action: " + d.action)
|
||||
}
|
||||
|
||||
args = append(args, "dependency", d.action, d.chart)
|
||||
|
||||
d.cmd = command(helmBin, args...)
|
||||
d.cmd.Stdout(d.stdout)
|
||||
d.cmd.Stderr(d.stderr)
|
||||
|
||||
if d.debug {
|
||||
fmt.Fprintf(d.stderr, "Generated command: '%s'\n", d.cmd.String())
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
131
internal/run/depaction_test.go
Normal file
131
internal/run/depaction_test.go
Normal file
@@ -0,0 +1,131 @@
|
||||
package run
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"github.com/golang/mock/gomock"
|
||||
"github.com/pelotech/drone-helm3/internal/env"
|
||||
"github.com/stretchr/testify/suite"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
type DepActionTestSuite struct {
|
||||
suite.Suite
|
||||
ctrl *gomock.Controller
|
||||
mockCmd *Mockcmd
|
||||
originalCommand func(string, ...string) cmd
|
||||
}
|
||||
|
||||
func (suite *DepActionTestSuite) BeforeTest(_, _ string) {
|
||||
suite.ctrl = gomock.NewController(suite.T())
|
||||
suite.mockCmd = NewMockcmd(suite.ctrl)
|
||||
|
||||
suite.originalCommand = command
|
||||
command = func(path string, args ...string) cmd { return suite.mockCmd }
|
||||
}
|
||||
|
||||
func (suite *DepActionTestSuite) AfterTest(_, _ string) {
|
||||
command = suite.originalCommand
|
||||
}
|
||||
|
||||
func TestDepActionTestSuite(t *testing.T) {
|
||||
suite.Run(t, new(DepActionTestSuite))
|
||||
}
|
||||
|
||||
func (suite *DepActionTestSuite) TestNewDepAction() {
|
||||
cfg := env.Config{
|
||||
Chart: "scatterplot",
|
||||
}
|
||||
d := NewDepAction(cfg)
|
||||
suite.Equal("scatterplot", d.chart)
|
||||
}
|
||||
|
||||
func (suite *DepActionTestSuite) TestPrepareAndExecuteBuild() {
|
||||
defer suite.ctrl.Finish()
|
||||
|
||||
stdout := strings.Builder{}
|
||||
stderr := strings.Builder{}
|
||||
cfg := env.Config{
|
||||
Chart: "your_top_songs_2019",
|
||||
Stdout: &stdout,
|
||||
Stderr: &stderr,
|
||||
DependenciesAction: "build",
|
||||
}
|
||||
|
||||
command = func(path string, args ...string) cmd {
|
||||
suite.Equal(helmBin, path)
|
||||
suite.Equal([]string{"dependency", "build", "your_top_songs_2019"}, args)
|
||||
|
||||
return suite.mockCmd
|
||||
}
|
||||
suite.mockCmd.EXPECT().
|
||||
Stdout(&stdout)
|
||||
suite.mockCmd.EXPECT().
|
||||
Stderr(&stderr)
|
||||
suite.mockCmd.EXPECT().
|
||||
Run().
|
||||
Times(1)
|
||||
|
||||
d := NewDepAction(cfg)
|
||||
|
||||
suite.Require().NoError(d.Prepare())
|
||||
suite.NoError(d.Execute())
|
||||
}
|
||||
|
||||
func (suite *DepActionTestSuite) TestPrepareAndExecuteUpdate() {
|
||||
defer suite.ctrl.Finish()
|
||||
|
||||
stdout := strings.Builder{}
|
||||
stderr := strings.Builder{}
|
||||
cfg := env.Config{
|
||||
Chart: "your_top_songs_2019",
|
||||
Stdout: &stdout,
|
||||
Stderr: &stderr,
|
||||
DependenciesAction: "update",
|
||||
}
|
||||
|
||||
command = func(path string, args ...string) cmd {
|
||||
suite.Equal(helmBin, path)
|
||||
suite.Equal([]string{"dependency", "update", "your_top_songs_2019"}, args)
|
||||
|
||||
return suite.mockCmd
|
||||
}
|
||||
suite.mockCmd.EXPECT().
|
||||
Stdout(&stdout)
|
||||
suite.mockCmd.EXPECT().
|
||||
Stderr(&stderr)
|
||||
suite.mockCmd.EXPECT().
|
||||
Run().
|
||||
Times(1)
|
||||
|
||||
d := NewDepAction(cfg)
|
||||
|
||||
suite.Require().NoError(d.Prepare())
|
||||
suite.NoError(d.Execute())
|
||||
}
|
||||
|
||||
func (suite *DepActionTestSuite) TestPrepareAndExecuteUnknown() {
|
||||
defer suite.ctrl.Finish()
|
||||
|
||||
stdout := strings.Builder{}
|
||||
stderr := strings.Builder{}
|
||||
cfg := env.Config{
|
||||
Chart: "your_top_songs_2019",
|
||||
Stdout: &stdout,
|
||||
Stderr: &stderr,
|
||||
DependenciesAction: "downgrade",
|
||||
}
|
||||
|
||||
d := NewDepAction(cfg)
|
||||
suite.Require().Equal(errors.New("unknown dependency_action: downgrade"), d.Prepare())
|
||||
}
|
||||
|
||||
func (suite *DepActionTestSuite) TestPrepareChartRequired() {
|
||||
d := NewDepAction(env.Config{})
|
||||
|
||||
suite.mockCmd.EXPECT().Stdout(gomock.Any()).AnyTimes()
|
||||
suite.mockCmd.EXPECT().Stderr(gomock.Any()).AnyTimes()
|
||||
|
||||
err := d.Prepare()
|
||||
suite.EqualError(err, "chart is required")
|
||||
}
|
||||
@@ -3,10 +3,11 @@ package run
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/pelotech/drone-helm3/internal/env"
|
||||
"io"
|
||||
"os"
|
||||
"text/template"
|
||||
|
||||
"github.com/pelotech/drone-helm3/internal/env"
|
||||
)
|
||||
|
||||
// InitKube is a step in a helm Plan that initializes the kubernetes config file.
|
||||
@@ -87,7 +88,7 @@ func (i *InitKube) Prepare() error {
|
||||
fmt.Fprintf(i.stderr, "kubeconfig file at %s\n", i.configFilename)
|
||||
}
|
||||
|
||||
i.configFile, err = os.Create(i.configFilename)
|
||||
i.configFile, err = os.OpenFile(i.configFilename, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0600)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not open kubeconfig file for writing: %w", err)
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package run
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/pelotech/drone-helm3/internal/env"
|
||||
)
|
||||
|
||||
@@ -22,7 +23,10 @@ type Upgrade struct {
|
||||
force bool
|
||||
atomic bool
|
||||
cleanupOnFail bool
|
||||
historyMax int
|
||||
certs *repoCerts
|
||||
createNamespace bool
|
||||
skipCrds bool
|
||||
|
||||
cmd cmd
|
||||
}
|
||||
@@ -44,7 +48,10 @@ func NewUpgrade(cfg env.Config) *Upgrade {
|
||||
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)
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user