banner



Broken Pipe (Write Failed). Upload to Artificatory Jenkins

Over time, during the development procedure, many artifacts are pushed into Artifactory. A best practice (with artifact servers) is to delete erstwhile and unused artifacts. This helps in reducing clutter, deejay space and tin can assistance with performance.

The cleanup process is the actual implementation of what is called a Data Retention Policy. Each organization defines his information retention policy based on their needs and evolution workflows. And, although the data retention policy is individual to each organization, there are some principal similarities. For example, most information memory policies will include (or accost) the following:

  • Reduce artifacts storage space.
  • Delete former and/or unused artifacts.
  • Keep only a limited number of non production artifacts (e.m. snapshots or nightly builds).

The following will depict how to delete quondam artifacts from an Artifactory'southward repository using Jenkins, bang-up scripts and REST API to automate the process.

This process uses what JFrog calls User Plugins. You tin read more about it here: https://www.jfrog.com/confluence/display/JFROG/User+Plugins

There are sample user plugins here: https://github.com/JFrog/artifactory-user-plugins

I accept used some of the sample user plugins as a bases for my own implementation.

Please note: This tutorial is using a specific data retention policy that was custom made for one of my clients. You will probably implement a different information retention policy, but the procedure will remain like.

The process outline

The process outline is equally follows:

  1. Create administrative user in Artifactory
  2. Mark existing artifacts to be ignored by the cleanup plugin
  3. Create the Swell script that will become the user plugins
  4. Copy the Peachy scripts (plugins) into plugin directory
  5. Load plugins into Artifactory using Residual API
  6. Create a new logger for clean-up procedure
  7. Define a Jenkins pipeline to:
  • Execute the plugins (with different options)
  • Trigger Artifactory'southward empty trash office
  • Trigger Artifactory'southward Garbage collector process

Please note: In this scenario, I've chosen to control the plugins using Jenkins and setup the pipeline to run in one case a twenty-four hours using cron. It is possible to set the cron within the plugin's groovy script and let information technology run internally inside Artifactory without Jenkins.

Also: The user plugins can only be run on an Artifactory Pro server.

For code demo, assume yous take an Artifactory Pro vii.iii.2 running as Docker container on a Ubuntu 18.04.iv LTS machine.

Artifactory Docker run command

          docker run --name artifactory --restart=e'er -v /art_pro_data:/var/opt/jfrog/artifactory -d -p 8081:8081 -p 8082:8082 docker.bintray.io/jfrog/artifactory-pro:7.3.2        

Create administrative user in Artifactory

If y'all're planning on controlling these plugins using Jenkins, you lot have to setup an Admin user in Artifactory and apply it'due south credentials in Jenkins' pipeline. Only an Admin user can run certain actions like loading plugins into the arrangement.

Mark existing artifacts to be ignored past the cleanup plugin

If you already have a repository filled with artifacts, it's easier to fix the initial 'practise not delete' property using Artifactory's UI. After that, it'southward amend to add this property, automatically (using Jenkins), to each upload of an artifact. This will ensure that the artifact(south) will never be deleted using the artifactCleanup plugin.

In Artifactory'south UI

  1. Select the artifact or path you lot wish to exclude from the delete (cleanup) process.
  2. Click the Properties tab
  3. Under Add: Property
    Blazon cleanup.skip in the Proper noun field
    Blazon true in the Value field
  4. If you are setting this belongings to a path and you want all artifacts under this path to be set besides, check the Recursive option.
  5. Click Add

In Jenkins Pipeline

Add the props property with value cleanup.skip=true to the File Spec.

          def upload_spec = """{
"files": [{
"pattern": "$archive_filename",
"target": "$upload_dir/",
"props": "cleanup.skip=true"
}]
}"""

Create the Not bad script that volition become the user plugins

For the process of artifacts cleanup nosotros volition need ii cracking scripts:

  • artifactCleanup.neat — This script will delete the artifacts.
  • deleteEmptyDirs.groovy — This script will delete the empty directories or paths left after the artifacts deletion.

I've started with downloading the above script from the user plugins github and then slightly modified them to suite the client needs.

The full script code is at the end of this readme.

Copy the Groovy scripts (plugins) into plugin directory

After you take updated the groovy scripts as needed (if it was necessary), copy them into the following path within Artifactory's file system:

          $JFROG_HOME/artifactory/var/etc/artifactory/plugins        

For example, in my production server the external (not inside the docker container) path is:

          /mnt/Jfrog_Dev/artifactory/etc/artifactory/plugins        

Load plugins into Artifactory using Remainder API

To load the plugins into Artifactory, use the following Residue API command. If you brand changes to the scripts, use this command to reload the plugin and the new changes.

          curl -u admin:pass -X Mail service http://artifactory:8081/artifactory/api/plugins/reload        

Note: If you lot setup a Jenkins pipeline to control this process, y'all tin can reload the plugins using Jenkins and don't have to run the above command manually.

Create a new logger for clean-upwards process

All the logs created by the plugins are written into the master log (called panel.log). It's more convenient to take a carve up logger for the cleanup plugins. Here are the instructions for creating such a log.

All logging settings are in file logback.xml. It'southward an XML file and it'south located here:

          $JFROG_HOME/artifactory/var/etc/artifactory/logback.xml        

Open the file in a text editor, locate the block that starts with:

          <appender proper name="Admission" class="ch.qos.logback.core.rolling.RollingFileAppender">        

and add the following afterwards the terminate of the block:

          <!-- User Plugins Appenders -->
<appender name="CLEANUP" class="ch.qos.logback.core.rolling.RollingFileAppender">
<File>${log.dir}/artifactory-cleanup.log</File>
<rollingPolicy class="org.jfrog.common.logging.logback.rolling.FixedWindowWithDateRollingPolicy">
<FileNamePattern>${log.dir.archived}/artifactory-cleanup.%i.log.gz</FileNamePattern>
</rollingPolicy>
<triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
<MaxFileSize>25MB</MaxFileSize>
</triggeringPolicy>
<encoder grade="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
<layout class="org.jfrog.mutual.logging.logback.layout.BackTracePatternLayout">
<pattern>%date{yyyy-MM-dd'T'HH:mm:ss.SSS, UTC}Z [jfrt ] - %m%n</design>
</layout>
</encoder>
</appender>

Then, in the aforementioned file locate the department marked as <!-- specialized appenders --> and add the following only higher up it:

          <!-- user plugins loggers -->
<logger proper noun="artifactCleanup" level="info">
<appender-ref ref="CLEANUP"/>
</logger>
<logger proper noun="deleteEmptyDirs" level="info"/>

Note: If you have chosen to not create a separate logger, you lot can skip the part of adding the <appender> department. And if that is the example, then you need to add the following <logger> section instead:

          <!-- user plugins loggers -->
<logger name="artifactCleanup" level="info"/>
<logger name="deleteEmptyDirs" level="info"/>

This will add the logging for the cleanup into the main logger only.

Location of logs in Artifactory

All logs are located in the following path:

          $JFROG_HOME/artifactory/var/log        

If yous created a custom log it will be here:

          $JFROG_HOME/artifactory/var/log/artifactory-cleanup.log        

If not, the logs volition exist in the following path:

          $JFROG_HOME/artifactory/var/log/console.log        

Define a Jenkins pipeline

It's possible to set up the cleanup plugins internally in Artifactory, simply when controlling the plugins with Jenkins, you lot get more than control over the plugins with Jenkins parameters combined with a pipeline.

The total Jenkins pipeline lawmaking is listed at the stop of this readme file.

A petty information on the pipeline

Because the cleanup plugin has many parameters that control how it executes, I've created a parameters in the pipeline to represent to each parameter in the cleanup script. All the parameter can exist adjusted except the repos parameter. The reason this parameter is hard-coded to a specific repository is considering, in my production server, there are 2 development teams that utilize the same Artifactory server and I don't want to delete the other squad's artifacts by mistake. I did fix the pipeline in such a way that you can easily make the necessary changes to brand this option editable.

Note: All the parameters have default values, then the job tin run using Jenkins cron trigger and perform the default make clean periodically.

Groovy Scripts

artifactCleanup.groovy

          import org.apache.commons.lang3.StringUtils
import org.artifactory.api.repo.exception.ItemNotFoundRuntimeException
import org.artifactory.exception.CancelException
import groovy.json.JsonSlurper
import groovy.time.TimeCategory
import swell.fourth dimension.TimeDuration
import great.transform.Field
import coffee.text.SimpleDateFormat @Field final Cord CONFIG_FILE_PATH = "plugins/${this.class.proper name}.json"
@Field concluding String PROPERTIES_FILE_PATH = "plugins/${this.grade.proper name}.backdrop"
@Field final String DEFAULT_TIME_UNIT = "day"
@Field final int DEFAULT_TIME_INTERVAL = 21 // 21 days = 3 weeks
grade Global {
static Boolean stopCleaning = false
static Boolean pauseCleaning = false
static int paceTimeMS = 0
}
def pluginGroup = 'cleaners' executions {
cleanup(groups: [pluginGroup]) { params ->
def timeUnit = params['timeUnit'] ? params['timeUnit'][0] as String : DEFAULT_TIME_UNIT
def timeInterval = params['timeInterval'] ? params['timeInterval'][0] equally int : DEFAULT_TIME_INTERVAL
def repos = params['repos'] every bit Cord[]
def dryRun = params['dryRun'] ? new Boolean(params['dryRun'][0]) : true
def disablePropertiesSupport = params['disablePropertiesSupport'] ? new Boolean(params['disablePropertiesSupport'][0]) : false
def paceTimeMS = params['paceTimeMS'] ? params['paceTimeMS'][0] as int : 0

// Enable fallback back up for deprecated month parameter
if ( params['months'] && !params['timeInterval'] ) {
log.info('Deprecated month parameter is still in use, please use the new timeInterval parameter instead!', properties)
timeInterval = params['months'][0] as int
} else if ( params['months'] ) {
log.warn('Deprecated month parameter and the new timeInterval are used in parallel: month has been ignored.', properties)
}

artifactCleanup(timeUnit, timeInterval, repos, log, paceTimeMS, dryRun, disablePropertiesSupport)
}
cleanupCtl(groups: [pluginGroup]) { params ->
def command = params['command'] ? params['command'][0] as Cord : ''
switch ( control ) {
case "stop":
Global.stopCleaning = true
log.info "Finish request detected"
break
instance "adjustPaceTimeMS":
def adjustPaceTimeMS = params['value'] ? params['value'][0] as int : 0
Global.paceTimeMS += adjustPaceTimeMS
log.info "Pacing adjustment request detected, adjusting stride time by $adjustPaceTimeMS to new value of $Global.paceTimeMS"
break
example "break":
Global.pauseCleaning = true
log.info "Interruption request detected"
suspension
case "resume":
Global.pauseCleaning = fake
log.info "Resume asking detected"
break
default:
log.info "Missing or invalid command, '$command'"
}
}
}
def deprecatedConfigFile = new File(ctx.artifactoryHome.etcDir, PROPERTIES_FILE_PATH)
def configFile = new File(ctx.artifactoryHome.etcDir, CONFIG_FILE_PATH)
if ( deprecatedConfigFile.exists() ) { if ( !configFile.exists() ) {
def config = new ConfigSlurper().parse(deprecatedConfigFile.toURL())
log.info "Schedule job policy list: $config.policies"
config.policies.each{ policySettings ->
def cron = policySettings[ 0 ] ? policySettings[ 0 ] as String : ["0 0 5 ? * one"]
def repos = policySettings[ 1 ] ? policySettings[ 1 ] as Cord[] : ["__none__"]
def months = policySettings[ 2 ] ? policySettings[ 2 ] as int : half dozen
def paceTimeMS = policySettings[ 3 ] ? policySettings[ 3 ] equally int : 0
def dryRun = policySettings[ 4 ] ? policySettings[ 4 ] as Boolean : false
def disablePropertiesSupport = policySettings[ 5 ] ? policySettings[ 5 ] every bit Boolean : faux
jobs {
"scheduledCleanup_$cron"(cron: cron) {
log.info "Policy settings for scheduled run at($cron): repo list($repos), timeUnit(calendar month), timeInterval($months), paceTimeMS($paceTimeMS) dryrun($dryRun) disablePropertiesSupport($disablePropertiesSupport)"
artifactCleanup( "calendar month", months, repos, log, paceTimeMS, dryRun, disablePropertiesSupport )
}
}
}
} else {
log.warn "Deprecated 'artifactCleanup.properties' file is all the same present, but ignored. Y'all should remove the file."
}
}
if ( configFile.exists() ) {

def config = new JsonSlurper().parse(configFile.toURL())
log.info "Schedule job policy list: $config.policies"

config.policies.each{ policySettings ->
def cron = policySettings.containsKey("cron") ? policySettings.cron as String : ["0 0 5 ? * ane"]
def repos = policySettings.containsKey("repos") ? policySettings.repos as Cord[] : ["__none__"]
def timeUnit = policySettings.containsKey("timeUnit") ? policySettings.timeUnit as String : DEFAULT_TIME_UNIT
def timeInterval = policySettings.containsKey("timeInterval") ? policySettings.timeInterval as int : DEFAULT_TIME_INTERVAL
def paceTimeMS = policySettings.containsKey("paceTimeMS") ? policySettings.paceTimeMS as int : 0
def dryRun = policySettings.containsKey("dryRun") ? new Boolean(policySettings.dryRun) : simulated
def disablePropertiesSupport = policySettings.containsKey("disablePropertiesSupport") ? new Boolean(policySettings.disablePropertiesSupport) : simulated
jobs {
"scheduledCleanup_$cron"(cron: cron) {
log.info "Policy settings for scheduled run at($cron): repo listing($repos), timeUnit($timeUnit), timeInterval($timeInterval), paceTimeMS($paceTimeMS) dryrun($dryRun) disablePropertiesSupport($disablePropertiesSupport)"
artifactCleanup( timeUnit, timeInterval, repos, log, paceTimeMS, dryRun, disablePropertiesSupport )
}
}
}
}
if ( deprecatedConfigFile.exists() && configFile.exists() ) {
log.warn "The deprecated artifactCleanup.backdrop and the new artifactCleanup.json are defined in parallel. You should migrate the old file and remove information technology."
}
private def artifactCleanup(String timeUnit, int timeInterval, Cord[] repos, log, paceTimeMS, dryRun = false, disablePropertiesSupport = false) {
log.info "Starting artifact cleanup for repositories $repos, until $timeInterval ${timeUnit}s ago with pacing interval $paceTimeMS ms, dryrun: $dryRun, disablePropertiesSupport: $disablePropertiesSupport"
// Create Map(repo, paths) of skiped paths (or others properties supported in future ...)
def skip = [:]
if ( ! disablePropertiesSupport && repos){
skip = getSkippedPaths(repos)
}
def calendarUntil = Agenda.getInstance() calendarUntil.add(mapTimeUnitToCalendar(timeUnit), -timeInterval) def calendarUntilFormatted = new SimpleDateFormat("yyyy/MM/dd HH:mm").format(calendarUntil.getTime());
log.info "Removing all artifacts not downloaded since $calendarUntilFormatted"
Global.stopCleaning = false
int cntFoundArtifacts = 0
int cntNoDeletePermissions = 0
long bytesFound = 0
long bytesFoundWithNoDeletePermission = 0

//def artifactsCleanedUp = searches.artifactsNotDownloadedSince(calendarUntil, calendarUntil, repos)
def artifactsCleanedUp = searches.artifactsCreatedOrModifiedInRange(zip, calendarUntil, repos)
artifactsCleanedUp.notice {
effort {
while ( Global.pauseCleaning ) {
log.info "Pausing by request"
sleep( 60000 )
}

if ( Global.stopCleaning ) {
log.info "Stopping by request, ending loop"
render true
}
if ( ! disablePropertiesSupport && skip[ it.repoKey ] && StringUtils.startsWithAny(it.path, skip[ it.repoKey ])){
if (log.isDebugEnabled()){
log.debug "Skip $it"
}
return false
}
bytesFound += repositories.getItemInfo(information technology)?.getSize()
cntFoundArtifacts++
if (!security.canDelete(information technology)) {
bytesFoundWithNoDeletePermission += repositories.getItemInfo(information technology)?.getSize()
cntNoDeletePermissions++
}
if (dryRun) {
log.info "Establish $it, $cntFoundArtifacts/$artifactsCleanedUp.size total $bytesFound bytes"
log.info "\t==> currentUser: ${security.currentUser().getUsername()}"
log.info "\t==> canDelete: ${security.canDelete(it)}"
} else {
if (security.canDelete(it)) {
log.info "Deleting $it, $cntFoundArtifacts/$artifactsCleanedUp.size total $bytesFound bytes"
repositories.delete it
} else {
log.info "Can't delete $it (user ${security.currentUser().getUsername()} has no delete permissions), " +
"$cntFoundArtifacts/$artifactsCleanedUp.size total $bytesFound bytes"
}
}
} catch (ItemNotFoundRuntimeException ex) {
log.info "Failed to discover $information technology, skipping"
}
def sleepTime = (Global.paceTimeMS > 0) ? Global.paceTimeMS : paceTimeMS
if (sleepTime > 0) {
sleep( sleepTime )
}
render simulated
}
if (dryRun) {
log.info "Dry run - zero deleted. Institute $cntFoundArtifacts artifacts consuming $bytesFound bytes"
if (cntNoDeletePermissions > 0) {
log.info "$cntNoDeletePermissions artifacts cannot exist deleted due to lack of permissions ($bytesFoundWithNoDeletePermission bytes)"
}
} else {
log.info "Finished cleanup, deleting $cntFoundArtifacts artifacts that took up $bytesFound bytes"
if (cntNoDeletePermissions > 0) {
log.info "$cntNoDeletePermissions artifacts could not be deleted due to lack of permissions ($bytesFoundWithNoDeletePermission bytes)"
}
}
}
individual def getSkippedPaths(String[] repos) {
def timeStart = new Date()
def skip = [:]
for (String repoKey : repos){
def pathsTmp = []
def aql = "items.find({\"repo\":\"" + repoKey + "\",\"type\": \"any\",\"@cleanup.skip\":\"true\"}).include(\"repo\", \"path\", \"name\", \"type\")"
searches.aql(aql.toString()) {
for (item in it) {
def path = item.path + '/' + particular.name
// Root path case behavior
if ('.' == particular.path){
path = item.name
}
if ('folder' == item.blazon){
path += '/'
}
if (log.isTraceEnabled()){
log.trace "skip found for " + repoKey + ":" + path
}
pathsTmp.add(path)
}
}
// Simplify list to accept merely parent paths
def paths = []
for (path in pathsTmp.sort{ it }) {
if (paths.size == 0 || ! path.startsWith(paths[-1])) {
if (log.isTraceEnabled()){
log.trace "skip added for " + repoKey + ":" + path
}
paths.add(path)
}
}
if (paths.size > 0){
skip[repoKey] = paths.toArray(new Cord[paths.size])
}
}
def timeStop = new Appointment()
TimeDuration duration = TimeCategory.minus(timeStop, timeStart)
log.info "Elapsed time to retrieve paths to skip: " + duration
return skip
}
private def mapTimeUnitToCalendar (String timeUnit) {
switch ( timeUnit ) {
example "minute":
render Calendar.MINUTE
case "hr":
return Calendar.Hour
case "day":
render Agenda.DAY_OF_YEAR
case "month":
render Calendar.MONTH
example "yr":
return Calendar.YEAR
default:
def errorMessage = "$timeUnit is not a valid fourth dimension unit. Please check your asking or scheduled policy."
log.mistake errorMessage
throw new CancelException(errorMessage, 400)
}
}

deleteEmptyDirs.groovy

          import slap-up.transform.Field
import org.artifactory.repo.RepoPath
import static java.lang.Thread.slumber
import static org.artifactory.repo.RepoPathFactory.create
/**
*
* @originalAuthor jbaruch
* @since sixteen/08/12
*
* @adaptedForClient boris_t
* @since xiv/04/xx
*
*/
executions { deleteEmptyDirsPlugin(version: '1.1', description: 'Deletes empty directories', users: ['admin'].toSet()) { params ->
if (!params || !params.paths) {
def errorMessage = 'Paths parameter is mandatory, please supply it.'
log.error errorMessage
status = 400
message = errorMessage
} else {
deleteEmptyDirectories(params.paths every bit Cord[])
}
}
}
private def deleteEmptyDirectories(String[] paths) {
def totalDeletedDirs = 0
paths.each {
log.info "Offset deleting empty directories for path($it)"
def deletedDirs = deleteEmptyDirsRecursively create(it)
log.info "Deleted($deletedDirs) empty directories for given path($it)"
totalDeletedDirs += deletedDirs
}
log.info "Finished deleting full($totalDeletedDirs) directories"
}
def deleteEmptyDirsRecursively(RepoPath path) {
def deletedDirs = 0
// let's permit other threads to do something.
sleep fifty
// if not folder - we're done, nothing to do hither
if (repositories.getItemInfo(path).folder) {
def children = repositories.getChildren path
children.each {
deletedDirs += deleteEmptyDirsRecursively it.repoPath
}
// now let's check again
if (repositories.getChildren(path).empty) {
// information technology is folder, and no children - delete!
log.info "Deleting empty directory($path)"
repositories.delete path
deletedDirs += i
}
}
return deletedDirs
}

Full Jenkins pipeline code

          backdrop([
parameters([
[$course: 'ChoiceParameter',
name: 'Reload_Plugins',
choiceType: 'PT_SINGLE_SELECT',
clarification: '<font size=3>Reload Plugins? Select Aye or No.</font>',
filterLength: ane,
filterable: false,
randomName: 'choice-parameter-108460119586981',
script: [
$class: 'GroovyScript',
fallbackScript: [
classpath: [],
sandbox: false,
script:
''
],
script: [
classpath: [],
sandbox: imitation,
script:
'return ["Aye","NO:selected"]'
]
]
],
[$class: 'DynamicReferenceParameter',
proper noun: 'Dry_Run',
choiceType: 'ET_FORMATTED_HTML',
description: '',
randomName: 'choice-parameter-108440134895850',
referencedParameters: 'Reload_Plugins',
omitValueField: truthful,
script: [
$class: 'GroovyScript',
fallbackScript: [
classpath: [],
sandbox: false,
script: ''
],
script: [
classpath: [],
sandbox: fake,
script: '''
if (Reload_Plugins.equals("Yeah")) {
return "<table style=width:100%><tr><td align=left><input name=value type=checkbox disabled></td><td align=left width=100%><font size=3>N/A</font></td></tr><tr><td colspan=2><font size=three>Dry Run? Check this option if yous wish to run without <b>deleting</b></span> artifacts.</font></td></tr></tabular array>"
}
render "<table style=width:100%><tr><td marshal=left><input name=value type=checkbox></td></tr><tr><td><font size=3>Dry Run? Check this option if y'all wish to run without <b>deleting</b></bridge> artifacts.</font></td></tr></table>"
'''
]
]
],
[$class: 'CascadeChoiceParameter',
choiceType: 'PT_SINGLE_SELECT',
description: '<font size=3>The unit (blazon) of the time interval.</font>',
filterLength: 1,
filterable: false,
name: 'Time_Unit',
randomName: 'selection-parameter-2930846449195033',
referencedParameters: 'Reload_Plugins',
script:
[$class: 'GroovyScript',
fallbackScript:
[classpath: [],
sandbox: false,
script: ''
],
script:
[classpath: [],
sandbox: false,
script: '''

if (Reload_Plugins.equals("YES")) {
return ["N/A:selected"]
}

return ["Year", "Month", "Twenty-four hours:selected", "Hour", "Minute"]
'''
]
]
],
[$class: 'DynamicReferenceParameter',
name: 'Time_Interval',
choiceType: 'ET_FORMATTED_HTML',
description: '',
randomName: 'selection-parameter-108460134875850',
referencedParameters: 'Reload_Plugins',
omitValueField: true,
script: [
$class: 'GroovyScript',
fallbackScript: [
classpath: [],
sandbox: false,
script: ''
],
script: [
classpath: [],
sandbox: false,
script:
'''
if (Reload_Plugins.equals("Yes")) {
render "<table manner=width:100%><tr><td align=left><input name=value type=text value='N/A' disabled></td></tr><tr><td><font size=iii><font size=3>The time interval to look back before deleting an artifact.</font></font></td></tr></tabular array>"
}
return "<table manner=width:100%><tr><td align=left><input name=value type=text value='21'></td></tr><tr><td><font size=3><font size=3>The time interval to look back earlier deleting an antiquity.</font></font></td></tr></table>"
'''
]
]
],
[$class: 'DynamicReferenceParameter',
name: 'Target_Repositories',
choiceType: 'ET_FORMATTED_HTML',
clarification: '',
randomName: 'choice-parameter-108461134575850',
referencedParameters: 'Reload_Plugins',
omitValueField: true,
script: [
$class: 'GroovyScript',
fallbackScript: [
classpath: [],
sandbox: false,
script: ''
],
script: [
classpath: [],
sandbox: simulated,
script:
'''
if (Reload_Plugins.equals("Yeah")) {
return "<table manner=width:100%><tr><td align=left><input proper name=value type=text value='Due north/A' disabled></td></tr><tr><td><font size=iii>A list of repositories to clean. This parameter is hardcoded to Segway repo.</font></td></tr></tabular array>"
}
return "<table style=width:100%><tr><td align=left><input proper name=value type=text value='Segway' disabled></td></tr><tr><td><font size=three>A list of repositories to clean. This parameter is hardcoded to Segway repo.</font></td></tr></table>"
'''
]
]
],
[$class: 'DynamicReferenceParameter',
proper noun: 'Pace_Time_ms',
choiceType: 'ET_FORMATTED_HTML',
description: '',
randomName: 'selection-parameter-108260134873850',
referencedParameters: 'Reload_Plugins',
omitValueField: true,
script: [
$class: 'GroovyScript',
fallbackScript: [
classpath: [],
sandbox: false,
script: ''
],
script: [
classpath: [],
sandbox: false,
script:
'''
if (Reload_Plugins.equals("Yeah")) {
return "<table style=width:100%><tr><td marshal=left><input name=value type=text value='North/A' disabled></td></tr><tr><td><font size=3>The number of milliseconds to delay between delete operations</font></td></tr></table>"
}
render "<table style=width:100%><tr><td align=left><input name=value blazon=text value='0'></td></tr><tr><td><font size=iii>The number of milliseconds to filibuster between delete operations</font></td></tr></table>"
'''
]
]
],
[$course: 'DynamicReferenceParameter',
proper noun: 'Ignore_Properties',
choiceType: 'ET_FORMATTED_HTML',
description: '',
randomName: 'choice-parameter-118460134875857',
referencedParameters: 'Reload_Plugins',
omitValueField: truthful,
script: [
$class: 'GroovyScript',
fallbackScript: [
classpath: [],
sandbox: imitation,
script: ''
],
script: [
classpath: [],
sandbox: false,
script:
'''
if (Reload_Plugins.equals("Yep")) {
return "<table style=width:100%><tr><td marshal=left><input name=value type=checkbox disabled></td><td align=left width=100%><font size=3>N/A</font></td></tr><tr><td colspan=2><font size=3>Ignore Artifacts Properties? Check this option if you wish to <b>ignore aertifactory backdrop</b></span>. <b>Non RECOMMENDET!</b></font></td></tr></tabular array>"
}
return "<tabular array mode=width:100%><tr><td align=left><input name=value type=checkbox></td></tr><tr><td><font size=3>Ignore Artifacts Properties? Check this option if you wish to <b>ignore aertifactory properties</b></bridge>. <b>Not RECOMMENDET!</b></font></td></tr></table>"
'''
]
]
],
])
])
// Go/Set Parameters values
def reload_plugins = (env.Reload_Plugins == "Yep") ? true : false
def dry_run = (env.Dry_Run == "true") ? true : fake
def time_unit = env.Time_Unit.toLowerCase()
def time_interval = env.Time_Interval.isInteger() ? env.Time_Interval.toInteger() : 21 // default = 21
def target_repositories = env.Target_Repositories
def pace_time_ms = env.Pace_Time_ms.isInteger() ? env.Pace_Time_ms.toInteger() : 0 // default = 0
def ignore_properties = (env.Ignore_Properties == "true") ? truthful : false
// Artifactory API vars
def artifactory_api = "http://artifactory:8081/artifactory/api"
def execute_cleaup = artifactory_api + "/plugins/execute/cleanup?params="
def execute_del_dir = artifactory_api + "/plugins/execute/deleteEmptyDirsPlugin?params="
pipeline {
amanuensis any
stages {
stage('Reload Plugins') {
when { expression { render reload_plugins } }
steps {
script {
withCredentials([[$class: 'UsernamePasswordMultiBinding', credentialsId: 'jenkins_artifactory_admin_user', usernameVariable: 'username', passwordVariable: 'password']]) {
def encoded_pass = URLEncoder.encode(password, "UTF-8")
sh """
gyre -u $username:$encoded_pass -X Mail "$artifactory_api/plugins/reload"
"""
}
}
}
} // Stop phase Reload Plugins
phase('Cleanup Plugin') {
when { expression { render !reload_plugins } }
steps {
script {
def curl_uri = execute_cleaup
// Add params
curl_uri += "timeUnit=$time_unit;"
curl_uri += "timeInterval=$time_interval;"
curl_uri += "repos=$target_repositories;"
curl_uri += "dryRun=$dry_run;"
curl_uri += "paceTimeMS=$pace_time_ms;"
curl_uri += "disablePropertiesSupport=$ignore_properties"
withCredentials([[$class: 'UsernamePasswordMultiBinding', credentialsId: 'jenkins_artifactory_admin_user', usernameVariable: 'username', passwordVariable: 'password']]) {
def encoded_pass = URLEncoder.encode(password, "UTF-eight")
sh """
curl -i -u $username:$encoded_pass -X Postal service "$curl_uri"
"""
}
}
}
} // End phase Cleanup Plugin
phase('Delete Empty Dirs Plugin') {
// Run this stage just if reload_plugins=fake and dry_run=false
when { expression { return (!reload_plugins && !dry_run) } }
steps {
script {
def curl_uri = execute_del_dir
// Add params
curl_uri += "paths=$target_repositories"
withCredentials([[$form: 'UsernamePasswordMultiBinding', credentialsId: 'jenkins_artifactory_admin_user', usernameVariable: 'username', passwordVariable: 'password']]) {
def encoded_pass = URLEncoder.encode(password, "UTF-8")
sh """
curl -i -u $username:$encoded_pass -X POST "$curl_uri"
"""
}
}
}
} // Stop stage Delete Empty Dirs Plugin
stage('Empty Trash Tin') {
// Run this stage just if reload_plugins=simulated and dry_run=false
when { expression { return (!reload_plugins && !dry_run) } }
steps {
script {
withCredentials([[$class: 'UsernamePasswordMultiBinding', credentialsId: 'jenkins_artifactory_admin_user', usernameVariable: 'username', passwordVariable: 'password']]) {
def encoded_pass = URLEncoder.encode(password, "UTF-8")
sh """
curl -u $username:$encoded_pass -10 POST "$artifactory_api/trash/empty"
"""
}
}
}
} // End stage Empty Trash Can
stage('Garbage Collector') {
// Run this stage only if reload_plugins=false and dry_run=false
when { expression { return (!reload_plugins && !dry_run) } }
steps {
script {
withCredentials([[$class: 'UsernamePasswordMultiBinding', credentialsId: 'jenkins_artifactory_admin_user', usernameVariable: 'username', passwordVariable: 'countersign']]) {
def encoded_pass = URLEncoder.encode(password, "UTF-8")
sh """
ringlet -u $username:$encoded_pass -10 Mail service "$artifactory_api/organization/storage/gc"
"""
}
}
}
} // Cease stage Garbage Collector
}
}

Related Artifactory REST API Commands

Reload Plugins

          ringlet -u admin:laissez passer -X Post http://artifactory:8081/artifactory/api/plugins/reload        

Retrieve Plugin Info

          curl -u admin:pass -X GET http://artifactory:8081/artifactory/api/plugins        

Execute Cleanup Artifacts Plugin (several variations)

          curl -i -u admin:pass -X Postal service "http://artifactory:8081/artifactory/api/plugins/execute/cleanup?params=timeUnit=calendar month;timeInterval=20;repos=Segway;dryRun=true;paceTimeMS=2;disablePropertiesSupport=false"          gyre -i -u admin:pass -X Post "http://artifactory:8081/artifactory/api/plugins/execute/cleanup?params=timeUnit=60 minutes;timeInterval=ane;repos=Segway;dryRun=true;paceTimeMS=two;disablePropertiesSupport=simulated"          curlicue -i -u admin:pass -10 Mail "http://artifactory:8081/artifactory/api/plugins/execute/cleanup?params=timeUnit=month;timeInterval=iii;repos=Segway;dryRun=true;disablePropertiesSupport=faux"          roll -i -u admin:pass -X Post "http://artifactory:8081/artifactory/api/plugins/execute/cleanup?params=timeUnit=month;timeInterval=xx;repos=Segway;dryRun=true"          curl -i -u admin:laissez passer -X Mail service "http://artifactory:8081/artifactory/api/plugins/execute/cleanup?params=timeUnit=month;timeInterval=5;repos=Segway;dryRun=true"          curlicue -i -u admin:pass -Ten Postal service "http://artifactory:8081/artifactory/api/plugins/execute/cleanup?params=repos=Segway;dryRun=true;disablePropertiesSupport=imitation"        

Execute Delete Empty Dirs Plugin

          coil -i -u admin:pass -X Mail "http://artifactory:8081/artifactory/api/plugins/execute/deleteEmptyDirsPlugin?params=paths=Segway"        

Empty Trash Can

          ringlet -i -u admin:laissez passer -X Mail service "http://artifactory:8081/artifactory/api/trash/empty"        

Run Garbage Collection

          curl -i -u admin:pass -X Mail service "http://artifactory:8081/artifactory/api/organisation/storage/gc"        

sicklerboured44.blogspot.com

Source: https://medium.com/develeap/artifactory-clean-up-f518427712ef

0 Response to "Broken Pipe (Write Failed). Upload to Artificatory Jenkins"

Post a Comment

Iklan Atas Artikel

Iklan Tengah Artikel 1

Iklan Tengah Artikel 2

Iklan Bawah Artikel