forked from TrueCloudLab/distribution
9c88801a12
Back in the before time, the best practices surrounding usage of Context weren't quite worked out. We defined our own type to make usage easier. As this packaged was used elsewhere, it make it more and more challenging to integrate with the forked `Context` type. Now that it is available in the standard library, we can just use that one directly. To make usage more consistent, we now use `dcontext` when referring to the distribution context package. Signed-off-by: Stephen J Day <stephen.day@docker.com>
139 lines
3.9 KiB
Go
139 lines
3.9 KiB
Go
package storage
|
|
|
|
import (
|
|
"context"
|
|
"path"
|
|
"strings"
|
|
"time"
|
|
|
|
storageDriver "github.com/docker/distribution/registry/storage/driver"
|
|
"github.com/docker/distribution/uuid"
|
|
"github.com/sirupsen/logrus"
|
|
)
|
|
|
|
// uploadData stored the location of temporary files created during a layer upload
|
|
// along with the date the upload was started
|
|
type uploadData struct {
|
|
containingDir string
|
|
startedAt time.Time
|
|
}
|
|
|
|
func newUploadData() uploadData {
|
|
return uploadData{
|
|
containingDir: "",
|
|
// default to far in future to protect against missing startedat
|
|
startedAt: time.Now().Add(time.Duration(10000 * time.Hour)),
|
|
}
|
|
}
|
|
|
|
// PurgeUploads deletes files from the upload directory
|
|
// created before olderThan. The list of files deleted and errors
|
|
// encountered are returned
|
|
func PurgeUploads(ctx context.Context, driver storageDriver.StorageDriver, olderThan time.Time, actuallyDelete bool) ([]string, []error) {
|
|
logrus.Infof("PurgeUploads starting: olderThan=%s, actuallyDelete=%t", olderThan, actuallyDelete)
|
|
uploadData, errors := getOutstandingUploads(ctx, driver)
|
|
var deleted []string
|
|
for _, uploadData := range uploadData {
|
|
if uploadData.startedAt.Before(olderThan) {
|
|
var err error
|
|
logrus.Infof("Upload files in %s have older date (%s) than purge date (%s). Removing upload directory.",
|
|
uploadData.containingDir, uploadData.startedAt, olderThan)
|
|
if actuallyDelete {
|
|
err = driver.Delete(ctx, uploadData.containingDir)
|
|
}
|
|
if err == nil {
|
|
deleted = append(deleted, uploadData.containingDir)
|
|
} else {
|
|
errors = append(errors, err)
|
|
}
|
|
}
|
|
}
|
|
|
|
logrus.Infof("Purge uploads finished. Num deleted=%d, num errors=%d", len(deleted), len(errors))
|
|
return deleted, errors
|
|
}
|
|
|
|
// getOutstandingUploads walks the upload directory, collecting files
|
|
// which could be eligible for deletion. The only reliable way to
|
|
// classify the age of a file is with the date stored in the startedAt
|
|
// file, so gather files by UUID with a date from startedAt.
|
|
func getOutstandingUploads(ctx context.Context, driver storageDriver.StorageDriver) (map[string]uploadData, []error) {
|
|
var errors []error
|
|
uploads := make(map[string]uploadData, 0)
|
|
|
|
inUploadDir := false
|
|
root, err := pathFor(repositoriesRootPathSpec{})
|
|
if err != nil {
|
|
return uploads, append(errors, err)
|
|
}
|
|
|
|
err = Walk(ctx, driver, root, func(fileInfo storageDriver.FileInfo) error {
|
|
filePath := fileInfo.Path()
|
|
_, file := path.Split(filePath)
|
|
if file[0] == '_' {
|
|
// Reserved directory
|
|
inUploadDir = (file == "_uploads")
|
|
|
|
if fileInfo.IsDir() && !inUploadDir {
|
|
return ErrSkipDir
|
|
}
|
|
|
|
}
|
|
|
|
uuid, isContainingDir := uuidFromPath(filePath)
|
|
if uuid == "" {
|
|
// Cannot reliably delete
|
|
return nil
|
|
}
|
|
ud, ok := uploads[uuid]
|
|
if !ok {
|
|
ud = newUploadData()
|
|
}
|
|
if isContainingDir {
|
|
ud.containingDir = filePath
|
|
}
|
|
if file == "startedat" {
|
|
if t, err := readStartedAtFile(driver, filePath); err == nil {
|
|
ud.startedAt = t
|
|
} else {
|
|
errors = pushError(errors, filePath, err)
|
|
}
|
|
|
|
}
|
|
|
|
uploads[uuid] = ud
|
|
return nil
|
|
})
|
|
|
|
if err != nil {
|
|
errors = pushError(errors, root, err)
|
|
}
|
|
return uploads, errors
|
|
}
|
|
|
|
// uuidFromPath extracts the upload UUID from a given path
|
|
// If the UUID is the last path component, this is the containing
|
|
// directory for all upload files
|
|
func uuidFromPath(path string) (string, bool) {
|
|
components := strings.Split(path, "/")
|
|
for i := len(components) - 1; i >= 0; i-- {
|
|
if u, err := uuid.Parse(components[i]); err == nil {
|
|
return u.String(), i == len(components)-1
|
|
}
|
|
}
|
|
return "", false
|
|
}
|
|
|
|
// readStartedAtFile reads the date from an upload's startedAtFile
|
|
func readStartedAtFile(driver storageDriver.StorageDriver, path string) (time.Time, error) {
|
|
// todo:(richardscothern) - pass in a context
|
|
startedAtBytes, err := driver.GetContent(context.Background(), path)
|
|
if err != nil {
|
|
return time.Now(), err
|
|
}
|
|
startedAt, err := time.Parse(time.RFC3339, string(startedAtBytes))
|
|
if err != nil {
|
|
return time.Now(), err
|
|
}
|
|
return startedAt, nil
|
|
}
|