/*
* 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.accumulo.server.util;
import static org.easymock.EasyMock.createMock;
import static org.easymock.EasyMock.expect;
import static org.easymock.EasyMock.expectLastCall;
import static org.easymock.EasyMock.replay;
import static org.easymock.EasyMock.verify;
import java.util.ArrayList;
import java.util.Collections;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.UUID;
import org.apache.accumulo.core.client.Connector;
import org.apache.accumulo.core.client.IteratorSetting;
import org.apache.accumulo.core.client.IteratorSetting.Column;
import org.apache.accumulo.core.client.admin.TableOperations;
import org.apache.accumulo.core.client.impl.ClientContext;
import org.apache.accumulo.core.client.impl.Credentials;
import org.apache.accumulo.core.client.impl.Writer;
import org.apache.accumulo.core.client.security.tokens.PasswordToken;
import org.apache.accumulo.core.conf.Property;
import org.apache.accumulo.core.data.ColumnUpdate;
import org.apache.accumulo.core.data.Mutation;
import org.apache.accumulo.core.data.Value;
import org.apache.accumulo.core.data.impl.KeyExtent;
import org.apache.accumulo.core.iterators.Combiner;
import org.apache.accumulo.core.iterators.IteratorUtil.IteratorScope;
import org.apache.accumulo.core.metadata.schema.MetadataSchema;
import org.apache.accumulo.core.metadata.schema.MetadataSchema.ReplicationSection;
import org.apache.accumulo.core.protobuf.ProtobufUtil;
import org.apache.accumulo.server.replication.StatusCombiner;
import org.apache.accumulo.server.replication.StatusUtil;
import org.apache.accumulo.server.replication.proto.Replication.Status;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.Text;
import org.easymock.EasyMock;
import org.easymock.IAnswer;
import org.junit.Assert;
import org.junit.Test;
public class ReplicationTableUtilTest {
@Test
public void properPathInRow() throws Exception {
Writer writer = EasyMock.createNiceMock(Writer.class);
writer.update(EasyMock.anyObject(Mutation.class));
final List<Mutation> mutations = new ArrayList<>();
// Mock a Writer to just add the mutation to a list
EasyMock.expectLastCall().andAnswer(new IAnswer<Object>() {
@Override
public Object answer() {
mutations.add(((Mutation) EasyMock.getCurrentArguments()[0]));
return null;
}
});
EasyMock.replay(writer);
Credentials creds = new Credentials("root", new PasswordToken(""));
ClientContext context = EasyMock.createMock(ClientContext.class);
EasyMock.expect(context.getCredentials()).andReturn(creds).anyTimes();
EasyMock.replay(context);
// Magic hook to create a Writer
ReplicationTableUtil.addWriter(creds, writer);
// Example file seen coming out of LogEntry
UUID uuid = UUID.randomUUID();
String myFile = "file:////home/user/accumulo/wal/server+port/" + uuid;
long createdTime = System.currentTimeMillis();
ReplicationTableUtil.updateFiles(context, new KeyExtent("1", null, null), myFile, StatusUtil.fileCreated(createdTime));
verify(writer);
Assert.assertEquals(1, mutations.size());
Mutation m = mutations.get(0);
Assert.assertEquals(MetadataSchema.ReplicationSection.getRowPrefix() + "file:/home/user/accumulo/wal/server+port/" + uuid, new Text(m.getRow()).toString());
List<ColumnUpdate> updates = m.getUpdates();
Assert.assertEquals(1, updates.size());
ColumnUpdate update = updates.get(0);
Assert.assertEquals(MetadataSchema.ReplicationSection.COLF, new Text(update.getColumnFamily()));
Assert.assertEquals("1", new Text(update.getColumnQualifier()).toString());
Assert.assertEquals(StatusUtil.fileCreatedValue(createdTime), new Value(update.getValue()));
}
@Test
public void replEntryMutation() {
// We stopped using a WAL -- we need a reference that this WAL needs to be replicated completely
Status stat = Status.newBuilder().setBegin(0).setEnd(0).setInfiniteEnd(true).setCreatedTime(System.currentTimeMillis()).build();
String file = "file:///accumulo/wal/127.0.0.1+9997" + UUID.randomUUID();
Path filePath = new Path(file);
Text row = new Text(filePath.toString());
KeyExtent extent = new KeyExtent("1", new Text("b"), new Text("a"));
Mutation m = ReplicationTableUtil.createUpdateMutation(filePath, ProtobufUtil.toValue(stat), extent);
Assert.assertEquals(new Text(MetadataSchema.ReplicationSection.getRowPrefix() + row), new Text(m.getRow()));
Assert.assertEquals(1, m.getUpdates().size());
ColumnUpdate col = m.getUpdates().get(0);
Assert.assertEquals(MetadataSchema.ReplicationSection.COLF, new Text(col.getColumnFamily()));
Assert.assertEquals(extent.getTableId(), new Text(col.getColumnQualifier()).toString());
Assert.assertEquals(0, col.getColumnVisibility().length);
Assert.assertArrayEquals(stat.toByteArray(), col.getValue());
}
@Test
public void setsCombinerOnMetadataCorrectly() throws Exception {
Connector conn = createMock(Connector.class);
TableOperations tops = createMock(TableOperations.class);
String myMetadataTable = "mymetadata";
Map<String,EnumSet<IteratorScope>> iterators = new HashMap<>();
iterators.put("vers", EnumSet.of(IteratorScope.majc, IteratorScope.minc, IteratorScope.scan));
IteratorSetting combiner = new IteratorSetting(9, "replcombiner", StatusCombiner.class);
Combiner.setColumns(combiner, Collections.singletonList(new Column(ReplicationSection.COLF)));
expect(conn.tableOperations()).andReturn(tops);
expect(tops.listIterators(myMetadataTable)).andReturn(iterators);
tops.attachIterator(myMetadataTable, combiner);
expectLastCall().once();
expect(tops.getProperties(myMetadataTable)).andReturn(Collections.<Entry<String,String>> emptyList());
tops.setProperty(myMetadataTable, Property.TABLE_FORMATTER_CLASS.getKey(), ReplicationTableUtil.STATUS_FORMATTER_CLASS_NAME);
expectLastCall().once();
replay(conn, tops);
ReplicationTableUtil.configureMetadataTable(conn, myMetadataTable);
verify(conn, tops);
}
}