Skip to content

feat(ISV-7560): internal-request uses k8s instead of kubectl - #1016

Open
mavaras wants to merge 1 commit into
konflux-ci:mainfrom
mavaras:ISV-7560
Open

feat(ISV-7560): internal-request uses k8s instead of kubectl#1016
mavaras wants to merge 1 commit into
konflux-ci:mainfrom
mavaras:ISV-7560

Conversation

@mavaras

@mavaras mavaras commented Sep 8, 2026

Copy link
Copy Markdown
Contributor
  • internal-request uses python library for k8s to replace the use of kubectl
  • kubernetes dependency added to pyproject
  • tests updated accordingly

@mavaras

mavaras commented Sep 8, 2026

Copy link
Copy Markdown
Contributor Author

@kosciCZ please take a look here

@qodo-app-for-konflux-ci

Copy link
Copy Markdown

PR Summary by Qodo

Use Kubernetes client for InternalRequest operations

✨ Enhancement 🧪 Tests ⚙️ Configuration changes 🕐 20-40 Minutes

Grey Divider

AI Description

• Replace kubectl subprocesses with Kubernetes CustomObjects API calls for InternalRequest lifecycle
 operations.
• Auto-detect in-cluster or kubeconfig credentials and resolve the active namespace.
• Add Kubernetes dependency and client-mocked coverage for creation, polling, cleanup, and results.
Diagram

sequenceDiagram
    actor Caller as Task Caller
    participant Helper as IR Helper
    participant Config as Config Loader
    participant Client as CustomObjects API
    participant API as Kubernetes API
    Caller->>Helper: Create or fetch
    Helper->>Config: Load cluster config
    Config-->>Helper: Credentials and namespace
    Helper->>Client: Create list get delete
    Client->>API: Custom resource request
    API-->>Client: Resource object
    Client-->>Helper: Parsed mapping
    Helper-->>Caller: Name status results
Loading
High-Level Assessment

The selected approach is appropriate: CustomObjectsApi directly supports the InternalRequest CRD without requiring generated client bindings, removes the kubectl runtime dependency, and enables straightforward client injection in tests. Retaining kubectl would preserve subprocess and parsing overhead, while generating a typed CRD client would add maintenance disproportionate to this helper's needs.

Files changed (3) +320 / -285

Enhancement (1) +113 / -75
internal_request.pyReplace kubectl with Kubernetes CustomObjects API calls +113/-75

Replace kubectl with Kubernetes CustomObjects API calls

• Migrates InternalRequest creation, listing, polling, cleanup, and result retrieval from kubectl subprocesses to CustomObjectsApi. Adds in-cluster configuration with kubeconfig fallback, namespace discovery, and injectable clients for reuse and testing.

src/helpers/internal_request/internal_request.py

Tests (1) +206 / -210
test_internal_request.pyTest Kubernetes client-based InternalRequest operations +206/-210

Test Kubernetes client-based InternalRequest operations

• Replaces subprocess mocks with an injectable CustomObjectsApi mock across lifecycle tests. Adds coverage for namespace resolution, configuration fallback, API responses, and creation logging.

src/helpers/internal_request/tests/test_internal_request.py

Other (1) +1 / -0
pyproject.tomlAdd the Kubernetes Python client dependency +1/-0

Add the Kubernetes Python client dependency

• Adds kubernetes 28.0 or newer as a runtime dependency for direct cluster API access.

pyproject.toml

@qodo-app-for-konflux-ci

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (2) 📘 Rule violations (1) 🔗 Cross-repo conflicts (1) 📜 Skill insights (0)

Grey Divider


Action required

1. Retries can overlap old pipelines 🔗 Cross-repo conflict ☼ Reliability
Description
cleanup_existing_requests treats delete_namespaced_custom_object returning as completed
deletion, replacing kubectl delete --wait=true --timeout=60s with an asynchronous API call
followed only by a fixed five-second sleep even though internal-services retains the InternalRequest
while its finalizer cancels the associated PipelineRun. When finalizer or PipelineRun reconciliation
takes longer than five seconds, create submits the replacement while the prior request is still
terminating and driving operator work, potentially before its PipelineRun has been patched to
Cancelled.
Code

src/helpers/internal_request/internal_request.py[R452-455]

+        k8s_api.delete_namespaced_custom_object(
+            group=_IR_GROUP,
+            version=_IR_VERSION,
+            namespace=namespace,
Relevance

●● Moderate

Strong reliability concern, but no repository precedent confirms acceptance of asynchronous deletion
fixes.

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The cleanup path invokes the deletion endpoint, logs success, and waits only a fixed five seconds
before the create path constructs and submits the replacement, without checking that the old
InternalRequest has disappeared. The removed implementation explicitly used `kubectl delete
--wait=true --timeout=60s`, while internal-services finalization occurs during a later deletion
reconciliation; its test shows the request remaining with a deletion timestamp until a second
reconcile patches the associated PipelineRun to Cancelled, proving that the new API call and sleep
do not preserve the previous ordering guarantee.

src/helpers/internal_request/internal_request.py[444-465]
src/helpers/internal_request/internal_request.py[561-575]
src/helpers/internal_request/internal_request.py[421-465]
src/helpers/internal_request/internal_request.py[537-575]
src/helpers/internal_request/internal_request.py[117-121]
src/helpers/internal_request/internal_request.py[452-465]
External repo: konflux-ci/internal-services, controllers/internalrequest/adapter.go [77-101]
External repo: konflux-ci/internal-services, controllers/internalrequest/adapter.go [134-145]
External repo: konflux-ci/internal-services, controllers/internalrequest/adapter_test.go [368-390]
External repo: konflux-ci/internal-services, tekton/pipeline_run.go [38-40]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Restore synchronous cleanup semantics after deleting an InternalRequest. The Kubernetes deletion API only requests deletion and can return while the resource remains terminating and the internal-services finalizer has not yet cancelled the associated PipelineRun, so cleanup must not allow a replacement request to be created at that point.

## Issue Context
The previous `kubectl delete --wait=true --timeout=60s` invocation waited for resource deletion, whereas the new `delete_namespaced_custom_object` call is followed only by a fixed five-second propagation delay. Poll each deleted InternalRequest until `get_namespaced_custom_object` returns a not-found response, or use an equivalent API-based wait, with a bounded timeout comparable to the former 60-second timeout. Retain the existing propagation delay only after deletion has completed, and ensure the subsequent create path cannot run while cleanup is still waiting. Update or extend the cleanup tests to cover asynchronous deletion completion and timeout behavior.

## Fix Focus Areas
- src/helpers/internal_request/internal_request.py[421-465]
- src/helpers/internal_request/internal_request.py[561-575]
- src/helpers/internal_request/tests/test_internal_request.py[107-128]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

2. Maintainers lose fixture type checks 📘 Rule violation ⚙ Maintainability
Description
The new k8s_api fixture declares no return type annotation. Type checking therefore cannot
validate the yielded mock client or detect incompatible fixture changes reaching every modified test
that consumes it.
Code

src/helpers/internal_request/tests/test_internal_request.py[R22-23]

+@pytest.fixture()
+def k8s_api():
Relevance

●●● Strong

Explicit fixture return annotation is a deterministic compliance fix required by the cited rule.

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
PR Compliance ID 913 requires every newly added function to have an explicit return annotation. The
added fixture at lines 22-27 defines k8s_api() without a -> annotation and yields a mock
Kubernetes client.

Rule 913: Require type hints for all function parameters and return types
src/helpers/internal_request/tests/test_internal_request.py[22-27]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The new `k8s_api` fixture is missing an explicit return type annotation.

## Issue Context
The fixture yields a `MagicMock`, and the compliance checklist requires annotations for all parameters and return types in newly added functions.

## Fix Focus Areas
- src/helpers/internal_request/tests/test_internal_request.py[22-27]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


3. Invalid requests report setup errors 🐞 Bug ≡ Correctness
Description
create() constructs the default Kubernetes client before validating pipeline and the required
Git resolver parameters. When a caller supplies invalid arguments outside a configured cluster
environment, configuration loading fails before the intended ValueError is raised.
Code

src/helpers/internal_request/internal_request.py[R537-538]

+    if k8s_api is None:
+        k8s_api = _default_k8s_api()
Relevance

●●● Strong

Input validation should precede Kubernetes configuration loading to preserve intended ValueError
behavior.

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The client initialization precedes all input checks, and the default-client helper attempts
in-cluster then kubeconfig loading. The existing tests establish that missing pipeline and Git
resolver parameters are validation failures, but inject a mock client and therefore do not exercise
the new initialization order.

src/helpers/internal_request/internal_request.py[145-156]
src/helpers/internal_request/internal_request.py[537-553]
src/helpers/internal_request/tests/test_internal_request.py[217-239]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Argument validation should run before Kubernetes configuration is loaded so invalid calls consistently raise the documented validation errors without requiring cluster credentials or a kubeconfig.

## Issue Context
Move default-client construction until after validation of `pipeline`, required Git parameters, and timeout values. Retain the injected-client behavior used by callers and tests.

## Fix Focus Areas
- src/helpers/internal_request/internal_request.py[537-560]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


4. Invalid waits report setup errors 🐞 Bug ≡ Correctness
Description
wait_for_completion() initializes the default Kubernetes client before checking that exactly one
selector was provided. Calls with neither or both selectors now require usable Kubernetes
configuration before they can receive the function's intended ValueError.
Code

src/helpers/internal_request/internal_request.py[R339-340]

+    if k8s_api is None:
+        k8s_api = _default_k8s_api()
Relevance

●●● Strong

Validation should precede Kubernetes configuration loading to preserve documented ValueError
behavior.

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The newly added initialization runs before the selector check, while the default client loads
in-cluster or kubeconfig configuration. The tests only cover invalid selectors with an injected mock
client, masking the behavior when callers use the public default path.

src/helpers/internal_request/internal_request.py[145-156]
src/helpers/internal_request/internal_request.py[339-346]
src/helpers/internal_request/tests/test_internal_request.py[300-308]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Selector validation should run before Kubernetes configuration is loaded so invalid wait calls consistently raise the declared `ValueError` without requiring cluster credentials or a kubeconfig.

## Issue Context
Perform the exactly-one-selector check first, then create the default client only for valid polling requests. Preserve support for an explicitly injected client.

## Fix Focus Areas
- src/helpers/internal_request/internal_request.py[339-346]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Context sources
✅ Compliance rules (platform): 23 rules
✅ Cross-repo context — repo relationships
  Explored: repo: konflux-ci/internal-services (sha: 7d8780e9)
  Explored: repo: konflux-ci/konflux-ci (sha: 85970a81)
  Explored: repo: konflux-ci/release-service (sha: dcccd163)
Review mode: 🧠 Deep: This replaces kubectl with Kubernetes API calls across creation, deletion, namespace/config discovery, polling, and result handling, creating many independent, behavior-sensitive paths where redundant review could catch subtle compatibility or cluster-configuration defects.

Grey Divider

Tip of the day
💡 Did you know, you can tweak Display preferences with a live preview to see your comment before it ships

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Qodo Logo

Comment on lines +22 to +23
@pytest.fixture()
def k8s_api():

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Remediation recommended

1. Maintainers lose fixture type checks 📘 Rule violation ⚙ Maintainability

The new k8s_api fixture declares no return type annotation. Type checking therefore cannot
validate the yielded mock client or detect incompatible fixture changes reaching every modified test
that consumes it.
Agent Prompt
## Issue description
The new `k8s_api` fixture is missing an explicit return type annotation.

## Issue Context
The fixture yields a `MagicMock`, and the compliance checklist requires annotations for all parameters and return types in newly added functions.

## Fix Focus Areas
- src/helpers/internal_request/tests/test_internal_request.py[22-27]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment on lines +537 to +538
if k8s_api is None:
k8s_api = _default_k8s_api()

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Remediation recommended

2. Invalid requests report setup errors 🐞 Bug ≡ Correctness

create() constructs the default Kubernetes client before validating pipeline and the required
Git resolver parameters. When a caller supplies invalid arguments outside a configured cluster
environment, configuration loading fails before the intended ValueError is raised.
Agent Prompt
## Issue description
Argument validation should run before Kubernetes configuration is loaded so invalid calls consistently raise the documented validation errors without requiring cluster credentials or a kubeconfig.

## Issue Context
Move default-client construction until after validation of `pipeline`, required Git parameters, and timeout values. Retain the injected-client behavior used by callers and tests.

## Fix Focus Areas
- src/helpers/internal_request/internal_request.py[537-560]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment on lines +339 to +340
if k8s_api is None:
k8s_api = _default_k8s_api()

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Remediation recommended

3. Invalid waits report setup errors 🐞 Bug ≡ Correctness

wait_for_completion() initializes the default Kubernetes client before checking that exactly one
selector was provided. Calls with neither or both selectors now require usable Kubernetes
configuration before they can receive the function's intended ValueError.
Agent Prompt
## Issue description
Selector validation should run before Kubernetes configuration is loaded so invalid wait calls consistently raise the declared `ValueError` without requiring cluster credentials or a kubeconfig.

## Issue Context
Perform the exactly-one-selector check first, then create the default client only for valid polling requests. Preserve support for an explicitly injected client.

## Fix Focus Areas
- src/helpers/internal_request/internal_request.py[339-346]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment on lines +452 to +455
k8s_api.delete_namespaced_custom_object(
group=_IR_GROUP,
version=_IR_VERSION,
namespace=namespace,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Action required

4. Retries can overlap old pipelines 🔗 Cross-repo conflict ☼ Reliability

cleanup_existing_requests treats delete_namespaced_custom_object returning as completed
deletion, replacing kubectl delete --wait=true --timeout=60s with an asynchronous API call
followed only by a fixed five-second sleep even though internal-services retains the InternalRequest
while its finalizer cancels the associated PipelineRun. When finalizer or PipelineRun reconciliation
takes longer than five seconds, create submits the replacement while the prior request is still
terminating and driving operator work, potentially before its PipelineRun has been patched to
Cancelled.
Agent Prompt
## Issue description
Restore synchronous cleanup semantics after deleting an InternalRequest. The Kubernetes deletion API only requests deletion and can return while the resource remains terminating and the internal-services finalizer has not yet cancelled the associated PipelineRun, so cleanup must not allow a replacement request to be created at that point.

## Issue Context
The previous `kubectl delete --wait=true --timeout=60s` invocation waited for resource deletion, whereas the new `delete_namespaced_custom_object` call is followed only by a fixed five-second propagation delay. Poll each deleted InternalRequest until `get_namespaced_custom_object` returns a not-found response, or use an equivalent API-based wait, with a bounded timeout comparable to the former 60-second timeout. Retain the existing propagation delay only after deletion has completed, and ensure the subsequent create path cannot run while cleanup is still waiting. Update or extend the cleanup tests to cover asynchronous deletion completion and timeout behavior.

## Fix Focus Areas
- src/helpers/internal_request/internal_request.py[421-465]
- src/helpers/internal_request/internal_request.py[561-575]
- src/helpers/internal_request/tests/test_internal_request.py[107-128]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

- internal-request uses python library for k8s to replace kubectl
- kubernetes dependency added to pyproject
- tests updated accordingly

Signed-off-by: mvaras <mvaras@redhat.com>
@codecov-commenter

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 89.79592% with 5 lines in your changes missing coverage. Please review.
✅ Project coverage is 95.80%. Comparing base (7a717c1) to head (faf5095).
⚠️ Report is 9 commits behind head on main.

Files with missing lines Patch % Lines
src/helpers/internal_request/internal_request.py 89.79% 5 Missing ⚠️
Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##             main    #1016      +/-   ##
==========================================
- Coverage   97.24%   95.80%   -1.45%     
==========================================
  Files         205      257      +52     
  Lines       12815    13199     +384     
==========================================
+ Hits        12462    12645     +183     
- Misses        353      554     +201     
Flag Coverage Δ
unit-tests 95.80% <89.79%> (-1.45%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
src/helpers/internal_request/internal_request.py 97.10% <89.79%> (-2.37%) ⬇️

... and 63 files with indirect coverage changes


Continue to review full report in Codecov by Harness.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 7a717c1...faf5095. Read the comment docs.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

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