Skip to content

feat: validate APISIX resources in webhooks#393

Open
AlinsRan wants to merge 25 commits intomasterfrom
feat/webhook-adc-validation
Open

feat: validate APISIX resources in webhooks#393
AlinsRan wants to merge 25 commits intomasterfrom
feat/webhook-adc-validation

Conversation

@AlinsRan
Copy link
Copy Markdown
Contributor

@AlinsRan AlinsRan commented Apr 27, 2026

Summary

  • add shared ADC-backed admission validation for APISIX CRD webhooks
  • deny writes on explicit validation failures while keeping transport and backend errors fail-open
  • add unit and e2e coverage for ApisixRoute, ApisixConsumer, Consumer, and ApisixTls webhook validation flows

Testing

  • go test ./internal/adc/client ./internal/webhook/v1/...
  • go test ./test/e2e/apisix -ginkgo.focus='Test (ApisixRoute|ApisixConsumer|Consumer|ApisixTls) Webhook' (currently 4 passed / 4 failed; route and consumer standalone validate payloads still need follow-up)

Summary by CodeRabbit

  • New Features

    • ADC-based admission validation added for Routes, Consumers, and TLS; supports standalone APISIX admin validation.
    • Executors now perform per-backend validation and aggregate server-specific validation results.
  • Improvements

    • Admission rejects invalid secrets/configs more strictly; preserves fail-open for infra errors.
    • Duplicate key-auth credential detection across same gateway prevents conflicting Consumers.
  • Tests

    • Expanded unit and e2e coverage for ADC flows, payload translation, and admission-deny scenarios.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 27, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds ADC validation: new Client.Validate and executor.Validate paths, APISIX-specific validate requests, structured ADC validation error types, controller helpers to prepare resources for validation, an adcAdmissionValidator wired into webhooks, and supporting unit and e2e tests that assert admission deny/fail-open behaviors.

Changes

Cohort / File(s) Summary
ADC client & executor
internal/adc/client/client.go, internal/adc/client/executor.go, internal/adc/client/executor_test.go
Add Client.Validate; extend ADCExecutor with Validate and implement in HTTPADCExecutor. Writes temp sync files, records I/O metrics, builds validate args, issues per-backend validate requests (including APISIX POST /apisix/admin/configs/validate), parses responses into ADCValidateResult, and aggregates per-server validation errors. Includes APISIX payload transformation tests.
Validation error types
internal/types/error.go
Introduce ADCValidationErrors, ADCValidationError, ADCValidationServerAddrError, ADCValidationDetail with Error() implementations to represent and format aggregated validation failures.
Controller validation helpers
internal/controller/webhook_validation.go
Add Prepare*ForValidation helpers to build translation contexts for ApisixRoute, ApisixConsumer, v1alpha1.Consumer, and ApisixTls (ingress-class/gateway resolution and reconciler processing).
Webhook admission integration
internal/webhook/v1/adc_validation.go, internal/webhook/v1/apisixroute_webhook.go, internal/webhook/v1/apisixconsumer_webhook.go, internal/webhook/v1/apisixtls_webhook.go, internal/webhook/v1/consumer_webhook.go
Add adcAdmissionValidator that translates admission objects into ADC tasks and calls client.Validate. Webhooks initialize/store adcValidator and initErr; ValidateCreate/ValidateUpdate compute warnings and invoke ADC validation or return init error. Consumer webhook adds duplicate key-auth detection across Consumers sharing a gateway.
Webhook tests & helpers
internal/webhook/v1/..., internal/webhook/v1/adc_validation_test.go
Update test scheme registration and ingress-class seeding; add mock ADC server helpers; add tests for ADC rejection, fail-open behavior, APISIX validate endpoint usage, and consumer duplicate-key cases.
E2E webhook tests & helpers
test/e2e/webhook/*.go, test/e2e/webhook/helpers.go
Add expectAdmissionDenied helper; update e2e tests to assert admission denial for missing references or ADC validation failures; add ADC-specific e2e tests (invalid TLS/cert/plugin/credentials) and success-after-fix cases.
CI workflow
.github/workflows/e2e-test-k8s.yml
Change Kind kubeconfig acquisition to use kind export kubeconfig and ensure prior cluster teardown with make kind-down before make kind-up.

Sequence Diagram(s)

sequenceDiagram
    participant K8sClient as Kubernetes Client
    participant Webhook as Admission Webhook
    participant ADCValidator as adcAdmissionValidator
    participant Controller as Controller Helper
    participant ADCClient as ADC Client
    participant Executor as HTTP Executor
    participant ADCServer as ADC / APISIX Server

    K8sClient->>Webhook: submit resource (Route/Consumer/TLS)
    Webhook->>ADCValidator: Validate(resource)
    ADCValidator->>Controller: Prepare*ForValidation(resource)
    Controller-->>ADCValidator: TranslateContext
    ADCValidator->>ADCClient: build Task / configs
    ADCClient->>ADCClient: write temp sync file, record IO metrics
    ADCClient->>Executor: Validate(config, args)
    Executor->>ADCServer: POST /validate or POST /apisix/admin/configs/validate
    ADCServer-->>Executor: 2xx/400 + payload
    Executor->>ADCClient: structured validation result or infra error
    ADCClient-->>ADCValidator: aggregated ADCValidationErrors or error
    ADCValidator-->>Webhook: error (deny) or nil (allow)
    Webhook-->>K8sClient: admission response (allowed/denied)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

🚥 Pre-merge checks | ✅ 5 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
E2e Test Quality Review ⚠️ Warning E2E test suite has critical gaps: 4 failing tests for new ADC validation, no fail-open behavior tests, insufficient scenario coverage, generic assertions, and conditional test execution. Fix 4 failing e2e tests, add fail-open behavior tests with ADC unavailability simulation, enhance assertions for specific validation errors, add edge case scenarios, verify all resource types pass with apisix-standalone.
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The pull request title 'feat: validate APISIX resources in webhooks' directly and clearly summarizes the main change: adding validation for APISIX resources through webhooks, which is the central theme across all modified files.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Security Check ✅ Passed Comprehensive security review of ADC validation implementation across seven vulnerability categories found no critical security issues. Credentials are properly handled without logging or exposure in error messages.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/webhook-adc-validation

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 27, 2026

conformance test report - apisix-standalone mode

apiVersion: gateway.networking.k8s.io/v1
date: "2026-04-27T17:02:49Z"
gatewayAPIChannel: experimental
gatewayAPIVersion: v1.3.0
implementation:
  contact: null
  organization: APISIX
  project: apisix-ingress-controller
  url: https://github.com/apache/apisix-ingress-controller.git
  version: v2.0.0
kind: ConformanceReport
mode: default
profiles:
- core:
    result: success
    statistics:
      Failed: 0
      Passed: 12
      Skipped: 0
  name: GATEWAY-GRPC
  summary: Core tests succeeded.
- core:
    result: partial
    skippedTests:
    - HTTPRouteHTTPSListener
    statistics:
      Failed: 0
      Passed: 32
      Skipped: 1
  extended:
    result: partial
    skippedTests:
    - HTTPRouteRedirectPortAndScheme
    statistics:
      Failed: 0
      Passed: 11
      Skipped: 1
    supportedFeatures:
    - GatewayAddressEmpty
    - GatewayPort8080
    - HTTPRouteBackendProtocolWebSocket
    - HTTPRouteDestinationPortMatching
    - HTTPRouteHostRewrite
    - HTTPRouteMethodMatching
    - HTTPRoutePathRewrite
    - HTTPRoutePortRedirect
    - HTTPRouteQueryParamMatching
    - HTTPRouteRequestMirror
    - HTTPRouteResponseHeaderModification
    - HTTPRouteSchemeRedirect
    unsupportedFeatures:
    - GatewayHTTPListenerIsolation
    - GatewayInfrastructurePropagation
    - GatewayStaticAddresses
    - HTTPRouteBackendProtocolH2C
    - HTTPRouteBackendRequestHeaderModification
    - HTTPRouteBackendTimeout
    - HTTPRouteParentRefPort
    - HTTPRoutePathRedirect
    - HTTPRouteRequestMultipleMirrors
    - HTTPRouteRequestPercentageMirror
    - HTTPRouteRequestTimeout
  name: GATEWAY-HTTP
  summary: Core tests partially succeeded with 1 test skips. Extended tests partially
    succeeded with 1 test skips.
- core:
    result: partial
    skippedTests:
    - TLSRouteSimpleSameNamespace
    statistics:
      Failed: 0
      Passed: 10
      Skipped: 1
  name: GATEWAY-TLS
  summary: Core tests partially succeeded with 1 test skips.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 27, 2026

conformance test report - apisix mode

apiVersion: gateway.networking.k8s.io/v1
date: "2026-04-27T17:03:33Z"
gatewayAPIChannel: experimental
gatewayAPIVersion: v1.3.0
implementation:
  contact: null
  organization: APISIX
  project: apisix-ingress-controller
  url: https://github.com/apache/apisix-ingress-controller.git
  version: v2.0.0
kind: ConformanceReport
mode: default
profiles:
- core:
    result: success
    statistics:
      Failed: 0
      Passed: 12
      Skipped: 0
  name: GATEWAY-GRPC
  summary: Core tests succeeded.
- core:
    failedTests:
    - HTTPRouteInvalidBackendRefUnknownKind
    result: failure
    skippedTests:
    - HTTPRouteHTTPSListener
    statistics:
      Failed: 1
      Passed: 31
      Skipped: 1
  extended:
    result: partial
    skippedTests:
    - HTTPRouteRedirectPortAndScheme
    statistics:
      Failed: 0
      Passed: 11
      Skipped: 1
    supportedFeatures:
    - GatewayAddressEmpty
    - GatewayPort8080
    - HTTPRouteBackendProtocolWebSocket
    - HTTPRouteDestinationPortMatching
    - HTTPRouteHostRewrite
    - HTTPRouteMethodMatching
    - HTTPRoutePathRewrite
    - HTTPRoutePortRedirect
    - HTTPRouteQueryParamMatching
    - HTTPRouteRequestMirror
    - HTTPRouteResponseHeaderModification
    - HTTPRouteSchemeRedirect
    unsupportedFeatures:
    - GatewayHTTPListenerIsolation
    - GatewayInfrastructurePropagation
    - GatewayStaticAddresses
    - HTTPRouteBackendProtocolH2C
    - HTTPRouteBackendRequestHeaderModification
    - HTTPRouteBackendTimeout
    - HTTPRouteParentRefPort
    - HTTPRoutePathRedirect
    - HTTPRouteRequestMultipleMirrors
    - HTTPRouteRequestPercentageMirror
    - HTTPRouteRequestTimeout
  name: GATEWAY-HTTP
  summary: Core tests failed with 1 test failures. Extended tests partially succeeded
    with 1 test skips.
- core:
    result: partial
    skippedTests:
    - TLSRouteSimpleSameNamespace
    statistics:
      Failed: 0
      Passed: 10
      Skipped: 1
  name: GATEWAY-TLS
  summary: Core tests partially succeeded with 1 test skips.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 27, 2026

conformance test report

apiVersion: gateway.networking.k8s.io/v1
date: "2026-04-27T17:26:29Z"
gatewayAPIChannel: experimental
gatewayAPIVersion: v1.3.0
implementation:
  contact: null
  organization: APISIX
  project: apisix-ingress-controller
  url: https://github.com/apache/apisix-ingress-controller.git
  version: v2.0.0
kind: ConformanceReport
mode: default
profiles:
- core:
    failedTests:
    - GRPCExactMethodMatching
    - GRPCRouteHeaderMatching
    - GRPCRouteListenerHostnameMatching
    - GatewayModifyListeners
    result: failure
    statistics:
      Failed: 4
      Passed: 8
      Skipped: 0
  name: GATEWAY-GRPC
  summary: Core tests failed with 4 test failures.
- core:
    failedTests:
    - GatewayModifyListeners
    result: failure
    skippedTests:
    - HTTPRouteHTTPSListener
    statistics:
      Failed: 1
      Passed: 31
      Skipped: 1
  extended:
    failedTests:
    - HTTPRouteBackendProtocolWebSocket
    result: failure
    skippedTests:
    - HTTPRouteRedirectPortAndScheme
    statistics:
      Failed: 1
      Passed: 10
      Skipped: 1
    supportedFeatures:
    - GatewayAddressEmpty
    - GatewayPort8080
    - HTTPRouteBackendProtocolWebSocket
    - HTTPRouteDestinationPortMatching
    - HTTPRouteHostRewrite
    - HTTPRouteMethodMatching
    - HTTPRoutePathRewrite
    - HTTPRoutePortRedirect
    - HTTPRouteQueryParamMatching
    - HTTPRouteRequestMirror
    - HTTPRouteResponseHeaderModification
    - HTTPRouteSchemeRedirect
    unsupportedFeatures:
    - GatewayHTTPListenerIsolation
    - GatewayInfrastructurePropagation
    - GatewayStaticAddresses
    - HTTPRouteBackendProtocolH2C
    - HTTPRouteBackendRequestHeaderModification
    - HTTPRouteBackendTimeout
    - HTTPRouteParentRefPort
    - HTTPRoutePathRedirect
    - HTTPRouteRequestMultipleMirrors
    - HTTPRouteRequestPercentageMirror
    - HTTPRouteRequestTimeout
  name: GATEWAY-HTTP
  summary: Core tests failed with 1 test failures. Extended tests failed with 1 test
    failures.
- core:
    failedTests:
    - GatewayModifyListeners
    - TLSRouteSimpleSameNamespace
    result: failure
    statistics:
      Failed: 2
      Passed: 9
      Skipped: 0
  name: GATEWAY-TLS
  summary: Core tests failed with 2 test failures.

@AlinsRan AlinsRan marked this pull request as ready for review April 27, 2026 07:21
Copilot AI review requested due to automatic review settings April 27, 2026 07:21
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR adds ADC-backed admission validation to APISIX-related validating webhooks so that explicit validation failures are rejected while infrastructure/backend issues remain fail-open, and extends unit/e2e test coverage for these flows.

Changes:

  • Introduce a shared adcAdmissionValidator to translate resources and call ADC validation during admission for ApisixRoute, ApisixConsumer, Consumer, and ApisixTls.
  • Extend ADC client/executor with a validation pathway (including APISIX standalone /apisix/admin/configs/validate support) and richer validation error types.
  • Update and add unit + e2e tests to assert admission rejections for invalid resources and duplicate credentials.

Reviewed changes

Copilot reviewed 20 out of 20 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
test/e2e/webhook/helpers.go Adds a helper for asserting admission denials and non-existence of rejected resources.
test/e2e/webhook/consumer.go Adds an e2e case for rejecting duplicate Consumer credentials.
test/e2e/webhook/apisixtls.go Switches missing-secret behavior to rejection and adds invalid TLS-material rejection coverage.
test/e2e/webhook/apisixroute.go Adds e2e denial coverage for ADC validation failures and adjusts missing-ref expectations.
test/e2e/webhook/apisixconsumer.go Switches missing-secret behavior to rejection and adds duplicate-credential denial coverage.
internal/webhook/v1/consumer_webhook_test.go Adds unit test coverage for duplicate key-auth credential denial.
internal/webhook/v1/consumer_webhook.go Wires ADC validation into admission and adds duplicate key-auth enforcement.
internal/webhook/v1/apisixtls_webhook_test.go Adds ADC-deny unit test and improves test ingress class setup.
internal/webhook/v1/apisixtls_webhook.go Wires ADC validation into ApisixTls admission validation.
internal/webhook/v1/apisixroute_webhook_test.go Adds ADC-deny + fail-open unit tests for ApisixRoute.
internal/webhook/v1/apisixroute_webhook.go Wires ADC validation into ApisixRoute admission validation.
internal/webhook/v1/apisixconsumer_webhook_test.go Adds ADC-deny unit test for ApisixConsumer and improves ingress class setup.
internal/webhook/v1/apisixconsumer_webhook.go Wires ADC validation into ApisixConsumer admission validation.
internal/webhook/v1/adc_validation_test.go Adds shared ADC mock helpers for webhook validation tests.
internal/webhook/v1/adc_validation.go Introduces shared admission validator translating resources and calling ADC validate with fail-open semantics.
internal/types/error.go Adds typed ADC validation error structures for propagating explicit validation failures.
internal/controller/webhook_validation.go Adds shared “prepare-for-validation” helpers used by admission validation translation.
internal/adc/client/executor_test.go Adds unit tests for APISIX standalone validate payload conversion for SSL certificates.
internal/adc/client/executor.go Adds ADC validation execution (incl. APISIX validate endpoint) and validation response parsing.
internal/adc/client/client.go Adds Client.Validate to drive executor validation per resolved config.
Comments suppressed due to low confidence (1)

internal/adc/client/executor.go:524

  • buildHTTPRequest logs the entire reqBody (including Token and full translated Config) at V(1). With admission validation this will run on user writes, so tokens/credentials can end up in logs. Please redact/remove sensitive fields from logs (token, credentials, TLS material), or replace with a minimal summary (cacheKey, backend, resource counts).
	reqBody := ADCServerRequest{
		Task: ADCServerTask{
			Opts: ADCServerOpts{
				Backend:             config.BackendType,
				Server:              strings.Split(serverAddr, ","),
				Token:               config.Token,
				LabelSelector:       labels,
				IncludeResourceType: types,
				TlsSkipVerify:       ptr.To(!tlsVerify),
				CacheKey:            config.Name,
			},
			Config: *resources,
		},
	}

	e.log.V(1).Info("prepared request body", "body", reqBody)


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

"url", validateURL,
"server", serverAddr,
"cacheKey", config.Name,
"body", body,
Copy link

Copilot AI Apr 27, 2026

Choose a reason for hiding this comment

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

buildAPISIXValidateRequest logs the full validate payload (including TLS certificates/private keys and other sensitive config) via e.log.V(1). This can leak secrets into controller logs during admission validation. Consider removing the payload from logs or logging only a redacted/summary view (e.g., counts/IDs) and never the key material.

Suggested change
"body", body,
"payloadBytes", len(jsonData),

Copilot uses AI. Check for mistakes.
}
body.SSLs = append(body.SSLs, sslMap)
}

Copy link

Copilot AI Apr 27, 2026

Choose a reason for hiding this comment

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

buildAPISIXValidatePayload defines fields for global_rules and plugin_metadata in apisixValidateRequest, but never populates them from resources.GlobalRules / resources.PluginMetadata. This can make standalone validation incomplete or inconsistent with what would be synced. Populate those fields (and any other relevant top-level resources) so validation matches actual config behavior.

Suggested change
for _, globalRule := range resources.GlobalRules {
if globalRule == nil {
continue
}
globalRuleMap, err := toMap(globalRule)
if err != nil {
return nil, err
}
body.GlobalRules = append(body.GlobalRules, globalRuleMap)
}
for _, pluginMetadata := range resources.PluginMetadata {
if pluginMetadata == nil {
continue
}
pluginMetadataMap, err := toMap(pluginMetadata)
if err != nil {
return nil, err
}
body.PluginMetadata = append(body.PluginMetadata, pluginMetadataMap)
}

Copilot uses AI. Check for mistakes.
Comment on lines +152 to +168
var consumers apisixv1alpha1.ConsumerList
if err := v.Client.List(ctx, &consumers); err != nil {
return err
}

for i := range consumers.Items {
existing := &consumers.Items[i]
if existing.Namespace == consumer.Namespace && existing.Name == consumer.Name {
continue
}
if !sameConsumerGatewayRef(existing, consumer) {
continue
}

existingKeys, err := v.extractKeyAuthKeys(ctx, existing)
if err != nil {
return err
Copy link

Copilot AI Apr 27, 2026

Choose a reason for hiding this comment

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

validateDuplicateKeyAuthCredentials lists all Consumer objects on every create/update, and then may fetch referenced Secrets for each existing Consumer. This is O(N) (and potentially N+1 Secret GETs) in the admission path and can become a latency/availability problem in clusters with many Consumers. Consider scoping the list when gatewayRef.namespace is empty (in-namespace only), and/or using an indexed field selector / cached index keyed by gatewayRef + key-auth key to avoid full scans and repeated secret fetches.

Copilot uses AI. Check for mistakes.
Key string `json:"key"`
}
if err := json.Unmarshal(credential.Config.Raw, &cfg); err != nil {
return "", nil
Copy link

Copilot AI Apr 27, 2026

Choose a reason for hiding this comment

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

extractCredentialKey silently ignores JSON unmarshal errors by returning "" and nil. If a key-auth credential has malformed JSON, this bypasses duplicate-key detection and may allow invalid config through (especially when ADC validation fails open). Return the unmarshal error (or convert it into an admission denial with a clear message) so malformed credential configs are rejected deterministically.

Suggested change
return "", nil
return "", fmt.Errorf("failed to parse key-auth credential config for consumer %s/%s: %w", consumer.Namespace, consumer.Name, err)

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 8

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
internal/webhook/v1/apisixconsumer_webhook_test.go (1)

45-66: ⚠️ Potential issue | 🟡 Minor

Extract "apisix" to a constant to fix linter error.

The string "apisix" appears multiple times across test files. The linter flags this as a goconst violation.

🔧 Proposed fix

Add a constant at package level (e.g., in a shared test helper file or at the top of this file):

const defaultIngressClassName = "apisix"

Then update the comparison and object creation:

 for _, obj := range objects {
   ingressClass, ok := obj.(*networkingv1.IngressClass)
-  if ok && ingressClass.Name == "apisix" {
+  if ok && ingressClass.Name == defaultIngressClassName {
     hasManagedIngressClass = true
     break
   }
 }
 if !hasManagedIngressClass {
   managed = append(managed, &networkingv1.IngressClass{
     ObjectMeta: metav1.ObjectMeta{
-      Name: "apisix",
+      Name: defaultIngressClassName,
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/webhook/v1/apisixconsumer_webhook_test.go` around lines 45 - 66,
Introduce a package-level constant (e.g., const defaultIngressClassName =
"apisix") and replace all literal "apisix" occurrences in this test with that
constant: use defaultIngressClassName when comparing ingressClass.Name in the
loop that sets hasManagedIngressClass and when constructing the
&networkingv1.IngressClass{ObjectMeta: {Name: ..., Annotations: ...}, Spec:
{Controller: config.ControllerConfig.ControllerName}} so the test no longer
violates goconst; ensure the new constant is visible to other test files if they
share the same package.
🧹 Nitpick comments (3)
internal/controller/webhook_validation.go (1)

44-49: Consider making supportsEndpointSlice configurable.

The value supportsEndpointSlice: true is hardcoded here. If the cluster doesn't support EndpointSlices (older Kubernetes versions), this could cause issues during validation processing.

Consider passing this as a parameter or deriving it from a shared configuration, similar to how the actual reconciler might determine this value at runtime. However, since EndpointSlices are GA since Kubernetes 1.21 and this is a recent codebase, hardcoding true is likely acceptable.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/controller/webhook_validation.go` around lines 44 - 49, The
hardcoded supportsEndpointSlice: true on the ApisixRouteReconciler instantiation
in webhook_validation.go can break clusters without EndpointSlice support;
change the construction of ApisixRouteReconciler to accept a configurable flag
or derive it from shared cluster feature detection (e.g., read from a passed-in
config struct or call a helper that checks API availability) and assign that
value to ApisixRouteReconciler.supportsEndpointSlice instead of the literal
true; update any callers that create this reconciler to pass the
configured/derived boolean so validation honors cluster capabilities.
internal/webhook/v1/consumer_webhook.go (1)

143-178: Consider performance impact of listing all Consumers on every validation.

validateDuplicateKeyAuthCredentials calls v.Client.List(ctx, &consumers) without any field/label selector, fetching all Consumer resources cluster-wide. For clusters with many consumers, this could add significant latency to every create/update admission.

Consider adding a label selector to filter by gateway reference, or implementing a cache/index if this becomes a bottleneck.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/webhook/v1/consumer_webhook.go` around lines 143 - 178, The code in
validateDuplicateKeyAuthCredentials currently does a cluster-wide
v.Client.List(ctx, &consumers) which is expensive; change the List invocation to
only fetch relevant Consumers by constructing a selector (e.g., use
client.MatchingLabels or client.MatchingFields) based on the consumer's gateway
reference and namespace before calling v.Client.List, or switch to a
cached/indexed lookup (via an informer/Lister) and query by the gatewayRef
index; update the List call in validateDuplicateKeyAuthCredentials (and any
helper that builds the query) so it only retrieves Consumers that could collide
according to sameConsumerGatewayRef instead of listing all Consumers.
internal/adc/client/executor.go (1)

328-340: Consider explicitly setting MinVersion on custom TLS client config.

Go's default minimum TLS version is TLS 1.2, so the current code already meets the policy requirement. However, explicitly setting MinVersion: tls.VersionTLS12 improves code clarity and avoids reliance on runtime defaults that may change in future versions.

Suggested fix
 func (e *HTTPADCExecutor) newBackendHTTPClient(config adctypes.Config) *http.Client {
 	transport := http.DefaultTransport.(*http.Transport).Clone()
 	if !config.TlsVerify {
 		if transport.TLSClientConfig == nil {
-			transport.TLSClientConfig = &tls.Config{}
+			transport.TLSClientConfig = &tls.Config{
+				MinVersion: tls.VersionTLS12,
+			}
+		} else if transport.TLSClientConfig.MinVersion == 0 {
+			transport.TLSClientConfig.MinVersion = tls.VersionTLS12
 		}
 		transport.TLSClientConfig.InsecureSkipVerify = true
 	}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/adc/client/executor.go` around lines 328 - 340, The TLS client
config created in newBackendHTTPClient should explicitly set MinVersion to
tls.VersionTLS12 instead of relying on runtime defaults; update the logic inside
HTTPADCExecutor.newBackendHTTPClient where transport.TLSClientConfig is
initialized so that if TLSClientConfig is nil you allocate it and set
TLSClientConfig.MinVersion = tls.VersionTLS12 (and keep InsecureSkipVerify
behavior unchanged).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@internal/adc/client/executor.go`:
- Around line 295-325: The log in buildAPISIXValidateRequest currently prints
sensitive data (config.Token and the request body containing creds/keys); change
it to log only non-sensitive metadata or a redacted payload: create a redacted
copy of the body (or call the type's MarshalLogObject/Redact method if
implemented) and replace config.Token with a placeholder (e.g., "<redacted>")
before logging, or remove body/token from the log entirely; update the logging
call in buildAPISIXValidateRequest (and the analogous logging in the other block
referred to) to use the redacted values so no raw tokens, consumer credentials,
certs, or private keys are emitted.

In `@internal/webhook/v1/adc_validation_test.go`:
- Around line 34-41: The helper withMockADCServer unnecessarily wraps the
already-typed http.HandlerFunc parameter when creating the test server; update
the call in withMockADCServer to pass handler directly to httptest.NewServer
(i.e., httptest.NewServer(handler)) and keep the rest of the function (t.Setenv,
t.Cleanup, return server.URL) unchanged to remove the redundant conversion.

In `@internal/webhook/v1/apisixconsumer_webhook.go`:
- Around line 47-63: The constructor stores initErr on
ApisixConsumerCustomValidator which causes a permanent hard-fail on any ADC init
error; remove the initErr field and stop storing that error in
NewApisixConsumerCustomValidator, and instead initialize adcValidator lazily
inside the validation entry points (e.g., the validator methods that run on
create/update) by calling newADCAdmissionValidator when adcValidator is nil; if
that init call returns an error, log the error with apisixConsumerLog and
proceed in "fail open" mode (do not block the request), ensuring reference to
the types/members ApisixConsumerCustomValidator,
NewApisixConsumerCustomValidator, adcValidator, and newADCAdmissionValidator so
you update the right places.

In `@internal/webhook/v1/apisixroute_webhook.go`:
- Around line 55-63: The validator currently stores initialization errors in
ApisixRouteCustomValidator.initErr (from newADCAdmissionValidator) and returns
them immediately in ValidateCreate/ValidateUpdate, causing permanent denials;
change ValidateCreate and ValidateUpdate on ApisixRouteCustomValidator to treat
initErr like runtime ADC unavailability: log the initErr via apisixRouteLog
(include context) and return nil (fail-open) instead of returning the initErr,
so runtime adcAdmissionValidator.Validate behavior and init-time failure are
consistent; keep the newADCAdmissionValidator and adcValidator fields as-is but
ensure all early-return paths reference initErr and use logging+nil return
rather than propagating the error.

In `@internal/webhook/v1/apisixtls_webhook.go`:
- Around line 82-87: The code currently returns v.initErr after collecting
warnings which turns ADC init failures into hard admission denials; change this
to fail-open: when v.initErr is non-nil, do not return the error — log or record
it but return the collected warnings with nil error and skip ADC validation
(i.e., do not call v.adcValidator.Validate when v.initErr != nil). Apply the
same change in the other similar block (lines 106-111) so ADC init/backend
problems do not permanently block admission or poison the validator.

In `@internal/webhook/v1/consumer_webhook.go`:
- Around line 49-65: The struct field initErr in ConsumerCustomValidator (and
the NewConsumerCustomValidator constructor) causes permanent admission denial
when initialization fails; remove the persistent initErr and instead handle
initialization failures by logging the error and returning a validator instance
that will operate in fail-open mode (e.g., set adcValidator to nil and proceed).
Update NewConsumerCustomValidator to not store initErr, and update any methods
that currently check initErr (such as Validate/Handle methods that may reside on
ConsumerCustomValidator) to treat a nil adcValidator as "uninitialized but
allow" rather than denying admission; keep reference.NewChecker and consumerLog
initialization as-is and ensure errors are logged via consumerLog.
- Around line 223-229: The code currently swallows json.Unmarshal errors when
parsing credential.Config.Raw into the local cfg struct and returns an empty
cfg.Key; change this so malformed JSON is surfaced: when
json.Unmarshal(credential.Config.Raw, &cfg) returns an error, return that error
(or at minimum log it with a contextual message including credential.Config.Raw)
instead of returning "", nil; update the function signature and callers as
required so callers can handle/report the unmarshalling error and include
references to json.Unmarshal, credential.Config.Raw, and cfg.Key when making the
change.

In `@test/e2e/webhook/apisixtls.go`:
- Around line 132-142: After recreating serverSecret with s.NewKubeTlsSecret,
wait until the cluster cache reflects the new Secret before creating the
ApisixTls to avoid race failures; implement a short polling loop that calls
s.GetResource("Secret", serverSecret) (or an existing helper like
s.GetKubeSecret) with a timeout/retry backoff and only proceed to call
s.CreateResourceFromString(tlsYAML) when the Secret is returned successfully,
ensuring you still check and Expect no error from that Get before continuing.

---

Outside diff comments:
In `@internal/webhook/v1/apisixconsumer_webhook_test.go`:
- Around line 45-66: Introduce a package-level constant (e.g., const
defaultIngressClassName = "apisix") and replace all literal "apisix" occurrences
in this test with that constant: use defaultIngressClassName when comparing
ingressClass.Name in the loop that sets hasManagedIngressClass and when
constructing the &networkingv1.IngressClass{ObjectMeta: {Name: ..., Annotations:
...}, Spec: {Controller: config.ControllerConfig.ControllerName}} so the test no
longer violates goconst; ensure the new constant is visible to other test files
if they share the same package.

---

Nitpick comments:
In `@internal/adc/client/executor.go`:
- Around line 328-340: The TLS client config created in newBackendHTTPClient
should explicitly set MinVersion to tls.VersionTLS12 instead of relying on
runtime defaults; update the logic inside HTTPADCExecutor.newBackendHTTPClient
where transport.TLSClientConfig is initialized so that if TLSClientConfig is nil
you allocate it and set TLSClientConfig.MinVersion = tls.VersionTLS12 (and keep
InsecureSkipVerify behavior unchanged).

In `@internal/controller/webhook_validation.go`:
- Around line 44-49: The hardcoded supportsEndpointSlice: true on the
ApisixRouteReconciler instantiation in webhook_validation.go can break clusters
without EndpointSlice support; change the construction of ApisixRouteReconciler
to accept a configurable flag or derive it from shared cluster feature detection
(e.g., read from a passed-in config struct or call a helper that checks API
availability) and assign that value to
ApisixRouteReconciler.supportsEndpointSlice instead of the literal true; update
any callers that create this reconciler to pass the configured/derived boolean
so validation honors cluster capabilities.

In `@internal/webhook/v1/consumer_webhook.go`:
- Around line 143-178: The code in validateDuplicateKeyAuthCredentials currently
does a cluster-wide v.Client.List(ctx, &consumers) which is expensive; change
the List invocation to only fetch relevant Consumers by constructing a selector
(e.g., use client.MatchingLabels or client.MatchingFields) based on the
consumer's gateway reference and namespace before calling v.Client.List, or
switch to a cached/indexed lookup (via an informer/Lister) and query by the
gatewayRef index; update the List call in validateDuplicateKeyAuthCredentials
(and any helper that builds the query) so it only retrieves Consumers that could
collide according to sameConsumerGatewayRef instead of listing all Consumers.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 1cf73cf2-2f73-47f0-8938-83053a0638bc

📥 Commits

Reviewing files that changed from the base of the PR and between 15b2f39 and ba91ae8.

📒 Files selected for processing (20)
  • internal/adc/client/client.go
  • internal/adc/client/executor.go
  • internal/adc/client/executor_test.go
  • internal/controller/webhook_validation.go
  • internal/types/error.go
  • internal/webhook/v1/adc_validation.go
  • internal/webhook/v1/adc_validation_test.go
  • internal/webhook/v1/apisixconsumer_webhook.go
  • internal/webhook/v1/apisixconsumer_webhook_test.go
  • internal/webhook/v1/apisixroute_webhook.go
  • internal/webhook/v1/apisixroute_webhook_test.go
  • internal/webhook/v1/apisixtls_webhook.go
  • internal/webhook/v1/apisixtls_webhook_test.go
  • internal/webhook/v1/consumer_webhook.go
  • internal/webhook/v1/consumer_webhook_test.go
  • test/e2e/webhook/apisixconsumer.go
  • test/e2e/webhook/apisixroute.go
  • test/e2e/webhook/apisixtls.go
  • test/e2e/webhook/consumer.go
  • test/e2e/webhook/helpers.go

Comment thread internal/adc/client/executor.go
Comment thread internal/webhook/v1/adc_validation_test.go
Comment on lines 47 to 63
type ApisixConsumerCustomValidator struct {
Client client.Client
checker reference.Checker
Client client.Client
checker reference.Checker
adcValidator *adcAdmissionValidator
initErr error
}

var _ webhook.CustomValidator = &ApisixConsumerCustomValidator{}

func NewApisixConsumerCustomValidator(c client.Client) *ApisixConsumerCustomValidator {
adcValidator, err := newADCAdmissionValidator(c, apisixConsumerLog)
return &ApisixConsumerCustomValidator{
Client: c,
checker: reference.NewChecker(c, apisixConsumerLog),
Client: c,
checker: reference.NewChecker(c, apisixConsumerLog),
adcValidator: adcValidator,
initErr: err,
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Same initErr concern applies here.

See comment on apisixroute_webhook.go – storing and returning initErr on every validation call means ADC validator initialization failures will permanently block all ApisixConsumer writes rather than failing open.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/webhook/v1/apisixconsumer_webhook.go` around lines 47 - 63, The
constructor stores initErr on ApisixConsumerCustomValidator which causes a
permanent hard-fail on any ADC init error; remove the initErr field and stop
storing that error in NewApisixConsumerCustomValidator, and instead initialize
adcValidator lazily inside the validation entry points (e.g., the validator
methods that run on create/update) by calling newADCAdmissionValidator when
adcValidator is nil; if that init call returns an error, log the error with
apisixConsumerLog and proceed in "fail open" mode (do not block the request),
ensuring reference to the types/members ApisixConsumerCustomValidator,
NewApisixConsumerCustomValidator, adcValidator, and newADCAdmissionValidator so
you update the right places.

Comment on lines 55 to 63
func NewApisixRouteCustomValidator(c client.Client) *ApisixRouteCustomValidator {
adcValidator, err := newADCAdmissionValidator(c, apisixRouteLog)
return &ApisixRouteCustomValidator{
Client: c,
checker: reference.NewChecker(c, apisixRouteLog),
Client: c,
checker: reference.NewChecker(c, apisixRouteLog),
adcValidator: adcValidator,
initErr: err,
}
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🌐 Web query:

What is the best practice for Kubernetes admission webhook initialization errors - fail open or fail closed?

💡 Result:

The best practice for Kubernetes admission webhook initialization errors (timeouts, connection failures, or server errors) is to fail open for mutating webhooks (failurePolicy: Ignore) and fail closed for validating webhooks (failurePolicy: Fail, the default). For mutating webhooks, set failurePolicy to Ignore ("fail open") to prevent downtime from blocking compliant resource creation. Pair this with a separate validating webhook or controller to enforce policies on the final object state. This avoids disruptions from webhook unavailability while maintaining enforcement. For validating webhooks, use the default Fail ("fail closed") to ensure policy violations are blocked even during webhook issues, prioritizing security over availability. This distinction balances availability and security: mutations are best-effort and recoverable via validation, while validations are authoritative.

Citations:


🏁 Script executed:

# Determine webhook type and find related configuration
fd -t f "apisixroute_webhook" --exec cat {} \;

Repository: api7/api7-ingress-controller

Length of output: 14040


🏁 Script executed:

# Check consumer_webhook.go for comparison of initialization pattern
fd -t f "consumer_webhook" --exec head -100 {} \;

Repository: api7/api7-ingress-controller

Length of output: 14771


🏁 Script executed:

# Search for any WebhookConfiguration or failurePolicy references related to ApisixRoute
rg -A 5 -B 5 "ApisixRoute.*Webhook" --type go

Repository: api7/api7-ingress-controller

Length of output: 2154


🏁 Script executed:

# Check ADC validator implementation to understand error handling semantics
fd -t f "*adc*" --type go | head -5

Repository: api7/api7-ingress-controller

Length of output: 242


🏁 Script executed:

# Find ADC validator implementation
fd -i "*adc*" | grep -v test | grep -v __pycache__

Repository: api7/api7-ingress-controller

Length of output: 357


🏁 Script executed:

# Search for newADCAdmissionValidator and its error conditions
rg -B 5 -A 20 "func newADCAdmissionValidator"

Repository: api7/api7-ingress-controller

Length of output: 1941


🏁 Script executed:

# Check if there are any comments or documentation about fail-open strategy
rg -i "fail.open|fail.closed|fail.close" --type go

Repository: api7/api7-ingress-controller

Length of output: 194


🏁 Script executed:

# Look for any tests that verify initErr behavior specifically
rg "initErr" --type go -B 3 -A 3

Repository: api7/api7-ingress-controller

Length of output: 7835


🏁 Script executed:

# Search for any comments or documentation about initErr strategy
rg "initialization|init.*error|fail.*open" internal/webhook -i -B 2 -A 2

Repository: api7/api7-ingress-controller

Length of output: 1732


🏁 Script executed:

# Check ADC client creation to understand failure modes
cat internal/webhook/v1/adc_validation.go | head -60

Repository: api7/api7-ingress-controller

Length of output: 2567


🏁 Script executed:

# Verify the actual webhook configuration - check for any failurePolicy clarity
rg "kubebuilder:webhook" internal/webhook/v1/ -A 1

Repository: api7/api7-ingress-controller

Length of output: 4551


🏁 Script executed:

# Check the Validate method in adcAdmissionValidator to see runtime error handling
rg -A 30 "func.*adcAdmissionValidator.*Validate" internal/webhook/v1/adc_validation.go | head -50

Repository: api7/api7-ingress-controller

Length of output: 840


Initialization errors create inconsistent fail behavior compared to runtime ADC unavailability.

The adcAdmissionValidator.Validate() method logs ADC connection errors (500) and returns nil, allowing admission (fail-open). However, newADCAdmissionValidator() initialization errors are stored in initErr and returned immediately on every ValidateCreate/ValidateUpdate call, permanently denying all writes.

This creates two failure modes for the same ADC integration:

  • ADC unavailable at validation time → admission succeeds ✓
  • ADC client initialization fails → admission fails ✗

Either both should fail-open (return nil to allow admission during transient failures), or initialization errors should be distinguished as configuration errors in code and documentation. Currently, the behavior is inconsistent with the fail-open philosophy applied to runtime errors.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/webhook/v1/apisixroute_webhook.go` around lines 55 - 63, The
validator currently stores initialization errors in
ApisixRouteCustomValidator.initErr (from newADCAdmissionValidator) and returns
them immediately in ValidateCreate/ValidateUpdate, causing permanent denials;
change ValidateCreate and ValidateUpdate on ApisixRouteCustomValidator to treat
initErr like runtime ADC unavailability: log the initErr via apisixRouteLog
(include context) and return nil (fail-open) instead of returning the initErr,
so runtime adcAdmissionValidator.Validate behavior and init-time failure are
consistent; keep the newADCAdmissionValidator and adcValidator fields as-is but
ensure all early-return paths reference initErr and use logging+nil return
rather than propagating the error.

Comment on lines +82 to +87
warnings := v.collectWarnings(ctx, tls)
if v.initErr != nil {
return warnings, v.initErr
}

return warnings, v.adcValidator.Validate(ctx, tls)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Do not block admission on ADC validator init failures.

Returning initErr here turns ADC setup/backend problems into hard admission failures, which is the opposite of the PR’s fail-open contract. It also permanently poisons this validator until restart if initialization fails once.

🔧 Suggested fix
  warnings := v.collectWarnings(ctx, tls)
  if v.initErr != nil {
- 	return warnings, v.initErr
+ 	apisixTlsLog.Error(v.initErr, "ADC validation unavailable, allowing admission")
+ 	return warnings, nil
  }

  return warnings, v.adcValidator.Validate(ctx, tls)

Also applies to: 106-111

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/webhook/v1/apisixtls_webhook.go` around lines 82 - 87, The code
currently returns v.initErr after collecting warnings which turns ADC init
failures into hard admission denials; change this to fail-open: when v.initErr
is non-nil, do not return the error — log or record it but return the collected
warnings with nil error and skip ADC validation (i.e., do not call
v.adcValidator.Validate when v.initErr != nil). Apply the same change in the
other similar block (lines 106-111) so ADC init/backend problems do not
permanently block admission or poison the validator.

Comment on lines 49 to 65
type ConsumerCustomValidator struct {
Client client.Client
checker reference.Checker
Client client.Client
checker reference.Checker
adcValidator *adcAdmissionValidator
initErr error
}

var _ webhook.CustomValidator = &ConsumerCustomValidator{}

func NewConsumerCustomValidator(c client.Client) *ConsumerCustomValidator {
adcValidator, err := newADCAdmissionValidator(c, consumerLog)
return &ConsumerCustomValidator{
Client: c,
checker: reference.NewChecker(c, consumerLog),
Client: c,
checker: reference.NewChecker(c, consumerLog),
adcValidator: adcValidator,
initErr: err,
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Same initErr concern applies here.

See comment on apisixroute_webhook.go – storing and returning initErr causes permanent admission denial rather than fail-open behavior.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/webhook/v1/consumer_webhook.go` around lines 49 - 65, The struct
field initErr in ConsumerCustomValidator (and the NewConsumerCustomValidator
constructor) causes permanent admission denial when initialization fails; remove
the persistent initErr and instead handle initialization failures by logging the
error and returning a validator instance that will operate in fail-open mode
(e.g., set adcValidator to nil and proceed). Update NewConsumerCustomValidator
to not store initErr, and update any methods that currently check initErr (such
as Validate/Handle methods that may reside on ConsumerCustomValidator) to treat
a nil adcValidator as "uninitialized but allow" rather than denying admission;
keep reference.NewChecker and consumerLog initialization as-is and ensure errors
are logged via consumerLog.

Comment on lines +223 to +229
var cfg struct {
Key string `json:"key"`
}
if err := json.Unmarshal(credential.Config.Raw, &cfg); err != nil {
return "", nil
}
return cfg.Key, nil
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

JSON unmarshal errors are silently ignored, potentially hiding malformed config.

If credential.Config.Raw contains invalid JSON, the error is swallowed and an empty key is returned. This could mask configuration errors from users.

Consider logging a warning or returning the error to surface malformed credential configs during validation.

🔧 Proposed fix to log malformed config
 var cfg struct {
   Key string `json:"key"`
 }
 if err := json.Unmarshal(credential.Config.Raw, &cfg); err != nil {
+  consumerLog.V(1).Info("failed to unmarshal key-auth config", "error", err, "consumer", consumer.Name)
   return "", nil
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/webhook/v1/consumer_webhook.go` around lines 223 - 229, The code
currently swallows json.Unmarshal errors when parsing credential.Config.Raw into
the local cfg struct and returns an empty cfg.Key; change this so malformed JSON
is surfaced: when json.Unmarshal(credential.Config.Raw, &cfg) returns an error,
return that error (or at minimum log it with a contextual message including
credential.Config.Raw) instead of returning "", nil; update the function
signature and callers as required so callers can handle/report the unmarshalling
error and include references to json.Unmarshal, credential.Config.Raw, and
cfg.Key when making the change.

Comment on lines +132 to +142
By("replacing the secret with valid certificate material")
err = s.DeleteResource("Secret", serverSecret)
Expect(err).NotTo(HaveOccurred(), "deleting invalid server TLS secret")

serverCert, serverKey := s.GenerateCert(GinkgoT(), []string{host})
err = s.NewKubeTlsSecret(serverSecret, serverCert.String(), serverKey.String())
Expect(err).NotTo(HaveOccurred(), "creating valid server TLS secret")

By("creating corrected ApisixTls")
err = s.CreateResourceFromString(tlsYAML)
Expect(err).NotTo(HaveOccurred(), "creating corrected ApisixTls")
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Wait for the recreated Secret before retrying admission.

This path recreates serverSecret and immediately submits ApisixTls. The webhook resolves Secrets through a cached client, so this can race cache propagation and make the e2e intermittently fail on stale secret state.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/e2e/webhook/apisixtls.go` around lines 132 - 142, After recreating
serverSecret with s.NewKubeTlsSecret, wait until the cluster cache reflects the
new Secret before creating the ApisixTls to avoid race failures; implement a
short polling loop that calls s.GetResource("Secret", serverSecret) (or an
existing helper like s.GetKubeSecret) with a timeout/retry backoff and only
proceed to call s.CreateResourceFromString(tlsYAML) when the Secret is returned
successfully, ensuring you still check and Expect no error from that Get before
continuing.

AlinsRan and others added 2 commits April 27, 2026 15:41
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Add ProviderTypeAPISIXStandalone skip guards to all ADC validation
  e2e tests so they are skipped on non-standalone backends that do not
  expose the /apisix/admin/configs/validate endpoint
- Replace the 'duplicate credentials' consumer tests with invalid
  jwt-auth algorithm tests; APISIX validates plugin config schema in
  isolation and cannot detect cross-consumer key uniqueness, but it
  does reject unknown enum values (e.g. algorithm: INVALID_ALGO)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings April 27, 2026 07:46
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@internal/adc/client/executor.go`:
- Around line 328-341: The TLS config created in
HTTPADCExecutor.newBackendHTTPClient currently sets InsecureSkipVerify but does
not enforce a minimum TLS version; update newBackendHTTPClient to ensure the
TLSClientConfig has MinVersion = tls.VersionTLS12 (or higher) when creating or
augmenting transport.TLSClientConfig, and if transport.TLSClientConfig already
exists only set MinVersion when it is 0 to avoid overwriting explicit settings;
reference newBackendHTTPClient, HTTPADCExecutor, transport.TLSClientConfig and
tls.Config.MinVersion/tls.VersionTLS12.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 51318ffa-868c-4719-99ba-f7e37b438dbe

📥 Commits

Reviewing files that changed from the base of the PR and between ba91ae8 and 0b74a9c.

📒 Files selected for processing (4)
  • internal/adc/client/executor.go
  • internal/adc/client/executor_test.go
  • internal/webhook/v1/adc_validation_test.go
  • internal/webhook/v1/apisixconsumer_webhook_test.go
🚧 Files skipped from review as they are similar to previous changes (2)
  • internal/webhook/v1/adc_validation_test.go
  • internal/adc/client/executor_test.go

Comment on lines +328 to +341
func (e *HTTPADCExecutor) newBackendHTTPClient(config adctypes.Config) *http.Client {
transport := http.DefaultTransport.(*http.Transport).Clone()
if !config.TlsVerify {
if transport.TLSClientConfig == nil {
transport.TLSClientConfig = &tls.Config{}
}
transport.TLSClientConfig.InsecureSkipVerify = true
}

return &http.Client{
Timeout: e.httpClient.Timeout,
Transport: transport,
}
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Set minimum TLS version to 1.2 or higher.

The TLS configuration lacks a MinVersion, which defaults to TLS 1.0 when acting as a client. This is a weak cryptographic configuration per CWE-327.

🔒 Proposed fix
 func (e *HTTPADCExecutor) newBackendHTTPClient(config adctypes.Config) *http.Client {
 	transport := http.DefaultTransport.(*http.Transport).Clone()
 	if !config.TlsVerify {
 		if transport.TLSClientConfig == nil {
-			transport.TLSClientConfig = &tls.Config{}
+			transport.TLSClientConfig = &tls.Config{
+				MinVersion: tls.VersionTLS12,
+			}
 		}
 		transport.TLSClientConfig.InsecureSkipVerify = true
+	} else {
+		if transport.TLSClientConfig == nil {
+			transport.TLSClientConfig = &tls.Config{}
+		}
+		transport.TLSClientConfig.MinVersion = tls.VersionTLS12
 	}
 
 	return &http.Client{

As per coding guidelines, "TLS and cryptographic configuration must use correct semantics... default TLS min version to 1.2 or higher".

🧰 Tools
🪛 ast-grep (0.42.1)

[warning] 331-331: MinVersionis missing from this TLS configuration. By default, TLS 1.2 is currently used as the minimum when acting as a client, and TLS 1.0 when acting as a server. General purpose web applications should default to TLS 1.3 with all other protocols disabled. Only where it is known that a web server must support legacy clients with unsupported an insecure browsers (such as Internet Explorer 10), it may be necessary to enable TLS 1.0 to provide support. AddMinVersion: tls.VersionTLS13' to the TLS configuration to bump the minimum version to TLS 1.3.
Context: tls.Config{}
Note: [CWE-327]: Use of a Broken or Risky Cryptographic Algorithm [OWASP A03:2017]: Sensitive Data Exposure [OWASP A02:2021]: Cryptographic Failures [REFERENCES]
https://owasp.org/Top10/A02_2021-Cryptographic_Failures

(missing-ssl-minversion-go)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/adc/client/executor.go` around lines 328 - 341, The TLS config
created in HTTPADCExecutor.newBackendHTTPClient currently sets
InsecureSkipVerify but does not enforce a minimum TLS version; update
newBackendHTTPClient to ensure the TLSClientConfig has MinVersion =
tls.VersionTLS12 (or higher) when creating or augmenting
transport.TLSClientConfig, and if transport.TLSClientConfig already exists only
set MinVersion when it is 0 to avoid overwriting explicit settings; reference
newBackendHTTPClient, HTTPADCExecutor, transport.TLSClientConfig and
tls.Config.MinVersion/tls.VersionTLS12.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 21 out of 21 changed files in this pull request and generated 1 comment.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +152 to +155
var consumers apisixv1alpha1.ConsumerList
if err := v.Client.List(ctx, &consumers); err != nil {
return err
}
Copy link

Copilot AI Apr 27, 2026

Choose a reason for hiding this comment

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

validateDuplicateKeyAuthCredentials lists all Consumer objects cluster-wide and then iterates them to detect duplicates. On large clusters this makes admission latency O(total Consumers) (plus potential secret lookups), which can become a bottleneck. Consider adding a field index (e.g., by resolved GatewayRef) and using MatchingFields/scoped listing to narrow candidates before scanning.

Copilot uses AI. Check for mistakes.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
.github/workflows/e2e-test-k8s.yml (1)

70-70: Use an isolated kubeconfig file for self-hosted runner stability.

Line 70 is correct, but exporting to the default kubeconfig path can cause state bleed across runs on self-hosted. Prefer an explicit KUBECONFIG file and persist it for downstream e2e steps.

♻️ Suggested hardening
       - name: Launch Kind Cluster
         env:
           KIND_NODE_IMAGE: kindest/node:v1.18.15
+          KIND_NAME: apisix-ingress-cluster
+          KUBECONFIG: ${{ runner.temp }}/kind-${{ github.run_id }}.kubeconfig
         run: |
           make kind-up
-          kind export kubeconfig --name apisix-ingress-cluster
+          kind export kubeconfig --name "${KIND_NAME}" --kubeconfig "${KUBECONFIG}"
+          echo "KUBECONFIG=${KUBECONFIG}" >> "${GITHUB_ENV}"
           kubectl wait --for=condition=Ready nodes --all
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/e2e-test-k8s.yml at line 70, The current use of "kind
export kubeconfig --name apisix-ingress-cluster" writes to the default
kubeconfig and can leak state on self-hosted runners; fix by directing kind to
an explicit file and exporting that path for downstream steps: create a
dedicated kube dir (e.g. .kube/config in the workspace), set the KUBECONFIG
environment variable to that file before invoking the "kind export kubeconfig
--name apisix-ingress-cluster" command, and persist that env/file for subsequent
e2e steps (via workflow env or step outputs) so all later steps use the isolated
kubeconfig.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In @.github/workflows/e2e-test-k8s.yml:
- Line 70: The current use of "kind export kubeconfig --name
apisix-ingress-cluster" writes to the default kubeconfig and can leak state on
self-hosted runners; fix by directing kind to an explicit file and exporting
that path for downstream steps: create a dedicated kube dir (e.g. .kube/config
in the workspace), set the KUBECONFIG environment variable to that file before
invoking the "kind export kubeconfig --name apisix-ingress-cluster" command, and
persist that env/file for subsequent e2e steps (via workflow env or step
outputs) so all later steps use the isolated kubeconfig.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: ad9e436c-5a93-4381-acc1-55002624b4af

📥 Commits

Reviewing files that changed from the base of the PR and between 10d76b4 and d9ba66c.

📒 Files selected for processing (1)
  • .github/workflows/e2e-test-k8s.yml

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings April 27, 2026 08:05
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 21 out of 21 changed files in this pull request and generated 3 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +44 to +49
reconciler := &ApisixRouteReconciler{
Client: c,
Log: log,
ICGV: networkingv1.SchemeGroupVersion,
supportsEndpointSlice: true,
}
Copy link

Copilot AI Apr 27, 2026

Choose a reason for hiding this comment

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

PrepareApisixRouteForValidation hardcodes supportsEndpointSlice: true, which can break validation on older clusters (e.g., k8s 1.18 where discovery.k8s.io/v1 EndpointSlice isn’t served). This can cause admission to error/deny routes because endpoint discovery fails. Consider detecting EndpointSlice support (similar to the reconciler’s HasAPIResource check) or defaulting to false/fallback to Endpoints for webhook validation.

Copilot uses AI. Check for mistakes.
Comment on lines +343 to +346
func buildAPISIXValidatePayload(resources *adctypes.Resources) (*apisixValidateRequest, error) {
body := &apisixValidateRequest{}

for _, service := range resources.Services {
Copy link

Copilot AI Apr 27, 2026

Choose a reason for hiding this comment

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

buildAPISIXValidatePayload populates routes/services/consumers/ssls, but it never includes resources.GlobalRules or resources.PluginMetadata even though the request struct has global_rules / plugin_metadata fields. This means standalone validation can silently skip validating those resources. Consider converting these fields into the payload as well (or remove the unused fields if unsupported).

Copilot uses AI. Check for mistakes.
Comment on lines +101 to +112
configs, err := v.buildIngressClassConfigs(ctx, resource.DeepCopy())
if err != nil {
return nil, err
}
if len(configs) == 0 {
return nil, nil
}
tctx, err = controller.PrepareApisixRouteForValidation(ctx, v.kubeClient, v.log, resource.DeepCopy())
if err != nil {
return nil, err
}
result, err = v.translator.TranslateApisixRoute(tctx, resource.DeepCopy())
Copy link

Copilot AI Apr 27, 2026

Choose a reason for hiding this comment

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

In buildTask for v2 resources, ingress class parameters/configs are processed twice: first via buildIngressClassConfigs(...), then again inside controller.Prepare...ForValidation(...) (which calls FindMatchingIngressClassByObject + ProcessIngressClassParameters). This doubles API calls during admission. Consider reusing the TranslateContext from Prepare...ForValidation to build configs (and drop buildIngressClassConfigs), or have Prepare...ForValidation return configs directly.

Copilot uses AI. Check for mistakes.
AlinsRan and others added 2 commits April 27, 2026 16:14
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings April 27, 2026 08:25
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 21 out of 21 changed files in this pull request and generated no new comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

AlinsRan and others added 2 commits April 27, 2026 16:49
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 25 out of 25 changed files in this pull request and generated 2 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +1 to +8
package main

import (
"log"
"net/http"

"github.com/gorilla/websocket"
)
Copy link

Copilot AI Apr 27, 2026

Choose a reason for hiding this comment

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

New Go files under cmd/ appear to require the ASF license header (e.g., cmd/main.go). cmd/e2e-echo-server/main.go is missing this header; please add the standard Apache license block at the top for consistency/compliance.

Copilot uses AI. Check for mistakes.
Comment thread Makefile
Comment on lines +226 to +228
build-e2e-echo-server-image:
@CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o bin/e2e-echo-server ./cmd/e2e-echo-server
@docker build -f test/e2e/images/echo-server.Dockerfile -t jmalloc/echo-server:latest .
Copy link

Copilot AI Apr 27, 2026

Choose a reason for hiding this comment

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

build-e2e-echo-server-image hard-codes GOARCH=amd64. This can break local dev and CI runners that are arm64 (or if KIND uses a non-amd64 node image). Consider deriving GOARCH from go env GOARCH / the kind node architecture, or using a multi-arch build strategy.

Copilot uses AI. Check for mistakes.
AlinsRan and others added 2 commits April 27, 2026 18:18
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings April 27, 2026 10:44
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 26 out of 26 changed files in this pull request and generated 3 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

}

var consumers apisixv1alpha1.ConsumerList
if err := v.Client.List(ctx, &consumers); err != nil {
Copy link

Copilot AI Apr 27, 2026

Choose a reason for hiding this comment

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

validateDuplicateKeyAuthCredentials lists all Consumer objects and then filters in-memory. In clusters with many Consumers this makes admission (create/update) O(n) and can become a bottleneck. Since the codebase already field-indexes Consumers by gatewayRef (internal/controller/indexer/indexer.go:160-166,238-250), consider using a MatchingFields list scoped to the gatewayRef index key so only Consumers for the same gateway are scanned.

Suggested change
if err := v.Client.List(ctx, &consumers); err != nil {
if err := v.Client.List(ctx, &consumers, client.MatchingFields{
"gatewayRef": consumer.Spec.GatewayRef.Name,
}); err != nil {

Copilot uses AI. Check for mistakes.
Comment thread test/e2e/framework/k8s.go
Comment on lines 263 to 305
@@ -286,14 +290,18 @@ func WaitPodsAvailable(t testing.TestingT, kubeOps *k8s.KubectlOptions, opts met
}
return true, nil
}
return waitExponentialBackoff(condFunc)
return waitExponentialBackoffWithTimeout(condFunc, timeout)
}

func waitExponentialBackoff(condFunc func() (bool, error)) error {
func waitExponentialBackoffWithTimeout(condFunc func() (bool, error), timeout time.Duration) error {
steps := int(timeout / (2 * time.Second))
if steps < 1 {
steps = 1
}
backoff := wait.Backoff{
Duration: 500 * time.Millisecond,
Factor: 2,
Steps: 8,
Duration: 2 * time.Second,
Factor: 1,
Steps: steps,
}
Copy link

Copilot AI Apr 27, 2026

Choose a reason for hiding this comment

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

WaitPodsAvailable now hardcodes a 1 minute timeout and the underlying backoff is effectively constant 2s polling (Factor=1). Previously this helper waited ~2+ minutes (500ms * 2^Steps) and is used in many e2e deploy paths; reducing the default timeout can introduce flakes for slower clusters. Consider restoring a longer default (or keeping the old backoff parameters) and using WaitPodsAvailableWithTimeout only when a shorter timeout is explicitly desired.

Copilot uses AI. Check for mistakes.
Comment on lines +100 to +113
case *apiv2.ApisixRoute:
configs, err := v.buildIngressClassConfigs(ctx, resource.DeepCopy())
if err != nil {
return nil, err
}
if len(configs) == 0 {
return nil, nil
}
tctx, err = controller.PrepareApisixRouteForValidation(ctx, v.kubeClient, v.log, resource.DeepCopy())
if err != nil {
return nil, err
}
result, err = v.translator.TranslateApisixRoute(tctx, resource.DeepCopy())
resourceTypes = append(resourceTypes, adctypes.TypeService)
Copy link

Copilot AI Apr 27, 2026

Choose a reason for hiding this comment

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

buildTask() does ingress-class resolution twice for ApisixRoute/ApisixConsumer/ApisixTls: buildIngressClassConfigs() calls FindMatchingIngressClassByObject + ProcessIngressClassParameters, and then PrepareForValidation does the same again. This duplicates client/cache lookups on every admission request and is avoidable. Consider calling PrepareForValidation first and deriving configs via v.buildConfigs(tctx), or refactoring so ingress-class processing happens only once per Validate().

Copilot uses AI. Check for mistakes.
AlinsRan and others added 2 commits April 27, 2026 19:17
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings April 27, 2026 11:33
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 31 out of 31 changed files in this pull request and generated 2 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +246 to +247
_, getErr := s.GetOutputFromString(resourceType, resourceName, "-o", "yaml")
Expect(getErr).To(HaveOccurred(), fmt.Sprintf("resource %s/%s should not exist after admission rejection", resourceType, resourceName))
Copy link

Copilot AI Apr 27, 2026

Choose a reason for hiding this comment

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

expectAdmissionDenied verifies that a follow-up kubectl get fails, but it doesn't assert the failure is specifically a NotFound. As written, the test could pass even if kubectl get failed for an unrelated reason (e.g., bad resourceType, transient API error). Tighten the assertion to check the error/output indicates the resource was not found (or use a typed NotFound check) so admission-deny tests can't produce false positives.

Copilot uses AI. Check for mistakes.
Comment thread test/e2e/framework/k8s.go
Factor: 2,
Steps: 8,
Duration: 2 * time.Second,
Factor: 1,
Copy link

Copilot AI Apr 27, 2026

Choose a reason for hiding this comment

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

waitExponentialBackoffWithTimeout uses wait.ExponentialBackoff with Factor=1, which results in a constant polling interval rather than exponential backoff. Either adjust the backoff parameters to actually be exponential, or rename the helper to avoid implying exponential behavior.

Suggested change
Factor: 1,
Factor: 2,

Copilot uses AI. Check for mistakes.
AlinsRan and others added 2 commits April 27, 2026 19:57
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings April 27, 2026 12:41
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 31 out of 31 changed files in this pull request and generated 6 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +101 to +114
configs, err := v.buildIngressClassConfigs(ctx, resource.DeepCopy())
if err != nil {
return nil, err
}
if len(configs) == 0 {
return nil, nil
}
tctx, err = controller.PrepareApisixRouteForValidation(ctx, v.kubeClient, v.log, resource.DeepCopy())
if err != nil {
return nil, err
}
result, err = v.translator.TranslateApisixRoute(tctx, resource.DeepCopy())
resourceTypes = append(resourceTypes, adctypes.TypeService)
if err != nil {
Copy link

Copilot AI Apr 27, 2026

Choose a reason for hiding this comment

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

buildTask calls buildIngressClassConfigs() (which runs FindMatchingIngressClassByObject + ProcessIngressClassParameters) and then calls PrepareApisix*ForValidation() which repeats the same ingress class matching/parameter processing. This duplicates API reads and makes admission slower than necessary. Consider building configs from the TranslateContext returned by the Prepare...ForValidation helpers instead of re-processing the IngressClass twice.

Copilot uses AI. Check for mistakes.
Comment on lines +1 to +3
package main

import (
Copy link

Copilot AI Apr 27, 2026

Choose a reason for hiding this comment

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

This new Go file is missing the standard ASF license header that other cmd/ entrypoints in this repo include. Add the Apache Software Foundation header comment at the top for license compliance/consistency.

Copilot uses AI. Check for mistakes.
@@ -44,16 +47,21 @@ func SetupConsumerWebhookWithManager(mgr ctrl.Manager) error {
// +kubebuilder:webhook:path=/validate-apisix-apache-org-v1alpha1-consumer,mutating=false,failurePolicy=fail,sideEffects=None,groups=apisix.apache.org,resources=consumers,verbs=create;update,versions=v1alpha1,name=vconsumer-v1alpha1.kb.io,admissionReviewVersions=v1,failurePolicy=Ignore
Copy link

Copilot AI Apr 27, 2026

Choose a reason for hiding this comment

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

The kubebuilder webhook marker declares failurePolicy twice (failurePolicy=fail and failurePolicy=Ignore). This is ambiguous for generated manifests and can lead to an unexpected admission behavior. Keep only a single failurePolicy value (whichever is intended for this webhook).

Suggested change
// +kubebuilder:webhook:path=/validate-apisix-apache-org-v1alpha1-consumer,mutating=false,failurePolicy=fail,sideEffects=None,groups=apisix.apache.org,resources=consumers,verbs=create;update,versions=v1alpha1,name=vconsumer-v1alpha1.kb.io,admissionReviewVersions=v1,failurePolicy=Ignore
// +kubebuilder:webhook:path=/validate-apisix-apache-org-v1alpha1-consumer,mutating=false,sideEffects=None,groups=apisix.apache.org,resources=consumers,verbs=create;update,versions=v1alpha1,name=vconsumer-v1alpha1.kb.io,admissionReviewVersions=v1,failurePolicy=Ignore

Copilot uses AI. Check for mistakes.
@@ -44,16 +44,21 @@ func SetupApisixRouteWebhookWithManager(mgr ctrl.Manager) error {
// +kubebuilder:webhook:path=/validate-apisix-apache-org-v2-apisixroute,mutating=false,failurePolicy=fail,sideEffects=None,groups=apisix.apache.org,resources=apisixroutes,verbs=create;update,versions=v2,name=vapisixroute-v2.kb.io,admissionReviewVersions=v1,failurePolicy=Ignore
Copy link

Copilot AI Apr 27, 2026

Choose a reason for hiding this comment

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

The kubebuilder webhook marker declares failurePolicy twice (failurePolicy=fail and failurePolicy=Ignore). This is ambiguous for generated manifests and can lead to an unexpected admission behavior. Keep only a single failurePolicy value (whichever is intended for this webhook).

Suggested change
// +kubebuilder:webhook:path=/validate-apisix-apache-org-v2-apisixroute,mutating=false,failurePolicy=fail,sideEffects=None,groups=apisix.apache.org,resources=apisixroutes,verbs=create;update,versions=v2,name=vapisixroute-v2.kb.io,admissionReviewVersions=v1,failurePolicy=Ignore
// +kubebuilder:webhook:path=/validate-apisix-apache-org-v2-apisixroute,mutating=false,failurePolicy=fail,sideEffects=None,groups=apisix.apache.org,resources=apisixroutes,verbs=create;update,versions=v2,name=vapisixroute-v2.kb.io,admissionReviewVersions=v1

Copilot uses AI. Check for mistakes.
@@ -45,16 +45,21 @@ func SetupApisixTlsWebhookWithManager(mgr ctrl.Manager) error {
// +kubebuilder:webhook:path=/validate-apisix-apache-org-v2-apisixtls,mutating=false,failurePolicy=fail,sideEffects=None,groups=apisix.apache.org,resources=apisixtlses,verbs=create;update,versions=v2,name=vapisixtls-v2.kb.io,admissionReviewVersions=v1,failurePolicy=Ignore
Copy link

Copilot AI Apr 27, 2026

Choose a reason for hiding this comment

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

The kubebuilder webhook marker declares failurePolicy twice (failurePolicy=fail and failurePolicy=Ignore). This is ambiguous for generated manifests and can lead to an unexpected admission behavior. Keep only a single failurePolicy value (whichever is intended for this webhook).

Suggested change
// +kubebuilder:webhook:path=/validate-apisix-apache-org-v2-apisixtls,mutating=false,failurePolicy=fail,sideEffects=None,groups=apisix.apache.org,resources=apisixtlses,verbs=create;update,versions=v2,name=vapisixtls-v2.kb.io,admissionReviewVersions=v1,failurePolicy=Ignore
// +kubebuilder:webhook:path=/validate-apisix-apache-org-v2-apisixtls,mutating=false,failurePolicy=fail,sideEffects=None,groups=apisix.apache.org,resources=apisixtlses,verbs=create;update,versions=v2,name=vapisixtls-v2.kb.io,admissionReviewVersions=v1

Copilot uses AI. Check for mistakes.
@@ -45,16 +45,21 @@ func SetupApisixConsumerWebhookWithManager(mgr ctrl.Manager) error {
// +kubebuilder:webhook:path=/validate-apisix-apache-org-v2-apisixconsumer,mutating=false,failurePolicy=fail,sideEffects=None,groups=apisix.apache.org,resources=apisixconsumers,verbs=create;update,versions=v2,name=vapisixconsumer-v2.kb.io,admissionReviewVersions=v1,failurePolicy=Ignore
Copy link

Copilot AI Apr 27, 2026

Choose a reason for hiding this comment

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

The kubebuilder webhook marker declares failurePolicy twice (failurePolicy=fail and failurePolicy=Ignore). This is ambiguous for generated manifests and can lead to an unexpected admission behavior. Keep only a single failurePolicy value (whichever is intended for this webhook).

Suggested change
// +kubebuilder:webhook:path=/validate-apisix-apache-org-v2-apisixconsumer,mutating=false,failurePolicy=fail,sideEffects=None,groups=apisix.apache.org,resources=apisixconsumers,verbs=create;update,versions=v2,name=vapisixconsumer-v2.kb.io,admissionReviewVersions=v1,failurePolicy=Ignore
// +kubebuilder:webhook:path=/validate-apisix-apache-org-v2-apisixconsumer,mutating=false,failurePolicy=fail,sideEffects=None,groups=apisix.apache.org,resources=apisixconsumers,verbs=create;update,versions=v2,name=vapisixconsumer-v2.kb.io,admissionReviewVersions=v1

Copilot uses AI. Check for mistakes.
AlinsRan and others added 2 commits April 27, 2026 20:48
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings April 27, 2026 12:59
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 31 out of 31 changed files in this pull request and generated 3 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +193 to +205
var errs types.ADCValidationErrors
for _, config := range task.Configs {
if config.BackendType == "" {
config.BackendType = c.defaultMode
}
if err := c.executor.Validate(ctx, config, args); err != nil {
var validationErr types.ADCValidationError
if errors.As(err, &validationErr) {
errs.Errors = append(errs.Errors, validationErr)
continue
}
return err
}
Copy link

Copilot AI Apr 27, 2026

Choose a reason for hiding this comment

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

Client.Validate returns immediately on the first non-ADCValidationError from executor.Validate. This can cause a mixed outcome to fail-open: if one GatewayProxy config has an infra error (e.g., backend unavailable) but another config would return explicit validation errors, the infra error will short-circuit and the admission layer will treat it as an infra error and allow the request. Consider aggregating infra errors across configs and only returning them if no validation errors were found for any config (similar to how per-server validation is aggregated in runHTTPValidate).

Copilot uses AI. Check for mistakes.
Comment on lines +6 to +15

"github.com/gorilla/websocket"
)

var upgrader = websocket.Upgrader{
CheckOrigin: func(*http.Request) bool {
return true
},
}

Copy link

Copilot AI Apr 27, 2026

Choose a reason for hiding this comment

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

websocket.Upgrader is configured with CheckOrigin always returning true, which disables the origin check entirely. Even for an e2e helper, this pattern is risky if the binary is ever reused outside tests; consider restricting origins (e.g., same-host) or making this explicitly configurable/documented as test-only behavior.

Suggested change
"github.com/gorilla/websocket"
)
var upgrader = websocket.Upgrader{
CheckOrigin: func(*http.Request) bool {
return true
},
}
"net/url"
"github.com/gorilla/websocket"
)
func sameHostOrigin(r *http.Request) bool {
origin := r.Header.Get("Origin")
if origin == "" {
return true
}
u, err := url.Parse(origin)
if err != nil {
return false
}
return u.Host == r.Host
}
var upgrader = websocket.Upgrader{
CheckOrigin: sameHostOrigin,
}

Copilot uses AI. Check for mistakes.
Comment thread test/e2e/framework/k8s.go
Comment on lines 263 to +264
func WaitPodsAvailable(t testing.TestingT, kubeOps *k8s.KubectlOptions, opts metav1.ListOptions) error {
return WaitPodsAvailableWithTimeout(t, kubeOps, opts, time.Minute)
Copy link

Copilot AI Apr 27, 2026

Choose a reason for hiding this comment

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

WaitPodsAvailable now hard-codes a 1 minute timeout, whereas the previous exponential backoff (500ms * 2^n, 8 steps) effectively waited ~2 minutes. This change can make e2e deployments flakier in slow CI clusters. Consider keeping the previous default (or increasing it) and using WaitPodsAvailableWithTimeout only at call sites that need custom bounds.

Suggested change
func WaitPodsAvailable(t testing.TestingT, kubeOps *k8s.KubectlOptions, opts metav1.ListOptions) error {
return WaitPodsAvailableWithTimeout(t, kubeOps, opts, time.Minute)
const waitPodsAvailableDefaultTimeout = 127500 * time.Millisecond
func WaitPodsAvailable(t testing.TestingT, kubeOps *k8s.KubectlOptions, opts metav1.ListOptions) error {
return WaitPodsAvailableWithTimeout(t, kubeOps, opts, waitPodsAvailableDefaultTimeout)

Copilot uses AI. Check for mistakes.
AlinsRan and others added 2 commits April 27, 2026 21:56
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings April 27, 2026 14:44
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 31 out of 31 changed files in this pull request and generated 2 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread Makefile

.PHONY: build-e2e-echo-server-image
build-e2e-echo-server-image:
@CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o bin/e2e-echo-server ./cmd/e2e-echo-server
Copy link

Copilot AI Apr 27, 2026

Choose a reason for hiding this comment

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

build-e2e-echo-server-image hard-codes GOARCH=amd64. This will break running e2e tests on arm64 hosts/clusters (the scratch image will contain an amd64 binary). Consider using the existing $(GOARCH) Makefile variable (or building/loading a multi-arch image) so the echo-server image matches the target platform.

Suggested change
@CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o bin/e2e-echo-server ./cmd/e2e-echo-server
@CGO_ENABLED=0 GOOS=linux GOARCH=$(GOARCH) go build -o bin/e2e-echo-server ./cmd/e2e-echo-server

Copilot uses AI. Check for mistakes.
require.Error(t, err)
require.Contains(t, err.Error(), `duplicate key-auth credential key "shared-key"`)
require.Contains(t, err.Error(), "default/existing")
}
Copy link

Copilot AI Apr 27, 2026

Choose a reason for hiding this comment

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

The new duplicate key-auth detection test only covers the inline credential.Config.Raw path. Since the implementation also supports extracting the key from credential.SecretRef (and has special handling for missing/invalid data), consider adding unit coverage for the SecretRef-based key extraction to ensure duplicate detection works for the common Secret-backed credential flow.

Suggested change
}
}
func TestConsumerValidator_DenyDuplicateKeyAuthCredentialFromSecretRef(t *testing.T) {
existing := &apisixv1alpha1.Consumer{
ObjectMeta: metav1.ObjectMeta{
Name: "existing",
Namespace: "default",
},
Spec: apisixv1alpha1.ConsumerSpec{
GatewayRef: apisixv1alpha1.GatewayRef{Name: "test-gateway"},
Credentials: []apisixv1alpha1.Credential{{
Type: "key-auth",
SecretRef: &apisixv1alpha1.SecretReference{
Name: "existing-key-secret",
},
}},
},
}
consumer := &apisixv1alpha1.Consumer{
ObjectMeta: metav1.ObjectMeta{
Name: "demo",
Namespace: "default",
},
Spec: apisixv1alpha1.ConsumerSpec{
GatewayRef: apisixv1alpha1.GatewayRef{Name: "test-gateway"},
Credentials: []apisixv1alpha1.Credential{{
Type: "key-auth",
SecretRef: &apisixv1alpha1.SecretReference{
Name: "demo-key-secret",
},
}},
},
}
validator := buildConsumerValidator(
t,
existing,
&corev1.Secret{
ObjectMeta: metav1.ObjectMeta{Name: "existing-key-secret", Namespace: "default"},
Data: map[string][]byte{
"key": []byte("shared-key"),
},
},
&corev1.Secret{
ObjectMeta: metav1.ObjectMeta{Name: "demo-key-secret", Namespace: "default"},
Data: map[string][]byte{
"key": []byte("shared-key"),
},
},
)
warnings, err := validator.ValidateCreate(context.Background(), consumer)
require.Empty(t, warnings)
require.Error(t, err)
require.Contains(t, err.Error(), `duplicate key-auth credential key "shared-key"`)
require.Contains(t, err.Error(), "default/existing")
}

Copilot uses AI. Check for mistakes.
AlinsRan and others added 2 commits April 28, 2026 00:45
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings April 27, 2026 16:48
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 32 out of 32 changed files in this pull request and generated no new comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

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.

2 participants