func()

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")
		}
	}
}