Replicate most of drone-helm's config
This commit is contained in:
parent
238ede6f9e
commit
e3051ec72e
|
@ -4,5 +4,10 @@ TODO:
|
|||
|
||||
* [x] Make a `.drone.yml` that's sufficient for building an image
|
||||
* [x] Make a `Dockerfile` that's sufficient for launching the built image
|
||||
* [ ] Make `cmd/drone-helm/main.go` actually invoke `helm`
|
||||
* [x] Make `cmd/drone-helm/main.go` actually invoke `helm`
|
||||
* [ ] Flesh out `helm upgrade` until it's capable of working
|
||||
* [ ] Implement `helm lint`
|
||||
* [ ] Implement `helm delete`
|
||||
* [ ] Implement all config settings
|
||||
* [ ] EKS support
|
||||
* [ ] Change `.drone.yml` to use a real docker registry
|
||||
|
|
|
@ -2,44 +2,34 @@ package main
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/urfave/cli"
|
||||
"github.com/kelseyhightower/envconfig"
|
||||
"os"
|
||||
|
||||
"github.com/pelotech/drone-helm3/internal/run"
|
||||
"github.com/pelotech/drone-helm3/internal/helm"
|
||||
)
|
||||
|
||||
func main() {
|
||||
app := cli.NewApp()
|
||||
app.Name = "helm plugin"
|
||||
app.Usage = "helm plugin"
|
||||
app.Action = execute
|
||||
app.Version = "0.0.1α"
|
||||
app.Flags = []cli.Flag{
|
||||
cli.StringFlag{
|
||||
Name: "helm_command",
|
||||
Usage: "Helm command to execute",
|
||||
EnvVar: "PLUGIN_HELM_COMMAND,HELM_COMMAND",
|
||||
},
|
||||
}
|
||||
var c helm.Config
|
||||
|
||||
if err := app.Run(os.Args); err != nil {
|
||||
if err := envconfig.Process("plugin", &c); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "%s\n", err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// Make the plan
|
||||
plan, err := helm.NewPlan(c)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// Execute the plan
|
||||
err = plan.Execute()
|
||||
|
||||
// Expect the plan to go off the rails
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, err.Error())
|
||||
// Throw away the plan
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
func execute(c *cli.Context) error {
|
||||
switch c.String("helm_command") {
|
||||
case "upgrade":
|
||||
run.Upgrade()
|
||||
case "help":
|
||||
run.Help()
|
||||
default:
|
||||
switch os.Getenv("DRONE_BUILD_EVENT") {
|
||||
case "push", "tag", "deployment", "pull_request", "promote", "rollback":
|
||||
run.Upgrade()
|
||||
default:
|
||||
run.Help()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
2
go.mod
2
go.mod
|
@ -4,6 +4,6 @@ go 1.13
|
|||
|
||||
require (
|
||||
github.com/golang/mock v1.3.1
|
||||
github.com/kelseyhightower/envconfig v1.4.0
|
||||
github.com/stretchr/testify v1.4.0
|
||||
github.com/urfave/cli v1.22.0
|
||||
)
|
||||
|
|
10
go.sum
10
go.sum
|
@ -1,19 +1,14 @@
|
|||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/cpuguy83/go-md2man v1.0.10 h1:BSKMNlYxDvnunlTymqtgONjNnaRV1sTpcovwwjF22jk=
|
||||
github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
|
||||
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/golang/mock v1.3.1 h1:qGJ6qTW+x6xX/my+8YUVl4WNpX9B7+/l2tRsHGZ7f2s=
|
||||
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
|
||||
github.com/kelseyhightower/envconfig v1.4.0 h1:Im6hONhd3pLkfDFsbRgu68RDNkGF1r3dvMUtDTo2cv8=
|
||||
github.com/kelseyhightower/envconfig v1.4.0/go.mod h1:cccZRl6mQpaq41TPp5QxidR+Sa3axMbJDNb//FQX6Gg=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/russross/blackfriday v1.5.2 h1:HyvC0ARfnZBqnXwABFeSZHpKvJHJJfPz81GNueLj0oo=
|
||||
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/urfave/cli v1.22.0 h1:8nz/RUUotroXnOpYzT/Fy3sBp+2XEbXaY641/s3nbFI=
|
||||
github.com/urfave/cli v1.22.0/go.mod h1:b3D7uWrF2GilkNgYpgcg6J+JMUw7ehmNkE8sZdliGLc=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
|
@ -21,6 +16,7 @@ golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5h
|
|||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/tools v0.0.0-20190425150028-36563e24a262 h1:qsl9y/CJx34tuA7QCPNp86JNJe4spst6Ff8MjvPUdPg=
|
||||
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
|
|
58
internal/helm/config.go
Normal file
58
internal/helm/config.go
Normal file
|
@ -0,0 +1,58 @@
|
|||
package helm
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
// Configuration for drone-helm itself
|
||||
Command HelmCommand `envconfig:"HELM_COMMAND"` // 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
|
||||
Repos []string `envconfig:"HELM_REPOS"` // call `helm repo add` before the main command
|
||||
Prefix string `` // Prefix to use when looking up secret env vars
|
||||
|
||||
// Global helm config
|
||||
Debug bool `` // global helm flag (also applies to drone-helm itself)
|
||||
KubeConfig string `split_words:"true" default:"/root/.kube/config"` // path to the kube config file
|
||||
Values string ``
|
||||
StringValues string `split_words:"true"`
|
||||
ValuesFiles []string `split_words:"true"`
|
||||
Namespace string ``
|
||||
Token string `envconfig:"KUBERNETES_TOKEN"`
|
||||
SkipTLSVerify bool `envconfig:"SKIP_TLS_VERIFY"`
|
||||
Certificate string `envconfig:"KUBERNETES_CERTIFICATE"`
|
||||
APIServer string `envconfig:"API_SERVER"`
|
||||
ServiceAccount string `envconfig:"SERVICE_ACCOUNT"` // Can't just use split_words; need envconfig to find the non-prefixed form
|
||||
|
||||
// Config specifically for `helm upgrade`
|
||||
ChartVersion string `split_words:"true"` //
|
||||
DryRun bool `split_words:"true"` // also available for `delete`
|
||||
Wait bool `` //
|
||||
ReuseValues bool `split_words:"true"` //
|
||||
Timeout string `` //
|
||||
Chart string `` // Also available for `lint`, in which case it must be a path to a chart directory
|
||||
Release string ``
|
||||
Force bool `` //
|
||||
}
|
||||
|
||||
type HelmCommand string
|
||||
|
||||
// HelmCommand.Decode checks the given value against the list of known commands and generates a helpful error if the command is unknown.
|
||||
func (cmd *HelmCommand) Decode(value string) error {
|
||||
known := []string{"upgrade", "delete", "lint", "help"}
|
||||
for _, c := range known {
|
||||
if value == c {
|
||||
*cmd = HelmCommand(value)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
if value == "" {
|
||||
return nil
|
||||
}
|
||||
known[len(known)-1] = fmt.Sprintf("or %s", known[len(known)-1])
|
||||
return fmt.Errorf("Unknown command '%s'. If specified, command must be %s.",
|
||||
value, strings.Join(known, ", "))
|
||||
}
|
28
internal/helm/config_test.go
Normal file
28
internal/helm/config_test.go
Normal file
|
@ -0,0 +1,28 @@
|
|||
package helm
|
||||
|
||||
import (
|
||||
"github.com/stretchr/testify/suite"
|
||||
"testing"
|
||||
)
|
||||
|
||||
type ConfigTestSuite struct {
|
||||
suite.Suite
|
||||
}
|
||||
|
||||
func TestConfigTestSuite(t *testing.T) {
|
||||
suite.Run(t, new(ConfigTestSuite))
|
||||
}
|
||||
|
||||
func (suite *ConfigTestSuite) TestHelmCommandDecodeSuccess() {
|
||||
cmd := HelmCommand("")
|
||||
err := cmd.Decode("upgrade")
|
||||
suite.Require().Nil(err)
|
||||
|
||||
suite.EqualValues(cmd, "upgrade")
|
||||
}
|
||||
|
||||
func (suite *ConfigTestSuite) TestHelmCommandDecodeFailure() {
|
||||
cmd := HelmCommand("")
|
||||
err := cmd.Decode("execute order 66")
|
||||
suite.EqualError(err, "Unknown command 'execute order 66'. If specified, command must be upgrade, delete, lint, or help.")
|
||||
}
|
62
internal/helm/plan.go
Normal file
62
internal/helm/plan.go
Normal file
|
@ -0,0 +1,62 @@
|
|||
package helm
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"github.com/pelotech/drone-helm3/internal/run"
|
||||
)
|
||||
|
||||
type Step interface {
|
||||
Run() error
|
||||
}
|
||||
|
||||
type Plan struct {
|
||||
steps []Step
|
||||
}
|
||||
|
||||
func NewPlan(cfg Config) (*Plan, error) {
|
||||
p := Plan{}
|
||||
switch cfg.Command {
|
||||
case "upgrade":
|
||||
steps, err := upgrade(cfg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
p.steps = steps
|
||||
case "delete":
|
||||
return nil, errors.New("not implemented")
|
||||
case "lint":
|
||||
return nil, errors.New("not implemented")
|
||||
case "help":
|
||||
return nil, errors.New("not implemented")
|
||||
default:
|
||||
switch cfg.DroneEvent {
|
||||
case "push", "tag", "deployment", "pull_request", "promote", "rollback":
|
||||
steps, err := upgrade(cfg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
p.steps = steps
|
||||
default:
|
||||
return nil, errors.New("not implemented")
|
||||
}
|
||||
}
|
||||
|
||||
return &p, nil
|
||||
}
|
||||
|
||||
func (p *Plan) Execute() error {
|
||||
for _, step := range p.steps {
|
||||
if err := step.Run(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func upgrade(cfg Config) ([]Step, error) {
|
||||
steps := make([]Step, 0)
|
||||
steps = append(steps, run.NewUpgrade(cfg.Release, cfg.Chart))
|
||||
|
||||
return steps, nil
|
||||
}
|
53
internal/helm/plan_test.go
Normal file
53
internal/helm/plan_test.go
Normal file
|
@ -0,0 +1,53 @@
|
|||
package helm
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/stretchr/testify/suite"
|
||||
"testing"
|
||||
|
||||
"github.com/pelotech/drone-helm3/internal/run"
|
||||
)
|
||||
|
||||
type PlanTestSuite struct {
|
||||
suite.Suite
|
||||
}
|
||||
|
||||
func TestPlanTestSuite(t *testing.T) {
|
||||
suite.Run(t, new(PlanTestSuite))
|
||||
}
|
||||
|
||||
func (suite *PlanTestSuite) TestNewPlanUpgradeCommand() {
|
||||
cfg := Config{
|
||||
Command: "upgrade",
|
||||
Chart: "billboard_top_100",
|
||||
Release: "post_malone_circles",
|
||||
}
|
||||
|
||||
plan, err := NewPlan(cfg)
|
||||
suite.Require().Nil(err)
|
||||
suite.Equal(1, len(plan.steps))
|
||||
|
||||
switch step := plan.steps[0].(type) {
|
||||
case *run.Upgrade:
|
||||
suite.Equal("billboard_top_100", step.Chart)
|
||||
suite.Equal("post_malone_circles", step.Release)
|
||||
default:
|
||||
suite.Failf("Wrong type for step 1", "Expected Upgrade, got %T", step)
|
||||
}
|
||||
}
|
||||
|
||||
func (suite *PlanTestSuite) TestNewPlanUpgradeFromDroneEvent() {
|
||||
cfg := Config{
|
||||
Chart: "billboard_top_100",
|
||||
Release: "lizzo_good_as_hell",
|
||||
}
|
||||
|
||||
upgradeEvents := []string{"push", "tag", "deployment", "pull_request", "promote", "rollback"}
|
||||
for _, event := range upgradeEvents {
|
||||
cfg.DroneEvent = event
|
||||
plan, err := NewPlan(cfg)
|
||||
suite.Require().Nil(err)
|
||||
suite.Require().Equal(1, len(plan.steps), fmt.Sprintf("for event type '%s'", event))
|
||||
suite.IsType(&run.Upgrade{}, plan.steps[0], fmt.Sprintf("for event type '%s'", event))
|
||||
}
|
||||
}
|
|
@ -4,12 +4,20 @@ import (
|
|||
"os"
|
||||
)
|
||||
|
||||
func Help(args ...string) error {
|
||||
args = append([]string{"help"}, args...)
|
||||
|
||||
cmd := Command(HELM_BIN, args...)
|
||||
cmd.Stdout(os.Stdout)
|
||||
cmd.Stderr(os.Stderr)
|
||||
|
||||
return cmd.Run()
|
||||
type Help struct {
|
||||
cmd cmd
|
||||
}
|
||||
|
||||
func (h *Help) Run() error {
|
||||
return h.cmd.Run()
|
||||
}
|
||||
|
||||
func NewHelp() *Help {
|
||||
h := Help{}
|
||||
|
||||
h.cmd = Command(HELM_BIN, "help")
|
||||
h.cmd.Stdout(os.Stdout)
|
||||
h.cmd.Stderr(os.Stderr)
|
||||
|
||||
return &h
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@ func TestHelp(t *testing.T) {
|
|||
|
||||
Command = func(path string, args ...string) cmd {
|
||||
assert.Equal(t, HELM_BIN, path)
|
||||
assert.Equal(t, []string{"help", "arg1", "arg2"}, args)
|
||||
assert.Equal(t, []string{"help"}, args)
|
||||
return mCmd
|
||||
}
|
||||
defer func() { Command = originalCommand }()
|
||||
|
@ -28,5 +28,6 @@ func TestHelp(t *testing.T) {
|
|||
Run().
|
||||
Times(1)
|
||||
|
||||
Help("arg1", "arg2")
|
||||
h := NewHelp()
|
||||
h.Run()
|
||||
}
|
||||
|
|
|
@ -4,12 +4,25 @@ import (
|
|||
"os"
|
||||
)
|
||||
|
||||
func Upgrade(args ...string) error {
|
||||
args = append([]string{"upgrade"}, args...)
|
||||
cmd := Command(HELM_BIN, args...)
|
||||
|
||||
cmd.Stdout(os.Stdout)
|
||||
cmd.Stderr(os.Stderr)
|
||||
|
||||
return cmd.Run()
|
||||
type Upgrade struct {
|
||||
Chart string
|
||||
Release string
|
||||
cmd cmd
|
||||
}
|
||||
|
||||
func (u *Upgrade) Run() error {
|
||||
return u.cmd.Run()
|
||||
}
|
||||
|
||||
func NewUpgrade(release, chart string) *Upgrade {
|
||||
u := Upgrade{
|
||||
Chart: chart,
|
||||
Release: release,
|
||||
cmd: Command(HELM_BIN, "upgrade", "--install", release, chart),
|
||||
}
|
||||
|
||||
u.cmd.Stdout(os.Stdout)
|
||||
u.cmd.Stderr(os.Stderr)
|
||||
|
||||
return &u
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@ import (
|
|||
"testing"
|
||||
)
|
||||
|
||||
func TestUpgrade(t *testing.T) {
|
||||
func TestNewUpgrade(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
defer ctrl.Finish()
|
||||
|
||||
|
@ -15,7 +15,7 @@ func TestUpgrade(t *testing.T) {
|
|||
|
||||
Command = func(path string, args ...string) cmd {
|
||||
assert.Equal(t, HELM_BIN, path)
|
||||
assert.Equal(t, []string{"upgrade", "arg1", "arg2"}, args)
|
||||
assert.Equal(t, []string{"upgrade", "--install", "jonas_brothers_only_human", "at40"}, args)
|
||||
|
||||
return mCmd
|
||||
}
|
||||
|
@ -29,5 +29,6 @@ func TestUpgrade(t *testing.T) {
|
|||
Run().
|
||||
Times(1)
|
||||
|
||||
Upgrade("arg1", "arg2")
|
||||
u := NewUpgrade("jonas_brothers_only_human", "at40")
|
||||
u.Run()
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue