Add new API endpoints for push mirrors management (#19841)
- Add a new push mirror to specific repository - Sync now ( send all the changes to the configured push mirrors ) - Get list of all push mirrors of a repository - Get a push mirror by ID - Delete push mirror by ID Signed-off-by: Mohamed Sekour <mohamed.sekour@exfo.com> Signed-off-by: Andrew Thornton <art27@cantab.net> Co-authored-by: zeripath <art27@cantab.net>
This commit is contained in:
parent
e819da0837
commit
0e61a74e5a
14 changed files with 787 additions and 44 deletions
|
@ -982,6 +982,15 @@ func Routes() *web.Route {
|
|||
})
|
||||
}, reqRepoReader(unit.TypeReleases))
|
||||
m.Post("/mirror-sync", reqToken(), reqRepoWriter(unit.TypeCode), repo.MirrorSync)
|
||||
m.Post("/push_mirrors-sync", reqAdmin(), repo.PushMirrorSync)
|
||||
m.Group("/push_mirrors", func() {
|
||||
m.Combo("").Get(repo.ListPushMirrors).
|
||||
Post(bind(api.CreatePushMirrorOption{}), repo.AddPushMirror)
|
||||
m.Combo("/{name}").
|
||||
Delete(repo.DeletePushMirrorByRemoteName).
|
||||
Get(repo.GetPushMirrorByName)
|
||||
}, reqAdmin())
|
||||
|
||||
m.Get("/editorconfig/{filename}", context.ReferencesGitRepo(), context.RepoRefForAPI, reqRepoReader(unit.TypeCode), repo.GetEditorconfig)
|
||||
m.Group("/pulls", func() {
|
||||
m.Combo("").Get(repo.ListPullRequests).
|
||||
|
|
|
@ -6,13 +6,25 @@ package repo
|
|||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"code.gitea.io/gitea/models"
|
||||
"code.gitea.io/gitea/models/db"
|
||||
repo_model "code.gitea.io/gitea/models/repo"
|
||||
"code.gitea.io/gitea/models/unit"
|
||||
"code.gitea.io/gitea/modules/context"
|
||||
"code.gitea.io/gitea/modules/convert"
|
||||
mirror_module "code.gitea.io/gitea/modules/mirror"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
api "code.gitea.io/gitea/modules/structs"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
"code.gitea.io/gitea/modules/web"
|
||||
"code.gitea.io/gitea/routers/api/v1/utils"
|
||||
"code.gitea.io/gitea/services/forms"
|
||||
"code.gitea.io/gitea/services/migrations"
|
||||
mirror_service "code.gitea.io/gitea/services/mirror"
|
||||
)
|
||||
|
||||
// MirrorSync adds a mirrored repository to the sync queue
|
||||
|
@ -63,3 +75,317 @@ func MirrorSync(ctx *context.APIContext) {
|
|||
|
||||
ctx.Status(http.StatusOK)
|
||||
}
|
||||
|
||||
// PushMirrorSync adds all push mirrored repositories to the sync queue
|
||||
func PushMirrorSync(ctx *context.APIContext) {
|
||||
// swagger:operation POST /repos/{owner}/{repo}/push_mirrors-sync repository repoPushMirrorSync
|
||||
// ---
|
||||
// summary: Sync all push mirrored repository
|
||||
// produces:
|
||||
// - application/json
|
||||
// parameters:
|
||||
// - name: owner
|
||||
// in: path
|
||||
// description: owner of the repo to sync
|
||||
// type: string
|
||||
// required: true
|
||||
// - name: repo
|
||||
// in: path
|
||||
// description: name of the repo to sync
|
||||
// type: string
|
||||
// required: true
|
||||
// responses:
|
||||
// "200":
|
||||
// "$ref": "#/responses/empty"
|
||||
// "400":
|
||||
// "$ref": "#/responses/error"
|
||||
// "403":
|
||||
// "$ref": "#/responses/forbidden"
|
||||
|
||||
if !setting.Mirror.Enabled {
|
||||
ctx.Error(http.StatusBadRequest, "PushMirrorSync", "Mirror feature is disabled")
|
||||
return
|
||||
}
|
||||
// Get All push mirrors of a specific repo
|
||||
pushMirrors, _, err := repo_model.GetPushMirrorsByRepoID(ctx, ctx.Repo.Repository.ID, db.ListOptions{})
|
||||
if err != nil {
|
||||
ctx.Error(http.StatusNotFound, "PushMirrorSync", err)
|
||||
return
|
||||
}
|
||||
for _, mirror := range pushMirrors {
|
||||
ok := mirror_service.SyncPushMirror(ctx, mirror.ID)
|
||||
if !ok {
|
||||
ctx.Error(http.StatusInternalServerError, "PushMirrorSync", "error occurred when syncing push mirror "+mirror.RemoteName)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
ctx.Status(http.StatusOK)
|
||||
}
|
||||
|
||||
// ListPushMirrors get list of push mirrors of a repository
|
||||
func ListPushMirrors(ctx *context.APIContext) {
|
||||
// swagger:operation GET /repos/{owner}/{repo}/push_mirrors repository repoListPushMirrors
|
||||
// ---
|
||||
// summary: Get all push mirrors of the repository
|
||||
// produces:
|
||||
// - application/json
|
||||
// parameters:
|
||||
// - name: owner
|
||||
// in: path
|
||||
// description: owner of the repo
|
||||
// type: string
|
||||
// required: true
|
||||
// - name: repo
|
||||
// in: path
|
||||
// description: name of the repo
|
||||
// type: string
|
||||
// required: true
|
||||
// - name: page
|
||||
// in: query
|
||||
// description: page number of results to return (1-based)
|
||||
// type: integer
|
||||
// - name: limit
|
||||
// in: query
|
||||
// description: page size of results
|
||||
// type: integer
|
||||
// responses:
|
||||
// "200":
|
||||
// "$ref": "#/responses/PushMirrorList"
|
||||
// "400":
|
||||
// "$ref": "#/responses/error"
|
||||
// "403":
|
||||
// "$ref": "#/responses/forbidden"
|
||||
|
||||
if !setting.Mirror.Enabled {
|
||||
ctx.Error(http.StatusBadRequest, "GetPushMirrorsByRepoID", "Mirror feature is disabled")
|
||||
return
|
||||
}
|
||||
|
||||
repo := ctx.Repo.Repository
|
||||
// Get all push mirrors for the specified repository.
|
||||
pushMirrors, count, err := repo_model.GetPushMirrorsByRepoID(ctx, repo.ID, utils.GetListOptions(ctx))
|
||||
if err != nil {
|
||||
ctx.Error(http.StatusNotFound, "GetPushMirrorsByRepoID", err)
|
||||
return
|
||||
}
|
||||
|
||||
responsePushMirrors := make([]*api.PushMirror, 0, len(pushMirrors))
|
||||
for _, mirror := range pushMirrors {
|
||||
m, err := convert.ToPushMirror(mirror)
|
||||
if err == nil {
|
||||
responsePushMirrors = append(responsePushMirrors, m)
|
||||
}
|
||||
|
||||
}
|
||||
ctx.SetLinkHeader(len(responsePushMirrors), utils.GetListOptions(ctx).PageSize)
|
||||
ctx.SetTotalCountHeader(count)
|
||||
ctx.JSON(http.StatusOK, responsePushMirrors)
|
||||
}
|
||||
|
||||
// GetPushMirrorByName get push mirror of a repository by name
|
||||
func GetPushMirrorByName(ctx *context.APIContext) {
|
||||
// swagger:operation GET /repos/{owner}/{repo}/push_mirrors/{name} repository repoGetPushMirrorByRemoteName
|
||||
// ---
|
||||
// summary: Get push mirror of the repository by remoteName
|
||||
// produces:
|
||||
// - application/json
|
||||
// parameters:
|
||||
// - name: owner
|
||||
// in: path
|
||||
// description: owner of the repo
|
||||
// type: string
|
||||
// required: true
|
||||
// - name: repo
|
||||
// in: path
|
||||
// description: name of the repo
|
||||
// type: string
|
||||
// required: true
|
||||
// - name: name
|
||||
// in: path
|
||||
// description: remote name of push mirror
|
||||
// type: string
|
||||
// required: true
|
||||
// responses:
|
||||
// "200":
|
||||
// "$ref": "#/responses/PushMirror"
|
||||
// "400":
|
||||
// "$ref": "#/responses/error"
|
||||
// "403":
|
||||
// "$ref": "#/responses/forbidden"
|
||||
|
||||
if !setting.Mirror.Enabled {
|
||||
ctx.Error(http.StatusBadRequest, "GetPushMirrorByRemoteName", "Mirror feature is disabled")
|
||||
return
|
||||
}
|
||||
|
||||
mirrorName := ctx.Params(":name")
|
||||
// Get push mirror of a specific repo by remoteName
|
||||
pushMirror, err := repo_model.GetPushMirror(ctx, repo_model.PushMirrorOptions{RepoID: ctx.Repo.Repository.ID, RemoteName: mirrorName})
|
||||
if err != nil {
|
||||
ctx.Error(http.StatusNotFound, "GetPushMirrors", err)
|
||||
return
|
||||
}
|
||||
m, err := convert.ToPushMirror(pushMirror)
|
||||
if err != nil {
|
||||
ctx.ServerError("GetPushMirrorByRemoteName", err)
|
||||
return
|
||||
}
|
||||
ctx.JSON(http.StatusOK, m)
|
||||
}
|
||||
|
||||
// AddPushMirror adds a push mirror to a repository
|
||||
func AddPushMirror(ctx *context.APIContext) {
|
||||
// swagger:operation POST /repos/{owner}/{repo}/push_mirrors repository repoAddPushMirror
|
||||
// ---
|
||||
// summary: add a push mirror to the repository
|
||||
// consumes:
|
||||
// - application/json
|
||||
// produces:
|
||||
// - application/json
|
||||
// parameters:
|
||||
// - name: owner
|
||||
// in: path
|
||||
// description: owner of the repo
|
||||
// type: string
|
||||
// required: true
|
||||
// - name: repo
|
||||
// in: path
|
||||
// description: name of the repo
|
||||
// type: string
|
||||
// required: true
|
||||
// - name: body
|
||||
// in: body
|
||||
// schema:
|
||||
// "$ref": "#/definitions/CreatePushMirrorOption"
|
||||
// responses:
|
||||
// "201":
|
||||
// "$ref": "#/responses/PushMirror"
|
||||
// "403":
|
||||
// "$ref": "#/responses/forbidden"
|
||||
// "400":
|
||||
// "$ref": "#/responses/error"
|
||||
|
||||
if !setting.Mirror.Enabled {
|
||||
ctx.Error(http.StatusBadRequest, "AddPushMirror", "Mirror feature is disabled")
|
||||
return
|
||||
}
|
||||
|
||||
pushMirror := web.GetForm(ctx).(*api.CreatePushMirrorOption)
|
||||
CreatePushMirror(ctx, pushMirror)
|
||||
}
|
||||
|
||||
// DeletePushMirrorByRemoteName deletes a push mirror from a repository by remoteName
|
||||
func DeletePushMirrorByRemoteName(ctx *context.APIContext) {
|
||||
// swagger:operation DELETE /repos/{owner}/{repo}/push_mirrors/{name} repository repoDeletePushMirror
|
||||
// ---
|
||||
// summary: deletes a push mirror from a repository by remoteName
|
||||
// produces:
|
||||
// - application/json
|
||||
// parameters:
|
||||
// - name: owner
|
||||
// in: path
|
||||
// description: owner of the repo
|
||||
// type: string
|
||||
// required: true
|
||||
// - name: repo
|
||||
// in: path
|
||||
// description: name of the repo
|
||||
// type: string
|
||||
// required: true
|
||||
// - name: name
|
||||
// in: path
|
||||
// description: remote name of the pushMirror
|
||||
// type: string
|
||||
// required: true
|
||||
// responses:
|
||||
// "204":
|
||||
// "$ref": "#/responses/empty"
|
||||
// "404":
|
||||
// "$ref": "#/responses/notFound"
|
||||
// "400":
|
||||
// "$ref": "#/responses/error"
|
||||
|
||||
if !setting.Mirror.Enabled {
|
||||
ctx.Error(http.StatusBadRequest, "DeletePushMirrorByName", "Mirror feature is disabled")
|
||||
return
|
||||
}
|
||||
|
||||
remoteName := ctx.Params(":name")
|
||||
// Delete push mirror on repo by name.
|
||||
err := repo_model.DeletePushMirrors(ctx, repo_model.PushMirrorOptions{RepoID: ctx.Repo.Repository.ID, RemoteName: remoteName})
|
||||
if err != nil {
|
||||
ctx.Error(http.StatusNotFound, "DeletePushMirrors", err)
|
||||
return
|
||||
}
|
||||
ctx.Status(http.StatusNoContent)
|
||||
}
|
||||
|
||||
func CreatePushMirror(ctx *context.APIContext, mirrorOption *api.CreatePushMirrorOption) {
|
||||
repo := ctx.Repo.Repository
|
||||
|
||||
interval, err := time.ParseDuration(mirrorOption.Interval)
|
||||
if err != nil || (interval != 0 && interval < setting.Mirror.MinInterval) {
|
||||
ctx.Error(http.StatusBadRequest, "CreatePushMirror", err)
|
||||
return
|
||||
}
|
||||
|
||||
address, err := forms.ParseRemoteAddr(mirrorOption.RemoteAddress, mirrorOption.RemoteUsername, mirrorOption.RemotePassword)
|
||||
if err == nil {
|
||||
err = migrations.IsMigrateURLAllowed(address, ctx.ContextUser)
|
||||
}
|
||||
if err != nil {
|
||||
HandleRemoteAddressError(ctx, err)
|
||||
return
|
||||
}
|
||||
|
||||
remoteSuffix, err := util.CryptoRandomString(10)
|
||||
if err != nil {
|
||||
ctx.ServerError("CryptoRandomString", err)
|
||||
return
|
||||
}
|
||||
|
||||
pushMirror := &repo_model.PushMirror{
|
||||
RepoID: repo.ID,
|
||||
Repo: repo,
|
||||
RemoteName: fmt.Sprintf("remote_mirror_%s", remoteSuffix),
|
||||
Interval: interval,
|
||||
}
|
||||
|
||||
if err = repo_model.InsertPushMirror(ctx, pushMirror); err != nil {
|
||||
ctx.ServerError("InsertPushMirror", err)
|
||||
return
|
||||
}
|
||||
|
||||
// if the registration of the push mirrorOption fails remove it from the database
|
||||
if err = mirror_service.AddPushMirrorRemote(ctx, pushMirror, address); err != nil {
|
||||
if err := repo_model.DeletePushMirrors(ctx, repo_model.PushMirrorOptions{ID: pushMirror.ID, RepoID: pushMirror.RepoID}); err != nil {
|
||||
ctx.ServerError("DeletePushMirrors", err)
|
||||
}
|
||||
ctx.ServerError("AddPushMirrorRemote", err)
|
||||
return
|
||||
}
|
||||
m, err := convert.ToPushMirror(pushMirror)
|
||||
if err != nil {
|
||||
ctx.ServerError("ToPushMirror", err)
|
||||
return
|
||||
}
|
||||
ctx.JSON(http.StatusOK, m)
|
||||
}
|
||||
|
||||
func HandleRemoteAddressError(ctx *context.APIContext, err error) {
|
||||
if models.IsErrInvalidCloneAddr(err) {
|
||||
addrErr := err.(*models.ErrInvalidCloneAddr)
|
||||
switch {
|
||||
case addrErr.IsProtocolInvalid:
|
||||
ctx.Error(http.StatusBadRequest, "CreatePushMirror", "Invalid mirror protocol")
|
||||
case addrErr.IsURLError:
|
||||
ctx.Error(http.StatusBadRequest, "CreatePushMirror", "Invalid Url ")
|
||||
case addrErr.IsPermissionDenied:
|
||||
ctx.Error(http.StatusUnauthorized, "CreatePushMirror", "Permission denied")
|
||||
default:
|
||||
ctx.Error(http.StatusBadRequest, "CreatePushMirror", "Unknown error")
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
|
|
|
@ -169,4 +169,7 @@ type swaggerParameterBodies struct {
|
|||
|
||||
// in:body
|
||||
CreateWikiPageOptions api.CreateWikiPageOptions
|
||||
|
||||
// in:body
|
||||
CreatePushMirrorOption api.CreatePushMirrorOption
|
||||
}
|
||||
|
|
|
@ -345,6 +345,20 @@ type swaggerWikiCommitList struct {
|
|||
Body api.WikiCommitList `json:"body"`
|
||||
}
|
||||
|
||||
// PushMirror
|
||||
// swagger:response PushMirror
|
||||
type swaggerPushMirror struct {
|
||||
// in:body
|
||||
Body api.PushMirror `json:"body"`
|
||||
}
|
||||
|
||||
// PushMirrorList
|
||||
// swagger:response PushMirrorList
|
||||
type swaggerPushMirrorList struct {
|
||||
// in:body
|
||||
Body []api.PushMirror `json:"body"`
|
||||
}
|
||||
|
||||
// RepoCollaboratorPermission
|
||||
// swagger:response RepoCollaboratorPermission
|
||||
type swaggerRepoCollaboratorPermission struct {
|
||||
|
|
|
@ -90,7 +90,7 @@ func SettingsCtxData(ctx *context.Context) {
|
|||
}
|
||||
ctx.Data["StatsIndexerStatus"] = status
|
||||
}
|
||||
pushMirrors, err := repo_model.GetPushMirrorsByRepoID(ctx.Repo.Repository.ID)
|
||||
pushMirrors, _, err := repo_model.GetPushMirrorsByRepoID(ctx, ctx.Repo.Repository.ID, db.ListOptions{})
|
||||
if err != nil {
|
||||
ctx.ServerError("GetPushMirrorsByRepoID", err)
|
||||
return
|
||||
|
@ -284,7 +284,7 @@ func SettingsPost(ctx *context.Context) {
|
|||
return
|
||||
}
|
||||
|
||||
m, err := selectPushMirrorByForm(form, repo)
|
||||
m, err := selectPushMirrorByForm(ctx, form, repo)
|
||||
if err != nil {
|
||||
ctx.NotFound("", nil)
|
||||
return
|
||||
|
@ -305,7 +305,7 @@ func SettingsPost(ctx *context.Context) {
|
|||
// as an error on the UI for this action
|
||||
ctx.Data["Err_RepoName"] = nil
|
||||
|
||||
m, err := selectPushMirrorByForm(form, repo)
|
||||
m, err := selectPushMirrorByForm(ctx, form, repo)
|
||||
if err != nil {
|
||||
ctx.NotFound("", nil)
|
||||
return
|
||||
|
@ -316,7 +316,7 @@ func SettingsPost(ctx *context.Context) {
|
|||
return
|
||||
}
|
||||
|
||||
if err = repo_model.DeletePushMirrorByID(m.ID); err != nil {
|
||||
if err = repo_model.DeletePushMirrors(ctx, repo_model.PushMirrorOptions{ID: m.ID, RepoID: m.RepoID}); err != nil {
|
||||
ctx.ServerError("DeletePushMirrorByID", err)
|
||||
return
|
||||
}
|
||||
|
@ -364,14 +364,14 @@ func SettingsPost(ctx *context.Context) {
|
|||
SyncOnCommit: form.PushMirrorSyncOnCommit,
|
||||
Interval: interval,
|
||||
}
|
||||
if err := repo_model.InsertPushMirror(m); err != nil {
|
||||
if err := repo_model.InsertPushMirror(ctx, m); err != nil {
|
||||
ctx.ServerError("InsertPushMirror", err)
|
||||
return
|
||||
}
|
||||
|
||||
if err := mirror_service.AddPushMirrorRemote(ctx, m, address); err != nil {
|
||||
if err := repo_model.DeletePushMirrorByID(m.ID); err != nil {
|
||||
log.Error("DeletePushMirrorByID %v", err)
|
||||
if err := repo_model.DeletePushMirrors(ctx, repo_model.PushMirrorOptions{ID: m.ID, RepoID: m.RepoID}); err != nil {
|
||||
log.Error("DeletePushMirrors %v", err)
|
||||
}
|
||||
ctx.ServerError("AddPushMirrorRemote", err)
|
||||
return
|
||||
|
@ -1222,13 +1222,13 @@ func SettingsDeleteAvatar(ctx *context.Context) {
|
|||
ctx.Redirect(ctx.Repo.RepoLink + "/settings")
|
||||
}
|
||||
|
||||
func selectPushMirrorByForm(form *forms.RepoSettingForm, repo *repo_model.Repository) (*repo_model.PushMirror, error) {
|
||||
func selectPushMirrorByForm(ctx *context.Context, form *forms.RepoSettingForm, repo *repo_model.Repository) (*repo_model.PushMirror, error) {
|
||||
id, err := strconv.ParseInt(form.PushMirrorID, 10, 64)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
pushMirrors, err := repo_model.GetPushMirrorsByRepoID(repo.ID)
|
||||
pushMirrors, _, err := repo_model.GetPushMirrorsByRepoID(ctx, repo.ID, db.ListOptions{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue