diff --git a/test/case/infix_containers/Readme.adoc b/test/case/infix_containers/Readme.adoc index 89cb0ef9..fc4c21b3 100644 --- a/test/case/infix_containers/Readme.adoc +++ b/test/case/infix_containers/Readme.adoc @@ -4,6 +4,8 @@ Verifies Infix Docker container support: - Basic web server container running in host network mode + - Container enable/disable functionality via configuration + - Container environment variable configuration and access - Common setup with a docker0 bridge, automatic VETH pairs to container(s) - Connecting a container with a VETH pair to a standard Linux bridge - Assigning a physical Ethernet interface to a container @@ -16,6 +18,8 @@ include::container_basic/Readme.adoc[] include::container_enabled/Readme.adoc[] +include::container_environment/Readme.adoc[] + include::container_bridge/Readme.adoc[] include::container_phys/Readme.adoc[] diff --git a/test/case/infix_containers/container_environment/Readme.adoc b/test/case/infix_containers/container_environment/Readme.adoc new file mode 120000 index 00000000..acda1027 --- /dev/null +++ b/test/case/infix_containers/container_environment/Readme.adoc @@ -0,0 +1 @@ +container_environment.adoc \ No newline at end of file diff --git a/test/case/infix_containers/container_environment/container_environment.adoc b/test/case/infix_containers/container_environment/container_environment.adoc new file mode 100644 index 00000000..d7050ef4 --- /dev/null +++ b/test/case/infix_containers/container_environment/container_environment.adoc @@ -0,0 +1,36 @@ +=== Container environment variables +==== Description +Verify that environment variables can be set in container configuration +and are available inside the running container. Tests the 'env' list +functionality by: + +1. Creating a container with multiple environment variables +2. Using a custom script to extract env vars and serve them via HTTP +3. Fetching the served content to verify environment variables are set correctly + +Uses the nftables container image with custom rc.local script. + +==== Topology +ifdef::topdoc[] +image::{topdoc}../../test/case/infix_containers/container_environment/topology.svg[Container environment variables topology] +endif::topdoc[] +ifndef::topdoc[] +ifdef::testgroup[] +image::container_environment/topology.svg[Container environment variables topology] +endif::testgroup[] +ifndef::testgroup[] +image::topology.svg[Container environment variables topology] +endif::testgroup[] +endif::topdoc[] +==== Test sequence +. Set up topology and attach to target DUT +. Configure data interface with static IPv4 +. Create container with environment variables +. Verify container has started +. Verify environment variables are available via HTTP +. Verify basic connectivity to data interface +. Verify environment variables in HTTP response + + +<<< + diff --git a/test/case/infix_containers/container_environment/test.py b/test/case/infix_containers/container_environment/test.py new file mode 100755 index 00000000..f571fcfb --- /dev/null +++ b/test/case/infix_containers/container_environment/test.py @@ -0,0 +1,117 @@ +#!/usr/bin/env python3 +""" +Container environment variables + +Verify that environment variables can be set in container configuration +and are available inside the running container. Tests the 'env' list +functionality by: + +1. Creating a container with multiple environment variables +2. Using a custom script to extract env vars and serve them via HTTP +3. Fetching the served content to verify environment variables are set correctly + +Uses the nftables container image with custom rc.local script. +""" +import infamy +from infamy.util import until, to_binary + + +with infamy.Test() as test: + NAME = "web-env" + DUTIP = "10.0.0.2" + OURIP = "10.0.0.1" + + with test.step("Set up topology and attach to target DUT"): + env = infamy.Env() + target = env.attach("target", "mgmt") + + if not target.has_model("infix-containers"): + test.skip() + + with test.step("Configure data interface with static IPv4"): + _, ifname = env.ltop.xlate("target", "data") + target.put_config_dict("ietf-interfaces", { + "interfaces": { + "interface": [{ + "name": f"{ifname}", + "ipv4": { + "address": [{ + "ip": f"{DUTIP}", + "prefix-length": 24 + }] + } + }] + } + }) + + with test.step("Create container with environment variables"): + script = to_binary("""#!/bin/sh +# Create HTTP response with environment variables +printf "HTTP/1.1 200 OK\\r\\n" > /var/www/response.txt +printf "Content-Type: text/plain\\r\\n" >> /var/www/response.txt +printf "Connection: close\\r\\n\\r\\n" >> /var/www/response.txt + +# Add environment variables using printf to control encoding +printf "TEST_VAR=\\"%s\\"\\n" "$TEST_VAR" >> /var/www/response.txt +printf "APP_PORT=%s\\n" "$APP_PORT" >> /var/www/response.txt +printf "DEBUG_MODE=\\"%s\\"\\n" "$DEBUG_MODE" >> /var/www/response.txt +printf "PATH_WITH_SPACES=\\"%s\\"\\n" "$PATH_WITH_SPACES" >> /var/www/response.txt + +while true; do + nc -l -p 8080 < /var/www/response.txt 2>>/var/www/debug.log || sleep 1 +done +""") + + target.put_config_dict("infix-containers", { + "containers": { + "container": [ + { + "name": f"{NAME}", + "image": f"oci-archive:{infamy.Container.NFTABLES_IMAGE}", + "env": [ + {"key": "TEST_VAR", "value": "hello-world"}, + {"key": "APP_PORT", "value": "8080"}, + {"key": "DEBUG_MODE", "value": "true"}, + {"key": "PATH_WITH_SPACES", "value": "/path with spaces/test"} + ], + "network": { + "host": True + }, + "mount": [ + { + "name": "rc.local", + "content": script, + "target": "/etc/rc.local", + "mode": "0755" + } + ], + "volume": [{ + "name": "www", + "target": "/var/www" + }] + } + ] + } + }) + + with test.step("Verify container has started"): + c = infamy.Container(target) + until(lambda: c.running(NAME), attempts=60) + + with test.step("Verify environment variables are available via HTTP"): + _, hport = env.ltop.xlate("host", "data") + url = infamy.Furl(f"http://{DUTIP}:8080/env.html") + + with infamy.IsolatedMacVlan(hport) as ns: + ns.addip(OURIP) + + with test.step("Verify basic connectivity to data interface"): + ns.must_reach(DUTIP) + + with test.step("Verify environment variables in HTTP response"): + until(lambda: url.nscheck(ns, "TEST_VAR=\"hello-world\""), attempts=10) + until(lambda: url.nscheck(ns, "APP_PORT=8080"), attempts=10) + until(lambda: url.nscheck(ns, "DEBUG_MODE=\"true\""), attempts=10) + until(lambda: url.nscheck(ns, "PATH_WITH_SPACES=\"/path with spaces/test\""), attempts=10) + + test.succeed() diff --git a/test/case/infix_containers/container_environment/topology.dot b/test/case/infix_containers/container_environment/topology.dot new file mode 100644 index 00000000..a66dae68 --- /dev/null +++ b/test/case/infix_containers/container_environment/topology.dot @@ -0,0 +1,24 @@ +graph "1x2" { + layout="neato"; + overlap="false"; + esep="+80"; + + node [shape=record, fontname="DejaVu Sans Mono, Book"]; + edge [color="cornflowerblue", penwidth="2", fontname="DejaVu Serif, Book"]; + + host [ + label="host | { mgmt | data }", + pos="0,12!", + requires="controller", + ]; + + target [ + label="{ mgmt | data } | target", + pos="10,12!", + + requires="infix", + ]; + + host:mgmt -- target:mgmt [requires="mgmt", color=lightgrey] + host:data -- target:data [color=black] +} \ No newline at end of file diff --git a/test/case/infix_containers/container_environment/topology.svg b/test/case/infix_containers/container_environment/topology.svg new file mode 100644 index 00000000..ff3d246b --- /dev/null +++ b/test/case/infix_containers/container_environment/topology.svg @@ -0,0 +1,42 @@ + + + + + + +1x2 + + + +host + +host + +mgmt + +data + + + +target + +mgmt + +data + +target + + + +host:mgmt--target:mgmt + + + + +host:data--target:data + + + + diff --git a/test/case/infix_containers/infix_containers.yaml b/test/case/infix_containers/infix_containers.yaml index 255799b9..83d07fd7 100644 --- a/test/case/infix_containers/infix_containers.yaml +++ b/test/case/infix_containers/infix_containers.yaml @@ -6,6 +6,10 @@ - name: container_enabled case: container_enabled/test.py +# Disabled for v25.08, new/unstable +# - name: container_environment +# case: container_environment/test.py + - name: container_bridge case: container_bridge/test.py