Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
gitpod-io
GitHub Repository: gitpod-io/gitpod
Path: blob/main/test/pkg/agent/workspace/main.go
2499 views
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
5
package main
6
7
import (
8
"bytes"
9
"fmt"
10
"io"
11
"net/http"
12
"os"
13
"os/exec"
14
"strconv"
15
"strings"
16
"time"
17
18
"github.com/prometheus/procfs"
19
"golang.org/x/sys/unix"
20
"golang.org/x/xerrors"
21
22
"github.com/gitpod-io/gitpod/test/pkg/agent/workspace/api"
23
"github.com/gitpod-io/gitpod/test/pkg/integration"
24
)
25
26
func main() {
27
done := make(chan struct{})
28
go func() {
29
mux := http.NewServeMux()
30
mux.Handle("/shutdown", shugtdownHandler(done))
31
_ = http.ListenAndServe(":8080", mux)
32
}()
33
34
err := enterSupervisorNamespaces()
35
if err != nil {
36
panic(fmt.Sprintf("enterSupervisorNamespaces: %v", err))
37
}
38
39
integration.ServeAgent(done, new(WorkspaceAgent))
40
}
41
42
func shugtdownHandler(done chan struct{}) http.HandlerFunc {
43
return func(w http.ResponseWriter, _ *http.Request) {
44
close(done)
45
w.Write([]byte("shutdown"))
46
w.WriteHeader(http.StatusOK)
47
}
48
}
49
50
func enterSupervisorNamespaces() error {
51
if os.Getenv("AGENT_IN_RING2") != "" {
52
return nil
53
}
54
55
nsenter, err := exec.LookPath("nsenter")
56
if err != nil {
57
return xerrors.Errorf("cannot find nsenter")
58
}
59
60
// This agent expectes to be called using the workspacekit lift (i.e. in ring1).
61
// We then enter the PID and mount namespace of supervisor.
62
// First, we need to find the supervisor process
63
proc, err := procfs.NewFS("/proc")
64
if err != nil {
65
return err
66
}
67
procs, err := proc.AllProcs()
68
if err != nil {
69
return err
70
}
71
var supervisorPID int
72
for _, p := range procs {
73
cmd, _ := p.CmdLine()
74
for _, c := range cmd {
75
if strings.HasSuffix(c, "supervisor") {
76
supervisorPID = p.PID
77
break
78
}
79
}
80
if supervisorPID != 0 {
81
break
82
}
83
}
84
if supervisorPID == 0 {
85
return xerrors.Errorf("no supervisor process found")
86
}
87
88
// Then we copy ourselves to a location that we can access from the supervisor NS
89
self, err := os.Executable()
90
if err != nil {
91
return err
92
}
93
fn := fmt.Sprintf("/proc/%d/root/agent", supervisorPID)
94
dst, err := os.OpenFile(fn, os.O_CREATE|os.O_WRONLY, 0755)
95
if err != nil {
96
return err
97
}
98
selfFD, err := os.Open(self)
99
if err != nil {
100
return err
101
}
102
defer selfFD.Close()
103
_, err = io.Copy(dst, selfFD)
104
if err != nil {
105
return xerrors.Errorf("error copying agent: %w", err)
106
}
107
108
return unix.Exec(nsenter, append([]string{nsenter, "-t", strconv.Itoa(supervisorPID), "-m", "-p", "/agent"}, os.Args[1:]...), append(os.Environ(), "AGENT_IN_RING2=true"))
109
}
110
111
// WorkspaceAgent provides ingteration test services from within a workspace
112
type WorkspaceAgent struct {
113
}
114
115
// ListDir lists a directory's content
116
func (*WorkspaceAgent) ListDir(req *api.ListDirRequest, resp *api.ListDirResponse) error {
117
dc, err := os.ReadDir(req.Dir)
118
if err != nil {
119
return err
120
}
121
122
*resp = api.ListDirResponse{}
123
for _, c := range dc {
124
resp.Files = append(resp.Files, c.Name())
125
}
126
return nil
127
}
128
129
// WriteFile writes a file in the workspace
130
func (*WorkspaceAgent) WriteFile(req *api.WriteFileRequest, resp *api.WriteFileResponse) (err error) {
131
err = os.WriteFile(req.Path, req.Content, req.Mode)
132
if err != nil {
133
return
134
}
135
136
*resp = api.WriteFileResponse{}
137
return
138
}
139
140
// Exec executes a command in the workspace
141
func (*WorkspaceAgent) Exec(req *api.ExecRequest, resp *api.ExecResponse) (err error) {
142
cmd := exec.Command(req.Command, req.Args...)
143
cmd.Env = os.Environ()
144
cmd.Env = append(cmd.Env, req.Env...)
145
if req.Dir != "" {
146
cmd.Dir = req.Dir
147
}
148
var (
149
stdout bytes.Buffer
150
stderr bytes.Buffer
151
)
152
cmd.Stdout = &stdout
153
cmd.Stderr = &stderr
154
err = cmd.Run()
155
156
var rc int
157
if err != nil {
158
exitError, ok := err.(*exec.ExitError)
159
if !ok {
160
fullCommand := strings.Join(append([]string{req.Command}, req.Args...), " ")
161
return xerrors.Errorf("%s: %w", fullCommand, err)
162
}
163
rc = exitError.ExitCode()
164
err = nil
165
}
166
167
*resp = api.ExecResponse{
168
ExitCode: rc,
169
Stdout: stdout.String(),
170
Stderr: stderr.String(),
171
}
172
return
173
}
174
175
func (*WorkspaceAgent) BurnCpu(req *api.BurnCpuRequest, resp *api.BurnCpuResponse) error {
176
done := make(chan int)
177
for i := 0; i < int(req.Procs); i++ {
178
go func() {
179
for {
180
select {
181
case <-done:
182
return
183
default:
184
}
185
}
186
}()
187
}
188
189
time.Sleep(req.Timeout)
190
close(done)
191
return nil
192
}
193
194