Skip to content

Commit 49d5d3f

Browse files
committed
Add pre-exec hook identical to pre-bootstrap
1 parent cfbc71e commit 49d5d3f

File tree

1 file changed

+52
-42
lines changed

1 file changed

+52
-42
lines changed

agent/job_runner.go

Lines changed: 52 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"io"
77
"os"
88
"path/filepath"
9+
"strconv"
910
"strings"
1011
"sync"
1112
"time"
@@ -268,6 +269,34 @@ func NewJobRunner(l logger.Logger, scope *metrics.Scope, ag *api.AgentRegisterRe
268269
return runner, nil
269270
}
270271

272+
type hookExit struct {
273+
ExitStatus int
274+
SignalReason string
275+
Ok bool
276+
}
277+
278+
func (r *JobRunner) preExecHook(ctx context.Context, hookName string) hookExit {
279+
exit := hookExit{Ok: true}
280+
if hookPath, _ := hook.Find(r.conf.AgentConfiguration.HooksPath, hookName); hookPath != "" {
281+
// Once we have a hook any failure to run it MUST be fatal to the job to guarantee a true
282+
// positive result from the hook
283+
okay, err := r.executePreExecHook(ctx, hookName, hookPath)
284+
if !okay {
285+
exit.Ok = false
286+
287+
// Ensure the Job UI knows why this job resulted in failure
288+
r.logStreamer.Process(fmt.Sprintf("%s hook rejected this job, see the buildkite-agent logs for more details", hookName))
289+
// But disclose more information in the agent logs
290+
r.logger.Error("%s hook rejected this job: %s", hookName, err)
291+
292+
exit.ExitStatus = -1
293+
exit.SignalReason = "agent_refused"
294+
}
295+
}
296+
297+
return exit
298+
}
299+
271300
// Runs the job
272301
func (r *JobRunner) Run(ctx context.Context) error {
273302
r.logger.Info("Starting job %s", r.job.ID)
@@ -303,43 +332,24 @@ func (r *JobRunner) Run(ctx context.Context) error {
303332
return err
304333
}
305334

306-
// Default exit status is no exit status
307-
exitStatus := ""
308-
signal := ""
309-
signalReason := ""
310-
311-
// Before executing the bootstrap process with the received Job env,
312-
// execute the pre-bootstrap hook (if present) for it to tell us
335+
// Before executing the job process with the received Job env,
336+
// execute the pre-bootstrap/pre-exec hook (if present) for it to tell us
313337
// whether it is happy to proceed.
314-
environmentCommandOkay := true
315-
316-
if hook, _ := hook.Find(r.conf.AgentConfiguration.HooksPath, "pre-bootstrap"); hook != "" {
317-
// Once we have a hook any failure to run it MUST be fatal to the job to guarantee a true
318-
// positive result from the hook
319-
okay, err := r.executePreBootstrapHook(ctx, hook)
320-
if !okay {
321-
environmentCommandOkay = false
322-
323-
// Ensure the Job UI knows why this job resulted in failure
324-
r.logStreamer.Process("pre-bootstrap hook rejected this job, see the buildkite-agent logs for more details")
325-
// But disclose more information in the agent logs
326-
r.logger.Error("pre-bootstrap hook rejected this job: %s", err)
338+
hookExit := r.preExecHook(ctx, "pre-bootstrap")
339+
hookExit = r.preExecHook(ctx, "pre-exec")
327340

328-
exitStatus = "-1"
329-
signalReason = "agent_refused"
330-
}
331-
}
332-
333-
// Used to wait on various routines that we spin up
334-
var wg sync.WaitGroup
341+
// Default exit status is no exit status
342+
signal := ""
343+
exitStatus := strconv.Itoa(hookExit.ExitStatus)
344+
signalReason := hookExit.SignalReason
335345

336346
// Set up a child context for helper goroutines related to running the job.
337347
cctx, cancel := context.WithCancel(ctx)
338348
defer cancel()
339349

340-
if environmentCommandOkay {
341-
// Kick off log streaming and job status checking when the process
342-
// starts.
350+
var wg sync.WaitGroup
351+
if hookExit.Ok {
352+
// Kick off log streaming and job status checking when the process starts.
343353
wg.Add(2)
344354
go r.jobLogStreamer(cctx, &wg)
345355
go r.jobCancellationChecker(cctx, &wg)
@@ -504,7 +514,7 @@ func (r *JobRunner) createEnvironment() ([]string, error) {
504514
}
505515

506516
// Certain env can only be set by agent configuration.
507-
// We show the user a warning in the bootstrap if they use any of these at a job level.
517+
// We show the user a warning in the job logs if they use any of these at a job level.
508518

509519
var protectedEnv = []string{
510520
"BUILDKITE_AGENT_ENDPOINT",
@@ -541,7 +551,7 @@ func (r *JobRunner) createEnvironment() ([]string, error) {
541551
}
542552
}
543553

544-
// Set BUILDKITE_IGNORED_ENV so the bootstrap can show warnings
554+
// Set BUILDKITE_IGNORED_ENV so the job executor can show warnings
545555
if len(ignoredEnv) > 0 {
546556
env["BUILDKITE_IGNORED_ENV"] = strings.Join(ignoredEnv, ",")
547557
}
@@ -590,19 +600,19 @@ func (r *JobRunner) createEnvironment() ([]string, error) {
590600
env["BUILDKITE_AGENT_EXPERIMENT"] = strings.Join(experiments.Enabled(), ",")
591601
env["BUILDKITE_REDACTED_VARS"] = strings.Join(r.conf.AgentConfiguration.RedactedVars, ",")
592602

593-
// propagate CancelSignal to bootstrap, unless it's the default SIGTERM
603+
// propagate CancelSignal to job executor, unless it's the default SIGTERM
594604
if r.conf.CancelSignal != process.SIGTERM {
595605
env["BUILDKITE_CANCEL_SIGNAL"] = r.conf.CancelSignal.String()
596606
}
597607

598-
// Whether to enable profiling in the bootstrap
608+
// Whether to enable profiling in the job executor
599609
if r.conf.AgentConfiguration.Profile != "" {
600610
env["BUILDKITE_AGENT_PROFILE"] = r.conf.AgentConfiguration.Profile
601611
}
602612

603-
// PTY-mode is enabled by default in `start` and `bootstrap`, so we only need
613+
// PTY-mode is enabled by default in `start` and `job-exec`, so we only need
604614
// to propagate it if it's explicitly disabled.
605-
if r.conf.AgentConfiguration.RunInPty == false {
615+
if !r.conf.AgentConfiguration.RunInPty {
606616
env["BUILDKITE_PTY"] = "false"
607617
}
608618

@@ -666,28 +676,28 @@ func (w LogWriter) Write(bytes []byte) (int, error) {
666676
return len(bytes), nil
667677
}
668678

669-
func (r *JobRunner) executePreBootstrapHook(ctx context.Context, hook string) (bool, error) {
670-
r.logger.Info("Running pre-bootstrap hook %q", hook)
679+
func (r *JobRunner) executePreExecHook(ctx context.Context, hookName, hookPath string) (bool, error) {
680+
r.logger.Info("Running %s hook %q", hookName, hookPath)
671681

672682
sh, err := shell.New()
673683
if err != nil {
674684
return false, err
675685
}
676686

677687
// This (plus inherited) is the only ENV that should be exposed
678-
// to the pre-bootstrap hook.
688+
// to the pre-exec hook.
679689
sh.Env.Set("BUILDKITE_ENV_FILE", r.envFile.Name())
680690

681691
sh.Writer = LogWriter{
682692
l: r.logger,
683693
}
684694

685-
if err := sh.RunWithoutPrompt(ctx, hook); err != nil {
686-
r.logger.Error("Finished pre-bootstrap hook %q: job rejected", hook)
695+
if err := sh.RunWithoutPrompt(ctx, hookPath); err != nil {
696+
r.logger.Error("Finished %s hook %q: hookName, job rejected", hookPath)
687697
return false, err
688698
}
689699

690-
r.logger.Info("Finished pre-bootstrap hook %q: job accepted", hook)
700+
r.logger.Info("Finished %s hook %q: hookName, job accepted", hookPath)
691701
return true, nil
692702
}
693703

0 commit comments

Comments
 (0)