Skip to content
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,198 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.hadoop.fs.ozone;

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.fs.TrashPolicyDefault;
import org.apache.hadoop.fs.FileAlreadyExistsException;
import org.apache.hadoop.fs.permission.FsAction;
import org.apache.hadoop.fs.permission.FsPermission;
import org.apache.hadoop.fs.InvalidPathException;
import org.apache.hadoop.hdds.conf.OzoneConfiguration;
import org.apache.hadoop.ozone.OFSPath;
import org.apache.hadoop.ozone.OzoneConsts;
import org.apache.hadoop.ozone.om.helpers.OzoneFSUtils;
import org.apache.hadoop.util.Time;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;


/**
* TrashPolicy for Ozone Specific Trash Operations.
*/
public class TrashPolicyOzone extends TrashPolicyDefault {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this the same as TrashPolicyOzone.java under ozone-manager module?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes it is same as ozone-manager module.
The problem is when client changes trash policy to use ozone trash policy, it can't find TrashPolicyOzone as it's not present inside ozone fs client jar.
For workaround we have to put ozone-manager service jar to make it work.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we move it to hadoop-ozone/common, and let OM's TrashPolicyOzone extend this one? See patch (on top of this PR). Would this work?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@adoroszlai thanks for the suggestion, let me try.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Before fix:
Perform delete and use OzoneTrashPolicy
hdfs dfs -Dfs.trash.classname=org.apache.hadoop.fs.ozone.TrashPolicyOzone -rm ofs://om/vol1/bucket1/key1

  24/11/20 06:40:12 DEBUG fs.FsShell: Error
java.lang.RuntimeException: java.lang.RuntimeException: java.lang.ClassNotFoundException: Class org.apache.hadoop.fs.ozone.TrashPolicyOzone not found
	at org.apache.hadoop.conf.Configuration.getClass(Configuration.java:2734)
	at org.apache.hadoop.fs.TrashPolicy.getInstance(TrashPolicy.java:145)
	at org.apache.hadoop.fs.Trash.<init>(Trash.java:58)
	at org.apache.hadoop.fs.Trash.moveToAppropriateTrash(Trash.java:95)
	at org.apache.hadoop.fs.shell.Delete$Rm.moveToTrash(Delete.java:153)
	at org.apache.hadoop.fs.shell.Delete$Rm.processPath(Delete.java:118)
	at org.apache.hadoop.fs.shell.Command.processPathInternal(Command.java:371)
	at org.apache.hadoop.fs.shell.Command.processPaths(Command.java:334)
	at org.apache.hadoop.fs.shell.Command.processPathArgument(Command.java:307)
	at org.apache.hadoop.fs.shell.Command.processArgument(Command.java:289)
	at org.apache.hadoop.fs.shell.Command.processArguments(Command.java:273)
	at org.apache.hadoop.fs.shell.FsCommand.processRawArguments(FsCommand.java:120)
	at org.apache.hadoop.fs.shell.Command.run(Command.java:180)
	at org.apache.hadoop.fs.FsShell.run(FsShell.java:328)
	at org.apache.hadoop.util.ToolRunner.run(ToolRunner.java:81)
	at org.apache.hadoop.util.ToolRunner.run(ToolRunner.java:95)
	at org.apache.hadoop.fs.FsShell.main(FsShell.java:391)
Caused by: java.lang.RuntimeException: java.lang.ClassNotFoundException: Class org.apache.hadoop.fs.ozone.TrashPolicyOzone not found
	at org.apache.hadoop.conf.Configuration.getClass(Configuration.java:2702)
	at org.apache.hadoop.conf.Configuration.getClass(Configuration.java:2726)
	... 16 more
Caused by: java.lang.ClassNotFoundException: Class org.apache.hadoop.fs.ozone.TrashPolicyOzone not found
	at org.apache.hadoop.conf.Configuration.getClassByName(Configuration.java:2606)
	at org.apache.hadoop.conf.Configuration.getClass(Configuration.java:2700)
	... 17 more
-rm: Fatal internal error

After fix:
24/11/20 08:15:37 INFO ozone.TrashPolicyOzone: Moved: 'ofs://om/vol1/bucket1/key1' to trash at: ofs://om/vol11/bucket1/.Trash/systest/Current/key1

@adoroszlai adoroszlai Nov 20, 2024

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @ashishkumar50 for updating the patch. Sorry about the findbugs failure:

The class name org.apache.hadoop.ozone.om.TrashPolicyOzone shadows the simple name of the superclass org.apache.hadoop.fs.ozone.TrashPolicyOzone

Since the client-side class is new, I guess we can rename it to avoid the problem.

org.apache.hadoop.fs.ozone.TrashPolicyOzone -> org.apache.hadoop.fs.ozone.OzoneTrashPolicy

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @adoroszlai, Renamed class.


private static final Logger LOG =
LoggerFactory.getLogger(TrashPolicyOzone.class);

private static final Path CURRENT = new Path("Current");

private static final FsPermission PERMISSION =
new FsPermission(FsAction.ALL, FsAction.NONE, FsAction.NONE);
private Configuration configuration;

private OzoneConfiguration ozoneConfiguration;

public TrashPolicyOzone() {
}

@Override
public void initialize(Configuration conf, FileSystem fs) {
this.fs = fs;
this.configuration = conf;
ozoneConfiguration = OzoneConfiguration.of(this.configuration);

}

@Override
public boolean moveToTrash(Path path) throws IOException {
if (validatePath(path)) {
if (!isEnabled()) {
return false;
}

if (!path.isAbsolute()) { // make path absolute
path = new Path(fs.getWorkingDirectory(), path);
}

// check that path exists
fs.getFileStatus(path);
String qpath = fs.makeQualified(path).toString();

Path trashRoot = fs.getTrashRoot(path);
Path trashCurrent = new Path(trashRoot, CURRENT);
if (qpath.startsWith(trashRoot.toString())) {
return false; // already in trash
}

if (trashRoot.getParent().toString().startsWith(qpath)) {
throw new IOException("Cannot move \"" + path
+ "\" to the trash, as it contains the trash");
}

Path trashPath;
Path baseTrashPath;
if (fs.getUri().getScheme().equals(OzoneConsts.OZONE_OFS_URI_SCHEME)) {
OFSPath ofsPath = new OFSPath(path, ozoneConfiguration);
// trimming volume and bucket in order to be compatible with o3fs
// Also including volume and bucket name in the path is redundant as
// the key is already in a particular volume and bucket.
Path trimmedVolumeAndBucket =
new Path(OzoneConsts.OZONE_URI_DELIMITER
+ ofsPath.getKeyName());
trashPath = makeTrashRelativePath(trashCurrent, trimmedVolumeAndBucket);
baseTrashPath = makeTrashRelativePath(trashCurrent,
trimmedVolumeAndBucket.getParent());
} else {
trashPath = makeTrashRelativePath(trashCurrent, path);
baseTrashPath = makeTrashRelativePath(trashCurrent, path.getParent());
}

IOException cause = null;

// try twice, in case checkpoint between the mkdirs() & rename()
for (int i = 0; i < 2; i++) {
try {
if (!fs.mkdirs(baseTrashPath, PERMISSION)) { // create current
LOG.warn("Can't create(mkdir) trash directory: " + baseTrashPath);
return false;
}
} catch (FileAlreadyExistsException e) {
// find the path which is not a directory, and modify baseTrashPath
// & trashPath, then mkdirs
Path existsFilePath = baseTrashPath;
while (!fs.exists(existsFilePath)) {
existsFilePath = existsFilePath.getParent();
}
baseTrashPath = new Path(baseTrashPath.toString()
.replace(existsFilePath.toString(),
existsFilePath.toString() + Time.now()));
trashPath = new Path(baseTrashPath, trashPath.getName());
// retry, ignore current failure
--i;
continue;
} catch (IOException e) {
LOG.warn("Can't create trash directory: " + baseTrashPath, e);
cause = e;
break;
}
try {
// if the target path in Trash already exists, then append with
// a current time in millisecs.
String orig = trashPath.toString();

while (fs.exists(trashPath)) {
trashPath = new Path(orig + Time.now());
}

// move to current trash
boolean renamed = fs.rename(path, trashPath);
if (!renamed) {
LOG.error("Failed to move to trash: {}", path);
throw new IOException("Failed to move to trash: " + path);
}
LOG.info("Moved: '" + path + "' to trash at: " + trashPath);
return true;
} catch (IOException e) {
cause = e;
}
}
throw (IOException) new IOException("Failed to move to trash: " + path)
.initCause(cause);
}
return false;
}

private boolean validatePath(Path path) throws IOException {
String key = path.toUri().getPath();
// Check to see if bucket is path item to be deleted.
// Cannot moveToTrash if bucket is deleted,
// return error for this condition
OFSPath ofsPath = new OFSPath(key.substring(1), ozoneConfiguration);
if (path.isRoot() || ofsPath.isBucket()) {
throw new IOException("Recursive rm of bucket "
+ path + " not permitted");
}

Path trashRoot = this.fs.getTrashRoot(path);

LOG.debug("Key path to moveToTrash: {}", key);
String trashRootKey = trashRoot.toUri().getPath();
LOG.debug("TrashrootKey for moveToTrash: {}", trashRootKey);

if (!OzoneFSUtils.isValidName(key)) {
throw new InvalidPathException("Invalid path Name " + key);
}
// first condition tests when length key is <= length trash
// and second when length key > length trash
if ((key.contains(this.fs.TRASH_PREFIX)) && (trashRootKey.startsWith(key))
|| key.startsWith(trashRootKey)) {
return false;
}
return true;
}

private Path makeTrashRelativePath(Path basePath, Path rmFilePath) {
return Path.mergePaths(basePath, rmFilePath);
}

}