org.apache.hadoop.mapred.TestJobACLs.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.hadoop.mapred.TestJobACLs.java

Source

/**
 * 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.mapred;

import java.io.IOException;
import java.security.PrivilegedExceptionAction;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.hadoop.examples.SleepJob;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.mapred.JobClient;
import org.apache.hadoop.mapred.JobConf;
import org.apache.hadoop.mapred.JobPriority;
import org.apache.hadoop.mapred.JobStatus;
import org.apache.hadoop.mapred.RunningJob;
import org.apache.hadoop.mapred.QueueManager.QueueACL;
import org.apache.hadoop.security.UserGroupInformation;
import org.junit.Before;
import org.junit.Test;
import org.junit.After;
import static org.junit.Assert.fail;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.assertNotNull;

/**
 * Verify the job-ACLs
 * 
 */
public class TestJobACLs {

    static final Log LOG = LogFactory.getLog(TestJobACLs.class);

    private MiniMRCluster mr = null;

    private static final Path TEST_DIR = new Path(System.getProperty("test.build.data", "/tmp"),
            TestJobACLs.class.getCanonicalName() + Path.SEPARATOR + "completed-job-store");

    private String jobSubmitter = "jobSubmitter";
    private String viewColleague = "viewColleague";
    private String modifyColleague = "modifyColleague";
    private String qAdmin = "qAdmin";

    /**
     * Start the cluster before running the actual test.
     * 
     * @throws IOException
     */
    @Before
    public void setup() throws IOException {
        // Start the cluster
        startCluster(false);
    }

    private void startCluster(boolean reStart) throws IOException {
        UserGroupInformation MR_UGI = UserGroupInformation.getLoginUser();
        JobConf conf = new JobConf();

        // Enable queue and job level authorization
        conf.setBoolean(JobConf.MR_ACLS_ENABLED, true);
        // qAdmin is a queue administrator for default queue
        conf.set(QueueManager.toFullPropertyName("default", QueueACL.ADMINISTER_JOBS.getAclName()), qAdmin);
        conf.set(QueueManager.toFullPropertyName("default", QueueACL.SUBMIT_JOB.getAclName()), jobSubmitter);

        // Enable CompletedJobStore
        FileSystem fs = FileSystem.getLocal(conf);
        if (!reStart) {
            fs.delete(TEST_DIR, true);
        }
        conf.set("mapred.job.tracker.persist.jobstatus.dir", fs.makeQualified(TEST_DIR).toString());
        conf.setBoolean("mapred.job.tracker.persist.jobstatus.active", true);
        conf.set("mapred.job.tracker.persist.jobstatus.hours", "1");

        // Let us have enough slots sothat there won't be contention for slots
        // for launching JOB_CLEANUP tasks
        conf.set("mapred.tasktracker.map.tasks.maximum", "4");

        mr = new MiniMRCluster(0, 0, 2, "file:///", 1, null, null, MR_UGI, conf);
    }

    /**
     * Kill the cluster after the test is done.
     */
    @After
    public void tearDown() {
        if (mr != null) {
            mr.shutdown();
        }
    }

    /**
     * Test view-job-acl, modify-job-acl and acl persistence to the
     * completed-jobs-store.
     * 
     * @throws IOException
     * @throws InterruptedException
     * @throws ClassNotFoundException
     */
    @Test
    public void testACLS() throws IOException, InterruptedException, ClassNotFoundException {
        verifyACLViewJob();
        verifyACLModifyJob(modifyColleague);
        verifyACLModifyJob(qAdmin);
        verifyACLPersistence();
    }

    /**
     * Verify JobContext.JOB_ACL_VIEW_JOB
     * 
     * @throws IOException
     * @throws InterruptedException
     */
    private void verifyACLViewJob() throws IOException, InterruptedException {

        // Set the job up.
        final JobConf myConf = mr.createJobConf();
        myConf.set(JobContext.JOB_ACL_VIEW_JOB, viewColleague);

        // Submit the job as user1
        RunningJob job = submitJobAsUser(myConf, jobSubmitter);

        final JobID jobId = job.getID();

        // Try operations as an unauthorized user.
        verifyViewJobAsUnauthorizedUser(myConf, jobId, modifyColleague);

        // Try operations as an authorized user, who is part of view-job-acl.
        verifyViewJobAsAuthorizedUser(myConf, jobId, viewColleague);

        // Try operations as an authorized user, who is a queue administrator.
        verifyViewJobAsAuthorizedUser(myConf, jobId, qAdmin);

        // Clean up the job
        job.killJob();
    }

    /**
     * Submits a sleep job with 1 map task that runs for a long time(2000 sec)
     * @param clusterConf
     * @param user the jobOwner
     * @return RunningJob that is started
     * @throws IOException
     * @throws InterruptedException
     */
    private RunningJob submitJobAsUser(final JobConf clusterConf, String user)
            throws IOException, InterruptedException {
        UserGroupInformation ugi = UserGroupInformation.createUserForTesting(user, new String[] {});
        RunningJob job = (RunningJob) ugi.doAs(new PrivilegedExceptionAction<Object>() {
            @Override
            public Object run() throws Exception {
                JobClient jobClient = new JobClient(clusterConf);
                SleepJob sleepJob = new SleepJob();
                sleepJob.setConf(clusterConf);
                JobConf jobConf = sleepJob.setupJobConf(1, 0, 2000, 1000, 1000, 1000);
                RunningJob runningJob = jobClient.submitJob(jobConf);
                return runningJob;
            }
        });
        return job;
    }

    private void verifyViewJobAsAuthorizedUser(final JobConf myConf, final JobID jobId, String authorizedUser)
            throws IOException, InterruptedException {
        UserGroupInformation authorizedUGI = UserGroupInformation.createUserForTesting(authorizedUser,
                new String[] {});
        authorizedUGI.doAs(new PrivilegedExceptionAction<Object>() {
            @SuppressWarnings("null")
            @Override
            public Object run() throws Exception {
                RunningJob myJob = null;
                JobClient client = null;
                try {
                    client = new JobClient(myConf);
                    myJob = client.getJob(jobId);
                } catch (Exception e) {
                    fail("Exception .." + e);
                }

                assertNotNull("Job " + jobId + " is not known to the JobTracker!", myJob);

                // Tests authorization with getCounters
                try {
                    myJob.getCounters();
                } catch (IOException ioe) {
                    fail("Unexpected.. exception.. " + ioe);
                }

                // Tests authorization  with getTaskReports
                try {
                    client.getCleanupTaskReports(jobId);
                } catch (IOException ioe) {
                    fail("Unexpected.. exception.. " + ioe);
                }

                return null;
            }
        });
    }

    private void verifyViewJobAsUnauthorizedUser(final JobConf myConf, final JobID jobId, String unauthorizedUser)
            throws IOException, InterruptedException {
        UserGroupInformation unauthorizedUGI = UserGroupInformation.createUserForTesting(unauthorizedUser,
                new String[] {});
        unauthorizedUGI.doAs(new PrivilegedExceptionAction<Object>() {
            @SuppressWarnings("null")
            @Override
            public Object run() {
                RunningJob myJob = null;
                JobClient client = null;
                try {
                    client = new JobClient(myConf);
                    myJob = client.getJob(jobId);
                } catch (Exception e) {
                    fail("Exception .." + e);
                }

                assertNotNull("Job " + jobId + " is not known to the JobTracker!", myJob);

                // Tests authorization failure with getCounters
                try {
                    myJob.getCounters();
                    fail("AccessControlException expected..");
                } catch (IOException ioe) {
                    assertTrue(ioe.getMessage().contains("AccessControlException"));
                }

                // Tests authorization failure with getTaskReports
                try {
                    client.getSetupTaskReports(jobId);
                    fail("AccessControlException expected..");
                } catch (IOException ioe) {
                    assertTrue(ioe.getMessage().contains("AccessControlException"));
                }

                return null;
            }
        });
    }

    /**
     * Verify JobContext.Job_ACL_MODIFY_JOB
     * 
     * @throws IOException
     * @throws InterruptedException
     * @throws ClassNotFoundException
     */
    private void verifyACLModifyJob(String authorizedUser)
            throws IOException, InterruptedException, ClassNotFoundException {

        // Set the job up.
        final JobConf myConf = mr.createJobConf();
        myConf.set(JobContext.JOB_ACL_MODIFY_JOB, modifyColleague);

        // Submit the job as user1
        RunningJob job = submitJobAsUser(myConf, jobSubmitter);

        final JobID jobId = job.getID();

        // Try operations as an unauthorized user.
        verifyModifyJobAsUnauthorizedUser(myConf, jobId, viewColleague);

        // Try operations as an authorized user.
        verifyModifyJobAsAuthorizedUser(myConf, jobId, authorizedUser);
    }

    private void verifyModifyJobAsAuthorizedUser(final JobConf clusterConf, final JobID jobId,
            String authorizedUser) throws IOException, InterruptedException {
        UserGroupInformation authorizedUGI = UserGroupInformation.createUserForTesting(authorizedUser,
                new String[] {});
        authorizedUGI.doAs(new PrivilegedExceptionAction<Object>() {
            @SuppressWarnings("null")
            @Override
            public Object run() throws Exception {
                RunningJob myJob = null;
                try {
                    JobClient client = new JobClient(clusterConf);
                    myJob = client.getJob(jobId);
                } catch (Exception e) {
                    fail("Exception .." + e);
                }

                assertNotNull("Job " + jobId + " is not known to the JobTracker!", myJob);

                // Test authorization success with setJobPriority
                try {
                    myJob.setJobPriority(JobPriority.HIGH.toString());
                } catch (IOException ioe) {
                    fail("Unexpected.. exception.. " + ioe);
                }

                // Test authorization success with killJob
                try {
                    myJob.killJob();
                } catch (IOException ioe) {
                    fail("Unexpected.. exception.. " + ioe);
                }

                return null;
            }
        });
    }

    private void verifyModifyJobAsUnauthorizedUser(final JobConf clusterConf, final JobID jobId,
            String unauthorizedUser) throws IOException, InterruptedException {
        UserGroupInformation unauthorizedUGI = UserGroupInformation.createUserForTesting(unauthorizedUser,
                new String[] {});
        unauthorizedUGI.doAs(new PrivilegedExceptionAction<Object>() {
            @SuppressWarnings("null")
            @Override
            public Object run() {
                RunningJob myJob = null;
                try {
                    JobClient client = new JobClient(clusterConf);
                    myJob = client.getJob(jobId);
                } catch (Exception e) {
                    fail("Exception .." + e);
                }

                assertNotNull("Job " + jobId + " is not known to the JobTracker!", myJob);

                // Tests authorization failure with killJob
                try {
                    myJob.killJob();
                    fail("AccessControlException expected..");
                } catch (IOException ioe) {
                    assertTrue(ioe.getMessage().contains("AccessControlException"));
                }

                // Tests authorization failure with setJobPriority
                try {
                    myJob.setJobPriority(JobPriority.HIGH.toString());
                    fail("AccessControlException expected..");
                } catch (IOException ioe) {
                    assertTrue(ioe.getMessage().contains("AccessControlException"));
                }

                return null;
            }
        });
    }

    private void verifyACLPersistence() throws IOException, InterruptedException {

        // Set the job up.
        final JobConf myConf = mr.createJobConf();
        myConf.set(JobContext.JOB_ACL_VIEW_JOB, viewColleague + " group2");

        // Submit the job as user1
        RunningJob job = submitJobAsUser(myConf, jobSubmitter);

        final JobID jobId = job.getID();

        // Kill the job and wait till it is actually killed so that it is written to
        // CompletedJobStore
        job.killJob();
        while (job.getJobState() != JobStatus.KILLED) {
            LOG.info("Waiting for the job to be killed successfully..");
            Thread.sleep(200);
        }

        // Now kill the cluster, so that the job is 'forgotten'
        tearDown();

        // Re-start the cluster
        startCluster(true);

        final JobConf myNewJobConf = mr.createJobConf();
        // Now verify view-job works off CompletedJobStore
        verifyViewJobAsAuthorizedUser(myNewJobConf, jobId, viewColleague);
        verifyViewJobAsAuthorizedUser(myNewJobConf, jobId, qAdmin);

        // Only JobCounters is persisted on the JobStore. So test counters only.
        UserGroupInformation unauthorizedUGI = UserGroupInformation.createUserForTesting(modifyColleague,
                new String[] {});

        unauthorizedUGI.doAs(new PrivilegedExceptionAction<Object>() {
            @SuppressWarnings("null")
            @Override
            public Object run() {
                RunningJob myJob = null;
                try {
                    JobClient client = new JobClient(myNewJobConf);
                    myJob = client.getJob(jobId);
                } catch (Exception e) {
                    fail("Exception .." + e);
                }

                assertNotNull("Job " + jobId + " is not known to the JobTracker!", myJob);

                // Tests authorization failure with getCounters
                try {
                    myJob.getCounters();
                    fail("AccessControlException expected..");
                } catch (IOException ioe) {
                    assertTrue(ioe.getMessage().contains("AccessControlException"));
                }

                return null;
            }
        });

    }
}