github.com/pingcap/tiflow@v0.0.0-20240520035814-5bf52d54e205/engine/pkg/httputil/job_client.go (about) 1 // Copyright 2022 PingCAP, Inc. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // See the License for the specific language governing permissions and 12 // limitations under the License. 13 14 package httputil 15 16 import ( 17 "context" 18 "encoding/json" 19 "fmt" 20 "io" 21 "net/http" 22 23 "github.com/pingcap/log" 24 "github.com/pingcap/tiflow/engine/pkg/openapi" 25 "github.com/pingcap/tiflow/pkg/errors" 26 "github.com/pingcap/tiflow/pkg/httputil" 27 "go.uber.org/zap" 28 ) 29 30 // JobHTTPClient is the http client for job operations, like 'get job detail' 31 type JobHTTPClient interface { 32 // GetJobDetail sends http request to the specific jobmaster to get job detail 33 // path format for get job detail openapi is: 'api/v1/jobs/${JobID}/status' 34 // If no error happens and the response status code is 2XX, return the response body as the job detail. 35 // Otherwise, return nil and an openapi.HTTPError with code and message. 36 GetJobDetail(ctx context.Context, jobMasterAddr string, jobID string) ([]byte, *openapi.HTTPError) 37 // Close releases the resources 38 Close() 39 } 40 41 // jobHTTPClientImpl is the http client for getting job detail 42 type jobHTTPClientImpl struct { 43 cli *httputil.Client 44 } 45 46 // NewJobHTTPClient news a JobHTTPClient. 47 func NewJobHTTPClient(cli *httputil.Client) JobHTTPClient { 48 return &jobHTTPClientImpl{ 49 cli: cli, 50 } 51 } 52 53 // GetJobDetail implements the JobHTTPClient.GetJobDetail 54 // if we get job detail from jobmaster successfully, returns job detail and nil 55 // if we get a not 2XX response from jobmaster, return response body and an error 56 // if we can't get response from jobmaster, return nil and an error 57 func (c *jobHTTPClientImpl) GetJobDetail(ctx context.Context, jobMasterAddr string, jobID string) ([]byte, *openapi.HTTPError) { 58 url := fmt.Sprintf("http://%s%s", jobMasterAddr, fmt.Sprintf(openapi.JobDetailAPIFormat, jobID)) 59 log.Info("get job detail from job master", zap.String("url", url)) 60 resp, err := c.cli.Get(ctx, url) 61 if err != nil { 62 errOut := errors.ErrJobManagerGetJobDetailFail.Wrap(err).GenWithStackByArgs() 63 return nil, openapi.NewHTTPError(errOut) 64 } 65 log.Debug("job master response", zap.Any("response status", resp.Status), zap.String("url", url)) 66 defer resp.Body.Close() 67 68 body, err := io.ReadAll(resp.Body) 69 if err != nil { 70 errOut := errors.ErrJobManagerGetJobDetailFail.Wrap(err).GenWithStackByArgs() 71 return nil, openapi.NewHTTPError(errOut) 72 } 73 74 // if response code is 2XX, body should be the job detail 75 if resp.StatusCode/100 == http.StatusOK/100 { 76 log.Debug("job master response", zap.Any("response body", string(body)), zap.String("url", url)) 77 return body, nil 78 } 79 log.Warn( 80 "failed to get job detail from job master", 81 zap.String("response body", string(body)), 82 zap.String("url", url), 83 zap.String("status", resp.Status), 84 ) 85 86 httpErr := &openapi.HTTPError{} 87 if err := json.Unmarshal(body, httpErr); err != nil { 88 errOut := errors.ErrJobManagerGetJobDetailFail.GenWithStack( 89 "get job detail from job master failed with wrong error format, status: %s, response body: %s", resp.Status, string(body)) 90 return nil, openapi.NewHTTPError(errOut) 91 } 92 return nil, httpErr 93 } 94 95 // Close implements the JobHTTPClient.Close 96 func (c *jobHTTPClientImpl) Close() { 97 if c.cli != nil { 98 c.cli.CloseIdleConnections() 99 } 100 }