This is an automated email from the ASF dual-hosted git repository. ronething pushed a commit to branch v2.0.0 in repository https://gitbox.apache.org/repos/asf/apisix-ingress-controller.git
commit b9c7f1e33b2d1d574819d604271c68fe4761bd73 Author: Ashing Zheng <[email protected]> AuthorDate: Thu Jan 8 11:04:28 2026 +0800 fix: adjust apisix standalone mode adc sync result (#2697) Signed-off-by: Ashing Zheng <[email protected]> (cherry picked from commit a979e253ce16c53539dc4d6d6386db8ea87a78b2) --- api/adc/types.go | 22 ++++-- internal/adc/client/executor.go | 168 +++++++--------------------------------- 2 files changed, 46 insertions(+), 144 deletions(-) diff --git a/api/adc/types.go b/api/adc/types.go index e5966ac2..f4557450 100644 --- a/api/adc/types.go +++ b/api/adc/types.go @@ -693,12 +693,22 @@ 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"` } type SyncStatus struct { diff --git a/internal/adc/client/executor.go b/internal/adc/client/executor.go index b919dcef..08608611 100644 --- a/internal/adc/client/executor.go +++ b/internal/adc/client/executor.go @@ -27,9 +27,7 @@ import ( "net" "net/http" "os" - "os/exec" "strings" - "sync" "time" "github.com/go-logr/logr" @@ -47,135 +45,6 @@ type ADCExecutor interface { Execute(ctx context.Context, config adctypes.Config, args []string) error } -type DefaultADCExecutor struct { - sync.Mutex - log logr.Logger -} - -func (e *DefaultADCExecutor) Execute(ctx context.Context, config adctypes.Config, args []string) error { - return e.runADC(ctx, config, args) -} - -func (e *DefaultADCExecutor) runADC(ctx context.Context, config adctypes.Config, args []string) error { - var execErrs = types.ADCExecutionError{ - Name: config.Name, - } - - for _, addr := range config.ServerAddrs { - if err := e.runForSingleServerWithTimeout(ctx, addr, config, args); err != nil { - e.log.Error(err, "failed to run adc for server", "server", addr) - var execErr types.ADCExecutionServerAddrError - if errors.As(err, &execErr) { - execErrs.FailedErrors = append(execErrs.FailedErrors, execErr) - } else { - execErrs.FailedErrors = append(execErrs.FailedErrors, types.ADCExecutionServerAddrError{ - ServerAddr: addr, - Err: err.Error(), - }) - } - } - } - if len(execErrs.FailedErrors) > 0 { - return execErrs - } - return nil -} - -func (e *DefaultADCExecutor) runForSingleServerWithTimeout(ctx context.Context, serverAddr string, config adctypes.Config, args []string) error { - ctx, cancel := context.WithTimeout(ctx, 15*time.Second) - defer cancel() - return e.runForSingleServer(ctx, serverAddr, config, args) -} - -func (e *DefaultADCExecutor) runForSingleServer(ctx context.Context, serverAddr string, config adctypes.Config, args []string) error { - cmdArgs := append([]string{}, args...) - if !config.TlsVerify { - cmdArgs = append(cmdArgs, "--tls-skip-verify") - } - - cmdArgs = append(cmdArgs, "--timeout", "15s") - - env := e.prepareEnv(serverAddr, config.BackendType, config.Token) - - var stdout, stderr bytes.Buffer - cmd := exec.CommandContext(ctx, "adc", cmdArgs...) - cmd.Stdout = &stdout - cmd.Stderr = &stderr - cmd.Env = append(os.Environ(), env...) - - e.log.V(1).Info("running adc command", - "command", strings.Join(cmd.Args, " "), - "env", filterSensitiveEnv(env), - ) - - if err := cmd.Run(); err != nil { - return e.buildCmdError(err, stdout.Bytes(), stderr.Bytes()) - } - - result, err := e.handleOutput(stdout.Bytes()) - if err != nil { - e.log.Error(err, "failed to handle adc output", - "stdout", stdout.String(), - "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, - } - } - e.log.V(1).Info("adc sync success", "result", result) - return nil -} - -func (e *DefaultADCExecutor) prepareEnv(serverAddr, mode, token string) []string { - return []string{ - "ADC_EXPERIMENTAL_FEATURE_FLAGS=remote-state-file,parallel-backend-request", - "ADC_RUNNING_MODE=ingress", - "ADC_BACKEND=" + mode, - "ADC_SERVER=" + serverAddr, - "ADC_TOKEN=" + token, - } -} - -// filterSensitiveEnv filters out sensitive information from environment variables for logging -func filterSensitiveEnv(env []string) []string { - filtered := make([]string, 0, len(env)) - for _, envVar := range env { - if strings.Contains(envVar, "ADC_TOKEN=") { - filtered = append(filtered, "ADC_TOKEN=***") - } else { - filtered = append(filtered, envVar) - } - } - return filtered -} - -func (e *DefaultADCExecutor) buildCmdError(runErr error, stdout, stderr []byte) error { - errMsg := string(stderr) - if errMsg == "" { - errMsg = string(stdout) - } - e.log.Error(runErr, "failed to run adc", "output", string(stdout), "stderr", string(stderr)) - return errors.New("failed to sync resources: " + errMsg + ", exit err: " + runErr.Error()) -} - -func (e *DefaultADCExecutor) handleOutput(output []byte) (*adctypes.SyncResult, error) { - e.log.V(1).Info("adc command output", "output", string(output)) - var result adctypes.SyncResult - if lines := bytes.Split(output, []byte{'\n'}); len(lines) > 0 { - output = lines[len(lines)-1] - } - if err := json.Unmarshal(output, &result); err != nil { - return nil, errors.New("failed to unmarshal response: " + string(output) + ", err: " + err.Error()) - } - return &result, nil -} - func BuildADCExecuteArgs(filePath string, labels map[string]string, types []string) []string { args := []string{ "sync", @@ -452,13 +321,36 @@ 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, + } + } + 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, + FailedStatuses: []adctypes.SyncStatus{ + {Reason: reason}, + }, + } + } } }
