From ee05bc031882cd47cf402f55e1bfefbe83ecb3c0 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=F0=9F=A4=93=20Mostafa=20Emami?= <mustafaemami@gmail.com>
Date: Tue, 21 Jun 2022 07:30:34 +0200
Subject: [PATCH] Fix network inspect compat API discrepancy
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

- containerInspect compat API expects field value PrefixLen
  instead of PrefixLength for type Address for SecondaryIPAddresses
- Add tests for network part of containerInspect compat api

Closes: containers#14674
Signed-off-by: 🤓 Mostafa Emami <mustafaemami@gmail.com>
---
 pkg/api/handlers/compat/containers.go         | 12 +++++
 .../python/rest_api/fixtures/api_testcase.py  |  4 ++
 .../python/rest_api/test_v2_0_0_container.py  | 47 +++++++++++++++++++
 3 files changed, 63 insertions(+)

diff --git a/pkg/api/handlers/compat/containers.go b/pkg/api/handlers/compat/containers.go
index 616f0a1388..efc4b1a4d6 100644
--- a/pkg/api/handlers/compat/containers.go
+++ b/pkg/api/handlers/compat/containers.go
@@ -395,6 +395,15 @@ func LibpodToContainer(l *libpod.Container, sz bool) (*handlers.Container, error
 	}, nil
 }
 
+func convertSecondaryIPPrefixLen(input *define.InspectNetworkSettings, output *types.NetworkSettings) {
+	for index, ip := range input.SecondaryIPAddresses {
+		output.SecondaryIPAddresses[index].PrefixLen = ip.PrefixLength
+	}
+	for index, ip := range input.SecondaryIPv6Addresses {
+		output.SecondaryIPv6Addresses[index].PrefixLen = ip.PrefixLength
+	}
+}
+
 func LibpodToContainerJSON(l *libpod.Container, sz bool) (*types.ContainerJSON, error) {
 	_, imageName := l.Image()
 	inspect, err := l.Inspect(sz)
@@ -585,6 +594,9 @@ func LibpodToContainerJSON(l *libpod.Container, sz bool) (*types.ContainerJSON,
 	if err := json.Unmarshal(n, &networkSettings); err != nil {
 		return nil, err
 	}
+
+	convertSecondaryIPPrefixLen(inspect.NetworkSettings, &networkSettings)
+
 	// do not report null instead use an empty map
 	if networkSettings.Networks == nil {
 		networkSettings.Networks = map[string]*network.EndpointSettings{}
diff --git a/test/apiv2/python/rest_api/fixtures/api_testcase.py b/test/apiv2/python/rest_api/fixtures/api_testcase.py
index 155e939282..f471365556 100644
--- a/test/apiv2/python/rest_api/fixtures/api_testcase.py
+++ b/test/apiv2/python/rest_api/fixtures/api_testcase.py
@@ -64,6 +64,10 @@ class APITestCase(unittest.TestCase):
     def uri(path):
         return APITestCase.PODMAN_URL + "/v2.0.0/libpod" + path
 
+    @staticmethod
+    def compat_uri(path):
+        return APITestCase.PODMAN_URL + "/v3.0.0/" + path
+
     def resolve_container(self, path):
         """Find 'first' container and return 'Id' formatted into given URI path."""
 
diff --git a/test/apiv2/python/rest_api/test_v2_0_0_container.py b/test/apiv2/python/rest_api/test_v2_0_0_container.py
index a44786c0df..a6cd93a1af 100644
--- a/test/apiv2/python/rest_api/test_v2_0_0_container.py
+++ b/test/apiv2/python/rest_api/test_v2_0_0_container.py
@@ -1,10 +1,12 @@
 import multiprocessing
 import queue
 import random
+import subprocess
 import threading
 import unittest
 
 import requests
+import os
 import time
 from dateutil.parser import parse
 
@@ -358,5 +360,50 @@ class ContainerTestCase(APITestCase):
         self.assertEqual(1000, out["HostConfig"]["Memory"])
 
 
+
+def execute_process(cmd):
+    return subprocess.run(
+                cmd,
+                shell=True,
+                check=True,
+                stdout=subprocess.PIPE,
+                stderr=subprocess.PIPE,
+            )
+
+def create_named_network_ns(network_ns_name):
+    execute_process(f"ip netns add {network_ns_name}")
+    execute_process(f"ip netns exec {network_ns_name} ip link add enp2s0 type veth peer name eth0")
+    execute_process(f"ip netns exec {network_ns_name} ip addr add 10.0.1.0/24 dev eth0")
+    execute_process(f"ip netns exec {network_ns_name} ip link set eth0 up")
+    execute_process(f"ip netns exec {network_ns_name} ip link add enp2s1 type veth peer name eth1")
+    execute_process(f"ip netns exec {network_ns_name} ip addr add 10.0.2.0/24 dev eth1")
+    execute_process(f"ip netns exec {network_ns_name} ip link set eth1 up")
+
+def delete_named_network_ns(network_ns_name):
+    execute_process(f"ip netns delete {network_ns_name}")
+
+class ContainerCompatibleAPITestCase(APITestCase):
+    def test_inspect_network(self):
+        if os.getuid() != 0:
+            self.skipTest("test needs to be executed as root!")
+        try:
+            network_ns_name = "test-compat-api"
+            create_named_network_ns(network_ns_name)
+            self.podman.run("rm", "--all", "--force", check=True)
+            self.podman.run("run", "--net", f"ns:/run/netns/{network_ns_name}", "-d", "alpine", "top", check=True)
+
+            r = requests.post(self.uri(self.resolve_container("/containers/{}/start")))
+            self.assertIn(r.status_code, (204, 304), r.text)
+
+            r = requests.get(self.compat_uri(self.resolve_container("/containers/{}/json")))
+            self.assertEqual(r.status_code, 200, r.text)
+            self.assertId(r.content)
+            out = r.json()
+
+            self.assertEqual("10.0.2.0", out["NetworkSettings"]["SecondaryIPAddresses"][0]["Addr"])
+            self.assertEqual(24, out["NetworkSettings"]["SecondaryIPAddresses"][0]["PrefixLen"])
+        finally:
+            delete_named_network_ns(network_ns_name)
+
 if __name__ == "__main__":
     unittest.main()