packager.go 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162
  1. package serverless
  2. import (
  3. "archive/tar"
  4. "compress/gzip"
  5. "errors"
  6. "io"
  7. "io/fs"
  8. "os"
  9. "path"
  10. "strings"
  11. "time"
  12. "github.com/langgenius/dify-plugin-daemon/internal/core/plugin_manager/aws_manager/dockerfile"
  13. "github.com/langgenius/dify-plugin-daemon/internal/core/plugin_packager/decoder"
  14. "github.com/langgenius/dify-plugin-daemon/internal/utils/tmpfile"
  15. )
  16. type Packager struct {
  17. decoder decoder.PluginDecoder
  18. }
  19. func NewPackager(decoder decoder.PluginDecoder) *Packager {
  20. return &Packager{
  21. decoder: decoder,
  22. }
  23. }
  24. type dockerFileInfo struct {
  25. fs.FileInfo
  26. size int64
  27. }
  28. func (d *dockerFileInfo) Size() int64 {
  29. return d.size
  30. }
  31. func (d *dockerFileInfo) Name() string {
  32. return "Dockerfile"
  33. }
  34. func (d *dockerFileInfo) Mode() os.FileMode {
  35. return 0644
  36. }
  37. func (d *dockerFileInfo) ModTime() time.Time {
  38. return time.Now()
  39. }
  40. func (d *dockerFileInfo) IsDir() bool {
  41. return false
  42. }
  43. func (d *dockerFileInfo) Sys() any {
  44. return nil
  45. }
  46. // Pack takes a plugin and packs it into a tar file with dockerfile inside
  47. // returns a *os.File with the tar file
  48. func (p *Packager) Pack() (*os.File, error) {
  49. declaration, err := p.decoder.Manifest()
  50. if err != nil {
  51. return nil, err
  52. }
  53. // walk through the plugin directory and add it to a tar file
  54. // create a tmpfile
  55. tmpfile, cleanup, err := tmpfile.CreateTempFile("plugin-aws-tar-*")
  56. if err != nil {
  57. return nil, err
  58. }
  59. success := false
  60. defer func() {
  61. if !success {
  62. cleanup()
  63. }
  64. }()
  65. gzipWriter, err := gzip.NewWriterLevel(tmpfile, gzip.BestCompression)
  66. if err != nil {
  67. return nil, err
  68. }
  69. defer gzipWriter.Close()
  70. tarWriter := tar.NewWriter(gzipWriter)
  71. defer tarWriter.Close()
  72. if err := p.decoder.Walk(func(filename, dir string) error {
  73. if strings.ToLower(filename) == "dockerfile" {
  74. return errors.New("dockerfile is not allowed to be in the plugin directory")
  75. }
  76. fullFilename := path.Join(dir, filename)
  77. state, err := p.decoder.Stat(fullFilename)
  78. if err != nil {
  79. return err
  80. }
  81. tarHeader, err := tar.FileInfoHeader(state, fullFilename)
  82. if err != nil {
  83. return err
  84. }
  85. tarHeader.Name = fullFilename
  86. // write tar header
  87. if err := tarWriter.WriteHeader(tarHeader); err != nil {
  88. return err
  89. }
  90. // write file content
  91. fileReader, err := p.decoder.FileReader(fullFilename)
  92. if err != nil {
  93. return err
  94. }
  95. if _, err := io.Copy(tarWriter, fileReader); err != nil {
  96. fileReader.Close()
  97. return err
  98. }
  99. // release resources
  100. fileReader.Close()
  101. return nil
  102. }); err != nil {
  103. return nil, err
  104. }
  105. // add dockerfile
  106. dockerfile, err := dockerfile.GenerateDockerfile(&declaration)
  107. if err != nil {
  108. return nil, err
  109. }
  110. tarHeader, err := tar.FileInfoHeader(&dockerFileInfo{
  111. size: int64(len(dockerfile)),
  112. }, "Dockerfile")
  113. if err != nil {
  114. return nil, err
  115. }
  116. // create a fake dockerfile stat
  117. if err := tarWriter.WriteHeader(tarHeader); err != nil {
  118. return nil, err
  119. }
  120. if _, err := tarWriter.Write([]byte(dockerfile)); err != nil {
  121. return nil, err
  122. }
  123. // close writers to flush data
  124. tarWriter.Close()
  125. gzipWriter.Close()
  126. tmpfile.Seek(0, io.SeekStart)
  127. success = true
  128. return tmpfile, nil
  129. }