docker.go 4.0 KB

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