mirror of
https://github.com/kernelkit/infix.git
synced 2026-08-03 22:33:02 +02:00
Merge pull request #955 from kernelkit/lingering-containers
Lingering containers
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
#
|
||||
# Automatically generated make config: don't edit
|
||||
# Busybox version: 1.36.1
|
||||
# Tue Oct 22 13:12:02 2024
|
||||
# Sun Feb 9 12:25:37 2025
|
||||
#
|
||||
CONFIG_HAVE_DOT_CONFIG=y
|
||||
|
||||
@@ -17,7 +17,7 @@ CONFIG_SHOW_USAGE=y
|
||||
CONFIG_FEATURE_VERBOSE_USAGE=y
|
||||
# CONFIG_FEATURE_COMPRESS_USAGE is not set
|
||||
CONFIG_LFS=y
|
||||
# CONFIG_PAM is not set
|
||||
CONFIG_PAM=y
|
||||
CONFIG_FEATURE_DEVPTS=y
|
||||
CONFIG_FEATURE_UTMP=y
|
||||
CONFIG_FEATURE_WTMP=y
|
||||
@@ -325,7 +325,7 @@ CONFIG_FEATURE_STAT_FILESYSTEM=y
|
||||
CONFIG_STTY=y
|
||||
CONFIG_SUM=y
|
||||
CONFIG_SYNC=y
|
||||
# CONFIG_FEATURE_SYNC_FANCY is not set
|
||||
CONFIG_FEATURE_SYNC_FANCY=y
|
||||
CONFIG_FSYNC=y
|
||||
# CONFIG_TAC is not set
|
||||
CONFIG_TAIL=y
|
||||
@@ -336,7 +336,7 @@ CONFIG_TEST=y
|
||||
CONFIG_TEST1=y
|
||||
CONFIG_TEST2=y
|
||||
CONFIG_FEATURE_TEST_64=y
|
||||
# CONFIG_TIMEOUT is not set
|
||||
CONFIG_TIMEOUT=y
|
||||
CONFIG_TOUCH=y
|
||||
CONFIG_FEATURE_TOUCH_SUSV3=y
|
||||
CONFIG_TR=y
|
||||
@@ -357,7 +357,7 @@ CONFIG_BASE32=y
|
||||
CONFIG_BASE64=y
|
||||
CONFIG_UUENCODE=y
|
||||
CONFIG_WC=y
|
||||
# CONFIG_FEATURE_WC_LARGE is not set
|
||||
CONFIG_FEATURE_WC_LARGE=y
|
||||
CONFIG_WHO=y
|
||||
CONFIG_W=y
|
||||
CONFIG_USERS=y
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
task name:container-%i :setup \
|
||||
[2345] container -n %i setup -- Setup container %i
|
||||
sysv <!usr/container:%i> :%i pid:!/run/container:%i.pid log:prio:local1,tag:%i kill:10 \
|
||||
[2345] container -n %i -- container %i
|
||||
# Start a container instance (%i) and redirect logs to /log/container
|
||||
# Give podman enough time to properly shut down the container. Every
|
||||
# time we start a container we run the setup stage, disable the Finit
|
||||
# timeout to allow the setup stage to run to completion.
|
||||
sysv log:prio:local1,tag:%i kill:10 pid:!/run/container:%i.pid \
|
||||
pre:0,/usr/sbin/container cleanup:0,/usr/sbin/container \
|
||||
[2345] <!> :%i container -n %i -- container %i
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/bin/sh
|
||||
#!/bin/bash
|
||||
# This script can be used to start, stop, create, and delete containers.
|
||||
# It is what confd use, with the Finit container@.conf template, to set
|
||||
# up, run, and delete containers.
|
||||
@@ -9,6 +9,7 @@
|
||||
DOWNLOADS=/var/lib/containers/oci
|
||||
BUILTIN=/lib/oci
|
||||
TMPDIR=/var/tmp
|
||||
container=$0
|
||||
checksum=""
|
||||
extracted=
|
||||
timeout=30
|
||||
@@ -109,33 +110,34 @@ EOF
|
||||
# If there are more index files, this function does not handle them.
|
||||
unpack_archive()
|
||||
{
|
||||
image=$1
|
||||
name=$2
|
||||
uri=$1
|
||||
tag=$2
|
||||
img=$(basename "$uri")
|
||||
|
||||
# Supported transports for load and create
|
||||
case "$image" in
|
||||
case "$uri" in
|
||||
oci:*) # Unpacked OCI image
|
||||
file=${image#oci:}
|
||||
file=${uri#oci:}
|
||||
;;
|
||||
oci-archive:*) # Packed OCI image, .tar or .tar.gz format
|
||||
file=${image#oci-archive:}
|
||||
file=${uri#oci-archive:}
|
||||
;;
|
||||
ftp://* | http://* | https://*)
|
||||
if ! file=$(fetch "$image"); then
|
||||
if ! file=$(fetch "$uri"); then
|
||||
return 1
|
||||
fi
|
||||
;;
|
||||
*) # docker://*, docker-archive:*, or URL
|
||||
if podman image exists "$image"; then
|
||||
echo "$image"
|
||||
if podman image exists "$img"; then
|
||||
echo "$img"
|
||||
return 0
|
||||
fi
|
||||
# XXX: use --retry=0 with Podman 5.0 or later.
|
||||
if ! id=$(podman pull --quiet "$image"); then
|
||||
log "Failed pulling $image"
|
||||
if ! id=$(podman pull --quiet "$uri"); then
|
||||
log "Failed pulling $uri"
|
||||
return 1
|
||||
fi
|
||||
# Echo image name to caller
|
||||
# Echo image tag to caller
|
||||
podman images --filter id="$id" --format "{{.Repository}}:{{.Tag}}"
|
||||
return 0
|
||||
;;
|
||||
@@ -147,7 +149,7 @@ unpack_archive()
|
||||
elif [ -e "$BUILTIN/$file" ]; then
|
||||
file="$BUILTIN/$file"
|
||||
else
|
||||
err 1 "cannot find OCI archive $file in search path."
|
||||
err 1 "cannot find OCI archive $file in URI $uri"
|
||||
fi
|
||||
fi
|
||||
|
||||
@@ -171,8 +173,8 @@ unpack_archive()
|
||||
|
||||
dir=$(dirname "$index")
|
||||
if echo "$dir" | grep -q ":"; then
|
||||
if [ -z "$name" ]; then
|
||||
name="$dir"
|
||||
if [ -z "$tag" ]; then
|
||||
tag="$dir"
|
||||
fi
|
||||
sanitized_dir=$(echo "$dir" | cut -d':' -f1)
|
||||
mv "$dir" "$sanitized_dir" || err 1 "failed renaming $dir to $sanitized_dir"
|
||||
@@ -188,21 +190,21 @@ unpack_archive()
|
||||
rm -rf "$dir"
|
||||
fi
|
||||
|
||||
# Rename image from podman default $dir:latest
|
||||
if [ -n "$name" ]; then
|
||||
podman tag "$dir" "$name" >/dev/null
|
||||
podman rmi "$dir" >/dev/null
|
||||
# Retag image from podman default $dir:latest
|
||||
if [ -n "$tag" ]; then
|
||||
podman tag "$dir" "$tag" >/dev/null
|
||||
podman rmi "$dir" >/dev/null
|
||||
else
|
||||
name=$dir
|
||||
tag=$dir
|
||||
fi
|
||||
|
||||
echo "$name"
|
||||
echo "$tag"
|
||||
}
|
||||
|
||||
running()
|
||||
{
|
||||
run=$(podman inspect "$1" 2>/dev/null |jq .[].State.Running)
|
||||
[ "$run" = "true" ] && return 0
|
||||
status=$(podman inspect -f '{{.State.Status}}' "$1" 2>/dev/null)
|
||||
[ "$status" = "running" ] && return 0
|
||||
return 1
|
||||
}
|
||||
|
||||
@@ -272,7 +274,6 @@ create()
|
||||
delete()
|
||||
{
|
||||
name=$1
|
||||
image=$2
|
||||
|
||||
if [ -z "$name" ]; then
|
||||
echo "Usage:"
|
||||
@@ -281,9 +282,11 @@ delete()
|
||||
fi
|
||||
|
||||
# Should already be stopped, but if not ...
|
||||
container stop "$name"
|
||||
log "$name: should already be stopped, double checking ..."
|
||||
container stop "$name" >/dev/null
|
||||
|
||||
while running "$name"; do
|
||||
log "$name: still running, waiting for it to stop ..."
|
||||
_=$((timeout -= 1))
|
||||
if [ $timeout -le 0 ]; then
|
||||
err 1 "timed out waiting for container $1 to stop before deleting it."
|
||||
@@ -291,6 +294,7 @@ delete()
|
||||
sleep 1
|
||||
done
|
||||
|
||||
log "$name: calling podman rm -vif ..."
|
||||
podman rm -vif "$name" >/dev/null 2>&1
|
||||
[ -n "$quiet" ] || log "Container $name has been removed."
|
||||
}
|
||||
@@ -315,7 +319,7 @@ start()
|
||||
return
|
||||
fi
|
||||
|
||||
initctl -bq cond set "container:$name"
|
||||
initctl start container:$name
|
||||
# Real work is done by wrap() courtesy of finit sysv emulation
|
||||
}
|
||||
|
||||
@@ -328,7 +332,7 @@ stop()
|
||||
return
|
||||
fi
|
||||
|
||||
initctl -bq cond clr "container:$name"
|
||||
initctl stop container:$name
|
||||
# Real work is done by wrap() courtesy of finit sysv emulation
|
||||
}
|
||||
|
||||
@@ -337,7 +341,8 @@ wrap()
|
||||
name=$1
|
||||
cmd=$2
|
||||
|
||||
podman "$cmd" "$name"
|
||||
# Skip "echo $name" from podman start in log
|
||||
podman "$cmd" "$name" >/dev/null
|
||||
}
|
||||
|
||||
# Removes network $1 from all containers
|
||||
@@ -426,11 +431,12 @@ commands:
|
||||
run NAME [CMD] Run a container interactively, with an optional command
|
||||
save IMAGE FILE Save a container image to an OCI tarball FILE[.tar.gz]
|
||||
setup NAME Create and set up container as a Finit task
|
||||
shell Start a shell inside a container
|
||||
shell [CMD] Start a shell, or run CMD, inside a container
|
||||
show [image | volume] Show containers, images, or volumes
|
||||
stat Show continuous stats about containers (Ctrl-C aborts)
|
||||
start [NAME] Start a container, see -n
|
||||
stop [NAME] Stop a container, see -n
|
||||
upgrade NAME Upgrade a running container (stop, pull, restart)
|
||||
volume [prune] Prune unused volumes
|
||||
EOF
|
||||
}
|
||||
@@ -582,19 +588,24 @@ case $cmd in
|
||||
;;
|
||||
delete)
|
||||
cmd=$1
|
||||
name=$2
|
||||
[ -n "$name" ] || name=$2
|
||||
if [ "$cmd" = "network" ] && [ -n "$name" ]; then
|
||||
netwrm "$name"
|
||||
else
|
||||
delete "$@"
|
||||
[ -n "$name" ] || name=$1
|
||||
delete "$name"
|
||||
fi
|
||||
;;
|
||||
exec)
|
||||
podman exec -it "$@"
|
||||
if [ -z "$name" ]; then
|
||||
name="$1"
|
||||
shift
|
||||
fi
|
||||
podman exec -i "$name" "$@"
|
||||
;;
|
||||
flush)
|
||||
echo "Cleaning up any lingering containers";
|
||||
podman rm -av
|
||||
podman rm -av $force
|
||||
;;
|
||||
find)
|
||||
cmd=$1
|
||||
@@ -700,17 +711,25 @@ case $cmd in
|
||||
script=/run/containers/${name}.sh
|
||||
[ -x "$script" ] || err 1 "setup: $script does not exist or is not executable."
|
||||
while ! "$script"; do
|
||||
# Wait for address/route changes, or retry every 60 secods
|
||||
# shellcheck disable=2162,3045
|
||||
ip monitor address route | while read -t 60 _; do break; done
|
||||
log "${name}: setup failed, waiting for network changes ..."
|
||||
read -t 60 _ < <(ip monitor address route)
|
||||
|
||||
# On IP address/route changes, wait a few seconds more to ensure
|
||||
# the system has ample time to react and set things up for us.
|
||||
log "${name}: retrying ..."
|
||||
sleep 2
|
||||
done
|
||||
;;
|
||||
shell)
|
||||
podman exec -it "$1" sh -l
|
||||
if [ -z "$name" ]; then
|
||||
name="$1"
|
||||
shift
|
||||
fi
|
||||
if [ $# -gt 0 ]; then
|
||||
podman exec -i "$name" sh -c "$*"
|
||||
else
|
||||
podman exec -it "$name" sh -l
|
||||
fi
|
||||
;;
|
||||
show)
|
||||
cmd=$1
|
||||
@@ -803,15 +822,15 @@ case $cmd in
|
||||
|
||||
# Likely an OCI archive, or local directory, assume user has updated image.
|
||||
if echo "$img" | grep -Eq '^localhost/'; then
|
||||
file=$(awk '{s=$NF} END{print s}' "$script")
|
||||
echo "Upgrading container ${1} with local archive: $file ..."
|
||||
file=$(awk '/^# meta-image:/ {print $3}' "$script")
|
||||
echo ">> Upgrading container $1 using $file ..."
|
||||
else
|
||||
printf ">> Stopping ... "
|
||||
podman stop "$1"
|
||||
printf ">> "
|
||||
podman pull "$img" || (echo "Failed fetching $img, check your network (settings)."; exit 1)
|
||||
echo ">> Starting $1 ..."
|
||||
fi
|
||||
echo ">> Starting $1 ..."
|
||||
if ! "$script"; then
|
||||
echo ">> Failed recreating container $1"
|
||||
exit 1
|
||||
@@ -831,6 +850,22 @@ case $cmd in
|
||||
esac
|
||||
;;
|
||||
*)
|
||||
if [ -n "$SERVICE_SCRIPT_TYPE" ] && [ -n "$SERVICE_ID" ]; then
|
||||
case "$SERVICE_SCRIPT_TYPE" in
|
||||
pre)
|
||||
# Called as pre-script from Finit service
|
||||
exec $container -q -n "$SERVICE_ID" setup
|
||||
;;
|
||||
cleanup)
|
||||
# Called as cleanup-script from Finit service
|
||||
log "Calling $container -n $SERVICE_ID delete"
|
||||
exec $container -q -n "$SERVICE_ID" delete
|
||||
;;
|
||||
*)
|
||||
false
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
usage
|
||||
exit 1
|
||||
;;
|
||||
|
||||
+9
-2
@@ -10,17 +10,24 @@ All notable changes to the project are documented in this file.
|
||||
### Changes
|
||||
- Upgrade Linux kernel to 6.12.16 (LTS)
|
||||
- Upgrade Buildroot to 2024.02.11 (LTS)
|
||||
- YANG type for SSH private/public keys has changed, from
|
||||
ietf-crypto-types to infix-crypto-types
|
||||
- Add support for link aggregation (lag), static (balance-xor) and LACP
|
||||
- Add support for the [i.MX 8M Plus EVK][EVK]
|
||||
- YANG type change for SSH private/public keys, from ietf-crypto-types
|
||||
to infix-crypto-types
|
||||
- Drop automatic default route (interface route) for IPv4 autoconf, not
|
||||
necessary and causes more confusion than good. Issue #923
|
||||
|
||||
### Fixes
|
||||
- Fix #902: containers "linger" in the system (state 'exited') after
|
||||
having removed them from the configuration
|
||||
- Fix #930: container configuration changes does not apply at runtime
|
||||
only when saved to `startup-config` and system is rebooted
|
||||
- Fix #956: CLI `copy` command complains it cannot change owner when
|
||||
copying `factory-config` to `running-config`. Bogus error, the
|
||||
latter is not really a file
|
||||
|
||||
[EVK]: https://www.nxp.com/design/design-center/development-boards-and-designs/8MPLUSLPD4-EVK
|
||||
|
||||
|
||||
[v25.01.0][] - 2025-01-31
|
||||
-------------------------
|
||||
|
||||
+40
-31
@@ -31,9 +31,9 @@ Infix comes with native support for Docker containers using [podman][].
|
||||
The [YANG model][1] describes the current level of support, complete
|
||||
enough to run both system and application containers.
|
||||
|
||||
Key design features, like using Linux switchdev, allow users to assign
|
||||
switch ports directly to containers, not just bridged VETH pairs, this
|
||||
is a rare and in many cases *unique* feature of Infix.
|
||||
Key design features of Infix, like using Linux switchdev, allow users to
|
||||
assign switch ports directly to containers, not just bridged VETH pairs.
|
||||
This is a rare and in many cases *unique* feature of Infix.
|
||||
|
||||
All network specific settings are done using the IETF interfaces YANG
|
||||
model, with augments for containers to ensure smooth integration with
|
||||
@@ -43,7 +43,7 @@ container networking in podman.
|
||||
> Even though the `podman` command can be used directly from a shell
|
||||
> prompt, we strongly recommend using the CLI commands instead. They
|
||||
> employ the services of a wrapper `container` script which handles the
|
||||
> integration of containers in the system.
|
||||
> integration of Docker containers in the system.
|
||||
|
||||
|
||||
Caution
|
||||
@@ -83,18 +83,20 @@ In the CLI, containers can be run in one of two ways:
|
||||
1. `container run IMAGE [COMMAND]`, or
|
||||
2. enter `configure` context, then `edit container NAME`
|
||||
|
||||
The former is useful mostly for testing, or running single commands in
|
||||
an image. It is a wrapper for `podman run -it --rm ...`, while the
|
||||
latter is a wrapper and adaptation of `podman create ...`.
|
||||
The first is useful mostly for testing, or running single commands in
|
||||
an image. It is a wrapper for `podman run -it --rm ...`.
|
||||
|
||||
The second creates a read-only container that is automatically started
|
||||
at every boot. When non-volatile storage is needed, data stored in a
|
||||
volume is persisted until explicitly removed from the configuration,
|
||||
i.e., across host and container reboots and upgrades.
|
||||
The second creates a read-only container that by default automatically
|
||||
start at every boot. It basically wraps `podman create ...`.
|
||||
|
||||
Another option is [Content Mounts](#content-mounts), where the content
|
||||
of a file mounted into the container is kept along with the container
|
||||
configuration in the device's `startup-config`.
|
||||
When non-volatile storage is needed two complementary options exist:
|
||||
|
||||
- **Volumes:** data stored in a volume is persisted until explicitly
|
||||
removed from the configuration, i.e., across host reboots and
|
||||
container upgrades
|
||||
- **[Content Mounts](#content-mounts):** where the content of a file
|
||||
mounted into the container is kept along with the container
|
||||
configuration in the device's `startup-config`
|
||||
|
||||
Podman ensures (using tmpfs) all containers have writable directories
|
||||
for certain critical file system paths: `/dev`, `/dev/shm`, `/run`,
|
||||
@@ -127,24 +129,30 @@ Classic Hello World:
|
||||
Hello from Docker!
|
||||
This message shows that your installation appears to be working correctly.
|
||||
|
||||
Persistent web server using nginx, sharing the host's network:
|
||||
A web server with [nginx][], using standard docker bridge. Podman will
|
||||
automatically create a VETH pair for us, connecting the container to the
|
||||
`docker0` bridge:
|
||||
|
||||
admin@example:/> configure
|
||||
admin@example:/config> edit container web
|
||||
admin@example:/config/container/web> set image docker://nginx:alpine
|
||||
admin@example:/config/container/web> set publish 80:80
|
||||
admin@example:/config/container/web> set network host
|
||||
admin@example:/config/container/web> leave
|
||||
admin@example:/config/> edit interface docker0
|
||||
admin@example:/config/interface/docker0/> set container-network
|
||||
admin@example:/config/interface/docker0/> end
|
||||
admin@example:/config/> edit container web
|
||||
admin@example:/config/container/web/> set image docker://nginx:alpine
|
||||
admin@example:/config/container/web/> set network publish 8080:80
|
||||
admin@example:/config/container/web/> set network interface docker0
|
||||
admin@example:/config/container/web/> set volume cache target /var/cache
|
||||
admin@example:/config/container/web/> leave
|
||||
admin@example:/> show container
|
||||
|
||||
Exit to the shell and verify the service with curl, or try to attach
|
||||
to your device's IP address using your browser:
|
||||
|
||||
admin@example:~$ curl http://localhost
|
||||
admin@example:~$ curl http://localhost:8080
|
||||
|
||||
or connect to port 80 of your running Infix system with a browser. See
|
||||
the following sections for how to add more interfaces and manage your
|
||||
container at runtime.
|
||||
or connect to port 8080 of your running Infix system with a browser.
|
||||
See the following sections for how to add more interfaces and manage
|
||||
your container at runtime.
|
||||
|
||||
|
||||
Container Images
|
||||
@@ -456,11 +464,11 @@ in a `bridge`. Below an example of a system container calls `set
|
||||
network interface docker0`, here we show how to set options for that
|
||||
network:
|
||||
|
||||
admin@example:/config/container/ntpd/> edit network docker0
|
||||
admin@example:/config/container/ntpd/network/docker0/>
|
||||
admin@example:/config/container/ntpd/network/docker0/> set option
|
||||
admin@example:/config/container/ntpd/> edit network interface docker0
|
||||
admin@example:/config/container/ntpd/network/interface/docker0/>
|
||||
admin@example:/config/container/ntpd/network/interface/docker0/> set option
|
||||
<string> Options for masquerading container bridges.
|
||||
admin@example:/config/container/ntpd/network/docker0/> help option
|
||||
admin@example:/config/container/ntpd/network/interface/docker0/> help option
|
||||
NAME
|
||||
option <string>
|
||||
|
||||
@@ -471,9 +479,9 @@ network:
|
||||
mac=00:01:02:c0:ff:ee -- set fixed MAC address in container
|
||||
interface_name=foo0 -- set interface name inside container
|
||||
|
||||
admin@example:/config/container/ntpd/network/docker0/> set option ip=172.17.0.2
|
||||
admin@example:/config/container/ntpd/network/docker0/> set option interface_name=wan
|
||||
admin@example:/config/container/ntpd/network/docker0/> leave
|
||||
admin@example:/config/container/ntpd/network/interface/docker0/> set option ip=172.17.0.2
|
||||
admin@example:/config/container/ntpd/network/interface/docker0/> set option interface_name=wan
|
||||
admin@example:/config/container/ntpd/network/interface/docker0/> leave
|
||||
|
||||
|
||||
### Container Host Interface
|
||||
@@ -903,4 +911,5 @@ Container Image](#upgrading-a-container-image) (above).
|
||||
[14]: https://github.com/kernelkit/curiOS/
|
||||
[15]: https://github.com/kernelkit/curiOS/blob/2e4748f65e356b2c117f586cd9420d7ba66f79d5/board/system/rootfs/etc/inittab
|
||||
[tini]: https://github.com/krallin/tini
|
||||
[nginx]: https://hub.docker.com/_/nginx
|
||||
[podman]: https://podman.io
|
||||
|
||||
@@ -17,17 +17,20 @@ run name:startup log:prio:user.notice norestart <pid/confd> env:/etc/default/con
|
||||
-- Loading startup-config
|
||||
|
||||
# Run if loading startup-config fails for some reason
|
||||
run name:failure log:prio:user.critical norestart <pid/confd> env:/etc/default/confd if:<run/startup/failure> \
|
||||
[S] /usr/libexec/confd/load -t $CONFD_TIMEOUT failure-config \
|
||||
run name:failure log:prio:user.critical norestart env:/etc/default/confd \
|
||||
if:<run/startup/failure> \
|
||||
[S] <pid/confd> /usr/libexec/confd/load -t $CONFD_TIMEOUT failure-config \
|
||||
-- Loading failure-config
|
||||
|
||||
run name:error :2 log:console norestart if:<run/failure/failure> \
|
||||
run name:error :2 log:console norestart \
|
||||
if:<run/failure/failure> \
|
||||
[S] /usr/libexec/confd/error --
|
||||
|
||||
service name:netopeer notify:none log <pid/confd> env:/etc/default/confd \
|
||||
[12345] netopeer2-server -F -t $CONFD_TIMEOUT -v 1 \
|
||||
service name:netopeer notify:none log env:/etc/default/confd \
|
||||
[12345] <pid/confd> netopeer2-server -F -t $CONFD_TIMEOUT -v 1 \
|
||||
-- NETCONF server
|
||||
|
||||
# Create initial /etc/resolv.conf after successful bootstrap
|
||||
task name:resolv :conf norestart <pid/dnsmasq> if:<run/startup/success> \
|
||||
[S] resolvconf -u -- Update DNS configuration
|
||||
task name:resolv :conf norestart \
|
||||
if:<run/startup/success> \
|
||||
[S] <pid/dnsmasq> resolvconf -u -- Update DNS configuration
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# From https://github.com/troglobit/finit/releases/
|
||||
sha256 7e4efaa4c165aabaf145ac3e6ae2ac907209e69758f4892c83cfccc579876c27 finit-4.9.tar.gz
|
||||
sha256 ead37606012ced935c66dcfd88786d533d08964b00adb7899ea156ae29b7896a finit-4.10-rc1.tar.gz
|
||||
|
||||
# Locally calculated
|
||||
sha256 2fd62c0fe6ea6d1861669f4c87bda83a0b5ceca64f4baa4d16dd078fbd218c14 LICENSE
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
#
|
||||
################################################################################
|
||||
|
||||
FINIT_VERSION = 4.9
|
||||
FINIT_VERSION = 4.10-rc1
|
||||
FINIT_SITE = https://github.com/troglobit/finit/releases/download/$(FINIT_VERSION)
|
||||
FINIT_LICENSE = MIT
|
||||
FINIT_LICENSE_FILES = LICENSE
|
||||
|
||||
@@ -75,9 +75,6 @@ int core_post_hook(sr_session_ctx_t *session, uint32_t sub_id, const char *modul
|
||||
return SR_ERR_SYS;
|
||||
}
|
||||
|
||||
/* Everything done, including interfaces, launch all container scripts */
|
||||
infix_containers_post_hook(session, priv);
|
||||
|
||||
/* skip reload in bootstrap, implicit reload in runlevel change */
|
||||
if (systemf("runlevel >/dev/null 2>&1"))
|
||||
return SR_ERR_OK;
|
||||
|
||||
@@ -213,13 +213,9 @@ int hostnamefmt (struct confd *confd, const char *fmt, char *hostnm, size_t
|
||||
|
||||
/* infix-containers.c */
|
||||
#ifdef CONTAINERS
|
||||
int infix_containers_init(struct confd *confd);
|
||||
void infix_containers_pre_hook(sr_session_ctx_t *session, struct confd *confd);
|
||||
void infix_containers_post_hook(sr_session_ctx_t *session, struct confd *confd);
|
||||
int infix_containers_init(struct confd *confd);
|
||||
#else
|
||||
static inline int infix_containers_init(struct confd *confd) { return 0; }
|
||||
static inline void infix_containers_pre_hook(sr_session_ctx_t *session, struct confd *confd) {}
|
||||
static inline void infix_containers_post_hook(sr_session_ctx_t *session, struct confd *confd) {}
|
||||
static inline int infix_containers_init(struct confd *confd) { return 0; }
|
||||
#endif
|
||||
|
||||
/* infix-dhcp-common.c */
|
||||
|
||||
@@ -21,12 +21,19 @@
|
||||
#define CFG_XPATH "/infix-containers:containers"
|
||||
|
||||
#define _PATH_CONT "/run/containers"
|
||||
#define _PATH_INBOX _PATH_CONT "/INBOX"
|
||||
|
||||
|
||||
/*
|
||||
* Create a setup/create/upgrade script and instantiate a new instance
|
||||
* that Finit will start when all networking and other dependencies are
|
||||
* out of the way. Finit calls the `/usr/sbin/container` wrapper script
|
||||
* in the pre: hook to fetch and create the container instance.
|
||||
*
|
||||
* The script we create here, on every boot, contains all information
|
||||
* needed to recreate and upgrade the user's container at runtime.
|
||||
*/
|
||||
static int add(const char *name, struct lyd_node *cif)
|
||||
{
|
||||
const char *restart_policy, *string;
|
||||
const char *restart_policy, *string, *image;
|
||||
struct lyd_node *node, *nets, *caps;
|
||||
char script[strlen(name) + 5];
|
||||
FILE *fp, *ap;
|
||||
@@ -53,10 +60,17 @@ static int add(const char *name, struct lyd_node *cif)
|
||||
* setup at creation/boot and for manual upgrade. The delete
|
||||
* command ensures any already running container is stopped and
|
||||
* deleted so that it releases all claimed resources.
|
||||
*
|
||||
* The odd meta data is not used by the script itself, instead
|
||||
* it is used by the /usr/sbin/container wrapper when upgrading
|
||||
* a running container instance.
|
||||
*/
|
||||
image = lydx_get_cattr(cif, "image");
|
||||
fprintf(fp, "#!/bin/sh\n"
|
||||
"# meta-name: %s\n"
|
||||
"# meta-image: %s\n"
|
||||
"container --quiet delete %s >/dev/null\n"
|
||||
"container --quiet", name);
|
||||
"container --quiet", name, image, name);
|
||||
|
||||
LYX_LIST_FOR_EACH(lyd_child(cif), node, "dns")
|
||||
fprintf(fp, " --dns %s", lyd_get_value(node));
|
||||
@@ -197,7 +211,7 @@ static int add(const char *name, struct lyd_node *cif)
|
||||
fprintf(fp, " --checksum sha512:%s", string);
|
||||
}
|
||||
|
||||
fprintf(fp, " create %s %s", name, lydx_get_cattr(cif, "image"));
|
||||
fprintf(fp, " create %s %s", name, image);
|
||||
|
||||
if ((string = lydx_get_cattr(cif, "command")))
|
||||
fprintf(fp, " %s", string);
|
||||
@@ -206,24 +220,22 @@ static int add(const char *name, struct lyd_node *cif)
|
||||
fchmod(fileno(fp), 0700);
|
||||
fclose(fp);
|
||||
|
||||
/* Enable, or update, container -- both trigger setup script. */
|
||||
/* Enable, or update, container -- both trigger container setup. */
|
||||
systemf("initctl -bnq enable container@%s.conf", name);
|
||||
systemf("initctl -bnq touch container@%s.conf", name);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Remove setup/create/upgrade script and disable the currently running
|
||||
* instance. The `/usr/sbin/container` wrapper script is called when
|
||||
* Finit removes the instance, and it does not need the file we erase.
|
||||
*/
|
||||
static int del(const char *name)
|
||||
{
|
||||
char fn[strlen(_PATH_CONT) + strlen(name) + 10];
|
||||
|
||||
/* Remove container setup script */
|
||||
snprintf(fn, sizeof(fn), "%s/%s.sh", _PATH_CONT, name);
|
||||
erase(fn);
|
||||
|
||||
/* Stop and schedule for deletion */
|
||||
systemf("initctl -bnq stop container:%s", name);
|
||||
writesf(name, "a", "%s", _PATH_INBOX);
|
||||
erasef("%s/%s.sh", _PATH_CONT, name);
|
||||
systemf("initctl -bnq disable container@%s.conf", name);
|
||||
|
||||
return SR_ERR_OK;
|
||||
}
|
||||
@@ -312,7 +324,7 @@ static int action(sr_session_ctx_t *session, uint32_t sub_id, const char *xpath,
|
||||
return SR_ERR_INTERNAL;
|
||||
cmd += 2;
|
||||
|
||||
DEBUG("CALLING 'container %s %s' (xpath %s)", cmd, name, xpath);
|
||||
DEBUG("RPC xpath %s, calling 'container %s %s'", xpath, cmd, name);
|
||||
if (systemf("container %s %s", cmd, name))
|
||||
return SR_ERR_INTERNAL;
|
||||
|
||||
@@ -335,34 +347,6 @@ static int oci_load(sr_session_ctx_t *session, uint32_t sub_id, const char *xpat
|
||||
return SR_ERR_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* Containers depend on a lot of other system resources being properly
|
||||
* set up, e.g., networking, which is run by dagger. So we need to wait
|
||||
* for all that before we can launch new, or modified, containers. This
|
||||
* post hook runs as (one of) the last actions on a config change/boot.
|
||||
*/
|
||||
void infix_containers_post_hook(sr_session_ctx_t *session, struct confd *confd)
|
||||
{
|
||||
char name[256];
|
||||
FILE *fp;
|
||||
|
||||
fp = fopen(_PATH_INBOX, "r");
|
||||
if (!fp)
|
||||
return; /* nothing to delete */
|
||||
|
||||
while (fgets(name, sizeof(name), fp)) {
|
||||
chomp(name);
|
||||
systemf("initctl -bnq disable container@%s.conf", name);
|
||||
systemf("container delete %s", name);
|
||||
systemf("initctl -bnq cond clr container:%s", name);
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
erase(_PATH_INBOX);
|
||||
|
||||
systemf("podman volume prune -f");
|
||||
}
|
||||
|
||||
int infix_containers_init(struct confd *confd)
|
||||
{
|
||||
int rc;
|
||||
|
||||
@@ -38,6 +38,7 @@
|
||||
<COMMAND name="prune" help="Clean up all unused containers, images and volume data">
|
||||
<ACTION sym="script" in="tty" out="tty" interrupt="true">
|
||||
doas podman system prune
|
||||
doas podman volume prune
|
||||
</ACTION>
|
||||
</COMMAND>
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ Verifies Infix Docker container support:
|
||||
- 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
|
||||
- Upgrading a container with a volume, verifying volume data is intact
|
||||
- Basic firewall container running in host network mode and full privileges
|
||||
|
||||
<<<
|
||||
@@ -19,4 +20,6 @@ include::container_phys/Readme.adoc[]
|
||||
|
||||
include::container_veth/Readme.adoc[]
|
||||
|
||||
include::container_volume/Readme.adoc[]
|
||||
|
||||
include::container_firewall_basic/Readme.adoc[]
|
||||
|
||||
@@ -21,10 +21,10 @@ endif::testgroup[]
|
||||
endif::topdoc[]
|
||||
==== Test sequence
|
||||
. Set up topology and attach to target DUT
|
||||
. Create container 'web-docker0' from bundled OCI image
|
||||
. Verify container 'web-docker0' has started
|
||||
. Verify basic DUT connectivity, host:data can ping DUT 10.0.0.2
|
||||
. Verify container 'web-docker0' is reachable on http://10.0.0.2:8080
|
||||
. Create httpd container from bundled OCI image
|
||||
. Verify container has started
|
||||
. Verify DUT connectivity, host can reach 10.0.0.2
|
||||
. Verify container is reachable on http://10.0.0.2:8080
|
||||
|
||||
|
||||
<<<
|
||||
|
||||
@@ -22,7 +22,7 @@ with infamy.Test() as test:
|
||||
NAME = "web-docker0"
|
||||
DUTIP = "10.0.0.2"
|
||||
OURIP = "10.0.0.1"
|
||||
BODY = "<html><body><p>Kilroy was here</p></body></html>"
|
||||
MESG = "It works"
|
||||
URL = f"http://{DUTIP}:8080/index.html"
|
||||
|
||||
with test.step("Set up topology and attach to target DUT"):
|
||||
@@ -32,61 +32,55 @@ with infamy.Test() as test:
|
||||
if not target.has_model("infix-containers"):
|
||||
test.skip()
|
||||
|
||||
with test.step("Create container 'web-docker0' from bundled OCI image"):
|
||||
with test.step("Create httpd container from bundled OCI image"):
|
||||
_, ifname = env.ltop.xlate("target", "data")
|
||||
data = to_binary(BODY)
|
||||
target.put_config_dict("ietf-interfaces", {
|
||||
"interfaces": {
|
||||
"interface": [
|
||||
{
|
||||
"name": f"{ifname}",
|
||||
"ipv4": {
|
||||
"address": [{
|
||||
"ip": f"{DUTIP}",
|
||||
"prefix-length": 24
|
||||
}]
|
||||
target.put_config_dicts({
|
||||
"ietf-interfaces": {
|
||||
"interfaces": {
|
||||
"interface": [
|
||||
{
|
||||
"name": f"{ifname}",
|
||||
"ipv4": {
|
||||
"address": [{
|
||||
"ip": f"{DUTIP}",
|
||||
"prefix-length": 24
|
||||
}]
|
||||
}
|
||||
}, {
|
||||
"name": "docker0",
|
||||
"type": "infix-if-type:bridge",
|
||||
"container-network": {
|
||||
"type": "bridge",
|
||||
"subnet": [
|
||||
{"subnet": "172.17.0.0/16"},
|
||||
{"subnet": "2a02:2789:724:eb8:1::/80"}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "docker0",
|
||||
"type": "infix-if-type:bridge",
|
||||
"container-network": {
|
||||
"type": "bridge",
|
||||
"subnet": [
|
||||
{ "subnet": "172.17.0.0/16" },
|
||||
{ "subnet": "2a02:2789:724:eb8:1::/80" }
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
})
|
||||
target.put_config_dict("infix-containers", {
|
||||
"containers": {
|
||||
"container": [
|
||||
{
|
||||
]
|
||||
}
|
||||
},
|
||||
"infix-containers": {
|
||||
"containers": {
|
||||
"container": [{
|
||||
"name": f"{NAME}",
|
||||
"image": f"oci-archive:{infamy.Container.HTTPD_IMAGE}",
|
||||
"command": "/usr/sbin/httpd -f -v -p 91",
|
||||
"mount": [
|
||||
{
|
||||
"name": "index.html",
|
||||
"content": f"{data}",
|
||||
"target": "/var/www/index.html"
|
||||
}
|
||||
],
|
||||
"command": "/usr/sbin/httpd -f -v",
|
||||
"network": {
|
||||
"interface": [
|
||||
{ "name": "docker0" }
|
||||
],
|
||||
"publish": [ "8080:91" ]
|
||||
"interface": [{
|
||||
"name": "docker0",
|
||||
"option": [
|
||||
"interface_name=wan",
|
||||
"ip=172.17.0.2"
|
||||
],
|
||||
}],
|
||||
"publish": ["8080:80"]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
})
|
||||
}]
|
||||
}
|
||||
}})
|
||||
|
||||
with test.step("Verify container 'web-docker0' has started"):
|
||||
with test.step("Verify container has started"):
|
||||
c = infamy.Container(target)
|
||||
until(lambda: c.running(NAME), attempts=10)
|
||||
|
||||
@@ -95,9 +89,11 @@ with infamy.Test() as test:
|
||||
|
||||
with infamy.IsolatedMacVlan(hport) as ns:
|
||||
ns.addip(OURIP)
|
||||
with test.step("Verify basic DUT connectivity, host:data can ping DUT 10.0.0.2"):
|
||||
|
||||
with test.step("Verify DUT connectivity, host can reach 10.0.0.2"):
|
||||
ns.must_reach(DUTIP)
|
||||
with test.step("Verify container 'web-docker0' is reachable on http://10.0.0.2:8080"):
|
||||
until(lambda: url.nscheck(ns, "Kilroy was here"), attempts=10)
|
||||
|
||||
with test.step("Verify container is reachable on http://10.0.0.2:8080"):
|
||||
until(lambda: url.nscheck(ns, MESG), attempts=10)
|
||||
|
||||
test.succeed()
|
||||
|
||||
@@ -17,10 +17,12 @@ endif::testgroup[]
|
||||
endif::topdoc[]
|
||||
==== Test sequence
|
||||
. Set up topology and attach to target DUT
|
||||
. Create container 'web-phys' from bundled OCI image
|
||||
. Verify container 'web-phys' has started
|
||||
. Create httpd container from bundled OCI image
|
||||
. Verify container has started
|
||||
. Verify host:data can ping 10.0.0.2
|
||||
. Verify container 'web-phys' is reachable on http://10.0.0.2:91
|
||||
. Verify container is reachable on http://10.0.0.2:91
|
||||
. Add a content mount, overriding index.html
|
||||
. Verify server is restarted and returns new content
|
||||
|
||||
|
||||
<<<
|
||||
|
||||
@@ -11,12 +11,14 @@ given a physical interface instead of an end of a VETH pair.
|
||||
"""
|
||||
import base64
|
||||
import infamy
|
||||
from infamy.util import until
|
||||
from infamy.util import until, to_binary
|
||||
|
||||
with infamy.Test() as test:
|
||||
NAME = "web-phys"
|
||||
DUTIP = "10.0.0.2"
|
||||
OURIP = "10.0.0.1"
|
||||
MESG = "Kilroy was here"
|
||||
BODY = f"<html><body><p>{MESG}</p></body></html>"
|
||||
URL = f"http://{DUTIP}:91/index.html"
|
||||
|
||||
with test.step("Set up topology and attach to target DUT"):
|
||||
@@ -26,43 +28,39 @@ with infamy.Test() as test:
|
||||
if not target.has_model("infix-containers"):
|
||||
test.skip()
|
||||
|
||||
with test.step("Create container 'web-phys' from bundled OCI image"):
|
||||
with test.step("Create httpd container from bundled OCI image"):
|
||||
_, 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
|
||||
}]
|
||||
},
|
||||
"container-network": {}
|
||||
}
|
||||
]
|
||||
"interface": [{
|
||||
"name": f"{ifname}",
|
||||
"ipv4": {
|
||||
"address": [{
|
||||
"ip": f"{DUTIP}",
|
||||
"prefix-length": 24
|
||||
}]
|
||||
},
|
||||
"container-network": {}
|
||||
}]
|
||||
}
|
||||
})
|
||||
target.put_config_dict("infix-containers", {
|
||||
"containers": {
|
||||
"container": [
|
||||
{
|
||||
"name": f"{NAME}",
|
||||
"image": f"oci-archive:{infamy.Container.HTTPD_IMAGE}",
|
||||
"command": "/usr/sbin/httpd -f -v -p 91",
|
||||
"network": {
|
||||
"interface": [
|
||||
{ "name": f"{ifname}" }
|
||||
]
|
||||
}
|
||||
"container": [{
|
||||
"name": f"{NAME}",
|
||||
"image": f"oci-archive:{infamy.Container.HTTPD_IMAGE}",
|
||||
"command": "/usr/sbin/httpd -f -v -p 91",
|
||||
"network": {
|
||||
"interface": [
|
||||
{"name": f"{ifname}"}
|
||||
]
|
||||
}
|
||||
]
|
||||
}]
|
||||
}
|
||||
})
|
||||
|
||||
with test.step("Verify container 'web-phys' has started"):
|
||||
with test.step("Verify container has started"):
|
||||
c = infamy.Container(target)
|
||||
until(lambda: c.running(NAME), attempts=10)
|
||||
|
||||
@@ -74,7 +72,27 @@ with infamy.Test() as test:
|
||||
with test.step("Verify host:data can ping 10.0.0.2"):
|
||||
ns.must_reach(DUTIP)
|
||||
|
||||
with test.step("Verify container 'web-phys' is reachable on http://10.0.0.2:91"):
|
||||
with test.step("Verify container is reachable on http://10.0.0.2:91"):
|
||||
until(lambda: url.nscheck(ns, "It works"), attempts=10)
|
||||
|
||||
with test.step("Add a content mount, overriding index.html"):
|
||||
# Verify modifying a running container takes, issue #930
|
||||
data = to_binary(BODY)
|
||||
|
||||
target.put_config_dict("infix-containers", {
|
||||
"containers": {
|
||||
"container": [{
|
||||
"name": f"{NAME}",
|
||||
"mount": [{
|
||||
"name": "index.html",
|
||||
"content": f"{data}",
|
||||
"target": "/var/www/index.html"
|
||||
}]
|
||||
}]
|
||||
}
|
||||
})
|
||||
|
||||
with test.step("Verify server is restarted and returns new content"):
|
||||
until(lambda: url.nscheck(ns, MESG), attempts=10)
|
||||
|
||||
test.succeed()
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
container_volume.adoc
|
||||
@@ -0,0 +1,30 @@
|
||||
=== Container Volume Persistence
|
||||
==== Description
|
||||
Verify that a container created from a local OCI archive, with a volume
|
||||
for persistent content, can be upgraded at runtime, without losing the
|
||||
content in the volume on restart.
|
||||
|
||||
==== Topology
|
||||
ifdef::topdoc[]
|
||||
image::{topdoc}../../test/case/infix_containers/container_volume/topology.svg[Container Volume Persistence topology]
|
||||
endif::topdoc[]
|
||||
ifndef::topdoc[]
|
||||
ifdef::testgroup[]
|
||||
image::container_volume/topology.svg[Container Volume Persistence topology]
|
||||
endif::testgroup[]
|
||||
ifndef::testgroup[]
|
||||
image::topology.svg[Container Volume Persistence topology]
|
||||
endif::testgroup[]
|
||||
endif::topdoc[]
|
||||
==== Test sequence
|
||||
. Set up topology and attach to target DUT
|
||||
. Create container with volume from bundled OCI image
|
||||
. Verify container has started
|
||||
. Modify container volume content
|
||||
. Verify container volume content
|
||||
. Upgrade container
|
||||
. Verify container volume content survived upgrade
|
||||
|
||||
|
||||
<<<
|
||||
|
||||
+71
@@ -0,0 +1,71 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Container Volume Persistence
|
||||
|
||||
Verify that a container created from a local OCI archive, with a volume
|
||||
for persistent content, can be upgraded at runtime, without losing the
|
||||
content in the volume on restart.
|
||||
|
||||
"""
|
||||
import infamy
|
||||
from infamy.util import until
|
||||
|
||||
with infamy.Test() as test:
|
||||
NAME = "web-volume"
|
||||
PORT = 8080
|
||||
MESG = "HEJ"
|
||||
|
||||
with test.step("Set up topology and attach to target DUT"):
|
||||
env = infamy.Env()
|
||||
target = env.attach("target", "mgmt")
|
||||
tgtssh = env.attach("target", "mgmt", "ssh")
|
||||
addr = target.get_mgmt_ip()
|
||||
url = infamy.Furl(f"http://[{addr}]:{PORT}/index.html")
|
||||
|
||||
if not target.has_model("infix-containers"):
|
||||
test.skip()
|
||||
|
||||
with test.step("Create container with volume from bundled OCI image"):
|
||||
target.put_config_dict("infix-containers", {
|
||||
"containers": {
|
||||
"container": [
|
||||
{
|
||||
"name": f"{NAME}",
|
||||
"image": f"oci-archive:{infamy.Container.HTTPD_IMAGE}",
|
||||
"command": f"/usr/sbin/httpd -f -v -p {PORT}",
|
||||
"network": {
|
||||
"host": True
|
||||
},
|
||||
"volume": [{
|
||||
"name": "www",
|
||||
"target": "/var/www"
|
||||
}]
|
||||
}
|
||||
]
|
||||
}
|
||||
})
|
||||
|
||||
with test.step("Verify container has started"):
|
||||
c = infamy.Container(target)
|
||||
until(lambda: c.running(NAME), attempts=10)
|
||||
|
||||
with test.step("Modify container volume content"):
|
||||
cmd = f"sudo container shell {NAME} 'echo {MESG} >/var/www/index.html'"
|
||||
tgtssh.runsh(cmd)
|
||||
|
||||
with test.step("Verify container volume content"):
|
||||
until(lambda: url.check(MESG), attempts=10)
|
||||
|
||||
with test.step("Upgrade container"):
|
||||
out = tgtssh.runsh(f"sudo container upgrade {NAME}")
|
||||
if ">> Done." not in out.stdout:
|
||||
msg = f"Failed upgrading container {NAME}:\n" \
|
||||
f"STDOUT:\n{out.stdout}\n" \
|
||||
f"STDERR:\n{out.stderr}"
|
||||
test.fail(msg)
|
||||
# else:
|
||||
# print(f"Container {NAME} upgraded: {out.stdout}")
|
||||
|
||||
with test.step("Verify container volume content survived upgrade"):
|
||||
until(lambda: url.check(MESG), attempts=10)
|
||||
|
||||
test.succeed()
|
||||
@@ -0,0 +1 @@
|
||||
../../../infamy/topologies/1x2.dot
|
||||
@@ -0,0 +1,42 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
|
||||
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
|
||||
<!-- Title: 1x2 Pages: 1 -->
|
||||
<svg width="424pt" height="55pt"
|
||||
viewBox="0.00 0.00 424.03 55.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 51)">
|
||||
<title>1x2</title>
|
||||
<polygon fill="white" stroke="transparent" points="-4,4 -4,-51 420.03,-51 420.03,4 -4,4"/>
|
||||
<!-- host -->
|
||||
<g id="node1" class="node">
|
||||
<title>host</title>
|
||||
<polygon fill="none" stroke="black" points="0,-0.5 0,-46.5 100,-46.5 100,-0.5 0,-0.5"/>
|
||||
<text text-anchor="middle" x="25" y="-19.8" font-family="DejaVu Sans Mono, Book" font-size="14.00">host</text>
|
||||
<polyline fill="none" stroke="black" points="50,-0.5 50,-46.5 "/>
|
||||
<text text-anchor="middle" x="75" y="-31.3" font-family="DejaVu Sans Mono, Book" font-size="14.00">mgmt</text>
|
||||
<polyline fill="none" stroke="black" points="50,-23.5 100,-23.5 "/>
|
||||
<text text-anchor="middle" x="75" y="-8.3" font-family="DejaVu Sans Mono, Book" font-size="14.00">data</text>
|
||||
</g>
|
||||
<!-- target -->
|
||||
<g id="node2" class="node">
|
||||
<title>target</title>
|
||||
<polygon fill="none" stroke="black" points="300.03,-0.5 300.03,-46.5 416.03,-46.5 416.03,-0.5 300.03,-0.5"/>
|
||||
<text text-anchor="middle" x="325.03" y="-31.3" font-family="DejaVu Sans Mono, Book" font-size="14.00">mgmt</text>
|
||||
<polyline fill="none" stroke="black" points="300.03,-23.5 350.03,-23.5 "/>
|
||||
<text text-anchor="middle" x="325.03" y="-8.3" font-family="DejaVu Sans Mono, Book" font-size="14.00">data</text>
|
||||
<polyline fill="none" stroke="black" points="350.03,-0.5 350.03,-46.5 "/>
|
||||
<text text-anchor="middle" x="383.03" y="-19.8" font-family="DejaVu Sans Mono, Book" font-size="14.00">target</text>
|
||||
</g>
|
||||
<!-- host--target -->
|
||||
<g id="edge1" class="edge">
|
||||
<title>host:mgmt--target:mgmt</title>
|
||||
<path fill="none" stroke="lightgrey" stroke-width="2" d="M100,-35.5C100,-35.5 300.03,-35.5 300.03,-35.5"/>
|
||||
</g>
|
||||
<!-- host--target -->
|
||||
<g id="edge2" class="edge">
|
||||
<title>host:data--target:data</title>
|
||||
<path fill="none" stroke="black" stroke-width="2" d="M100,-11.5C100,-11.5 300.03,-11.5 300.03,-11.5"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.2 KiB |
@@ -12,6 +12,9 @@
|
||||
- name: container_veth
|
||||
case: container_veth/test.py
|
||||
|
||||
- name: container_volume
|
||||
case: container_volume/test.py
|
||||
|
||||
- name: container_firewall_basic
|
||||
case: container_firewall_basic/test.py
|
||||
|
||||
|
||||
@@ -572,6 +572,7 @@ def config_abr(target, data, link1, link2, link3):
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
with infamy.Test() as test:
|
||||
with test.step("Set up topology and attach to target DUTs"):
|
||||
env = infamy.Env()
|
||||
@@ -585,6 +586,7 @@ with infamy.Test() as test:
|
||||
test.skip()
|
||||
if not R3.has_model("infix-containers"):
|
||||
test.skip()
|
||||
|
||||
with test.step("Configure DUTs"):
|
||||
_, R1ring1 = env.ltop.xlate("R1", "ring1")
|
||||
_, R1ring2 = env.ltop.xlate("R1", "ring2")
|
||||
@@ -607,7 +609,7 @@ with infamy.Test() as test:
|
||||
|
||||
_, R1data = env.ltop.xlate("R1", "data")
|
||||
_, R3data = env.ltop.xlate("R3", "data")
|
||||
_, ABRdata = env.ltop.xlate("ABR", "data")
|
||||
_, ABRdata = env.ltop.xlate("ABR", "data")
|
||||
_, ABRlink1 = env.ltop.xlate("ABR", "link1")
|
||||
_, ABRlink2 = env.ltop.xlate("ABR", "link2")
|
||||
_, ABRlink3 = env.ltop.xlate("ABR", "link3")
|
||||
@@ -617,13 +619,15 @@ with infamy.Test() as test:
|
||||
lambda: config_generic(R3, 3, R3ring1, R3ring2, R3link),
|
||||
lambda: config_abr(ABR, ABRdata, ABRlink1, ABRlink2, ABRlink3))
|
||||
|
||||
with infamy.IsolatedMacVlans({hostR1ring1: "iface1", hostR2ring2: "iface2"}) as sw1,\
|
||||
with infamy.IsolatedMacVlans({hostR1ring1: "iface1", hostR2ring2: "iface2"}) as sw1, \
|
||||
infamy.IsolatedMacVlans({hostR2ring1: "iface1", hostR3ring2: "iface2"}) as sw2, \
|
||||
infamy.IsolatedMacVlans({hostR3ring1: "iface1", hostR1ring2: "iface2"}) as sw3:
|
||||
create_vlan_bridge(sw1)
|
||||
create_vlan_bridge(sw2)
|
||||
create_vlan_bridge(sw3)
|
||||
#breakpoint()
|
||||
|
||||
util.parallel(lambda: create_vlan_bridge(sw1),
|
||||
lambda: create_vlan_bridge(sw2),
|
||||
lambda: create_vlan_bridge(sw3))
|
||||
# breakpoint()
|
||||
|
||||
_, hport0 = env.ltop.xlate("host", "data4")
|
||||
|
||||
with test.step("Wait for all routers to peer"):
|
||||
@@ -641,14 +645,14 @@ with infamy.Test() as test:
|
||||
with infamy.IsolatedMacVlan(hport0) as ns:
|
||||
ns.addip("192.168.100.2")
|
||||
ns.addroute("0.0.0.0/0", "192.168.100.1")
|
||||
#breakpoint()
|
||||
# breakpoint()
|
||||
with test.step("Verify ABR:data can access container A on R1 (10.1.1.101)"):
|
||||
furl=Furl("http://10.1.1.101:8080")
|
||||
furl = Furl("http://10.1.1.101:8080")
|
||||
util.until(lambda: furl.nscheck(ns, BODY))
|
||||
with test.step("Verify ABR:data can access container A on R2 (10.1.2.101)"):
|
||||
furl=Furl("http://10.1.2.101:8080")
|
||||
furl = Furl("http://10.1.2.101:8080")
|
||||
util.until(lambda: furl.nscheck(ns, BODY))
|
||||
with test.step("Verify ABR:data can access container A on R3 (10.1.3.101)"):
|
||||
furl=Furl("http://10.1.3.101:8080")
|
||||
furl = Furl("http://10.1.3.101:8080")
|
||||
util.until(lambda: furl.nscheck(ns, BODY))
|
||||
test.succeed()
|
||||
|
||||
@@ -5,8 +5,8 @@ from infamy.util import warn
|
||||
|
||||
class Container:
|
||||
"""Helper methods"""
|
||||
HTTPD_IMAGE = "curios-httpd-latest.tar.gz"
|
||||
NFTABLES_IMAGE = "curios-nftables-latest.tar.gz"
|
||||
HTTPD_IMAGE = "/lib/oci/curios-httpd-latest.tar.gz"
|
||||
NFTABLES_IMAGE = "/lib/oci/curios-nftables-latest.tar.gz"
|
||||
|
||||
def __init__(self, target):
|
||||
self.system = target
|
||||
|
||||
@@ -11,37 +11,37 @@ graph "ring-4-duts" {
|
||||
host [
|
||||
label=" { host | { <mgmt4> mgmt4 | <data4> data4 | <mgmt1> mgmt1 | <data1> data1 | <data2> data2 | <mgmt2> mgmt2 | <data3> data3 | <mgmt3> mgmt3 } }",
|
||||
pos="150,200!"
|
||||
kind="controller",
|
||||
requires="controller",
|
||||
];
|
||||
|
||||
R1 [
|
||||
label="{ { <mgmt> mgmt | <data> data } | \n R1 \n 10.0.0.1/32 \n(lo) } | { <ring1> ring1 | <cross> cross | <ring2> ring2 }",
|
||||
pos="50,150!",
|
||||
kind="infix",
|
||||
requires="infix",
|
||||
];
|
||||
|
||||
R4 [
|
||||
label="{ { <mgmt> mgmt | <data> data } | \n R4 \n 10.0.0.4/32 \n(lo) } | { <ring1> ring1 | <cross> cross | <ring2> ring2 }",
|
||||
pos="50,50!"
|
||||
kind="infix",
|
||||
requires="infix",
|
||||
];
|
||||
|
||||
R2 [
|
||||
label="{ <ring2> ring2 | <cross> cross | <ring1> ring1 } | { { <data> data | <mgmt> mgmt } | \n R2 \n10.0.0.2/32 \n(lo) }",
|
||||
pos="250,150!"
|
||||
kind="infix",
|
||||
requires="infix",
|
||||
];
|
||||
|
||||
R3 [
|
||||
label="{ <ring2> ring2 | <cross> cross | <ring1> ring1 } | { { <data> data | <mgmt> mgmt } | \n R3 \n10.0.0.3/32 \n(lo) }",
|
||||
pos="250,50!"
|
||||
kind="infix",
|
||||
requires="infix",
|
||||
];
|
||||
|
||||
host:mgmt1 -- R1:mgmt [kind=mgmt, color="lightgray"]
|
||||
host:mgmt2 -- R2:mgmt [kind=mgmt, color="lightgray"]
|
||||
host:mgmt3 -- R3:mgmt [kind=mgmt, color="lightgray"]
|
||||
host:mgmt4 -- R4:mgmt [kind=mgmt, color="lightgray"]
|
||||
host:mgmt1 -- R1:mgmt [requires="mgmt", color="lightgray"]
|
||||
host:mgmt2 -- R2:mgmt [requires="mgmt", color="lightgray"]
|
||||
host:mgmt3 -- R3:mgmt [requires="mgmt", color="lightgray"]
|
||||
host:mgmt4 -- R4:mgmt [requires="mgmt", color="lightgray"]
|
||||
|
||||
// host-Dut links
|
||||
host:data1 -- R1:data [color="darkgreen"]
|
||||
|
||||
Reference in New Issue
Block a user