Skip to content
Snippets Groups Projects
Unverified Commit 94a3f42f authored by Kamil Trzcinski's avatar Kamil Trzcinski
Browse files

Refactor code to split that into separate structures

parent b675eb26
No related branches found
No related tags found
No related merge requests found
docker-distribution-pruner
examples/registry/
config-dev.yml
blobs.go 0 → 100644
package main
import (
"fmt"
"github.com/Sirupsen/logrus"
"path/filepath"
"strings"
)
type blobData struct {
name string
size int64
references int64
}
func (b *blobData) path() string {
return filepath.Join("blobs", "sha256", b.name[0:2], b.name, "data")
}
type blobsData map[string]*blobData
func (b blobsData) mark(name string) error {
blob := b[name]
if blob == nil {
return fmt.Errorf("blob not found: %v", name)
}
blob.references++
return nil
}
func (b blobsData) sweep() {
for _, blob := range b {
if blob.references == 0 {
scheduleDelete(blob.path(), blob.size)
}
}
}
func (b blobsData) addBlob(segments []string, info fileInfo) error {
if len(segments) != 4 {
return fmt.Errorf("unparseable path: %v", segments)
}
if segments[0] != "sha256" {
return fmt.Errorf("path needs to start with sha256: %v", segments)
}
if segments[3] != "data" {
return fmt.Errorf("file needs to be data: %v", segments)
}
name := segments[2]
if len(name) != 64 {
return fmt.Errorf("blobs need to be sha256: %v", segments)
}
if segments[1] != name[0:2] {
return fmt.Errorf("path needs to be prefixed with %v: %v", name[0:2], segments)
}
blob := &blobData{
name: name,
size: info.size,
}
b[name] = blob
return nil
}
func (b blobsData) walk(path string, info fileInfo, err error) error {
err = b.addBlob(strings.Split(path, "/"), info)
logrus.Infoln("BLOB:", path, ":", err)
return err
}
package main
import (
"path/filepath"
"github.com/Sirupsen/logrus"
"syscall"
)
var (
deletedLinks int
deletedBlobs int
deletedBlobSize int64
deletes []string
)
const linkFileSize = int64(len("sha256:") + 64)
func scheduleDelete(path string, size int64) {
logrus.Infoln("DELETE", path, size)
name := filepath.Base(path)
if name == "link" {
deletedLinks++
} else if name == "data" {
deletedBlobs++
}
deletedBlobSize += size
deletes = append(deletes, path)
}
func runDeletes() {
for _, path := range deletes {
err := syscall.Unlink(path)
if err != nil {
logrus.Fatalln(err)
}
}
}
package main
import (
"io/ioutil"
"path/filepath"
"syscall"
"flag"
"os"
"strings"
)
type fsStorage struct {
rootDir string
}
var fsRootDir = flag.String("fs-root-dir", "examples/registry", "root directory")
func (f *fsStorage) fullPath(path string) string {
return filepath.Join(*fsRootDir, "docker", "registry", "v2", path)
}
func (f *fsStorage) Walk(rootDir string, fn walkFunc) error {
rootDir, err := filepath.Abs(f.fullPath(rootDir))
if err != nil {
return nil
}
rootDir += "/"
return filepath.Walk(rootDir, func(path string, info os.FileInfo, err error) error {
if info.IsDir() {
return nil
}
if strings.HasPrefix(path, rootDir) {
path = path[len(rootDir):]
}
fi := fileInfo{path: path, size: info.Size()}
return fn(path, fi, err)
})
}
func (f *fsStorage) Read(path string) ([]byte, error) {
return ioutil.ReadFile(f.fullPath(path))
}
func (f *fsStorage) Delete(path string) error {
return syscall.Unlink(f.fullPath(path))
}
links.go 0 → 100644
package main
import (
"errors"
"fmt"
"strings"
)
func analyzeLink(args []string) (string, error) {
if len(args) != 3 {
return "", fmt.Errorf("invalid args for link: %v", args)
}
if args[0] != "sha256" {
return "", fmt.Errorf("only sha256 is supported: %v", args[0])
}
if args[2] != "link" {
return "", fmt.Errorf("expected link as path component: %v", args[2])
}
return args[1], nil
}
func readLink(path string) (string, error) {
data, err := currentStorage.Read(path)
if err != nil {
return "", nil
}
link := string(data)
if !strings.HasPrefix(link, "sha256:") {
return "", errors.New("Link has to start with sha256")
}
link = link[len("sha256:"):]
if len(link) != 64 {
return "", fmt.Errorf("Link has to be exactly 256 bit: %v", link)
}
return link, nil
}
Loading
Loading
@@ -2,509 +2,15 @@ package main
 
import (
"flag"
"os"
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"path/filepath"
"strings"
"syscall"
"github.com/Sirupsen/logrus"
"github.com/docker/distribution"
"github.com/docker/distribution/manifest"
"github.com/docker/distribution/manifest/manifestlist"
"github.com/docker/distribution/manifest/schema1"
"github.com/docker/distribution/manifest/schema2"
)
 
var (
debug = flag.Bool("debug", false, "Print debug messages")
verbose = flag.Bool("verbose", true, "Print verbose messages")
dryRun = flag.Bool("dry-run", true, "Dry run")
rootDir = flag.String("root-dir", "examples/registry/docker/registry", "root directory")
debug = flag.Bool("debug", false, "Print debug messages")
verbose = flag.Bool("verbose", true, "Print verbose messages")
dryRun = flag.Bool("dry-run", true, "Dry run")
)
 
var (
deletedLinks int
deletedBlobs int
deletedBlobSize int64
)
const linkFileSize = int64(len("sha256:") + 64)
type tag struct {
repository *repository
name string
current string
versions []string
}
func (t *tag) currentLinkPath() string {
return filepath.Join(repositoriesPath, t.repository.name, "_manifests", "tags", t.name, "current", "link")
}
func (t *tag) versionLinkPath(version string) string {
return filepath.Join(repositoriesPath, t.repository.name, "_manifests", "tags", t.name, "index", "sha256", version, "link")
}
type manifestData struct {
name string
layers []string
}
type repository struct {
name string
layers map[string]int
manifests map[string]int
tags map[string]*tag
uploads map[string]int
}
func (r *repository) layerLinkPath(layer string) string {
return filepath.Join(repositoriesPath, r.name, "_layers", "sha256", layer, "link")
}
func (r *repository) manifestRevisionPath(revision string) string {
return filepath.Join(repositoriesPath, r.name, "_manifests", "revisions", "sha256", revision, "link")
}
func (r *repository) uploadPath(upload string) string {
return filepath.Join(repositoriesPath, r.name, "_uploads", upload, "link")
}
type blobData struct {
name string
size int64
references int64
}
func (b *blobData) path() string {
return filepath.Join(blobsPath, "sha256", b.name[0:2], b.name, "data")
}
var blobs map[string]*blobData = make(map[string]*blobData)
var manifests map[string]*manifestData = make(map[string]*manifestData)
var repositories map[string]*repository = make(map[string]*repository)
var deletes []string
var repositoriesPath string
var blobsPath string
func scheduleDelete(path string, size int64) {
logrus.Infoln("DELETE", path, size)
name := filepath.Base(path)
if name == "link" {
deletedLinks++
} else if name == "data" {
deletedBlobs++
}
deletedBlobSize += size
deletes = append(deletes, path)
}
func getRepository(path []string) *repository {
repositoryName := strings.Join(path, "/")
r := repositories[repositoryName]
if r == nil {
r = &repository{
name: repositoryName,
layers: make(map[string]int),
manifests: make(map[string]int),
tags: make(map[string]*tag),
uploads: make(map[string]int),
}
repositories[repositoryName] = r
}
return r
}
func (r *repository) getTag(name string) *tag {
t := r.tags[name]
if t == nil {
t = &tag{
repository: r,
name: name,
}
r.tags[name] = t
}
return t
}
func deserializeManifest(data []byte) (distribution.Manifest, error) {
var versioned manifest.Versioned
if err := json.Unmarshal(data, &versioned); err != nil {
return nil, err
}
switch versioned.SchemaVersion {
case 1:
var sm schema1.SignedManifest
err := json.Unmarshal(data, &sm)
return sm, err
case 2:
// This can be an image manifest or a manifest list
switch versioned.MediaType {
case schema2.MediaTypeManifest:
var m schema2.DeserializedManifest
err := json.Unmarshal(data, &m)
return m, err
case manifestlist.MediaTypeManifestList:
var m manifestlist.DeserializedManifestList
err := json.Unmarshal(data, &m)
return m, err
default:
return nil, distribution.ErrManifestVerification{fmt.Errorf("unrecognized manifest content type %s", versioned.MediaType)}
}
}
return nil, fmt.Errorf("unrecognized manifest schema version %d", versioned.SchemaVersion)
}
func getManifest(name string) (*manifestData, error) {
m := manifests[name]
if m == nil {
blobPath := filepath.Join(blobsPath, "sha256", name[0:2], name, "data")
data, err := ioutil.ReadFile(blobPath)
if err != nil {
return nil, err
}
manifest, err := deserializeManifest(data)
if err != nil {
return nil, err
}
m = &manifestData{
name: name,
}
for _, reference := range manifest.References() {
m.layers = append(m.layers, reference.Digest.Hex())
}
manifests[name] = m
}
return m, nil
}
func analyzeLink(args []string) (string, error) {
if len(args) != 3 {
return "", fmt.Errorf("invalid args for link: %v", args)
}
if args[0] != "sha256" {
return "", fmt.Errorf("only sha256 is supported: %v", args[0])
}
if args[2] != "link" {
return "", fmt.Errorf("expected link as path component: %v", args[2])
}
return args[1], nil
}
func analyzeLayer(repository *repository, args []string) error {
// /test/_layers/sha256/579c7fc9b0d60a19706cd6c1573fec9a28fa758bfe1ece86a1e5c68ad6f4e9d1/link
link, err := analyzeLink(args)
if err != nil {
return err
}
readLink, err := readLink(repository.layerLinkPath(link))
if err != nil {
return err
}
if readLink != link {
return fmt.Errorf("read link for %s is not equal %s", link, readLink)
}
repository.layers[link] = 0
return nil
}
func analyzeManifestRevision(repository *repository, args []string) error {
// /test2/_manifests/revisions/sha256/708519982eae159899e908639f5fa22d23d247ad923f6e6ad6128894c5d497a0/link
link, err := analyzeLink(args)
if err != nil {
return err
}
readLink, err := readLink(repository.manifestRevisionPath(link))
if err != nil {
return err
}
if readLink != link {
return fmt.Errorf("read link for %s is not equal %s", link, readLink)
}
repository.manifests[link] = 0
return nil
}
func readLink(path string) (string, error) {
data, err := ioutil.ReadFile(path)
if err != nil {
return "", nil
}
link := string(data)
if !strings.HasPrefix(link, "sha256:") {
return "", errors.New("Link has to start with sha256")
}
link = link[len("sha256:"):]
if len(link) != 64 {
return "", fmt.Errorf("Link has to be exactly 256 bit: %v", link)
}
return link, nil
}
func analyzeManifestTagCurrent(tag *tag) error {
//INFO[0000] /test2/_manifests/tags/latest/current/link
readLink, err := readLink(tag.currentLinkPath())
if err != nil {
return err
}
tag.current = readLink
return nil
}
func analyzeManifestTagVersion(tag *tag, args []string) error {
//INFO[0000] /test2/_manifests/tags/latest/index/sha256/af8338145978acd626bfb9e863fa446bebfc9f2660bee1af99ed29efc48d73b4/link
link, err := analyzeLink(args)
if err != nil {
return err
}
readLink, err := readLink(tag.versionLinkPath(link))
if err != nil {
return err
}
if readLink != link {
return fmt.Errorf("read link for %s is not equal %s", link, readLink)
}
tag.versions = append(tag.versions, link)
return nil
}
func analyzeManifestTag(repository *repository, args []string) error {
//INFO[0000] /test2/_manifests/tags/latest/current/link
//INFO[0000] /test2/_manifests/tags/latest/index/sha256/af8338145978acd626bfb9e863fa446bebfc9f2660bee1af99ed29efc48d73b4/link
tag := repository.getTag(args[0])
if args[1] == "current" {
return analyzeManifestTagCurrent(tag)
} else if args[1] == "index" {
return analyzeManifestTagVersion(tag, args[2:])
} else {
return fmt.Errorf("undefined manifest tag type: %v", args[1])
}
}
func analyzeManifest(repository *repository, args []string) error {
//INFO[0000] /test2/_manifests/revisions/sha256/708519982eae159899e908639f5fa22d23d247ad923f6e6ad6128894c5d497a0/link
//INFO[0000] /test2/_manifests/revisions/sha256/af8338145978acd626bfb9e863fa446bebfc9f2660bee1af99ed29efc48d73b4/link
//INFO[0000] /test2/_manifests/tags/latest/current/link
//INFO[0000] /test2/_manifests/tags/latest/index/sha256/af8338145978acd626bfb9e863fa446bebfc9f2660bee1af99ed29efc48d73b4/link
//INFO[0000] /test2/_manifests/tags/latest2/current/link
//INFO[0000] /test2/_manifests/tags/latest2/index/sha256/708519982eae159899e908639f5fa22d23d247ad923f6e6ad6128894c5d497a0/link
if args[0] == "revisions" {
return analyzeManifestRevision(repository, args[1:])
} else if args[0] == "tags" {
return analyzeManifestTag(repository, args[1:])
} else {
return fmt.Errorf("undefined manifest type: %v", args[0])
}
}
func analyzeUploads(repository *repository, args []string) error {
// /test/_uploads/579c7fc9b0d60a19706cd6c1573fec9a28fa758bfe1ece86a1e5c68ad6f4e9d1
if len(args) != 1 {
return fmt.Errorf("invalid args for uploads: %v", args)
}
repository.uploads[args[0]] = 1
return nil
}
func analyzeRepositoryPath(segments []string) error {
for idx := 0; idx < len(segments)-1; idx++ {
repository := segments[0:idx]
args := segments[idx+1:]
switch segments[idx] {
case "_layers":
return analyzeLayer(getRepository(repository), args)
case "_manifests":
return analyzeManifest(getRepository(repository), args)
case "_uploads":
return analyzeUploads(getRepository(repository), args)
}
}
return fmt.Errorf("unparseable path: %v", segments)
}
func walkRepository(path string, info os.FileInfo, err error) error {
if info.IsDir() {
return nil
}
if strings.HasPrefix(path, repositoriesPath) {
path = path[len(repositoriesPath):]
}
err = analyzeRepositoryPath(strings.Split(path, "/"))
logrus.Infoln("REPOSITORY:", path, ":", err)
return err
}
func markBlob(name string) error {
b := blobs[name]
if b == nil {
return fmt.Errorf("blob not found: %v", name)
}
b.references++
return nil
}
func (t *tag) mark() error {
if t.current != "" {
t.repository.manifests[t.current]++
}
for _, version := range t.versions {
if version != t.current {
scheduleDelete(t.versionLinkPath(version), linkFileSize)
}
}
return nil
}
func (r *repository) markManifest(name string) error {
err := markBlob(name)
if err != nil {
return err
}
manifest, err := getManifest(name)
if err != nil {
return err
}
for _, layer := range manifest.layers {
_, ok := r.layers[layer]
if !ok {
return fmt.Errorf("layer %s not found reference from manifest %s", layer, name)
}
r.layers[layer]++
}
return nil
}
func (r *repository) markLayer(name string) error {
return markBlob(name)
}
func (r *repository) mark() error {
for _, t := range r.tags {
err := t.mark()
if err != nil {
return err
}
}
for name, used := range r.manifests {
if used > 0 {
err := r.markManifest(name)
if err != nil {
return err
}
} else {
scheduleDelete(r.manifestRevisionPath(name), linkFileSize)
}
}
for name, used := range r.layers {
if used > 0 {
err := r.markLayer(name)
if err != nil {
return err
}
} else {
scheduleDelete(r.layerLinkPath(name), linkFileSize)
}
}
return nil
}
func analyzeBlobPath(segments []string, size int64) error {
if len(segments) != 4 {
return fmt.Errorf("unparseable path: %v", segments)
}
if segments[0] != "sha256" {
return fmt.Errorf("path needs to start with sha256: %v", segments)
}
if segments[3] != "data" {
return fmt.Errorf("file needs to be data: %v", segments)
}
blob := segments[2]
if len(blob) != 64 {
return fmt.Errorf("blobs need to be sha256: %v", segments)
}
if segments[1] != blob[0:2] {
return fmt.Errorf("path needs to be prefixed with %v: %v", blob[0:2], segments)
}
b := &blobData{
name: blob,
size: size,
}
blobs[blob] = b
return nil
}
func walkBlob(path string, info os.FileInfo, err error) error {
if info.IsDir() {
return nil
}
if strings.HasPrefix(path, blobsPath) {
path = path[len(blobsPath):]
}
err = analyzeBlobPath(strings.Split(path, "/"), info.Size())
logrus.Infoln("BLOB:", path, ":", err)
return err
}
func main() {
flag.Parse()
 
Loading
Loading
@@ -518,59 +24,39 @@ func main() {
 
var err error
 
repositoriesPath = filepath.Join(*rootDir, "v2", "repositories")
repositoriesPath, err = filepath.Abs(repositoriesPath)
if err != nil {
logrus.Fatalln(err)
}
repositoriesPath += "/"
currentStorage = &fsStorage{}
 
blobsPath = filepath.Join(*rootDir, "v2", "blobs")
blobsPath, err = filepath.Abs(blobsPath)
if err != nil {
logrus.Fatalln(err)
}
blobsPath += "/"
blobs := make(blobsData)
repositories := make(repositoriesData)
 
logrus.Infoln("Walking BLOBS...")
err = filepath.Walk(blobsPath, walkBlob)
err = currentStorage.Walk("blobs", blobs.walk)
if err != nil {
logrus.Fatalln(err)
}
 
logrus.Infoln("Walking REPOSITORIES...")
err = filepath.Walk(repositoriesPath, walkRepository)
err = currentStorage.Walk("repositories", repositories.walk)
if err != nil {
logrus.Fatalln(err)
}
 
logrus.Infoln("Marking REPOSITORIES...")
for _, r := range repositories {
err := r.mark()
if err != nil {
logrus.Fatalln(err)
}
}
logrus.Infoln("Marking BLOBS...")
for _, b := range blobs {
if b.references == 0 {
scheduleDelete(b.path(), b.size)
}
err = repositories.mark(blobs)
if err != nil {
logrus.Fatalln(err)
}
 
if !*dryRun {
logrus.Infoln("Sweeping...")
for _, path := range deletes {
err := syscall.Unlink(path)
if err != nil {
logrus.Fatalln(err)
}
}
}
logrus.Infoln("Sweeping BLOBS...")
blobs.sweep()
 
logrus.Warningln("Deleted:", deletedLinks, "links,",
deletedBlobs, "blobs,",
deletedBlobSize/1024/1024, "in MB",
)
if !*dryRun {
logrus.Infoln("Sweeping...")
runDeletes()
}
}
package main
import (
"encoding/json"
"fmt"
"path/filepath"
"github.com/docker/distribution"
"github.com/docker/distribution/manifest"
"github.com/docker/distribution/manifest/manifestlist"
"github.com/docker/distribution/manifest/schema1"
"github.com/docker/distribution/manifest/schema2"
)
type manifestData struct {
name string
layers []string
}
type manifestsData map[string]*manifestData
var manifests manifestsData = make(map[string]*manifestData)
func deserializeManifest(data []byte) (distribution.Manifest, error) {
var versioned manifest.Versioned
if err := json.Unmarshal(data, &versioned); err != nil {
return nil, err
}
switch versioned.SchemaVersion {
case 1:
var sm schema1.SignedManifest
err := json.Unmarshal(data, &sm)
return sm, err
case 2:
// This can be an image manifest or a manifest list
switch versioned.MediaType {
case schema2.MediaTypeManifest:
var m schema2.DeserializedManifest
err := json.Unmarshal(data, &m)
return m, err
case manifestlist.MediaTypeManifestList:
var m manifestlist.DeserializedManifestList
err := json.Unmarshal(data, &m)
return m, err
default:
return nil, distribution.ErrManifestVerification{fmt.Errorf("unrecognized manifest content type %s", versioned.MediaType)}
}
}
return nil, fmt.Errorf("unrecognized manifest schema version %d", versioned.SchemaVersion)
}
func (m *manifestData) path() string {
return filepath.Join("blobs", "sha256", m.name[0:2], m.name, "data")
}
func (m *manifestData) load() error {
data, err := currentStorage.Read(m.path())
if err != nil {
return err
}
manifest, err := deserializeManifest(data)
if err != nil {
return err
}
for _, reference := range manifest.References() {
m.layers = append(m.layers, reference.Digest.Hex())
}
return nil
}
func (m manifestsData) get(name string) (*manifestData, error) {
manifest := m[name]
if manifest == nil {
manifest = &manifestData{
name: name,
}
err := manifest.load()
if err != nil {
return nil, err
}
m[name] = manifest
}
return manifest, nil
}
package main
import (
"fmt"
"github.com/Sirupsen/logrus"
"path/filepath"
"strings"
)
type repositoryData struct {
name string
layers map[string]int
manifests map[string]int
tags map[string]*tag
uploads map[string]int
}
type repositoriesData map[string]*repositoryData
func newRepositoryData(name string) *repositoryData {
return &repositoryData{
name: name,
layers: make(map[string]int),
manifests: make(map[string]int),
tags: make(map[string]*tag),
uploads: make(map[string]int),
}
}
func (r *repositoryData) layerLinkPath(layer string) string {
return filepath.Join("repositories", r.name, "_layers", "sha256", layer, "link")
}
func (r *repositoryData) manifestRevisionPath(revision string) string {
return filepath.Join("repositories", r.name, "_manifests", "revisions", "sha256", revision, "link")
}
func (r *repositoryData) uploadPath(upload string) string {
return filepath.Join("repositories", r.name, "_uploads", upload, "link")
}
func (r *repositoryData) tag(name string) *tag {
t := r.tags[name]
if t == nil {
t = &tag{
repository: r,
name: name,
}
r.tags[name] = t
}
return t
}
func (r *repositoryData) markManifest(blobs blobsData, name string) error {
err := blobs.mark(name)
if err != nil {
return err
}
manifest, err := manifests.get(name)
if err != nil {
return err
}
for _, layer := range manifest.layers {
_, ok := r.layers[layer]
if !ok {
return fmt.Errorf("layer %s not found reference from manifest %s", layer, name)
}
r.layers[layer]++
}
return nil
}
func (r *repositoryData) markLayer(blobs blobsData, name string) error {
return blobs.mark(name)
}
func (r *repositoryData) mark(blobs blobsData) error {
for _, t := range r.tags {
err := t.mark(blobs)
if err != nil {
return err
}
}
for name, used := range r.manifests {
if used > 0 {
err := r.markManifest(blobs, name)
if err != nil {
return err
}
} else {
scheduleDelete(r.manifestRevisionPath(name), linkFileSize)
}
}
for name, used := range r.layers {
if used > 0 {
err := r.markLayer(blobs, name)
if err != nil {
return err
}
} else {
scheduleDelete(r.layerLinkPath(name), linkFileSize)
}
}
return nil
}
func (r repositoriesData) get(path []string) *repositoryData {
repositoryName := strings.Join(path, "/")
repository := r[repositoryName]
if repository == nil {
repository = newRepositoryData(repositoryName)
r[repositoryName] = repository
}
return repository
}
func (r *repositoryData) addLayer(args []string, info fileInfo) error {
// /test/_layers/sha256/579c7fc9b0d60a19706cd6c1573fec9a28fa758bfe1ece86a1e5c68ad6f4e9d1/link
link, err := analyzeLink(args)
if err != nil {
return err
}
readLink, err := readLink(r.layerLinkPath(link))
if err != nil {
return err
}
if readLink != link {
return fmt.Errorf("read link for %s is not equal %s", link, readLink)
}
r.layers[link] = 0
return nil
}
func (r *repositoryData) addManifestRevision(args []string, info fileInfo) error {
// /test2/_manifests/revisions/sha256/708519982eae159899e908639f5fa22d23d247ad923f6e6ad6128894c5d497a0/link
link, err := analyzeLink(args)
if err != nil {
return err
}
readLink, err := readLink(r.manifestRevisionPath(link))
if err != nil {
return err
}
if readLink != link {
return fmt.Errorf("read link for %s is not equal %s", link, readLink)
}
r.manifests[link] = 0
return nil
}
func (r *repositoryData) addTag(args []string, info fileInfo) error {
//INFO[0000] /test2/_manifests/tags/latest/current/link
//INFO[0000] /test2/_manifests/tags/latest/index/sha256/af8338145978acd626bfb9e863fa446bebfc9f2660bee1af99ed29efc48d73b4/link
tag := r.tag(args[0])
if args[1] == "current" {
return tag.setCurrent(info)
} else if args[1] == "index" {
return tag.addVersion(args[2:], info)
} else {
return fmt.Errorf("undefined manifest tag type: %v", args[1])
}
}
func (r *repositoryData) addManifest(args []string, info fileInfo) error {
//INFO[0000] /test2/_manifests/revisions/sha256/708519982eae159899e908639f5fa22d23d247ad923f6e6ad6128894c5d497a0/link
//INFO[0000] /test2/_manifests/revisions/sha256/af8338145978acd626bfb9e863fa446bebfc9f2660bee1af99ed29efc48d73b4/link
//INFO[0000] /test2/_manifests/tags/latest/current/link
//INFO[0000] /test2/_manifests/tags/latest/index/sha256/af8338145978acd626bfb9e863fa446bebfc9f2660bee1af99ed29efc48d73b4/link
//INFO[0000] /test2/_manifests/tags/latest2/current/link
//INFO[0000] /test2/_manifests/tags/latest2/index/sha256/708519982eae159899e908639f5fa22d23d247ad923f6e6ad6128894c5d497a0/link
if args[0] == "revisions" {
return r.addManifestRevision(args[1:], info)
} else if args[0] == "tags" {
return r.addTag(args[1:], info)
} else {
return fmt.Errorf("undefined manifest type: %v", args[0])
}
}
func (r *repositoryData) addUpload(args []string, info fileInfo) error {
// /test/_uploads/579c7fc9b0d60a19706cd6c1573fec9a28fa758bfe1ece86a1e5c68ad6f4e9d1
if len(args) != 1 {
return fmt.Errorf("invalid args for uploads: %v", args)
}
r.uploads[args[0]] = 1
return nil
}
func (r repositoriesData) process(segments []string, info fileInfo) error {
for idx := 0; idx < len(segments)-1; idx++ {
repository := segments[0:idx]
args := segments[idx+1:]
switch segments[idx] {
case "_layers":
return r.get(repository).addLayer(args, info)
case "_manifests":
return r.get(repository).addManifest(args, info)
case "_uploads":
return r.get(repository).addUpload(args, info)
}
}
return fmt.Errorf("unparseable path: %v", segments)
}
func (r repositoriesData) walk(path string, info fileInfo, err error) error {
err = r.process(strings.Split(path, "/"), info)
logrus.Infoln("REPOSITORY:", path, ":", err)
return err
}
func (r repositoriesData) mark(blobs blobsData) error {
for _, repository := range r {
err := repository.mark(blobs)
if err != nil {
return err
}
}
return nil
}
package main
type fileInfo struct {
path string
size int64
etag string
}
type walkFunc func(path string, info fileInfo, err error) error
type storage interface {
Walk(path string, fn walkFunc) error
Read(path string) ([]byte, error)
Delete(path string) error
}
var currentStorage storage
tags.go 0 → 100644
package main
import (
"fmt"
"path/filepath"
)
type tag struct {
repository *repositoryData
name string
current string
versions []string
}
func (t *tag) currentLinkPath() string {
return filepath.Join("repositories", t.repository.name, "_manifests", "tags", t.name, "current", "link")
}
func (t *tag) versionLinkPath(version string) string {
return filepath.Join("repositories", t.repository.name, "_manifests", "tags", t.name, "index", "sha256", version, "link")
}
func (t *tag) mark(blobs blobsData) error {
if t.current != "" {
t.repository.manifests[t.current]++
}
for _, version := range t.versions {
if version != t.current {
scheduleDelete(t.versionLinkPath(version), linkFileSize)
}
}
return nil
}
func (t *tag) setCurrent(info fileInfo) error {
//INFO[0000] /test2/_manifests/tags/latest/current/link
readLink, err := readLink(t.currentLinkPath())
if err != nil {
return err
}
t.current = readLink
return nil
}
func (t *tag) addVersion(args []string, info fileInfo) error {
//INFO[0000] /test2/_manifests/tags/latest/index/sha256/af8338145978acd626bfb9e863fa446bebfc9f2660bee1af99ed29efc48d73b4/link
link, err := analyzeLink(args)
if err != nil {
return err
}
readLink, err := readLink(t.versionLinkPath(link))
if err != nil {
return err
}
if readLink != link {
return fmt.Errorf("read link for %s is not equal %s", link, readLink)
}
t.versions = append(t.versions, link)
return nil
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment