Skip to content

Instantly share code, notes, and snippets.

@dwamara
Last active December 8, 2020 08:10
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dwamara/506a51483549eb1c8f90eb88c82e17a6 to your computer and use it in GitHub Desktop.
Save dwamara/506a51483549eb1c8f90eb88c82e17a6 to your computer and use it in GitHub Desktop.
Due to the requirement from Maven to not have unique versions of SNAPSHOTS, even when a policy to clean the SNAPSHOTS is specified in Nexus 3, SNAPSHOTS that are not cleaned still contain several versions of the SNAPSHOTS artefacts timestamped. This script allows to delete the timestamped SNAPSHOTS and to keep a certain specified amount of them
import groovy.json.JsonSlurper
import groovy.transform.Field
import static groovy.time.TimeCategory.minus
import static javax.xml.bind.DatatypeConverter.printBase64Binary
@Field def params = [:]
@Field def nexus = [:]
@Field def artifactsToVersion = [:]
@Field def was_at_least_one_element_delete = false
// -------------------- process ---------------------
initParameters()
def timeStart = new Date()
println "###################### Cleaning >Start< ###################### "
// Start the search of elements to delete
sendSearchRequests()
// display or delete elements found
artifactsToVersion.each { k1, v1 ->
//println "${k1} : ${v1}"
v1.each { k2, v2 ->
//println "${k2} : ${v2}"
v2.eachWithIndex { entry, index ->
if (index >= params.deletionIndexStart) {
if (params.action == "delete") {
println "Deleting element : id [$entry.key] --> $k1:$entry.value"
sendRequest (("http://" + nexus.host + ":" + nexus.port + nexus.baseDeleteUrl).replace("{component_id}", entry.key), "DELETE")
was_at_least_one_element_delete = true
} else {
println "Found element to delete : id [$entry.key] --> $k1:$entry.value"
}
}
}
}
}
if (params.action == "delete" && was_at_least_one_element_delete) {
// Search if there is a "compact blob store" task that can be launched
println "###################### Trying to compact the blob store ###################### "
String searchCompactBlobstoreUrl = ("http://" + nexus.host + ":" + nexus.port + nexus.baseSearchTaskUrl).replace("{task.type}", "blobstore.compact")
def blobstoreSearchResponse = sendRequest(searchCompactBlobstoreUrl)
def blobstoreTaskId = processBlobstoreSearchResponse(blobstoreSearchResponse)
if (blobstoreTaskId != null) {
sendRequest(("http://" + nexus.host + ":" + nexus.port + nexus.baseTaskUrl).replace("{taskId}", blobstoreTaskId), "POST")
println "###################### Starting blob store compacting - The script will end even though the compacting is running ###################### "
} else {
println "###################### No compact blob store task set, please create one and either start it manually or by restartng this script###################### "
}
}
def timeStop = new Date()
println "###################### Time elapsed > " + minus(timeStop, timeStart) + " < ###################### "
// ----------------- Methods ------------------
def initParameters() {
params = [
action:System.env['ACTION'],
countToKeep:System.env['COUNT_TO_KEEP'].toInteger(),
deletionIndexStart:System.env['COUNT_TO_KEEP'].toInteger() - 1, // the search API never returns the last timestamped SNAPSHOT -> if there is only one, it won't be returned by the search API
snapshots_repository:System.env['SNAPSHOTS_REPOSITORY']
]
nexus = [
host:System.env['NEXUS_SERVER'],
port:System.env['NEXUS_PORT'],
baseSearchUrl:'/service/rest/v1/search?sort=version&repository={snapshots_repository}',
baseDeleteUrl:'/service/rest/v1/components/{component_id}',
baseSearchTaskUrl:'/service/rest/v1/tasks?type={task.type}',
baseTaskUrl:'/service/rest/v1/tasks/{taskId}/run',
username:System.env['NEXUS_RESTAPI_USERNAME'],
password:System.env['NEXUS_RESTAPI_PASSWORD']
]
}
def sendSearchRequests() {
String searchUrl = ("http://" + nexus.host + ":" + nexus.port + nexus.baseSearchUrl).replace("{snapshots_repository}", params.snapshots_repository)
def json = sendRequest(searchUrl)
processJsonResponse(json)
def continuationToken = json.continuationToken
if (continuationToken != null) {
while (continuationToken != null) {
json = sendRequest(searchUrl + '&continuationToken=' + continuationToken)
processJsonResponse(json)
continuationToken = json.continuationToken
}
}
}
static def processBlobstoreSearchResponse(def json) {
def taskId
json.items.each { item ->
if (item.currentState.equalsIgnoreCase("WAITING")) {
taskId = item.id
}
}
return taskId
}
def processJsonResponse(def json) {
json.items.each { item ->
String itemName = item.name
if (artifactsToVersion.containsKey(itemName)) {
String itemVersion = item.version
def timestamp_year = "-2019"; // default year
def timestamp_year_match = (itemVersion =~ '-20[012]\\d')
if (timestamp_year_match.find()) {
timestamp_year = timestamp_year_match.group(0)
}
String version = itemVersion.substring(0, itemVersion.indexOf(timestamp_year))
String timestamp = itemVersion.substring(itemVersion.indexOf(version))
def map1 = artifactsToVersion.get(itemName)
if (map1.containsKey(version)) {
def map2 = map1.get(version)
map2[item.id] = timestamp
} else {
def map2 = [:]
map1[version] = map2
map2[item.id] = timestamp
}
artifactsToVersion[item.name] = map1
} else {
def map = [:]
artifactsToVersion[item.name] = map
}
}
}
def sendRequest(def url, String method = "GET") {
println method + ' ' + url
String userPass = "${nexus.username}:${nexus.password}"
String basicAuth = "Basic " + "${printBase64Binary(userPass.getBytes())}"
def connection = new URL( url ).openConnection() as HttpURLConnection
connection.setRequestProperty('Accept', 'application/json' )
connection.setRequestProperty('Authorization', basicAuth)
connection.setRequestMethod(method)
try {
if ( connection.responseCode <= 299 ) {
if (connection.responseCode == 200) {
return connection.inputStream.withCloseable { inStream -> new JsonSlurper().parse( inStream as InputStream ) }
}
} else {
println connection.responseCode + ": " + connection.inputStream.text
}
} catch(Exception exc) {
println exc.getMessage()
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment