|
6 | 6 | "io" |
7 | 7 | "os" |
8 | 8 | "path/filepath" |
| 9 | + "strconv" |
9 | 10 | "strings" |
10 | 11 | "sync" |
11 | 12 | "time" |
@@ -268,6 +269,34 @@ func NewJobRunner(l logger.Logger, scope *metrics.Scope, ag *api.AgentRegisterRe |
268 | 269 | return runner, nil |
269 | 270 | } |
270 | 271 |
|
| 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 | + |
271 | 300 | // Runs the job |
272 | 301 | func (r *JobRunner) Run(ctx context.Context) error { |
273 | 302 | r.logger.Info("Starting job %s", r.job.ID) |
@@ -303,43 +332,24 @@ func (r *JobRunner) Run(ctx context.Context) error { |
303 | 332 | return err |
304 | 333 | } |
305 | 334 |
|
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 |
313 | 337 | // 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") |
327 | 340 |
|
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 |
335 | 345 |
|
336 | 346 | // Set up a child context for helper goroutines related to running the job. |
337 | 347 | cctx, cancel := context.WithCancel(ctx) |
338 | 348 | defer cancel() |
339 | 349 |
|
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. |
343 | 353 | wg.Add(2) |
344 | 354 | go r.jobLogStreamer(cctx, &wg) |
345 | 355 | go r.jobCancellationChecker(cctx, &wg) |
@@ -504,7 +514,7 @@ func (r *JobRunner) createEnvironment() ([]string, error) { |
504 | 514 | } |
505 | 515 |
|
506 | 516 | // 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. |
508 | 518 |
|
509 | 519 | var protectedEnv = []string{ |
510 | 520 | "BUILDKITE_AGENT_ENDPOINT", |
@@ -541,7 +551,7 @@ func (r *JobRunner) createEnvironment() ([]string, error) { |
541 | 551 | } |
542 | 552 | } |
543 | 553 |
|
544 | | - // Set BUILDKITE_IGNORED_ENV so the bootstrap can show warnings |
| 554 | + // Set BUILDKITE_IGNORED_ENV so the job executor can show warnings |
545 | 555 | if len(ignoredEnv) > 0 { |
546 | 556 | env["BUILDKITE_IGNORED_ENV"] = strings.Join(ignoredEnv, ",") |
547 | 557 | } |
@@ -590,19 +600,19 @@ func (r *JobRunner) createEnvironment() ([]string, error) { |
590 | 600 | env["BUILDKITE_AGENT_EXPERIMENT"] = strings.Join(experiments.Enabled(), ",") |
591 | 601 | env["BUILDKITE_REDACTED_VARS"] = strings.Join(r.conf.AgentConfiguration.RedactedVars, ",") |
592 | 602 |
|
593 | | - // propagate CancelSignal to bootstrap, unless it's the default SIGTERM |
| 603 | + // propagate CancelSignal to job executor, unless it's the default SIGTERM |
594 | 604 | if r.conf.CancelSignal != process.SIGTERM { |
595 | 605 | env["BUILDKITE_CANCEL_SIGNAL"] = r.conf.CancelSignal.String() |
596 | 606 | } |
597 | 607 |
|
598 | | - // Whether to enable profiling in the bootstrap |
| 608 | + // Whether to enable profiling in the job executor |
599 | 609 | if r.conf.AgentConfiguration.Profile != "" { |
600 | 610 | env["BUILDKITE_AGENT_PROFILE"] = r.conf.AgentConfiguration.Profile |
601 | 611 | } |
602 | 612 |
|
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 |
604 | 614 | // to propagate it if it's explicitly disabled. |
605 | | - if r.conf.AgentConfiguration.RunInPty == false { |
| 615 | + if !r.conf.AgentConfiguration.RunInPty { |
606 | 616 | env["BUILDKITE_PTY"] = "false" |
607 | 617 | } |
608 | 618 |
|
@@ -666,28 +676,28 @@ func (w LogWriter) Write(bytes []byte) (int, error) { |
666 | 676 | return len(bytes), nil |
667 | 677 | } |
668 | 678 |
|
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) |
671 | 681 |
|
672 | 682 | sh, err := shell.New() |
673 | 683 | if err != nil { |
674 | 684 | return false, err |
675 | 685 | } |
676 | 686 |
|
677 | 687 | // This (plus inherited) is the only ENV that should be exposed |
678 | | - // to the pre-bootstrap hook. |
| 688 | + // to the pre-exec hook. |
679 | 689 | sh.Env.Set("BUILDKITE_ENV_FILE", r.envFile.Name()) |
680 | 690 |
|
681 | 691 | sh.Writer = LogWriter{ |
682 | 692 | l: r.logger, |
683 | 693 | } |
684 | 694 |
|
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) |
687 | 697 | return false, err |
688 | 698 | } |
689 | 699 |
|
690 | | - r.logger.Info("Finished pre-bootstrap hook %q: job accepted", hook) |
| 700 | + r.logger.Info("Finished %s hook %q: hookName, job accepted", hookPath) |
691 | 701 | return true, nil |
692 | 702 | } |
693 | 703 |
|
|
0 commit comments