Skip to content

Commit 07e70f2

Browse files
committed
secondary-storage: delete temp directory while deleting entity download
url Signed-off-by: Abhishek Kumar <abhishek.mrt22@gmail.com>
1 parent 1998867 commit 07e70f2

File tree

2 files changed

+106
-0
lines changed

2 files changed

+106
-0
lines changed

services/secondary-storage/server/src/main/java/org/apache/cloudstack/storage/template/UploadManagerImpl.java

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
import javax.naming.ConfigurationException;
3636

3737
import org.apache.cloudstack.storage.resource.SecondaryStorageResource;
38+
import org.apache.commons.lang3.StringUtils;
3839

3940
import com.cloud.agent.api.Answer;
4041
import com.cloud.agent.api.ConvertSnapshotCommand;
@@ -52,7 +53,9 @@
5253
import com.cloud.storage.template.TemplateUploader;
5354
import com.cloud.storage.template.TemplateUploader.Status;
5455
import com.cloud.storage.template.TemplateUploader.UploadCompleteCallback;
56+
import com.cloud.utils.FileUtil;
5557
import com.cloud.utils.NumbersUtil;
58+
import com.cloud.utils.UuidUtils;
5659
import com.cloud.utils.component.ManagerBase;
5760
import com.cloud.utils.exception.CloudRuntimeException;
5861
import com.cloud.utils.script.Script;
@@ -347,6 +350,7 @@ public Answer handleDeleteEntityDownloadURLCommand(DeleteEntityDownloadURLComman
347350
// This is because the ssvm might already be destroyed and the symlinks do not exist.
348351
logger.warn("Error in deleting symlink :" + result);
349352
}
353+
deleteEntitySymlinkRootPathIfNeeded(cmd, linkPath);
350354
}
351355

352356
// If its a volume or archive also delete the Hard link since it was created only for the purpose of download.
@@ -380,6 +384,30 @@ public Answer handleDeleteEntityDownloadURLCommand(DeleteEntityDownloadURLComman
380384
return new Answer(cmd, true, "");
381385
}
382386

387+
protected void deleteEntitySymlinkRootPathIfNeeded(DeleteEntityDownloadURLCommand cmd, String linkPath) {
388+
if (StringUtils.isEmpty(linkPath)) {
389+
return;
390+
}
391+
String[] parts = linkPath.split("/");
392+
if (parts.length == 0) {
393+
return;
394+
}
395+
String rootDir = parts[0];
396+
if (StringUtils.isEmpty(rootDir) || !UuidUtils.isUuid(rootDir)) {
397+
return;
398+
}
399+
logger.info("Deleting symlink root directory: {} for {}", rootDir, cmd.getExtractUrl());
400+
Path rootDirPath = Path.of(BASE_EXTRACT_DIR + rootDir);
401+
String failMsg = "Failed to delete symlink root directory: {} for {}";
402+
try {
403+
if (!FileUtil.deleteRecursively(rootDirPath)) {
404+
logger.warn(failMsg, rootDir, cmd.getExtractUrl());
405+
}
406+
} catch (IOException e) {
407+
logger.warn(failMsg, rootDir, cmd.getExtractUrl(), e);
408+
}
409+
}
410+
383411
private String getInstallPath(String jobId) {
384412
// TODO Auto-generated method stub
385413
return null;
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
// Licensed to the Apache Software Foundation (ASF) under one
2+
// or more contributor license agreements. See the NOTICE file
3+
// distributed with this work for additional information
4+
// regarding copyright ownership. The ASF licenses this file
5+
// to you under the Apache License, Version 2.0 (the
6+
// "License"); you may not use this file except in compliance
7+
// with the License. You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing,
12+
// software distributed under the License is distributed on an
13+
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
// KIND, either express or implied. See the License for the
15+
// specific language governing permissions and limitations
16+
// under the License.
17+
package org.apache.cloudstack.storage.template;
18+
19+
import static org.mockito.ArgumentMatchers.any;
20+
import static org.mockito.Mockito.mock;
21+
import static org.mockito.Mockito.mockStatic;
22+
import static org.mockito.Mockito.never;
23+
import static org.mockito.Mockito.times;
24+
25+
import java.nio.file.Path;
26+
27+
import org.junit.After;
28+
import org.junit.Before;
29+
import org.junit.Test;
30+
import org.junit.runner.RunWith;
31+
import org.mockito.InjectMocks;
32+
import org.mockito.MockedStatic;
33+
import org.mockito.Mockito;
34+
import org.mockito.junit.MockitoJUnitRunner;
35+
36+
import com.cloud.agent.api.storage.DeleteEntityDownloadURLCommand;
37+
import com.cloud.utils.FileUtil;
38+
39+
@RunWith(MockitoJUnitRunner.class)
40+
public class UploadManagerImplTest {
41+
42+
@InjectMocks
43+
UploadManagerImpl uploadManager;
44+
45+
MockedStatic<FileUtil> fileUtilMock;
46+
47+
@Before
48+
public void setup() {
49+
fileUtilMock = mockStatic(FileUtil.class, Mockito.CALLS_REAL_METHODS);
50+
fileUtilMock.when(() -> FileUtil.deleteRecursively(any(Path.class))).thenReturn(true);
51+
}
52+
53+
@After
54+
public void tearDown() {
55+
fileUtilMock.close();
56+
}
57+
58+
@Test
59+
public void doesNotDeleteWhenLinkPathIsEmpty() {
60+
String emptyLinkPath = "";
61+
uploadManager.deleteEntitySymlinkRootPathIfNeeded(mock(DeleteEntityDownloadURLCommand.class), emptyLinkPath);
62+
fileUtilMock.verify(() -> FileUtil.deleteRecursively(any(Path.class)), never());
63+
}
64+
65+
@Test
66+
public void doesNotDeleteWhenRootDirIsNotUuid() {
67+
String invalidLinkPath = "invalidRootDir/file";
68+
uploadManager.deleteEntitySymlinkRootPathIfNeeded(mock(DeleteEntityDownloadURLCommand.class), invalidLinkPath);
69+
fileUtilMock.verify(() -> FileUtil.deleteRecursively(any(Path.class)), never());
70+
}
71+
72+
@Test
73+
public void deletesSymlinkRootDirectoryWhenValidUuid() {
74+
String validLinkPath = "123e4567-e89b-12d3-a456-426614174000/file";
75+
uploadManager.deleteEntitySymlinkRootPathIfNeeded(mock(DeleteEntityDownloadURLCommand.class), validLinkPath);
76+
fileUtilMock.verify(() -> FileUtil.deleteRecursively(any(Path.class)), times(1));
77+
}
78+
}

0 commit comments

Comments
 (0)