in registry/handlers/repositories.go [708:812]
func (h *repositoryHandler) RenameRepository(w http.ResponseWriter, r *http.Request) {
l := log.GetLogger(log.WithContext(h)).WithFields(log.Fields{"path": h.Repository.Named().Name()})
// this endpoint is only available on a registry that utilizes the redis cache,
// we make sure we fail with a 404 and detailing a missing dependecy error if no redis cache is found
if h.App.redisCache == nil {
detail := v1.MissingServerDependencyTypeErrorDetail("redis")
h.Errors = append(h.Errors, v1.ErrorCodeNotImplemented.WithDetail(detail))
return
}
// verify that the parameters of the rename request are valid
validatedRenameRequest, err := validateRenameRequest(h, r, h.Repository.Named().Name())
if err != nil {
h.Errors = append(h.Errors, errcode.FromUnknownError(err))
return
}
rStore := datastore.NewRepositoryStore(h.db.Primary())
nStore := datastore.NewNamespaceStore(h.db.Primary())
// find the origin repository for the path to be renamed (if it exists), if the origin path does not exist
// we still need to check and rename the sub-repositories of the provided path (if they exist)
repo, renameOriginRepo, err := inferRepository(h.Context, h.Repository.Named().Name(), rStore, nStore)
if err != nil {
h.Errors = append(h.Errors, errcode.FromUnknownError(err))
return
}
// count the number of repositories under the origin path
repoCount, err := rStore.CountPathSubRepositories(h.Context, repo.NamespaceID, repo.Path)
if err != nil {
h.Errors = append(h.Errors, errcode.FromUnknownError(err))
return
}
err = assertRenameRepositoryCount(h, repo, repoCount)
if err != nil {
h.Errors = append(h.Errors, errcode.FromUnknownError(err))
return
}
// extract the new path and/or new name of the origin repository
var (
newPath string
newName = validatedRenameRequest.renameObject.Name
isRenameNamespaceRequest = validatedRenameRequest.isRenameNamespaceRequest
)
if isRenameNamespaceRequest {
// bottomLevelPathSegment is the base repository name
pathSegments := strings.Split(h.Repository.Named().Name(), "/")
bottomLevelPathSegment := pathSegments[len(pathSegments)-1]
newPath = validatedRenameRequest.renameObject.Namespace + "/" + bottomLevelPathSegment
newName = bottomLevelPathSegment
} else {
newPath = replacePathName(repo.Path, newName)
}
// check that no base repository or sub repository exists for the new target path
nameTaken, err := isRepositoryNameTaken(h.Context, rStore, repo.NamespaceID, newName, newPath)
if nameTaken {
l.WithError(err).WithFields(log.Fields{
"rename_path": newPath,
}).Info("repository rename conflicts with existing repository")
}
if err != nil {
h.Errors = append(h.Errors, errcode.FromUnknownError(err))
return
}
// at this point everything checks out with the request and there are no conflicts
// with existing repositories/sub-repositories within the registry. we proceed
// with procuring a lease for the rename operation and/or executing the rename in the datastore.
rsp := renameStoreParams{
source: repo,
newPath: newPath,
newName: newName,
isDryRun: validatedRenameRequest.isDryRun,
isPathOriginRepo: renameOriginRepo,
}
renamed, err := handleRenameStoreOperation(h.Context, w, rsp, h.App.redisCache, h.db.Primary())
if err != nil {
h.Errors = append(h.Errors, errcode.FromUnknownError(err))
return
}
// send notification for completed rename.
if renamed {
renameType := notifications.NameRename
if isRenameNamespaceRequest {
renameType = notifications.NamespaceRename
}
if err := h.queueBridge.RepoRenamed(h.Repository.Named(),
notifications.Rename{
Type: renameType,
From: repo.Path,
To: newPath,
}); err != nil {
l.WithError(err).Error("dispatching repo rename event to queue")
}
}
}