project.go 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165
  1. package compose
  2. import (
  3. "fmt"
  4. "io/ioutil"
  5. "os"
  6. "path/filepath"
  7. "regexp"
  8. "strings"
  9. "github.com/compose-spec/compose-go/loader"
  10. "github.com/compose-spec/compose-go/types"
  11. "github.com/sirupsen/logrus"
  12. )
  13. type Project struct {
  14. types.Config
  15. projectDir string
  16. Name string `yaml:"-" json:"-"`
  17. }
  18. func NewProject(config types.ConfigDetails, name string) (*Project, error) {
  19. model, err := loader.Load(config)
  20. if err != nil {
  21. return nil, err
  22. }
  23. p := Project{
  24. Config: *model,
  25. projectDir: config.WorkingDir,
  26. Name: name,
  27. }
  28. return &p, nil
  29. }
  30. // projectFromOptions load a compose project based on command line options
  31. func ProjectFromOptions(options *ProjectOptions) (*Project, error) {
  32. configPath, err := getConfigPathFromOptions(options)
  33. if err != nil {
  34. return nil, err
  35. }
  36. name := options.Name
  37. if name == "" {
  38. name = os.Getenv("COMPOSE_PROJECT_NAME")
  39. }
  40. workingDir := filepath.Dir(configPath[0])
  41. if name == "" {
  42. r := regexp.MustCompile(`[^a-z0-9\\-_]+`)
  43. name = r.ReplaceAllString(strings.ToLower(filepath.Base(workingDir)), "")
  44. }
  45. configs, err := parseConfigs(configPath)
  46. if err != nil {
  47. return nil, err
  48. }
  49. return NewProject(types.ConfigDetails{
  50. WorkingDir: workingDir,
  51. ConfigFiles: configs,
  52. Environment: environment(),
  53. }, name)
  54. }
  55. func getConfigPathFromOptions(options *ProjectOptions) ([]string, error) {
  56. paths := []string{}
  57. pwd, err := os.Getwd()
  58. if err != nil {
  59. return nil, err
  60. }
  61. if len(options.ConfigPaths) != 0 {
  62. for _, f := range options.ConfigPaths {
  63. if f == "-" {
  64. paths = append(paths, f)
  65. continue
  66. }
  67. if !filepath.IsAbs(f) {
  68. f = filepath.Join(pwd, f)
  69. }
  70. if _, err := os.Stat(f); err != nil {
  71. return nil, err
  72. }
  73. paths = append(paths, f)
  74. }
  75. return paths, nil
  76. }
  77. sep := os.Getenv("COMPOSE_FILE_SEPARATOR")
  78. if sep == "" {
  79. sep = string(os.PathListSeparator)
  80. }
  81. f := os.Getenv("COMPOSE_FILE")
  82. if f != "" {
  83. return strings.Split(f, sep), nil
  84. }
  85. for {
  86. candidates := []string{}
  87. for _, n := range SupportedFilenames {
  88. f := filepath.Join(pwd, n)
  89. if _, err := os.Stat(f); err == nil {
  90. candidates = append(candidates, f)
  91. }
  92. }
  93. if len(candidates) > 0 {
  94. winner := candidates[0]
  95. if len(candidates) > 1 {
  96. logrus.Warnf("Found multiple config files with supported names: %s", strings.Join(candidates, ", "))
  97. logrus.Warnf("Using %s\n", winner)
  98. }
  99. return []string{winner}, nil
  100. }
  101. parent := filepath.Dir(pwd)
  102. if parent == pwd {
  103. return nil, fmt.Errorf("Can't find a suitable configuration file in this directory or any parent. Are you in the right directory?")
  104. }
  105. pwd = parent
  106. }
  107. }
  108. var SupportedFilenames = []string{"compose.yaml", "compose.yml", "docker-compose.yml", "docker-compose.yaml"}
  109. func parseConfigs(configPaths []string) ([]types.ConfigFile, error) {
  110. files := []types.ConfigFile{}
  111. for _, f := range configPaths {
  112. var (
  113. b []byte
  114. err error
  115. )
  116. if f == "-" {
  117. b, err = ioutil.ReadAll(os.Stdin)
  118. } else {
  119. if _, err := os.Stat(f); err != nil {
  120. return nil, err
  121. }
  122. b, err = ioutil.ReadFile(f)
  123. }
  124. if err != nil {
  125. return nil, err
  126. }
  127. config, err := loader.ParseYAML(b)
  128. if err != nil {
  129. return nil, err
  130. }
  131. files = append(files, types.ConfigFile{Filename: f, Config: config})
  132. }
  133. return files, nil
  134. }
  135. func environment() map[string]string {
  136. return getAsEqualsMap(os.Environ())
  137. }
  138. // getAsEqualsMap split key=value formatted strings into a key : value map
  139. func getAsEqualsMap(em []string) map[string]string {
  140. m := make(map[string]string)
  141. for _, v := range em {
  142. kv := strings.SplitN(v, "=", 2)
  143. m[kv[0]] = kv[1]
  144. }
  145. return m
  146. }