1
0

docker_cp.go 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107
  1. /*
  2. Copyright 2023 Docker Compose CLI authors
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. package sync
  14. import (
  15. "context"
  16. "errors"
  17. "fmt"
  18. "io"
  19. "io/fs"
  20. "os"
  21. "github.com/compose-spec/compose-go/types"
  22. "github.com/docker/compose/v2/pkg/api"
  23. "github.com/sirupsen/logrus"
  24. )
  25. type ComposeClient interface {
  26. Exec(ctx context.Context, projectName string, options api.RunOptions) (int, error)
  27. Copy(ctx context.Context, projectName string, options api.CopyOptions) error
  28. }
  29. type DockerCopy struct {
  30. client ComposeClient
  31. projectName string
  32. infoWriter io.Writer
  33. }
  34. var _ Syncer = &DockerCopy{}
  35. func NewDockerCopy(projectName string, client ComposeClient, infoWriter io.Writer) *DockerCopy {
  36. return &DockerCopy{
  37. projectName: projectName,
  38. client: client,
  39. infoWriter: infoWriter,
  40. }
  41. }
  42. func (d *DockerCopy) Sync(ctx context.Context, service types.ServiceConfig, paths []PathMapping) error {
  43. var errs []error
  44. for i := range paths {
  45. if err := d.sync(ctx, service, paths[i]); err != nil {
  46. errs = append(errs, err)
  47. }
  48. }
  49. return errors.Join(errs...)
  50. }
  51. func (d *DockerCopy) sync(ctx context.Context, service types.ServiceConfig, pathMapping PathMapping) error {
  52. scale := 1
  53. if service.Deploy != nil && service.Deploy.Replicas != nil {
  54. scale = int(*service.Deploy.Replicas)
  55. }
  56. if fi, statErr := os.Stat(pathMapping.HostPath); statErr == nil {
  57. if fi.IsDir() {
  58. for i := 1; i <= scale; i++ {
  59. _, err := d.client.Exec(ctx, d.projectName, api.RunOptions{
  60. Service: service.Name,
  61. Command: []string{"mkdir", "-p", pathMapping.ContainerPath},
  62. Index: i,
  63. })
  64. if err != nil {
  65. logrus.Warnf("failed to create %q from %s: %v", pathMapping.ContainerPath, service.Name, err)
  66. }
  67. }
  68. fmt.Fprintf(d.infoWriter, "%s created\n", pathMapping.ContainerPath)
  69. } else {
  70. err := d.client.Copy(ctx, d.projectName, api.CopyOptions{
  71. Source: pathMapping.HostPath,
  72. Destination: fmt.Sprintf("%s:%s", service.Name, pathMapping.ContainerPath),
  73. })
  74. if err != nil {
  75. return err
  76. }
  77. fmt.Fprintf(d.infoWriter, "%s updated\n", pathMapping.ContainerPath)
  78. }
  79. } else if errors.Is(statErr, fs.ErrNotExist) {
  80. for i := 1; i <= scale; i++ {
  81. _, err := d.client.Exec(ctx, d.projectName, api.RunOptions{
  82. Service: service.Name,
  83. Command: []string{"rm", "-rf", pathMapping.ContainerPath},
  84. Index: i,
  85. })
  86. if err != nil {
  87. logrus.Warnf("failed to delete %q from %s: %v", pathMapping.ContainerPath, service.Name, err)
  88. }
  89. }
  90. fmt.Fprintf(d.infoWriter, "%s deleted from service\n", pathMapping.ContainerPath)
  91. }
  92. return nil
  93. }