Skip to content

Commit 5edd6e4

Browse files
committed
chore(execd): complete the work for #332, and fix the linter errors.
1 parent cb899f9 commit 5edd6e4

6 files changed

Lines changed: 61 additions & 28 deletions

File tree

components/execd/main.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,15 @@ package main
1717
import (
1818
"fmt"
1919

20+
"github.com/alibaba/opensandbox/internal/version"
21+
2022
_ "go.uber.org/automaxprocs/maxprocs"
2123

2224
"github.com/alibaba/opensandbox/execd/pkg/flag"
2325
"github.com/alibaba/opensandbox/execd/pkg/log"
2426
_ "github.com/alibaba/opensandbox/execd/pkg/util/safego"
2527
"github.com/alibaba/opensandbox/execd/pkg/web"
2628
"github.com/alibaba/opensandbox/execd/pkg/web/controller"
27-
"github.com/alibaba/opensandbox/internal/version"
2829
)
2930

3031
// main initializes and starts the execd server.

components/execd/pkg/runtime/command.go

Lines changed: 12 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ import (
3737

3838
func buildCredential(uid, gid *uint32) (*syscall.Credential, error) {
3939
if uid == nil && gid == nil {
40-
return nil, nil
40+
return nil, nil //nolint:nilnil
4141
}
4242

4343
cred := &syscall.Credential{}
@@ -95,9 +95,20 @@ func (c *Controller) runCommand(ctx context.Context, request *ExecuteCodeRequest
9595
log.Info("received command: %v", request.Code)
9696
cmd := exec.CommandContext(ctx, "bash", "-c", request.Code)
9797

98+
// Configure credentials and process group
99+
cred, err := buildCredential(request.Uid, request.Gid)
100+
if err != nil {
101+
return fmt.Errorf("failed to build credential: %w", err)
102+
}
103+
cmd.SysProcAttr = &syscall.SysProcAttr{
104+
Setpgid: true,
105+
Credential: cred,
106+
}
107+
98108
cmd.Stdout = stdout
99109
cmd.Stderr = stderr
100110
cmd.Env = mergeEnvs(os.Environ(), loadExtraEnvFromFile())
111+
cmd.Dir = request.Cwd
101112

102113
done := make(chan struct{}, 1)
103114
var wg sync.WaitGroup
@@ -111,20 +122,7 @@ func (c *Controller) runCommand(ctx context.Context, request *ExecuteCodeRequest
111122
c.tailStdPipe(stderrPath, request.Hooks.OnExecuteStderr, done)
112123
})
113124

114-
cmd.Dir = request.Cwd
115-
116-
// Configure credentials and process group
117-
cred, err := buildCredential(request.Uid, request.Gid)
118-
if err != nil {
119-
log.Error("failed to build credentials: %v", err)
120-
}
121-
cmd.SysProcAttr = &syscall.SysProcAttr{
122-
Setpgid: true,
123-
Credential: cred,
124-
}
125-
126125
err = cmd.Start()
127-
128126
if err != nil {
129127
request.Hooks.OnExecuteInit(session)
130128
request.Hooks.OnExecuteError(&execute.ErrorOutput{EName: "CommandExecError", EValue: err.Error()})
@@ -219,9 +217,7 @@ func (c *Controller) runBackgroundCommand(ctx context.Context, cancel context.Ca
219217
startAt := time.Now()
220218
log.Info("received command: %v", request.Code)
221219
cmd := exec.CommandContext(ctx, "bash", "-c", request.Code)
222-
223220
cmd.Dir = request.Cwd
224-
225221
// Configure credentials and process group
226222
cred, err := buildCredential(request.Uid, request.Gid)
227223
if err != nil {
@@ -233,7 +229,6 @@ func (c *Controller) runBackgroundCommand(ctx context.Context, cancel context.Ca
233229
}
234230

235231
cmd.Stdout = pipe
236-
237232
cmd.Stderr = pipe
238233
cmd.Env = mergeEnvs(os.Environ(), loadExtraEnvFromFile())
239234

components/execd/pkg/runtime/types.go

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -34,16 +34,17 @@ type ExecuteResultHook struct {
3434

3535
// ExecuteCodeRequest represents a code execution request with context and hooks.
3636
type ExecuteCodeRequest struct {
37-
Language Language `json:"language"`
38-
Code string `json:"code"`
39-
Context string `json:"context"`
40-
Timeout time.Duration `json:"timeout"`
41-
Cwd string `json:"cwd"`
42-
Envs map[string]string `json:"envs"`
43-
Uid *uint32 `json:"uid,omitempty"`
44-
Gid *uint32 `json:"gid,omitempty"`
45-
Hooks ExecuteResultHook
37+
Language Language `json:"language"`
38+
Code string `json:"code"`
39+
Context string `json:"context"`
40+
Timeout time.Duration `json:"timeout"`
41+
Cwd string `json:"cwd"`
42+
Envs map[string]string `json:"envs"`
43+
Uid *uint32 `json:"uid,omitempty"`
44+
Gid *uint32 `json:"gid,omitempty"`
45+
Hooks ExecuteResultHook
4646
}
47+
4748
// SetDefaultHooks installs stdout logging fallbacks for unset hooks.
4849
func (req *ExecuteCodeRequest) SetDefaultHooks() {
4950
if req.Hooks.OnExecuteResult == nil {

components/execd/pkg/web/controller/command.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,13 +133,17 @@ func (c *CodeInterpretingController) buildExecuteCommandRequest(request model.Ru
133133
Code: request.Command,
134134
Cwd: request.Cwd,
135135
Timeout: timeout,
136+
Gid: request.Gid,
137+
Uid: request.Uid,
136138
}
137139
} else {
138140
return &runtime.ExecuteCodeRequest{
139141
Language: runtime.Command,
140142
Code: request.Command,
141143
Cwd: request.Cwd,
142144
Timeout: timeout,
145+
Gid: request.Gid,
146+
Uid: request.Uid,
143147
}
144148
}
145149
}

components/execd/pkg/web/model/codeinterpreting.go

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ package model
1616

1717
import (
1818
"encoding/json"
19+
"errors"
1920
"fmt"
2021
"strings"
2122

@@ -53,11 +54,20 @@ type RunCommandRequest struct {
5354
Background bool `json:"background,omitempty"`
5455
// TimeoutMs caps execution duration; 0 uses server default.
5556
TimeoutMs int64 `json:"timeout,omitempty" validate:"omitempty,gte=1"`
57+
58+
Uid *uint32 `json:"uid,omitempty"`
59+
Gid *uint32 `json:"gid,omitempty"`
5660
}
5761

5862
func (r *RunCommandRequest) Validate() error {
5963
validate := validator.New()
60-
return validate.Struct(r)
64+
if err := validate.Struct(r); err != nil {
65+
return err
66+
}
67+
if r.Gid != nil && r.Uid == nil {
68+
return errors.New("uid is required when gid is provided")
69+
}
70+
return nil
6171
}
6272

6373
type ServerStreamEventType string

components/execd/pkg/web/model/codeinterpreting_test.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,28 @@ func TestRunCommandRequestValidate(t *testing.T) {
6060
}
6161
}
6262

63+
func ptr32(v uint32) *uint32 { return &v }
64+
65+
func TestRunCommandRequestValidateUidGid(t *testing.T) {
66+
// uid-only: valid
67+
req := RunCommandRequest{Command: "id", Uid: ptr32(1000)}
68+
if err := req.Validate(); err != nil {
69+
t.Fatalf("expected success with uid only: %v", err)
70+
}
71+
72+
// uid + gid: valid
73+
req = RunCommandRequest{Command: "id", Uid: ptr32(1000), Gid: ptr32(1000)}
74+
if err := req.Validate(); err != nil {
75+
t.Fatalf("expected success with uid and gid: %v", err)
76+
}
77+
78+
// gid-only: must be rejected
79+
req = RunCommandRequest{Command: "id", Gid: ptr32(1000)}
80+
if err := req.Validate(); err == nil {
81+
t.Fatalf("expected validation error when gid is set without uid")
82+
}
83+
}
84+
6385
func TestServerStreamEventToJSON(t *testing.T) {
6486
event := ServerStreamEvent{
6587
Type: StreamEventTypeStdout,

0 commit comments

Comments
 (0)