docker.go 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196
  1. package docker
  2. import (
  3. "errors"
  4. "fmt"
  5. "os"
  6. "path/filepath"
  7. "strings"
  8. "text/template"
  9. "github.com/logrusorgru/aurora"
  10. "github.com/spf13/cobra"
  11. "github.com/zeromicro/go-zero/tools/goctl/util"
  12. "github.com/zeromicro/go-zero/tools/goctl/util/env"
  13. "github.com/zeromicro/go-zero/tools/goctl/util/pathx"
  14. )
  15. const (
  16. dockerfileName = "Dockerfile"
  17. etcDir = "etc"
  18. yamlEtx = ".yaml"
  19. )
  20. // Docker describes a dockerfile
  21. type Docker struct {
  22. Chinese bool
  23. GoRelPath string
  24. GoFile string
  25. ExeFile string
  26. BaseImage string
  27. HasPort bool
  28. Port int
  29. Argument string
  30. Version string
  31. HasTimezone bool
  32. Timezone string
  33. }
  34. // dockerCommand provides the entry for goctl docker
  35. func dockerCommand(_ *cobra.Command, _ []string) (err error) {
  36. defer func() {
  37. if err == nil {
  38. fmt.Println(aurora.Green("Done."))
  39. }
  40. }()
  41. goFile := varStringGo
  42. home := varStringHome
  43. version := varStringVersion
  44. remote := varStringRemote
  45. branch := varStringBranch
  46. timezone := varStringTZ
  47. if len(remote) > 0 {
  48. repo, _ := util.CloneIntoGitHome(remote, branch)
  49. if len(repo) > 0 {
  50. home = repo
  51. }
  52. }
  53. if len(version) > 0 {
  54. version = version + "-"
  55. }
  56. if len(home) > 0 {
  57. pathx.RegisterGoctlHome(home)
  58. }
  59. if len(goFile) == 0 {
  60. return errors.New("-go can't be empty")
  61. }
  62. if !pathx.FileExists(goFile) {
  63. return fmt.Errorf("file %q not found", goFile)
  64. }
  65. base := varStringBase
  66. port := varIntPort
  67. if _, err := os.Stat(etcDir); os.IsNotExist(err) {
  68. return generateDockerfile(goFile, base, port, version, timezone)
  69. }
  70. cfg, err := findConfig(goFile, etcDir)
  71. if err != nil {
  72. return err
  73. }
  74. if err := generateDockerfile(goFile, base, port, version, timezone, "-f", "etc/"+cfg); err != nil {
  75. return err
  76. }
  77. projDir, ok := pathx.FindProjectPath(goFile)
  78. if ok {
  79. fmt.Printf("Hint: run \"docker build ...\" command in dir:\n %s\n", projDir)
  80. }
  81. return nil
  82. }
  83. func findConfig(file, dir string) (string, error) {
  84. var files []string
  85. err := filepath.Walk(dir, func(path string, f os.FileInfo, _ error) error {
  86. if !f.IsDir() {
  87. if filepath.Ext(f.Name()) == yamlEtx {
  88. files = append(files, f.Name())
  89. }
  90. }
  91. return nil
  92. })
  93. if err != nil {
  94. return "", err
  95. }
  96. if len(files) == 0 {
  97. return "", errors.New("no yaml file")
  98. }
  99. name := strings.TrimSuffix(filepath.Base(file), ".go")
  100. for _, f := range files {
  101. if strings.Index(f, name) == 0 {
  102. return f, nil
  103. }
  104. }
  105. return files[0], nil
  106. }
  107. func generateDockerfile(goFile, base string, port int, version, timezone string, args ...string) error {
  108. projPath, err := getFilePath(filepath.Dir(goFile))
  109. if err != nil {
  110. return err
  111. }
  112. if len(projPath) == 0 {
  113. projPath = "."
  114. }
  115. out, err := pathx.CreateIfNotExist(dockerfileName)
  116. if err != nil {
  117. return err
  118. }
  119. defer out.Close()
  120. text, err := pathx.LoadTemplate(category, dockerTemplateFile, dockerTemplate)
  121. if err != nil {
  122. return err
  123. }
  124. var builder strings.Builder
  125. for _, arg := range args {
  126. builder.WriteString(`, "` + arg + `"`)
  127. }
  128. absGoPath, err := filepath.Abs(goFile)
  129. if err != nil {
  130. return err
  131. }
  132. t := template.Must(template.New("dockerfile").Parse(text))
  133. return t.Execute(out, Docker{
  134. Chinese: env.InChina(),
  135. GoRelPath: projPath,
  136. GoFile: goFile,
  137. ExeFile: filepath.Base(absGoPath),
  138. BaseImage: base,
  139. HasPort: port > 0,
  140. Port: port,
  141. Argument: builder.String(),
  142. Version: version,
  143. HasTimezone: len(timezone) > 0,
  144. Timezone: timezone,
  145. })
  146. }
  147. func getFilePath(file string) (string, error) {
  148. wd, err := os.Getwd()
  149. if err != nil {
  150. return "", err
  151. }
  152. projPath, ok := pathx.FindGoModPath(filepath.Join(wd, file))
  153. if !ok {
  154. projPath, err = pathx.PathFromGoSrc()
  155. if err != nil {
  156. return "", errors.New("no go.mod found, or not in GOPATH")
  157. }
  158. // ignore project root directory for GOPATH mode
  159. pos := strings.IndexByte(projPath, os.PathSeparator)
  160. if pos >= 0 {
  161. projPath = projPath[pos+1:]
  162. }
  163. }
  164. return projPath, nil
  165. }