-
Notifications
You must be signed in to change notification settings - Fork 1.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
CHE-2435: Add component for clean up workspace folder after remove workspace. #2544
Conversation
* | ||
* @author Alexander Andrienko | ||
*/ | ||
public interface WorkspaceProjectStorageCleaner { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
WorkspaceFileStorageCleaner
or WorkspaceFSStorageCleaner
looks somehow better for me. WDYT?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, it's make sense. I'll renamed it soon. Thank you.
Build success. https://ci.codenvycorp.com/job/che-pullrequests-build/476/ |
* @throws TimeoutException | ||
* if process gets more time then defined by {@code timeout} | ||
*/ | ||
public static Process execute(String[] commandLine, int timeout, TimeUnit timeUnit, LineConsumer outputConsumer) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If process is ended/killed by the end of execution of this method what for it returns process object?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
throws InterruptedException or IOExeption.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see that it is needed to process return code
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Overall OK, but needs some fixes
ProcessBuilder pb = new ProcessBuilder(commandLine).redirectErrorStream(true); | ||
|
||
// process will be stopped after timeout | ||
Watchdog watcher = new Watchdog(timeout, timeUnit); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please use name for watchdog thread - it will help understand thread dump in case of troubles with the server.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This class is utility.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ok
// consume logs until process ends | ||
process(process, outputConsumer); | ||
|
||
process.waitFor(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please use timeout here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK
process.waitFor(); | ||
|
||
if (isTimeoutExceeded.get()) { | ||
throw new TimeoutException(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This exception would be clearer if it had a message on what has happened.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This class is utility.
@@ -326,6 +329,9 @@ public void removeWorkspace(String workspaceId) throws ConflictException, Server | |||
if (runtimes.hasRuntime(workspaceId)) { | |||
throw new ConflictException("The workspace '" + workspaceId + "' is currently running and cannot be removed."); | |||
} | |||
|
|||
workspaceProjectStorageCleaner.remove(workspaceId); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If it is clearer then method name clear
looks more natural for me than remove
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
@Override | ||
public void remove(String workspaceId) { | ||
try { | ||
File workspaceStorage = new File(workspaceFolderPathProvider.getPath(workspaceId)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Have you covered the case when workspace are sharing single folder as a storage of projects?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, I''ll look at this case.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
@evoevodin Please take a look |
QA is needed |
512b665
to
6860d92
Compare
Build success. https://ci.codenvycorp.com/job/che-pullrequests-build/533/ |
6860d92
to
31b0078
Compare
Build success. https://ci.codenvycorp.com/job/che-pullrequests-build/538/ |
public interface WorkspaceFSStorageCleaner { | ||
|
||
/** | ||
* Removes workspace project folder with all data by {@code workspaceId}. Note: all user's projects will be deleted. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
From the definition it's not clear for me what is workspace project folder? Does the workspace have the only one project?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Docs changed
package org.eclipse.che.api.workspace.server; | ||
|
||
/** | ||
* This component removes workspace folder from workspace storage. It's used for "delete workspace" operation |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's used for "delete workspace" operation
If this interface exists for certain needs please document this needs better, as it's not clear what the phrase above means. As far as i understand we need this interface for cleaning file system resources after workspace is removed?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This interface need to realize delete workspace folder with projects after remove workspace, I'll fix doc.
String workspacePath = workspaceFolderPathProvider.getPath(workspaceId); | ||
File workspaceStorage = new File(workspacePath); | ||
if (!workspacePath.equals(hostProjectsFolder) && workspaceStorage.exists()) { | ||
FileCleaner.addFile(workspaceStorage); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are you sure that it's expected behaviour, should it be asynchronous? If yes, do you think it's better to document it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think remove workspace folder can be potentionally long time operation(if workspace contains a lot of files and server computer is busy to done this task quickly), so yes I think it should be asynchronous operation. Thank you, I will document it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Documented in the interface WorkspaceFSStorageCleaner
if (!workspacePath.equals(hostProjectsFolder) && workspaceStorage.exists()) { | ||
FileCleaner.addFile(workspaceStorage); | ||
} | ||
} catch (IOException e) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it's better throw the exception from the method, document it, and catch it on callers side. The motivation is that if we need to add another cleaner, that cleaner will have to implement a batch of the same logic, like exception handling and it's processing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think that failed clean up workspace folder is reason to cancel delete workspace operation. That why I handle exception here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But it is still better to handle batch of delete operations on higher level and decide there completion of which operation is optional and which not. +1 to what Eugene said
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please comment on my comments/questions
// consume logs until process ends | ||
process(process, outputConsumer); | ||
|
||
process.waitFor(timeout, timeUnit); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What if instead of using Watchdog
we consume all the logs in separate thread and wait for pocess to end or reach timeout, i mean we can use the value returned from the waitFor
method to achive this goal,
e.g.
if (!process.waitFor(timeout, timeUnit)) {
ProcessUtil.kill(process);
}
@garagatyi, @AndrienkoAleksandr what do you think?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you @evoevodin, I think we can do it, but this only another way of implementation, it has not any advantages. And current version of code is already tested, so I don't think that we should spend time to another implementation without good reason.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@evoevodin I think it can be really clean solution in term of code.
WDYT about usage of ComplletableFuture.runAsync in such cases? Without that addition of executor and everything like that would not make it simpler.
Build # 574 - FAILED Please check console output at https://ci.codenvycorp.com/job/che-pullrequests-build/574/ to view the results. |
@@ -326,6 +329,9 @@ public void removeWorkspace(String workspaceId) throws ConflictException, Server | |||
if (runtimes.hasRuntime(workspaceId)) { | |||
throw new ConflictException("The workspace '" + workspaceId + "' is currently running and cannot be removed."); | |||
} | |||
|
|||
workspaceFSStorageCleaner.clear(workspaceId); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please cover in tests
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
final boolean createSnapshot = autoSnapshotAttr == null ? defaultAutoSnapshot : parseBoolean(autoSnapshotAttr); | ||
boolean createSnapshot; | ||
if (workspace.isTemporary()) { | ||
createSnapshot = false; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is very nice that you added that fix but , please, cover it in tests
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
Build # 592 - FAILED Please check console output at https://ci.codenvycorp.com/job/che-pullrequests-build/592/ to view the results. |
Build # 602 - FAILED Please check console output at https://ci.codenvycorp.com/job/che-pullrequests-build/602/ to view the results. |
Build # 609 - FAILED Please check console output at https://ci.codenvycorp.com/job/che-pullrequests-build/609/ to view the results. |
String workspacePath = workspaceFolderPathProvider.getPathByName(workspace.getConfig().getName()); | ||
File workspaceStorage = new File(workspacePath); | ||
if (!workspacePath.equals(hostProjectsFolder) && workspaceStorage.exists()) { | ||
FileCleaner.addFile(workspaceStorage); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe it would be better to use not bad alternative: IoUtil.deleteRecursive(workspaceStorage); What do you think @garagatyi @evoevodin ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For now class LocalWorkspaceFilesCleaner.java works synchronously so I used IoUtil.deleteRecursive
Build # 643 - FAILED Please check console output at https://ci.codenvycorp.com/job/che-pullrequests-build/643/ to view the results. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good job, please fix some small remarks
* @throws TimeoutException | ||
* if process gets more time then defined by {@code timeout} | ||
*/ | ||
public static Process execute(String[] commandLine, int timeout, TimeUnit timeUnit, LineConsumer outputConsumer) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i'd probably rename it to executeAndWait
as the behaviour is different from the existing execute
method
// consume logs until process ends | ||
process(process, outputConsumer); | ||
} catch (IOException e) { | ||
LOG.error(format("Failed to complete process '%s'.", Joiner.on(" ").join(commandLine)), e); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
let's change the exception method to something like Failed to complete reading of the process '%s' output due to occurred error
}); | ||
|
||
if (!process.waitFor(timeout, timeUnit)) { | ||
ProcessUtil.kill(process); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
let's wrap process kill to ensure that user gets TimeoutException
e.g.
try {
ProcessUtil.kill(process);
} catch (RuntimeException x) {
LOG.error("An error occurred while killing process '{}'", Joiner.on(" ").join(commandLine));
}
@Inject | ||
public RemoveWorkspaceListener(EventService eventService, WorkspaceFilesCleaner workspaceFilesCleaner) { | ||
this.workspaceFilesCleaner = workspaceFilesCleaner; | ||
eventService.subscribe(this); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
subscribe on events in @PostConstruct
try { | ||
workspaceFilesCleaner.clear(workspace); | ||
} catch (IOException | ServerException e) { | ||
LOG.error("Failed to delete folder for workspace with id: '{}'. Cause: '{}'", workspace.getId(), e.getMessage()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
consider a different message, less concrete, let's say it's Failed to remove workspace files
Build # 665 - FAILED Please check console output at https://ci.codenvycorp.com/job/che-pullrequests-build/665/ to view the results. |
3ce3b40
to
3a184b4
Compare
Build # 695 - FAILED Please check console output at https://ci.codenvycorp.com/job/che-pullrequests-build/695/ to view the results. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good!
Signed-off-by: Aleksandr Andrienko <[email protected]>
3a184b4
to
26dcfe5
Compare
Build # 734 - FAILED Please check console output at https://ci.codenvycorp.com/job/che-pullrequests-build/734/ to view the results. |
…he#2544) Signed-off-by: Aleksandr Andrienko <[email protected]>
What does this PR do?
Add component for clean up workspace folder after remove workspace.
What issues does this PR fix or reference?
#2435
PR type
Minor change checklist
Major change checklist
@garagatyi review please.
Signed-off-by: Aleksandr Andrienko [email protected]