Last active
December 8, 2020 08:10
-
-
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
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 characters
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