summary refs log tree commit diff
path: root/tools/nm-in-container
diff options
context:
space:
mode:
authorMichael Biebl <biebl@debian.org>2024-05-05 00:07:30 +0200
committerMichael Biebl <biebl@debian.org>2024-05-05 00:07:30 +0200
commit34bb501be08aa2b313d88e67d6e0a7e0a3f9cfa6 (patch)
tree4e6220877828be4c6f261de09ec0cb2d80e32389 /tools/nm-in-container
parentbba2e4b4de668db525cbfdfc35292e5a0b51671a (diff)
New upstream version 1.47.90 upstream/1.47.90
Diffstat (limited to 'tools/nm-in-container')
-rwxr-xr-xtools/nm-in-container520
1 files changed, 520 insertions, 0 deletions
diff --git a/tools/nm-in-container b/tools/nm-in-container
new file mode 100755
index 00000000..7ff073d0
--- /dev/null
+++ b/tools/nm-in-container
@@ -0,0 +1,520 @@
+#!/bin/bash
+
+set -e
+
+###############################################################################
+# Script to create a podman container for testing NetworkManager.
+#
+# Commands:
+#  - build: build a new image, named "$CONTAINER_NAME_REPOSITORY:$CONTAINER_NAME_TAG" ("nm:nm").
+#  - run: start the container and tag it "$CONTAINER_NAME_NAME" ("nm").
+#  - exec: run bash inside the container (this is the default).
+#  - journal|j: print the journal from inside the container.
+#  - stop: stop the container.
+#  - reset: stop and delete the container.
+#  - clean: stop and delete the container and the image.
+#
+# Options:
+#  --no-cleanup: don't delete the CONTAINERFILE and other artifacts
+#  --stop: only has effect with "run". It will stop the container afterwards.
+#  -- [EXTRA_ARGS]:
+#    - with command "exec", provide a command and arguments to run in the container.
+#      Defaults to "bash".
+#    - with command "journal", additional arguments that are passed to journalctl.
+#
+# It bind mounts the current working directory inside the container.
+# You can run `make install` and run tests.
+# There is a script nm-env-prepare.sh to generate a net1 interface for testing.
+#
+# This will bind-mount the NetworkManager working tree inside the container (and symlink
+# from /NetworkManager). Create a file ".git/nm-in-container-host" to bind mount the host's
+# "/" to "/Host".
+#
+# Create a symlink ./.git/NetworkManager-ci, to also bind-mount the CI directory.
+# Create additional symlinks ./.git/nm-guest-link-*, to bind mount additional
+# directories.
+#
+# Currently NM-ci requires a working eth1.
+# Hence call `nm-env-prepare.sh --prefix eth -i 1 && sleep 1 && nmcli device connect eth1` before
+# running a CI test.
+###############################################################################
+
+if [ -z "$BASE_IMAGE" ]; then
+    if grep -q "^ID=fedora$" /etc/os-release 2>/dev/null ; then
+        BASE_IMAGE="$(sed -n 's/^VERSION_ID=\([0-9]\+\)$/fedora:\1/p' /etc/os-release)"
+    fi
+fi
+if [ -z "$BASE_IMAGE" ]; then
+    BASE_IMAGE=fedora:latest
+fi
+
+BASEDIR_NM="$(readlink -f "$(dirname "$(readlink -f "$0")")/..")"
+BASEDIR_DATA="$BASEDIR_NM/tools/nm-guest-data"
+
+SYMLINK_NAME=()
+SYMLINK_TARGET=()
+for d in $(ls -1d "$BASEDIR_NM/.git/NetworkManager-ci" "$BASEDIR_NM/.git/nm-guest-link-"* 2>/dev/null) ; do
+    NAME="${d##*/}"
+    NAME="${NAME##nm-guest-link-}"
+    TARGET="$(readlink -f "$d")"
+    test -e "$TARGET"
+    SYMLINK_NAME+=("$NAME")
+    SYMLINK_TARGET+=("$TARGET")
+done
+
+CONTAINER_NAME_REPOSITORY=${CONTAINER_NAME_REPOSITORY:-nm}
+CONTAINER_NAME_TAG=${CONTAINER_NAME_TAG:-nm}
+CONTAINER_NAME_NAME=${CONTAINER_NAME_NAME:-nm}
+
+EXEC_ENV=()
+
+###############################################################################
+
+usage() {
+    cat <<EOF
+$0: build|run|exec|stop|reset|reexec|clean|journal [--no-cleanup] [--stop] [-- EXTRA_ARGS]
+EOF
+    echo
+    awk '/^####*$/{ if(on) exit; on=1} { if (on) { if (on==2) print(substr($0,3)); on=2; } }' "$BASH_SOURCE"
+    echo
+}
+
+###############################################################################
+
+die() {
+    (
+        echo -n -e "\033[31m"
+        printf "%s" "$*"
+        echo -e "\033[0m"
+    ) >&2
+    exit 1
+}
+
+###############################################################################
+
+CLEANUP_FILES=()
+DO_CLEANUP=1
+cleanup() {
+    test "$DO_CLEANUP" = 1 || return 0
+    for f in "${CLEANUP_FILES[@]}" ; do
+        rm -rf "$f"
+    done
+}
+
+trap cleanup EXIT
+
+###############################################################################
+
+tmp_file() {
+    cat > "$1"
+    CLEANUP_FILES+=( "$1" )
+    test -z "$2" || chmod "$2" "$1"
+}
+
+gen_file() {
+    local PERMS
+    [[ $1 =~ bin-* ]] && PERMS=755 || PERMS=644
+    sed "s|{{BASEDIR_NM}}|$BASEDIR_NM|g" "$BASEDIR_DATA/$1.in" \
+        | tmp_file "$BASEDIR_DATA/data-$1" $PERMS
+}
+
+bind_files() {
+    VARIABLE_NAME="$1"
+
+    ARR=()
+    H=~
+
+    ARR+=( -v "$BASEDIR_NM:$BASEDIR_NM" )
+
+    if [ -e "$BASEDIR_NM/.git/nm-in-container-host" ] ; then
+        ARR+=( -v /:/Host )
+    fi
+
+    for i in $(seq 1 ${#SYMLINK_TARGET[@]}) ; do
+        j=$((i - 1))
+        ARR+=( -v "${SYMLINK_TARGET[$j]}:${SYMLINK_TARGET[$j]}" )
+    done
+
+    for f in ~/.gitconfig* ~/.vim* ; do
+        test -e "$f" || continue
+        f2="${f#$H/}"
+        [[ "$f2" = .viminf* ]] && continue
+        [[ "$f2" = *.tmp ]] && continue
+        [[ "$f2" = *~ ]] && continue
+        f2="/root/$f2"
+        ARR+=( -v "$f:$f2" )
+    done
+
+    eval "$VARIABLE_NAME=( \"\${ARR[@]}\" )"
+}
+
+create_dockerfile() {
+
+    local CONTAINERFILE="$1"
+    local BASE_IMAGE="$2"
+    local GEN_FILES="bin-nm-env-prepare.sh bin-nm-deploy.sh bin-_nm-in-container-setup.sh
+        etc-rc.local etc-motd-container etc-bashrc.my etc-bashrc-motd.my nm-90-my.conf
+        nm-95-user.conf home-bash_history home-gdbinit home-gdb_history home-behaverc
+        systemd-20-nm.override"
+
+    cp "$BASEDIR_NM/contrib/scripts/NM-log" "$BASEDIR_DATA/data-bin-NM-log"
+    CLEANUP_FILES+=( "$BASEDIR_DATA/data-NM-log" )
+
+    for f in $GEN_FILES; do
+        gen_file "$f"
+    done
+    chmod 755 "$BASEDIR_DATA/data-etc-rc.local"
+
+    RUN_LN_BASEDIR_NM=
+    if [ -n "$BASEDIR_NM" -a "$BASEDIR_NM" != "/NetworkManager" ] ; then
+        RUN_LN_BASEDIR_NM="RUN ln -snf \"$BASEDIR_NM\" /NetworkManager"
+    fi
+
+    RUN_LN_SYMLINK_CMDS=""
+    for i in $(seq 1 ${#SYMLINK_NAME[@]}) ; do
+        j=$((i - 1))
+        if [ -d "${SYMLINK_TARGET[$j]}" ] ; then
+            RUN_LN_SYMLINK_CMDS="$RUN_LN_SYMLINK_CMDS"$'\n'"RUN ln -snf \"${SYMLINK_TARGET[$j]}\" \"/${SYMLINK_NAME[$j]}\""
+        fi
+    done
+
+    cat <<EOF | tmp_file "$CONTAINERFILE"
+FROM $BASE_IMAGE
+
+ENTRYPOINT ["/sbin/init"]
+
+RUN sed -i 's/^tsflags=.*/tsflags=/' /etc/dnf/dnf.conf
+
+RUN dnf install -y \\
+    --skip-broken \\
+    \\
+    /usr/bin/python \\
+    /usr/sbin/ausearch \\
+    ModemManager-devel \\
+    ModemManager-glib-devel \\
+    NetworkManager \\
+    audit-libs-devel \\
+    bash-completion \\
+    bind-utils \\
+    black \\
+    bluez-libs-devel \\
+    ccache \\
+    clang \\
+    clang-tools-extra \\
+    cscope \\
+    dbus-devel \\
+    dbus-x11 \\
+    dhclient \\
+    dnsmasq \\
+    firewalld-filesystem \\
+    gcc-c++ \\
+    gdb \\
+    gettext-devel \\
+    git \\
+    glib2-doc \\
+    glibc-langpack-pl \\
+    gnutls-devel \\
+    gobject-introspection-devel \\
+    gtk-doc \\
+    intltool \\
+    iproute \\
+    iproute-tc \\
+    iptables \\
+    jansson-devel \\
+    libasan \\
+    libcurl-devel \\
+    libndp-devel \\
+    libpsl-devel \\
+    libselinux-devel \\
+    libtool \\
+    libubsan \\
+    libuuid-devel \\
+    make \\
+    meson \\
+    mlocate \\
+    mobile-broadband-provider-info-devel \\
+    newt-devel \\
+    nispor \\
+    nmstate \\
+    nss-devel \\
+    polkit-devel \\
+    ppp \\
+    ppp-devel \\
+    procps \\
+    python3-behave \\
+    python3-black \\
+    python3-dbus \\
+    python3-devel \\
+    python3-gobject \\
+    python3-pexpect \\
+    python3-pip \\
+    python3-pyte \\
+    python3-pyyaml \\
+    qt-devel \\
+    radvd \\
+    readline-devel \\
+    rp-pppoe \\
+    rpm-build \\
+    strace \\
+    systemd \\
+    systemd-devel \\
+    tcpdump \\
+    teamd-devel \\
+    vala \\
+    vala-devel \\
+    valgrind \\
+    vim \\
+    which \\
+    \\
+    'dbus*' \\
+    'openvswitch2*' \\
+    /usr/bin/pytest \\
+    /usr/bin/debuginfo-install \\
+    NetworkManager-openvpn \\
+    NetworkManager-pptp \\
+    NetworkManager-strongswan \\
+    NetworkManager-vpnc \\
+    NetworkManager-ovs \\
+    NetworkManager-wifi \\
+    NetworkManager-team \\
+    NetworkManager-ppp \\
+    cryptsetup \\
+    dhcp-client \\
+    dhcp-relay \\
+    dhcp-server \\
+    dnsmasq \\
+    dracut-network \\
+    ethtool \\
+    firewalld \\
+    gcc \\
+    git \\
+    hostapd \\
+    iproute-tc \\
+    ipsec-tools \\
+    iputils \\
+    iscsi-initiator-utils \\
+    iw \\
+    ldns \\
+    libreswan \\
+    libselinux-utils \\
+    libyaml-devel \\
+    logrotate \\
+    lvm2 \\
+    mdadm \\
+    net-tools \\
+    nfs-utils \\
+    nmap-ncat \\
+    nss-tools \\
+    openvpn \\
+    perl-IO-Pty-Easy \\
+    perl-IO-Tty \\
+    psmisc \\
+    python3-dbus \\
+    python3-gobject \\
+    python3-netaddr \\
+    qemu-kvm \\
+    radvd \\
+    rp-pppoe \\
+    scsi-target-utils \\
+    tcpdump \\
+    tcpreplay \\
+    tuned \\
+    wireguard-tools \\
+    wireshark-cli
+
+RUN dnf debuginfo-install --skip-broken NetworkManager \$(ldd /usr/sbin/NetworkManager | sed -n 's/.* => \\(.*\\) (0x[0-9A-Fa-f]*)$/\1/p' | xargs -n1 readlink -f) -y
+
+RUN dnf clean all
+
+RUN pip3 install --user behave_html_formatter || true
+
+RUN mkdir -p /etc/systemd/system/NetworkManager.service.d
+
+COPY data-bin-NM-log "/usr/bin/NM-log"
+COPY data-bin-nm-env-prepare.sh "/usr/bin/nm-env-prepare.sh"
+COPY data-bin-nm-deploy.sh "/usr/bin/nm-deploy.sh"
+COPY data-bin-_nm-in-container-setup.sh "/usr/bin/_nm-in-container-setup.sh"
+COPY data-etc-rc.local "/etc/rc.d/rc.local"
+COPY data-etc-motd-container /etc/motd
+COPY data-etc-bashrc.my /etc/bashrc.my
+COPY data-etc-bashrc-motd.my /etc/bashrc-motd.my
+COPY data-nm-90-my.conf /etc/NetworkManager/conf.d/90-my.conf
+COPY data-nm-95-user.conf /etc/NetworkManager/conf.d/95-user.conf
+COPY data-home-bash_history /root/.bash_history
+COPY data-home-gdbinit /root/.gdbinit
+COPY data-home-gdb_history /root/.gdb_history
+COPY data-home-behaverc /root/.behaverc
+COPY data-systemd-20-nm.override /etc/systemd/system/NetworkManager.service.d/20-nm.override
+
+RUN systemctl enable NetworkManager
+
+# Generate a stable machine id.
+RUN echo "10001000100010001000100010001000" > /etc/machine-id
+
+RUN echo -e "# Default from the container image\nnameserver 8.8.8.8" > /etc/resolv.conf
+
+# Generate a fixed (version 1) secret key.
+RUN mkdir -p /var/lib/NetworkManager
+RUN chmod 700 /var/lib/NetworkManager
+RUN echo -n "nm-in-container-secret-key" > /var/lib/NetworkManager/secret_key
+RUN chmod 600 /var/lib/NetworkManager/secret_key
+
+RUN sed 's/.*RateLimitBurst=.*/RateLimitBurst=0/' /etc/systemd/journald.conf -i
+
+$RUN_LN_BASEDIR_NM
+$RUN_LN_SYMLINK_CMDS
+
+RUN rm -rf /etc/NetworkManager/system-connections/*
+
+RUN echo -e '\n. /etc/bashrc.my\n' >> /etc/bashrc
+RUN echo -e '\n. /etc/bashrc-motd.my\n' >> /etc/bashrc
+
+RUN updatedb
+EOF
+}
+
+###############################################################################
+
+container_image_exists() {
+    podman image exists "$1" || return 1
+}
+
+container_exists() {
+    podman container exists "$1" || return 1
+}
+
+container_is_running() {
+    test "$(podman ps --format "{{.ID}} {{.Names}}" | sed -n "s/ $1\$/\0/p")" != "" || return 1
+}
+
+###############################################################################
+
+do_reset() {
+    podman stop "$CONTAINER_NAME_NAME" || :
+    podman rm "$CONTAINER_NAME_NAME" || :
+}
+
+do_clean() {
+    do_reset
+    podman rmi "$CONTAINER_NAME_REPOSITORY:$CONTAINER_NAME_TAG" || :
+}
+
+do_build() {
+    container_image_exists "$CONTAINER_NAME_REPOSITORY:$CONTAINER_NAME_TAG" && return 0
+
+    CONTAINERFILE="$BASEDIR_DATA/containerfile"
+    create_dockerfile "$CONTAINERFILE" "$BASE_IMAGE"
+    podman build --squash-all --tag "$CONTAINER_NAME_REPOSITORY:$CONTAINER_NAME_TAG" -f "$CONTAINERFILE"
+}
+
+do_run() {
+    do_build
+
+    if container_is_running "$CONTAINER_NAME_NAME" ; then
+        return 0
+    fi
+
+    if container_exists "$CONTAINER_NAME_NAME" ; then
+        podman start "$CONTAINER_NAME_NAME"
+        return 0
+    fi
+
+    bind_files BIND_FILES
+
+    podman run --privileged \
+        --name "$CONTAINER_NAME_NAME" \
+        --dns=none \
+        --no-hosts \
+        -d \
+        "${BIND_FILES[@]}" \
+        "$CONTAINER_NAME_REPOSITORY:$CONTAINER_NAME_TAG"
+}
+
+do_exec() {
+    do_run
+
+    local e
+    local EXTRA_ARGS=("$@")
+    if [ "${#EXTRA_ARGS[@]}" = 0 ]; then
+        EXTRA_ARGS=('bash')
+    fi
+
+    local ENV=()
+    for e in "${EXEC_ENV[@]}" ; do
+        ENV+=(-e "$e")
+    done
+
+    podman exec "${ENV[@]}" --workdir "$BASEDIR_NM" -it "$CONTAINER_NAME_NAME" "${EXTRA_ARGS[@]}"
+
+    if [ "$DO_STOP" = 1 ]; then
+        do_stop
+    fi
+}
+
+do_reexec() {
+    do_reset
+    do_exec "$@"
+}
+
+do_journal() {
+    EXEC_ENV+=( "SYSTEMD_COLORS=0" )
+    do_exec "journalctl" --no-pager "$@"
+}
+
+do_stop() {
+    container_is_running "$CONTAINER_NAME_NAME" || return 0
+    podman stop "$CONTAINER_NAME_NAME"
+}
+
+###############################################################################
+
+DO_STOP=0
+CMD=exec
+EXTRA_ARGS=()
+for (( i=1 ; i<="$#" ; )) ; do
+    c="${@:$i:1}"
+    i=$((i+1))
+    case "$c" in
+        --no-cleanup)
+            DO_CLEANUP=0
+            ;;
+        --stop)
+            DO_STOP=1
+            ;;
+        j)
+            CMD=journal
+            ;;
+        build|run|exec|stop|reset|reexec|clean|journal)
+            CMD=$c
+            ;;
+        --)
+            EXTRA_ARGS=( "${@:$i}" )
+            break
+            ;;
+        -h|--help)
+            usage
+            exit 0
+            ;;
+        *)
+            if [ "$CMD" = "journal" ]; then
+                EXTRA_ARGS=( "${@:$((i-1))}" )
+                break;
+            else
+                usage
+                die "invalid argument: $c"
+            fi
+            ;;
+    esac
+done
+
+###############################################################################
+
+test "$UID" != 0 || die "cannot run as root"
+
+if test "$CMD" != exec -a "$CMD" != journal -a "$CMD" != reexec -a "${#EXTRA_ARGS[@]}" != 0 ; then
+    die "Extra arguments are only allowed with exec|journal|reexec command"
+fi
+
+###############################################################################
+
+do_$CMD "${EXTRA_ARGS[@]}"