Navigation Menu

Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Star 14 You must be signed in to star a gist
  • Fork 4 You must be signed in to fork a gist
  • Save lukewpatterson/bf9d19410094ea8bced1d4bb0523b67f to your computer and use it in GitHub Desktop.
Save lukewpatterson/bf9d19410094ea8bced1d4bb0523b67f to your computer and use it in GitHub Desktop.
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://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)
}
}
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()
}
@berendt
Copy link

berendt commented Dec 21, 2016

Thanks for sharing this!

@zhaojiayu
Copy link

how to run this script? when I run, a problem hadpened. Log as follows:

Caught: groovy.lang.MissingPropertyException: No such property: repository for class: delete_docker
groovy.lang.MissingPropertyException: No such property: repository for class: delete_docker
	at delete_docker.run(delete_docker.groovy:8)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment