blob: 787000bc7515445575b06b99629e0222d7d74ec7 [file] [log] [blame]
/**
* 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.hdfs;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.io.IOException;
import java.util.UUID;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.CommonConfigurationKeys;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.fs.TestTrash;
import org.apache.hadoop.fs.Trash;
import org.apache.hadoop.fs.permission.FsAction;
import org.apache.hadoop.fs.permission.FsPermission;
import org.apache.hadoop.security.AccessControlException;
import org.apache.hadoop.security.UserGroupInformation;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import org.mockito.Mockito;
/**
* Test trash using HDFS
*/
public class TestHDFSTrash {
public static final Log LOG = LogFactory.getLog(TestHDFSTrash.class);
private static MiniDFSCluster cluster = null;
private static FileSystem fs;
private static Configuration conf = new HdfsConfiguration();
private final static Path TEST_ROOT = new Path("/TestHDFSTrash-ROOT");
private final static Path TRASH_ROOT = new Path("/TestHDFSTrash-TRASH");
final private static String GROUP1_NAME = "group1";
final private static String GROUP2_NAME = "group2";
final private static String GROUP3_NAME = "group3";
final private static String USER1_NAME = "user1";
final private static String USER2_NAME = "user2";
private static UserGroupInformation superUser;
private static UserGroupInformation user1;
private static UserGroupInformation user2;
@BeforeClass
public static void setUp() throws Exception {
cluster = new MiniDFSCluster.Builder(conf).numDataNodes(2).build();
fs = FileSystem.get(conf);
superUser = UserGroupInformation.getCurrentUser();
user1 = UserGroupInformation.createUserForTesting(USER1_NAME,
new String[] {GROUP1_NAME, GROUP2_NAME});
user2 = UserGroupInformation.createUserForTesting(USER2_NAME,
new String[] {GROUP2_NAME, GROUP3_NAME});
// Init test and trash root dirs in HDFS
fs.mkdirs(TEST_ROOT);
fs.setPermission(TEST_ROOT, new FsPermission((short) 0777));
DFSTestUtil.verifyFilePermission(
fs.getFileStatus(TEST_ROOT),
superUser.getShortUserName(),
null, FsAction.ALL, FsAction.ALL, FsAction.ALL);
fs.mkdirs(TRASH_ROOT);
fs.setPermission(TRASH_ROOT, new FsPermission((short) 0777));
DFSTestUtil.verifyFilePermission(
fs.getFileStatus(TRASH_ROOT),
superUser.getShortUserName(),
null, FsAction.ALL, FsAction.ALL, FsAction.ALL);
}
@AfterClass
public static void tearDown() {
if (cluster != null) { cluster.shutdown(); }
}
@Test
public void testTrash() throws IOException {
TestTrash.trashShell(cluster.getFileSystem(), new Path("/"));
}
@Test
public void testNonDefaultFS() throws IOException {
FileSystem fileSystem = cluster.getFileSystem();
Configuration config = fileSystem.getConf();
config.set(CommonConfigurationKeys.FS_DEFAULT_NAME_KEY,
fileSystem.getUri().toString());
TestTrash.trashNonDefaultFS(config);
}
@Test
public void testHDFSTrashPermission() throws IOException {
FileSystem fileSystem = cluster.getFileSystem();
Configuration config = fileSystem.getConf();
config.set(CommonConfigurationKeys.FS_TRASH_INTERVAL_KEY, "0.2");
TestTrash.verifyTrashPermission(fileSystem, config);
}
@Test
public void testMoveEmptyDirToTrash() throws IOException {
FileSystem fileSystem = cluster.getFileSystem();
Configuration config = fileSystem.getConf();
config.set(CommonConfigurationKeys.FS_TRASH_INTERVAL_KEY, "1");
TestTrash.verifyMoveEmptyDirToTrash(fileSystem, config);
}
@Test
public void testDeleteTrash() throws Exception {
Configuration testConf = new Configuration(conf);
testConf.set(CommonConfigurationKeys.FS_TRASH_INTERVAL_KEY, "10");
Path user1Tmp = new Path(TEST_ROOT, "test-del-u1");
Path user2Tmp = new Path(TEST_ROOT, "test-del-u2");
// login as user1, move something to trash
// verify user1 can remove its own trash dir
fs = DFSTestUtil.login(fs, testConf, user1);
fs.mkdirs(user1Tmp);
Trash u1Trash = getPerUserTrash(user1, fs, testConf);
Path u1t = u1Trash.getCurrentTrashDir(user1Tmp);
assertTrue(String.format("Failed to move %s to trash", user1Tmp),
u1Trash.moveToTrash(user1Tmp));
assertTrue(
String.format(
"%s should be allowed to remove its own trash directory %s",
user1.getUserName(), u1t),
fs.delete(u1t, true));
assertFalse(fs.exists(u1t));
// login as user2, move something to trash
fs = DFSTestUtil.login(fs, testConf, user2);
fs.mkdirs(user2Tmp);
Trash u2Trash = getPerUserTrash(user2, fs, testConf);
u2Trash.moveToTrash(user2Tmp);
Path u2t = u2Trash.getCurrentTrashDir(user2Tmp);
try {
// user1 should not be able to remove user2's trash dir
fs = DFSTestUtil.login(fs, testConf, user1);
fs.delete(u2t, true);
fail(String.format("%s should not be able to remove %s trash directory",
USER1_NAME, USER2_NAME));
} catch (AccessControlException e) {
assertTrue(e instanceof AccessControlException);
assertTrue("Permission denied messages must carry the username",
e.getMessage().contains(USER1_NAME));
}
}
/**
* Return a {@link Trash} instance using giving configuration.
* The trash root directory is set to an unique directory under
* {@link #TRASH_ROOT}. Use this method to isolate trash
* directories for different users.
*/
private Trash getPerUserTrash(UserGroupInformation ugi,
FileSystem fileSystem, Configuration config) throws IOException {
// generate an unique path per instance
UUID trashId = UUID.randomUUID();
StringBuffer sb = new StringBuffer()
.append(ugi.getUserName())
.append("-")
.append(trashId.toString());
Path userTrashRoot = new Path(TRASH_ROOT, sb.toString());
FileSystem spyUserFs = Mockito.spy(fileSystem);
Mockito.when(spyUserFs.getTrashRoot(Mockito.any(Path.class)))
.thenReturn(userTrashRoot);
return new Trash(spyUserFs, config);
}
}