feat: make environment context available (#832)

* feat: make environment context available

This feature adds the "{{.Environment.KubeContext}}" variable.

Discussion #829

Signed-off-by: sewieland <sebastian.wieland@iav.de>

* chore: fix tests which compare logging outputs

This commit adds an addtional space wherever needed to the expected log outputs due to the added "KubeContext" in the environment struct.

Discussion #829

Signed-off-by: Sebastian Wieland <wieland.s@mailbox.org>

* docs: added documentation for `Environment.KubeContext`

Discussion #829

Signed-off-by: Sebastian Wieland <wieland.s@mailbox.org>

* test: make sure the `Environment.KubeContext` is mapped out correctly

Discussion #829

Signed-off-by: Sebastian Wieland <wieland.s@mailbox.org>

---------

Signed-off-by: sewieland <sebastian.wieland@iav.de>
Signed-off-by: Sebastian Wieland <wieland.s@mailbox.org>
Co-authored-by: sewieland <sebastian.wieland@iav.de>
This commit is contained in:
SeWieland 2023-05-22 06:43:46 +02:00 committed by GitHub
parent c299cd930d
commit 8b3ad5b793
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
142 changed files with 964 additions and 843 deletions

View File

@ -3,13 +3,14 @@
- `Environment`: The information about the environment. This is set by the
`--environment` flag. It has several objects inside of it:
- `Environment.Name`: The name of the environment
- `Environment.KubeContext`: The kube context to be used by default for releases in the environment.
- `Values`: Values passed into the environment.
- `StateValues`: alias for `Values`.
- `Namespace`: The namespace to be released into
# release template built-in objects
it be used for the below tow cases:
it be used for the below two cases:
1. release definition
```
@ -56,6 +57,7 @@ releases:
- `Environment`: The information about the environment. This is set by the
`--environment` flag. It has several objects inside of it:
- `Environment.Name`: The name of the environment
- `Environment.KubeContext`: The kube context to be used by default for releases in the environment.
- `Chart`: The chart name for the release.
- `KubeContext`: The kube context to be used for the release
- `Namespace`: The namespace to be released into

View File

@ -1353,6 +1353,67 @@ releases:
For templating, imagine that you created a hook that generates a helm chart on-the-fly by running an external tool like ksonnet, kustomize, or your own template engine.
It will allow you to write your helm releases with any language you like, while still leveraging goodies provided by helm.
### Hooks, Kubectl and Environments
Hooks can also be used in combination with small tasks using `kubectl` directly,
e.g.: in order to install a custom storage class.
In the following example, a specific release depends on a custom storage class.
Further, all enviroments have a default kube context configured where releases are deployed into.
The `.Environment.KubeContext` is used in order to apply / remove the YAML to the correct context depending on the environment.
`environments.yaml`:
```yaml
environments:
dev:
values:
- ../values/default.yaml
- ../values/dev.yaml
kubeContext: dev-cluster
prod:
values:
- ../values/default.yaml
- ../values/prod.yaml
kubeContext: prod-cluster
```
`helmfile.yaml`:
```yaml
bases:
- ./environments.yaml
---
releases:
- name: myService
namespace: my-ns
installed: true
chart: mychart
version: "1.2.3"
values:
- ../services/my-service/values.yaml.gotmpl
hooks:
- events: ["presync"]
showlogs: true
command: "kubectl"
args:
- "apply"
- "-f"
- "./custom-storage-class.yaml"
- "--context"
- "{{`{{.Environment.KubeContext}}`}}"
- events: ["postuninstall"]
showlogs: true
command: "kubectl"
args:
- "delete"
- "-f"
- "./custom-storage-class.yaml"
- "--context"
- "{{`{{.Environment.KubeContext}}`}}"
```
### Global Hooks
In contrast to the per release hooks mentioned above these are run only once at the very beginning and end of the execution of a helmfile command and only the `prepare` and `cleanup` hooks are available respectively.

View File

@ -9,6 +9,7 @@ import (
type Environment struct {
Name string
KubeContext string
Values map[string]interface{}
Defaults map[string]interface{}
}
@ -19,6 +20,7 @@ var EmptyEnvironment Environment
func New(name string) *Environment {
return &Environment{
Name: name,
KubeContext: "",
Values: map[string]interface{}{},
Defaults: map[string]interface{}{},
}
@ -53,6 +55,7 @@ func (e Environment) DeepCopy() Environment {
return Environment{
Name: e.Name,
KubeContext: e.KubeContext,
Values: values,
Defaults: defaults,
}

View File

@ -252,7 +252,7 @@ func (c *StateCreator) loadEnvValues(st *HelmState, name string, failOnMissingEn
return nil, &UndefinedEnvError{msg: fmt.Sprintf("environment \"%s\" is not defined", name)}
}
newEnv := &environment.Environment{Name: name, Values: envVals}
newEnv := &environment.Environment{Name: name, Values: envVals, KubeContext: envSpec.KubeContext}
if ctxEnv != nil {
intCtxEnv := *ctxEnv

View File

@ -489,3 +489,58 @@ func TestReadFromYaml_Helmfiles_Selectors(t *testing.T) {
require.Equalf(t, test.helmfiles, st.Helmfiles, "for path %s", test.path)
}
}
func TestReadFromYaml_EnvironmentContext(t *testing.T) {
yamlFile := "/example/path/to/helmfile.yaml"
yamlContent := []byte(`environments:
production:
values: []
kubeContext: myCtx
releases:
- name: myrelease
namespace: mynamespace
chart: mychart
values:
- values.yaml.gotmpl
`)
valuesFile := "/example/path/to/values.yaml.gotmpl"
valuesContent := []byte(`envName: {{ .Environment.Name }}
envContext: {{ .Environment.KubeContext }}
releaseName: {{ .Release.Name }}
releaseContext: {{ .Release.KubeContext }}
`)
expectedValues := `envName: production
envContext: myCtx
releaseName: myrelease
releaseContext:
`
testFs := testhelper.NewTestFs(map[string]string{
valuesFile: string(valuesContent),
})
testFs.Cwd = "/example/path/to"
r := remote.NewRemote(logger, testFs.Cwd, testFs.ToFileSystem())
state, err := NewCreator(logger, testFs.ToFileSystem(), nil, nil, "", r, false, "").
ParseAndLoad(yamlContent, filepath.Dir(yamlFile), yamlFile, "production", true, nil, nil)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
release := state.Releases[0]
state.ApplyOverrides(&release)
actualValuesData, err := state.RenderReleaseValuesFileToBytes(&release, valuesFile)
if err != nil {
t.Errorf("unexpected error: %v", err)
}
actualValues := string(actualValuesData)
if !reflect.DeepEqual(expectedValues, actualValues) {
t.Errorf("unexpected values: expected=%v, actual=%v", expectedValues, actualValues)
}
}