diff --git a/docs/swagger.yaml b/docs/swagger.yaml index c03c0a40c..3101e5d8a 100644 --- a/docs/swagger.yaml +++ b/docs/swagger.yaml @@ -1230,12 +1230,12 @@ paths: description: | This endpoint is for ping validates whether the target is reachable and whether the credential is valid. parameters: - - name: id - in: query - type: integer - format: int64 - required: false - description: The replication's policy ID. + - name: target + in: body + description: The target object. + required: true + schema: + $ref: '#/definitions/PingTarget' tags: - Products responses: @@ -1249,6 +1249,31 @@ paths: description: Target not found. 500: description: Unexpected internal errors. + /targets/{id}/ping: + post: + summary: Ping target. + description: | + This endpoint is for ping target. + parameters: + - name: id + in: path + type: integer + format: int64 + required: true + description: The replication's target ID. + tags: + - Products + responses: + 200: + description: Ping replication's target successfully. + 400: + description: Can not ping target. + 401: + description: User need to log in first. + 404: + description: Target ID does not exist. + 500: + description: Unexpected internal errors. /targets/{id}: put: summary: Update replication's target. @@ -1265,7 +1290,7 @@ paths: in: body required: true schema: - $ref: '#/definitions/RepTargetPost' + $ref: '#/definitions/PutTarget' description: Updates of replication's target. tags: - Products @@ -1999,6 +2024,33 @@ definitions: password: type: string description: The target server password. + PingTarget: + type: object + properties: + endpoint: + type: string + description: The target address URL string. + username: + type: string + description: The target server username. + password: + type: string + description: The target server password. + PutTarget: + type: object + properties: + name: + type: string + description: The target name. + endpoint: + type: string + description: The target address URL string. + username: + type: string + description: The target server username. + password: + type: string + description: The target server password. HasAdminRole: type: object properties: diff --git a/src/ui/api/harborapi_test.go b/src/ui/api/harborapi_test.go index db87fa0b0..438632ffa 100644 --- a/src/ui/api/harborapi_test.go +++ b/src/ui/api/harborapi_test.go @@ -96,6 +96,7 @@ func init() { beego.Router("/api/targets/:id([0-9]+)", &TargetAPI{}) beego.Router("/api/targets/:id([0-9]+)/policies/", &TargetAPI{}, "get:ListPolicies") beego.Router("/api/targets/ping", &TargetAPI{}, "post:Ping") + beego.Router("/api/targets/:id([0-9]+)/ping", &TargetAPI{}, "post:PingByID") beego.Router("/api/policies/replication/:id([0-9]+)", &RepPolicyAPI{}) beego.Router("/api/policies/replication", &RepPolicyAPI{}, "get:List") beego.Router("/api/policies/replication", &RepPolicyAPI{}, "post:Post;delete:Delete") @@ -628,11 +629,24 @@ func (a testapi) ListTargets(authInfo usrInfo, targetName string) (int, []apilib return httpStatusCode, successPayload, err } -//Ping target by targetID -func (a testapi) PingTargetsByID(authInfo usrInfo, targetID string) (int, error) { +//Ping target +func (a testapi) PingTarget(authInfo usrInfo, body interface{}) (int, error) { _sling := sling.New().Post(a.basePath) - path := "/api/targets/ping?id=" + targetID + path := "/api/targets/ping" + + _sling = _sling.Path(path) + _sling = _sling.BodyJSON(body) + + httpStatusCode, _, err := request(_sling, jsonAcceptHeader, authInfo) + return httpStatusCode, err +} + +//PingTargetByID ... +func (a testapi) PingTargetByID(authInfo usrInfo, id int) (int, error) { + _sling := sling.New().Post(a.basePath) + + path := fmt.Sprintf("/api/targets/%d/ping", id) _sling = _sling.Path(path) diff --git a/src/ui/api/target.go b/src/ui/api/target.go index bb671cb07..7161d44ec 100644 --- a/src/ui/api/target.go +++ b/src/ui/api/target.go @@ -60,48 +60,7 @@ func (t *TargetAPI) Prepare() { } } -// Ping validates whether the target is reachable and whether the credential is valid -func (t *TargetAPI) Ping() { - var endpoint, username, password string - - idStr := t.GetString("id") - if len(idStr) != 0 { - id, err := strconv.ParseInt(idStr, 10, 64) - if err != nil { - t.CustomAbort(http.StatusBadRequest, fmt.Sprintf("id %s is invalid", idStr)) - } - - target, err := dao.GetRepTarget(id) - if err != nil { - log.Errorf("failed to get target %d: %v", id, err) - t.CustomAbort(http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError)) - } - - if target == nil { - t.CustomAbort(http.StatusNotFound, http.StatusText(http.StatusNotFound)) - } - - endpoint = target.URL - username = target.Username - password = target.Password - - if len(password) != 0 { - password, err = utils.ReversibleDecrypt(password, t.secretKey) - if err != nil { - log.Errorf("failed to decrypt password: %v", err) - t.CustomAbort(http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError)) - } - } - } else { - endpoint = t.GetString("endpoint") - if len(endpoint) == 0 { - t.CustomAbort(http.StatusBadRequest, "id or endpoint is needed") - } - - username = t.GetString("username") - password = t.GetString("password") - } - +func (t *TargetAPI) ping(endpoint, username, password string) { verify, err := config.VerifyRemoteCert() if err != nil { log.Errorf("failed to check whether insecure or not: %v", err) @@ -133,6 +92,48 @@ func (t *TargetAPI) Ping() { } } +// PingByID ping target by ID +func (t *TargetAPI) PingByID() { + id := t.GetIDFromURL() + + target, err := dao.GetRepTarget(id) + if err != nil { + log.Errorf("failed to get target %d: %v", id, err) + t.CustomAbort(http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError)) + } + if target == nil { + t.CustomAbort(http.StatusNotFound, fmt.Sprintf("target %d not found", id)) + } + + endpoint := target.URL + username := target.Username + password := target.Password + if len(password) != 0 { + password, err = utils.ReversibleDecrypt(password, t.secretKey) + if err != nil { + log.Errorf("failed to decrypt password: %v", err) + t.CustomAbort(http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError)) + } + } + t.ping(endpoint, username, password) +} + +// Ping validates whether the target is reachable and whether the credential is valid +func (t *TargetAPI) Ping() { + req := struct { + Endpoint string `json:"endpoint"` + Username string `json:"username"` + Password string `json:"password"` + }{} + t.DecodeJSONReq(&req) + + if len(req.Endpoint) == 0 { + t.CustomAbort(http.StatusBadRequest, "endpoint is required") + } + + t.ping(req.Endpoint, req.Username, req.Password) +} + // Get ... func (t *TargetAPI) Get() { id := t.GetIDFromURL() @@ -217,13 +218,13 @@ func (t *TargetAPI) Post() { func (t *TargetAPI) Put() { id := t.GetIDFromURL() - originalTarget, err := dao.GetRepTarget(id) + target, err := dao.GetRepTarget(id) if err != nil { log.Errorf("failed to get target %d: %v", id, err) t.CustomAbort(http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError)) } - if originalTarget == nil { + if target == nil { t.CustomAbort(http.StatusNotFound, http.StatusText(http.StatusNotFound)) } @@ -245,10 +246,33 @@ func (t *TargetAPI) Put() { t.CustomAbort(http.StatusBadRequest, "the target is associated with policy which is enabled") } - target := &models.RepTarget{} - t.DecodeJSONReqAndValidate(target) + req := struct { + Name *string `json:"name"` + Endpoint *string `json:"endpoint"` + Username *string `json:"username"` + Password *string `json:"password"` + }{} + t.DecodeJSONReq(&req) - if target.Name != originalTarget.Name { + originalName := target.Name + originalURL := target.URL + + if req.Name != nil { + target.Name = *req.Name + } + if req.Endpoint != nil { + target.URL = *req.Endpoint + } + if req.Username != nil { + target.Username = *req.Username + } + if req.Password != nil { + target.Password = *req.Password + } + + t.Validate(target) + + if target.Name != originalName { ta, err := dao.GetRepTargetByName(target.Name) if err != nil { log.Errorf("failed to get target %s: %v", target.Name, err) @@ -260,7 +284,7 @@ func (t *TargetAPI) Put() { } } - if target.URL != originalTarget.URL { + if target.URL != originalURL { ta, err := dao.GetRepTargetByEndpoint(target.URL) if err != nil { log.Errorf("failed to get target [ %s ]: %v", target.URL, err) @@ -272,8 +296,6 @@ func (t *TargetAPI) Put() { } } - target.ID = id - if len(target.Password) != 0 { target.Password, err = utils.ReversibleEncrypt(target.Password, t.secretKey) if err != nil { diff --git a/src/ui/api/target_test.go b/src/ui/api/target_test.go index 62709a39e..a313a5cf3 100644 --- a/src/ui/api/target_test.go +++ b/src/ui/api/target_test.go @@ -103,12 +103,48 @@ func TestTargetPing(t *testing.T) { assert := assert.New(t) apiTest := newHarborAPI() + fmt.Println("Testing Targets Ping Post API") + //case 1 + body := struct { + Endpoint string `json:"endpoint"` + Username string `json:"username"` + Password string `json:"password"` + }{ + Endpoint: os.Getenv("REGISTRY_URL"), + Username: adminName, + Password: adminPwd, + } + httpStatusCode, err = apiTest.PingTarget(*admin, body) + if err != nil { + t.Error("Error while ping target", err.Error()) + t.Log(err) + } else { + assert.Equal(int(200), httpStatusCode, "") + } + + //case 2 + body.Endpoint = "" + httpStatusCode, err = apiTest.PingTarget(*admin, body) + if err != nil { + t.Error("Error while ping target", err.Error()) + } else { + assert.Equal(int(400), httpStatusCode, "") + } +} + +func TestTargetPingByID(t *testing.T) { + var httpStatusCode int + var err error + + assert := assert.New(t) + apiTest := newHarborAPI() + fmt.Println("Testing Targets Ping Post API") //-------------------case 1 : response code = 200------------------------// fmt.Println("case 1 : response code = 200") - id := strconv.Itoa(addTargetID) - httpStatusCode, err = apiTest.PingTargetsByID(*admin, id) + id := addTargetID + httpStatusCode, err = apiTest.PingTargetByID(*admin, id) if err != nil { t.Error("Error whihle ping target", err.Error()) t.Log(err) @@ -118,26 +154,15 @@ func TestTargetPing(t *testing.T) { //--------------case 2 : response code = 404,target not found------------// fmt.Println("case 2 : response code = 404,target not found") - id = "1111" - httpStatusCode, err = apiTest.PingTargetsByID(*admin, id) + + id = 1111 + httpStatusCode, err = apiTest.PingTargetByID(*admin, id) if err != nil { t.Error("Error whihle ping target", err.Error()) t.Log(err) } else { assert.Equal(int(404), httpStatusCode, "httpStatusCode should be 404") } - - //------------case 3 : response code = 400,targetID is invalid-----------// - fmt.Println("case 2 : response code = 400,target not found") - id = "cc" - httpStatusCode, err = apiTest.PingTargetsByID(*admin, id) - if err != nil { - t.Error("Error whihle ping target", err.Error()) - t.Log(err) - } else { - assert.Equal(int(400), httpStatusCode, "httpStatusCode should be 400") - } - } func TestTargetGetByID(t *testing.T) { diff --git a/src/ui/router.go b/src/ui/router.go index d76158a31..0b65b5165 100644 --- a/src/ui/router.go +++ b/src/ui/router.go @@ -89,6 +89,7 @@ func initRouters() { beego.Router("/api/targets/:id([0-9]+)", &api.TargetAPI{}) beego.Router("/api/targets/:id([0-9]+)/policies/", &api.TargetAPI{}, "get:ListPolicies") beego.Router("/api/targets/ping", &api.TargetAPI{}, "post:Ping") + beego.Router("/api/targets/:id([0-9]+)/ping", &api.TargetAPI{}, "post:PingByID") beego.Router("/api/users/:id/sysadmin", &api.UserAPI{}, "put:ToggleUserAdminRole") beego.Router("/api/repositories/top", &api.RepositoryAPI{}, "get:GetTopRepos") beego.Router("/api/logs", &api.LogAPI{})