Add secrets support via helm-secrets plugin (#53)

Resolves #41
This commit is contained in:
Shane Starcher 2018-03-16 09:43:02 -04:00 committed by KUOKA Yusuke
parent c88d6c431c
commit ffa39af554
5 changed files with 106 additions and 41 deletions

View File

@ -31,6 +31,8 @@ releases:
namespace: vault # target namespace
chart: roboll/vault-secret-manager # the chart being installed to create this release, referenced by `repository/chart` syntax
values: [ vault.yaml ] # value files (--values)
secrets:
- vault_secret.yaml # will attempt to decrypt it using helm-secrets plugin
set: # values (--set)
- name: address
value: https://vault.example.com
@ -83,10 +85,17 @@ GLOBAL OPTIONS:
The `helmfile diff` sub-command executes the [helm-diff](https://github.com/databus23/helm-diff) plugin across all of
the charts/releases defined in the manifest.
Under the covers Helmfile is simply using the `helm diff` plugin, so that needs to be installed prior. For Helm 2.3+
To supply the diff functionality Helmfile needs the `helm diff` plugin installed. For Helm 2.3+
you should be able to simply execute `helm plugin install https://github.com/databus23/helm-diff`. For more details
please look at their [documentation](https://github.com/databus23/helm-diff#helm-diff-plugin).
### secrets
The `secrets` parameter in a `helmfile.yaml` causes the [helm-secrets](https://github.com/futuresimple/helm-secrets) plugin to be executed to decrypt the file.
To supply the secret functionality Helmfile needs the `helm secrets` plugin installed. For Helm 2.3+
you should be able to simply execute `helm plugin install https://github.com/futuresimple/helm-secrets
`.
## Paths Overview
Using manifest files in conjunction with command line argument can be a bit confusing.

View File

@ -59,6 +59,14 @@ func (helm *execer) SyncRelease(name, chart string, flags ...string) error {
return err
}
func (helm *execer) DecryptSecret(name string) (string, error) {
out, err := helm.exec(append([]string{"secrets", "dec", name})...)
if helm.writer != nil {
helm.writer.Write(out)
}
return name + ".dec", err
}
func (helm *execer) DiffRelease(name, chart string, flags ...string) error {
out, err := helm.exec(append([]string{"diff", name, chart}, flags...)...)
if helm.writer != nil {

View File

@ -9,4 +9,6 @@ type Interface interface {
SyncRelease(name, chart string, flags ...string) error
DiffRelease(name, chart string, flags ...string) error
DeleteRelease(name string) error
DecryptSecret(name string) (string, error)
}

75
main.go
View File

@ -5,7 +5,9 @@ import (
"io"
"log"
"os"
"os/signal"
"strings"
"syscall"
"github.com/roboll/helmfile/helmexec"
"github.com/roboll/helmfile/state"
@ -67,13 +69,8 @@ func main() {
helm.SetExtraArgs(strings.Split(args, " ")...)
}
if errs := state.SyncRepos(helm); errs != nil && len(errs) > 0 {
for _, err := range errs {
fmt.Printf("err: %s\n", err.Error())
}
os.Exit(1)
}
return nil
errs := state.SyncRepos(helm)
return clean(state, errs)
},
},
{
@ -109,13 +106,8 @@ func main() {
values := c.StringSlice("values")
workers := c.Int("concurrency")
if errs := state.SyncReleases(helm, values, workers); errs != nil && len(errs) > 0 {
for _, err := range errs {
fmt.Printf("err: %s\n", err.Error())
}
os.Exit(1)
}
return nil
errs := state.SyncReleases(helm, values, workers)
return clean(state, errs)
},
},
{
@ -158,13 +150,8 @@ func main() {
values := c.StringSlice("values")
if errs := state.DiffReleases(helm, values); errs != nil && len(errs) > 0 {
for _, err := range errs {
fmt.Printf("err: %s\n", err.Error())
}
os.Exit(1)
}
return nil
errs := state.DiffReleases(helm, values)
return clean(state, errs)
},
},
{
@ -197,13 +184,8 @@ func main() {
values := c.StringSlice("values")
workers := c.Int("concurrency")
if errs := state.SyncReleases(helm, values, workers); errs != nil && len(errs) > 0 {
for _, err := range errs {
fmt.Printf("err: %s\n", err.Error())
}
os.Exit(1)
}
return nil
errs := state.SyncReleases(helm, values, workers)
return clean(state, errs)
},
},
{
@ -215,13 +197,8 @@ func main() {
return err
}
if errs := state.DeleteReleases(helm); errs != nil && len(errs) > 0 {
for _, err := range errs {
fmt.Printf("err: %s\n", err.Error())
}
os.Exit(1)
}
return nil
errs := state.DeleteReleases(helm)
return clean(state, errs)
},
},
}
@ -267,5 +244,33 @@ func before(c *cli.Context) (*state.HelmState, helmexec.Interface, error) {
writer = os.Stdout
}
sigs := make(chan os.Signal, 1)
signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)
go func() {
sig := <-sigs
errs := []error{fmt.Errorf("Recived [%s] to shutdown ", sig)}
clean(st, errs)
}()
return st, helmexec.NewHelmExec(writer, kubeContext), nil
}
func clean(state *state.HelmState, errs []error) error {
if errs == nil {
errs = []error{}
}
cleanErrs := state.Clean()
if cleanErrs != nil {
errs = append(errs, cleanErrs...)
}
if errs != nil && len(errs) > 0 {
for _, err := range errs {
fmt.Printf("err: %s\n", err.Error())
}
os.Exit(1)
}
return nil
}

View File

@ -45,10 +45,14 @@ type ReleaseSpec struct {
Name string `yaml:"name"`
Namespace string `yaml:"namespace"`
Values []string `yaml:"values"`
Secrets []string `yaml:"secrets"`
SetValues []SetValue `yaml:"set"`
// The 'env' section is not really necessary any longer, as 'set' would now provide the same functionality
EnvValues []SetValue `yaml:"env"`
// generatedValues are values that need cleaned up on exit
generatedValues []string
}
type SetValue struct {
@ -157,7 +161,7 @@ func (state *HelmState) SyncReleases(helm helmexec.Interface, additonalValues []
go func() {
for release := range jobQueue {
releaseWithDefaults := state.applyDefaultsTo(release)
flags, flagsErr := flagsForRelease(state.BaseChartPath, &releaseWithDefaults)
flags, flagsErr := flagsForRelease(helm, state.BaseChartPath, &releaseWithDefaults)
if flagsErr != nil {
errQueue <- flagsErr
doneQueue <- true
@ -214,15 +218,17 @@ func (state *HelmState) DiffReleases(helm helmexec.Interface, additonalValues []
var wg sync.WaitGroup
errs := []error{}
for _, release := range state.Releases {
for i := 0; i < len(state.Releases); i++ {
release := &state.Releases[i]
wg.Add(1)
go func(wg *sync.WaitGroup, release ReleaseSpec) {
go func(wg *sync.WaitGroup, release *ReleaseSpec) {
// Plugin command doesn't support explicit namespace
release.Namespace = ""
flags, flagsErr := flagsForRelease(state.BaseChartPath, &release)
flags, flagsErr := flagsForRelease(helm, state.BaseChartPath, release)
if flagsErr != nil {
errs = append(errs, flagsErr)
}
for _, value := range additonalValues {
valfile, err := filepath.Abs(value)
if err != nil {
@ -269,6 +275,26 @@ func (state *HelmState) DeleteReleases(helm helmexec.Interface) []error {
return nil
}
// Clean will remove any generated secrets
func (state *HelmState) Clean() []error {
errs := []error{}
for _, release := range state.Releases {
for _, value := range release.generatedValues {
err := os.Remove(value)
if err != nil {
errs = append(errs, err)
}
}
}
if len(errs) != 0 {
return errs
}
return nil
}
// normalizeChart allows for the distinction between a file path reference and repository references.
// - Any single (or double character) followed by a `/` will be considered a local file reference and
// be constructed relative to the `base path`.
@ -281,7 +307,7 @@ func normalizeChart(basePath, chart string) string {
return filepath.Join(basePath, chart)
}
func flagsForRelease(basePath string, release *ReleaseSpec) ([]string, error) {
func flagsForRelease(helm helmexec.Interface, basePath string, release *ReleaseSpec) ([]string, error) {
flags := []string{}
if release.Version != "" {
flags = append(flags, "--version", release.Version)
@ -300,6 +326,21 @@ func flagsForRelease(basePath string, release *ReleaseSpec) ([]string, error) {
}
flags = append(flags, "--values", valfileRendered)
}
for _, value := range release.Secrets {
valfile := filepath.Join(basePath, value)
path, err := renderTemplateString(valfile)
if err != nil {
return nil, err
}
valfileRendered, err := helm.DecryptSecret(path)
if err != nil {
return nil, err
}
release.generatedValues = append(release.generatedValues, valfileRendered)
flags = append(flags, "--values", valfileRendered)
}
if len(release.SetValues) > 0 {
val := []string{}
for _, set := range release.SetValues {