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:
		
							parent
							
								
									c299cd930d
								
							
						
					
					
						commit
						8b3ad5b793
					
				|  | @ -3,13 +3,14 @@ | ||||||
| - `Environment`: The information about the environment. This is set by the | - `Environment`: The information about the environment. This is set by the | ||||||
|   `--environment` flag. It has several objects inside of it: |   `--environment` flag. It has several objects inside of it: | ||||||
|   - `Environment.Name`: The name of the environment |   - `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. | - `Values`: Values passed into the environment. | ||||||
| - `StateValues`: alias for `Values`. | - `StateValues`: alias for `Values`. | ||||||
| - `Namespace`: The namespace to be released into | - `Namespace`: The namespace to be released into | ||||||
| 
 | 
 | ||||||
| # release template built-in objects | # release template built-in objects | ||||||
| 
 | 
 | ||||||
| it be used for the below tow cases: | it be used for the below two cases: | ||||||
| 
 | 
 | ||||||
| 1. release definition | 1. release definition | ||||||
| ``` | ``` | ||||||
|  | @ -56,6 +57,7 @@ releases: | ||||||
| - `Environment`: The information about the environment. This is set by the | - `Environment`: The information about the environment. This is set by the | ||||||
|   `--environment` flag. It has several objects inside of it: |   `--environment` flag. It has several objects inside of it: | ||||||
|   - `Environment.Name`: The name of the environment |   - `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. | - `Chart`: The chart name for the release. | ||||||
| - `KubeContext`: The kube context to be used for the release | - `KubeContext`: The kube context to be used for the release | ||||||
| - `Namespace`: The namespace to be released into | - `Namespace`: The namespace to be released into | ||||||
|  |  | ||||||
|  | @ -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. | 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. | 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 | ### 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. | 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. | ||||||
|  |  | ||||||
|  | @ -9,6 +9,7 @@ import ( | ||||||
| 
 | 
 | ||||||
| type Environment struct { | type Environment struct { | ||||||
| 	Name        string | 	Name        string | ||||||
|  | 	KubeContext string | ||||||
| 	Values      map[string]interface{} | 	Values      map[string]interface{} | ||||||
| 	Defaults    map[string]interface{} | 	Defaults    map[string]interface{} | ||||||
| } | } | ||||||
|  | @ -19,6 +20,7 @@ var EmptyEnvironment Environment | ||||||
| func New(name string) *Environment { | func New(name string) *Environment { | ||||||
| 	return &Environment{ | 	return &Environment{ | ||||||
| 		Name:        name, | 		Name:        name, | ||||||
|  | 		KubeContext: "", | ||||||
| 		Values:      map[string]interface{}{}, | 		Values:      map[string]interface{}{}, | ||||||
| 		Defaults:    map[string]interface{}{}, | 		Defaults:    map[string]interface{}{}, | ||||||
| 	} | 	} | ||||||
|  | @ -53,6 +55,7 @@ func (e Environment) DeepCopy() Environment { | ||||||
| 
 | 
 | ||||||
| 	return Environment{ | 	return Environment{ | ||||||
| 		Name:        e.Name, | 		Name:        e.Name, | ||||||
|  | 		KubeContext: e.KubeContext, | ||||||
| 		Values:      values, | 		Values:      values, | ||||||
| 		Defaults:    defaults, | 		Defaults:    defaults, | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | @ -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)} | 		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 { | 	if ctxEnv != nil { | ||||||
| 		intCtxEnv := *ctxEnv | 		intCtxEnv := *ctxEnv | ||||||
|  |  | ||||||
|  | @ -489,3 +489,58 @@ func TestReadFromYaml_Helmfiles_Selectors(t *testing.T) { | ||||||
| 		require.Equalf(t, test.helmfiles, st.Helmfiles, "for path %s", test.path) | 		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) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue