@@ -43,6 +43,7 @@ const (
4343
4444type ADCExecutor interface {
4545 Execute (ctx context.Context , config adctypes.Config , args []string ) error
46+ Validate (ctx context.Context , config adctypes.Config , args []string ) error
4647}
4748
4849func BuildADCExecuteArgs (filePath string , labels map [string ]string , types []string ) []string {
@@ -81,6 +82,12 @@ type ADCServerOpts struct {
8182 CacheKey string `json:"cacheKey"`
8283}
8384
85+ type ADCValidateResult struct {
86+ Success * bool `json:"success,omitempty"`
87+ ErrorMessage string `json:"message,omitempty"`
88+ Errors []types.ADCValidationDetail `json:"errors,omitempty"`
89+ }
90+
8491// HTTPADCExecutor implements ADCExecutor interface using HTTP calls to ADC Server
8592type HTTPADCExecutor struct {
8693 httpClient * http.Client
@@ -123,6 +130,10 @@ func (e *HTTPADCExecutor) Execute(ctx context.Context, config adctypes.Config, a
123130 return e .runHTTPSync (ctx , config , args )
124131}
125132
133+ func (e * HTTPADCExecutor ) Validate (ctx context.Context , config adctypes.Config , args []string ) error {
134+ return e .runHTTPValidate (ctx , config , args )
135+ }
136+
126137// runHTTPSync performs HTTP sync to ADC Server for each server address
127138func (e * HTTPADCExecutor ) runHTTPSync (ctx context.Context , config adctypes.Config , args []string ) error {
128139 var execErrs = types.ADCExecutionError {
@@ -157,6 +168,38 @@ func (e *HTTPADCExecutor) runHTTPSync(ctx context.Context, config adctypes.Confi
157168 return nil
158169}
159170
171+ func (e * HTTPADCExecutor ) runHTTPValidate (ctx context.Context , config adctypes.Config , args []string ) error {
172+ var validationErr = types.ADCValidationError {
173+ Name : config .Name ,
174+ }
175+ var infraErrs []error
176+
177+ serverAddrs := func () []string {
178+ return config .ServerAddrs
179+ }()
180+ e .log .V (1 ).Info ("running http validate" , "serverAddrs" , serverAddrs )
181+
182+ for _ , addr := range serverAddrs {
183+ if err := e .runHTTPValidateForSingleServer (ctx , addr , config , args ); err != nil {
184+ e .log .Error (err , "failed to run http validate for server" , "server" , addr )
185+ var validationServerErr types.ADCValidationServerAddrError
186+ if errors .As (err , & validationServerErr ) {
187+ validationErr .FailedErrors = append (validationErr .FailedErrors , validationServerErr )
188+ continue
189+ }
190+ infraErrs = append (infraErrs , err )
191+ }
192+ }
193+
194+ if len (validationErr .FailedErrors ) > 0 {
195+ return validationErr
196+ }
197+ if len (infraErrs ) > 0 {
198+ return errors .Join (infraErrs ... )
199+ }
200+ return nil
201+ }
202+
160203// runHTTPSyncForSingleServer performs HTTP sync to a single ADC Server
161204func (e * HTTPADCExecutor ) runHTTPSyncForSingleServer (ctx context.Context , serverAddr string , config adctypes.Config , args []string ) error {
162205 ctx , cancel := context .WithTimeout (ctx , e .httpClient .Timeout )
@@ -175,7 +218,7 @@ func (e *HTTPADCExecutor) runHTTPSyncForSingleServer(ctx context.Context, server
175218 }
176219
177220 // Build HTTP request
178- req , err := e .buildHTTPRequest (ctx , serverAddr , config , labels , types , resources )
221+ req , err := e .buildHTTPRequest (ctx , serverAddr , config , labels , types , resources , http . MethodPut , "/sync" )
179222 if err != nil {
180223 return fmt .Errorf ("failed to build HTTP request: %w" , err )
181224 }
@@ -195,6 +238,38 @@ func (e *HTTPADCExecutor) runHTTPSyncForSingleServer(ctx context.Context, server
195238 return e .handleHTTPResponse (resp , serverAddr )
196239}
197240
241+ func (e * HTTPADCExecutor ) runHTTPValidateForSingleServer (ctx context.Context , serverAddr string , config adctypes.Config , args []string ) error {
242+ ctx , cancel := context .WithTimeout (ctx , e .httpClient .Timeout )
243+ defer cancel ()
244+
245+ labels , types , filePath , err := e .parseArgs (args )
246+ if err != nil {
247+ return fmt .Errorf ("failed to parse args: %w" , err )
248+ }
249+
250+ resources , err := e .loadResourcesFromFile (filePath )
251+ if err != nil {
252+ return fmt .Errorf ("failed to load resources from file %s: %w" , filePath , err )
253+ }
254+
255+ req , err := e .buildHTTPRequest (ctx , serverAddr , config , labels , types , resources , http .MethodPut , "/validate" )
256+ if err != nil {
257+ return fmt .Errorf ("failed to build validate request: %w" , err )
258+ }
259+
260+ resp , err := e .httpClient .Do (req )
261+ if err != nil {
262+ return fmt .Errorf ("failed to send HTTP request: %w" , err )
263+ }
264+ defer func () {
265+ if closeErr := resp .Body .Close (); closeErr != nil {
266+ e .log .Error (closeErr , "failed to close response body" )
267+ }
268+ }()
269+
270+ return e .handleHTTPValidateResponse (resp , serverAddr )
271+ }
272+
198273// parseArgs parses the command line arguments to extract labels, types, and file path
199274func (e * HTTPADCExecutor ) parseArgs (args []string ) (map [string ]string , []string , string , error ) {
200275 labels := make (map [string ]string )
@@ -248,7 +323,7 @@ func (e *HTTPADCExecutor) loadResourcesFromFile(filePath string) (*adctypes.Reso
248323}
249324
250325// buildHTTPRequest builds the HTTP request for ADC Server
251- func (e * HTTPADCExecutor ) buildHTTPRequest (ctx context.Context , serverAddr string , config adctypes.Config , labels map [string ]string , types []string , resources * adctypes.Resources ) (* http.Request , error ) {
326+ func (e * HTTPADCExecutor ) buildHTTPRequest (ctx context.Context , serverAddr string , config adctypes.Config , labels map [string ]string , types []string , resources * adctypes.Resources , method string , path string ) (* http.Request , error ) {
252327 // Prepare request body
253328 tlsVerify := config .TlsVerify
254329 reqBody := ADCServerRequest {
@@ -274,7 +349,7 @@ func (e *HTTPADCExecutor) buildHTTPRequest(ctx context.Context, serverAddr strin
274349 }
275350
276351 e .log .V (1 ).Info ("sending HTTP request to ADC Server" ,
277- "url" , e .serverURL + "/sync" ,
352+ "url" , e .serverURL + path ,
278353 "server" , serverAddr ,
279354 "mode" , config .BackendType ,
280355 "cacheKey" , config .Name ,
@@ -284,7 +359,7 @@ func (e *HTTPADCExecutor) buildHTTPRequest(ctx context.Context, serverAddr strin
284359 )
285360
286361 // Create HTTP request
287- req , err := http .NewRequestWithContext (ctx , "PUT" , e .serverURL + "/sync" , bytes .NewBuffer (jsonData ))
362+ req , err := http .NewRequestWithContext (ctx , method , e .serverURL + path , bytes .NewBuffer (jsonData ))
288363 if err != nil {
289364 return nil , fmt .Errorf ("failed to create HTTP request: %w" , err )
290365 }
@@ -357,3 +432,63 @@ func (e *HTTPADCExecutor) handleHTTPResponse(resp *http.Response, serverAddr str
357432 e .log .V (1 ).Info ("ADC Server sync success" , "result" , result )
358433 return nil
359434}
435+
436+ func (e * HTTPADCExecutor ) handleHTTPValidateResponse (resp * http.Response , serverAddr string ) error {
437+ body , err := io .ReadAll (resp .Body )
438+ if err != nil {
439+ return fmt .Errorf ("failed to read response body: %w" , err )
440+ }
441+
442+ e .log .V (1 ).Info ("received HTTP validate response from ADC Server" ,
443+ "server" , serverAddr ,
444+ "status" , resp .StatusCode ,
445+ "response" , string (body ),
446+ )
447+
448+ parseValidationResult := func () * ADCValidateResult {
449+ if len (body ) == 0 {
450+ return nil
451+ }
452+ var result ADCValidateResult
453+ if err := json .Unmarshal (body , & result ); err != nil {
454+ return nil
455+ }
456+ return & result
457+ }
458+
459+ if resp .StatusCode == http .StatusBadRequest {
460+ result := parseValidationResult ()
461+ errMsg := string (body )
462+ if result != nil && result .ErrorMessage != "" {
463+ errMsg = result .ErrorMessage
464+ }
465+ return types.ADCValidationServerAddrError {
466+ ServerAddr : serverAddr ,
467+ Err : errMsg ,
468+ ValidationErrors : func () []types.ADCValidationDetail {
469+ if result == nil {
470+ return nil
471+ }
472+ return result .Errors
473+ }(),
474+ }
475+ }
476+
477+ if resp .StatusCode / 100 != 2 {
478+ return fmt .Errorf ("HTTP %d: %s" , resp .StatusCode , string (body ))
479+ }
480+
481+ if result := parseValidationResult (); result != nil && result .Success != nil && ! * result .Success {
482+ errMsg := result .ErrorMessage
483+ if errMsg == "" {
484+ errMsg = "ADC validation failed"
485+ }
486+ return types.ADCValidationServerAddrError {
487+ ServerAddr : serverAddr ,
488+ Err : errMsg ,
489+ ValidationErrors : result .Errors ,
490+ }
491+ }
492+
493+ return nil
494+ }
0 commit comments