fix: Various helmfile commands should not leave temp values files (#520)
Fixes #504
This commit is contained in:
		
							parent
							
								
									f5e565ea3e
								
							
						
					
					
						commit
						283dac1531
					
				|  | @ -114,6 +114,18 @@ func (c *creator) CreateFromYaml(content []byte, file string, env string) (*Helm | |||
| 	state.Env = *e | ||||
| 
 | ||||
| 	state.readFile = c.readFile | ||||
| 	state.removeFile = os.Remove | ||||
| 	state.fileExists = func(path string) (bool, error) { | ||||
| 		_, err := os.Stat(path) | ||||
| 
 | ||||
| 		if err != nil { | ||||
| 			if os.IsNotExist(err) { | ||||
| 				return false, nil | ||||
| 			} | ||||
| 			return false, err | ||||
| 		} | ||||
| 		return true, nil | ||||
| 	} | ||||
| 
 | ||||
| 	return &state, nil | ||||
| } | ||||
|  |  | |||
|  | @ -47,6 +47,9 @@ type HelmState struct { | |||
| 
 | ||||
| 	readFile func(string) ([]byte, error) | ||||
| 
 | ||||
| 	removeFile func(string) error | ||||
| 	fileExists func(string) (bool, error) | ||||
| 
 | ||||
| 	runner helmexec.Runner | ||||
| } | ||||
| 
 | ||||
|  | @ -233,8 +236,11 @@ func (st *HelmState) prepareSyncReleases(helm helmexec.Interface, additionalValu | |||
| 						errs = append(errs, &ReleaseError{release, err}) | ||||
| 					} | ||||
| 
 | ||||
| 					if _, err := os.Stat(valfile); os.IsNotExist(err) { | ||||
| 					ok, err := st.fileExists(valfile) | ||||
| 					if err != nil { | ||||
| 						errs = append(errs, &ReleaseError{release, err}) | ||||
| 					} else if !ok { | ||||
| 						errs = append(errs, &ReleaseError{release, fmt.Errorf("file does not exist: %s", valfile)}) | ||||
| 					} | ||||
| 					flags = append(flags, "--values", valfile) | ||||
| 				} | ||||
|  | @ -749,7 +755,7 @@ func (st *HelmState) Clean() []error { | |||
| 
 | ||||
| 	for _, release := range st.Releases { | ||||
| 		for _, value := range release.generatedValues { | ||||
| 			err := os.Remove(value) | ||||
| 			err := st.removeFile(value) | ||||
| 			if err != nil { | ||||
| 				errs = append(errs, err) | ||||
| 			} | ||||
|  | @ -1068,23 +1074,25 @@ func (st *HelmState) RenderValuesFileToBytes(path string) ([]byte, error) { | |||
| 	return r.RenderToBytes(path) | ||||
| } | ||||
| 
 | ||||
| func (st *HelmState) namespaceAndValuesFlags(helm helmexec.Interface, release *ReleaseSpec) ([]string, error) { | ||||
| 	flags := []string{} | ||||
| 	if release.Namespace != "" { | ||||
| 		flags = append(flags, "--namespace", release.Namespace) | ||||
| 	} | ||||
| 	for _, value := range release.Values { | ||||
| func (st *HelmState) generateTemporaryValuesFiles(values []interface{}, missingFileHandler *string) ([]string, error) { | ||||
| 	generatedFiles := []string{} | ||||
| 
 | ||||
| 	for _, value := range values { | ||||
| 		switch typedValue := value.(type) { | ||||
| 		case string: | ||||
| 			path := st.normalizePath(release.ValuesPathPrefix + typedValue) | ||||
| 			path := st.normalizePath(typedValue) | ||||
| 
 | ||||
| 			if _, err := os.Stat(path); os.IsNotExist(err) { | ||||
| 				if release.MissingFileHandler == nil || *release.MissingFileHandler == "Error" { | ||||
| 			ok, err := st.fileExists(path) | ||||
| 			if err != nil { | ||||
| 				return nil, err | ||||
| 				} else if *release.MissingFileHandler == "Warn" { | ||||
| 			} | ||||
| 			if !ok { | ||||
| 				if missingFileHandler == nil || *missingFileHandler == "Error" { | ||||
| 					return nil, fmt.Errorf("file does not exist: %s", path) | ||||
| 				} else if *missingFileHandler == "Warn" { | ||||
| 					st.logger.Warnf("skipping missing values file \"%s\"", path) | ||||
| 					continue | ||||
| 				} else if *release.MissingFileHandler == "Info" { | ||||
| 				} else if *missingFileHandler == "Info" { | ||||
| 					st.logger.Infof("skipping missing values file \"%s\"", path) | ||||
| 					continue | ||||
| 				} else { | ||||
|  | @ -1108,8 +1116,7 @@ func (st *HelmState) namespaceAndValuesFlags(helm helmexec.Interface, release *R | |||
| 				return nil, fmt.Errorf("failed to write %s: %v", valfile.Name(), err) | ||||
| 			} | ||||
| 			st.logger.Debugf("successfully generated the value file at %s. produced:\n%s", path, string(yamlBytes)) | ||||
| 			flags = append(flags, "--values", valfile.Name()) | ||||
| 
 | ||||
| 			generatedFiles = append(generatedFiles, valfile.Name()) | ||||
| 		case map[interface{}]interface{}: | ||||
| 			valfile, err := ioutil.TempFile("", "values") | ||||
| 			if err != nil { | ||||
|  | @ -1121,14 +1128,49 @@ func (st *HelmState) namespaceAndValuesFlags(helm helmexec.Interface, release *R | |||
| 			if err := encoder.Encode(typedValue); err != nil { | ||||
| 				return nil, err | ||||
| 			} | ||||
| 			release.generatedValues = append(release.generatedValues, valfile.Name()) | ||||
| 			flags = append(flags, "--values", valfile.Name()) | ||||
| 			generatedFiles = append(generatedFiles, valfile.Name()) | ||||
| 		default: | ||||
| 			return nil, fmt.Errorf("unexpected type of values entry: %T", typedValue) | ||||
| 		} | ||||
| 	} | ||||
| 	return generatedFiles, nil | ||||
| } | ||||
| 
 | ||||
| func (st *HelmState) namespaceAndValuesFlags(helm helmexec.Interface, release *ReleaseSpec) ([]string, error) { | ||||
| 	flags := []string{} | ||||
| 	if release.Namespace != "" { | ||||
| 		flags = append(flags, "--namespace", release.Namespace) | ||||
| 	} | ||||
| 
 | ||||
| 	values := []interface{}{} | ||||
| 	for _, v := range release.Values { | ||||
| 		switch typedValue := v.(type) { | ||||
| 		case string: | ||||
| 			path := st.normalizePath(release.ValuesPathPrefix + typedValue) | ||||
| 			values = append(values, path) | ||||
| 		default: | ||||
| 			values = append(values, v) | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	generatedFiles, err := st.generateTemporaryValuesFiles(values, release.MissingFileHandler) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	for _, f := range generatedFiles { | ||||
| 		flags = append(flags, "--values", f) | ||||
| 	} | ||||
| 
 | ||||
| 	release.generatedValues = append(release.generatedValues, generatedFiles...) | ||||
| 
 | ||||
| 	for _, value := range release.Secrets { | ||||
| 		path := st.normalizePath(release.ValuesPathPrefix + value) | ||||
| 		if _, err := os.Stat(path); os.IsNotExist(err) { | ||||
| 		ok, err := st.fileExists(path) | ||||
| 		if err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
| 		if !ok { | ||||
| 			if release.MissingFileHandler == nil || *release.MissingFileHandler == "Error" { | ||||
| 				return nil, err | ||||
| 			} else if *release.MissingFileHandler == "Warn" { | ||||
|  |  | |||
|  | @ -8,6 +8,7 @@ import ( | |||
| 	"errors" | ||||
| 	"strings" | ||||
| 
 | ||||
| 	"fmt" | ||||
| 	"github.com/roboll/helmfile/helmexec" | ||||
| ) | ||||
| 
 | ||||
|  | @ -883,6 +884,95 @@ func TestHelmState_SyncReleases(t *testing.T) { | |||
| 	} | ||||
| } | ||||
| 
 | ||||
| func TestHelmState_SyncReleasesCleanup(t *testing.T) { | ||||
| 	tests := []struct { | ||||
| 		name                    string | ||||
| 		releases                []ReleaseSpec | ||||
| 		helm                    *mockHelmExec | ||||
| 		expectedNumRemovedFiles int | ||||
| 	}{ | ||||
| 		{ | ||||
| 			name: "normal release", | ||||
| 			releases: []ReleaseSpec{ | ||||
| 				{ | ||||
| 					Name:  "releaseName", | ||||
| 					Chart: "foo", | ||||
| 				}, | ||||
| 			}, | ||||
| 			helm:                    &mockHelmExec{}, | ||||
| 			expectedNumRemovedFiles: 0, | ||||
| 		}, | ||||
| 		{ | ||||
| 			name: "inline values", | ||||
| 			releases: []ReleaseSpec{ | ||||
| 				{ | ||||
| 					Name:  "releaseName", | ||||
| 					Chart: "foo", | ||||
| 					Values: []interface{}{ | ||||
| 						map[interface{}]interface{}{ | ||||
| 							"someList": "a,b,c", | ||||
| 						}, | ||||
| 					}, | ||||
| 				}, | ||||
| 			}, | ||||
| 			helm:                    &mockHelmExec{}, | ||||
| 			expectedNumRemovedFiles: 1, | ||||
| 		}, | ||||
| 		{ | ||||
| 			name: "inline values and values file", | ||||
| 			releases: []ReleaseSpec{ | ||||
| 				{ | ||||
| 					Name:  "releaseName", | ||||
| 					Chart: "foo", | ||||
| 					Values: []interface{}{ | ||||
| 						map[interface{}]interface{}{ | ||||
| 							"someList": "a,b,c", | ||||
| 						}, | ||||
| 						"someFile", | ||||
| 					}, | ||||
| 				}, | ||||
| 			}, | ||||
| 			helm:                    &mockHelmExec{}, | ||||
| 			expectedNumRemovedFiles: 2, | ||||
| 		}, | ||||
| 	} | ||||
| 	for _, tt := range tests { | ||||
| 		t.Run(tt.name, func(t *testing.T) { | ||||
| 			numRemovedFiles := 0 | ||||
| 			state := &HelmState{ | ||||
| 				Releases: tt.releases, | ||||
| 				logger:   logger, | ||||
| 				readFile: func(f string) ([]byte, error) { | ||||
| 					if f != "someFile" { | ||||
| 						return nil, fmt.Errorf("unexpected file to read: %s", f) | ||||
| 					} | ||||
| 					someFileContent := []byte(`foo: bar | ||||
| `) | ||||
| 					return someFileContent, nil | ||||
| 				}, | ||||
| 				removeFile: func(f string) error { | ||||
| 					numRemovedFiles += 1 | ||||
| 					return nil | ||||
| 				}, | ||||
| 				fileExists: func(f string) (bool, error) { | ||||
| 					return true, nil | ||||
| 				}, | ||||
| 			} | ||||
| 			if errs := state.SyncReleases(tt.helm, []string{}, 1); errs != nil && len(errs) > 0 { | ||||
| 				t.Errorf("unexpected errors: %v", errs) | ||||
| 			} | ||||
| 
 | ||||
| 			if errs := state.Clean(); errs != nil && len(errs) > 0 { | ||||
| 				t.Errorf("unexpected errors: %v", errs) | ||||
| 			} | ||||
| 
 | ||||
| 			if numRemovedFiles != tt.expectedNumRemovedFiles { | ||||
| 				t.Errorf("unexpected number of removed files: expected %d, got %d", tt.expectedNumRemovedFiles, numRemovedFiles) | ||||
| 			} | ||||
| 		}) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func TestHelmState_UpdateDeps(t *testing.T) { | ||||
| 	state := &HelmState{ | ||||
| 		basePath: "/src", | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue