envfile.go 2.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081
  1. package opts
  2. import (
  3. "bufio"
  4. "bytes"
  5. "fmt"
  6. "os"
  7. "strings"
  8. "unicode"
  9. "unicode/utf8"
  10. )
  11. // ParseEnvFile reads a file with environment variables enumerated by lines
  12. //
  13. // ``Environment variable names used by the utilities in the Shell and
  14. // Utilities volume of IEEE Std 1003.1-2001 consist solely of uppercase
  15. // letters, digits, and the '_' (underscore) from the characters defined in
  16. // Portable Character Set and do not begin with a digit. *But*, other
  17. // characters may be permitted by an implementation; applications shall
  18. // tolerate the presence of such names.''
  19. // -- http://pubs.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap08.html
  20. //
  21. // As of #16585, it's up to application inside docker to validate or not
  22. // environment variables, that's why we just strip leading whitespace and
  23. // nothing more.
  24. func ParseEnvFile(filename string) ([]string, error) {
  25. fh, err := os.Open(filename)
  26. if err != nil {
  27. return []string{}, err
  28. }
  29. defer fh.Close()
  30. lines := []string{}
  31. scanner := bufio.NewScanner(fh)
  32. currentLine := 0
  33. utf8bom := []byte{0xEF, 0xBB, 0xBF}
  34. for scanner.Scan() {
  35. scannedBytes := scanner.Bytes()
  36. if !utf8.Valid(scannedBytes) {
  37. return []string{}, fmt.Errorf("env file %s contains invalid utf8 bytes at line %d: %v", filename, currentLine+1, scannedBytes)
  38. }
  39. // We trim UTF8 BOM
  40. if currentLine == 0 {
  41. scannedBytes = bytes.TrimPrefix(scannedBytes, utf8bom)
  42. }
  43. // trim the line from all leading whitespace first
  44. line := strings.TrimLeftFunc(string(scannedBytes), unicode.IsSpace)
  45. currentLine++
  46. // line is not empty, and not starting with '#'
  47. if len(line) > 0 && !strings.HasPrefix(line, "#") {
  48. data := strings.SplitN(line, "=", 2)
  49. // trim the front of a variable, but nothing else
  50. variable := strings.TrimLeft(data[0], whiteSpaces)
  51. if strings.ContainsAny(variable, whiteSpaces) {
  52. return []string{}, ErrBadEnvVariable{fmt.Sprintf("variable '%s' has white spaces", variable)}
  53. }
  54. if len(data) > 1 {
  55. // pass the value through, no trimming
  56. lines = append(lines, fmt.Sprintf("%s=%s", variable, data[1]))
  57. } else {
  58. // if only a pass-through variable is given, clean it up.
  59. lines = append(lines, fmt.Sprintf("%s=%s", strings.TrimSpace(line), os.Getenv(line)))
  60. }
  61. }
  62. }
  63. return lines, scanner.Err()
  64. }
  65. var whiteSpaces = " \t"
  66. // ErrBadEnvVariable typed error for bad environment variable
  67. type ErrBadEnvVariable struct {
  68. msg string
  69. }
  70. func (e ErrBadEnvVariable) Error() string {
  71. return fmt.Sprintf("poorly formatted environment: %s", e.msg)
  72. }