packager.go 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166
  1. package aws_manager
  2. import (
  3. "archive/tar"
  4. "compress/gzip"
  5. "errors"
  6. "fmt"
  7. "io"
  8. "io/fs"
  9. "os"
  10. "path"
  11. "strings"
  12. "time"
  13. "github.com/langgenius/dify-plugin-daemon/internal/core/plugin_manager/aws_manager/dockerfile"
  14. "github.com/langgenius/dify-plugin-daemon/internal/core/plugin_packager/decoder"
  15. "github.com/langgenius/dify-plugin-daemon/internal/types/entities/plugin_entities"
  16. "github.com/langgenius/dify-plugin-daemon/internal/utils/tmpfile"
  17. )
  18. type Packager struct {
  19. runtime plugin_entities.PluginRuntimeInterface
  20. decoder decoder.PluginDecoder
  21. }
  22. func NewPackager(runtime plugin_entities.PluginRuntimeInterface, decoder decoder.PluginDecoder) *Packager {
  23. return &Packager{
  24. runtime: runtime,
  25. decoder: decoder,
  26. }
  27. }
  28. type dockerFileInfo struct {
  29. fs.FileInfo
  30. size int64
  31. }
  32. func (d *dockerFileInfo) Size() int64 {
  33. return d.size
  34. }
  35. func (d *dockerFileInfo) Name() string {
  36. return "Dockerfile"
  37. }
  38. func (d *dockerFileInfo) Mode() os.FileMode {
  39. return 0644
  40. }
  41. func (d *dockerFileInfo) ModTime() time.Time {
  42. return time.Now()
  43. }
  44. func (d *dockerFileInfo) IsDir() bool {
  45. return false
  46. }
  47. func (d *dockerFileInfo) Sys() any {
  48. return nil
  49. }
  50. // Pack takes a plugin and packs it into a tar file with dockerfile inside
  51. // returns a *os.File with the tar file
  52. func (p *Packager) Pack() (*os.File, error) {
  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. gzip_writer, err := gzip.NewWriterLevel(tmpfile, gzip.BestCompression)
  66. if err != nil {
  67. return nil, err
  68. }
  69. defer gzip_writer.Close()
  70. tar_writer := tar.NewWriter(gzip_writer)
  71. defer tar_writer.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. full_filename := path.Join(dir, filename)
  77. state, err := p.decoder.Stat(full_filename)
  78. if err != nil {
  79. return err
  80. }
  81. if state.Size() > 1024*1024*10 {
  82. // 10MB, 1 single file is too large
  83. return fmt.Errorf("file size is too large: %s, max 10MB", full_filename)
  84. }
  85. tar_header, err := tar.FileInfoHeader(state, full_filename)
  86. if err != nil {
  87. return err
  88. }
  89. tar_header.Name = filename
  90. // write tar header
  91. if err := tar_writer.WriteHeader(tar_header); err != nil {
  92. return err
  93. }
  94. // write file content
  95. file_reader, err := p.decoder.FileReader(full_filename)
  96. if err != nil {
  97. return err
  98. }
  99. if _, err := io.Copy(tar_writer, file_reader); err != nil {
  100. file_reader.Close()
  101. return err
  102. }
  103. // release resources
  104. file_reader.Close()
  105. return nil
  106. }); err != nil {
  107. return nil, err
  108. }
  109. // add dockerfile
  110. dockerfile, err := dockerfile.GenerateDockerfile(p.runtime.Configuration())
  111. if err != nil {
  112. return nil, err
  113. }
  114. tar_header, err := tar.FileInfoHeader(&dockerFileInfo{
  115. size: int64(len(dockerfile)),
  116. }, "Dockerfile")
  117. if err != nil {
  118. return nil, err
  119. }
  120. // create a fake dockerfile stat
  121. if err := tar_writer.WriteHeader(tar_header); err != nil {
  122. return nil, err
  123. }
  124. if _, err := tar_writer.Write([]byte(dockerfile)); err != nil {
  125. return nil, err
  126. }
  127. // close writers to flush data
  128. tar_writer.Close()
  129. gzip_writer.Close()
  130. tmpfile.Seek(0, io.SeekStart)
  131. success = true
  132. return tmpfile, nil
  133. }