Skip to content

Commit e2f1c6d

Browse files
authored
Merge pull request #2 from AnishaGharat/revert-1-feature/delete-specific-cache-version-remotely
Revert "Updated code for deleting specific cache version"
2 parents 433d676 + 006be8f commit e2f1c6d

File tree

12 files changed

+111
-167
lines changed

12 files changed

+111
-167
lines changed

src/main/java/org/jenkinsci/plugins/workflow/libs/LibraryAdder.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -202,7 +202,7 @@ static List<URL> retrieve(@NonNull LibraryRecord record, @NonNull LibraryRetriev
202202
FilePath libDir = new FilePath(execution.getOwner().getRootDir()).child("libs/" + record.getDirectoryName());
203203
Boolean shouldCache = cachingConfiguration != null;
204204
final FilePath versionCacheDir = new FilePath(LibraryCachingConfiguration.getGlobalLibrariesCacheDir(), record.getDirectoryName());
205-
ReentrantReadWriteLock retrieveLock = getReadWriteLockFor(record.getName());
205+
ReentrantReadWriteLock retrieveLock = getReadWriteLockFor(record.getDirectoryName());
206206
final FilePath lastReadFile = new FilePath(versionCacheDir, LibraryCachingConfiguration.LAST_READ_FILE);
207207

208208
if(shouldCache && cachingConfiguration.isExcluded(version)) {
@@ -238,7 +238,7 @@ static List<URL> retrieve(@NonNull LibraryRecord record, @NonNull LibraryRetriev
238238
}
239239

240240
if (retrieve) {
241-
listener.getLogger().println("Caching library " + name + "@" + version);
241+
listener.getLogger().println("Caching library " + name + "@" + version);
242242
versionCacheDir.mkdirs();
243243
retriever.retrieve(name, version, changelog, versionCacheDir, run, listener);
244244
}
@@ -251,7 +251,7 @@ static List<URL> retrieve(@NonNull LibraryRecord record, @NonNull LibraryRetriev
251251
}
252252

253253
lastReadFile.touch(System.currentTimeMillis());
254-
versionCacheDir.withSuffix("-name.txt").write(name + "@" + version, "UTF-8");
254+
versionCacheDir.withSuffix("-name.txt").write(name, "UTF-8");
255255
versionCacheDir.copyRecursiveTo(libDir);
256256
} finally {
257257
retrieveLock.readLock().unlock();

src/main/java/org/jenkinsci/plugins/workflow/libs/LibraryCachingCleanup.java

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -28,13 +28,14 @@ public LibraryCachingCleanup() {
2828
@Override protected void execute(TaskListener listener) throws IOException, InterruptedException {
2929
FilePath globalCacheDir = LibraryCachingConfiguration.getGlobalLibrariesCacheDir();
3030
for (FilePath library : globalCacheDir.list()) {
31-
for (FilePath versionDir : library.listDirectories()) {
32-
if (!removeIfExpiredCacheDirectory(versionDir)) {
33-
FilePath parent = versionDir.getParent();
34-
if (parent != null) {
35-
parent.deleteRecursive();
31+
if (!removeIfExpiredCacheDirectory(library)) {
32+
// Prior to the SECURITY-2586 fix, library caches had a two-level directory structure.
33+
// These caches will never be used again, so we delete any that we find.
34+
for (FilePath version: library.list()) {
35+
if (version.child(LibraryCachingConfiguration.LAST_READ_FILE).exists()) {
36+
library.deleteRecursive();
37+
break;
3638
}
37-
break;
3839
}
3940
}
4041
}
@@ -47,15 +48,14 @@ public LibraryCachingCleanup() {
4748
*/
4849
private boolean removeIfExpiredCacheDirectory(FilePath library) throws IOException, InterruptedException {
4950
final FilePath lastReadFile = new FilePath(library, LibraryCachingConfiguration.LAST_READ_FILE);
50-
if (lastReadFile.exists() && library.withSuffix("-name.txt").exists()) {
51+
if (lastReadFile.exists()) {
5152
ReentrantReadWriteLock retrieveLock = LibraryAdder.getReadWriteLockFor(library.getName());
5253
retrieveLock.writeLock().lockInterruptibly();
5354
try {
5455
if (System.currentTimeMillis() - lastReadFile.lastModified() > TimeUnit.DAYS.toMillis(EXPIRE_AFTER_READ_DAYS)) {
55-
FilePath parent = library.getParent();
56-
if (parent != null) {
57-
parent.deleteRecursive();
58-
}
56+
57+
library.deleteRecursive();
58+
library.withSuffix("-name.txt").delete();
5959
}
6060
} finally {
6161
retrieveLock.writeLock().unlock();

src/main/java/org/jenkinsci/plugins/workflow/libs/LibraryCachingConfiguration.java

Lines changed: 63 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@ public final class LibraryCachingConfiguration extends AbstractDescribableImpl<L
3131
private String excludedVersionsStr;
3232
private String includedVersionsStr;
3333

34-
3534
private static final String VERSIONS_SEPARATOR = " ";
3635
public static final String GLOBAL_LIBRARIES_DIR = "global-libraries-cache";
3736
public static final String LAST_READ_FILE = "last_read";
@@ -40,10 +39,9 @@ public final class LibraryCachingConfiguration extends AbstractDescribableImpl<L
4039
this.refreshTimeMinutes = refreshTimeMinutes;
4140
this.excludedVersionsStr = excludedVersionsStr;
4241
this.includedVersionsStr = includedVersionsStr;
43-
44-
4542
}
4643

44+
4745
public int getRefreshTimeMinutes() {
4846
return refreshTimeMinutes;
4947
}
@@ -59,10 +57,8 @@ public Boolean isRefreshEnabled() {
5957
public String getExcludedVersionsStr() {
6058
return excludedVersionsStr;
6159
}
60+
public String getIncludedVersionsStr() { return includedVersionsStr; }
6261

63-
public String getIncludedVersionsStr() {
64-
return includedVersionsStr;
65-
}
6662

6763
private List<String> getExcludedVersions() {
6864
if (excludedVersionsStr == null) {
@@ -121,61 +117,78 @@ public static FilePath getGlobalLibrariesCacheDir() {
121117
}
122118

123119
@Extension public static class DescriptorImpl extends Descriptor<LibraryCachingConfiguration> {
124-
public FormValidation doClearCache(@QueryParameter String name, @QueryParameter String cachedLibraryRef, @QueryParameter boolean forceDelete) throws InterruptedException {
120+
public FormValidation doClearCache(@QueryParameter String name, @QueryParameter boolean forceDelete) throws InterruptedException {
125121
Jenkins.get().checkPermission(Jenkins.ADMINISTER);
126-
String cacheDirName = null;
122+
127123
try {
128124
if (LibraryCachingConfiguration.getGlobalLibrariesCacheDir().exists()) {
129-
outer: for (FilePath libraryCache : LibraryCachingConfiguration.getGlobalLibrariesCacheDir().listDirectories()) {
130-
for (FilePath libraryNamePath : libraryCache.list("*-name.txt")) {
131-
if (libraryNamePath.readToString().startsWith(name + "@")) {
132-
FilePath libraryCachePath = libraryNamePath.getParent();
133-
if (libraryCachePath != null) {
134-
FilePath versionCachePath = new FilePath(libraryCachePath, libraryNamePath.getName().replace("-name.txt", ""));
135-
LOGGER.log(Level.FINER, "Safe deleting cache for {0}", name);
136-
ReentrantReadWriteLock retrieveLock = LibraryAdder.getReadWriteLockFor(libraryCachePath.getName());
137-
if (forceDelete || retrieveLock.writeLock().tryLock(10, TimeUnit.SECONDS)) {
138-
if (forceDelete) {
139-
LOGGER.log(Level.FINER, "Force deleting cache for {0}", name);
140-
} else {
141-
LOGGER.log(Level.FINER, "Safe deleting cache for {0}", name);
142-
}
143-
try {
144-
if (StringUtils.isNotEmpty(cachedLibraryRef)) {
145-
if (libraryNamePath.readToString().equals(name + "@" + cachedLibraryRef)) {
146-
cacheDirName = name + "@" + cachedLibraryRef;
147-
libraryNamePath.delete();
148-
versionCachePath.deleteRecursive();
149-
break outer;
150-
}
151-
} else {
152-
cacheDirName = name;
153-
libraryCachePath.deleteRecursive();
154-
break outer;
155-
}
156-
} finally {
157-
if (!forceDelete) {
158-
retrieveLock.writeLock().unlock();
159-
}
160-
}
161-
} else {
162-
return FormValidation.error("The cache dir could not be deleted because it is currently being used by another thread. Please try again.");
125+
for (FilePath libraryNamePath : LibraryCachingConfiguration.getGlobalLibrariesCacheDir().list("*-name.txt")) {
126+
// Libraries configured in distinct locations may have the same name. Since only admins are allowed here, this is not a huge issue, but it is probably unexpected.
127+
String cacheName;
128+
try (InputStream stream = libraryNamePath.read()) {
129+
cacheName = IOUtils.toString(stream, StandardCharsets.UTF_8);
130+
}
131+
if (cacheName.equals(name)) {
132+
FilePath libraryCachePath = LibraryCachingConfiguration.getGlobalLibrariesCacheDir()
133+
.child(libraryNamePath.getName().replace("-name.txt", ""));
134+
if (forceDelete) {
135+
LOGGER.log(Level.FINER, "Force deleting cache for {0}", name);
136+
libraryCachePath.deleteRecursive();
137+
libraryNamePath.delete();
138+
} else {
139+
LOGGER.log(Level.FINER, "Safe deleting cache for {0}", name);
140+
ReentrantReadWriteLock retrieveLock = LibraryAdder.getReadWriteLockFor(libraryCachePath.getName());
141+
if (retrieveLock.writeLock().tryLock(10, TimeUnit.SECONDS)) {
142+
try {
143+
libraryCachePath.deleteRecursive();
144+
libraryNamePath.delete();
145+
} finally {
146+
retrieveLock.writeLock().unlock();
163147
}
148+
} else {
149+
return FormValidation.error("The cache dir could not be deleted because it is currently being used by another thread. Please try again.");
164150
}
165151
}
166152
}
167153
}
168154
}
169155
} catch (IOException ex) {
170-
return FormValidation.error(ex, String.format("The cache dir %s was not deleted successfully", cacheDirName));
171-
}
172-
173-
if (cacheDirName == null) {
174-
return FormValidation.ok(String.format("The version %s was not found for library %s.", cachedLibraryRef, name));
175-
} else {
176-
return FormValidation.ok(String.format("The cache dir %s was deleted successfully.", cacheDirName));
156+
return FormValidation.error(ex, "The cache dir was not deleted successfully");
177157
}
158+
return FormValidation.ok("The cache dir was deleted successfully.");
178159
}
160+
}
179161

162+
/**
163+
* Method can be called from an external source to delete cache of a particular version of the shared library
164+
* Example: delete cache from through pipeline script once the build is successful
165+
* @param jenkinsLibrary Name of the shared library
166+
* @param version Name of the version to delete the cache
167+
* @throws IOException
168+
* @throws InterruptedException
169+
*/
170+
public static void deleteLibraryCacheForBranch(String jenkinsLibrary, String version) throws IOException, InterruptedException {
171+
if (LibraryCachingConfiguration.getGlobalLibrariesCacheDir().exists()) {
172+
for (FilePath libraryNamePath : LibraryCachingConfiguration.getGlobalLibrariesCacheDir().list("*-name.txt")) {
173+
String cacheName;
174+
try (InputStream stream = libraryNamePath.read()) {
175+
cacheName = IOUtils.toString(stream, StandardCharsets.UTF_8);
176+
177+
if (cacheName.equals(jenkinsLibrary)) {
178+
FilePath libraryCachePath = LibraryCachingConfiguration.getGlobalLibrariesCacheDir()
179+
.child(libraryNamePath.getName().replace("-name.txt", ""));
180+
ReentrantReadWriteLock retrieveLock = LibraryAdder.getReadWriteLockFor(libraryCachePath.getName());
181+
if (retrieveLock.writeLock().tryLock(10, TimeUnit.SECONDS)) {
182+
try {
183+
libraryCachePath.deleteRecursive();
184+
libraryNamePath.delete();
185+
} finally {
186+
retrieveLock.writeLock().unlock();
187+
}
188+
}
189+
}
190+
}
191+
}
192+
}
180193
}
181-
}
194+
}

src/main/java/org/jenkinsci/plugins/workflow/libs/LibraryRecord.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@
2424

2525
package org.jenkinsci.plugins.workflow.libs;
2626

27-
import java.io.File;
2827
import java.util.Collections;
2928
import java.util.Set;
3029
import java.util.TreeSet;
@@ -63,7 +62,7 @@ public final class LibraryRecord {
6362
this.trusted = trusted;
6463
this.changelog = changelog;
6564
this.cachingConfiguration = cachingConfiguration;
66-
this.directoryName = directoryNameFor(name, String.valueOf(trusted), source) + File.separator + directoryNameFor(version);
65+
this.directoryName = directoryNameFor(name, version, String.valueOf(trusted), source);
6766
}
6867

6968
@Exported

src/main/resources/org/jenkinsci/plugins/workflow/libs/LibraryCachingConfiguration/config.jelly

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -32,15 +32,12 @@ THE SOFTWARE.
3232
<f:textbox />
3333
</f:entry>
3434
<f:entry title="${%Versions to include}" field="includedVersionsStr">
35-
<f:textbox />
35+
<f:textbox />
3636
</f:entry>
3737
<j:if test="${h.hasPermission(app.ADMINISTER)}">
38-
<f:entry title="${%Clear cache for ref}" field="cachedLibraryRef">
39-
<f:textbox />
40-
</f:entry>
41-
<f:entry title="${%Force clear cache}" field="forceDelete">
38+
<f:entry title="${%Force clear cache}" field="forceDelete">
4239
<f:checkbox/>
4340
</f:entry>
44-
<f:validateButton title="${%Clear cache}" progress="${%Clearing...}" method="clearCache" with="name,cachedLibraryRef,forceDelete" />
41+
<f:validateButton title="${%Clear cache}" progress="${%Clearing...}" method="clearCache" with="name,forceDelete" />
4542
</j:if>
4643
</j:jelly>

src/main/resources/org/jenkinsci/plugins/workflow/libs/LibraryCachingConfiguration/help-cachedLibraryRef.html

Lines changed: 0 additions & 3 deletions
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
11
<div>
2-
Space separated list of versions to include to allow caching via substring search using .contains() method. Ex: "release/ master".
2+
Space separated list of versions to include to allow caching via substring search using .contains() method. Ex: "release/ master".
3+
</div>
4+
<div>
5+
Note: Excluded versions will always take precedence over included versions
36
</div>

src/test/java/org/jenkinsci/plugins/workflow/libs/LibraryAdderTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -493,7 +493,7 @@ public void parallelBuildsDontInterfereWithExpiredCache() throws Throwable {
493493
new SCMSourceRetriever(new GitSCMSource(null, sampleRepo.toString(), "", "*", "", true)));
494494
config.setDefaultVersion("master");
495495
config.setImplicit(true);
496-
config.setCachingConfiguration(new LibraryCachingConfiguration(30, null, null));
496+
config.setCachingConfiguration(new LibraryCachingConfiguration(30, null,null));
497497
GlobalLibraries.get().getLibraries().add(config);
498498
WorkflowJob p1 = r.createProject(WorkflowJob.class);
499499
WorkflowJob p2 = r.createProject(WorkflowJob.class);

src/test/java/org/jenkinsci/plugins/workflow/libs/LibraryCachingCleanupTest.java

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -71,16 +71,14 @@ public void smokes() throws Throwable {
7171
assertThat(new File(cache.getRemote()), anExistingDirectory());
7272
// Run LibraryCachingCleanup and show that cache is not deleted.
7373
ExtensionList.lookupSingleton(LibraryCachingCleanup.class).execute(StreamTaskListener.fromStderr());
74-
assertThat(new File(cache.getParent().getRemote()), anExistingDirectory());
7574
assertThat(new File(cache.getRemote()), anExistingDirectory());
7675
assertThat(new File(cache.withSuffix("-name.txt").getRemote()), anExistingFile());
7776
// Run LibraryCachingCleanup after modifying LAST_READ_FILE to be an old date and and show that cache is deleted.
7877
long oldMillis = ZonedDateTime.now().minusDays(LibraryCachingCleanup.EXPIRE_AFTER_READ_DAYS + 1).toInstant().toEpochMilli();
7978
cache.child(LibraryCachingConfiguration.LAST_READ_FILE).touch(oldMillis);
8079
ExtensionList.lookupSingleton(LibraryCachingCleanup.class).execute(StreamTaskListener.fromStderr());
81-
assertThat(new File(cache.getParent().getRemote()), not(anExistingDirectory()));
82-
assertThat(new File(cache.withSuffix("-name.txt").getRemote()), not(anExistingFile()));
83-
80+
assertThat(new File(cache.getRemote()), not(anExistingDirectory()));
81+
assertThat(new File(cache.withSuffix("-name.txt").getRemote()), not(anExistingDirectory()));
8482
}
8583

8684
@Test

0 commit comments

Comments
 (0)