Skip to content

Commit d3cb96e

Browse files
authored
Merge pull request #4077 from apostasie/tigron-2-debugging
[Tigron]: command env WhiteList support and cosmetic changes
2 parents 9570d20 + 734f7a1 commit d3cb96e

29 files changed

+446
-246
lines changed

mod/tigron/.golangci.yml

Lines changed: 72 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -12,25 +12,81 @@ issues:
1212

1313
linters:
1414
default: all
15+
enable:
16+
# These are the default set of golangci (errcheck is disabled, see below)
17+
- govet # Vet examines Go source code and reports suspicious constructs. It is roughly the same as 'go vet' and uses its passes.
18+
- ineffassign # Detects when assignments to existing variables are not used.
19+
- staticcheck # It's the set of rules from staticcheck.
20+
- unused # Checks Go code for unused constants, variables, functions and types.
21+
# These are the linters we knowingly want enabled in addition to the default set
22+
- containedctx # avoid embedding context into structs
23+
- depguard # Allows to explicitly allow or disallow third party modules
24+
- err113 # encourage static errors
25+
- gochecknoglobals # globals should be avoided as much as possible
26+
- godot # forces dot at the end of comments
27+
- gosec # various security checks
28+
- interfacebloat # limit complexity in public APIs
29+
- paralleltest # enforces tests using parallel
30+
- revive # meta linter (see settings below)
31+
- testpackage # test packages should be separate from the package they test (eg: name them package_test)
32+
- testableexamples # makes sure that examples are testable (have an expected output)
33+
- thelper # enforces use of t.Helper()
34+
- varnamelen # encourage readable descriptive names for variables instead of x, y, z
1535
disable:
1636
# These are the linters that we know we do not want
17-
- cyclop # provided by revive
18-
- exhaustruct # does not serve much of a purpose
19-
- funlen # provided by revive
20-
- gocognit # provided by revive
21-
- goconst # provided by revive
22-
- godox # not helpful unless we could downgrade it to warning / info
23-
- ginkgolinter # no ginkgo
24-
- gomodguard # we use depguard instead
25-
- ireturn # too annoying with not enough value
26-
- lll # provided by golines
27-
- nonamedreturns # named returns are occasionally useful
28-
- prealloc # premature optimization
29-
- promlinter # no prometheus
30-
- sloglint # no slog
31-
- testifylint # no testify
32-
- zerologlint # no zerolog
37+
- cyclop # provided by revive
38+
- exhaustruct # does not serve much of a purpose
39+
- errcheck # provided by revive
40+
- forcetypeassert # provided by revive
41+
- funlen # provided by revive
42+
- gocognit # provided by revive
43+
- goconst # provided by revive
44+
- godox # not helpful unless we could downgrade it to warning / info
45+
- ginkgolinter # no ginkgo
46+
- gomodguard # we use depguard instead
47+
- ireturn # too annoying with not enough value
48+
- lll # provided by golines
49+
- nestif # already provided ten different ways with revive cognitive complexity, etc
50+
- nonamedreturns # named returns are occasionally useful
51+
- prealloc # premature optimization
52+
- promlinter # no prometheus
53+
- sloglint # no slog
54+
- testifylint # no testify
55+
- zerologlint # no zerolog
3356
settings:
57+
interfacebloat:
58+
# Default is 10
59+
max: 13
60+
revive:
61+
enable-all-rules: true
62+
rules:
63+
- name: cognitive-complexity
64+
# Default is 7
65+
arguments: [60]
66+
- name: function-length
67+
# Default is 50, 75
68+
arguments: [80, 180]
69+
- name: cyclomatic
70+
# Default is 10
71+
arguments: [30]
72+
- name: add-constant
73+
arguments:
74+
- allowInts: "0,1,2"
75+
allowStrs: '""'
76+
- name: flag-parameter
77+
# Not sure why this is valuable.
78+
disabled: true
79+
- name: line-length-limit
80+
# Formatter `golines` takes care of this.
81+
disabled: true
82+
- name: unhandled-error
83+
arguments:
84+
- "fmt.Print"
85+
- "fmt.Println"
86+
- "fmt.Printf"
87+
- "fmt.Fprint"
88+
- "fmt.Fprintln"
89+
- "fmt.Fprintf"
3490
depguard:
3591
rules:
3692
main:

mod/tigron/expect/comparators_test.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,11 @@
1414
limitations under the License.
1515
*/
1616

17+
//revive:disable:add-constant
1718
package expect_test
1819

20+
// TODO: add a lot more tests including failure conditions with mimicry
21+
1922
import (
2023
"regexp"
2124
"testing"

mod/tigron/internal/com/command.go

Lines changed: 53 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -43,16 +43,14 @@ var (
4343
ErrFailedStarting = errors.New("command failed starting")
4444
// ErrSignaled is returned by Wait() if a signal was sent to the command while running.
4545
ErrSignaled = errors.New("command execution signaled")
46-
// ErrExecutionFailed is returned by Wait() when a command executes but returns a non-zero error
47-
// code.
46+
// ErrExecutionFailed is returned by Wait() when a command executes but returns a non-zero error code.
4847
ErrExecutionFailed = errors.New("command returned a non-zero exit code")
4948
// ErrFailedSendingSignal may happen if sending a signal to an already terminated process.
5049
ErrFailedSendingSignal = errors.New("failed sending signal")
5150

5251
// ErrExecAlreadyStarted is a system error normally indicating a bogus double call to Run().
5352
ErrExecAlreadyStarted = errors.New("command has already been started (double `Run`)")
54-
// ErrExecNotStarted is a system error normally indicating that Wait() has been called without
55-
// first calling Run().
53+
// ErrExecNotStarted is a system error normally indicating that Wait() has been called without first calling Run().
5654
ErrExecNotStarted = errors.New("command has not been started (call `Run` first)")
5755
// ErrExecAlreadyFinished is a system error indicating a double call to Wait().
5856
ErrExecAlreadyFinished = errors.New("command is already finished")
@@ -75,7 +73,7 @@ type Result struct {
7573
}
7674

7775
type execution struct {
78-
//nolint:containedctx
76+
//nolint:containedctx // Is there a way around this?
7977
context context.Context
8078
cancel context.CancelFunc
8179
command *exec.Cmd
@@ -93,10 +91,10 @@ type Command struct {
9391
WrapArgs []string
9492
Timeout time.Duration
9593

96-
WorkingDir string
97-
Env map[string]string
98-
// FIXME: EnvBlackList might change for a better mechanism (regexp and/or whitelist + blacklist)
94+
WorkingDir string
95+
Env map[string]string
9996
EnvBlackList []string
97+
EnvWhiteList []string
10098

10199
writers []func() io.Reader
102100

@@ -122,6 +120,7 @@ func (gc *Command) Clone() *Command {
122120
WorkingDir: gc.WorkingDir,
123121
Env: map[string]string{},
124122
EnvBlackList: append([]string(nil), gc.EnvBlackList...),
123+
EnvWhiteList: append([]string(nil), gc.EnvWhiteList...),
125124

126125
writers: append([]func() io.Reader(nil), gc.writers...),
127126

@@ -137,27 +136,24 @@ func (gc *Command) Clone() *Command {
137136
return com
138137
}
139138

140-
// WithPTY requests that the command be executed with a pty for std streams. Parameters allow
141-
// showing which streams
142-
// are to be tied to the pty.
139+
// WithPTY requests that the command be executed with a pty for std streams.
140+
// Parameters allow showing which streams are to be tied to the pty.
143141
// This command has no effect if Run has already been called.
144142
func (gc *Command) WithPTY(stdin, stdout, stderr bool) {
145143
gc.ptyStdout = stdout
146144
gc.ptyStderr = stderr
147145
gc.ptyStdin = stdin
148146
}
149147

150-
// WithFeeder ensures that the provider function will be executed and its output fed to the command
151-
// stdin. WithFeeder, like Feed, can be used multiple times, and writes will be performed
152-
// sequentially, in order.
148+
// WithFeeder ensures that the provider function will be executed and its output fed to the command stdin.
149+
// WithFeeder, like Feed, can be used multiple times, and writes will be performed sequentially, in order.
153150
// This command has no effect if Run has already been called.
154151
func (gc *Command) WithFeeder(writers ...func() io.Reader) {
155152
gc.writers = append(gc.writers, writers...)
156153
}
157154

158155
// Feed ensures that the provider reader will be copied on the command stdin.
159-
// Feed, like WithFeeder, can be used multiple times, and writes will be performed in sequentially,
160-
// in order.
156+
// Feed, like WithFeeder, can be used multiple times, and writes will be performed in sequentially, in order.
161157
// This command has no effect if Run has already been called.
162158
func (gc *Command) Feed(reader io.Reader) {
163159
gc.writers = append(gc.writers, func() io.Reader {
@@ -197,7 +193,6 @@ func (gc *Command) Run(parentCtx context.Context) error {
197193

198194
// Create a contextual command, set the logger
199195
cmd = gc.buildCommand(ctx)
200-
201196
// Get a debug-logger from the context
202197
var (
203198
log logger.Logger
@@ -338,8 +333,7 @@ func (gc *Command) wrap() error {
338333
err error
339334
)
340335

341-
// XXXgolang: this is troubling. cmd.ProcessState.ExitCode() is always fine, even if
342-
// cmd.ProcessState is nil.
336+
// XXXgolang: this is troubling. cmd.ProcessState.ExitCode() is always fine, even if cmd.ProcessState is nil.
343337
exitCode = cmd.ProcessState.ExitCode()
344338

345339
if cmd.ProcessState != nil {
@@ -356,7 +350,7 @@ func (gc *Command) wrap() error {
356350
}
357351
}
358352

359-
// Catch-up on the context
353+
// Catch-up on the context.
360354
switch ctx.Err() {
361355
case context.DeadlineExceeded:
362356
err = ErrTimeout
@@ -365,7 +359,7 @@ func (gc *Command) wrap() error {
365359
default:
366360
}
367361

368-
// Stuff everything in Result and return err
362+
// Stuff everything in Result and return err.
369363
gc.result = &Result{
370364
ExitCode: exitCode,
371365
Stdout: pipes.fromStdout,
@@ -382,7 +376,7 @@ func (gc *Command) wrap() error {
382376
}
383377

384378
func (gc *Command) buildCommand(ctx context.Context) *exec.Cmd {
385-
// Build arguments and binary
379+
// Build arguments and binary.
386380
args := gc.Args
387381
if gc.PrependArgs != nil {
388382
args = append(gc.PrependArgs, args...)
@@ -399,26 +393,55 @@ func (gc *Command) buildCommand(ctx context.Context) *exec.Cmd {
399393
//nolint:gosec
400394
cmd := exec.CommandContext(ctx, binary, args...)
401395

402-
// Add dir
396+
// Add dir.
403397
cmd.Dir = gc.WorkingDir
404398

405-
// Set wait delay after waits returns
399+
// Set wait delay after waits returns.
406400
cmd.WaitDelay = delayAfterWait
407401

408-
// Build env
402+
// Build env.
409403
cmd.Env = []string{}
410-
// TODO: replace with regexps? and/or whitelist?
404+
405+
const (
406+
star = "*"
407+
equal = "="
408+
)
409+
411410
for _, envValue := range os.Environ() {
412411
add := true
413412

414-
for _, b := range gc.EnvBlackList {
415-
if b == "*" || strings.HasPrefix(envValue, b+"=") {
413+
for _, needle := range gc.EnvBlackList {
414+
if strings.HasSuffix(needle, star) {
415+
needle = strings.TrimSuffix(needle, star)
416+
} else if needle != star && !strings.Contains(needle, equal) {
417+
needle += equal
418+
}
419+
420+
if needle == star || strings.HasPrefix(envValue, needle) {
416421
add = false
417422

418423
break
419424
}
420425
}
421426

427+
if len(gc.EnvWhiteList) > 0 {
428+
add = false
429+
430+
for _, needle := range gc.EnvWhiteList {
431+
if strings.HasSuffix(needle, star) {
432+
needle = strings.TrimSuffix(needle, star)
433+
} else if needle != star && !strings.Contains(needle, equal) {
434+
needle += equal
435+
}
436+
437+
if needle == star || strings.HasPrefix(envValue, needle) {
438+
add = true
439+
440+
break
441+
}
442+
}
443+
}
444+
422445
if add {
423446
cmd.Env = append(cmd.Env, envValue)
424447
}
@@ -429,12 +452,12 @@ func (gc *Command) buildCommand(ctx context.Context) *exec.Cmd {
429452
cmd.Env = append(cmd.Env, k+"="+v)
430453
}
431454

432-
// Attach platform ProcAttr and get optional custom cancellation routine
455+
// Attach platform ProcAttr and get optional custom cancellation routine.
433456
if cancellation := addAttr(cmd); cancellation != nil {
434457
cmd.Cancel = func() error {
435458
gc.exec.log.Log("command cancelled")
436459

437-
// Call the platform dependent cancellation routine
460+
// Call the platform dependent cancellation routine.
438461
return cancellation()
439462
}
440463
}

mod/tigron/internal/com/command_other.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,10 @@ import (
2424
)
2525

2626
func addAttr(cmd *exec.Cmd) func() error {
27-
// Default shutdown will leave child processes behind in certain circumstances
27+
// Default shutdown will leave child processes behind in certain circumstances.
2828
cmd.SysProcAttr = &syscall.SysProcAttr{
2929
Setsid: true,
30-
// FIXME: understand why we would want that
30+
// FIXME: understand why we would want that.
3131
// Setctty: true,
3232
}
3333

0 commit comments

Comments
 (0)