backend.go 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176
  1. package moby
  2. import (
  3. "context"
  4. "io"
  5. "time"
  6. "github.com/docker/api/context/cloud"
  7. "github.com/docker/docker/api/types"
  8. "github.com/docker/docker/api/types/container"
  9. "github.com/docker/docker/client"
  10. "github.com/pkg/errors"
  11. "github.com/docker/api/backend"
  12. "github.com/docker/api/compose"
  13. "github.com/docker/api/containers"
  14. "github.com/docker/api/errdefs"
  15. )
  16. type mobyService struct {
  17. apiClient *client.Client
  18. }
  19. func init() {
  20. backend.Register("moby", "moby", func(ctx context.Context) (backend.Service, error) {
  21. return New()
  22. })
  23. }
  24. // New returns a moby backend implementation
  25. func New() (backend.Service, error) {
  26. apiClient, err := client.NewClientWithOpts(client.FromEnv)
  27. if err != nil {
  28. return nil, err
  29. }
  30. return &mobyService{
  31. apiClient,
  32. }, nil
  33. }
  34. func (ms *mobyService) ContainerService() containers.Service {
  35. return ms
  36. }
  37. func (ms *mobyService) ComposeService() compose.Service {
  38. return nil
  39. }
  40. func (ms *mobyService) CloudService() cloud.Service {
  41. return nil
  42. }
  43. func (ms *mobyService) List(ctx context.Context, all bool) ([]containers.Container, error) {
  44. css, err := ms.apiClient.ContainerList(ctx, types.ContainerListOptions{
  45. All: all,
  46. })
  47. if err != nil {
  48. return []containers.Container{}, err
  49. }
  50. var result []containers.Container
  51. for _, container := range css {
  52. result = append(result, containers.Container{
  53. ID: container.ID,
  54. Image: container.Image,
  55. // TODO: `Status` is a human readable string ("Up 24 minutes"),
  56. // we need to return the `State` instead but first we need to
  57. // define an enum on the proto side with all the possible container
  58. // statuses. We also need to add a `Created` property on the gRPC side.
  59. Status: container.Status,
  60. Command: container.Command,
  61. Ports: getPorts(container.Ports),
  62. })
  63. }
  64. return result, nil
  65. }
  66. func (ms *mobyService) Run(ctx context.Context, r containers.ContainerConfig) error {
  67. create, err := ms.apiClient.ContainerCreate(ctx, &container.Config{
  68. Image: r.Image,
  69. Labels: r.Labels,
  70. }, nil, nil, r.ID)
  71. if err != nil {
  72. return err
  73. }
  74. return ms.apiClient.ContainerStart(ctx, create.ID, types.ContainerStartOptions{})
  75. }
  76. func (ms *mobyService) Stop(ctx context.Context, containerID string, timeout *uint32) error {
  77. var t *time.Duration
  78. if timeout != nil {
  79. timeoutValue := time.Duration(*timeout) * time.Second
  80. t = &timeoutValue
  81. }
  82. return ms.apiClient.ContainerStop(ctx, containerID, t)
  83. }
  84. func (ms *mobyService) Exec(ctx context.Context, name string, command string, reader io.Reader, writer io.Writer) error {
  85. cec, err := ms.apiClient.ContainerExecCreate(ctx, name, types.ExecConfig{
  86. Cmd: []string{command},
  87. Tty: true,
  88. AttachStderr: true,
  89. AttachStdin: true,
  90. AttachStdout: true,
  91. })
  92. if err != nil {
  93. return err
  94. }
  95. resp, err := ms.apiClient.ContainerExecAttach(ctx, cec.ID, types.ExecStartCheck{})
  96. if err != nil {
  97. return err
  98. }
  99. defer resp.Close()
  100. readChannel := make(chan error, 10)
  101. writeChannel := make(chan error, 10)
  102. go func() {
  103. _, err := io.Copy(writer, resp.Reader)
  104. readChannel <- err
  105. }()
  106. go func() {
  107. _, err := io.Copy(resp.Conn, reader)
  108. writeChannel <- err
  109. }()
  110. for {
  111. select {
  112. case err := <-readChannel:
  113. return err
  114. case err := <-writeChannel:
  115. return err
  116. }
  117. }
  118. }
  119. func (ms *mobyService) Logs(ctx context.Context, containerName string, request containers.LogsRequest) error {
  120. r, err := ms.apiClient.ContainerLogs(ctx, containerName, types.ContainerLogsOptions{
  121. ShowStdout: true,
  122. ShowStderr: true,
  123. })
  124. if err != nil {
  125. return err
  126. }
  127. _, err = io.Copy(request.Writer, r)
  128. return err
  129. }
  130. func (ms *mobyService) Delete(ctx context.Context, containerID string, force bool) error {
  131. err := ms.apiClient.ContainerRemove(ctx, containerID, types.ContainerRemoveOptions{
  132. Force: force,
  133. })
  134. if client.IsErrNotFound(err) {
  135. return errors.Wrapf(errdefs.ErrNotFound, "container %q", containerID)
  136. }
  137. return err
  138. }
  139. func getPorts(ports []types.Port) []containers.Port {
  140. result := []containers.Port{}
  141. for _, port := range ports {
  142. result = append(result, containers.Port{
  143. ContainerPort: uint32(port.PrivatePort),
  144. HostPort: uint32(port.PublicPort),
  145. HostIP: port.IP,
  146. Protocol: port.Type,
  147. })
  148. }
  149. return result
  150. }