PageRenderTime 46ms CodeModel.GetById 20ms RepoModel.GetById 1ms app.codeStats 0ms

/test/pkg/agent/workspace/main.go

https://gitlab.com/geropl/gitpod
Go | 156 lines | 126 code | 19 blank | 11 comment | 36 complexity | 37cd9d5fde324e17d90a0513381ca7aa MD5 | raw file
  1. // Copyright (c) 2020 Gitpod GmbH. All rights reserved.
  2. // Licensed under the GNU Affero General Public License (AGPL).
  3. // See License-AGPL.txt in the project root for license information.
  4. package main
  5. import (
  6. "bytes"
  7. "fmt"
  8. "io"
  9. "os"
  10. "os/exec"
  11. "strconv"
  12. "strings"
  13. "github.com/prometheus/procfs"
  14. "golang.org/x/sys/unix"
  15. "golang.org/x/xerrors"
  16. "github.com/gitpod-io/gitpod/test/pkg/agent/workspace/api"
  17. "github.com/gitpod-io/gitpod/test/pkg/integration"
  18. )
  19. func main() {
  20. err := enterSupervisorNamespaces()
  21. if err != nil {
  22. panic(fmt.Sprintf("enterSupervisorNamespaces: %v", err))
  23. }
  24. integration.ServeAgent(new(WorkspaceAgent))
  25. }
  26. func enterSupervisorNamespaces() error {
  27. if os.Getenv("AGENT_IN_RING2") != "" {
  28. return nil
  29. }
  30. nsenter, err := exec.LookPath("nsenter")
  31. if err != nil {
  32. return xerrors.Errorf("cannot find nsenter")
  33. }
  34. // This agent expectes to be called using the workspacekit lift (i.e. in ring1).
  35. // We then enter the PID and mount namespace of supervisor.
  36. // First, we need to find the supervisor process
  37. proc, err := procfs.NewFS("/proc")
  38. if err != nil {
  39. return err
  40. }
  41. procs, err := proc.AllProcs()
  42. if err != nil {
  43. return err
  44. }
  45. var supervisorPID int
  46. for _, p := range procs {
  47. cmd, _ := p.CmdLine()
  48. for _, c := range cmd {
  49. if strings.HasSuffix(c, "supervisor") {
  50. supervisorPID = p.PID
  51. break
  52. }
  53. }
  54. if supervisorPID != 0 {
  55. break
  56. }
  57. }
  58. if supervisorPID == 0 {
  59. return xerrors.Errorf("no supervisor process found")
  60. }
  61. // Then we copy ourselves to a location that we can access from the supervisor NS
  62. self, err := os.Executable()
  63. if err != nil {
  64. return err
  65. }
  66. fn := fmt.Sprintf("/proc/%d/root/agent", supervisorPID)
  67. dst, err := os.OpenFile(fn, os.O_CREATE|os.O_WRONLY, 0755)
  68. if err != nil {
  69. return err
  70. }
  71. selfFD, err := os.Open(self)
  72. if err != nil {
  73. return err
  74. }
  75. defer selfFD.Close()
  76. _, err = io.Copy(dst, selfFD)
  77. if err != nil {
  78. return xerrors.Errorf("error copying agent: %w", err)
  79. }
  80. return unix.Exec(nsenter, append([]string{nsenter, "-t", strconv.Itoa(supervisorPID), "-m", "-p", "/agent"}, os.Args[1:]...), append(os.Environ(), "AGENT_IN_RING2=true"))
  81. }
  82. // WorkspaceAgent provides ingteration test services from within a workspace
  83. type WorkspaceAgent struct {
  84. }
  85. // ListDir lists a directory's content
  86. func (*WorkspaceAgent) ListDir(req *api.ListDirRequest, resp *api.ListDirResponse) error {
  87. dc, err := os.ReadDir(req.Dir)
  88. if err != nil {
  89. return err
  90. }
  91. *resp = api.ListDirResponse{}
  92. for _, c := range dc {
  93. resp.Files = append(resp.Files, c.Name())
  94. }
  95. return nil
  96. }
  97. // WriteFile writes a file in the workspace
  98. func (*WorkspaceAgent) WriteFile(req *api.WriteFileRequest, resp *api.WriteFileResponse) (err error) {
  99. err = os.WriteFile(req.Path, req.Content, req.Mode)
  100. if err != nil {
  101. return
  102. }
  103. *resp = api.WriteFileResponse{}
  104. return
  105. }
  106. // Exec executes a command in the workspace
  107. func (*WorkspaceAgent) Exec(req *api.ExecRequest, resp *api.ExecResponse) (err error) {
  108. cmd := exec.Command(req.Command, req.Args...)
  109. cmd.Env = os.Environ()
  110. cmd.Env = append(cmd.Env, req.Env...)
  111. if req.Dir != "" {
  112. cmd.Dir = req.Dir
  113. }
  114. var (
  115. stdout bytes.Buffer
  116. stderr bytes.Buffer
  117. )
  118. cmd.Stdout = &stdout
  119. cmd.Stderr = &stderr
  120. err = cmd.Run()
  121. var rc int
  122. if err != nil {
  123. exitError, ok := err.(*exec.ExitError)
  124. if !ok {
  125. fullCommand := strings.Join(append([]string{req.Command}, req.Args...), " ")
  126. return xerrors.Errorf("%s: %w", fullCommand, err)
  127. }
  128. rc = exitError.ExitCode()
  129. err = nil
  130. }
  131. *resp = api.ExecResponse{
  132. ExitCode: rc,
  133. Stdout: stdout.String(),
  134. Stderr: stderr.String(),
  135. }
  136. return
  137. }