@@ -1089,4 +1089,262 @@ extension IntegrationSuite {
10891089 throw IntegrationError . assert ( msg: " container2 should NOT have service-a entry, got: \( output2) " )
10901090 }
10911091 }
1092+
1093+ func testPodLevelDNS( ) async throws {
1094+ let id = " test-pod-level-dns "
1095+
1096+ let bs = try await bootstrap ( id)
1097+ let pod = try LinuxPod ( id, vmm: bs. vmm) { config in
1098+ config. cpus = 4
1099+ config. memoryInBytes = 1024 . mib ( )
1100+ config. bootLog = bs. bootLog
1101+ // Set DNS at the pod level
1102+ config. dns = DNS ( nameservers: [ " 9.9.9.9 " , " 149.112.112.112 " ] )
1103+ }
1104+
1105+ let buffer1 = BufferWriter ( )
1106+ let buffer2 = BufferWriter ( )
1107+
1108+ // Neither container specifies DNS. We should inherit from pod
1109+ try await pod. addContainer ( " container1 " , rootfs: try cloneRootfs ( bs. rootfs, testID: id, containerID: " container1 " ) ) { config in
1110+ config. process. arguments = [ " cat " , " /etc/resolv.conf " ]
1111+ config. process. stdout = buffer1
1112+ }
1113+
1114+ try await pod. addContainer ( " container2 " , rootfs: try cloneRootfs ( bs. rootfs, testID: id, containerID: " container2 " ) ) { config in
1115+ config. process. arguments = [ " cat " , " /etc/resolv.conf " ]
1116+ config. process. stdout = buffer2
1117+ }
1118+
1119+ try await pod. create ( )
1120+
1121+ try await pod. startContainer ( " container1 " )
1122+ let status1 = try await pod. waitContainer ( " container1 " )
1123+
1124+ try await pod. startContainer ( " container2 " )
1125+ let status2 = try await pod. waitContainer ( " container2 " )
1126+
1127+ try await pod. stop ( )
1128+
1129+ guard status1. exitCode == 0 else {
1130+ throw IntegrationError . assert ( msg: " container1 cat failed with status \( status1) " )
1131+ }
1132+ guard status2. exitCode == 0 else {
1133+ throw IntegrationError . assert ( msg: " container2 cat failed with status \( status2) " )
1134+ }
1135+
1136+ guard let output1 = String ( data: buffer1. data, encoding: . utf8) else {
1137+ throw IntegrationError . assert ( msg: " failed to convert container1 stdout to UTF8 " )
1138+ }
1139+ guard let output2 = String ( data: buffer2. data, encoding: . utf8) else {
1140+ throw IntegrationError . assert ( msg: " failed to convert container2 stdout to UTF8 " )
1141+ }
1142+
1143+ // Both containers should have the pod-level DNS
1144+ guard output1. contains ( " 9.9.9.9 " ) && output1. contains ( " 149.112.112.112 " ) else {
1145+ throw IntegrationError . assert ( msg: " container1 should have pod-level DNS (9.9.9.9), got: \( output1) " )
1146+ }
1147+ guard output2. contains ( " 9.9.9.9 " ) && output2. contains ( " 149.112.112.112 " ) else {
1148+ throw IntegrationError . assert ( msg: " container2 should have pod-level DNS (9.9.9.9), got: \( output2) " )
1149+ }
1150+ }
1151+
1152+ func testPodLevelDNSWithContainerOverride( ) async throws {
1153+ let id = " test-pod-level-dns-override "
1154+
1155+ let bs = try await bootstrap ( id)
1156+ let pod = try LinuxPod ( id, vmm: bs. vmm) { config in
1157+ config. cpus = 4
1158+ config. memoryInBytes = 1024 . mib ( )
1159+ config. bootLog = bs. bootLog
1160+ // Set DNS at the pod level
1161+ config. dns = DNS ( nameservers: [ " 9.9.9.9 " ] )
1162+ }
1163+
1164+ let buffer1 = BufferWriter ( )
1165+ let buffer2 = BufferWriter ( )
1166+
1167+ // Container1 does NOT specify DNS. It should inherit from pod
1168+ try await pod. addContainer ( " container1 " , rootfs: try cloneRootfs ( bs. rootfs, testID: id, containerID: " container1 " ) ) { config in
1169+ config. process. arguments = [ " cat " , " /etc/resolv.conf " ]
1170+ config. process. stdout = buffer1
1171+ }
1172+
1173+ // Container2 specifies its own DNS. It should override pod-level
1174+ try await pod. addContainer ( " container2 " , rootfs: try cloneRootfs ( bs. rootfs, testID: id, containerID: " container2 " ) ) { config in
1175+ config. process. arguments = [ " cat " , " /etc/resolv.conf " ]
1176+ config. process. stdout = buffer2
1177+ config. dns = DNS ( nameservers: [ " 8.8.8.8 " ] )
1178+ }
1179+
1180+ try await pod. create ( )
1181+
1182+ try await pod. startContainer ( " container1 " )
1183+ let status1 = try await pod. waitContainer ( " container1 " )
1184+
1185+ try await pod. startContainer ( " container2 " )
1186+ let status2 = try await pod. waitContainer ( " container2 " )
1187+
1188+ try await pod. stop ( )
1189+
1190+ guard status1. exitCode == 0 else {
1191+ throw IntegrationError . assert ( msg: " container1 cat failed with status \( status1) " )
1192+ }
1193+ guard status2. exitCode == 0 else {
1194+ throw IntegrationError . assert ( msg: " container2 cat failed with status \( status2) " )
1195+ }
1196+
1197+ guard let output1 = String ( data: buffer1. data, encoding: . utf8) else {
1198+ throw IntegrationError . assert ( msg: " failed to convert container1 stdout to UTF8 " )
1199+ }
1200+ guard let output2 = String ( data: buffer2. data, encoding: . utf8) else {
1201+ throw IntegrationError . assert ( msg: " failed to convert container2 stdout to UTF8 " )
1202+ }
1203+
1204+ // Container1 should have pod-level DNS
1205+ guard output1. contains ( " 9.9.9.9 " ) && !output1. contains ( " 8.8.8.8 " ) else {
1206+ throw IntegrationError . assert ( msg: " container1 should have pod-level DNS (9.9.9.9), got: \( output1) " )
1207+ }
1208+ // Container2 should have its own DNS, not pod-level
1209+ guard output2. contains ( " 8.8.8.8 " ) && !output2. contains ( " 9.9.9.9 " ) else {
1210+ throw IntegrationError . assert ( msg: " container2 should have container-level DNS (8.8.8.8), got: \( output2) " )
1211+ }
1212+ }
1213+
1214+ func testPodLevelHosts( ) async throws {
1215+ let id = " test-pod-level-hosts "
1216+
1217+ let bs = try await bootstrap ( id)
1218+ let pod = try LinuxPod ( id, vmm: bs. vmm) { config in
1219+ config. cpus = 4
1220+ config. memoryInBytes = 1024 . mib ( )
1221+ config. bootLog = bs. bootLog
1222+ // Set hosts at the pod level
1223+ config. hosts = Hosts ( entries: [
1224+ Hosts . Entry. localHostIPV4 ( ) ,
1225+ Hosts . Entry ( ipAddress: " 10.0.0.100 " , hostnames: [ " shared-service.local " ] ) ,
1226+ ] )
1227+ }
1228+
1229+ let buffer1 = BufferWriter ( )
1230+ let buffer2 = BufferWriter ( )
1231+
1232+ // Neither container specifies hosts. It should inherit from pod
1233+ try await pod. addContainer ( " container1 " , rootfs: try cloneRootfs ( bs. rootfs, testID: id, containerID: " container1 " ) ) { config in
1234+ config. process. arguments = [ " cat " , " /etc/hosts " ]
1235+ config. process. stdout = buffer1
1236+ }
1237+
1238+ try await pod. addContainer ( " container2 " , rootfs: try cloneRootfs ( bs. rootfs, testID: id, containerID: " container2 " ) ) { config in
1239+ config. process. arguments = [ " cat " , " /etc/hosts " ]
1240+ config. process. stdout = buffer2
1241+ }
1242+
1243+ try await pod. create ( )
1244+
1245+ try await pod. startContainer ( " container1 " )
1246+ let status1 = try await pod. waitContainer ( " container1 " )
1247+
1248+ try await pod. startContainer ( " container2 " )
1249+ let status2 = try await pod. waitContainer ( " container2 " )
1250+
1251+ try await pod. stop ( )
1252+
1253+ guard status1. exitCode == 0 else {
1254+ throw IntegrationError . assert ( msg: " container1 cat failed with status \( status1) " )
1255+ }
1256+ guard status2. exitCode == 0 else {
1257+ throw IntegrationError . assert ( msg: " container2 cat failed with status \( status2) " )
1258+ }
1259+
1260+ guard let output1 = String ( data: buffer1. data, encoding: . utf8) else {
1261+ throw IntegrationError . assert ( msg: " failed to convert container1 stdout to UTF8 " )
1262+ }
1263+ guard let output2 = String ( data: buffer2. data, encoding: . utf8) else {
1264+ throw IntegrationError . assert ( msg: " failed to convert container2 stdout to UTF8 " )
1265+ }
1266+
1267+ // Both containers should have the pod-level hosts entry
1268+ guard output1. contains ( " 10.0.0.100 " ) && output1. contains ( " shared-service.local " ) else {
1269+ throw IntegrationError . assert ( msg: " container1 should have pod-level hosts entry, got: \( output1) " )
1270+ }
1271+ guard output2. contains ( " 10.0.0.100 " ) && output2. contains ( " shared-service.local " ) else {
1272+ throw IntegrationError . assert ( msg: " container2 should have pod-level hosts entry, got: \( output2) " )
1273+ }
1274+ }
1275+
1276+ func testPodLevelHostsWithContainerOverride( ) async throws {
1277+ let id = " test-pod-level-hosts-override "
1278+
1279+ let bs = try await bootstrap ( id)
1280+ let pod = try LinuxPod ( id, vmm: bs. vmm) { config in
1281+ config. cpus = 4
1282+ config. memoryInBytes = 1024 . mib ( )
1283+ config. bootLog = bs. bootLog
1284+ // Set hosts at the pod level
1285+ config. hosts = Hosts ( entries: [
1286+ Hosts . Entry. localHostIPV4 ( ) ,
1287+ Hosts . Entry ( ipAddress: " 10.0.0.100 " , hostnames: [ " shared-service.local " ] ) ,
1288+ ] )
1289+ }
1290+
1291+ let buffer1 = BufferWriter ( )
1292+ let buffer2 = BufferWriter ( )
1293+
1294+ // Container1 does NOT specify hosts. It should inherit from pod
1295+ try await pod. addContainer ( " container1 " , rootfs: try cloneRootfs ( bs. rootfs, testID: id, containerID: " container1 " ) ) { config in
1296+ config. process. arguments = [ " cat " , " /etc/hosts " ]
1297+ config. process. stdout = buffer1
1298+ }
1299+
1300+ // Container2 specifies its own hosts. It should override pod-level
1301+ try await pod. addContainer ( " container2 " , rootfs: try cloneRootfs ( bs. rootfs, testID: id, containerID: " container2 " ) ) { config in
1302+ config. process. arguments = [ " cat " , " /etc/hosts " ]
1303+ config. process. stdout = buffer2
1304+ config. hosts = Hosts ( entries: [
1305+ Hosts . Entry. localHostIPV4 ( ) ,
1306+ Hosts . Entry ( ipAddress: " 10.0.0.200 " , hostnames: [ " container-specific.local " ] ) ,
1307+ ] )
1308+ }
1309+
1310+ try await pod. create ( )
1311+
1312+ try await pod. startContainer ( " container1 " )
1313+ let status1 = try await pod. waitContainer ( " container1 " )
1314+
1315+ try await pod. startContainer ( " container2 " )
1316+ let status2 = try await pod. waitContainer ( " container2 " )
1317+
1318+ try await pod. stop ( )
1319+
1320+ guard status1. exitCode == 0 else {
1321+ throw IntegrationError . assert ( msg: " container1 cat failed with status \( status1) " )
1322+ }
1323+ guard status2. exitCode == 0 else {
1324+ throw IntegrationError . assert ( msg: " container2 cat failed with status \( status2) " )
1325+ }
1326+
1327+ guard let output1 = String ( data: buffer1. data, encoding: . utf8) else {
1328+ throw IntegrationError . assert ( msg: " failed to convert container1 stdout to UTF8 " )
1329+ }
1330+ guard let output2 = String ( data: buffer2. data, encoding: . utf8) else {
1331+ throw IntegrationError . assert ( msg: " failed to convert container2 stdout to UTF8 " )
1332+ }
1333+
1334+ // Container1 should have pod-level hosts entry
1335+ guard output1. contains ( " 10.0.0.100 " ) && output1. contains ( " shared-service.local " ) else {
1336+ throw IntegrationError . assert ( msg: " container1 should have pod-level hosts entry, got: \( output1) " )
1337+ }
1338+ guard !output1. contains ( " 10.0.0.200 " ) && !output1. contains ( " container-specific.local " ) else {
1339+ throw IntegrationError . assert ( msg: " container1 should NOT have container2's hosts entry, got: \( output1) " )
1340+ }
1341+
1342+ // Container2 should have its own hosts entry, not pod-level
1343+ guard output2. contains ( " 10.0.0.200 " ) && output2. contains ( " container-specific.local " ) else {
1344+ throw IntegrationError . assert ( msg: " container2 should have container-level hosts entry, got: \( output2) " )
1345+ }
1346+ guard !output2. contains ( " 10.0.0.100 " ) && !output2. contains ( " shared-service.local " ) else {
1347+ throw IntegrationError . assert ( msg: " container2 should NOT have pod-level hosts entry, got: \( output2) " )
1348+ }
1349+ }
10921350}
0 commit comments