Skip to content

Commit

Permalink
HDDS-1480. Verify if the saved ip address differs from the current da…
Browse files Browse the repository at this point in the history
…tanode ip address
  • Loading branch information
ptlrs committed Dec 3, 2024
1 parent 9b61937 commit e35572c
Show file tree
Hide file tree
Showing 3 changed files with 85 additions and 36 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.nio.file.Paths;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
Expand Down Expand Up @@ -169,25 +171,37 @@ public static synchronized void writeDatanodeDetailsTo(
* @return {@link DatanodeDetails}
* @throws IOException If the id file is malformed or other I/O exceptions
*/
public static synchronized DatanodeDetails readDatanodeDetailsFrom(File path)
throws IOException {
public static synchronized DatanodeDetails readDatanodeDetailsFrom(File path) throws IOException {
if (!path.exists()) {
throw new IOException("Datanode ID file not found.");
}

DatanodeDetails datanodeDetails;
try {
return DatanodeIdYaml.readDatanodeIdFile(path);
datanodeDetails = DatanodeIdYaml.readDatanodeIdFile(path);
} catch (IOException e) {
LOG.warn("Error loading DatanodeDetails yaml from {}",
path.getAbsolutePath(), e);
LOG.warn("Error loading DatanodeDetails yaml from {}", path.getAbsolutePath(), e);
// Try to load as protobuf before giving up
try (FileInputStream in = new FileInputStream(path)) {
return DatanodeDetails.getFromProtoBuf(
HddsProtos.DatanodeDetailsProto.parseFrom(in));
datanodeDetails = DatanodeDetails.getFromProtoBuf(HddsProtos.DatanodeDetailsProto.parseFrom(in));
} catch (IOException io) {
throw new IOException("Failed to parse DatanodeDetails from "
+ path.getAbsolutePath(), io);
throw new IOException("Failed to parse DatanodeDetails from " + path.getAbsolutePath(), io);
}
}

try {
InetAddress inetAddress = InetAddress.getByName(datanodeDetails.getHostName());

if (!inetAddress.getHostAddress().equals(datanodeDetails.getIpAddress())) {
LOG.warn("Resolved IP address '{}' differs from the persisted IP address '{}' for the datanode '{}'",
inetAddress.getHostAddress(), datanodeDetails.getIpAddress(), datanodeDetails.getHostName());
datanodeDetails.setIpAddress(inetAddress.getHostAddress());
}
} catch (UnknownHostException e) {
LOG.warn("Failed to validate IP address for the datanode '{}'", datanodeDetails.getHostName(), e);
}

return datanodeDetails;
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,12 @@
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
import org.mockito.MockedStatic;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.InetAddress;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;

Expand All @@ -44,7 +46,11 @@
import static org.apache.hadoop.hdds.HddsUtils.processForDebug;
import static org.apache.hadoop.ozone.container.ContainerTestHelper.getDummyCommandRequestProto;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.mockStatic;
import static org.mockito.Mockito.when;

/**
* Test for {@link ContainerUtils}.
Expand Down Expand Up @@ -91,34 +97,49 @@ public void testTarName() throws IOException {
public void testDatanodeIDPersistent(@TempDir File tempDir) throws Exception {
// Generate IDs for testing
DatanodeDetails id1 = randomDatanodeDetails();
id1.setPort(DatanodeDetails.newStandalonePort(1));
assertWriteRead(tempDir, id1);

// Add certificate serial id.
id1.setCertSerialId("" + RandomUtils.nextLong());
assertWriteRead(tempDir, id1);

// Read should return an empty value if file doesn't exist
File nonExistFile = new File(tempDir, "non_exist.id");
assertThrows(IOException.class,
() -> ContainerUtils.readDatanodeDetailsFrom(nonExistFile));

// Read should fail if the file is malformed
File malformedFile = new File(tempDir, "malformed.id");
createMalformedIDFile(malformedFile);
assertThrows(IOException.class,
() -> ContainerUtils.readDatanodeDetailsFrom(malformedFile));

// Test upgrade scenario - protobuf file instead of yaml
File protoFile = new File(tempDir, "valid-proto.id");
try (FileOutputStream out = new FileOutputStream(protoFile)) {
HddsProtos.DatanodeDetailsProto proto = id1.getProtoBufMessage();
proto.writeTo(out);
try (MockedStatic<InetAddress> mockedStaticInetAddress = mockStatic(InetAddress.class)) {
InetAddress mockedInetAddress = mock(InetAddress.class);
mockedStaticInetAddress.when(() -> InetAddress.getByName(id1.getHostName()))
.thenReturn(mockedInetAddress);

// If persisted ip address is different from resolved ip address,
// DatanodeDetails should be set with the resolved ip address
when(mockedInetAddress.getHostAddress())
.thenReturn("127.0.0.1");
assertWriteReadWithChangedIpAddress(tempDir, id1);

when(mockedInetAddress.getHostAddress())
.thenReturn(id1.getIpAddress());

id1.setPort(DatanodeDetails.newStandalonePort(1));
assertWriteRead(tempDir, id1);

// Add certificate serial id.
id1.setCertSerialId("" + RandomUtils.nextLong());
assertWriteRead(tempDir, id1);

// Read should return an empty value if file doesn't exist
File nonExistFile = new File(tempDir, "non_exist.id");
assertThrows(IOException.class,
() -> ContainerUtils.readDatanodeDetailsFrom(nonExistFile));

// Read should fail if the file is malformed
File malformedFile = new File(tempDir, "malformed.id");
createMalformedIDFile(malformedFile);
assertThrows(IOException.class,
() -> ContainerUtils.readDatanodeDetailsFrom(malformedFile));

// Test upgrade scenario - protobuf file instead of yaml
File protoFile = new File(tempDir, "valid-proto.id");
try (FileOutputStream out = new FileOutputStream(protoFile)) {
HddsProtos.DatanodeDetailsProto proto = id1.getProtoBufMessage();
proto.writeTo(out);
}
assertDetailsEquals(id1, ContainerUtils.readDatanodeDetailsFrom(protoFile));

id1.setInitialVersion(1);
assertWriteRead(tempDir, id1);
}
assertDetailsEquals(id1, ContainerUtils.readDatanodeDetailsFrom(protoFile));

id1.setInitialVersion(1);
assertWriteRead(tempDir, id1);
}

private void assertWriteRead(@TempDir File tempDir,
Expand All @@ -133,6 +154,17 @@ private void assertWriteRead(@TempDir File tempDir,
assertEquals(details.getCurrentVersion(), read.getCurrentVersion());
}

private void assertWriteReadWithChangedIpAddress(@TempDir File tempDir,
DatanodeDetails details) throws IOException {
// Write a single ID to the file and read it out
File file = new File(tempDir, "valid-values.id");
ContainerUtils.writeDatanodeDetailsTo(details, file, conf);

DatanodeDetails read = ContainerUtils.readDatanodeDetailsFrom(file);

assertNotEquals(details.toString(), read.toString());
}

private void createMalformedIDFile(File malformedFile)
throws IOException {
DatanodeDetails id = randomDatanodeDetails();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ x-om:
services:
dn1:
<<: *datanode
hostname: dn1
networks:
net:
ipv4_address: 10.9.0.11
Expand All @@ -57,6 +58,7 @@ services:
- ../..:${OZONE_DIR}
dn2:
<<: *datanode
hostname: dn2
networks:
net:
ipv4_address: 10.9.0.12
Expand All @@ -65,6 +67,7 @@ services:
- ../..:${OZONE_DIR}
dn3:
<<: *datanode
hostname: dn3
networks:
net:
ipv4_address: 10.9.0.13
Expand Down

0 comments on commit e35572c

Please sign in to comment.