packager.go 3.2 KB

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