This is an automated email from the ASF dual-hosted git repository.

sammichen pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/ozone.git


The following commit(s) were added to refs/heads/master by this push:
     new db4f08678b1 HDDS-5306. Add `ozone sh * getacl` option to match 
`setacl` input format (#9564)
db4f08678b1 is described below

commit db4f08678b1485ec4c0998856f517c1646468336
Author: Eric C. Ho <[email protected]>
AuthorDate: Mon Jan 5 11:24:10 2026 +0800

    HDDS-5306. Add `ozone sh * getacl` option to match `setacl` input format 
(#9564)
---
 .../hadoop/ozone/shell/acl/GetAclHandler.java      |  47 ++++-
 .../hadoop/ozone/shell/acl/TestGetAclHandler.java  | 225 +++++++++++++++++++++
 2 files changed, 271 insertions(+), 1 deletion(-)

diff --git 
a/hadoop-ozone/cli-shell/src/main/java/org/apache/hadoop/ozone/shell/acl/GetAclHandler.java
 
b/hadoop-ozone/cli-shell/src/main/java/org/apache/hadoop/ozone/shell/acl/GetAclHandler.java
index b73bfbff066..aba3907ef98 100644
--- 
a/hadoop-ozone/cli-shell/src/main/java/org/apache/hadoop/ozone/shell/acl/GetAclHandler.java
+++ 
b/hadoop-ozone/cli-shell/src/main/java/org/apache/hadoop/ozone/shell/acl/GetAclHandler.java
@@ -19,19 +19,64 @@
 
 import java.io.IOException;
 import java.util.List;
+import java.util.stream.Collectors;
 import org.apache.hadoop.ozone.OzoneAcl;
 import org.apache.hadoop.ozone.client.OzoneClient;
 import org.apache.hadoop.ozone.security.acl.OzoneObj;
+import picocli.CommandLine.Option;
 
 /**
  * Get ACLs.
  */
 public abstract class GetAclHandler extends AclHandler {
 
+  @Option(names = "--json", negatable = true,
+      defaultValue = "true", fallbackValue = "true",
+      description = {
+          "Format output as JSON. Default is true.",
+          "Use --json=false or --no-json to turn off output JSON format."
+      })
+  private boolean json;
+
   @Override
   protected void execute(OzoneClient client, OzoneObj obj) throws IOException {
     List<OzoneAcl> result = client.getObjectStore().getAcl(obj);
-    printObjectAsJson(result);
+    if (json) {
+      printObjectAsJson(result);
+    } else {
+      printAclsAsString(result);
+    }
+  }
+
+  /**
+   * Prints ACLs as a comma-separated string in the format:
+   * type:name:permissions or type:name:permissions[scope] if scope is DEFAULT.
+   * This format is compatible with setacl/addacl commands.
+   *
+   * @param acls List of OzoneAcl objects to print
+   */
+  private void printAclsAsString(List<OzoneAcl> acls) {
+    String aclString = acls.stream()
+        .map(this::formatAcl)
+        .collect(Collectors.joining(","));
+    out().println(aclString);
+  }
+
+  /**
+   * Formats a single ACL for string output.
+   * Omits the scope if it's ACCESS (the default) to match the input format.
+   *
+   * @param acl The OzoneAcl to format
+   * @return Formatted ACL string
+   */
+  private String formatAcl(OzoneAcl acl) {
+    String baseAcl = acl.toString();
+    // If the scope is ACCESS (default), remove it from the output
+    if (acl.getAclScope() == OzoneAcl.AclScope.ACCESS) {
+      // Remove [ACCESS] from the end
+      return baseAcl.replaceAll("\\[ACCESS\\]$", "");
+    }
+    return baseAcl;
   }
 
 }
diff --git 
a/hadoop-ozone/cli-shell/src/test/java/org/apache/hadoop/ozone/shell/acl/TestGetAclHandler.java
 
b/hadoop-ozone/cli-shell/src/test/java/org/apache/hadoop/ozone/shell/acl/TestGetAclHandler.java
new file mode 100644
index 00000000000..c4f93517dab
--- /dev/null
+++ 
b/hadoop-ozone/cli-shell/src/test/java/org/apache/hadoop/ozone/shell/acl/TestGetAclHandler.java
@@ -0,0 +1,225 @@
+/*
+ * 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.ozone.shell.acl;
+
+import static org.apache.hadoop.ozone.OzoneAcl.AclScope.ACCESS;
+import static org.apache.hadoop.ozone.OzoneAcl.AclScope.DEFAULT;
+import static 
org.apache.hadoop.ozone.security.acl.IAccessAuthorizer.ACLIdentityType.GROUP;
+import static 
org.apache.hadoop.ozone.security.acl.IAccessAuthorizer.ACLIdentityType.USER;
+import static 
org.apache.hadoop.ozone.security.acl.IAccessAuthorizer.ACLType.ALL;
+import static 
org.apache.hadoop.ozone.security.acl.IAccessAuthorizer.ACLType.READ;
+import static 
org.apache.hadoop.ozone.security.acl.IAccessAuthorizer.ACLType.WRITE;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.io.UnsupportedEncodingException;
+import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
+import java.util.List;
+import org.apache.hadoop.ozone.OzoneAcl;
+import org.apache.hadoop.ozone.client.ObjectStore;
+import org.apache.hadoop.ozone.client.OzoneClient;
+import org.apache.hadoop.ozone.security.acl.OzoneObj;
+import org.apache.hadoop.ozone.security.acl.OzoneObjInfo;
+import org.apache.hadoop.ozone.shell.bucket.GetAclBucketHandler;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import picocli.CommandLine;
+
+/**
+ * Tests for GetAclHandler with string format output.
+ */
+public class TestGetAclHandler {
+
+  private TestableGetAclBucketHandler cmd;
+  private final ByteArrayOutputStream outContent = new ByteArrayOutputStream();
+  private final ByteArrayOutputStream errContent = new ByteArrayOutputStream();
+  private final PrintStream originalOut = System.out;
+  private final PrintStream originalErr = System.err;
+  private static final String DEFAULT_ENCODING = StandardCharsets.UTF_8.name();
+
+  /**
+   * Testable version of GetAclBucketHandler that exposes execute method.
+   */
+  static class TestableGetAclBucketHandler extends GetAclBucketHandler {
+    public void publicExecute(OzoneClient client, OzoneObj obj)
+        throws IOException {
+      execute(client, obj);
+    }
+  }
+
+  @BeforeEach
+  public void setup() throws UnsupportedEncodingException {
+    cmd = new TestableGetAclBucketHandler();
+    System.setOut(new PrintStream(outContent, false, DEFAULT_ENCODING));
+    System.setErr(new PrintStream(errContent, false, DEFAULT_ENCODING));
+  }
+
+  @AfterEach
+  public void tearDown() {
+    System.setOut(originalOut);
+    System.setErr(originalErr);
+  }
+
+  @Test
+  public void testGetAclAsJson() throws IOException {
+    // Setup mock objects
+    OzoneClient client = mock(OzoneClient.class);
+    ObjectStore objectStore = mock(ObjectStore.class);
+    when(client.getObjectStore()).thenReturn(objectStore);
+
+    List<OzoneAcl> acls = Arrays.asList(
+        OzoneAcl.of(USER, "user1", ACCESS, READ, WRITE),
+        OzoneAcl.of(GROUP, "hadoop", ACCESS, ALL)
+    );
+
+    OzoneObj obj = OzoneObjInfo.Builder.newBuilder()
+        .setBucketName("bucket")
+        .setVolumeName("volume")
+        .setResType(OzoneObj.ResourceType.BUCKET)
+        .setStoreType(OzoneObj.StoreType.OZONE)
+        .build();
+
+    when(objectStore.getAcl(obj)).thenReturn(acls);
+
+    // Parse command line without flag (default JSON output)
+    CommandLine commandLine = new CommandLine(cmd);
+    commandLine.parseArgs("o3://ozone1/volume/bucket");
+
+    // Execute command with default JSON output
+    cmd.publicExecute(client, obj);
+
+    // Verify JSON output
+    ObjectMapper mapper = new ObjectMapper();
+    JsonNode json = mapper.readTree(outContent.toString(DEFAULT_ENCODING));
+    assertTrue(json.isArray());
+    assertEquals(2, json.size());
+    assertEquals("USER", json.get(0).get("type").asText());
+    assertEquals("user1", json.get(0).get("name").asText());
+    assertEquals("GROUP", json.get(1).get("type").asText());
+    assertEquals("hadoop", json.get(1).get("name").asText());
+  }
+
+  @Test
+  public void testGetAclAsStringWithAccessScope() throws IOException {
+    // Setup mock objects
+    OzoneClient client = mock(OzoneClient.class);
+    ObjectStore objectStore = mock(ObjectStore.class);
+    when(client.getObjectStore()).thenReturn(objectStore);
+
+    List<OzoneAcl> acls = Arrays.asList(
+        OzoneAcl.of(USER, "user1", ACCESS, READ, WRITE),
+        OzoneAcl.of(GROUP, "hadoop", ACCESS, ALL)
+    );
+
+    OzoneObj obj = OzoneObjInfo.Builder.newBuilder()
+        .setBucketName("bucket")
+        .setVolumeName("volume")
+        .setResType(OzoneObj.ResourceType.BUCKET)
+        .setStoreType(OzoneObj.StoreType.OZONE)
+        .build();
+
+    when(objectStore.getAcl(obj)).thenReturn(acls);
+
+    // Parse command line with --json=false flag (string output)
+    CommandLine commandLine = new CommandLine(cmd);
+    commandLine.parseArgs("--json=false", "o3://ozone1/volume/bucket");
+
+    // Execute command with --json=false flag
+    cmd.publicExecute(client, obj);
+
+    // Verify string output (should omit [ACCESS] for default scope)
+    String output = outContent.toString(DEFAULT_ENCODING).trim();
+    assertEquals("user:user1:rw,group:hadoop:a", output);
+  }
+
+  @Test
+  public void testGetAclAsStringWithDefaultScope() throws IOException {
+    // Setup mock objects
+    OzoneClient client = mock(OzoneClient.class);
+    ObjectStore objectStore = mock(ObjectStore.class);
+    when(client.getObjectStore()).thenReturn(objectStore);
+
+    List<OzoneAcl> acls = Arrays.asList(
+        OzoneAcl.of(USER, "user1", DEFAULT, READ, WRITE),
+        OzoneAcl.of(GROUP, "hadoop", DEFAULT, ALL)
+    );
+
+    OzoneObj obj = OzoneObjInfo.Builder.newBuilder()
+        .setBucketName("bucket")
+        .setVolumeName("volume")
+        .setResType(OzoneObj.ResourceType.BUCKET)
+        .setStoreType(OzoneObj.StoreType.OZONE)
+        .build();
+
+    when(objectStore.getAcl(obj)).thenReturn(acls);
+
+    // Parse command line with --json=false flag (string output)
+    CommandLine commandLine = new CommandLine(cmd);
+    commandLine.parseArgs("--json=false", "o3://ozone1/volume/bucket");
+
+    // Execute command with --json=false flag
+    cmd.publicExecute(client, obj);
+
+    // Verify string output (should include [DEFAULT] for non-default scope)
+    String output = outContent.toString(DEFAULT_ENCODING).trim();
+    assertEquals("user:user1:rw[DEFAULT],group:hadoop:a[DEFAULT]", output);
+  }
+
+  @Test
+  public void testGetAclAsStringMixedScopes() throws IOException {
+    // Setup mock objects
+    OzoneClient client = mock(OzoneClient.class);
+    ObjectStore objectStore = mock(ObjectStore.class);
+    when(client.getObjectStore()).thenReturn(objectStore);
+
+    List<OzoneAcl> acls = Arrays.asList(
+        OzoneAcl.of(USER, "user1", ACCESS, READ, WRITE),
+        OzoneAcl.of(GROUP, "hadoop", DEFAULT, ALL)
+    );
+
+    OzoneObj obj = OzoneObjInfo.Builder.newBuilder()
+        .setBucketName("bucket")
+        .setVolumeName("volume")
+        .setResType(OzoneObj.ResourceType.BUCKET)
+        .setStoreType(OzoneObj.StoreType.OZONE)
+        .build();
+
+    when(objectStore.getAcl(obj)).thenReturn(acls);
+
+    // Parse command line with --json=false flag (string output)
+    CommandLine commandLine = new CommandLine(cmd);
+    commandLine.parseArgs("--json=false", "o3://ozone1/volume/bucket");
+
+    // Execute command with --json=false flag
+    cmd.publicExecute(client, obj);
+
+    // Verify string output (mixed scopes)
+    String output = outContent.toString(DEFAULT_ENCODING).trim();
+    assertEquals("user:user1:rw,group:hadoop:a[DEFAULT]", output);
+  }
+
+}


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

Reply via email to