Skip to content

Commit 39b3719

Browse files
authored
feat: add timeout to run host collector (#1435)
* feat: add timeout to run host collector * return error on invalid timeout * return -1 on context deadline exceeded
1 parent d4c1e9c commit 39b3719

File tree

7 files changed

+112
-1
lines changed

7 files changed

+112
-1
lines changed

config/crds/troubleshoot.sh_hostcollectors.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1386,6 +1386,8 @@ spec:
13861386
type: object
13871387
outputDir:
13881388
type: string
1389+
timeout:
1390+
type: string
13891391
required:
13901392
- args
13911393
- command

config/crds/troubleshoot.sh_hostpreflights.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1386,6 +1386,8 @@ spec:
13861386
type: object
13871387
outputDir:
13881388
type: string
1389+
timeout:
1390+
type: string
13891391
required:
13901392
- args
13911393
- command

config/crds/troubleshoot.sh_supportbundles.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12195,6 +12195,8 @@ spec:
1219512195
type: object
1219612196
outputDir:
1219712197
type: string
12198+
timeout:
12199+
type: string
1219812200
required:
1219912201
- args
1220012202
- command

pkg/apis/troubleshoot/v1beta2/hostcollector_shared.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,7 @@ type HostRun struct {
187187
Env []string `json:"env,omitempty" yaml:"env,omitempty"`
188188
InheritEnvs []string `json:"inheritEnvs,omitempty" yaml:"inheritEnvs,omitempty"`
189189
IgnoreParentEnvs bool `json:"ignoreParentEnvs,omitempty" yaml:"ignoreParentEnvs,omitempty"`
190+
Timeout string `json:"timeout,omitempty" yaml:"timeout,omitempty"`
190191
}
191192

192193
type HostCollect struct {

pkg/collect/host_run.go

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,14 @@ package collect
22

33
import (
44
"bytes"
5+
"context"
56
"encoding/json"
67
"fmt"
78
"os"
89
"os/exec"
910
"path/filepath"
1011
"strings"
12+
"time"
1113

1214
"github.com/pkg/errors"
1315
troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
@@ -49,7 +51,29 @@ func (c *CollectHostRun) Collect(progressChan chan<- interface{}) (map[string][]
4951
collectorName = "run-host"
5052
}
5153

52-
cmd := exec.Command(c.attemptToConvertCmdToAbsPath(), runHostCollector.Args...)
54+
var (
55+
timeout time.Duration
56+
cmd *exec.Cmd
57+
errInvalidDuration error
58+
)
59+
60+
ctx := context.Background()
61+
cmdPath := c.attemptToConvertCmdToAbsPath()
62+
63+
if runHostCollector.Timeout != "" {
64+
timeout, errInvalidDuration = time.ParseDuration(runHostCollector.Timeout)
65+
if errInvalidDuration != nil {
66+
return nil, errors.Wrapf(errInvalidDuration, "failed to parse timeout %q", runHostCollector.Timeout)
67+
}
68+
}
69+
70+
if timeout <= time.Duration(0) {
71+
cmd = exec.Command(cmdPath, runHostCollector.Args...)
72+
} else {
73+
timeoutCtx, cancel := context.WithTimeout(ctx, timeout)
74+
defer cancel()
75+
cmd = exec.CommandContext(timeoutCtx, cmdPath, runHostCollector.Args...)
76+
}
5377

5478
klog.V(2).Infof("Run host collector command: %q", cmd.String())
5579
runInfo := &HostRunInfo{
@@ -118,6 +142,9 @@ func (c *CollectHostRun) Collect(progressChan chan<- interface{}) (map[string][]
118142
if werr, ok := err.(*exec.ExitError); ok {
119143
runInfo.ExitCode = strings.TrimPrefix(werr.Error(), "exit status ")
120144
runInfo.Error = stderr.String()
145+
} else if err == context.DeadlineExceeded {
146+
runInfo.ExitCode = "-1"
147+
runInfo.Error = fmt.Sprintf("command timed out after %s", timeout.String())
121148
} else {
122149
return nil, errors.Wrap(err, "failed to run")
123150
}

pkg/collect/host_run_test.go

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,3 +158,77 @@ func TestCollectHostRun_Collect(t *testing.T) {
158158
}
159159
}
160160
}
161+
162+
func TestCollectHostRunCollectWithTimeout(t *testing.T) {
163+
tests := []struct {
164+
name string
165+
collector *CollectHostRun
166+
wantError bool
167+
}{
168+
{
169+
name: "no timeout",
170+
collector: &CollectHostRun{
171+
hostCollector: &troubleshootv1beta2.HostRun{
172+
HostCollectorMeta: troubleshootv1beta2.HostCollectorMeta{
173+
CollectorName: "no timeout",
174+
},
175+
Command: "echo",
176+
Args: []string{"1"},
177+
},
178+
},
179+
wantError: false,
180+
},
181+
{
182+
name: "invalid timeout",
183+
collector: &CollectHostRun{
184+
hostCollector: &troubleshootv1beta2.HostRun{
185+
HostCollectorMeta: troubleshootv1beta2.HostCollectorMeta{
186+
CollectorName: "negative timeout",
187+
},
188+
Command: "echo",
189+
Args: []string{"1"},
190+
Timeout: "thisistheway",
191+
},
192+
},
193+
wantError: true,
194+
},
195+
{
196+
name: "negative timeout",
197+
collector: &CollectHostRun{
198+
hostCollector: &troubleshootv1beta2.HostRun{
199+
HostCollectorMeta: troubleshootv1beta2.HostCollectorMeta{
200+
CollectorName: "negative timeout",
201+
},
202+
Command: "echo",
203+
Args: []string{"1"},
204+
Timeout: "-10s",
205+
},
206+
},
207+
wantError: false,
208+
},
209+
{
210+
name: "endless command with timeout",
211+
collector: &CollectHostRun{
212+
hostCollector: &troubleshootv1beta2.HostRun{
213+
HostCollectorMeta: troubleshootv1beta2.HostCollectorMeta{
214+
CollectorName: "endless command",
215+
},
216+
Command: "ping",
217+
Args: []string{"google.com"},
218+
Timeout: "200ms",
219+
},
220+
},
221+
wantError: false,
222+
},
223+
}
224+
225+
for _, test := range tests {
226+
got, err := test.collector.Collect(nil)
227+
if test.wantError {
228+
assert.Error(t, err)
229+
} else {
230+
assert.NoError(t, err)
231+
assert.NotNil(t, got)
232+
}
233+
}
234+
}

schemas/supportbundle-troubleshoot-v1beta2.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11839,6 +11839,9 @@
1183911839
},
1184011840
"outputDir": {
1184111841
"type": "string"
11842+
},
11843+
"timeout": {
11844+
"type": "string"
1184211845
}
1184311846
}
1184411847
},

0 commit comments

Comments
 (0)