Last active
June 8, 2023 09:08
Revisions
-
lukewpatterson revised this gist
Dec 8, 2016 . No changes.There are no files selected for viewing
-
lukewpatterson revised this gist
Dec 8, 2016 . 1 changed file with 1 addition and 1 deletion.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -18,7 +18,7 @@ try { // remove Components which don't own any Assets. Docker Registry API-based DELETE calls were causing // this scenario in Nexus 3.1.0-4. symptoms included deleted Component remaining in API retrieval results, but // not appearing on the UI Components list. // https://github.com/docker/distribution/blob/8d096a4f4213ef0d856459f80f09dc5ce33bbdcf/docs/spec/api.md#deleting-an-image if (storageTx.browseAssets(component).asCollection().isEmpty()) { log.info("Deleting Orphaned Nexus Component: ${component.toString()}") storageTx.deleteComponent(component) -
lukewpatterson created this gist
Dec 8, 2016 .There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,88 @@ import groovy.json.JsonSlurper import org.sonatype.nexus.repository.storage.Asset import org.sonatype.nexus.repository.storage.StorageFacet def DOCKER_REPOSITORY_NAME = 'docker-hosted' def dockerRepository = repository.repositoryManager.get(DOCKER_REPOSITORY_NAME) def dockerBlobStore = blobStore.blobStoreManager.get(dockerRepository.configuration.attributes.storage.blobStoreName) def storageTx = dockerRepository.facet(StorageFacet.class).txSupplier().get() try { storageTx.begin() dockerRepository.stop() def bucket = storageTx.findBucket(dockerRepository) def images = storageTx.browseComponents(bucket).asCollection().asImmutable() log.info("Nexus Components - Docker Images: ${images.size()}") images.forEach { component -> // remove Components which don't own any Assets. Docker Registry API-based DELETE calls were causing // this scenario in Nexus 3.1.0-4. symptoms included deleted Component remaining in API retrieval results, but // not appearing on the UI Components list. // https://docs.docker.com/registry/spec/api/#deleting-an-image if (storageTx.browseAssets(component).asCollection().isEmpty()) { log.info("Deleting Orphaned Nexus Component: ${component.toString()}") storageTx.deleteComponent(component) } } def manifests = [] as Set<Asset> def nonManifests = [] as Set<Asset> storageTx.browseAssets(bucket).forEach { asset -> def contentType = asset.contentType() // https://github.com/docker/distribution/blob/844b92879f179f16a26ce441631880aa3079b7f4/docs/spec/manifest-v2-2.md#media-types switch (contentType) { case 'application/vnd.docker.distribution.manifest.v2+json': manifests.add(asset) break case 'application/vnd.docker.image.rootfs.diff.tar.gzip': // blob/layer case 'application/vnd.docker.container.image.v1+json': // configuration nonManifests.add(asset) break default: // fail if any type we aren't prepared for and/or don't understand throw new IllegalArgumentException("Unexpected Content-Type '${contentType}' on Asset '${asset.name()}'") } } log.info("Nexus Assets - Docker Manifests: ${manifests.size()}") log.info("Nexus Assets - Docker Non-Manifests: ${nonManifests.size()}") log.info("Nexus Assets Total: ${manifests.size() + nonManifests.size()}") // initialized with digest of 'zero size' layer, something about backwards compat with v1 // https://github.com/docker/distribution/issues/1810#issuecomment-231152078 def digests = ['sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4'] as Set<String> manifests.each { manifest -> def manifestJson = new JsonSlurper().parse(dockerBlobStore.get(manifest.blobRef().blobId).inputStream) if (manifestJson.schemaVersion != 2) { throw new IllegalArgumentException( "Unexpected schemaVersion '${manifestJson.schemaVersion}' on Manifest '${manifest.name()}'") } // https://github.com/docker/distribution/blob/844b92879f179f16a26ce441631880aa3079b7f4/docs/spec/manifest-v2-2.md#image-manifest digests.add(manifestJson.config.digest) manifestJson.layers.forEach { layer -> def digest = layer.digest.toString() // fail if assumption made for the algorithm of the hardcoded 'zero size' value from earlier isn't valid // https://github.com/docker/docker.github.io/blob/84cbb88f7580307bc89eefc1c476f675e09a4394/registry/spec/api.md#content-digests if (!digest.startsWith('sha256:')) { throw new IllegalArgumentException("Unexpected Digest algorithm in '${digest}' on Asset '${asset.name()}'") } digests.add(digest) } } digests.forEach { digest -> nonManifests.removeIf { candidateNonManifest -> candidateNonManifest.name().endsWith(digest) } } log.info("Nexus Assets - Docker Non-Manifests - Orphaned: ${nonManifests.size()}") nonManifests.forEach { nonManifest -> log.info("Deleting Orphaned Docker Non-Manifest: ${nonManifest.name().replaceAll('v2/-/blobs/','')}") storageTx.deleteAsset(nonManifest) } storageTx.commit() dockerBlobStore.compact() } finally { dockerRepository.start() storageTx.close() }