This is an automated email from the ASF dual-hosted git repository.

ronething pushed a commit to branch fix/apisix-standalone-endpoint-status
in repository https://gitbox.apache.org/repos/asf/apisix-ingress-controller.git

commit 8f51a1b04ed4a2af7aac3aafcc15d1ce2e842b2a
Author: Ashing Zheng <[email protected]>
AuthorDate: Wed Jan 7 16:57:53 2026 +0800

    fix: adjust apisix standalone mode adc sync result
    
    Signed-off-by: Ashing Zheng <[email protected]>
---
 api/adc/types.go                   | 23 +++++++++----
 go.mod                             |  3 --
 go.sum                             |  6 ----
 internal/adc/client/executor.go    | 69 ++++++++++++++++++++++++++++++--------
 internal/manager/controllers.go    |  2 +-
 internal/provider/apisix/status.go | 60 ++++++++++++++++++++++++++++++++-
 internal/types/error.go            |  7 ++--
 7 files changed, 136 insertions(+), 34 deletions(-)

diff --git a/api/adc/types.go b/api/adc/types.go
index e5966ac2..ab5e566e 100644
--- a/api/adc/types.go
+++ b/api/adc/types.go
@@ -693,12 +693,23 @@ const (
 )
 
 type SyncResult struct {
-       Status         string       `json:"status"`
-       TotalResources int          `json:"total_resources"`
-       SuccessCount   int          `json:"success_count"`
-       FailedCount    int          `json:"failed_count"`
-       Success        []SyncStatus `json:"success"`
-       Failed         []SyncStatus `json:"failed"`
+       Status         string           `json:"status"`
+       TotalResources int              `json:"total_resources"`
+       SuccessCount   int              `json:"success_count"`
+       FailedCount    int              `json:"failed_count"`
+       Success        []SyncStatus     `json:"success"`
+       Failed         []SyncStatus     `json:"failed"`
+       EndpointStatus []EndpointStatus `json:"endpoint_status,omitempty"`
+}
+
+// EndpointStatus represents the synchronization status of an APISIX 
standalone endpoint.
+// This is only used in apisix-standalone mode where endpoint-level status is 
reported
+// instead of resource-level status.
+type EndpointStatus struct {
+       Server      string    `json:"server"`
+       Success     bool      `json:"success"`
+       Reason      string    `json:"reason,omitempty"`
+       RequestedAt time.Time `json:"requested_at,omitempty"`
 }
 
 type SyncStatus struct {
diff --git a/go.mod b/go.mod
index 757f8e28..cab91fbc 100644
--- a/go.mod
+++ b/go.mod
@@ -21,7 +21,6 @@ require (
        github.com/olekukonko/tablewriter v1.1.1
        github.com/onsi/ginkgo/v2 v2.22.0
        github.com/onsi/gomega v1.36.1
-       github.com/panjf2000/ants/v2 v2.11.3
        github.com/pkg/errors v0.9.1
        github.com/prometheus/client_golang v1.19.1
        github.com/samber/lo v1.47.0
@@ -35,7 +34,6 @@ require (
        k8s.io/apiextensions-apiserver v0.32.3
        k8s.io/apimachinery v0.32.3
        k8s.io/client-go v0.32.3
-       k8s.io/code-generator v0.32.3
        k8s.io/kubectl v0.30.3
        k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738
        sigs.k8s.io/controller-runtime v0.20.4
@@ -213,7 +211,6 @@ require (
        gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
        k8s.io/apiserver v0.32.3 // indirect
        k8s.io/component-base v0.32.3 // indirect
-       k8s.io/gengo/v2 v2.0.0-20240911193312-2b36238f13e9 // indirect
        k8s.io/klog/v2 v2.130.1 // indirect
        k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f // indirect
        moul.io/http2curl/v2 v2.3.0 // indirect
diff --git a/go.sum b/go.sum
index eddf72b5..a1c9c8d3 100644
--- a/go.sum
+++ b/go.sum
@@ -333,8 +333,6 @@ github.com/onsi/ginkgo/v2 v2.22.0 
h1:Yed107/8DjTr0lKCNt7Dn8yQ6ybuDRQoMGrNFKzMfHg
 github.com/onsi/ginkgo/v2 v2.22.0/go.mod 
h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C+NkhLQo=
 github.com/onsi/gomega v1.36.1 h1:bJDPBO7ibjxcbHMgSCoo4Yj18UWbKDlLwX1x9sybDcw=
 github.com/onsi/gomega v1.36.1/go.mod 
h1:PvZbdDc8J6XJEpDK4HCuRBm8a6Fzp9/DmhC9C7yFlog=
-github.com/panjf2000/ants/v2 v2.11.3 
h1:AfI0ngBoXJmYOpDh9m516vjqoUu2sLrIVgppI9TZVpg=
-github.com/panjf2000/ants/v2 v2.11.3/go.mod 
h1:8u92CYMUc6gyvTIw8Ru7Mt7+/ESnJahz5EVtqfrilek=
 github.com/pelletier/go-toml/v2 v2.0.1/go.mod 
h1:r9LEWfGN8R5k0VXJ+0BkIe7MYkRdwZOjgMj2KwnJFUo=
 github.com/pkg/diff v0.0.0-20200914180035-5b29258ca4f7/go.mod 
h1:zO8QMzTeZd5cpnIkz/Gn6iK0jDfGicM1nynOkkPIl28=
 github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod 
h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
@@ -597,12 +595,8 @@ k8s.io/apiserver v0.32.3 
h1:kOw2KBuHOA+wetX1MkmrxgBr648ksz653j26ESuWNY8=
 k8s.io/apiserver v0.32.3/go.mod h1:q1x9B8E/WzShF49wh3ADOh6muSfpmFL0I2t+TG0Zdgc=
 k8s.io/client-go v0.32.3 h1:RKPVltzopkSgHS7aS98QdscAgtgah/+zmpAogooIqVU=
 k8s.io/client-go v0.32.3/go.mod h1:3v0+3k4IcT9bXTc4V2rt+d2ZPPG700Xy6Oi0Gdl2PaY=
-k8s.io/code-generator v0.32.3 h1:31p2TVzC9+hVdSkAFruAk3JY+iSfzrJ83Qij1yZutyw=
-k8s.io/code-generator v0.32.3/go.mod 
h1:+mbiYID5NLsBuqxjQTygKM/DAdKpAjvBzrJd64NU1G8=
 k8s.io/component-base v0.32.3 h1:98WJvvMs3QZ2LYHBzvltFSeJjEx7t5+8s71P7M74u8k=
 k8s.io/component-base v0.32.3/go.mod 
h1:LWi9cR+yPAv7cu2X9rZanTiFKB2kHA+JjmhkKjCZRpI=
-k8s.io/gengo/v2 v2.0.0-20240911193312-2b36238f13e9 
h1:si3PfKm8dDYxgfbeA6orqrtLkvvIeH8UqffFJDl0bz4=
-k8s.io/gengo/v2 v2.0.0-20240911193312-2b36238f13e9/go.mod 
h1:EJykeLsmFC60UQbYJezXkEsG2FLrt0GPNkU5iK5GWxU=
 k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk=
 k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
 k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f 
h1:GA7//TjRY9yWGy1poLzYYJJ4JRdzg3+O6e8I+e+8T5Y=
diff --git a/internal/adc/client/executor.go b/internal/adc/client/executor.go
index b919dcef..77f15a6e 100644
--- a/internal/adc/client/executor.go
+++ b/internal/adc/client/executor.go
@@ -119,13 +119,34 @@ func (e *DefaultADCExecutor) runForSingleServer(ctx 
context.Context, serverAddr
                        "stderr", stderr.String())
                return fmt.Errorf("failed to handle adc output: %w", err)
        }
-       if result.FailedCount > 0 && len(result.Failed) > 0 {
-               reason := result.Failed[0].Reason
-               e.log.Error(fmt.Errorf("adc sync failed: %s", reason), "adc 
sync failed", "result", result)
-               return types.ADCExecutionServerAddrError{
-                       ServerAddr:     serverAddr,
-                       Err:            reason,
-                       FailedStatuses: result.Failed,
+       // Check for sync failures
+       // For apisix-standalone mode: Failed is always empty, check 
EndpointStatus instead
+       if result.FailedCount > 0 {
+               if len(result.Failed) > 0 {
+                       reason := result.Failed[0].Reason
+                       e.log.Error(fmt.Errorf("adc sync failed: %s", reason), 
"adc sync failed", "result", result)
+                       return types.ADCExecutionServerAddrError{
+                               ServerAddr:     serverAddr,
+                               Err:            reason,
+                               FailedStatuses: result.Failed,
+                       }
+               } else if len(result.EndpointStatus) > 0 {
+                       // apisix-standalone mode: use EndpointStatus
+                       var failedEndpoints []string
+                       for _, ep := range result.EndpointStatus {
+                               if !ep.Success {
+                                       failedEndpoints = 
append(failedEndpoints, fmt.Sprintf("%s: %s", ep.Server, ep.Reason))
+                               }
+                       }
+                       if len(failedEndpoints) > 0 {
+                               reason := strings.Join(failedEndpoints, "; ")
+                               e.log.Error(fmt.Errorf("adc sync failed 
(standalone mode): %s", reason), "adc sync failed", "result", result)
+                               return types.ADCExecutionServerAddrError{
+                                       ServerAddr:       serverAddr,
+                                       Err:              reason,
+                                       EndpointStatuses: result.EndpointStatus,
+                               }
+                       }
                }
        }
        e.log.V(1).Info("adc sync success", "result", result)
@@ -452,13 +473,33 @@ func (e *HTTPADCExecutor) handleHTTPResponse(resp 
*http.Response, serverAddr str
        }
 
        // Check for sync failures
-       if result.FailedCount > 0 && len(result.Failed) > 0 {
-               reason := result.Failed[0].Reason
-               e.log.Error(fmt.Errorf("ADC Server sync failed: %s", reason), 
"ADC Server sync failed", "result", result)
-               return types.ADCExecutionServerAddrError{
-                       ServerAddr:     serverAddr,
-                       Err:            reason,
-                       FailedStatuses: result.Failed,
+       // For apisix-standalone mode: Failed is always empty, check 
EndpointStatus instead
+       if result.FailedCount > 0 {
+               if len(result.Failed) > 0 {
+                       reason := result.Failed[0].Reason
+                       e.log.Error(fmt.Errorf("ADC Server sync failed: %s", 
reason), "ADC Server sync failed", "result", result)
+                       return types.ADCExecutionServerAddrError{
+                               ServerAddr:     serverAddr,
+                               Err:            reason,
+                               FailedStatuses: result.Failed,
+                       }
+               } else if len(result.EndpointStatus) > 0 {
+                       // apisix-standalone mode: use EndpointStatus
+                       var failedEndpoints []string
+                       for _, ep := range result.EndpointStatus {
+                               if !ep.Success {
+                                       failedEndpoints = 
append(failedEndpoints, fmt.Sprintf("%s: %s", ep.Server, ep.Reason))
+                               }
+                       }
+                       if len(failedEndpoints) > 0 {
+                               reason := strings.Join(failedEndpoints, "; ")
+                               e.log.Error(fmt.Errorf("ADC Server sync failed 
(standalone mode): %s", reason), "ADC Server sync failed", "result", result)
+                               return types.ADCExecutionServerAddrError{
+                                       ServerAddr:       serverAddr,
+                                       Err:              reason,
+                                       EndpointStatuses: result.EndpointStatus,
+                               }
+                       }
                }
        }
 
diff --git a/internal/manager/controllers.go b/internal/manager/controllers.go
index 38d437ac..a3970566 100644
--- a/internal/manager/controllers.go
+++ b/internal/manager/controllers.go
@@ -20,6 +20,7 @@ package manager
 import (
        "context"
 
+       "github.com/go-logr/logr"
        netv1 "k8s.io/api/networking/v1"
        "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
        "k8s.io/apimachinery/pkg/runtime"
@@ -40,7 +41,6 @@ import (
        "github.com/apache/apisix-ingress-controller/internal/provider"
        types "github.com/apache/apisix-ingress-controller/internal/types"
        "github.com/apache/apisix-ingress-controller/pkg/utils"
-       "github.com/go-logr/logr"
 )
 
 // K8s
diff --git a/internal/provider/apisix/status.go 
b/internal/provider/apisix/status.go
index 9a2824a8..b7e93c4a 100644
--- a/internal/provider/apisix/status.go
+++ b/internal/provider/apisix/status.go
@@ -259,7 +259,10 @@ func (d *apisixProvider) resolveADCExecutionErrors(
        for configName, execErrors := range statusesMap {
                for _, execErr := range execErrors.Errors {
                        for _, failedStatus := range execErr.FailedErrors {
-                               if len(failedStatus.FailedStatuses) == 0 {
+                               // For apisix-standalone mode: EndpointStatuses 
is used instead of FailedStatuses
+                               if len(failedStatus.EndpointStatuses) > 0 {
+                                       d.handleEndpointStatuses(configName, 
failedStatus, statusUpdateMap)
+                               } else if len(failedStatus.FailedStatuses) == 0 
{
                                        d.handleEmptyFailedStatuses(configName, 
failedStatus, statusUpdateMap)
                                } else {
                                        
d.handleDetailedFailedStatuses(configName, failedStatus, statusUpdateMap)
@@ -304,6 +307,61 @@ func (d *apisixProvider) handleEmptyFailedStatuses(
        }
 }
 
+// handleEndpointStatuses handles the endpoint status for apisix-standalone 
mode.
+// In this mode, the sync result contains endpoint-level status instead of 
resource-level status.
+// All resources in the config are marked as failed with the endpoint error 
information.
+func (d *apisixProvider) handleEndpointStatuses(
+       configName string,
+       failedStatus types.ADCExecutionServerAddrError,
+       statusUpdateMap map[types.NamespacedNameKind][]string,
+) {
+       // Build error message from failed endpoints
+       var failedEndpointMsgs []string
+       for _, ep := range failedStatus.EndpointStatuses {
+               if !ep.Success {
+                       failedEndpointMsgs = append(failedEndpointMsgs, 
fmt.Sprintf("%s: %s", ep.Server, ep.Reason))
+               }
+       }
+       errorMsg := fmt.Sprintf("Endpoint sync failed: %s", 
strings.Join(failedEndpointMsgs, "; "))
+
+       // Mark all resources in the config as failed
+       d.markAllResourcesAsFailed(configName, errorMsg, statusUpdateMap)
+}
+
+// markAllResourcesAsFailed marks all resources in the config as failed with 
the given error message.
+func (d *apisixProvider) markAllResourcesAsFailed(
+       configName string,
+       errorMsg string,
+       statusUpdateMap map[types.NamespacedNameKind][]string,
+) {
+       resource, err := d.client.GetResources(configName)
+       if err != nil {
+               d.log.Error(err, "failed to get resources from store", 
"configName", configName)
+               return
+       }
+
+       for _, obj := range resource.Services {
+               d.addResourceToStatusUpdateMap(obj.GetLabels(), errorMsg, 
statusUpdateMap)
+       }
+
+       for _, obj := range resource.Consumers {
+               d.addResourceToStatusUpdateMap(obj.GetLabels(), errorMsg, 
statusUpdateMap)
+       }
+
+       for _, obj := range resource.SSLs {
+               d.addResourceToStatusUpdateMap(obj.GetLabels(), errorMsg, 
statusUpdateMap)
+       }
+
+       globalRules, err := d.client.ListGlobalRules(configName)
+       if err != nil {
+               d.log.Error(err, "failed to list global rules", "configName", 
configName)
+               return
+       }
+       for _, rule := range globalRules {
+               d.addResourceToStatusUpdateMap(rule.GetLabels(), errorMsg, 
statusUpdateMap)
+       }
+}
+
 func (d *apisixProvider) handleDetailedFailedStatuses(
        configName string,
        failedStatus types.ADCExecutionServerAddrError,
diff --git a/internal/types/error.go b/internal/types/error.go
index 80dbf568..5576bcae 100644
--- a/internal/types/error.go
+++ b/internal/types/error.go
@@ -84,9 +84,10 @@ func (e ADCExecutionError) Error() string {
 }
 
 type ADCExecutionServerAddrError struct {
-       Err            string
-       ServerAddr     string
-       FailedStatuses []adc.SyncStatus
+       Err              string
+       ServerAddr       string
+       FailedStatuses   []adc.SyncStatus
+       EndpointStatuses []adc.EndpointStatus
 }
 
 func (e ADCExecutionServerAddrError) Error() string {

Reply via email to