Skip to content

Commit 4aa9558

Browse files
committed
Added option struct for run hook functions, also added workdir option
Signed-off-by: Fang-Pen Lin <[email protected]>
1 parent 3273ac8 commit 4aa9558

File tree

4 files changed

+99
-18
lines changed

4 files changed

+99
-18
lines changed

pkg/hooks/exec/exec.go

+39-5
Original file line numberDiff line numberDiff line change
@@ -16,16 +16,50 @@ import (
1616
// DefaultPostKillTimeout is the recommended default post-kill timeout.
1717
const DefaultPostKillTimeout = time.Duration(10) * time.Second
1818

19+
type RunOptions struct {
20+
// The hook to run
21+
Hook *rspec.Hook
22+
// The workdir to change when invoking the hook
23+
Dir string
24+
// The container state data to pass into the hook process
25+
State []byte
26+
// Stdout from the hook process
27+
Stdout io.Writer
28+
// Stderr from the hook process
29+
Stderr io.Writer
30+
// Timeout for waiting process killed
31+
PostKillTimeout time.Duration
32+
}
33+
1934
// Run executes the hook and waits for it to complete or for the
2035
// context or hook-specified timeout to expire.
36+
//
37+
// Deprecated: Too many arguments, has been refactored and replaced by RunHook instead
2138
func Run(ctx context.Context, hook *rspec.Hook, state []byte, stdout io.Writer, stderr io.Writer, postKillTimeout time.Duration) (hookErr, err error) {
39+
return RunHook(
40+
ctx,
41+
RunOptions{
42+
Hook: hook,
43+
State: state,
44+
Stdout: stdout,
45+
Stderr: stderr,
46+
PostKillTimeout: postKillTimeout,
47+
},
48+
)
49+
}
50+
51+
// RunHook Run executes the hook and waits for it to complete or for the
52+
// context or hook-specified timeout to expire.
53+
func RunHook(ctx context.Context, options RunOptions) (hookErr, err error) {
54+
hook := options.Hook
2255
cmd := osexec.Cmd{
2356
Path: hook.Path,
2457
Args: hook.Args,
2558
Env: hook.Env,
26-
Stdin: bytes.NewReader(state),
27-
Stdout: stdout,
28-
Stderr: stderr,
59+
Dir: options.Dir,
60+
Stdin: bytes.NewReader(options.State),
61+
Stdout: options.Stdout,
62+
Stderr: options.Stderr,
2963
}
3064
if cmd.Env == nil {
3165
cmd.Env = []string{}
@@ -57,11 +91,11 @@ func Run(ctx context.Context, hook *rspec.Hook, state []byte, stdout io.Writer,
5791
if err := cmd.Process.Kill(); err != nil {
5892
logrus.Errorf("Failed to kill pid %v", cmd.Process)
5993
}
60-
timer := time.NewTimer(postKillTimeout)
94+
timer := time.NewTimer(options.PostKillTimeout)
6195
defer timer.Stop()
6296
select {
6397
case <-timer.C:
64-
err = fmt.Errorf("failed to reap process within %s of the kill signal", postKillTimeout)
98+
err = fmt.Errorf("failed to reap process within %s of the kill signal", options.PostKillTimeout)
6599
case err = <-exit:
66100
}
67101
return err, ctx.Err()

pkg/hooks/exec/exec_test.go

+28-6
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ func TestRun(t *testing.T) {
2929
Args: []string{"sh", "-c", "cat"},
3030
}
3131
var stderr, stdout bytes.Buffer
32-
hookErr, err := Run(ctx, hook, []byte("{}"), &stdout, &stderr, DefaultPostKillTimeout)
32+
hookErr, err := RunHook(ctx, RunOptions{Hook: hook, State: []byte("{}"), Stdout: &stdout, Stderr: &stderr, PostKillTimeout: DefaultPostKillTimeout})
3333
if err != nil {
3434
t.Fatal(err)
3535
}
@@ -46,7 +46,7 @@ func TestRunIgnoreOutput(t *testing.T) {
4646
Path: path,
4747
Args: []string{"sh", "-c", "cat"},
4848
}
49-
hookErr, err := Run(ctx, hook, []byte("{}"), nil, nil, DefaultPostKillTimeout)
49+
hookErr, err := RunHook(ctx, RunOptions{Hook: hook, State: []byte("{}"), PostKillTimeout: DefaultPostKillTimeout})
5050
if err != nil {
5151
t.Fatal(err)
5252
}
@@ -60,7 +60,7 @@ func TestRunFailedStart(t *testing.T) {
6060
hook := &rspec.Hook{
6161
Path: "/does/not/exist",
6262
}
63-
hookErr, err := Run(ctx, hook, []byte("{}"), nil, nil, DefaultPostKillTimeout)
63+
hookErr, err := RunHook(ctx, RunOptions{Hook: hook, State: []byte("{}"), PostKillTimeout: DefaultPostKillTimeout})
6464
if err == nil {
6565
t.Fatal("unexpected success")
6666
}
@@ -125,7 +125,7 @@ func TestRunEnvironment(t *testing.T) {
125125
t.Run(test.name, func(t *testing.T) {
126126
var stderr, stdout bytes.Buffer
127127
hook.Env = test.env
128-
hookErr, err := Run(ctx, hook, []byte("{}"), &stdout, &stderr, DefaultPostKillTimeout)
128+
hookErr, err := RunHook(ctx, RunOptions{Hook: hook, State: []byte("{}"), Stdout: &stdout, Stderr: &stderr, PostKillTimeout: DefaultPostKillTimeout})
129129
if err != nil {
130130
t.Fatal(err)
131131
}
@@ -143,6 +143,28 @@ func TestRunEnvironment(t *testing.T) {
143143
}
144144
}
145145

146+
func TestRunCwd(t *testing.T) {
147+
ctx := context.Background()
148+
hook := &rspec.Hook{
149+
Path: path,
150+
Args: []string{"sh", "-c", "pwd"},
151+
}
152+
cwd, err := os.MkdirTemp("", "userdata")
153+
if err != nil {
154+
t.Fatal(err)
155+
}
156+
var stderr, stdout bytes.Buffer
157+
hookErr, err := RunHook(ctx, RunOptions{Hook: hook, Dir: cwd, State: []byte("{}"), Stdout: &stdout, Stderr: &stderr, PostKillTimeout: DefaultPostKillTimeout})
158+
if err != nil {
159+
t.Fatal(err)
160+
}
161+
if hookErr != nil {
162+
t.Fatal(hookErr)
163+
}
164+
assert.Equal(t, "", stderr.String())
165+
assert.Equal(t, strings.TrimSuffix(stdout.String(), "\n"), cwd)
166+
}
167+
146168
func TestRunCancel(t *testing.T) {
147169
hook := &rspec.Hook{
148170
Path: path,
@@ -186,7 +208,7 @@ func TestRunCancel(t *testing.T) {
186208
defer cancel()
187209
}
188210
hook.Timeout = test.hookTimeout
189-
hookErr, err := Run(ctx, hook, []byte("{}"), &stdout, &stderr, DefaultPostKillTimeout)
211+
hookErr, err := RunHook(ctx, RunOptions{Hook: hook, State: []byte("{}"), Stdout: &stdout, Stderr: &stderr, PostKillTimeout: DefaultPostKillTimeout})
190212
assert.Equal(t, test.expectedRunError, err)
191213
if test.expectedHookError == "" {
192214
if hookErr != nil {
@@ -208,7 +230,7 @@ func TestRunKillTimeout(t *testing.T) {
208230
Path: path,
209231
Args: []string{"sh", "-c", "sleep 1"},
210232
}
211-
hookErr, err := Run(ctx, hook, []byte("{}"), nil, nil, time.Duration(0))
233+
hookErr, err := RunHook(ctx, RunOptions{Hook: hook, State: []byte("{}"), PostKillTimeout: time.Duration(0)})
212234
assert.Equal(t, context.DeadlineExceeded, err)
213235
assert.Regexp(t, "^(failed to reap process within 0s of the kill signal|executing \\[sh -c sleep 1]: signal: killed)$", hookErr)
214236
}

pkg/hooks/exec/runtimeconfigfilter.go

+31-6
Original file line numberDiff line numberDiff line change
@@ -21,19 +21,44 @@ var spewConfig = spew.ConfigState{
2121
SortKeys: true,
2222
}
2323

24+
type RuntimeConfigFilterOptions struct {
25+
// The hooks to run
26+
Hooks []spec.Hook
27+
// The workdir to change when invoking the hook
28+
Dir string
29+
// The container config spec to pass into the hook processes and potentially get modified by them
30+
Config *spec.Spec
31+
// Timeout for waiting process killed
32+
PostKillTimeout time.Duration
33+
}
34+
2435
// RuntimeConfigFilter calls a series of hooks. But instead of
2536
// passing container state on their standard input,
2637
// RuntimeConfigFilter passes the proposed runtime configuration (and
2738
// reads back a possibly-altered form from their standard output).
39+
//
40+
// Deprecated: Too many arguments, has been refactored and replaced by RuntimeConfigFilterWithOptions instead
2841
func RuntimeConfigFilter(ctx context.Context, hooks []spec.Hook, config *spec.Spec, postKillTimeout time.Duration) (hookErr, err error) {
29-
data, err := json.Marshal(config)
42+
return RuntimeConfigFilterWithOptions(ctx, RuntimeConfigFilterOptions{
43+
Hooks: hooks,
44+
Config: config,
45+
PostKillTimeout: postKillTimeout,
46+
})
47+
}
48+
49+
// RuntimeConfigFilterWithOptions calls a series of hooks. But instead of
50+
// passing container state on their standard input,
51+
// RuntimeConfigFilterWithOptions passes the proposed runtime configuration (and
52+
// reads back a possibly-altered form from their standard output).
53+
func RuntimeConfigFilterWithOptions(ctx context.Context, options RuntimeConfigFilterOptions) (hookErr, err error) {
54+
data, err := json.Marshal(options.Config)
3055
if err != nil {
3156
return nil, err
3257
}
33-
for i, hook := range hooks {
58+
for i, hook := range options.Hooks {
3459
hook := hook
3560
var stdout bytes.Buffer
36-
hookErr, err = Run(ctx, &hook, data, &stdout, nil, postKillTimeout)
61+
hookErr, err = RunHook(ctx, RunOptions{Hook: &hook, Dir: options.Dir, State: data, Stdout: &stdout, PostKillTimeout: options.PostKillTimeout})
3762
if err != nil {
3863
return hookErr, err
3964
}
@@ -46,8 +71,8 @@ func RuntimeConfigFilter(ctx context.Context, hooks []spec.Hook, config *spec.Sp
4671
return nil, fmt.Errorf("unmarshal output from config-filter hook %d: %w", i, err)
4772
}
4873

49-
if !reflect.DeepEqual(config, &newConfig) {
50-
oldConfig := spewConfig.Sdump(config)
74+
if !reflect.DeepEqual(options.Config, &newConfig) {
75+
oldConfig := spewConfig.Sdump(options.Config)
5176
newConfig := spewConfig.Sdump(&newConfig)
5277
diff, err := difflib.GetUnifiedDiffString(difflib.UnifiedDiff{
5378
A: difflib.SplitLines(oldConfig),
@@ -65,7 +90,7 @@ func RuntimeConfigFilter(ctx context.Context, hooks []spec.Hook, config *spec.Sp
6590
}
6691
}
6792

68-
*config = newConfig
93+
*options.Config = newConfig
6994
}
7095

7196
return nil, nil

pkg/hooks/exec/runtimeconfigfilter_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -243,7 +243,7 @@ func TestRuntimeConfigFilter(t *testing.T) {
243243
ctx, cancel = context.WithTimeout(ctx, test.contextTimeout)
244244
defer cancel()
245245
}
246-
hookErr, err := RuntimeConfigFilter(ctx, test.hooks, test.input, DefaultPostKillTimeout)
246+
hookErr, err := RuntimeConfigFilterWithOptions(ctx, RuntimeConfigFilterOptions{Hooks: test.hooks, Config: test.input, PostKillTimeout: DefaultPostKillTimeout})
247247
if test.expectedRunErrorString != "" {
248248
// We have to compare the error strings in that case because
249249
// errors.Is works differently.

0 commit comments

Comments
 (0)