blob: 7deccb064fd75b39af779796d9e95caf9c718b32 [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.sentry.service.thrift;
import org.apache.hadoop.hive.metastore.api.NotificationEvent;
import org.apache.hive.hcatalog.messaging.MessageDeserializer;
import org.apache.sentry.binding.metastore.messaging.json.SentryJSONAddPartitionMessage;
import org.apache.sentry.binding.metastore.messaging.json.SentryJSONAlterPartitionMessage;
import org.apache.sentry.binding.metastore.messaging.json.SentryJSONAlterTableMessage;
import org.apache.sentry.binding.metastore.messaging.json.SentryJSONCreateDatabaseMessage;
import org.apache.sentry.binding.metastore.messaging.json.SentryJSONCreateTableMessage;
import org.apache.sentry.binding.metastore.messaging.json.SentryJSONDropDatabaseMessage;
import org.apache.sentry.binding.metastore.messaging.json.SentryJSONDropPartitionMessage;
import org.apache.sentry.binding.metastore.messaging.json.SentryJSONDropTableMessage;
import org.apache.sentry.binding.metastore.messaging.json.SentryJSONMessageDeserializer;
import org.junit.Test;
import org.mockito.Mockito;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import static org.apache.hive.hcatalog.messaging.HCatEventMessage.EventType.*;
import static org.junit.Assert.*;
public class TestFullUpdateModifier {
private static final String SERVER = "s";
private static final String PRINCIPAL = "p";
private static final String DB = "Db1";
private static final String TABLE = "Tab1";
private static final String AUTH = DB.toLowerCase() + "." + TABLE.toLowerCase();
private static final String PATH = "foo/bar";
private static final String LOCATION = uri(PATH);
/**
* Convert path to HDFS URI
*/
private static final String uri(String path) {
return "hdfs:///" + path;
}
/**
* Test create database event. It should add database and its location.
* As a result we should have entry {"db1": {foo/bar}}
* @throws Exception
*/
@Test
public void testCreateDatabase() throws Exception {
Map<String, Set<String>> update = new HashMap<>();
NotificationEvent event = new NotificationEvent(0, 0, CREATE_DATABASE.toString(), "");
MessageDeserializer deserializer = Mockito.mock(SentryJSONMessageDeserializer.class);
SentryJSONCreateDatabaseMessage message =
new SentryJSONCreateDatabaseMessage(SERVER, PRINCIPAL, DB, 0L, LOCATION);
Mockito.when(deserializer.getCreateDatabaseMessage("")).thenReturn(message);
FullUpdateModifier.applyEvent(update, event, deserializer);
Map<String, Set<String>> expected = new HashMap<>();
expected.put(DB.toLowerCase(), Collections.singleton(PATH));
assertEquals(expected, update);
}
/**
* Test drop database event. It should drop database record.
* @throws Exception
*/
@Test
public void testDropDatabase() throws Exception {
Map<String, Set<String>> update = new HashMap<>();
update.put(DB.toLowerCase(), Collections.singleton(PATH));
NotificationEvent event = new NotificationEvent(0, 0, DROP_DATABASE.toString(), "");
MessageDeserializer deserializer = Mockito.mock(SentryJSONMessageDeserializer.class);
SentryJSONDropDatabaseMessage message =
new SentryJSONDropDatabaseMessage(SERVER, PRINCIPAL, DB, 0L, LOCATION);
Mockito.when(deserializer.getDropDatabaseMessage("")).thenReturn(message);
FullUpdateModifier.applyEvent(update, event, deserializer);
assertTrue(update.isEmpty());
}
/**
* Test drop database event when dropped database location doesn't
* match original database location. Should leave update intact.
* @throws Exception
*/
@Test
public void testDropDatabaseWrongLocation() throws Exception {
Map<String, Set<String>> update = new HashMap<>();
update.put(DB.toLowerCase(), Collections.singleton(PATH));
NotificationEvent event = new NotificationEvent(0, 0, DROP_DATABASE.toString(), "");
MessageDeserializer deserializer = Mockito.mock(SentryJSONMessageDeserializer.class);
SentryJSONDropDatabaseMessage message =
new SentryJSONDropDatabaseMessage(SERVER, PRINCIPAL, DB, 0L,
"hdfs:///bad/location");
Mockito.when(deserializer.getDropDatabaseMessage("")).thenReturn(message);
FullUpdateModifier.applyEvent(update, event, deserializer);
// DB should stay
Map<String, Set<String>> expected = new HashMap<>();
expected.put(DB.toLowerCase(), Collections.singleton(PATH));
assertEquals(expected, update);
}
/**
* Test drop database which has tables/partitions.
* Should drop all reated database records but leave unrelated records in place.
* @throws Exception
*/
@Test
public void testDropDatabaseWithTables() throws Exception {
Map<String, Set<String>> update = new HashMap<>();
update.put(DB.toLowerCase(), Collections.singleton(PATH));
update.put(AUTH, Collections.singleton(PATH));
update.put("unrelated", Collections.singleton(PATH));
NotificationEvent event = new NotificationEvent(0, 0, DROP_DATABASE.toString(), "");
MessageDeserializer deserializer = Mockito.mock(SentryJSONMessageDeserializer.class);
SentryJSONDropDatabaseMessage message =
new SentryJSONDropDatabaseMessage(SERVER, PRINCIPAL, DB, 0L, LOCATION);
Mockito.when(deserializer.getDropDatabaseMessage("")).thenReturn(message);
FullUpdateModifier.applyEvent(update, event, deserializer);
Map<String, Set<String>> expected = new HashMap<>();
expected.put("unrelated", Collections.singleton(PATH));
assertEquals(expected, update);
}
/**
* Test create table event. It should add table and its location.
* As a result we should have entry {"db1.tab1": {foo/bar}}
* @throws Exception
*/
@Test
public void testCreateTable() throws Exception {
Map<String, Set<String>> update = new HashMap<>();
NotificationEvent event = new NotificationEvent(0, 0, CREATE_TABLE.toString(), "");
MessageDeserializer deserializer = Mockito.mock(SentryJSONMessageDeserializer.class);
SentryJSONCreateTableMessage message =
new SentryJSONCreateTableMessage(SERVER, PRINCIPAL, DB, TABLE, 0L, LOCATION);
Mockito.when(deserializer.getCreateTableMessage("")).thenReturn(message);
FullUpdateModifier.applyEvent(update, event, deserializer);
Map<String, Set<String>> expected = new HashMap<>();
expected.put(AUTH, Collections.singleton(PATH));
assertEquals(expected, update);
}
/**
* Test drop table event. It should drop table record.
* @throws Exception
*/
@Test
public void testDropTable() throws Exception {
Map<String, Set<String>> update = new HashMap<>();
update.put(AUTH, Collections.singleton(PATH));
NotificationEvent event = new NotificationEvent(0, 0, DROP_TABLE.toString(), "");
MessageDeserializer deserializer = Mockito.mock(SentryJSONMessageDeserializer.class);
SentryJSONDropTableMessage message =
new SentryJSONDropTableMessage(SERVER, PRINCIPAL, DB, TABLE, 0L, LOCATION);
Mockito.when(deserializer.getDropTableMessage("")).thenReturn(message);
FullUpdateModifier.applyEvent(update, event, deserializer);
assertTrue(update.isEmpty());
}
/**
* Test drop table event. It should drop table record.
* @throws Exception
*/
@Test
public void testDropTableWrongLocation() throws Exception {
Map<String, Set<String>> update = new HashMap<>();
update.put(AUTH, Collections.singleton(PATH));
NotificationEvent event = new NotificationEvent(0, 0, DROP_TABLE.toString(), "");
MessageDeserializer deserializer = Mockito.mock(SentryJSONMessageDeserializer.class);
SentryJSONDropTableMessage message =
new SentryJSONDropTableMessage(SERVER, PRINCIPAL, DB, TABLE, 0L,
"hdfs:///bad/location");
Mockito.when(deserializer.getDropTableMessage("")).thenReturn(message);
FullUpdateModifier.applyEvent(update, event, deserializer);
// DB should stay
assertEquals(Collections.singleton(PATH), update.get(AUTH));
assertEquals(1, update.size());
}
/**
* Test add partition event. It should add table and its location.
* As a result we should have entry {"db1.tab1": {foo/bar, hello/world}}
* @throws Exception
*/
@Test
public void testAddPartition() throws Exception {
Map<String, Set<String>> update = new HashMap<>();
Set<String> locations = new HashSet<>();
locations.add(PATH);
update.put(AUTH, locations);
NotificationEvent event = new NotificationEvent(0, 0, ADD_PARTITION.toString(), "");
MessageDeserializer deserializer = Mockito.mock(SentryJSONMessageDeserializer.class);
String partPath = "hello/world";
String partLocation = uri(partPath);
SentryJSONAddPartitionMessage message =
new SentryJSONAddPartitionMessage(SERVER, PRINCIPAL, DB, TABLE,
Collections.<Map<String,String>>emptyList(), 0L,
Collections.singletonList(partLocation));
Mockito.when(deserializer.getAddPartitionMessage("")).thenReturn(message);
FullUpdateModifier.applyEvent(update, event, deserializer);
Set<String> expected = new HashSet<>(2);
expected.add(PATH);
expected.add(partPath);
assertEquals(expected, update.get(AUTH));
}
/**
* Test drop partition event. It should drop partition info from the list of locations.
* @throws Exception
*/
@Test
public void testDropPartitions() throws Exception {
String partPath = "hello/world";
String partLocation = uri(partPath);
Map<String, Set<String>> update = new HashMap<>();
Set<String> locations = new HashSet<>();
locations.add(PATH);
locations.add(partPath);
update.put(AUTH, locations);
NotificationEvent event = new NotificationEvent(0, 0, DROP_PARTITION.toString(), "");
MessageDeserializer deserializer = Mockito.mock(SentryJSONMessageDeserializer.class);
SentryJSONDropPartitionMessage message =
new SentryJSONDropPartitionMessage(SERVER, PRINCIPAL, DB, TABLE,
Collections.<Map<String,String>>emptyList(), 0L, Collections.singletonList(partLocation));
Mockito.when(deserializer.getDropPartitionMessage("")).thenReturn(message);
FullUpdateModifier.applyEvent(update, event, deserializer);
assertEquals(Collections.singleton(PATH), update.get(AUTH));
}
/**
* Test alter partition event. It should change partition location
* @throws Exception
*/
@Test
public void testAlterPartition() throws Exception {
String partPath = "hello/world";
String partLocation = uri(partPath);
String newPath = "better/world";
String newLocation = uri(newPath);
Map<String, Set<String>> update = new HashMap<>();
Set<String> locations = new HashSet<>();
locations.add(PATH);
locations.add(partPath);
update.put(AUTH, locations);
NotificationEvent event = new NotificationEvent(0, 0, ALTER_PARTITION.toString(), "");
MessageDeserializer deserializer = Mockito.mock(SentryJSONMessageDeserializer.class);
SentryJSONAlterPartitionMessage message =
new SentryJSONAlterPartitionMessage(SERVER, PRINCIPAL, DB, TABLE,
Collections.<String>emptyList(), Collections.<String>emptyList(), 0L,
partLocation, newLocation);
Mockito.when(deserializer.getAlterPartitionMessage("")).thenReturn(message);
FullUpdateModifier.applyEvent(update, event, deserializer);
Set<String> expected = new HashSet<>(2);
expected.add(PATH);
expected.add(newPath);
assertEquals(expected, update.get(AUTH));
}
/**
* Test alter table event that changes database name when there are no tables.
* @throws Exception
*/
@Test
public void testAlterTableChangeDbNameNoTables() throws Exception {
Map<String, Set<String>> update = new HashMap<>();
update.put(DB.toLowerCase(), Collections.singleton(PATH));
String newDbName = "Db2";
NotificationEvent event = new NotificationEvent(0, 0, ALTER_TABLE.toString(), "");
event.setDbName(newDbName);
event.setTableName(TABLE);
MessageDeserializer deserializer = Mockito.mock(SentryJSONMessageDeserializer.class);
SentryJSONAlterTableMessage message =
new SentryJSONAlterTableMessage(SERVER, PRINCIPAL, DB, TABLE, 0L,
LOCATION, LOCATION);
Mockito.when(deserializer.getAlterTableMessage("")).thenReturn(message);
FullUpdateModifier.applyEvent(update, event, deserializer);
assertEquals(Collections.singleton(PATH), update.get(newDbName.toLowerCase()));
assertFalse(update.containsKey(DB.toLowerCase()));
}
@Test
/**
* Test alter table event that changes database name when there are tables.
* All entries like "dbName.tableName" should have dbName changed to the new name.
* @throws Exception
*/
public void testAlterTableChangeDbNameWithTables() throws Exception {
Map<String, Set<String>> update = new HashMap<>();
update.put(DB.toLowerCase(), Collections.singleton(PATH));
Set<String> locations = new HashSet<>(1);
locations.add(PATH);
update.put(AUTH, locations);
String newDbName = "Db2";
String newAuth = newDbName.toLowerCase() + "." + TABLE.toLowerCase();
NotificationEvent event = new NotificationEvent(0, 0, ALTER_TABLE.toString(), "");
event.setDbName(newDbName);
event.setTableName(TABLE);
MessageDeserializer deserializer = Mockito.mock(SentryJSONMessageDeserializer.class);
SentryJSONAlterTableMessage message =
new SentryJSONAlterTableMessage(SERVER, PRINCIPAL, DB, TABLE, 0L,
LOCATION, LOCATION);
Mockito.when(deserializer.getAlterTableMessage("")).thenReturn(message);
FullUpdateModifier.applyEvent(update, event, deserializer);
Map<String, Set<String>> expected = new HashMap<>(2);
expected.put(newDbName.toLowerCase(), Collections.singleton(PATH));
expected.put(newAuth, Collections.singleton(PATH));
assertEquals(expected, update);
}
/**
* Test alter table event that changes table name.
* @throws Exception
*/
@Test
public void testAlterTableChangeTableName() throws Exception {
Map<String, Set<String>> update = new HashMap<>();
update.put(DB.toLowerCase(), Collections.singleton(PATH));
Set<String> locations = new HashSet<>(1);
locations.add(PATH);
update.put(AUTH, locations);
String newTableName = "Table2";
String newAuth = DB.toLowerCase() + "." + newTableName.toLowerCase();
NotificationEvent event = new NotificationEvent(0, 0, ALTER_TABLE.toString(), "");
event.setDbName(DB);
event.setTableName(newTableName);
MessageDeserializer deserializer = Mockito.mock(SentryJSONMessageDeserializer.class);
SentryJSONAlterTableMessage message =
new SentryJSONAlterTableMessage(SERVER, PRINCIPAL, DB, TABLE, 0L,
LOCATION, LOCATION);
Mockito.when(deserializer.getAlterTableMessage("")).thenReturn(message);
FullUpdateModifier.applyEvent(update, event, deserializer);
Map<String, Set<String>> expected = new HashMap<>(2);
expected.put(DB.toLowerCase(), Collections.singleton(PATH));
expected.put(newAuth, Collections.singleton(PATH));
assertEquals(expected, update);
}
/**
* Test alter table event that changes object location.
* @throws Exception
*/
@Test
public void testAlterTableChangeLocation() throws Exception {
Map<String, Set<String>> update = new HashMap<>();
update.put(DB.toLowerCase(), Collections.singleton(PATH));
Set<String> locations = new HashSet<>(1);
locations.add(PATH);
update.put(AUTH, locations);
NotificationEvent event = new NotificationEvent(0, 0, ALTER_TABLE.toString(), "");
event.setDbName(DB);
event.setTableName(TABLE);
String newPath = "hello/world";
String newLocation = uri(newPath);
MessageDeserializer deserializer = Mockito.mock(SentryJSONMessageDeserializer.class);
SentryJSONAlterTableMessage message =
new SentryJSONAlterTableMessage(SERVER, PRINCIPAL, DB, TABLE, 0L,
LOCATION, newLocation);
Mockito.when(deserializer.getAlterTableMessage("")).thenReturn(message);
FullUpdateModifier.applyEvent(update, event, deserializer);
Map<String, Set<String>> expected = new HashMap<>(2);
expected.put(DB.toLowerCase(), Collections.singleton(PATH));
expected.put(AUTH.toLowerCase(), Collections.singleton(newPath));
assertEquals(expected, update);
}
/**
* Test renamePrefixKeys function.
* We ask to rename "foo.bar" key to "foo.baz" key.
* @throws Exception
*/
@Test
public void testRenamePrefixKeys() throws Exception {
String oldKey = "foo.";
String newKey = "baz.";
String postfix = "bar";
Map<String, Set<String>> update = new HashMap<>();
update.put(oldKey + postfix , Collections.<String>emptySet());
FullUpdateModifier.renamePrefixKeys(update, oldKey, newKey);
assertEquals(1, update.size());
assertTrue(update.containsKey(newKey + postfix));
}
/**
* Test renamePostfixKeys and RenamePrefixKeys functions mwhen the destination keys exist.
* Should nto change anything.
* We ask to rename "foo.bar" key to "baz.bar" key.
* @throws Exception
*/
@Test
public void testRenameKeysWithConflicts() throws Exception {
Map<String, Set<String>> update = new HashMap<>();
update.put("foo.bar", Collections.<String>emptySet());
update.put("baz.bar", Collections.<String>emptySet());
Map<String, Set<String>> expected = new HashMap<>(update);
FullUpdateModifier.renamePrefixKeys(update, "foo.", "baz.");
assertEquals(update, expected);
}
}