experimental/repository.go (308 lines of code) (raw):

package experimental import ( "fmt" "io" "path/filepath" "strings" "sync" multierror "github.com/hashicorp/go-multierror" "github.com/Sirupsen/logrus" "github.com/dustin/go-humanize" ) type repositoryData struct { name string layers map[digest]int manifests map[digest]int manifestSignatures map[digest][]digest tags map[string]*tagData uploads []string lock sync.Mutex } func (r *repositoryData) layerLinkPath(layer digest) string { return filepath.Join("repositories", r.name, "_layers", layer.path(), "link") } func (r *repositoryData) manifestRevisionPath(revision digest) string { return filepath.Join("repositories", r.name, "_manifests", "revisions", revision.path(), "link") } func (r *repositoryData) manifestRevisionSignaturePath(revision, signature digest) string { return filepath.Join("repositories", r.name, "_manifests", "revisions", revision.path(), "signatures", signature.path(), "link") } func (r *repositoryData) uploadPath(upload string) string { return filepath.Join("repositories", r.name, "_uploads", upload, "link") } func (r *repositoryData) tag(name string) *tagData { r.lock.Lock() defer r.lock.Unlock() t := r.tags[name] if t == nil { t = &tagData{ repository: r, name: name, } r.tags[name] = t } return t } func (r *repositoryData) markManifest(revision digest) error { r.lock.Lock() defer r.lock.Unlock() r.manifests[revision]++ return nil } func (r *repositoryData) markManifestLayers(blobs blobsData, revision digest) error { err := blobs.mark(revision) if err != nil { return err } manifest, err := manifests.get(revision, blobs) if err != nil { return err } r.lock.Lock() defer r.lock.Unlock() var resultErr error for _, layer := range manifest.layers { _, ok := r.layers[layer] if !ok { resultErr = multierror.Append(resultErr, fmt.Errorf("layer %s not found reference from manifest %s", layer, revision)) } r.layers[layer]++ } return resultErr } func (r *repositoryData) markManifestSignatures(blobs blobsData, revision digest, signatures []digest) error { if r.manifests[revision] == 0 { return nil } for _, signature := range signatures { blobs.mark(signature) } return nil } func (r *repositoryData) sweepManifestSignatures(revision digest, signatures []digest) error { if r.manifests[revision] > 0 { return nil } for _, signature := range signatures { err := deleteFile(r.manifestRevisionSignaturePath(revision, signature), digestReferenceSize) if err != nil { return err } } return nil } func (r *repositoryData) markLayer(blobs blobsData, revision digest) error { return blobs.mark(revision) } func (r *repositoryData) mark(blobs blobsData) error { for name, t := range r.tags { err := t.mark(blobs) if err != nil { if *softErrors { logrus.Errorln("MARK:", r.name, "TAG:", name, "ERROR:", err) continue } return err } } for revision, used := range r.manifests { if used == 0 { continue } err := r.markManifestLayers(blobs, revision) if err != nil { if *softErrors { logrus.Errorln("MARK:", r.name, "MANIFEST:", revision, "ERROR:", err) continue } return err } } for revision, signatures := range r.manifestSignatures { err := r.markManifestSignatures(blobs, revision, signatures) if err != nil { if *softErrors { logrus.Errorln("MARK:", r.name, "MANIFEST SIGNATURE:", revision, "ERROR:", err) continue } return err } } for digest, used := range r.layers { if used == 0 { continue } err := r.markLayer(blobs, digest) if err != nil { if *softErrors { logrus.Errorln("MARK:", r.name, "LAYER:", digest, "ERROR:", err) continue } return err } } return nil } func (r *repositoryData) sweep() error { for name, t := range r.tags { err := t.sweep() if err != nil { if *softErrors { logrus.Errorln("SWEEP:", r.name, "TAG:", name, "ERROR:", err) continue } return err } } for revision, used := range r.manifests { if used > 0 { continue } err := deleteFile(r.manifestRevisionPath(revision), digestReferenceSize) if err != nil { if *softErrors { logrus.Errorln("SWEEP:", r.name, "MANIFEST:", revision, "ERROR:", err) continue } return err } } for revision, signatures := range r.manifestSignatures { err := r.sweepManifestSignatures(revision, signatures) if err != nil { if *softErrors { logrus.Errorln("MARK:", r.name, "MANIFEST SIGNATURES:", revision, "ERROR:", err) continue } return err } } for digest, used := range r.layers { if used > 0 { continue } err := deleteFile(r.layerLinkPath(digest), digestReferenceSize) if err != nil { if *softErrors { logrus.Errorln("MARK:", r.name, "LAYER:", digest, "ERROR:", err) continue } return err } } return nil } func (r *repositoryData) addLayer(args []string, info fileInfo) error { // /test/_layers/sha256/579c7fc9b0d60a19706cd6c1573fec9a28fa758bfe1ece86a1e5c68ad6f4e9d1/link link, err := analyzeLink(args) if err != nil { return err } err = verifyLink(link, r.layerLinkPath(link), info.etag) if err != nil { return err } r.lock.Lock() defer r.lock.Unlock() 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 { err = verifyLink(link, r.manifestRevisionPath(link), info.etag) if err != nil { return err } r.lock.Lock() defer r.lock.Unlock() r.manifests[link] = 0 return nil } link, signature, err := analyzeLinkSignature(args) if err == nil { err = verifyLink(signature, r.manifestRevisionSignaturePath(link, signature), info.etag) if err != nil { return err } r.lock.Lock() defer r.lock.Unlock() r.manifestSignatures[link] = append(r.manifestSignatures[link], signature) return nil } return err } 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/f82d2b61-f130-4be5-b4f6-92cb18c7cf89/startedat // /test/_uploads/f82d2b61-f130-4be5-b4f6-92cb18c7cf89/hashstates/sha256/0 if len(args) < 1 { return fmt.Errorf("invalid args for uploads: %v", args) } r.lock.Lock() defer r.lock.Unlock() r.uploads = append(r.uploads, strings.Join(args, "/")) return nil } func (r *repositoryData) info(blobs blobsData, stream io.WriteCloser) { var layersUsed, layersUnused int var manifestsUsed, manifestsUnused int var tagsVersions int var layersUsedSize, layersUnusedSize int64 for digest, used := range r.layers { if used > 0 { layersUsed++ layersUsedSize += blobs.size(digest) } else { layersUnused++ layersUnusedSize += blobs.size(digest) } } for _, used := range r.manifests { if used > 0 { manifestsUsed++ } else { manifestsUnused++ } } for _, tag := range r.tags { tagsVersions += len(tag.versions) } logrus.Println("REPOSITORY INFO:", r.name, ":", "Tags/Versions:", len(r.tags), "/", tagsVersions, "Manifests/Unused:", manifestsUsed, "/", manifestsUnused, "Layers/Unused:", layersUsed, "/", layersUnused, "Data/Unused:", humanize.Bytes(uint64(layersUsedSize)), "/", humanize.Bytes(uint64(layersUnusedSize))) if stream != nil { fmt.Fprintf(stream, "%s,%d,%d,%d,%d,%d,%d,%s,%s,%d,%d\n", r.name, len(r.tags), tagsVersions, manifestsUsed, manifestsUnused, layersUsed, layersUnused, humanize.Bytes(uint64(layersUsedSize)), humanize.Bytes(uint64(layersUnusedSize)), layersUsedSize/1024/1024, layersUnusedSize/1024/1024) } } func newRepositoryData(name string) *repositoryData { return &repositoryData{ name: name, layers: make(map[digest]int), manifests: make(map[digest]int), manifestSignatures: make(map[digest][]digest), tags: make(map[string]*tagData), } }