diff --git a/plugin-yarn/pom.xml b/plugin-yarn/pom.xml
index cae76d874f..43971c6e61 100644
--- a/plugin-yarn/pom.xml
+++ b/plugin-yarn/pom.xml
@@ -195,6 +195,24 @@
${jersey-client.version}
runtime
+
+ org.junit.jupiter
+ junit-jupiter
+ ${junit.jupiter.version}
+ test
+
+
+ org.mockito
+ mockito-core
+ ${mockito.version}
+ test
+
+
+ org.mockito
+ mockito-junit-jupiter
+ ${mockito.version}
+ test
+
org.slf4j
log4j-over-slf4j
diff --git a/plugin-yarn/src/test/java/org/apache/ranger/authorization/yarn/authorizer/TestRangerYarnAuthorizer.java b/plugin-yarn/src/test/java/org/apache/ranger/authorization/yarn/authorizer/TestRangerYarnAuthorizer.java
new file mode 100644
index 0000000000..adfc19b98f
--- /dev/null
+++ b/plugin-yarn/src/test/java/org/apache/ranger/authorization/yarn/authorizer/TestRangerYarnAuthorizer.java
@@ -0,0 +1,576 @@
+/*
+ * 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.ranger.authorization.yarn.authorizer;
+
+import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.ipc.Server;
+import org.apache.hadoop.security.UserGroupInformation;
+import org.apache.hadoop.security.authorize.AccessControlList;
+import org.apache.hadoop.yarn.security.AccessRequest;
+import org.apache.hadoop.yarn.security.AccessType;
+import org.apache.hadoop.yarn.security.Permission;
+import org.apache.hadoop.yarn.security.PrivilegedEntity;
+import org.apache.hadoop.yarn.security.PrivilegedEntity.EntityType;
+import org.apache.ranger.audit.model.AuthzAuditEvent;
+import org.apache.ranger.authorization.hadoop.config.RangerPluginConfig;
+import org.apache.ranger.authorization.hadoop.constants.RangerHadoopConstants;
+import org.apache.ranger.plugin.policyengine.RangerAccessRequest;
+import org.apache.ranger.plugin.policyengine.RangerAccessResult;
+import org.apache.ranger.plugin.policyengine.RangerAccessResultProcessor;
+import org.apache.ranger.plugin.service.RangerBasePlugin;
+import org.apache.ranger.plugin.util.RangerPerfTracer;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.MethodOrderer;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.TestMethodOrder;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.MockedStatic;
+import org.mockito.junit.jupiter.MockitoExtension;
+import org.slf4j.Logger;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.net.InetAddress;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.atLeastOnce;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.mockStatic;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+/**
+ * @generated by Cursor
+ * @description : Unit Test cases for RangerYarnAuthorizer
+ */
+@ExtendWith(MockitoExtension.class)
+@TestMethodOrder(MethodOrderer.MethodName.class)
+class TestRangerYarnAuthorizer {
+ @AfterEach
+ void clearStaticYarnPlugin() throws Exception {
+ Field f = RangerYarnAuthorizer.class.getDeclaredField("yarnPlugin");
+ f.setAccessible(true);
+ f.set(null, null);
+ }
+
+ private static void setYarnPluginMock(RangerBasePlugin mockPlugin) throws Exception {
+ Field f = RangerYarnAuthorizer.class.getDeclaredField("yarnPlugin");
+ f.setAccessible(true);
+ f.set(null, mockPlugin);
+ }
+
+ @SuppressWarnings("rawtypes")
+ private static RangerBasePlugin newInnerPluginMock() throws ClassNotFoundException {
+ Class pluginClass = Class.forName(
+ "org.apache.ranger.authorization.yarn.authorizer.RangerYarnAuthorizer$RangerYarnPlugin");
+ return (RangerBasePlugin) mock(pluginClass);
+ }
+
+ private static void setYarnAuthEnabled(RangerYarnAuthorizer authorizer, boolean enabled) throws Exception {
+ Field f = RangerYarnAuthorizer.class.getDeclaredField("yarnAuthEnabled");
+ f.setAccessible(true);
+ f.set(authorizer, enabled);
+ }
+
+ @Test
+ void test01_isAdmin_whenAdminsNotSet_returnsFalse() throws Exception {
+ RangerYarnAuthorizer authorizer = new RangerYarnAuthorizer();
+ UserGroupInformation ugi = UserGroupInformation.createRemoteUser("alice");
+
+ assertFalse(authorizer.isAdmin(ugi));
+ }
+
+ @Test
+ void test02_isAdmin_whenAclAllowsUser_returnsTrue() throws Exception {
+ RangerYarnAuthorizer authorizer = new RangerYarnAuthorizer();
+ UserGroupInformation ugi = UserGroupInformation.createRemoteUser("bob");
+ AccessControlList acl = mock(AccessControlList.class);
+ when(acl.isUserAllowed(ugi)).thenReturn(true);
+ authorizer.setAdmins(acl, ugi);
+
+ assertTrue(authorizer.isAdmin(ugi));
+ }
+
+ @Test
+ void test03_setPermission_withEmptyList_completes() throws Exception {
+ RangerYarnAuthorizer authorizer = new RangerYarnAuthorizer();
+ UserGroupInformation ugi = UserGroupInformation.createRemoteUser("carol");
+
+ assertDoesNotThrow(() -> authorizer.setPermission(Collections.emptyList(), ugi));
+ }
+
+ @Test
+ void test04_checkPermission_withNullPlugin_usesFallbackPathWithoutThrowing() throws Exception {
+ RangerYarnAuthorizer authorizer = new RangerYarnAuthorizer();
+ UserGroupInformation ugi = UserGroupInformation.createRemoteUser("dave");
+ PrivilegedEntity entity = new PrivilegedEntity(EntityType.QUEUE, "root.default");
+ AccessRequest request = new AccessRequest(entity, ugi, AccessType.SUBMIT_APP, "10.0.0.1", "10.0.0.1",
+ null, Collections.emptyList());
+
+ assertDoesNotThrow(() -> authorizer.checkPermission(request));
+ }
+
+ @Test
+ void test05_init_withoutRangerAdminConfig_throws() {
+ RangerYarnAuthorizer authorizer = new RangerYarnAuthorizer();
+
+ assertThrows(IllegalArgumentException.class, () -> authorizer.init(new Configuration()));
+ }
+
+ @Test
+ void test06_checkPermission_withMockPlugin_usesEngineResultWhenDetermined() throws Exception {
+ RangerBasePlugin mockPlugin = newInnerPluginMock();
+ RangerAccessResult result = mock(RangerAccessResult.class);
+ when(result.getIsAllowed()).thenReturn(true);
+ when(mockPlugin.isAccessAllowed(any(RangerAccessRequest.class), any())).thenReturn(result);
+ setYarnPluginMock(mockPlugin);
+
+ RangerYarnAuthorizer authorizer = new RangerYarnAuthorizer();
+ setYarnAuthEnabled(authorizer, false);
+ UserGroupInformation ugi = UserGroupInformation.createRemoteUser("e1");
+ PrivilegedEntity entity = new PrivilegedEntity(EntityType.QUEUE, "root");
+ AccessRequest request = new AccessRequest(entity, ugi, AccessType.SUBMIT_APP, null, null, "10.0.0.2",
+ Collections.emptyList());
+
+ assertTrue(authorizer.checkPermission(request));
+ }
+
+ @Test
+ void test07_checkPermission_submit_app_withMockPlugin_deniesWhenDisallowed() throws Exception {
+ RangerBasePlugin mockPlugin = newInnerPluginMock();
+ when(mockPlugin.isAccessAllowed(any(RangerAccessRequest.class), any())).thenAnswer(inv -> {
+ RangerAccessResult r = mock(RangerAccessResult.class);
+ when(r.getIsAllowed()).thenReturn(false);
+ return r;
+ });
+ setYarnPluginMock(mockPlugin);
+
+ RangerYarnAuthorizer authorizer = new RangerYarnAuthorizer();
+ setYarnAuthEnabled(authorizer, false);
+ UserGroupInformation ugi = UserGroupInformation.createRemoteUser("e2");
+ PrivilegedEntity entity = new PrivilegedEntity(EntityType.QUEUE, "root");
+ AccessRequest request = new AccessRequest(entity, ugi, AccessType.SUBMIT_APP, "10.0.0.1", "10.0.0.1",
+ null, Collections.emptyList());
+
+ assertFalse(authorizer.checkPermission(request));
+ }
+
+ @Test
+ void test08_checkPermission_administer_queue_invokesMockPlugin() throws Exception {
+ RangerBasePlugin mockPlugin = newInnerPluginMock();
+ when(mockPlugin.isAccessAllowed(any(RangerAccessRequest.class), any())).thenAnswer(inv -> {
+ RangerAccessResult r = mock(RangerAccessResult.class);
+ when(r.getIsAllowed()).thenReturn(true);
+ return r;
+ });
+ setYarnPluginMock(mockPlugin);
+
+ RangerYarnAuthorizer authorizer = new RangerYarnAuthorizer();
+ setYarnAuthEnabled(authorizer, false);
+ UserGroupInformation ugi = UserGroupInformation.createRemoteUser("e3");
+ PrivilegedEntity entity = new PrivilegedEntity(EntityType.QUEUE, "root");
+ AccessRequest request = new AccessRequest(entity, ugi, AccessType.ADMINISTER_QUEUE, "10.0.0.1",
+ "10.0.0.1", null, Collections.emptyList());
+
+ assertTrue(authorizer.checkPermission(request));
+ }
+
+ @Test
+ void test09_checkPermission_runsAuditHandlerWhenPluginProcessesResult() throws Exception {
+ RangerBasePlugin mockPlugin = newInnerPluginMock();
+ when(mockPlugin.isAccessAllowed(any(RangerAccessRequest.class), any())).thenAnswer(invocation -> {
+ RangerAccessResultProcessor proc = invocation.getArgument(1);
+ RangerAccessResult r = mock(RangerAccessResult.class);
+ when(r.getIsAudited()).thenReturn(true);
+ when(r.getIsAllowed()).thenReturn(true);
+ if (proc != null) {
+ proc.processResult(r);
+ }
+ return r;
+ });
+ setYarnPluginMock(mockPlugin);
+
+ RangerYarnAuthorizer authorizer = new RangerYarnAuthorizer();
+ setYarnAuthEnabled(authorizer, false);
+ UserGroupInformation ugi = UserGroupInformation.createRemoteUser("e4");
+ PrivilegedEntity entity = new PrivilegedEntity(EntityType.QUEUE, "root");
+ AccessRequest request = new AccessRequest(entity, ugi, AccessType.SUBMIT_APP, "10.0.0.1", "10.0.0.1",
+ null, Collections.emptyList());
+
+ assertTrue(authorizer.checkPermission(request));
+ }
+
+ @Test
+ void test10_isAllowedByYarnAcl_whenAclMatchesHierarchy_returnsTrue() throws Exception {
+ RangerYarnAuthorizer authorizer = new RangerYarnAuthorizer();
+ UserGroupInformation ugi = UserGroupInformation.createRemoteUser("fab");
+
+ PrivilegedEntity parent = new PrivilegedEntity(EntityType.QUEUE, "root");
+ PrivilegedEntity child = new PrivilegedEntity(EntityType.QUEUE, "root.batch");
+
+ AccessControlList acl = mock(AccessControlList.class);
+ when(acl.isUserAllowed(ugi)).thenReturn(true);
+ Map acls = new HashMap<>();
+ acls.put(AccessType.SUBMIT_APP, acl);
+ Permission perm = new Permission(parent, acls);
+ authorizer.setPermission(Collections.singletonList(perm), ugi);
+
+ assertTrue(authorizer.isAllowedByYarnAcl(AccessType.SUBMIT_APP, child, ugi, null));
+ }
+
+ @Test
+ void test11_isAllowedByYarnAcl_whenNoMatchingAcl_returnsFalse() throws Exception {
+ RangerYarnAuthorizer authorizer = new RangerYarnAuthorizer();
+ UserGroupInformation ugi = UserGroupInformation.createRemoteUser("gus");
+ PrivilegedEntity entity = new PrivilegedEntity(EntityType.QUEUE, "other");
+
+ assertFalse(authorizer.isAllowedByYarnAcl(AccessType.SUBMIT_APP, entity, ugi, null));
+ }
+
+ @Test
+ void test12_checkPermission_whenEngineUndeterminedAndYarnAuthOn_fallsBackToAcl() throws Exception {
+ RangerBasePlugin mockPlugin = newInnerPluginMock();
+ RangerAccessResult r = mock(RangerAccessResult.class);
+ when(r.getIsAccessDetermined()).thenReturn(false);
+ when(mockPlugin.isAccessAllowed(any(RangerAccessRequest.class), any())).thenReturn(r);
+ setYarnPluginMock(mockPlugin);
+
+ RangerYarnAuthorizer authorizer = new RangerYarnAuthorizer();
+ setYarnAuthEnabled(authorizer, true);
+ UserGroupInformation ugi = UserGroupInformation.createRemoteUser("fb");
+ PrivilegedEntity entity = new PrivilegedEntity(EntityType.QUEUE, "solo");
+ AccessRequest request = new AccessRequest(entity, ugi, AccessType.SUBMIT_APP, "10.0.0.1",
+ "10.0.0.1", null, Collections.emptyList());
+
+ assertFalse(authorizer.checkPermission(request));
+ }
+
+ @Test
+ void test13_isAllowedByYarnAcl_whenEntityEqualsAclEntity_matches() throws Exception {
+ RangerYarnAuthorizer authorizer = new RangerYarnAuthorizer();
+ UserGroupInformation ugi = UserGroupInformation.createRemoteUser("hal");
+ PrivilegedEntity entity = new PrivilegedEntity(EntityType.QUEUE, "same.q");
+
+ AccessControlList acl = mock(AccessControlList.class);
+ when(acl.isUserAllowed(ugi)).thenReturn(true);
+ Map acls = new HashMap<>();
+ acls.put(AccessType.SUBMIT_APP, acl);
+ Permission perm = new Permission(entity, acls);
+ authorizer.setPermission(Collections.singletonList(perm), ugi);
+
+ assertTrue(authorizer.isAllowedByYarnAcl(AccessType.SUBMIT_APP, entity, ugi, null));
+ }
+
+ @Test
+ void test14_init_whenYarnPluginAlreadyRegistered_configuresFromExistingPlugin() throws Exception {
+ RangerBasePlugin mockPlugin = newInnerPluginMock();
+ RangerPluginConfig cfg = mock(RangerPluginConfig.class);
+ when(cfg.getBoolean(eq(RangerHadoopConstants.RANGER_ADD_YARN_PERMISSION_PROP), anyBoolean())).thenReturn(false);
+ when(cfg.get(eq(RangerHadoopConstants.AUDITLOG_YARN_MODULE_ACL_NAME_PROP), anyString())).thenReturn("yarn-acl");
+ when(mockPlugin.getConfig()).thenReturn(cfg);
+ setYarnPluginMock(mockPlugin);
+
+ RangerYarnAuthorizer authorizer = new RangerYarnAuthorizer();
+ authorizer.init(new Configuration());
+
+ verify(cfg).setIsFallbackSupported(false);
+ }
+
+ @Test
+ void test15_checkPermission_whenPerfTracingEnabled_invokesPerfTracer() throws Exception {
+ RangerBasePlugin mockPlugin = newInnerPluginMock();
+ RangerAccessResult result = mock(RangerAccessResult.class);
+ when(result.getIsAllowed()).thenReturn(true);
+ when(mockPlugin.isAccessAllowed(any(RangerAccessRequest.class), any())).thenReturn(result);
+ setYarnPluginMock(mockPlugin);
+
+ RangerPerfTracer tracer = mock(RangerPerfTracer.class);
+ try (MockedStatic perf = mockStatic(RangerPerfTracer.class)) {
+ perf.when(() -> RangerPerfTracer.isPerfTraceEnabled(any(Logger.class))).thenReturn(true);
+ perf.when(() -> RangerPerfTracer.getPerfTracer(any(Logger.class), anyString())).thenReturn(tracer);
+
+ RangerYarnAuthorizer authorizer = new RangerYarnAuthorizer();
+ setYarnAuthEnabled(authorizer, false);
+ UserGroupInformation ugi = UserGroupInformation.createRemoteUser("perf1");
+ PrivilegedEntity entity = new PrivilegedEntity(EntityType.QUEUE, "root");
+ AccessRequest request = new AccessRequest(entity, ugi, AccessType.SUBMIT_APP, null, null, null,
+ Collections.emptyList());
+
+ assertTrue(authorizer.checkPermission(request));
+ perf.verify(() -> RangerPerfTracer.log(tracer), atLeastOnce());
+ }
+ }
+
+ @Test
+ void test16_checkPermission_yarnAclFallback_whenPerfEnabled_tracesNativeAclPath() throws Exception {
+ RangerBasePlugin mockPlugin = newInnerPluginMock();
+ RangerAccessResult r = mock(RangerAccessResult.class);
+ when(r.getIsAccessDetermined()).thenReturn(false);
+ when(mockPlugin.isAccessAllowed(any(RangerAccessRequest.class), any())).thenReturn(r);
+ setYarnPluginMock(mockPlugin);
+
+ RangerPerfTracer tracer = mock(RangerPerfTracer.class);
+ try (MockedStatic perf = mockStatic(RangerPerfTracer.class)) {
+ perf.when(() -> RangerPerfTracer.isPerfTraceEnabled(any(Logger.class))).thenReturn(true);
+ perf.when(() -> RangerPerfTracer.getPerfTracer(any(Logger.class), anyString())).thenReturn(tracer);
+
+ RangerYarnAuthorizer authorizer = new RangerYarnAuthorizer();
+ setYarnAuthEnabled(authorizer, true);
+ UserGroupInformation ugi = UserGroupInformation.createRemoteUser("perf2");
+ PrivilegedEntity entity = new PrivilegedEntity(EntityType.QUEUE, "solo");
+ AccessRequest request = new AccessRequest(entity, ugi, AccessType.SUBMIT_APP, null, null, null,
+ Collections.emptyList());
+
+ assertFalse(authorizer.checkPermission(request));
+ perf.verify(() -> RangerPerfTracer.log(tracer), atLeastOnce());
+ }
+ }
+
+ @Test
+ void test17_checkPermission_applicationMaxPriority_mapsAccessTypeToNullWithoutFailure() throws Exception {
+ RangerBasePlugin mockPlugin = newInnerPluginMock();
+ when(mockPlugin.isAccessAllowed(any(RangerAccessRequest.class), any())).thenAnswer(inv -> {
+ RangerAccessResult res = mock(RangerAccessResult.class);
+ when(res.getIsAllowed()).thenReturn(true);
+ return res;
+ });
+ setYarnPluginMock(mockPlugin);
+
+ RangerYarnAuthorizer authorizer = new RangerYarnAuthorizer();
+ setYarnAuthEnabled(authorizer, false);
+ UserGroupInformation ugi = UserGroupInformation.createRemoteUser("prio");
+ PrivilegedEntity entity = new PrivilegedEntity(EntityType.QUEUE, "root");
+ AccessRequest request = new AccessRequest(entity, ugi, AccessType.APPLICATION_MAX_PRIORITY, "10.0.0.1",
+ "10.0.0.1", null, Collections.emptyList());
+
+ assertTrue(authorizer.checkPermission(request));
+ }
+
+ @Test
+ void test18_checkPermission_nullEntity_remoteIpUsesServerWhenAddressMissing() throws Exception {
+ RangerBasePlugin mockPlugin = newInnerPluginMock();
+ when(mockPlugin.isAccessAllowed(any(RangerAccessRequest.class), any())).thenAnswer(inv -> {
+ RangerAccessResult res = mock(RangerAccessResult.class);
+ when(res.getIsAllowed()).thenReturn(true);
+ return res;
+ });
+ setYarnPluginMock(mockPlugin);
+
+ try (MockedStatic server = mockStatic(Server.class)) {
+ server.when(Server::getRemoteIp).thenReturn(InetAddress.getByName("192.168.1.10"));
+
+ RangerYarnAuthorizer authorizer = new RangerYarnAuthorizer();
+ setYarnAuthEnabled(authorizer, false);
+ UserGroupInformation ugi = UserGroupInformation.createRemoteUser("nent");
+ AccessRequest request = new AccessRequest(null, ugi, AccessType.SUBMIT_APP, null, null, null,
+ Collections.emptyList());
+
+ assertTrue(authorizer.checkPermission(request));
+ }
+ }
+
+ @Test
+ void test19_isAllowedByYarnAcl_whenPermissionMapNull_skipsAccessTypeLookup() throws Exception {
+ RangerYarnAuthorizer authorizer = new RangerYarnAuthorizer();
+ UserGroupInformation ugi = UserGroupInformation.createRemoteUser("nilmap");
+ PrivilegedEntity entity = new PrivilegedEntity(EntityType.QUEUE, "root.x");
+ Permission perm = new Permission(new PrivilegedEntity(EntityType.QUEUE, "root"), null);
+ authorizer.setPermission(Collections.singletonList(perm), ugi);
+
+ assertFalse(authorizer.isAllowedByYarnAcl(AccessType.SUBMIT_APP, entity, ugi, null));
+ }
+
+ @Test
+ void test20_isSelfOrChildOf_whenParentAlreadyEndsWithDot_stillMatchesHierarchy() throws Exception {
+ RangerYarnAuthorizer authorizer = new RangerYarnAuthorizer();
+ UserGroupInformation ugi = UserGroupInformation.createRemoteUser("dotp");
+ PrivilegedEntity parent = new PrivilegedEntity(EntityType.QUEUE, "root.");
+ PrivilegedEntity child = new PrivilegedEntity(EntityType.QUEUE, "root.batch");
+
+ AccessControlList acl = mock(AccessControlList.class);
+ when(acl.isUserAllowed(ugi)).thenReturn(true);
+ Map acls = new HashMap<>();
+ acls.put(AccessType.SUBMIT_APP, acl);
+ Permission perm = new Permission(parent, acls);
+ authorizer.setPermission(Collections.singletonList(perm), ugi);
+
+ assertTrue(authorizer.isAllowedByYarnAcl(AccessType.SUBMIT_APP, child, ugi, null));
+ }
+
+ @Test
+ void test21_isSelfOrChildOf_whenQueueHasNoDot_doesNotPrefixMatch() throws Exception {
+ RangerYarnAuthorizer authorizer = new RangerYarnAuthorizer();
+ UserGroupInformation ugi = UserGroupInformation.createRemoteUser("nodot");
+ PrivilegedEntity parent = new PrivilegedEntity(EntityType.QUEUE, "root");
+ PrivilegedEntity child = new PrivilegedEntity(EntityType.QUEUE, "rootx");
+
+ AccessControlList acl = mock(AccessControlList.class);
+ when(acl.isUserAllowed(ugi)).thenReturn(true);
+ Map acls = new HashMap<>();
+ acls.put(AccessType.SUBMIT_APP, acl);
+ Permission perm = new Permission(parent, acls);
+ authorizer.setPermission(Collections.singletonList(perm), ugi);
+
+ assertFalse(authorizer.isAllowedByYarnAcl(AccessType.SUBMIT_APP, child, ugi, null));
+ }
+
+ @Test
+ void test22_isSelfOrChildOf_whenParentQueueNameBlank_skipsHierarchyMatch() throws Exception {
+ RangerYarnAuthorizer authorizer = new RangerYarnAuthorizer();
+ UserGroupInformation ugi = UserGroupInformation.createRemoteUser("blankp");
+ PrivilegedEntity parent = new PrivilegedEntity(EntityType.QUEUE, "");
+ PrivilegedEntity child = new PrivilegedEntity(EntityType.QUEUE, "root.batch");
+
+ AccessControlList acl = mock(AccessControlList.class);
+ when(acl.isUserAllowed(ugi)).thenReturn(true);
+ Map acls = new HashMap<>();
+ acls.put(AccessType.SUBMIT_APP, acl);
+ Permission perm = new Permission(parent, acls);
+ authorizer.setPermission(Collections.singletonList(perm), ugi);
+
+ assertFalse(authorizer.isAllowedByYarnAcl(AccessType.SUBMIT_APP, child, ugi, null));
+ }
+
+ @Test
+ void test23_isAllowedByYarnAcl_whenAclDeniedUser_returnsFalse() throws Exception {
+ RangerYarnAuthorizer authorizer = new RangerYarnAuthorizer();
+ UserGroupInformation ugi = UserGroupInformation.createRemoteUser("denied");
+ PrivilegedEntity parent = new PrivilegedEntity(EntityType.QUEUE, "root");
+ PrivilegedEntity child = new PrivilegedEntity(EntityType.QUEUE, "root.batch");
+
+ AccessControlList acl = mock(AccessControlList.class);
+ when(acl.isUserAllowed(ugi)).thenReturn(false);
+ Map acls = new HashMap<>();
+ acls.put(AccessType.SUBMIT_APP, acl);
+ Permission perm = new Permission(parent, acls);
+ authorizer.setPermission(Collections.singletonList(perm), ugi);
+
+ assertFalse(authorizer.isAllowedByYarnAcl(AccessType.SUBMIT_APP, child, ugi, null));
+ }
+
+ @Test
+ void test24_auditHandler_processResult_coversFlagTransitions() throws Exception {
+ Class> clazz = Class.forName(
+ "org.apache.ranger.authorization.yarn.authorizer.RangerYarnAuthorizer$RangerYarnAuditHandler");
+ Constructor> ctor = clazz.getDeclaredConstructor(String.class);
+ ctor.setAccessible(true);
+ Object handler = ctor.newInstance("yarn-mod");
+
+ Method process = clazz.getDeclaredMethod("processResult", RangerAccessResult.class);
+ process.setAccessible(true);
+ RangerAccessResult notAudited = mock(RangerAccessResult.class);
+ when(notAudited.getIsAudited()).thenReturn(false);
+ process.invoke(handler, notAudited);
+
+ RangerAccessResult audited = mock(RangerAccessResult.class);
+ when(audited.getIsAudited()).thenReturn(true);
+ process.invoke(handler, audited);
+ RangerAccessResult second = mock(RangerAccessResult.class);
+ process.invoke(handler, second);
+ }
+
+ @Test
+ void test25_auditHandler_logYarnAclEvent_setsAccessResultForGrantAndDeny() throws Exception {
+ Class> clazz = Class.forName(
+ "org.apache.ranger.authorization.yarn.authorizer.RangerYarnAuthorizer$RangerYarnAuditHandler");
+ Constructor> ctor = clazz.getDeclaredConstructor(String.class);
+ ctor.setAccessible(true);
+ Object handler = ctor.newInstance("yarn-mod");
+
+ AuthzAuditEvent event = mock(AuthzAuditEvent.class);
+ Field auditField = clazz.getDeclaredField("auditEvent");
+ auditField.setAccessible(true);
+ auditField.set(handler, event);
+
+ Method logYarnAcl = clazz.getDeclaredMethod("logYarnAclEvent", boolean.class);
+ logYarnAcl.setAccessible(true);
+ logYarnAcl.invoke(handler, true);
+ logYarnAcl.invoke(handler, false);
+
+ verify(event).setAccessResult((short) 1);
+ verify(event).setAccessResult((short) 0);
+ verify(event, times(2)).setAclEnforcer("yarn-mod");
+ verify(event, times(2)).setPolicyId(-1);
+ }
+
+ @Test
+ void test26_checkPermission_whenServerReturnsNullRemoteIp_clientIpStaysNull() throws Exception {
+ RangerBasePlugin mockPlugin = newInnerPluginMock();
+ when(mockPlugin.isAccessAllowed(any(RangerAccessRequest.class), any())).thenAnswer(inv -> {
+ RangerAccessResult res = mock(RangerAccessResult.class);
+ when(res.getIsAllowed()).thenReturn(true);
+ return res;
+ });
+ setYarnPluginMock(mockPlugin);
+
+ try (MockedStatic server = mockStatic(Server.class)) {
+ server.when(Server::getRemoteIp).thenReturn(null);
+
+ RangerYarnAuthorizer authorizer = new RangerYarnAuthorizer();
+ setYarnAuthEnabled(authorizer, false);
+ UserGroupInformation ugi = UserGroupInformation.createRemoteUser("noip");
+ AccessRequest request = new AccessRequest(new PrivilegedEntity(EntityType.QUEUE, "q"), ugi,
+ AccessType.SUBMIT_APP, null, null, null, Collections.emptyList());
+
+ assertTrue(authorizer.checkPermission(request));
+ }
+ }
+
+ @Test
+ void test27_checkPermission_whenNoPluginAndYarnAuthDisabled_usesNegatedEngineDecision() throws Exception {
+ RangerYarnAuthorizer authorizer = new RangerYarnAuthorizer();
+ setYarnAuthEnabled(authorizer, false);
+ UserGroupInformation ugi = UserGroupInformation.createRemoteUser("noplug");
+ PrivilegedEntity entity = new PrivilegedEntity(EntityType.QUEUE, "root");
+ AccessRequest request = new AccessRequest(entity, ugi, AccessType.SUBMIT_APP, "10.0.0.1", "10.0.0.1", null,
+ Collections.emptyList());
+
+ assertFalse(authorizer.checkPermission(request));
+ }
+
+ @Test
+ void test28_checkPermission_whenYarnAuthOnAndEngineDetermined_skipsYarnNativeAcl() throws Exception {
+ RangerBasePlugin mockPlugin = newInnerPluginMock();
+ RangerAccessResult r = mock(RangerAccessResult.class);
+ when(r.getIsAccessDetermined()).thenReturn(true);
+ when(r.getIsAllowed()).thenReturn(true);
+ when(mockPlugin.isAccessAllowed(any(RangerAccessRequest.class), any())).thenReturn(r);
+ setYarnPluginMock(mockPlugin);
+
+ RangerYarnAuthorizer authorizer = new RangerYarnAuthorizer();
+ setYarnAuthEnabled(authorizer, true);
+ UserGroupInformation ugi = UserGroupInformation.createRemoteUser("determined");
+ PrivilegedEntity entity = new PrivilegedEntity(EntityType.QUEUE, "root");
+ AccessRequest request = new AccessRequest(entity, ugi, AccessType.SUBMIT_APP, "10.0.0.1", "10.0.0.1", null,
+ Collections.emptyList());
+
+ assertTrue(authorizer.checkPermission(request));
+ }
+}
diff --git a/plugin-yarn/src/test/java/org/apache/ranger/services/yarn/TestRangerServiceYarn.java b/plugin-yarn/src/test/java/org/apache/ranger/services/yarn/TestRangerServiceYarn.java
new file mode 100644
index 0000000000..0e722fcd5b
--- /dev/null
+++ b/plugin-yarn/src/test/java/org/apache/ranger/services/yarn/TestRangerServiceYarn.java
@@ -0,0 +1,299 @@
+/*
+ * 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.ranger.services.yarn;
+
+import org.apache.ranger.authorization.yarn.authorizer.RangerYarnAuthorizer;
+import org.apache.ranger.plugin.model.RangerPolicy;
+import org.apache.ranger.plugin.model.RangerService;
+import org.apache.ranger.plugin.model.RangerServiceDef;
+import org.apache.ranger.plugin.model.RangerServiceDef.RangerAccessTypeDef;
+import org.apache.ranger.plugin.model.RangerServiceDef.RangerResourceDef;
+import org.apache.ranger.plugin.resourcematcher.RangerDefaultResourceMatcher;
+import org.apache.ranger.plugin.service.RangerBaseService;
+import org.apache.ranger.plugin.service.ResourceLookupContext;
+import org.apache.ranger.services.yarn.client.YarnResourceMgr;
+import org.junit.jupiter.api.MethodOrderer;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.TestMethodOrder;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.MockedStatic;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertSame;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mockStatic;
+
+/**
+ * @generated by Cursor
+ * @description : Unit Test cases for RangerServiceYarn
+ */
+@ExtendWith(MockitoExtension.class)
+@TestMethodOrder(MethodOrderer.MethodName.class)
+class TestRangerServiceYarn {
+ @Test
+ void test01_validateConfig_whenConfigsNull_returnsEmptyMap() {
+ RangerServiceYarn svc = new RangerServiceYarn();
+
+ Map response = svc.validateConfig();
+
+ assertTrue(response.isEmpty());
+ }
+
+ @Test
+ void test02_validateConfig_delegatesToYarnResourceMgr() {
+ Map cfg = new HashMap<>();
+ cfg.put("yarn.url", "http://rm:8088");
+ cfg.put("username", "u");
+ cfg.put("password", "p");
+ Map expected = new HashMap<>();
+ expected.put("connectivityStatus", Boolean.TRUE);
+
+ try (MockedStatic mocked = mockStatic(YarnResourceMgr.class)) {
+ mocked.when(() -> YarnResourceMgr.validateConfig(eq("sn"), eq(cfg))).thenReturn(expected);
+
+ RangerServiceYarn svc = new RangerServiceYarn();
+ svc.setServiceName("sn");
+ svc.setConfigs(cfg);
+
+ assertSame(expected, svc.validateConfig());
+ }
+ }
+
+ @Test
+ void test03_lookupResource_whenContextNull_returnsEmpty() {
+ assertTrue(new RangerServiceYarn().lookupResource(null).isEmpty());
+ }
+
+ @Test
+ void test04_lookupResource_delegatesToYarnResourceMgr() {
+ Map cfg = new HashMap<>();
+ cfg.put("yarn.url", "http://rm:8088");
+ ResourceLookupContext ctx = new ResourceLookupContext();
+ List expected = Collections.singletonList("default");
+
+ try (MockedStatic mocked = mockStatic(YarnResourceMgr.class)) {
+ mocked.when(() -> YarnResourceMgr.getYarnResources(eq("sn"), eq(cfg), eq(ctx))).thenReturn(expected);
+
+ RangerServiceYarn svc = new RangerServiceYarn();
+ svc.setServiceName("sn");
+ svc.setConfigs(cfg);
+
+ assertEquals(expected, svc.lookupResource(ctx));
+ }
+ }
+
+ @Test
+ void test05_getDefaultRangerPolicies_whenHierarchyDisabled_returnsEmptyList() throws Exception {
+ RangerServiceDef def = new RangerServiceDef();
+ def.setName("yarn");
+ def.setResources(new ArrayList<>());
+ Map options = new HashMap<>();
+ options.put("create.default.policy.per.hierarchy", "false");
+ def.setOptions(options);
+
+ RangerService rs = new RangerService();
+ rs.setName("yarn-svc");
+ rs.setConfigs(new HashMap<>());
+
+ RangerServiceYarn svc = new RangerServiceYarn();
+ svc.init(def, rs);
+
+ List policies = svc.getDefaultRangerPolicies();
+
+ assertTrue(policies.isEmpty());
+ }
+
+ @Test
+ void test06_init_setsServiceFieldsFromRangerService() {
+ RangerServiceDef def = new RangerServiceDef();
+ def.setName("yarn");
+ RangerService rs = new RangerService();
+ rs.setName("svc1");
+ rs.setType("yarn");
+ Map cfg = new HashMap<>();
+ cfg.put("yarn.url", "http://rm:8088");
+ rs.setConfigs(cfg);
+
+ RangerServiceYarn svc = new RangerServiceYarn();
+ svc.init(def, rs);
+
+ assertEquals("svc1", svc.getServiceName());
+ assertEquals("yarn", svc.getServiceType());
+ assertEquals(cfg, svc.getConfigs());
+ assertSame(def, svc.getServiceDef());
+ }
+
+ @Test
+ void test07_validateConfig_whenYarnResourceMgrThrows_propagates() {
+ Map cfg = new HashMap<>();
+ cfg.put("yarn.url", "http://rm:8088");
+
+ try (MockedStatic mocked = mockStatic(YarnResourceMgr.class)) {
+ mocked.when(() -> YarnResourceMgr.validateConfig(eq("sn"), eq(cfg)))
+ .thenThrow(new IllegalStateException("bad"));
+
+ RangerServiceYarn svc = new RangerServiceYarn();
+ svc.setServiceName("sn");
+ svc.setConfigs(cfg);
+
+ assertThrows(IllegalStateException.class, svc::validateConfig);
+ }
+ }
+
+ @Test
+ void test08_lookupResource_whenYarnResourceMgrThrows_propagates() {
+ Map cfg = new HashMap<>();
+ cfg.put("yarn.url", "http://rm:8088");
+ ResourceLookupContext ctx = new ResourceLookupContext();
+
+ try (MockedStatic mocked = mockStatic(YarnResourceMgr.class)) {
+ mocked.when(() -> YarnResourceMgr.getYarnResources(eq("sn"), eq(cfg), eq(ctx)))
+ .thenThrow(new IllegalStateException("lookup"));
+
+ RangerServiceYarn svc = new RangerServiceYarn();
+ svc.setServiceName("sn");
+ svc.setConfigs(cfg);
+
+ assertThrows(IllegalStateException.class, () -> svc.lookupResource(ctx));
+ }
+ }
+
+ @Test
+ void test09_getDefaultRangerPolicies_withHierarchy_addsSubmitAppForLookupUser() throws Exception {
+ RangerServiceDef def = new RangerServiceDef();
+ def.setName("yarn");
+ Map options = new HashMap<>();
+ options.put("create.default.policy.per.hierarchy", "true");
+ def.setOptions(options);
+
+ RangerAccessTypeDef accessType = new RangerAccessTypeDef();
+ accessType.setName(RangerServiceYarn.ACCESS_TYPE_SUBMIT_APP);
+ def.setAccessTypes(Collections.singletonList(accessType));
+
+ RangerResourceDef queue = new RangerResourceDef();
+ queue.setName(RangerYarnAuthorizer.KEY_RESOURCE_QUEUE);
+ queue.setMandatory(true);
+ queue.setMatcher(RangerDefaultResourceMatcher.class.getName());
+ def.setResources(Collections.singletonList(queue));
+
+ RangerService rs = new RangerService();
+ rs.setName("yarn-defpol");
+ rs.setConfigs(new HashMap<>());
+
+ RangerServiceYarn svc = new RangerServiceYarn();
+ Field lookUpField = RangerBaseService.class.getDeclaredField("lookUpUser");
+ lookUpField.setAccessible(true);
+ lookUpField.set(svc, "lookup-runner");
+ svc.init(def, rs);
+
+ List policies = svc.getDefaultRangerPolicies();
+
+ assertFalse(policies.isEmpty());
+ }
+
+ @Test
+ void test10_getDefaultRangerPolicies_customPolicyWithoutAllInName_doesNotAugmentResources() throws Exception {
+ RangerServiceDef def = new RangerServiceDef();
+ def.setName("yarn");
+ def.setResources(new ArrayList<>());
+ Map options = new HashMap<>();
+ options.put("create.default.policy.per.hierarchy", "false");
+ def.setOptions(options);
+
+ RangerService rs = new RangerService();
+ rs.setName("yarn-custom");
+ Map svcCfg = new HashMap<>();
+ svcCfg.put("setup.additional.default.policies", "true");
+ svcCfg.put("default-policy.1.name", "restricted-queues");
+ svcCfg.put("default-policy.1.resource.queue", "root");
+ rs.setConfigs(svcCfg);
+
+ RangerServiceYarn svc = new RangerServiceYarn();
+ svc.init(def, rs);
+
+ List policies = svc.getDefaultRangerPolicies();
+
+ assertEquals(1, policies.size());
+ assertFalse(policies.get(0).getName().contains("all"));
+ }
+
+ @Test
+ void test11_getDefaultRangerPolicies_whenLookupUserBlank_skipsUserGrant() throws Exception {
+ RangerServiceDef def = new RangerServiceDef();
+ def.setName("yarn");
+ Map options = new HashMap<>();
+ options.put("create.default.policy.per.hierarchy", "true");
+ def.setOptions(options);
+
+ RangerAccessTypeDef accessType = new RangerAccessTypeDef();
+ accessType.setName(RangerServiceYarn.ACCESS_TYPE_SUBMIT_APP);
+ def.setAccessTypes(Collections.singletonList(accessType));
+
+ RangerResourceDef queue = new RangerResourceDef();
+ queue.setName(RangerYarnAuthorizer.KEY_RESOURCE_QUEUE);
+ queue.setMandatory(true);
+ queue.setMatcher(RangerDefaultResourceMatcher.class.getName());
+ def.setResources(Collections.singletonList(queue));
+
+ RangerService rs = new RangerService();
+ rs.setName("yarn-nolookup");
+ rs.setConfigs(new HashMap<>());
+
+ RangerServiceYarn svc = new RangerServiceYarn();
+ Field lookUpField = RangerBaseService.class.getDeclaredField("lookUpUser");
+ lookUpField.setAccessible(true);
+ lookUpField.set(svc, "");
+ svc.init(def, rs);
+
+ assertFalse(svc.getDefaultRangerPolicies().isEmpty());
+ }
+
+ @Test
+ void test12_getDefaultRangerPolicies_policyNameContainsAllButNoQueueResource_logsWarning() throws Exception {
+ RangerServiceDef def = new RangerServiceDef();
+ def.setName("yarn");
+ def.setResources(new ArrayList<>());
+ Map options = new HashMap<>();
+ options.put("create.default.policy.per.hierarchy", "false");
+ def.setOptions(options);
+
+ RangerService rs = new RangerService();
+ rs.setName("yarn-warn-q");
+ Map svcCfg = new HashMap<>();
+ svcCfg.put("setup.additional.default.policies", "true");
+ svcCfg.put("default-policy.1.name", "custom-all-queues");
+ svcCfg.put("default-policy.1.resource.cluster", "c1");
+ rs.setConfigs(svcCfg);
+
+ RangerServiceYarn svc = new RangerServiceYarn();
+ svc.init(def, rs);
+
+ assertFalse(svc.getDefaultRangerPolicies().isEmpty());
+ }
+}
diff --git a/plugin-yarn/src/test/java/org/apache/ranger/services/yarn/client/TestYarnClient.java b/plugin-yarn/src/test/java/org/apache/ranger/services/yarn/client/TestYarnClient.java
new file mode 100644
index 0000000000..356f3ffab0
--- /dev/null
+++ b/plugin-yarn/src/test/java/org/apache/ranger/services/yarn/client/TestYarnClient.java
@@ -0,0 +1,661 @@
+/*
+ * 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.ranger.services.yarn.client;
+
+import org.apache.ranger.plugin.client.HadoopException;
+import org.apache.ranger.plugin.util.RangerJersey2ClientBuilder;
+import org.junit.jupiter.api.MethodOrderer;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.TestMethodOrder;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.MockedStatic;
+import org.mockito.Mockito;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+import javax.security.auth.Subject;
+import javax.ws.rs.ProcessingException;
+import javax.ws.rs.client.Client;
+import javax.ws.rs.client.Invocation;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import java.security.PrivilegedAction;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.isNull;
+import static org.mockito.ArgumentMatchers.same;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.mockStatic;
+import static org.mockito.Mockito.when;
+
+/**
+ * @generated by Cursor
+ * @description : Unit Test cases for YarnClient
+ */
+@ExtendWith(MockitoExtension.class)
+@TestMethodOrder(MethodOrderer.MethodName.class)
+class TestYarnClient {
+ private Map sampleConfigs() {
+ Map cfg = new HashMap<>();
+ cfg.put("yarn.url", "http://rm:8088");
+ cfg.put("username", "u");
+ cfg.put("password", "p");
+ return cfg;
+ }
+
+ private static void mockSubjectDoAsRunsAction(MockedStatic subjectStatic) {
+ subjectStatic.when(() -> Subject.doAs(any(Subject.class), any(PrivilegedAction.class)))
+ .thenAnswer(invocation -> {
+ PrivilegedAction> action = invocation.getArgument(1);
+ return action.run();
+ });
+ }
+
+ private static String fairSchedulerJsonQueues(String... queues) {
+ StringBuilder queuesJson = new StringBuilder();
+ for (int i = 0; i < queues.length; i++) {
+ if (i > 0) {
+ queuesJson.append(",");
+ }
+ queuesJson.append("{\"queueName\":\"").append(queues[i]).append("\"}");
+ }
+ return "{\"scheduler\":{\"schedulerInfo\":{\"type\":\"fairScheduler\",\"rootQueue\":"
+ + "{\"queueName\":\"root\",\"childQueues\":{\"queue\":[" + queuesJson + "]}}}}}";
+ }
+
+ @Test
+ void test01_getYarnClient_whenConfigsEmptyOrNull_throwsHadoopException() {
+ assertThrows(HadoopException.class, () -> YarnClient.getYarnClient("svc", Collections.emptyMap()));
+ assertThrows(HadoopException.class, () -> YarnClient.getYarnClient("svc", null));
+ }
+
+ @Test
+ void test02_getYarnClient_whenConfigsValid_returnsInstance() {
+ Map cfg = sampleConfigs();
+
+ YarnClient client = YarnClient.getYarnClient("svc", cfg);
+
+ assertNotNull(client);
+ }
+
+ @Test
+ void test03_connectionTest_whenQueuesFound_marksConnectivitySuccess() {
+ Map cfg = sampleConfigs();
+ YarnClient mockClient = mock(YarnClient.class);
+
+ try (MockedStatic mocked = mockStatic(YarnClient.class, Mockito.CALLS_REAL_METHODS)) {
+ mocked.when(() -> YarnClient.getYarnClient(eq("svc"), eq(cfg))).thenReturn(mockClient);
+ mocked.when(() -> YarnClient.getYarnResource(same(mockClient), eq(""), isNull()))
+ .thenReturn(Collections.singletonList("root"));
+
+ Map response = YarnClient.connectionTest("svc", cfg);
+
+ assertTrue(Boolean.TRUE.equals(response.get("connectivityStatus")));
+ }
+ }
+
+ @Test
+ void test04_connectionTest_whenNoQueues_marksConnectivityFailure() {
+ Map cfg = sampleConfigs();
+ YarnClient mockClient = mock(YarnClient.class);
+
+ try (MockedStatic mocked = mockStatic(YarnClient.class, Mockito.CALLS_REAL_METHODS)) {
+ mocked.when(() -> YarnClient.getYarnClient(eq("svc"), eq(cfg))).thenReturn(mockClient);
+ mocked.when(() -> YarnClient.getYarnResource(same(mockClient), eq(""), isNull()))
+ .thenReturn(Collections.emptyList());
+
+ Map response = YarnClient.connectionTest("svc", cfg);
+
+ assertFalse(Boolean.TRUE.equals(response.get("connectivityStatus")));
+ }
+ }
+
+ @Test
+ void test05_getYarnResource_whenClientNull_throwsHadoopException() {
+ assertThrows(HadoopException.class, () -> YarnClient.getYarnResource(null, "q", null));
+ }
+
+ @Test
+ void test06_getYarnResource_whenQueueNameNull_returnsEmptyList() {
+ Map cfg = sampleConfigs();
+ YarnClient client = YarnClient.getYarnClient("svc", cfg);
+
+ List queues = YarnClient.getYarnResource(client, null, null);
+
+ assertNotNull(queues);
+ assertTrue(queues.isEmpty());
+ }
+
+ @Test
+ void test07_timedTask_invokesCallable() throws Exception {
+ String value = YarnClient.timedTask(() -> "ok", 1, TimeUnit.SECONDS);
+
+ assertEquals("ok", value);
+ }
+
+ @Test
+ void test08_getYarnResource_whenGetQueueListThrows_wrapsAsHadoopException() {
+ Map cfg = sampleConfigs();
+ YarnClient client = new ExplodingGetQueueListYarnClient("svc", cfg);
+
+ assertThrows(HadoopException.class, () -> YarnClient.getYarnResource(client, "q", null));
+ }
+
+ @Test
+ void test09_getQueueList_whenYarnUrlBlank_returnsNull() {
+ Map cfg = sampleConfigs();
+ cfg.put("yarn.url", " ");
+ YarnClient client = new FixedSubjectYarnClient("svc", cfg);
+
+ try (MockedStatic subjectStatic = mockStatic(Subject.class)) {
+ mockSubjectDoAsRunsAction(subjectStatic);
+
+ List out = client.getQueueList("root", null);
+
+ assertNull(out);
+ }
+ }
+
+ @Test
+ void test10_getQueueList_whenResponse200_returnsMatchingQueues() {
+ Map cfg = sampleConfigs();
+ YarnClient client = new FixedSubjectYarnClient("svc", cfg);
+ String json = fairSchedulerJsonQueues("root", "default", "prodline");
+
+ Response response = mock(Response.class);
+ when(response.getStatus()).thenReturn(200);
+ when(response.readEntity(String.class)).thenReturn(json);
+
+ Client jaxrsClient = mock(Client.class);
+ WebTarget target = mock(WebTarget.class);
+ Invocation.Builder builder = mock(Invocation.Builder.class);
+ when(jaxrsClient.target(anyString())).thenReturn(target);
+ when(target.request(MediaType.APPLICATION_JSON)).thenReturn(builder);
+ when(builder.get()).thenReturn(response);
+
+ try (MockedStatic subjectStatic = mockStatic(Subject.class);
+ MockedStatic jer = mockStatic(RangerJersey2ClientBuilder.class)) {
+ mockSubjectDoAsRunsAction(subjectStatic);
+ jer.when(RangerJersey2ClientBuilder::newClient).thenReturn(jaxrsClient);
+
+ List out = client.getQueueList("pro", null);
+
+ assertNotNull(out);
+ assertTrue(out.contains("prodline"));
+ assertFalse(out.contains("root"));
+ }
+ }
+
+ @Test
+ void test11_getQueueList_skipsAlreadyListedQueues() {
+ Map cfg = sampleConfigs();
+ YarnClient client = new FixedSubjectYarnClient("svc", cfg);
+ String json = fairSchedulerJsonQueues("root", "default");
+
+ Response response = mock(Response.class);
+ when(response.getStatus()).thenReturn(200);
+ when(response.readEntity(String.class)).thenReturn(json);
+
+ Client jaxrsClient = mock(Client.class);
+ WebTarget target = mock(WebTarget.class);
+ Invocation.Builder builder = mock(Invocation.Builder.class);
+ when(jaxrsClient.target(anyString())).thenReturn(target);
+ when(target.request(MediaType.APPLICATION_JSON)).thenReturn(builder);
+ when(builder.get()).thenReturn(response);
+
+ try (MockedStatic subjectStatic = mockStatic(Subject.class);
+ MockedStatic jer = mockStatic(RangerJersey2ClientBuilder.class)) {
+ mockSubjectDoAsRunsAction(subjectStatic);
+ jer.when(RangerJersey2ClientBuilder::newClient).thenReturn(jaxrsClient);
+
+ List out = client.getQueueList("", Collections.singletonList("root"));
+
+ assertNotNull(out);
+ assertTrue(out.contains("default"));
+ assertFalse(out.contains("root"));
+ }
+ }
+
+ @Test
+ void test12_getQueueList_whenJsonInvalid_throwsHadoopException() {
+ Map cfg = sampleConfigs();
+ YarnClient client = new FixedSubjectYarnClient("svc", cfg);
+
+ Response response = mock(Response.class);
+ when(response.getStatus()).thenReturn(200);
+ when(response.readEntity(String.class)).thenReturn("not-json{");
+
+ Client jaxrsClient = mock(Client.class);
+ WebTarget target = mock(WebTarget.class);
+ Invocation.Builder builder = mock(Invocation.Builder.class);
+ when(jaxrsClient.target(anyString())).thenReturn(target);
+ when(target.request(MediaType.APPLICATION_JSON)).thenReturn(builder);
+ when(builder.get()).thenReturn(response);
+
+ try (MockedStatic subjectStatic = mockStatic(Subject.class);
+ MockedStatic jer = mockStatic(RangerJersey2ClientBuilder.class)) {
+ mockSubjectDoAsRunsAction(subjectStatic);
+ jer.when(RangerJersey2ClientBuilder::newClient).thenReturn(jaxrsClient);
+
+ assertThrows(HadoopException.class, () -> client.getQueueList("x", null));
+ }
+ }
+
+ @Test
+ void test13_getQueueList_whenHttpGetThrows_continuesToFailure() {
+ Map cfg = new HashMap<>();
+ cfg.put("yarn.url", "http://bad:8088");
+ cfg.put("username", "u");
+ cfg.put("password", "p");
+ YarnClient client = new FixedSubjectYarnClient("svc", cfg);
+
+ Client jaxrsClient = mock(Client.class);
+ WebTarget target = mock(WebTarget.class);
+ Invocation.Builder builder = mock(Invocation.Builder.class);
+ when(jaxrsClient.target(anyString())).thenReturn(target);
+ when(target.request(MediaType.APPLICATION_JSON)).thenReturn(builder);
+ when(builder.get()).thenThrow(new ProcessingException("network"));
+
+ try (MockedStatic subjectStatic = mockStatic(Subject.class);
+ MockedStatic jer = mockStatic(RangerJersey2ClientBuilder.class)) {
+ mockSubjectDoAsRunsAction(subjectStatic);
+ jer.when(RangerJersey2ClientBuilder::newClient).thenReturn(jaxrsClient);
+
+ assertThrows(HadoopException.class, () -> client.getQueueList("q", null));
+ }
+ }
+
+ @Test
+ void test14_getQueueList_whenStatusNot200_fallsThroughToFailure() {
+ Map cfg = sampleConfigs();
+ YarnClient client = new FixedSubjectYarnClient("svc", cfg);
+
+ Response response = mock(Response.class);
+ when(response.getStatus()).thenReturn(404);
+ when(response.readEntity(String.class)).thenReturn("err");
+
+ Client jaxrsClient = mock(Client.class);
+ WebTarget target = mock(WebTarget.class);
+ Invocation.Builder builder = mock(Invocation.Builder.class);
+ when(jaxrsClient.target(anyString())).thenReturn(target);
+ when(target.request(MediaType.APPLICATION_JSON)).thenReturn(builder);
+ when(builder.get()).thenReturn(response);
+
+ try (MockedStatic subjectStatic = mockStatic(Subject.class);
+ MockedStatic jer = mockStatic(RangerJersey2ClientBuilder.class)) {
+ mockSubjectDoAsRunsAction(subjectStatic);
+ jer.when(RangerJersey2ClientBuilder::newClient).thenReturn(jaxrsClient);
+
+ assertThrows(HadoopException.class, () -> client.getQueueList("q", null));
+ }
+ }
+
+ @Test
+ void test15_getQueueList_whenSecondUrlSucceeds_returnsQueues() {
+ Map cfg = new HashMap<>();
+ cfg.put("yarn.url", "http://skip:8088,http://ok:8088");
+ cfg.put("username", "u");
+ cfg.put("password", "p");
+ YarnClient client = new FixedSubjectYarnClient("svc", cfg);
+ String json = fairSchedulerJsonQueues("alpha");
+
+ Response bad = mock(Response.class);
+ when(bad.getStatus()).thenReturn(500);
+
+ Response good = mock(Response.class);
+ when(good.getStatus()).thenReturn(200);
+ when(good.readEntity(String.class)).thenReturn(json);
+
+ Client jaxrsClient = mock(Client.class);
+ WebTarget target = mock(WebTarget.class);
+ Invocation.Builder builder = mock(Invocation.Builder.class);
+ when(jaxrsClient.target(anyString())).thenReturn(target);
+ when(target.request(MediaType.APPLICATION_JSON)).thenReturn(builder);
+ when(builder.get()).thenReturn(bad, good);
+
+ try (MockedStatic subjectStatic = mockStatic(Subject.class);
+ MockedStatic jer = mockStatic(RangerJersey2ClientBuilder.class)) {
+ mockSubjectDoAsRunsAction(subjectStatic);
+ jer.when(RangerJersey2ClientBuilder::newClient).thenReturn(jaxrsClient);
+
+ List out = client.getQueueList("al", null);
+
+ assertNotNull(out);
+ assertTrue(out.contains("alpha"));
+ }
+ }
+
+ @Test
+ void test16_getQueueList_whenTimedTaskThrows_wrapsAsHadoopException() {
+ Map cfg = sampleConfigs();
+ YarnClient client = new FixedSubjectYarnClient("svc", cfg);
+
+ try (MockedStatic yc = mockStatic(YarnClient.class, Mockito.CALLS_REAL_METHODS)) {
+ yc.when(() -> YarnClient.timedTask(any(), any(Long.class), any(TimeUnit.class)))
+ .thenThrow(new RuntimeException("clock"));
+
+ assertThrows(HadoopException.class, () -> client.getQueueList("q", null));
+ }
+ }
+
+ @Test
+ void test17_getQueueList_whenReadEntityThrowsHadoopException_propagates() {
+ Map cfg = sampleConfigs();
+ YarnClient client = new FixedSubjectYarnClient("svc", cfg);
+
+ Response response = mock(Response.class);
+ when(response.getStatus()).thenReturn(200);
+ when(response.readEntity(String.class)).thenThrow(new HadoopException("rest"));
+
+ Client jaxrsClient = mock(Client.class);
+ WebTarget target = mock(WebTarget.class);
+ Invocation.Builder builder = mock(Invocation.Builder.class);
+ when(jaxrsClient.target(anyString())).thenReturn(target);
+ when(target.request(MediaType.APPLICATION_JSON)).thenReturn(builder);
+ when(builder.get()).thenReturn(response);
+
+ try (MockedStatic subjectStatic = mockStatic(Subject.class);
+ MockedStatic jer = mockStatic(RangerJersey2ClientBuilder.class)) {
+ mockSubjectDoAsRunsAction(subjectStatic);
+ jer.when(RangerJersey2ClientBuilder::newClient).thenReturn(jaxrsClient);
+
+ assertThrows(HadoopException.class, () -> client.getQueueList("q", null));
+ }
+ }
+
+ @Test
+ void test18_getQueueList_skipsBlankUrlSegmentsBetweenSeparators() {
+ Map cfg = new HashMap<>();
+ cfg.put("yarn.url", "http://a:8088,,;http://b:8088");
+ cfg.put("username", "u");
+ cfg.put("password", "p");
+ YarnClient client = new FixedSubjectYarnClient("svc", cfg);
+ String json = fairSchedulerJsonQueues("z1");
+
+ Response response = mock(Response.class);
+ when(response.getStatus()).thenReturn(200);
+ when(response.readEntity(String.class)).thenReturn(json);
+
+ Client jaxrsClient = mock(Client.class);
+ WebTarget target = mock(WebTarget.class);
+ Invocation.Builder builder = mock(Invocation.Builder.class);
+ when(jaxrsClient.target(anyString())).thenReturn(target);
+ when(target.request(MediaType.APPLICATION_JSON)).thenReturn(builder);
+ when(builder.get()).thenReturn(response);
+
+ try (MockedStatic subjectStatic = mockStatic(Subject.class);
+ MockedStatic jer = mockStatic(RangerJersey2ClientBuilder.class)) {
+ mockSubjectDoAsRunsAction(subjectStatic);
+ jer.when(RangerJersey2ClientBuilder::newClient).thenReturn(jaxrsClient);
+
+ List out = client.getQueueList("z", null);
+
+ assertNotNull(out);
+ assertTrue(out.contains("z1"));
+ }
+ }
+
+ @Test
+ void test19_connectionTest_whenGetYarnResourceReturnsNull_marksFailure() {
+ Map cfg = sampleConfigs();
+
+ try (MockedStatic mocked = mockStatic(YarnClient.class, Mockito.CALLS_REAL_METHODS)) {
+ YarnClient mockClient = mock(YarnClient.class);
+ mocked.when(() -> YarnClient.getYarnClient(eq("svc"), eq(cfg))).thenReturn(mockClient);
+ mocked.when(() -> YarnClient.getYarnResource(same(mockClient), eq(""), isNull())).thenReturn(null);
+
+ Map response = YarnClient.connectionTest("svc", cfg);
+
+ assertFalse(Boolean.TRUE.equals(response.get("connectivityStatus")));
+ }
+ }
+
+ @Test
+ void test20_constructor_whenOptionalFieldsBlank_logsWarnings() {
+ Map cfg = new HashMap<>();
+ cfg.put("yarn.url", "");
+ cfg.put("username", "");
+ cfg.put("password", "");
+
+ assertDoesNotThrow(() -> new YarnClient("svc", cfg));
+ }
+
+ @Test
+ void test21_getQueueList_whenSubjectMissing_returnsNullWithoutHttp() {
+ Map cfg = sampleConfigs();
+ YarnClient client = new NullSubjectYarnClient("svc", cfg);
+
+ assertNull(client.getQueueList("any", null));
+ }
+
+ @Test
+ void test22_getQueueList_whenSchedulerJsonNull_returnsEmptyLeafList() {
+ Map cfg = sampleConfigs();
+ YarnClient client = new FixedSubjectYarnClient("svc", cfg);
+
+ Response response = mock(Response.class);
+ when(response.getStatus()).thenReturn(200);
+ when(response.readEntity(String.class)).thenReturn("null");
+
+ Client jaxrsClient = mock(Client.class);
+ WebTarget target = mock(WebTarget.class);
+ Invocation.Builder builder = mock(Invocation.Builder.class);
+ when(jaxrsClient.target(anyString())).thenReturn(target);
+ when(target.request(MediaType.APPLICATION_JSON)).thenReturn(builder);
+ when(builder.get()).thenReturn(response);
+
+ try (MockedStatic subjectStatic = mockStatic(Subject.class);
+ MockedStatic jer = mockStatic(RangerJersey2ClientBuilder.class)) {
+ mockSubjectDoAsRunsAction(subjectStatic);
+ jer.when(RangerJersey2ClientBuilder::newClient).thenReturn(jaxrsClient);
+
+ List out = client.getQueueList("pre", null);
+
+ assertNotNull(out);
+ assertTrue(out.isEmpty());
+ }
+ }
+
+ @Test
+ void test23_getQueueList_whenNoQueuesMatchPrefix_returnsEmptyList() {
+ Map cfg = sampleConfigs();
+ YarnClient client = new FixedSubjectYarnClient("svc", cfg);
+ String json = fairSchedulerJsonQueues("alpha", "beta");
+
+ Response response = mock(Response.class);
+ when(response.getStatus()).thenReturn(200);
+ when(response.readEntity(String.class)).thenReturn(json);
+
+ Client jaxrsClient = mock(Client.class);
+ WebTarget target = mock(WebTarget.class);
+ Invocation.Builder builder = mock(Invocation.Builder.class);
+ when(jaxrsClient.target(anyString())).thenReturn(target);
+ when(target.request(MediaType.APPLICATION_JSON)).thenReturn(builder);
+ when(builder.get()).thenReturn(response);
+
+ try (MockedStatic subjectStatic = mockStatic(Subject.class);
+ MockedStatic jer = mockStatic(RangerJersey2ClientBuilder.class)) {
+ mockSubjectDoAsRunsAction(subjectStatic);
+ jer.when(RangerJersey2ClientBuilder::newClient).thenReturn(jaxrsClient);
+
+ List out = client.getQueueList("zzz", null);
+
+ assertNotNull(out);
+ assertTrue(out.isEmpty());
+ }
+ }
+
+ @Test
+ void test24_getQueueList_whenResponseGetStatusThrows_nonHadoopExceptionHandledPerUrl() {
+ Map cfg = sampleConfigs();
+ YarnClient client = new FixedSubjectYarnClient("svc", cfg);
+
+ Response response = mock(Response.class);
+ when(response.getStatus()).thenThrow(new RuntimeException("bad-status"));
+
+ Client jaxrsClient = mock(Client.class);
+ WebTarget target = mock(WebTarget.class);
+ Invocation.Builder builder = mock(Invocation.Builder.class);
+ when(jaxrsClient.target(anyString())).thenReturn(target);
+ when(target.request(MediaType.APPLICATION_JSON)).thenReturn(builder);
+ when(builder.get()).thenReturn(response);
+
+ try (MockedStatic subjectStatic = mockStatic(Subject.class);
+ MockedStatic jer = mockStatic(RangerJersey2ClientBuilder.class)) {
+ mockSubjectDoAsRunsAction(subjectStatic);
+ jer.when(RangerJersey2ClientBuilder::newClient).thenReturn(jaxrsClient);
+
+ assertThrows(HadoopException.class, () -> client.getQueueList("q", null));
+ }
+ }
+
+ @Test
+ void test25_getQueueResponse_whenHttpReturnsNullResponse_retriesUntilFailure() {
+ Map cfg = sampleConfigs();
+ YarnClient client = new FixedSubjectYarnClient("svc", cfg);
+
+ Client jaxrsClient = mock(Client.class);
+ WebTarget target = mock(WebTarget.class);
+ Invocation.Builder builder = mock(Invocation.Builder.class);
+ when(jaxrsClient.target(anyString())).thenReturn(target);
+ when(target.request(MediaType.APPLICATION_JSON)).thenReturn(builder);
+ when(builder.get()).thenReturn(null);
+
+ try (MockedStatic subjectStatic = mockStatic(Subject.class);
+ MockedStatic jer = mockStatic(RangerJersey2ClientBuilder.class)) {
+ mockSubjectDoAsRunsAction(subjectStatic);
+ jer.when(RangerJersey2ClientBuilder::newClient).thenReturn(jaxrsClient);
+
+ assertThrows(HadoopException.class, () -> client.getQueueList("q", null));
+ }
+ }
+
+ @Test
+ void test26_getQueueList_whenYarnUrlUnset_returnsNullFromPrivilegedAction() {
+ Map cfg = new HashMap<>();
+ cfg.put("username", "u");
+ cfg.put("password", "p");
+ YarnClient client = new FixedSubjectYarnClient("svc", cfg);
+
+ try (MockedStatic subjectStatic = mockStatic(Subject.class)) {
+ mockSubjectDoAsRunsAction(subjectStatic);
+ assertNull(client.getQueueList("q", null));
+ }
+ }
+
+ @Test
+ void test27_getQueueList_whenNon200ResponseCloseThrowsHadoopException_propagates() {
+ Map cfg = sampleConfigs();
+ YarnClient client = new FixedSubjectYarnClient("svc", cfg);
+
+ Response response = mock(Response.class);
+ when(response.getStatus()).thenReturn(500);
+ doThrow(new HadoopException("close")).when(response).close();
+
+ Client jaxrsClient = mock(Client.class);
+ WebTarget target = mock(WebTarget.class);
+ Invocation.Builder builder = mock(Invocation.Builder.class);
+ when(jaxrsClient.target(anyString())).thenReturn(target);
+ when(target.request(MediaType.APPLICATION_JSON)).thenReturn(builder);
+ when(builder.get()).thenReturn(response);
+
+ try (MockedStatic subjectStatic = mockStatic(Subject.class);
+ MockedStatic jer = mockStatic(RangerJersey2ClientBuilder.class)) {
+ mockSubjectDoAsRunsAction(subjectStatic);
+ jer.when(RangerJersey2ClientBuilder::newClient).thenReturn(jaxrsClient);
+
+ assertThrows(HadoopException.class, () -> client.getQueueList("q", null));
+ }
+ }
+
+ @Test
+ void test28_getQueueList_after200ResponseCloseThrows_propagatesHadoopException() {
+ Map cfg = sampleConfigs();
+ YarnClient client = new FixedSubjectYarnClient("svc", cfg);
+ String json = fairSchedulerJsonQueues("leaf");
+
+ Response response = mock(Response.class);
+ when(response.getStatus()).thenReturn(200);
+ when(response.readEntity(String.class)).thenReturn(json);
+ doThrow(new HadoopException("close")).when(response).close();
+
+ Client jaxrsClient = mock(Client.class);
+ WebTarget target = mock(WebTarget.class);
+ Invocation.Builder builder = mock(Invocation.Builder.class);
+ when(jaxrsClient.target(anyString())).thenReturn(target);
+ when(target.request(MediaType.APPLICATION_JSON)).thenReturn(builder);
+ when(builder.get()).thenReturn(response);
+
+ try (MockedStatic subjectStatic = mockStatic(Subject.class);
+ MockedStatic jer = mockStatic(RangerJersey2ClientBuilder.class)) {
+ mockSubjectDoAsRunsAction(subjectStatic);
+ jer.when(RangerJersey2ClientBuilder::newClient).thenReturn(jaxrsClient);
+
+ assertThrows(HadoopException.class, () -> client.getQueueList("leaf", null));
+ }
+ }
+
+ private static final class FixedSubjectYarnClient extends YarnClient {
+ private FixedSubjectYarnClient(String serviceName, Map configs) {
+ super(serviceName, configs);
+ }
+
+ @Override
+ protected Subject getLoginSubject() {
+ return new Subject();
+ }
+ }
+
+ private static final class ExplodingGetQueueListYarnClient extends YarnClient {
+ private ExplodingGetQueueListYarnClient(String serviceName, Map configs) {
+ super(serviceName, configs);
+ }
+
+ @Override
+ public List getQueueList(String queueNameMatching, List existingQueueList) {
+ throw new RuntimeException("boom");
+ }
+ }
+
+ private static final class NullSubjectYarnClient extends YarnClient {
+ private NullSubjectYarnClient(String serviceName, Map configs) {
+ super(serviceName, configs);
+ }
+
+ @Override
+ protected Subject getLoginSubject() {
+ return null;
+ }
+ }
+}
diff --git a/plugin-yarn/src/test/java/org/apache/ranger/services/yarn/client/TestYarnConnectionMgr.java b/plugin-yarn/src/test/java/org/apache/ranger/services/yarn/client/TestYarnConnectionMgr.java
new file mode 100644
index 0000000000..4041ba0281
--- /dev/null
+++ b/plugin-yarn/src/test/java/org/apache/ranger/services/yarn/client/TestYarnConnectionMgr.java
@@ -0,0 +1,55 @@
+/*
+ * 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.ranger.services.yarn.client;
+
+import org.junit.jupiter.api.MethodOrderer;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.TestMethodOrder;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+/**
+ * @generated by Cursor
+ * @description : Unit Test cases for YarnConnectionMgr
+ */
+@ExtendWith(MockitoExtension.class)
+@TestMethodOrder(MethodOrderer.MethodName.class)
+class TestYarnConnectionMgr {
+ @Test
+ void test01_getYarnClient_withValidConfigs_returnsClient() {
+ Map cfg = new HashMap<>();
+ cfg.put("yarn.url", "http://rm:8088");
+ cfg.put("username", "u");
+ cfg.put("password", "p");
+
+ YarnClient client = YarnConnectionMgr.getYarnClient("svc", cfg);
+
+ assertNotNull(client);
+ }
+
+ @Test
+ void test02_getYarnClient_withNullConfigs_throwsNullPointerException() {
+ assertThrows(NullPointerException.class, () -> YarnConnectionMgr.getYarnClient("svc", null));
+ }
+}
diff --git a/plugin-yarn/src/test/java/org/apache/ranger/services/yarn/client/TestYarnResourceMgr.java b/plugin-yarn/src/test/java/org/apache/ranger/services/yarn/client/TestYarnResourceMgr.java
new file mode 100644
index 0000000000..69a3cb5790
--- /dev/null
+++ b/plugin-yarn/src/test/java/org/apache/ranger/services/yarn/client/TestYarnResourceMgr.java
@@ -0,0 +1,216 @@
+/*
+ * 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.ranger.services.yarn.client;
+
+import org.apache.ranger.plugin.service.ResourceLookupContext;
+import org.junit.jupiter.api.MethodOrderer;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.TestMethodOrder;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.MockedStatic;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.isNull;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.mockStatic;
+import static org.mockito.Mockito.when;
+
+/**
+ * @generated by Cursor
+ * @description : Unit Test cases for YarnResourceMgr
+ */
+@ExtendWith(MockitoExtension.class)
+@TestMethodOrder(MethodOrderer.MethodName.class)
+class TestYarnResourceMgr {
+ private Map sampleConfigs() {
+ Map cfg = new HashMap<>();
+ cfg.put("yarn.url", "http://rm:8088");
+ cfg.put("username", "u");
+ cfg.put("password", "p");
+ return cfg;
+ }
+
+ @Test
+ void test01_validateConfig_delegatesToYarnClient() {
+ Map cfg = sampleConfigs();
+ Map expected = new HashMap<>();
+ expected.put("connectivityStatus", Boolean.TRUE);
+
+ try (MockedStatic mocked = mockStatic(YarnClient.class)) {
+ mocked.when(() -> YarnClient.connectionTest(eq("svc"), eq(cfg))).thenReturn(expected);
+
+ Map actual = YarnResourceMgr.validateConfig("svc", cfg);
+
+ assertEquals(expected, actual);
+ mocked.verify(() -> YarnClient.connectionTest(eq("svc"), eq(cfg)));
+ }
+ }
+
+ @Test
+ void test02_validateConfig_whenYarnClientThrows_propagatesException() {
+ Map cfg = sampleConfigs();
+ RuntimeException boom = new RuntimeException("boom");
+
+ try (MockedStatic mocked = mockStatic(YarnClient.class)) {
+ mocked.when(() -> YarnClient.connectionTest(eq("svc"), eq(cfg))).thenThrow(boom);
+
+ assertThrows(RuntimeException.class, () -> YarnResourceMgr.validateConfig("svc", cfg));
+ }
+ }
+
+ @Test
+ void test03_getYarnResources_whenConfigsEmpty_returnsNull() {
+ ResourceLookupContext ctx = new ResourceLookupContext();
+ ctx.setUserInput("q");
+ ctx.setResourceName("queue");
+
+ List out = YarnResourceMgr.getYarnResources("svc", Collections.emptyMap(), ctx);
+
+ assertNull(out);
+ }
+
+ @Test
+ void test04_getYarnResources_whenConfigsPresent_returnsQueueListFromClient() {
+ Map cfg = sampleConfigs();
+ ResourceLookupContext ctx = new ResourceLookupContext();
+ ctx.setUserInput("root");
+ ctx.setResourceName("queue");
+
+ List expected = Collections.singletonList("root.default");
+ YarnClient mockClient = mock(YarnClient.class);
+ when(mockClient.getQueueList(eq("root"), isNull())).thenReturn(expected);
+
+ try (MockedStatic mgr = mockStatic(YarnConnectionMgr.class)) {
+ mgr.when(() -> YarnConnectionMgr.getYarnClient(eq("svc"), eq(cfg))).thenReturn(mockClient);
+
+ List actual = YarnResourceMgr.getYarnResources("svc", cfg, ctx);
+
+ assertEquals(expected, actual);
+ }
+ }
+
+ @Test
+ void test05_getYarnResource_whenClientNull_returnsNull() {
+ Map cfg = sampleConfigs();
+
+ try (MockedStatic mgr = mockStatic(YarnConnectionMgr.class)) {
+ mgr.when(() -> YarnConnectionMgr.getYarnClient(anyString(), any())).thenReturn(null);
+
+ assertNull(YarnResourceMgr.getYarnResource("svc", cfg, "q", null));
+ }
+ }
+
+ @Test
+ void test06_getYarnResource_whenClientPresent_returnsFilteredQueues() {
+ Map cfg = sampleConfigs();
+ List expected = Collections.singletonList("prod");
+ YarnClient mockClient = mock(YarnClient.class);
+ when(mockClient.getQueueList(eq("pro"), eq(Collections.singletonList("dev")))).thenReturn(expected);
+
+ try (MockedStatic mgr = mockStatic(YarnConnectionMgr.class)) {
+ mgr.when(() -> YarnConnectionMgr.getYarnClient(eq("svc"), eq(cfg))).thenReturn(mockClient);
+
+ List actual = YarnResourceMgr.getYarnResource("svc", cfg, "pro",
+ Collections.singletonList("dev"));
+
+ assertEquals(expected, actual);
+ }
+ }
+
+ @Test
+ void test07_getYarnResources_whenResourceMapContainsQueue_passesExistingQueues() {
+ Map cfg = sampleConfigs();
+ ResourceLookupContext ctx = new ResourceLookupContext();
+ ctx.setUserInput("needle");
+ ctx.setResourceName("queue");
+ Map> resourceMap = new HashMap<>();
+ resourceMap.put("queue", Collections.singletonList("existing"));
+ ctx.setResources(resourceMap);
+
+ List expected = Collections.singletonList("needle-found");
+ YarnClient mockClient = mock(YarnClient.class);
+ when(mockClient.getQueueList(eq("needle"), eq(Collections.singletonList("existing")))).thenReturn(
+ expected);
+
+ try (MockedStatic mgr = mockStatic(YarnConnectionMgr.class)) {
+ mgr.when(() -> YarnConnectionMgr.getYarnClient(eq("svc"), eq(cfg))).thenReturn(mockClient);
+
+ assertEquals(expected, YarnResourceMgr.getYarnResources("svc", cfg, ctx));
+ }
+ }
+
+ @Test
+ void test08_getYarnResources_whenConfigsNull_logsAndReturnsNull() {
+ ResourceLookupContext ctx = new ResourceLookupContext();
+ ctx.setUserInput("x");
+ ctx.setResourceName("queue");
+
+ assertNull(YarnResourceMgr.getYarnResources("svc", null, ctx));
+ }
+
+ @Test
+ void test09_getYarnResources_whenResourceMapMissingQueueKey_doesNotPassExistingQueues() {
+ Map cfg = sampleConfigs();
+ ResourceLookupContext ctx = new ResourceLookupContext();
+ ctx.setUserInput("root");
+ ctx.setResourceName("queue");
+ Map> resourceMap = new HashMap<>();
+ resourceMap.put("other", Collections.singletonList("x"));
+ ctx.setResources(resourceMap);
+
+ List expected = Collections.singletonList("root.default");
+ YarnClient mockClient = mock(YarnClient.class);
+ when(mockClient.getQueueList(eq("root"), isNull())).thenReturn(expected);
+
+ try (MockedStatic mgr = mockStatic(YarnConnectionMgr.class)) {
+ mgr.when(() -> YarnConnectionMgr.getYarnClient(eq("svc"), eq(cfg))).thenReturn(mockClient);
+
+ assertEquals(expected, YarnResourceMgr.getYarnResources("svc", cfg, ctx));
+ }
+ }
+
+ @Test
+ void test10_getYarnResources_whenResourceMapEmpty_skipsExistingQueuesBranch() {
+ Map cfg = sampleConfigs();
+ ResourceLookupContext ctx = new ResourceLookupContext();
+ ctx.setUserInput("root");
+ ctx.setResourceName("queue");
+ ctx.setResources(new HashMap>());
+
+ List expected = Collections.singletonList("root.default");
+ YarnClient mockClient = mock(YarnClient.class);
+ when(mockClient.getQueueList(eq("root"), isNull())).thenReturn(expected);
+
+ try (MockedStatic mgr = mockStatic(YarnConnectionMgr.class)) {
+ mgr.when(() -> YarnConnectionMgr.getYarnClient(eq("svc"), eq(cfg))).thenReturn(mockClient);
+
+ assertEquals(expected, YarnResourceMgr.getYarnResources("svc", cfg, ctx));
+ }
+ }
+}
diff --git a/plugin-yarn/src/test/java/org/apache/ranger/services/yarn/client/json/model/TestYarnSchedulerResponse.java b/plugin-yarn/src/test/java/org/apache/ranger/services/yarn/client/json/model/TestYarnSchedulerResponse.java
new file mode 100644
index 0000000000..18246099f8
--- /dev/null
+++ b/plugin-yarn/src/test/java/org/apache/ranger/services/yarn/client/json/model/TestYarnSchedulerResponse.java
@@ -0,0 +1,183 @@
+/*
+ * 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.ranger.services.yarn.client.json.model;
+
+import com.google.gson.Gson;
+import org.junit.jupiter.api.MethodOrderer;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.TestMethodOrder;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+/**
+ * @generated by Cursor
+ * @description : Unit Test cases for YarnSchedulerResponse
+ */
+@ExtendWith(MockitoExtension.class)
+@TestMethodOrder(MethodOrderer.MethodName.class)
+class TestYarnSchedulerResponse {
+ @Test
+ void test01_getQueueNames_fairScheduler_json_collectsRootAndChildren() {
+ String json = "{\"scheduler\":{\"schedulerInfo\":{\"type\":\"fairScheduler\",\"rootQueue\":"
+ + "{\"queueName\":\"root\",\"childQueues\":{\"queue\":[{\"queueName\":\"default\"}]}}}}}";
+
+ YarnSchedulerResponse response = new Gson().fromJson(json, YarnSchedulerResponse.class);
+
+ List names = response.getQueueNames();
+
+ assertTrue(names.contains("root"));
+ assertTrue(names.contains("default"));
+ }
+
+ @Test
+ void test02_getQueueNames_whenSchedulerNull_returnsEmpty() {
+ YarnSchedulerResponse response = new YarnSchedulerResponse();
+
+ List names = response.getQueueNames();
+
+ assertTrue(names.isEmpty());
+ }
+
+ @Test
+ void test03_getQueueNames_capacityScheduler_nested_fqdn() {
+ String json = "{\"scheduler\":{\"schedulerInfo\":{\"type\":\"capacityScheduler\","
+ + "\"queueName\":\"root\",\"queues\":{\"queue\":[{\"queueName\":\"leaf\","
+ + "\"type\":\"capacitySchedulerLeafQueueInfo\"}]}}}}";
+
+ List names = new Gson().fromJson(json, YarnSchedulerResponse.class).getQueueNames();
+
+ assertTrue(names.contains("root"));
+ assertTrue(names.contains("root.leaf"));
+ }
+
+ @Test
+ void test04_yarnScheduler_collectQueueNames_whenSchedulerInfoNull_noOp() {
+ YarnSchedulerResponse.YarnScheduler scheduler =
+ new YarnSchedulerResponse.YarnScheduler();
+ List acc = new ArrayList<>();
+ scheduler.collectQueueNames(acc);
+ assertTrue(acc.isEmpty());
+ }
+
+ @Test
+ void test05_schedulerInfo_fair_whenRootNull_collectDoesNothing() {
+ YarnSchedulerResponse.YarnSchedulerInfo info =
+ new YarnSchedulerResponse.YarnSchedulerInfo();
+ List q = new ArrayList<>();
+ info.collectQueueNames(q, null);
+ assertTrue(q.isEmpty());
+ }
+
+ @Test
+ void test06_schedulerInfo_unknownType_collectDoesNothing() {
+ YarnSchedulerResponse.YarnSchedulerInfo info =
+ new Gson().fromJson("{\"type\":\"other\"}", YarnSchedulerResponse.YarnSchedulerInfo.class);
+ List q = new ArrayList<>();
+ info.collectQueueNames(q, null);
+ assertTrue(q.isEmpty());
+ }
+
+ @Test
+ void test07_yarnQueues_collect_whenQueueListNull_noOp() {
+ YarnSchedulerResponse.YarnQueues queues = new YarnSchedulerResponse.YarnQueues();
+ List q = new ArrayList<>();
+ queues.collectQueueNames(q, "parent");
+ assertTrue(q.isEmpty());
+ }
+
+ @Test
+ void test08_rootQueue_whenQueueNameNull_noNamesAdded() {
+ YarnSchedulerResponse.RootQueue root = new YarnSchedulerResponse.RootQueue();
+ List q = new ArrayList<>();
+ root.collectQueueNames(q);
+ assertTrue(q.isEmpty());
+ }
+
+ @Test
+ void test09_childQueues_whenQueueNull_collectDoesNothing() {
+ YarnSchedulerResponse.ChildQueues c = new YarnSchedulerResponse.ChildQueues();
+ List q = new ArrayList<>();
+ c.collectQueueNames(q);
+ assertTrue(q.isEmpty());
+ }
+
+ @Test
+ void test10_getters_on_schedulerInfo_and_queues() {
+ YarnSchedulerResponse.YarnSchedulerInfo info =
+ new YarnSchedulerResponse.YarnSchedulerInfo();
+ assertNull(info.getType());
+ assertNull(info.getRootQueue());
+ assertNull(info.getQueueName());
+ assertNull(info.getQueues());
+ }
+
+ @Test
+ void test11_getScheduler_on_response_and_yarnScheduler() {
+ YarnSchedulerResponse r = new YarnSchedulerResponse();
+ assertNull(r.getScheduler());
+ YarnSchedulerResponse.YarnScheduler s =
+ new YarnSchedulerResponse.YarnScheduler();
+ assertNull(s.getSchedulerInfo());
+ }
+
+ @Test
+ void test12_fairScheduler_whenRootQueueNull_collectSkipsChildren() {
+ YarnSchedulerResponse.YarnSchedulerInfo info = new Gson().fromJson(
+ "{\"type\":\"fairScheduler\"}", YarnSchedulerResponse.YarnSchedulerInfo.class);
+
+ List q = new ArrayList<>();
+ info.collectQueueNames(q, null);
+ assertTrue(q.isEmpty());
+ }
+
+ @Test
+ void test13_capacityScheduler_whenQueueNameNull_skipsQueuesTraversal() {
+ YarnSchedulerResponse.YarnSchedulerInfo info = new Gson().fromJson(
+ "{\"type\":\"capacityScheduler\",\"queues\":{\"queue\":[]}}",
+ YarnSchedulerResponse.YarnSchedulerInfo.class);
+
+ List q = new ArrayList<>();
+ info.collectQueueNames(q, null);
+ assertTrue(q.isEmpty());
+ }
+
+ @Test
+ void test14_rootQueue_getters_reflectJsonTree() {
+ String json = "{\"queueName\":\"r\",\"childQueues\":{\"queue\":[{\"queueName\":\"leaf\"}]}}";
+ YarnSchedulerResponse.RootQueue rq = new Gson().fromJson(json,
+ YarnSchedulerResponse.RootQueue.class);
+
+ assertEquals("r", rq.getQueueName());
+ assertEquals(1, rq.getChildQueues().getQueue().size());
+ assertEquals("leaf", rq.getChildQueues().getQueue().get(0).getQueueName());
+ }
+
+ @Test
+ void test16_yarnQueues_whenQueueFieldAbsent_getQueueReturnsNull() {
+ YarnSchedulerResponse.YarnQueues yq = new Gson().fromJson("{}", YarnSchedulerResponse.YarnQueues.class);
+
+ assertNull(yq.getQueue());
+ }
+}