Skip to content

Add --timeout flag to kpt fn render and kpt fn eval - #4798

Open
Nikshay1 wants to merge 5 commits into
kptdev:mainfrom
Nikshay1:feat/fn-timeout-flag-4473
Open

Nikshay1 wants to merge 5 commits into
kptdev:mainfrom
Nikshay1:feat/fn-timeout-flag-4473

Conversation

@Nikshay1

@Nikshay1 Nikshay1 commented Oct 11, 2026 •

Copy link
Copy Markdown
Contributor

Description

Adds a --timeout flag to kpt fn render and kpt fn eval.

  • kpt fn render --timeout 10m limits the run time of the whole pipeline, including the functions of subpackages. When the time has passed, the function that is running is stopped and the render fails.
  • kpt fn eval --timeout 30s limits the run time of its function.

When the flag is not set, nothing changes: there is no limit for the whole pipeline, and each function is allowed to run for 5 minutes.

The timeout is a new Timeout field in RunnerOptions. For render, Renderer.Execute runs the pipeline with a context that ends when the timeout has passed (fnruntime.WithRunTimeout), and ContainerFn and ExecFn now run the function with that context. For eval, the timeout is passed to the Timeout field that ContainerFn and ExecFn already had (thirdparty/kyaml/runfn/runfn.go).

A function that is stopped by a timeout is reported like any other function failure, with one more line:

$ kpt fn render pkg --allow-exec --timeout 3s
Package "pkg":
[RUNNING] "./quick.sh" on package "pkg"
[PASS] "./quick.sh" in 1.044s
[RUNNING] "./stuck.sh" on package "pkg"
[FAIL] "./stuck.sh" in 1.957s
  Stderr:
    waiting for a lock that never comes
  Exit code: -1
  Timed out: pipeline execution timeout of 3s exceeded

$ kpt fn eval pkg --exec ./stuck.sh --timeout 1s
[RUNNING] "./stuck.sh"
[FAIL] "./stuck.sh" in 1.001s
  Stderr:
    waiting for a lock that never comes
  Exit code: -1
  Timed out: function execution timeout of 1s exceeded

Before, the only sign of a timeout was Exit code: -1.

Motivation

Part of #4473. This is the first of two PRs: this one adds the CLI flag, and the next one will add the per-function timeout field to the Kptfile pipeline. I split them because the Kptfile field changes the api module.

Notes for reviewers

  • The default of 5 minutes per function. When --timeout is set on kpt fn render, it is the only limit, so the default of 5 minutes per function does not apply. This way --timeout 20m already helps a function that needs more than 5 minutes, before the Kptfile field exists. I can keep the 5 minute limit per function instead if you prefer.
  • Zero and negative values. --timeout 0 is the same as not passing the flag. A negative value is rejected.
  • ExecError. The timeout is reported through the existing ExecError, with two new fields, Timeout and RunTimeout, so the stderr of the function and the exit code are still printed and still written to results.yaml.
  • WaitDelay for exec functions. An exec function that is a shell script can start another process that keeps the output open. kpt then waited for that process even after the timeout had killed the script, so --timeout 100ms on sh -c 'sleep 30; true' took 30 seconds. ExecFn now sets cmd.WaitDelay to 1 second. This also applies when there is no timeout: a function that exits but leaves a background process holding its output now fails after 1 second, where kpt used to wait for that process. I can remove this if you prefer to keep the PR smaller.
  • Child processes of an exec function. On a timeout kpt kills only the function process, not the processes it started. This was already the case and is not changed here; Exec function timeout does not stop the child processes of the function #4799 is the follow-up issue.
  • Container functions. On a timeout kpt kills the docker/podman/nerdctl CLI, as it did before. I did not change that, and I could not check on my machine whether the container itself is stopped, so the docs only say that the function is stopped and the render fails.
  • Not covered. The timeout stops container and exec functions. A wasm or builtin function that is running is not stopped.

Testing

  • TestExecFn_Timeout: a function that outlives its timeout (stderr and exit code are kept), a function that outlives the timeout of the whole run, both timeouts set with either one shorter, a child process that keeps the output open, a function that finishes in time, and a zero timeout.
  • TestContainerFn_Timeout: the same for the container runtime, with a small script in place of the container CLI, so it runs without Docker.
  • TestRenderer_Execute_Timeout: Renderer.Execute stops a pipeline whose function outlives RunnerOptions.Timeout.
  • TestNewRunner_RunTimeout and TestDefaultFnFilterProvider_execTimeout check that the timeout reaches the runtime in the render path and in the eval path.
  • TestCmd_timeoutFlag for render, and new cases in TestRunFnCommand_preRunE for eval, cover flag parsing and the negative value.
  • Locally, with Go 1.26.5: go test for pkg/fn/runtime, pkg/lib/runneroptions, commands/fn/render, thirdparty/kyaml/runfn and thirdparty/cmdconfig/commands/cmdeval, and TestRenderer_Execute_Timeout in pkg/lib/kptops, all pass. I also ran a built binary against a package with a quick and a stuck exec function for both commands. I could not run the tests that need Docker on my machine.

AI usage

I have used AI in the creation of this PR.

I have used the following AI tools:

  • Claude Code (Claude Opus 5.5) to trace where the timeout was dropped, and to write the change, the tests and the docs
  • Codex (gpt-6.1-sol) to review the first version of the change

The container and exec function runtimes accept a timeout, but nothing set it, so every function ran with the default of 5 minutes. Add a --timeout flag to both commands and pass it to the runtimes. A function that times out is now reported with its stderr and a 'Timed out after' line, and kpt no longer waits past the timeout for a child process that keeps the output open.

Part of kptdev#4473

Assisted-by: Claude:claude-opus-5-5

Assisted-by: Codex:gpt-6.1-sol
Signed-off-by: Nikshay <nikshayyadav90@gmail.com>
@Nikshay1
Nikshay1 requested review from a team and a balanced review from Copilot October 11, 2026 16:42
@netlify

netlify Bot commented Oct 11, 2026 •

Copy link
Copy Markdown

✅ Deploy Preview for kptdocs ready!

Name Link
🔨 Latest commit ba5dc16
🔍 Latest deploy log https://app.netlify.com/projects/kptdocs/deploys/6ad11090fef5e6912211e59e
😎 Deploy Preview https://deploy-preview-4798--kptdocs.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
🤖 Make changes Run an agent on this branch

To edit notification comments on pull requests, go to your Netlify project configuration.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Timed-out exec functions can leave descendant processes running, and the container timeout path lacks execution coverage.

3 open findings
What changed in this PR

Adds configurable per-function timeouts to kpt fn render and kpt fn eval.

Changes:

  • Adds and validates the --timeout flag.
  • Propagates timeouts to exec and container runtimes.
  • Reports timeout failures and adds documentation/tests.
File Description
thirdparty/​kyaml/​runfn/​runfn.go Propagates eval timeouts.
thirdparty/​kyaml/​runfn/​runfn_test.go Tests eval exec timeout behavior.
thirdparty/​cmdconfig/​commands/​cmdeval/​cmdeval.go Adds the eval flag and validation.
thirdparty/​cmdconfig/​commands/​cmdeval/​cmdeval_test.go Tests eval flag parsing.
pkg/​lib/​runneroptions/​runneroptions.go Adds shared timeout configuration.
pkg/​fn/​runtime/​runner.go Propagates and reports render timeouts.
pkg/​fn/​runtime/​runner_test.go Tests render timeout propagation.
pkg/​fn/​runtime/​fnerrors.go Adds timeout details to execution errors.
pkg/​fn/​runtime/​fnerrors_test.go Tests timeout error formatting.
pkg/​fn/​runtime/​exec.go Implements exec timeout handling.
pkg/​fn/​runtime/​exec_test.go Tests exec timeout scenarios.
pkg/​fn/​runtime/​container.go Reports container timeout failures.
internal/​docs/​generated/​fndocs/​docs.go Updates generated CLI documentation.
documentation/​content/​en/​reference/​cli/​fn/​render/​_index.md Documents render timeouts.
documentation/​content/​en/​reference/​cli/​fn/​eval/​_index.md Documents eval timeouts.
commands/​fn/​render/​cmdrender.go Adds the render flag and validation.
commands/​fn/​render/​cmdrender_test.go Tests render flag parsing.

🧠 Review effort: Balanced


💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread pkg/fn/runtime/exec.go
Comment thread pkg/fn/runtime/container.go Outdated
Comment thread pkg/fn/runtime/exec.go Outdated
Assisted-by: Claude:claude-opus-5-5
Signed-off-by: Nikshay <nikshayyadav90@gmail.com>
Comment thread commands/fn/render/cmdrender.go Outdated
Comment thread pkg/fn/runtime/exec.go
Signed-off-by: Nikshay <nikshayyadav90@gmail.com>
Assisted-by: Claude:claude-opus-5-5
@liamfallon

Copy link
Copy Markdown
Contributor

Quality Gate Failed Quality Gate failed

Failed conditions B Security Rating on New Code (required ≥ A)

See analysis details on SonarQube Cloud

Catch issues before they fail your Quality Gate with our IDE extension SonarQube for IDE

Can you try to resolve the high/medium issues? I think it's pretty easy to do.

Signed-off-by: Nikshay <nikshayyadav90@gmail.com>
Assisted-by: Claude:claude-opus-5-5
@Nikshay1

Copy link
Copy Markdown
Contributor Author

@liamfallon done in 4e1534c:

  • go:S1186: the empty cancel function in timeout.go now has a comment saying why it is empty.
  • godre:S8242: the Ctx field this PR had added to ExecFn is gone. The context is now passed as a parameter to a new ExecFn.RunContext(ctx, r, w); Run calls it with context.Background(), so existing callers are unchanged.

The three remaining findings are the Minor go:S4036 ones in container.go (dockerCmdAvailable, podmanCmdAvailable, nerdctlCmdAvailable). Those lines date from 2022 and this PR does not change them; they look up the container CLI on PATH on purpose, so I left them. They are what keeps the Security Rating at B.

@liamfallon liamfallon left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks very much for this, it's terrific. A few rather pedantic comments.

Comment thread pkg/fn/runtime/fnerrors.go Outdated
case fe.Timeout == 0:
return ""
case fe.RunTimeout:
return fmt.Sprintf("the whole run took longer than %v", fe.Timeout)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
return fmt.Sprintf("the whole run took longer than %v", fe.Timeout)
return fmt.Sprintf("pipeline execution timeout of %v exceeded", fe.Timeout)

I think it's better to explicitly mention that there is a timeout and that it has been exceeded.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in ba5dc16. The whole-run case now says pipeline execution timeout of X exceeded. For the per-function case (fn eval, or the 5m default per function) I used function execution timeout of X exceeded, because "pipeline" would be wrong there. Happy to change it if you'd prefer one message for both. The test expectations you pointed at are updated too.

Comment thread pkg/fn/runtime/fnerrors.go Outdated
case fe.RunTimeout:
return fmt.Sprintf("the whole run took longer than %v", fe.Timeout)
default:
return fmt.Sprintf("the function took longer than %v", fe.Timeout)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
return fmt.Sprintf("the function took longer than %v", fe.Timeout)
return fmt.Sprintf("pipeline execution timeout of %v exceeded", fe.Timeout)

Comment thread pkg/fn/runtime/fnerrors_test.go Outdated
},
expected: `[Stderr]:
error message Exit Code: -1
Timed Out: the function took longer than 30s

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You might have to change this?

Comment thread pkg/fn/runtime/fnerrors_test.go Outdated
},
expected: `[Stderr]:
error message Exit Code: -1
Timed Out: the whole run took longer than 30s

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

and this.

Comment thread pkg/fn/runtime/runner_test.go Outdated

_, err = fr.Filter(nil)
assert.Error(t, err)
assert.Contains(t, out.String(), "Timed out: the whole run took longer than 100ms")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

and this

Comment thread pkg/lib/kptops/render_executor_test.go Outdated
start := time.Now()
_, err := renderer.Execute(ctx)
assert.Error(t, err)
assert.Contains(t, outputBuffer.String(), "Timed out: the whole run took longer than 100ms")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here too.

Comment thread thirdparty/kyaml/runfn/runfn_test.go Outdated

_, err = fltr.Filter(nil)
assert.Error(t, err)
assert.Contains(t, out.String(), "Timed out: the function took longer than 100ms")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

and here.

@liamfallon

Copy link
Copy Markdown
Contributor

@liamfallon done in 4e1534c:

* `go:S1186`: the empty cancel function in `timeout.go` now has a comment saying why it is empty.

* `godre:S8242`: the `Ctx` field this PR had added to `ExecFn` is gone. The context is now passed as a parameter to a new `ExecFn.RunContext(ctx, r, w)`; `Run` calls it with `context.Background()`, so existing callers are unchanged.

The three remaining findings are the Minor go:S4036 ones in container.go (dockerCmdAvailable, podmanCmdAvailable, nerdctlCmdAvailable). Those lines date from 2022 and this PR does not change them; they look up the container CLI on PATH on purpose, so I left them. They are what keeps the Security Rating at B.

@Nikshay1 Thanks, that's great. The minor comments were fine. We justtry and keep the SonarCloud green if it's not too much effort.

Signed-off-by: Nikshay <nikshayyadav90@gmail.com>
@sonarqubecloud

Copy link
Copy Markdown

Quality Gate Failed Quality Gate failed

Failed conditions
B Security Rating on New Code (required ≥ A)

See analysis details on SonarQube Cloud

Catch issues before they fail your Quality Gate with our IDE extension SonarQube for IDE

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants