Use --namespace value within Helmfile template (#343)

* Use --namespace value within Helmfile template (#326).

* Tabs and spaces.

* Tab/spaces fix.

* Pacifying "go fmt".

* Hard-coding namespace in test convenience function.

* MR feedback: Add comment to exported identifier.

* MR feedback: Put comment in correct location.

* fix(ci): fix never-ending build issue by auto-approving `helmfile delete`
This commit is contained in:
Dan Helfman 2018-09-17 05:28:45 -07:00 committed by KUOKA Yusuke
parent 6453768f2e
commit 1ade353c1a
6 changed files with 62 additions and 27 deletions

View File

@ -83,6 +83,9 @@ releases:
values: values:
- 1 - 1
- 2 - 2
# set a templated value
- name: namespace
value: {{ .Namespace }}
# will attempt to decrypt it using helm-secrets plugin # will attempt to decrypt it using helm-secrets plugin
secrets: secrets:
- vault_secret.yaml - vault_secret.yaml
@ -200,7 +203,7 @@ GLOBAL OPTIONS:
--quiet, -q Silence output. Equivalent to log-level warn --quiet, -q Silence output. Equivalent to log-level warn
--kube-context value Set kubectl context. Uses current context by default --kube-context value Set kubectl context. Uses current context by default
--log-level value Set log level, default info --log-level value Set log level, default info
--namespace value, -n value Set namespace. Uses the namespace set in the context by default --namespace value, -n value Set namespace. Uses the namespace set in the context by default, and is available in templates as {{ .Namespace }}
--selector value, -l value Only run using the releases that match labels. Labels can take the form of foo=bar or foo!=bar. --selector value, -l value Only run using the releases that match labels. Labels can take the form of foo=bar or foo!=bar.
A release must match all labels in a group in order to be used. Multiple groups can be specified at once. A release must match all labels in a group in order to be used. Multiple groups can be specified at once.
--selector tier=frontend,tier!=proxy --selector tier=backend. Will match all frontend, non-proxy releases AND all backend releases. --selector tier=frontend,tier!=proxy --selector tier=backend. Will match all frontend, non-proxy releases AND all backend releases.

26
main.go
View File

@ -91,7 +91,7 @@ func main() {
}, },
cli.StringFlag{ cli.StringFlag{
Name: "namespace, n", Name: "namespace, n",
Usage: "Set namespace. Uses the namespace set in the context by default", Usage: "Set namespace. Uses the namespace set in the context by default, and is available in templates as {{ .Namespace }}",
}, },
cli.StringSliceFlag{ cli.StringSliceFlag{
Name: "selector, l", Name: "selector, l",
@ -636,11 +636,12 @@ func prependLineNumbers(text string) string {
} }
type twoPassRenderer struct { type twoPassRenderer struct {
reader func(string) ([]byte, error) reader func(string) ([]byte, error)
env string env string
filename string namespace string
logger *zap.SugaredLogger filename string
abs func(string) (string, error) logger *zap.SugaredLogger
abs func(string) (string, error)
} }
func (r *twoPassRenderer) renderEnvironment(content []byte) environment.Environment { func (r *twoPassRenderer) renderEnvironment(content []byte) environment.Environment {
@ -673,7 +674,7 @@ func (r *twoPassRenderer) renderTemplate(content []byte) (*bytes.Buffer, error)
// try a first pass render. This will always succeed, but can produce a limited env // try a first pass render. This will always succeed, but can produce a limited env
firstPassEnv := r.renderEnvironment(content) firstPassEnv := r.renderEnvironment(content)
secondPassRenderer := tmpl.NewFileRenderer(r.reader, filepath.Dir(r.filename), firstPassEnv) secondPassRenderer := tmpl.NewFileRenderer(r.reader, filepath.Dir(r.filename), firstPassEnv, r.namespace)
yamlBuf, err := secondPassRenderer.RenderTemplateContentToBuffer(content) yamlBuf, err := secondPassRenderer.RenderTemplateContentToBuffer(content)
if err != nil { if err != nil {
if r.logger != nil { if r.logger != nil {
@ -702,11 +703,12 @@ func (a *app) FindAndIterateOverDesiredStates(fileOrDir string, converge func(*s
} }
// render template, in two runs // render template, in two runs
r := &twoPassRenderer{ r := &twoPassRenderer{
reader: a.readFile, reader: a.readFile,
env: env, env: env,
filename: f, namespace: namespace,
logger: a.logger, filename: f,
abs: a.abs, logger: a.logger,
abs: a.abs,
} }
yamlBuf, err := r.renderTemplate(content) yamlBuf, err := r.renderTemplate(content)
if err != nil { if err != nil {

View File

@ -43,11 +43,12 @@ func TestReadFromYaml_DuplicateReleaseName(t *testing.T) {
func makeRenderer(readFile func(string) ([]byte, error), env string) *twoPassRenderer { func makeRenderer(readFile func(string) ([]byte, error), env string) *twoPassRenderer {
return &twoPassRenderer{ return &twoPassRenderer{
reader: readFile, reader: readFile,
env: env, env: env,
filename: "", namespace: "namespace",
logger: logger, filename: "",
abs: filepath.Abs, logger: logger,
abs: filepath.Abs,
} }
} }
@ -99,7 +100,7 @@ releases:
func TestReadFromYaml_RenderTemplate(t *testing.T) { func TestReadFromYaml_RenderTemplate(t *testing.T) {
defaultValuesYalm := []byte(` defaultValuesYaml := []byte(`
releaseName: "hello" releaseName: "hello"
conditionalReleaseTag: "yes" conditionalReleaseTag: "yes"
`) `)
@ -127,7 +128,7 @@ releases:
if !strings.HasSuffix(filename, expectedFilename) { if !strings.HasSuffix(filename, expectedFilename) {
return nil, fmt.Errorf("unexpected filename: expected=%s, actual=%s", expectedFilename, filename) return nil, fmt.Errorf("unexpected filename: expected=%s, actual=%s", expectedFilename, filename)
} }
return defaultValuesYalm, nil return defaultValuesYaml, nil
} }
r := makeRenderer(fileReader, "staging") r := makeRenderer(fileReader, "staging")
@ -158,7 +159,7 @@ releases:
} }
func TestReadFromYaml_RenderTemplateWithValuesReferenceError(t *testing.T) { func TestReadFromYaml_RenderTemplateWithValuesReferenceError(t *testing.T) {
defaultValuesYalm := []byte("") defaultValuesYaml := []byte("")
yamlContent := []byte(` yamlContent := []byte(`
environments: environments:
@ -176,7 +177,7 @@ releases:
// make a reader that returns a simulated context // make a reader that returns a simulated context
fileReader := func(filename string) ([]byte, error) { fileReader := func(filename string) ([]byte, error) {
return defaultValuesYalm, nil return defaultValuesYaml, nil
} }
r := makeRenderer(fileReader, "staging") r := makeRenderer(fileReader, "staging")
@ -193,7 +194,7 @@ releases:
// This does not apply to .gotmpl files, which is a nice side-effect. // This does not apply to .gotmpl files, which is a nice side-effect.
func TestReadFromYaml_RenderTemplateWithGotmpl(t *testing.T) { func TestReadFromYaml_RenderTemplateWithGotmpl(t *testing.T) {
defaultValuesYalmGotmpl := []byte(` defaultValuesYamlGotmpl := []byte(`
releaseName: {{ readFile "nonIgnoredFile" }} releaseName: {{ readFile "nonIgnoredFile" }}
`) `)
@ -215,7 +216,7 @@ releases:
if strings.HasSuffix(filename, "nonIgnoredFile") { if strings.HasSuffix(filename, "nonIgnoredFile") {
return []byte("release-a"), nil return []byte("release-a"), nil
} }
return defaultValuesYalmGotmpl, nil return defaultValuesYamlGotmpl, nil
} }
r := makeRenderer(fileReader, "staging") r := makeRenderer(fileReader, "staging")
@ -232,3 +233,29 @@ releases:
t.Fatal("release should have been declared") t.Fatal("release should have been declared")
} }
} }
func TestReadFromYaml_RenderTemplateWithNamespace(t *testing.T) {
defaultValuesYaml := []byte(``)
yamlContent := []byte(`releases:
- name: {{ .Namespace }}-myrelease
chart: mychart
`)
// make a reader that returns a simulated context
fileReader := func(filename string) ([]byte, error) {
return defaultValuesYaml, nil
}
r := makeRenderer(fileReader, "staging")
yamlBuf, err := r.renderTemplate(yamlContent)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
var state state.HelmState
err = yaml.Unmarshal(yamlBuf.Bytes(), &state)
if state.Releases[0].Name != "namespace-myrelease" {
t.Errorf("release name should be namespace-myrelease")
}
}

View File

@ -61,7 +61,7 @@ wait_deploy_ready httpbin-httpbin
retry 5 "curl --fail $(minikube service --url --namespace=${test_ns} httpbin-httpbin)/status/200" retry 5 "curl --fail $(minikube service --url --namespace=${test_ns} httpbin-httpbin)/status/200"
[ ${retry_result} -eq 0 ] || fail "httpbin failed to return 200 OK" [ ${retry_result} -eq 0 ] || fail "httpbin failed to return 200 OK"
info "Deleting release" info "Deleting release"
${helmfile} -f ${dir}/happypath.yaml delete ${helmfile} -f ${dir}/happypath.yaml delete --auto-approve
${helm} status --namespace=${test_ns} httpbin &> /dev/null && fail "release should not exist anymore after a delete" ${helm} status --namespace=${test_ns} httpbin &> /dev/null && fail "release should not exist anymore after a delete"
test_pass "happypath" test_pass "happypath"

View File

@ -16,13 +16,15 @@ type templateFileRenderer struct {
type TemplateData struct { type TemplateData struct {
// Environment is accessible as `.Environment` from any template executed by the renderer // Environment is accessible as `.Environment` from any template executed by the renderer
Environment environment.Environment Environment environment.Environment
// Namespace is accessible as `.Namespace` from any non-values template executed by the renderer
Namespace string
} }
type FileRenderer interface { type FileRenderer interface {
RenderTemplateFileToBuffer(file string) (*bytes.Buffer, error) RenderTemplateFileToBuffer(file string) (*bytes.Buffer, error)
} }
func NewFileRenderer(readFile func(filename string) ([]byte, error), basePath string, env environment.Environment) *templateFileRenderer { func NewFileRenderer(readFile func(filename string) ([]byte, error), basePath string, env environment.Environment, namespace string) *templateFileRenderer {
return &templateFileRenderer{ return &templateFileRenderer{
ReadFile: readFile, ReadFile: readFile,
Context: &Context{ Context: &Context{
@ -31,6 +33,7 @@ func NewFileRenderer(readFile func(filename string) ([]byte, error), basePath st
}, },
Data: TemplateData{ Data: TemplateData{
Environment: env, Environment: env,
Namespace: namespace,
}, },
} }
} }

View File

@ -15,7 +15,7 @@ type renderer struct {
func NewRenderer(readFile func(filename string) ([]byte, error), basePath string, env environment.Environment) *renderer { func NewRenderer(readFile func(filename string) ([]byte, error), basePath string, env environment.Environment) *renderer {
return &renderer{ return &renderer{
readFile: readFile, readFile: readFile,
tmplFileRenderer: tmpl.NewFileRenderer(readFile, basePath, env), tmplFileRenderer: tmpl.NewFileRenderer(readFile, basePath, env, ""),
} }
} }