woodpecker-helm3/internal/helm/plan.go
Erin Call 08ddf5e27a
Log debug output in helm.Config [#9]
Redacting KubeToken may not be sufficient, since it's possible that
someone would put secrets in Values or StringValues. Unilaterally
redacting those seems unhelpful, though, since they may be the very
thing the user is trying to debug. I've settled on redacting the obvious
field without trying to promise that all sensitive data will be hidden.
2019-12-24 11:08:09 -08:00

150 lines
3.1 KiB
Go

package helm
import (
"fmt"
"github.com/pelotech/drone-helm3/internal/run"
"os"
)
const (
kubeConfigTemplate = "/root/.kube/config.tpl"
kubeConfigFile = "/root/.kube/config"
)
// A Step is one step in the plan.
type Step interface {
Prepare(run.Config) error
Execute(run.Config) error
}
// A Plan is a series of steps to perform.
type Plan struct {
steps []Step
cfg Config
runCfg run.Config
}
// NewPlan makes a plan for running a helm operation.
func NewPlan(cfg Config) (*Plan, error) {
p := Plan{
cfg: cfg,
runCfg: run.Config{
Debug: cfg.Debug,
Values: cfg.Values,
StringValues: cfg.StringValues,
ValuesFiles: cfg.ValuesFiles,
Namespace: cfg.Namespace,
Stdout: cfg.Stdout,
Stderr: cfg.Stderr,
},
}
p.steps = (*determineSteps(cfg))(cfg)
for i, step := range p.steps {
if cfg.Debug {
fmt.Fprintf(os.Stderr, "calling %T.Prepare (step %d)\n", step, i)
}
if err := step.Prepare(p.runCfg); err != nil {
err = fmt.Errorf("while preparing %T step: %w", step, err)
return nil, err
}
}
return &p, nil
}
// 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.
func determineSteps(cfg Config) *func(Config) []Step {
switch cfg.Command {
case "upgrade":
return &upgrade
case "uninstall", "delete":
return &uninstall
case "lint":
return &lint
case "help":
return &help
default:
switch cfg.DroneEvent {
case "push", "tag", "deployment", "pull_request", "promote", "rollback":
return &upgrade
case "delete":
return &uninstall
default:
panic("not implemented")
}
}
}
// Execute runs each step in the plan, aborting and reporting on error
func (p *Plan) Execute() error {
for i, step := range p.steps {
if p.cfg.Debug {
fmt.Fprintf(p.cfg.Stderr, "calling %T.Execute (step %d)\n", step, i)
}
if err := step.Execute(p.runCfg); err != nil {
return fmt.Errorf("in execution step %d: %w", i, err)
}
}
return nil
}
var upgrade = func(cfg Config) []Step {
steps := initKube(cfg)
steps = append(steps, &run.Upgrade{
Chart: cfg.Chart,
Release: cfg.Release,
ChartVersion: cfg.ChartVersion,
DryRun: cfg.DryRun,
Wait: cfg.Wait,
ReuseValues: cfg.ReuseValues,
Timeout: cfg.Timeout,
Force: cfg.Force,
})
return steps
}
var uninstall = func(cfg Config) []Step {
steps := initKube(cfg)
steps = append(steps, &run.Uninstall{
Release: cfg.Release,
DryRun: cfg.DryRun,
})
return steps
}
var lint = func(cfg Config) []Step {
lint := &run.Lint{
Chart: cfg.Chart,
}
return []Step{lint}
}
var help = func(cfg Config) []Step {
help := &run.Help{}
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,
},
}
}