Skip to content

Commit d95462e

Browse files
AlinsRanCopilot
andcommitted
refactor: use ADC server /validate endpoint for all backends
Previously the apisix-standalone backend bypassed the ADC server and called APISIX's /apisix/admin/configs/validate directly. With api7/adc#440, the ADC server now exposes a /validate endpoint (same input format as /sync) that handles both apisix-standalone and apisix backends uniformly. Changes: - Remove apisix-standalone special-case in runHTTPValidateForSingleServer; all backends now call ADC server POST /validate - Fix ADCValidateResult.ErrorMessage JSON tag: errorMessage -> message to match the ADC server response format from api7/adc#440 - Remove buildAPISIXValidateRequest, apisixValidateRequest, newBackendHTTPClient, buildAPISIXValidatePayload and helpers - Update unit tests accordingly Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 70137d0 commit d95462e

6 files changed

Lines changed: 8 additions & 394 deletions

File tree

internal/adc/client/executor.go

Lines changed: 3 additions & 238 deletions
Original file line numberDiff line numberDiff line change
@@ -20,14 +20,12 @@ package client
2020
import (
2121
"bytes"
2222
"context"
23-
"crypto/tls"
2423
"encoding/json"
2524
"errors"
2625
"fmt"
2726
"io"
2827
"net"
2928
"net/http"
30-
"net/url"
3129
"os"
3230
"strings"
3331
"time"
@@ -86,7 +84,7 @@ type ADCServerOpts struct {
8684

8785
type ADCValidateResult struct {
8886
Success *bool `json:"success,omitempty"`
89-
ErrorMessage string `json:"errorMessage,omitempty"`
87+
ErrorMessage string `json:"message,omitempty"`
9088
Errors []types.ADCValidationDetail `json:"errors,omitempty"`
9189
}
9290

@@ -254,21 +252,12 @@ func (e *HTTPADCExecutor) runHTTPValidateForSingleServer(ctx context.Context, se
254252
return fmt.Errorf("failed to load resources from file %s: %w", filePath, err)
255253
}
256254

257-
var (
258-
req *http.Request
259-
httpClient = e.httpClient
260-
)
261-
if config.BackendType == "apisix-standalone" {
262-
req, err = e.buildAPISIXValidateRequest(ctx, serverAddr, config, resources)
263-
httpClient = e.newBackendHTTPClient(config)
264-
} else {
265-
req, err = e.buildHTTPRequest(ctx, serverAddr, config, labels, types, resources, http.MethodPost, "/validate")
266-
}
255+
req, err := e.buildHTTPRequest(ctx, serverAddr, config, labels, types, resources, http.MethodPost, "/validate")
267256
if err != nil {
268257
return fmt.Errorf("failed to build validate request: %w", err)
269258
}
270259

271-
resp, err := httpClient.Do(req)
260+
resp, err := e.httpClient.Do(req)
272261
if err != nil {
273262
return fmt.Errorf("failed to send HTTP request: %w", err)
274263
}
@@ -281,230 +270,6 @@ func (e *HTTPADCExecutor) runHTTPValidateForSingleServer(ctx context.Context, se
281270
return e.handleHTTPValidateResponse(resp, serverAddr)
282271
}
283272

284-
type apisixValidateRequest struct {
285-
Routes []map[string]any `json:"routes,omitempty"`
286-
Services []map[string]any `json:"services,omitempty"`
287-
Consumers []map[string]any `json:"consumers,omitempty"`
288-
SSLs []map[string]any `json:"ssls,omitempty"`
289-
GlobalRules []map[string]any `json:"global_rules,omitempty"`
290-
StreamRoutes []map[string]any `json:"stream_routes,omitempty"`
291-
PluginMetadata []map[string]any `json:"plugin_metadata,omitempty"`
292-
Upstreams []map[string]any `json:"upstreams,omitempty"`
293-
}
294-
295-
func (e *HTTPADCExecutor) buildAPISIXValidateRequest(ctx context.Context, serverAddr string, config adctypes.Config, resources *adctypes.Resources) (*http.Request, error) {
296-
body, err := buildAPISIXValidatePayload(resources)
297-
if err != nil {
298-
return nil, err
299-
}
300-
301-
jsonData, err := json.Marshal(body)
302-
if err != nil {
303-
return nil, fmt.Errorf("failed to marshal APISIX validate request body: %w", err)
304-
}
305-
306-
validateURL, err := url.JoinPath(serverAddr, "/apisix/admin/configs/validate")
307-
if err != nil {
308-
return nil, fmt.Errorf("failed to build APISIX validate URL: %w", err)
309-
}
310-
311-
e.log.V(1).Info("sending APISIX validate request",
312-
"url", validateURL,
313-
"server", serverAddr,
314-
"cacheKey", config.Name,
315-
"bodyLen", len(jsonData),
316-
)
317-
318-
req, err := http.NewRequestWithContext(ctx, http.MethodPost, validateURL, bytes.NewBuffer(jsonData))
319-
if err != nil {
320-
return nil, fmt.Errorf("failed to create APISIX validate request: %w", err)
321-
}
322-
323-
req.Header.Set("Content-Type", "application/json")
324-
req.Header.Set("X-API-KEY", config.Token)
325-
return req, nil
326-
}
327-
328-
func (e *HTTPADCExecutor) newBackendHTTPClient(config adctypes.Config) *http.Client {
329-
transport := http.DefaultTransport.(*http.Transport).Clone()
330-
if transport.TLSClientConfig == nil {
331-
transport.TLSClientConfig = &tls.Config{}
332-
}
333-
transport.TLSClientConfig.MinVersion = tls.VersionTLS12
334-
if !config.TlsVerify {
335-
transport.TLSClientConfig.InsecureSkipVerify = true
336-
}
337-
338-
return &http.Client{
339-
Timeout: e.httpClient.Timeout,
340-
Transport: transport,
341-
}
342-
}
343-
344-
func buildAPISIXValidatePayload(resources *adctypes.Resources) (*apisixValidateRequest, error) {
345-
body := &apisixValidateRequest{}
346-
347-
for _, service := range resources.Services {
348-
if service == nil {
349-
continue
350-
}
351-
352-
serviceMap, err := toMap(service)
353-
if err != nil {
354-
return nil, err
355-
}
356-
delete(serviceMap, "routes")
357-
delete(serviceMap, "stream_routes")
358-
delete(serviceMap, "upstreams")
359-
360-
body.Services = append(body.Services, serviceMap)
361-
362-
for _, upstream := range service.Upstreams {
363-
upstreamMap, err := toMap(upstream)
364-
if err != nil {
365-
return nil, err
366-
}
367-
body.Upstreams = append(body.Upstreams, upstreamMap)
368-
}
369-
370-
for _, route := range service.Routes {
371-
routeMap, err := buildAPISIXRouteValidateObject(route)
372-
if err != nil {
373-
return nil, err
374-
}
375-
if service.ID != "" {
376-
routeMap["service_id"] = service.ID
377-
}
378-
body.Routes = append(body.Routes, routeMap)
379-
}
380-
381-
for _, streamRoute := range service.StreamRoutes {
382-
streamRouteMap, err := toMap(streamRoute)
383-
if err != nil {
384-
return nil, err
385-
}
386-
body.StreamRoutes = append(body.StreamRoutes, streamRouteMap)
387-
}
388-
}
389-
390-
for _, consumer := range resources.Consumers {
391-
consumerMap, err := buildAPISIXConsumerValidateObject(consumer)
392-
if err != nil {
393-
return nil, err
394-
}
395-
body.Consumers = append(body.Consumers, consumerMap)
396-
}
397-
398-
for _, ssl := range resources.SSLs {
399-
sslMap, err := buildAPISIXSSLValidateObject(ssl)
400-
if err != nil {
401-
return nil, err
402-
}
403-
body.SSLs = append(body.SSLs, sslMap)
404-
}
405-
406-
if len(resources.GlobalRules) > 0 {
407-
globalRuleMap, err := toMap(&adctypes.GlobalRuleItem{
408-
Metadata: adctypes.Metadata{ID: "validation-global-rule"},
409-
Plugins: adctypes.Plugins(resources.GlobalRules),
410-
})
411-
if err != nil {
412-
return nil, err
413-
}
414-
body.GlobalRules = append(body.GlobalRules, globalRuleMap)
415-
}
416-
417-
for pluginName, pluginConfig := range resources.PluginMetadata {
418-
m := map[string]any{"id": pluginName}
419-
if cfg, ok := pluginConfig.(map[string]any); ok {
420-
for k, v := range cfg {
421-
m[k] = v
422-
}
423-
}
424-
body.PluginMetadata = append(body.PluginMetadata, m)
425-
}
426-
427-
return body, nil
428-
}
429-
430-
func buildAPISIXRouteValidateObject(route *adctypes.Route) (map[string]any, error) {
431-
routeMap, err := toMap(route)
432-
if err != nil {
433-
return nil, err
434-
}
435-
436-
delete(routeMap, "description")
437-
return routeMap, nil
438-
}
439-
440-
func buildAPISIXConsumerValidateObject(consumer *adctypes.Consumer) (map[string]any, error) {
441-
consumerMap, err := toMap(consumer)
442-
if err != nil {
443-
return nil, err
444-
}
445-
446-
if len(consumer.Credentials) == 0 {
447-
return consumerMap, nil
448-
}
449-
450-
plugins, ok := consumerMap["plugins"].(map[string]any)
451-
if !ok || plugins == nil {
452-
plugins = make(map[string]any, len(consumer.Credentials))
453-
}
454-
455-
for _, credential := range consumer.Credentials {
456-
plugins[credential.Type] = credential.Config
457-
}
458-
459-
consumerMap["plugins"] = plugins
460-
delete(consumerMap, "credentials")
461-
return consumerMap, nil
462-
}
463-
464-
func buildAPISIXSSLValidateObject(ssl *adctypes.SSL) (map[string]any, error) {
465-
sslMap, err := toMap(ssl)
466-
if err != nil {
467-
return nil, err
468-
}
469-
470-
delete(sslMap, "certificates")
471-
472-
switch len(ssl.Certificates) {
473-
case 0:
474-
return sslMap, nil
475-
case 1:
476-
sslMap["cert"] = ssl.Certificates[0].Certificate
477-
sslMap["key"] = ssl.Certificates[0].Key
478-
default:
479-
sslMap["cert"] = ssl.Certificates[0].Certificate
480-
sslMap["key"] = ssl.Certificates[0].Key
481-
482-
certs := make([]string, 0, len(ssl.Certificates)-1)
483-
keys := make([]string, 0, len(ssl.Certificates)-1)
484-
for _, certificate := range ssl.Certificates[1:] {
485-
certs = append(certs, certificate.Certificate)
486-
keys = append(keys, certificate.Key)
487-
}
488-
sslMap["certs"] = certs
489-
sslMap["keys"] = keys
490-
}
491-
492-
return sslMap, nil
493-
}
494-
495-
func toMap(obj any) (map[string]any, error) {
496-
data, err := json.Marshal(obj)
497-
if err != nil {
498-
return nil, fmt.Errorf("failed to marshal validation object: %w", err)
499-
}
500-
501-
var out map[string]any
502-
if err := json.Unmarshal(data, &out); err != nil {
503-
return nil, fmt.Errorf("failed to unmarshal validation object: %w", err)
504-
}
505-
return out, nil
506-
}
507-
508273
// parseArgs parses the command line arguments to extract labels, types, and file path
509274
func (e *HTTPADCExecutor) parseArgs(args []string) (map[string]string, []string, string, error) {
510275
labels := make(map[string]string)

0 commit comments

Comments
 (0)