func checkRepo(registry distribution.Namespace, repoName string) error { ctx := context.Background() repo, err := registry.Repository(ctx, repoName) if err != nil { return fmt.Errorf("unexpected error getting repository: %v", err) } manifests, err := repo.Manifests(ctx) if err != nil { return fmt.Errorf("unexpected error getting manifests: %v", err) } tags, err := manifests.Tags() if err != nil { return fmt.Errorf("unexpected error getting tags: %v", err) } fmt.Fprintf(os.Stderr, "checking repo %s (%d tags)\n", repoName) for _, tag := range tags { mnfst, err := manifests.GetByTag(tag) if err != nil { return fmt.Errorf("unexpected error getting manifest by tag: %v", err) } if err = checkManifest(repoName, mnfst); err != nil { return err } } return nil }
func allBlobs(t *testing.T, registry distribution.Namespace) map[digest.Digest]struct{} { ctx := context.Background() blobService := registry.Blobs() allBlobsMap := make(map[digest.Digest]struct{}) err := blobService.Enumerate(ctx, func(dgst digest.Digest) error { allBlobsMap[dgst] = struct{}{} return nil }) if err != nil { t.Fatalf("Error getting all blobs: %v", err) } return allBlobsMap }
func makeRepository(t *testing.T, registry distribution.Namespace, name string) distribution.Repository { ctx := context.Background() // Initialize a dummy repository named, err := reference.ParseNamed(name) if err != nil { t.Fatalf("Failed to parse name %s: %v", name, err) } repo, err := registry.Repository(ctx, named) if err != nil { t.Fatalf("Failed to construct repository: %v", err) } return repo }
func makeRepo(t *testing.T, ctx context.Context, name string, reg distribution.Namespace) { named, err := reference.ParseNamed(name) if err != nil { t.Fatal(err) } repo, _ := reg.Repository(ctx, named) manifests, _ := repo.Manifests(ctx) layers, err := testutil.CreateRandomLayers(1) if err != nil { t.Fatal(err) } err = testutil.UploadBlobs(repo, layers) if err != nil { t.Fatalf("failed to upload layers: %v", err) } getKeys := func(digests map[digest.Digest]io.ReadSeeker) (ds []digest.Digest) { for d := range digests { ds = append(ds, d) } return } manifest, err := testutil.MakeSchema1Manifest(getKeys(layers)) if err != nil { t.Fatal(err) } _, err = manifests.Put(ctx, manifest) if err != nil { t.Fatalf("manifest upload failed: %v", err) } }
// NewRegistryPullThroughCache creates a registry acting as a pull through cache func NewRegistryPullThroughCache(ctx context.Context, registry distribution.Namespace, driver driver.StorageDriver, config configuration.Proxy) (distribution.Namespace, error) { remoteURL, err := url.Parse(config.RemoteURL) if err != nil { return nil, err } v := storage.NewVacuum(ctx, driver) s := scheduler.New(ctx, driver, "/scheduler-state.json") s.OnBlobExpire(func(ref reference.Reference) error { var r reference.Canonical var ok bool if r, ok = ref.(reference.Canonical); !ok { return fmt.Errorf("unexpected reference type : %T", ref) } repo, err := registry.Repository(ctx, r) if err != nil { return err } blobs := repo.Blobs(ctx) // Clear the repository reference and descriptor caches err = blobs.Delete(ctx, r.Digest()) if err != nil { return err } err = v.RemoveBlob(r.Digest().String()) if err != nil { return err } return nil }) s.OnManifestExpire(func(ref reference.Reference) error { var r reference.Canonical var ok bool if r, ok = ref.(reference.Canonical); !ok { return fmt.Errorf("unexpected reference type : %T", ref) } repo, err := registry.Repository(ctx, r) if err != nil { return err } manifests, err := repo.Manifests(ctx) if err != nil { return err } err = manifests.Delete(ctx, r.Digest()) if err != nil { return err } return nil }) err = s.Start() if err != nil { return nil, err } cs, err := configureAuth(config.Username, config.Password, config.RemoteURL) if err != nil { return nil, err } return &proxyingRegistry{ embedded: registry, scheduler: s, remoteURL: *remoteURL, authChallenger: &remoteAuthChallenger{ remoteURL: *remoteURL, cm: auth.NewSimpleChallengeManager(), cs: cs, }, }, nil }
func storeTestImage( ctx context.Context, reg distribution.Namespace, imageReference reference.NamedTagged, schemaVersion int, managedByOpenShift bool, ) (*imageapi.Image, error) { repo, err := reg.Repository(ctx, imageReference) if err != nil { return nil, fmt.Errorf("unexpected error getting repo %q: %v", imageReference.Name(), err) } var ( m distribution.Manifest m1 schema1.Manifest ) switch schemaVersion { case 1: m1 = schema1.Manifest{ Versioned: manifest.Versioned{ SchemaVersion: 1, }, Name: imageReference.Name(), Tag: imageReference.Tag(), } case 2: // TODO fallthrough default: return nil, fmt.Errorf("unsupported manifest version %d", schemaVersion) } for i := 0; i < testImageLayerCount; i++ { rs, ds, err := registrytest.CreateRandomTarFile() if err != nil { return nil, fmt.Errorf("unexpected error generating test layer file: %v", err) } dgst := digest.Digest(ds) wr, err := repo.Blobs(ctx).Create(ctx) if err != nil { return nil, fmt.Errorf("unexpected error creating test upload: %v", err) } defer wr.Close() n, err := io.Copy(wr, rs) if err != nil { return nil, fmt.Errorf("unexpected error copying to upload: %v", err) } if schemaVersion == 1 { m1.FSLayers = append(m1.FSLayers, schema1.FSLayer{BlobSum: dgst}) m1.History = append(m1.History, schema1.History{V1Compatibility: fmt.Sprintf(`{"size":%d}`, n)}) } // TODO v2 if _, err := wr.Commit(ctx, distribution.Descriptor{Digest: dgst, MediaType: schema1.MediaTypeManifestLayer}); err != nil { return nil, fmt.Errorf("unexpected error finishing upload: %v", err) } } var dgst digest.Digest var payload []byte if schemaVersion == 1 { pk, err := libtrust.GenerateECP256PrivateKey() if err != nil { return nil, fmt.Errorf("unexpected error generating private key: %v", err) } m, err = schema1.Sign(&m1, pk) if err != nil { return nil, fmt.Errorf("error signing manifest: %v", err) } _, payload, err = m.Payload() if err != nil { return nil, fmt.Errorf("error getting payload %#v", err) } dgst = digest.FromBytes(payload) } //TODO v2 image := &imageapi.Image{ ObjectMeta: kapi.ObjectMeta{ Name: dgst.String(), }, DockerImageManifest: string(payload), DockerImageReference: imageReference.Name() + "@" + dgst.String(), } if managedByOpenShift { image.Annotations = map[string]string{imageapi.ManagedByOpenShiftAnnotation: "true"} } if schemaVersion == 1 { signedManifest := m.(*schema1.SignedManifest) signatures, err := signedManifest.Signatures() if err != nil { return nil, err } for _, signDigest := range signatures { image.DockerImageSignatures = append(image.DockerImageSignatures, signDigest) } } err = imageapi.ImageWithMetadata(image) if err != nil { return nil, fmt.Errorf("failed to fill image with metadata: %v", err) } return image, nil }
func pushImageToRegistry(ctx context.Context, serviceName, commitSha string, repo, tag string, ib *ImageBundle, privateKey libtrust.PrivateKey, registry distribution.Namespace) (string, error) { repository, err := registry.Repository(ctx, serviceName) blobs := repository.Blobs(ctx) layer, err := ib.GetImage(repo, tag) if err != nil { return "", err } m := &schema1.Manifest{ Versioned: manifest.Versioned{ SchemaVersion: 1, }, Name: serviceName, Tag: commitSha, Architecture: layer.Architecture, FSLayers: []schema1.FSLayer{}, History: []schema1.History{}, } for { jsonData, err := ib.GetLayerJSON(layer.ID) if err != nil { return "", err } dgst, err := ib.GetDigest(layer.ID) if err != nil { return "", err } fmt.Printf("Layer %s digest %s\n", layer.ID, dgst) var exists bool _, err = blobs.Stat(ctx, *dgst) switch err { case nil: exists = true case distribution.ErrBlobUnknown: // nop default: } if !exists { descriptor, err := pushLayer(ctx, layer.ID, ib.imageDir, blobs) if err != nil { return "", err } dgst = &descriptor.Digest } m.FSLayers = append(m.FSLayers, schema1.FSLayer{BlobSum: *dgst}) m.History = append(m.History, schema1.History{V1Compatibility: string(jsonData)}) if layer.Parent == "" { break } layer, err = ib.GetLayer(layer.Parent) if err != nil { return "", err } } // log.Printf("WE PUSHED: %v\n", m) return pushManifest(ctx, m, privateKey, repository) }
// MarkAndSweep performs a mark and sweep of registry data func MarkAndSweep(ctx context.Context, storageDriver driver.StorageDriver, registry distribution.Namespace, dryRun bool) error { repositoryEnumerator, ok := registry.(distribution.RepositoryEnumerator) if !ok { return fmt.Errorf("unable to convert Namespace to RepositoryEnumerator") } // mark markSet := make(map[digest.Digest]struct{}) err := repositoryEnumerator.Enumerate(ctx, func(repoName string) error { emit(repoName) var err error named, err := reference.ParseNamed(repoName) if err != nil { return fmt.Errorf("failed to parse repo name %s: %v", repoName, err) } repository, err := registry.Repository(ctx, named) if err != nil { return fmt.Errorf("failed to construct repository: %v", err) } manifestService, err := repository.Manifests(ctx) if err != nil { return fmt.Errorf("failed to construct manifest service: %v", err) } manifestEnumerator, ok := manifestService.(distribution.ManifestEnumerator) if !ok { return fmt.Errorf("unable to convert ManifestService into ManifestEnumerator") } err = manifestEnumerator.Enumerate(ctx, func(dgst digest.Digest) error { // Mark the manifest's blob emit("%s: marking manifest %s ", repoName, dgst) markSet[dgst] = struct{}{} manifest, err := manifestService.Get(ctx, dgst) if err != nil { return fmt.Errorf("failed to retrieve manifest for digest %v: %v", dgst, err) } descriptors := manifest.References() for _, descriptor := range descriptors { markSet[descriptor.Digest] = struct{}{} emit("%s: marking blob %s", repoName, descriptor.Digest) } return nil }) if err != nil { // In certain situations such as unfinished uploads, deleting all // tags in S3 or removing the _manifests folder manually, this // error may be of type PathNotFound. // // In these cases we can continue marking other manifests safely. if _, ok := err.(driver.PathNotFoundError); ok { return nil } } return err }) if err != nil { return fmt.Errorf("failed to mark: %v\n", err) } // sweep blobService := registry.Blobs() deleteSet := make(map[digest.Digest]struct{}) err = blobService.Enumerate(ctx, func(dgst digest.Digest) error { // check if digest is in markSet. If not, delete it! if _, ok := markSet[dgst]; !ok { deleteSet[dgst] = struct{}{} } return nil }) if err != nil { return fmt.Errorf("error enumerating blobs: %v", err) } emit("\n%d blobs marked, %d blobs eligible for deletion", len(markSet), len(deleteSet)) // Construct vacuum vacuum := NewVacuum(ctx, storageDriver) for dgst := range deleteSet { emit("blob eligible for deletion: %s", dgst) if dryRun { continue } err = vacuum.RemoveBlob(string(dgst)) if err != nil { return fmt.Errorf("failed to delete blob %s: %v\n", dgst, err) } } return err }
func markAndSweep(ctx context.Context, storageDriver driver.StorageDriver, registry distribution.Namespace) error { repositoryEnumerator, ok := registry.(distribution.RepositoryEnumerator) if !ok { return fmt.Errorf("unable to convert Namespace to RepositoryEnumerator") } // mark markSet := make(map[digest.Digest]struct{}) err := repositoryEnumerator.Enumerate(ctx, func(repoName string) error { emit(repoName) var err error named, err := reference.ParseNamed(repoName) if err != nil { return fmt.Errorf("failed to parse repo name %s: %v", repoName, err) } repository, err := registry.Repository(ctx, named) if err != nil { return fmt.Errorf("failed to construct repository: %v", err) } manifestService, err := repository.Manifests(ctx) if err != nil { return fmt.Errorf("failed to construct manifest service: %v", err) } manifestEnumerator, ok := manifestService.(distribution.ManifestEnumerator) if !ok { return fmt.Errorf("unable to convert ManifestService into ManifestEnumerator") } err = manifestEnumerator.Enumerate(ctx, func(dgst digest.Digest) error { // Mark the manifest's blob emit("%s: marking manifest %s ", repoName, dgst) markSet[dgst] = struct{}{} manifest, err := manifestService.Get(ctx, dgst) if err != nil { return fmt.Errorf("failed to retrieve manifest for digest %v: %v", dgst, err) } descriptors := manifest.References() for _, descriptor := range descriptors { markSet[descriptor.Digest] = struct{}{} emit("%s: marking blob %s", repoName, descriptor.Digest) } switch manifest.(type) { case *schema1.SignedManifest: signaturesGetter, ok := manifestService.(distribution.SignaturesGetter) if !ok { return fmt.Errorf("unable to convert ManifestService into SignaturesGetter") } signatures, err := signaturesGetter.GetSignatures(ctx, dgst) if err != nil { return fmt.Errorf("failed to get signatures for signed manifest: %v", err) } for _, signatureDigest := range signatures { emit("%s: marking signature %s", repoName, signatureDigest) markSet[signatureDigest] = struct{}{} } break case *schema2.DeserializedManifest: config := manifest.(*schema2.DeserializedManifest).Config emit("%s: marking configuration %s", repoName, config.Digest) markSet[config.Digest] = struct{}{} break } return nil }) return err }) if err != nil { return fmt.Errorf("failed to mark: %v\n", err) } // sweep blobService := registry.Blobs() deleteSet := make(map[digest.Digest]struct{}) err = blobService.Enumerate(ctx, func(dgst digest.Digest) error { // check if digest is in markSet. If not, delete it! if _, ok := markSet[dgst]; !ok { deleteSet[dgst] = struct{}{} } return nil }) if err != nil { return fmt.Errorf("error enumerating blobs: %v", err) } emit("\n%d blobs marked, %d blobs eligible for deletion", len(markSet), len(deleteSet)) // Construct vacuum vacuum := storage.NewVacuum(ctx, storageDriver) for dgst := range deleteSet { emit("blob eligible for deletion: %s", dgst) if dryRun { continue } err = vacuum.RemoveBlob(string(dgst)) if err != nil { return fmt.Errorf("failed to delete blob %s: %v\n", dgst, err) } } return err }