smengcl commented on code in PR #4246:
URL: https://github.com/apache/ozone/pull/4246#discussion_r1138402524


##########
hadoop-ozone/ozonefs-common/src/main/java/org/apache/hadoop/fs/ozone/BasicRootedOzoneFileSystem.java:
##########
@@ -659,22 +659,27 @@ public boolean delete(Path f, boolean recursive) throws 
IOException {
         }
       }
 
+      // handling posix symlink delete behaviours
+      // i.) rm [-r] <symlink path>, delete symlink not target bucket contents
+      // ii.) rm -r <symlink path>/, delete target bucket contents not symlink
+      boolean handleTrailingSlash = false;
       boolean isBucketLink = false;
       // check for bucket link
       if (ofsPath.isBucket()) {
         isBucketLink = adapterImpl.getBucket(ofsPath, false)
             .isLink();
+        handleTrailingSlash = f.toString().endsWith(OZONE_URI_DELIMITER);
       }
 
       // if link, don't delete contents
-      if (isBucketLink) {
+      if (isBucketLink && !handleTrailingSlash) {
         result = true;
       } else {
         result = innerDelete(f, recursive);
       }
 
       // Handle delete bucket
-      if (ofsPath.isBucket()) {
+      if (ofsPath.isBucket() && !handleTrailingSlash) {

Review Comment:
   Same here



##########
hadoop-ozone/ozonefs-common/src/main/java/org/apache/hadoop/fs/ozone/BasicRootedOzoneFileSystem.java:
##########
@@ -659,22 +659,27 @@ public boolean delete(Path f, boolean recursive) throws 
IOException {
         }
       }
 
+      // handling posix symlink delete behaviours
+      // i.) rm [-r] <symlink path>, delete symlink not target bucket contents
+      // ii.) rm -r <symlink path>/, delete target bucket contents not symlink
+      boolean handleTrailingSlash = false;
       boolean isBucketLink = false;
       // check for bucket link
       if (ofsPath.isBucket()) {
         isBucketLink = adapterImpl.getBucket(ofsPath, false)
             .isLink();
+        handleTrailingSlash = f.toString().endsWith(OZONE_URI_DELIMITER);
       }
 
       // if link, don't delete contents
-      if (isBucketLink) {
+      if (isBucketLink && !handleTrailingSlash) {

Review Comment:
   Need to update the comment above accordingly



##########
hadoop-ozone/ozonefs-common/src/main/java/org/apache/hadoop/fs/ozone/OzoneDelete.java:
##########
@@ -0,0 +1,206 @@
+/*
+ * 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
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * 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 java.io.FileNotFoundException;
+import java.io.IOException;
+import java.net.URI;
+import java.util.LinkedList;
+import java.util.List;
+
+import org.apache.hadoop.classification.InterfaceAudience;
+import org.apache.hadoop.classification.InterfaceStability;
+import org.apache.hadoop.fs.ContentSummary;
+import org.apache.hadoop.fs.Path;
+import org.apache.hadoop.fs.PathIOException;
+import org.apache.hadoop.fs.PathIsDirectoryException;
+import org.apache.hadoop.fs.PathNotFoundException;
+import org.apache.hadoop.fs.Trash;
+import org.apache.hadoop.fs.shell.CommandFactory;
+import org.apache.hadoop.fs.shell.CommandFormat;
+import org.apache.hadoop.fs.shell.FsCommand;
+import org.apache.hadoop.fs.shell.PathData;
+import org.apache.hadoop.util.ToolRunner;
+import static 
org.apache.hadoop.fs.CommonConfigurationKeysPublic.HADOOP_SHELL_SAFELY_DELETE_LIMIT_NUM_FILES;
+import static 
org.apache.hadoop.fs.CommonConfigurationKeysPublic.HADOOP_SHELL_SAFELY_DELETE_LIMIT_NUM_FILES_DEFAULT;
+import static org.apache.hadoop.ozone.OzoneConsts.OZONE_URI_DELIMITER;
+
+/**
+ * Classes that delete paths.
+ */
[email protected]
[email protected]
+
+public final class OzoneDelete {

Review Comment:
   What about `OzoneFsDelete`?
   ```suggestion
   public final class OzoneFsDelete {
   ```



##########
hadoop-ozone/ozonefs-common/src/main/java/org/apache/hadoop/fs/ozone/OzoneDelete.java:
##########
@@ -0,0 +1,206 @@
+/*
+ * 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
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * 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 java.io.FileNotFoundException;
+import java.io.IOException;
+import java.net.URI;
+import java.util.LinkedList;
+import java.util.List;
+
+import org.apache.hadoop.classification.InterfaceAudience;
+import org.apache.hadoop.classification.InterfaceStability;
+import org.apache.hadoop.fs.ContentSummary;
+import org.apache.hadoop.fs.Path;
+import org.apache.hadoop.fs.PathIOException;
+import org.apache.hadoop.fs.PathIsDirectoryException;
+import org.apache.hadoop.fs.PathNotFoundException;
+import org.apache.hadoop.fs.Trash;
+import org.apache.hadoop.fs.shell.CommandFactory;
+import org.apache.hadoop.fs.shell.CommandFormat;
+import org.apache.hadoop.fs.shell.FsCommand;
+import org.apache.hadoop.fs.shell.PathData;
+import org.apache.hadoop.util.ToolRunner;
+import static 
org.apache.hadoop.fs.CommonConfigurationKeysPublic.HADOOP_SHELL_SAFELY_DELETE_LIMIT_NUM_FILES;
+import static 
org.apache.hadoop.fs.CommonConfigurationKeysPublic.HADOOP_SHELL_SAFELY_DELETE_LIMIT_NUM_FILES_DEFAULT;
+import static org.apache.hadoop.ozone.OzoneConsts.OZONE_URI_DELIMITER;
+
+/**
+ * Classes that delete paths.
+ */
[email protected]
[email protected]
+
+public final class OzoneDelete {
+
+  private OzoneDelete() {
+  }
+
+  public static void registerCommands(CommandFactory factory) {
+    factory.addClass(OzoneDelete.Rm.class, "-rm");
+    factory.addClass(OzoneDelete.Rmr.class, "-rmr");
+  }
+
+  /** remove non-directory paths. */
+  public static class Rm extends FsCommand {
+    public static final String NAME = "rm";
+    public static final String USAGE = "[-f] [-r|-R] [-skipTrash] " +
+        "[-safely] <src> ...";
+    public static final String DESCRIPTION =
+        "Delete all files that match the specified file pattern. " +
+            "Equivalent to the Unix command \"rm <src>\"\n" +
+            "-f: If the file does not exist, do not display a diagnostic " +
+            "message or modify the exit status to reflect an error.\n" +
+            "-[rR]:  Recursively deletes directories.\n" +
+            "-skipTrash: option bypasses trash, if enabled, and immediately " +
+            "deletes <src>.\n" +
+            "-safely: option requires safety confirmation, if enabled, " +
+            "requires confirmation before deleting large directory with more " 
+
+            "than <hadoop.shell.delete.limit.num.files> files. Delay is " +
+            "expected when walking over large directory recursively to count " 
+
+            "the number of files to be deleted before the confirmation.\n";
+
+    private boolean skipTrash = false;
+    private boolean deleteDirs = false;
+    private boolean ignoreFNF = false;
+    private boolean safeDelete = false;
+    private boolean trailing = false;
+
+    @Override
+    protected void processOptions(LinkedList<String> args) throws IOException {
+      CommandFormat cf = new CommandFormat(
+          1, Integer.MAX_VALUE, "f", "r", "R", "skipTrash", "safely");
+      cf.parse(args);
+      ignoreFNF = cf.getOpt("f");
+      deleteDirs = cf.getOpt("r") || cf.getOpt("R");
+      skipTrash = cf.getOpt("skipTrash");
+      safeDelete = cf.getOpt("safely");
+    }
+
+    @Override
+    protected List<PathData> expandArgument(String arg) throws IOException {
+      try {
+        // handle trailing slash for symlinks
+        if (arg.endsWith(OZONE_URI_DELIMITER)) {
+          trailing = true;
+        }
+        return super.expandArgument(arg);
+      } catch (PathNotFoundException e) {
+        if (!ignoreFNF) {
+          throw e;
+        }
+        // prevent -f on a non-existent glob from failing
+        return new LinkedList<PathData>();
+      }
+    }
+
+    @Override
+    protected void processNonexistentPath(PathData item) throws IOException {
+      if (!ignoreFNF) {
+        super.processNonexistentPath(item);
+      }
+    }
+
+    @Override
+    protected void processPath(PathData item) throws IOException {
+      boolean isSymlink = false;
+      if (item.fs.supportsSymlinks()) {
+        isSymlink = item.fs.getLinkTarget(item.path) != item.path;
+      }
+      // support posix rm of symlink in addition to rm -r for directories
+      if (item.stat.isDirectory() && !deleteDirs && !isSymlink) {
+        throw new PathIsDirectoryException(item.toString());
+      }
+      // support posix symlink delete with trailing slash 'rm <path symlink>/'
+      // deletes contents of symlink bucket and retains symlink
+      Path path = item.path;
+      if (isSymlink && trailing) {
+        path = new Path(URI.create(path.toString() + OZONE_URI_DELIMITER));
+      }
+      // TODO: if the user wants the trash to be used but there is any
+      // problem (ie. creating the trash dir, moving the item to be deleted,
+      // etc), then the path will just be deleted because moveToTrash returns
+      // false and it falls thru to fs.delete.  this doesn't seem right
+      if (moveToTrash(item) || !canBeSafelyDeleted(item)) {
+        return;
+      }

Review Comment:
   I'm trying to understand this TODO here.
   
   I think in this case (where `skipTrash == false` but moving to trash has 
issues) we should just return error to the user. So that they either need to 
deal with that trash issue first (maybe some ACL issue), or set `-skipTrash`.
   
   I don't think falling back to to `skipTrash` would be the intended user 
behavior, cause they could lose keys when they think those would be moved to 
the trash.
   
   Also it looks like `moveToTrash()` is already throwing exception 
appropriately? Do we need extra handling here? If not we can just fix the 
comment.



##########
hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/fs/ozone/TestRootedOzoneFileSystem.java:
##########
@@ -1509,6 +1511,190 @@ public void testDeleteVolumeBucketAndKey() throws 
IOException {
     Assert.assertFalse(volumeExist(volumeStr3));
   }
 
+  private void createSymlinkSrcDestPaths(String srcVol,
+      String srcBucket, String destVol, String destBucket) throws IOException {
+    // src srcVol/srcBucket
+    Path volumeSrcPath = new Path(OZONE_URI_DELIMITER + srcVol);
+    Path bucketSrcPath = Path.mergePaths(volumeSrcPath,
+        new Path(OZONE_URI_DELIMITER + srcBucket));
+    fs.mkdirs(volumeSrcPath);
+    OzoneVolume volume = objectStore.getVolume(srcVol);
+    Assert.assertEquals(srcVol, volume.getName());
+    fs.mkdirs(bucketSrcPath);
+    OzoneBucket bucket = volume.getBucket(srcBucket);
+    Assert.assertEquals(srcBucket, bucket.getName());
+
+    // dest link destVol/destBucket -> srcVol/srcBucket
+    Path volumeLinkPath = new Path(OZONE_URI_DELIMITER + destVol);
+    fs.mkdirs(volumeLinkPath);
+    volume = objectStore.getVolume(destVol);
+    Assert.assertEquals(destVol, volume.getName());
+    createLinkBucket(destVol, destBucket, srcVol, srcBucket);
+  }
+
+  @Test
+  public void testSymlinkList() throws Exception {
+    OzoneFsShell shell = new OzoneFsShell(conf);
+    // setup symlink, destVol/destBucket -> srcVol/srcBucket
+    String srcVolume = getRandomNonExistVolumeName();
+    final String srcBucket = "bucket";
+    String destVolume = getRandomNonExistVolumeName();
+    createSymlinkSrcDestPaths(srcVolume, srcBucket, destVolume, srcBucket);
+
+    try {
+      // test symlink -ls -R destVol/destBucket -> srcVol/srcBucket
+      // srcBucket no keys
+      // run toolrunner ozone fs shell commands
+      try (GenericTestUtils.SystemOutCapturer capture =
+               new GenericTestUtils.SystemOutCapturer()) {
+        String linkPathStr = rootPath + destVolume;
+        ToolRunner.run(shell, new String[]{"-ls", "-R", linkPathStr});
+        Assert.assertTrue(capture.getOutput().contains("drwxrwxrwx"));
+        Assert.assertTrue(capture.getOutput().contains(linkPathStr +
+            OZONE_URI_DELIMITER + srcBucket));
+      } finally {
+        shell.close();
+      }
+
+      // add key in source bucket
+      final String key = "object-dir/object-name1";
+      try (OzoneOutputStream outputStream = objectStore.getVolume(srcVolume)
+          .getBucket(srcBucket)
+          .createKey(key, 1)) {
+        outputStream.write(RandomUtils.nextBytes(1));
+      }
+      Assert.assertEquals(objectStore.getVolume(srcVolume)
+          .getBucket(srcBucket).getKey(key).getName(), key);
+
+      // test ls -R /destVol/destBucket, srcBucket with key (non-empty)
+      try (GenericTestUtils.SystemOutCapturer capture =
+               new GenericTestUtils.SystemOutCapturer()) {
+        String linkPathStr = rootPath + destVolume;
+        ToolRunner.run(shell, new String[]{"-ls", "-R",
+            linkPathStr + OZONE_URI_DELIMITER + srcBucket});
+        Assert.assertTrue(capture.getOutput().contains("drwxrwxrwx"));
+        Assert.assertTrue(capture.getOutput().contains(linkPathStr +
+            OZONE_URI_DELIMITER + srcBucket));
+        Assert.assertTrue(capture.getOutput().contains("-rw-rw-rw-"));
+        Assert.assertTrue(capture.getOutput().contains(linkPathStr +
+            OZONE_URI_DELIMITER + srcBucket + OZONE_URI_DELIMITER + key));
+      } finally {
+        shell.close();
+      }
+    } finally {
+      // cleanup; note must delete link before link src bucket
+      // due to bug - HDDS-7884
+      fs.delete(new Path(OZONE_URI_DELIMITER + destVolume +
+          OZONE_URI_DELIMITER + srcBucket));
+      fs.delete(new Path(OZONE_URI_DELIMITER + srcVolume), true);
+      fs.delete(new Path(OZONE_URI_DELIMITER + destVolume), true);
+    }
+  }
+
+  @Test
+  public void testSymlinkPosixDelete() throws Exception {
+    OzoneFsShell shell = new OzoneFsShell(conf);
+    // setup symlink, destVol/destBucket -> srcVol/srcBucket
+    String srcVolume = getRandomNonExistVolumeName();
+    final String srcBucket = "bucket";
+    String destVolume = getRandomNonExistVolumeName();
+    createSymlinkSrcDestPaths(srcVolume, srcBucket, destVolume, srcBucket);
+
+    try {
+      // test symlink destVol/destBucket -> srcVol/srcBucket exists
+      Assert.assertTrue(fs.exists(new Path(OZONE_URI_DELIMITER +
+          destVolume + OZONE_URI_DELIMITER + srcBucket)));
+
+      // add key to srcBucket
+      final String key = "object-dir/object-name1";
+      try (OzoneOutputStream outputStream = objectStore.getVolume(srcVolume)
+          .getBucket(srcBucket)
+          .createKey(key, 1)) {
+        outputStream.write(RandomUtils.nextBytes(1));
+      }
+      Assert.assertEquals(objectStore.getVolume(srcVolume)
+          .getBucket(srcBucket).getKey(key).getName(), key);
+
+      // test symlink -rm destVol/destBucket -> srcVol/srcBucket
+      // should delete only link, srcBucket and key unaltered
+      // run toolrunner ozone fs shell commands
+      try {
+        String linkPathStr = rootPath + destVolume + OZONE_URI_DELIMITER +
+            srcBucket;
+        int res = ToolRunner.run(shell, new String[]{"-rm", "-skipTrash",
+            linkPathStr});
+        Assert.assertEquals(0, res);
+
+        try {
+          objectStore.getVolume(destVolume).getBucket(destVolume);
+          Assert.fail("Bucket should not exist, should throw OMException");
+        } catch (OMException ex) {
+          Assert.assertEquals(BUCKET_NOT_FOUND, ex.getResult());
+        }
+
+        Assert.assertEquals(srcBucket, objectStore.getVolume(srcVolume)
+            .getBucket(srcBucket).getName());
+        Assert.assertEquals(key, objectStore.getVolume(srcVolume)
+            .getBucket(srcBucket).getKey(key).getName());
+
+        // re-create symlink
+        createLinkBucket(destVolume, srcBucket, srcVolume, srcBucket);
+        Assert.assertTrue(fs.exists(new Path(OZONE_URI_DELIMITER +
+            destVolume + OZONE_URI_DELIMITER + srcBucket)));
+
+        // test symlink -rm -R -f destVol/destBucket/ -> srcVol/srcBucket
+        // should delete only link contents (src bucket contents),
+        // link and srcBucket unaltered
+        // run toolrunner ozone fs shell commands
+        linkPathStr = rootPath + destVolume + OZONE_URI_DELIMITER + srcBucket;
+        res = ToolRunner.run(shell, new String[]{"-rm", "-skipTrash",
+            "-f", "-R", linkPathStr + OZONE_URI_DELIMITER});
+        Assert.assertEquals(0, res);
+
+        Assert.assertEquals(srcBucket, objectStore.getVolume(destVolume)
+            .getBucket(srcBucket).getName());
+        Assert.assertEquals(true, objectStore.getVolume(destVolume)
+            .getBucket(srcBucket).isLink());
+        Assert.assertEquals(srcBucket, objectStore.getVolume(srcVolume)
+            .getBucket(srcBucket).getName());
+        try {
+          objectStore.getVolume(srcVolume).getBucket(srcBucket).getKey(key);
+          Assert.fail("Key should be deleted under srcBucket, " +
+              "OMException expected");
+        } catch (OMException ex) {
+          Assert.assertEquals(KEY_NOT_FOUND, ex.getResult());
+        }
+
+        // test symlink -rm -R -f destVol/destBucket -> srcVol/srcBucket
+        // should delete only link
+        // run toolrunner ozone fs shell commands
+        linkPathStr = rootPath + destVolume + OZONE_URI_DELIMITER + srcBucket;
+        res = ToolRunner.run(shell, new String[]{"-rm", "-skipTrash",
+            "-f", "-R", linkPathStr});
+        Assert.assertEquals(0, res);
+
+        Assert.assertEquals(srcBucket, objectStore.getVolume(srcVolume)
+            .getBucket(srcBucket).getName());

Review Comment:
   Can also check `/srcVolume/srcBucket/key` existence



##########
hadoop-ozone/ozonefs-common/src/main/java/org/apache/hadoop/fs/ozone/OzoneFsShell.java:
##########
@@ -58,6 +59,8 @@ protected void registerCommands(CommandFactory factory) {
     // commands, and then this method can be abstract
     if (this.getClass().equals(OzoneFsShell.class)) {
       factory.registerCommands(FsCommand.class);
+      // ozone delete rm command registration superscedes fs delete

Review Comment:
   nit
   ```suggestion
         // ozone delete rm command registration. supercedes fs delete
   ```



##########
hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/fs/ozone/TestRootedOzoneFileSystem.java:
##########
@@ -1509,6 +1511,190 @@ public void testDeleteVolumeBucketAndKey() throws 
IOException {
     Assert.assertFalse(volumeExist(volumeStr3));
   }
 
+  private void createSymlinkSrcDestPaths(String srcVol,
+      String srcBucket, String destVol, String destBucket) throws IOException {
+    // src srcVol/srcBucket
+    Path volumeSrcPath = new Path(OZONE_URI_DELIMITER + srcVol);
+    Path bucketSrcPath = Path.mergePaths(volumeSrcPath,
+        new Path(OZONE_URI_DELIMITER + srcBucket));
+    fs.mkdirs(volumeSrcPath);
+    OzoneVolume volume = objectStore.getVolume(srcVol);
+    Assert.assertEquals(srcVol, volume.getName());
+    fs.mkdirs(bucketSrcPath);
+    OzoneBucket bucket = volume.getBucket(srcBucket);
+    Assert.assertEquals(srcBucket, bucket.getName());
+
+    // dest link destVol/destBucket -> srcVol/srcBucket
+    Path volumeLinkPath = new Path(OZONE_URI_DELIMITER + destVol);
+    fs.mkdirs(volumeLinkPath);
+    volume = objectStore.getVolume(destVol);
+    Assert.assertEquals(destVol, volume.getName());
+    createLinkBucket(destVol, destBucket, srcVol, srcBucket);
+  }
+
+  @Test
+  public void testSymlinkList() throws Exception {
+    OzoneFsShell shell = new OzoneFsShell(conf);
+    // setup symlink, destVol/destBucket -> srcVol/srcBucket
+    String srcVolume = getRandomNonExistVolumeName();
+    final String srcBucket = "bucket";
+    String destVolume = getRandomNonExistVolumeName();
+    createSymlinkSrcDestPaths(srcVolume, srcBucket, destVolume, srcBucket);
+
+    try {
+      // test symlink -ls -R destVol/destBucket -> srcVol/srcBucket
+      // srcBucket no keys
+      // run toolrunner ozone fs shell commands
+      try (GenericTestUtils.SystemOutCapturer capture =
+               new GenericTestUtils.SystemOutCapturer()) {
+        String linkPathStr = rootPath + destVolume;
+        ToolRunner.run(shell, new String[]{"-ls", "-R", linkPathStr});
+        Assert.assertTrue(capture.getOutput().contains("drwxrwxrwx"));
+        Assert.assertTrue(capture.getOutput().contains(linkPathStr +
+            OZONE_URI_DELIMITER + srcBucket));
+      } finally {
+        shell.close();
+      }
+
+      // add key in source bucket
+      final String key = "object-dir/object-name1";
+      try (OzoneOutputStream outputStream = objectStore.getVolume(srcVolume)
+          .getBucket(srcBucket)
+          .createKey(key, 1)) {
+        outputStream.write(RandomUtils.nextBytes(1));
+      }
+      Assert.assertEquals(objectStore.getVolume(srcVolume)
+          .getBucket(srcBucket).getKey(key).getName(), key);
+
+      // test ls -R /destVol/destBucket, srcBucket with key (non-empty)
+      try (GenericTestUtils.SystemOutCapturer capture =
+               new GenericTestUtils.SystemOutCapturer()) {
+        String linkPathStr = rootPath + destVolume;
+        ToolRunner.run(shell, new String[]{"-ls", "-R",
+            linkPathStr + OZONE_URI_DELIMITER + srcBucket});
+        Assert.assertTrue(capture.getOutput().contains("drwxrwxrwx"));
+        Assert.assertTrue(capture.getOutput().contains(linkPathStr +
+            OZONE_URI_DELIMITER + srcBucket));
+        Assert.assertTrue(capture.getOutput().contains("-rw-rw-rw-"));
+        Assert.assertTrue(capture.getOutput().contains(linkPathStr +
+            OZONE_URI_DELIMITER + srcBucket + OZONE_URI_DELIMITER + key));
+      } finally {
+        shell.close();
+      }
+    } finally {
+      // cleanup; note must delete link before link src bucket
+      // due to bug - HDDS-7884
+      fs.delete(new Path(OZONE_URI_DELIMITER + destVolume +
+          OZONE_URI_DELIMITER + srcBucket));
+      fs.delete(new Path(OZONE_URI_DELIMITER + srcVolume), true);
+      fs.delete(new Path(OZONE_URI_DELIMITER + destVolume), true);
+    }
+  }
+
+  @Test
+  public void testSymlinkPosixDelete() throws Exception {
+    OzoneFsShell shell = new OzoneFsShell(conf);
+    // setup symlink, destVol/destBucket -> srcVol/srcBucket
+    String srcVolume = getRandomNonExistVolumeName();
+    final String srcBucket = "bucket";
+    String destVolume = getRandomNonExistVolumeName();
+    createSymlinkSrcDestPaths(srcVolume, srcBucket, destVolume, srcBucket);
+
+    try {
+      // test symlink destVol/destBucket -> srcVol/srcBucket exists
+      Assert.assertTrue(fs.exists(new Path(OZONE_URI_DELIMITER +
+          destVolume + OZONE_URI_DELIMITER + srcBucket)));
+
+      // add key to srcBucket
+      final String key = "object-dir/object-name1";
+      try (OzoneOutputStream outputStream = objectStore.getVolume(srcVolume)
+          .getBucket(srcBucket)
+          .createKey(key, 1)) {
+        outputStream.write(RandomUtils.nextBytes(1));
+      }
+      Assert.assertEquals(objectStore.getVolume(srcVolume)
+          .getBucket(srcBucket).getKey(key).getName(), key);
+
+      // test symlink -rm destVol/destBucket -> srcVol/srcBucket
+      // should delete only link, srcBucket and key unaltered
+      // run toolrunner ozone fs shell commands
+      try {
+        String linkPathStr = rootPath + destVolume + OZONE_URI_DELIMITER +
+            srcBucket;
+        int res = ToolRunner.run(shell, new String[]{"-rm", "-skipTrash",
+            linkPathStr});
+        Assert.assertEquals(0, res);
+
+        try {
+          objectStore.getVolume(destVolume).getBucket(destVolume);

Review Comment:
   typo?
   ```suggestion
             objectStore.getVolume(destVolume).getBucket(srcBucket);
   ```



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to