Skip to content
Merged
Changes from 52 commits
Commits
Show all changes
55 commits
Select commit Hold shift + click to select a range
daf876e
fix race condition
sdickhoven Feb 15, 2025
e391969
address shell linter complaint
sdickhoven Feb 19, 2025
70fa5ae
undo unrelated changes
sdickhoven Feb 20, 2025
f19925b
simplify initial `inotifywait` trigger logic
sdickhoven May 27, 2025
967e463
remove all but one improbable race condition
sdickhoven May 30, 2025
f35a71c
nit
sdickhoven May 31, 2025
0d24d29
revert non-functional edits
sdickhoven Jun 9, 2025
f26afb3
address shellcheck complaints
sdickhoven Jun 11, 2025
af1282f
Bump k8s.io/client-go from 0.32.0 to 0.32.2 (#476)
dependabot[bot] Feb 17, 2025
d0e67bb
Bump docker/setup-qemu-action from 3.2.0 to 3.4.0 (#471)
dependabot[bot] Feb 17, 2025
3ce9c37
Bump sigstore/cosign-installer from 3.7.0 to 3.8.0 (#470)
dependabot[bot] Feb 17, 2025
ca8381f
Bump golang.org/x/net from 0.30.0 to 0.33.0 (#479)
dependabot[bot] Feb 17, 2025
a9dbdda
Bump golang in /proxy-init/integration/iptables (#472)
dependabot[bot] Feb 17, 2025
28e335e
Bump DavidAnson/markdownlint-cli2-action from 18.0.0 to 19.1.0 (#468)
dependabot[bot] Feb 17, 2025
ee6ec4a
Bump softprops/action-gh-release from 2.2.0 to 2.2.1 (#462)
dependabot[bot] Feb 17, 2025
52daf12
Bump linkerd/dev from 44 to 45 (#459)
dependabot[bot] Feb 17, 2025
555336d
build(deps): bump libc to 0.2.169, ring to 0.17.9, openssl to 0.10.71…
alpeb Feb 17, 2025
6fbc0a9
build(deps): bump github.com/spf13/cobra from 1.8.1 to 1.9.1 (#481)
dependabot[bot] Feb 17, 2025
798aab6
build(deps): bump actions/cache from 4.2.0 to 4.2.1 (#485)
dependabot[bot] Feb 20, 2025
5f984e0
test(deps): use setup-rust in cni-plugin-integration/repair-controlle…
alpeb Feb 20, 2025
a97c205
fix(linkerd-cni): improve SA token rotation detection (#478)
alpeb Feb 20, 2025
f94d0e3
build(deps): bump EmbarkStudios/cargo-deny-action from 2.0.4 to 2.0.5…
dependabot[bot] Feb 25, 2025
59b7fd8
build(deps): bump sigstore/cosign-installer from 3.8.0 to 3.8.1 (#487)
dependabot[bot] Feb 25, 2025
1867a94
build(deps): bump dev to v45 (#483)
alpeb Feb 25, 2025
e61fd73
build(deps): bump alpine from 3.21.0 to 3.21.3 (#482)
dependabot[bot] Feb 25, 2025
82b0910
Bump golang from 1.23-alpine to 1.24-alpine (#473)
dependabot[bot] Feb 25, 2025
fe8a1e4
build(deps): bump EmbarkStudios/cargo-deny-action from 2.0.5 to 2.0.6…
dependabot[bot] Feb 26, 2025
2ec5177
build(deps): bump actions/download-artifact from 4.1.8 to 4.1.9 (#491)
dependabot[bot] Feb 26, 2025
7574345
build(deps): bump docker/setup-qemu-action from 3.4.0 to 3.5.0 (#490)
dependabot[bot] Feb 26, 2025
49cd49a
build(deps): bump docker/setup-qemu-action from 3.5.0 to 3.6.0 (#493)
dependabot[bot] Feb 28, 2025
8de3761
build(deps): bump actions/cache from 4.2.1 to 4.2.2 (#492)
dependabot[bot] Feb 28, 2025
7aa3f83
fix(linkerd-cni): fix cleanup logic (#494)
alpeb Mar 3, 2025
8437fc2
fix(ci): remove EmbarkStudios/cargo-deny-action (#495)
olix0r Mar 3, 2025
c9e3218
build(deps): bump tokio from 1.35.1 to 1.38.2 (#507)
dependabot[bot] Apr 8, 2025
cd9a99c
fix(cni-plugin): append inbound skip ports instead of replacing (#518)
adleong May 17, 2025
ca37702
build(deps): bump actions/download-artifact from 4.1.9 to 4.3.0 (#517)
dependabot[bot] May 26, 2025
6e200be
build(deps): bump sigstore/cosign-installer from 3.8.1 to 3.8.2 (#510)
dependabot[bot] May 26, 2025
9119c32
build(deps): bump ring from 0.17.9 to 0.17.14 (#519)
dependabot[bot] May 26, 2025
d9a7077
build(deps): bump golang.org/x/net from 0.33.0 to 0.38.0 (#508)
dependabot[bot] May 26, 2025
10845da
build(deps): bump softprops/action-gh-release from 2.2.1 to 2.2.2 (#509)
dependabot[bot] May 26, 2025
36f44fd
build(deps): bump github.com/containernetworking/cni from 1.2.3 to 1.…
dependabot[bot] May 26, 2025
a5af4b3
build(deps): bump actions/cache from 4.2.2 to 4.2.3 (#504)
dependabot[bot] May 26, 2025
2615f0b
build(deps): bump docker/login-action from 3.3.0 to 3.4.0 (#501)
dependabot[bot] May 26, 2025
6908957
build(deps): bump DavidAnson/markdownlint-cli2-action (#523)
dependabot[bot] May 26, 2025
00db641
build(deps): bump alpine from 3.21.3 to 3.22.0 (#525)
dependabot[bot] Jun 5, 2025
63ea7dc
deps: have dependabot update composite actions (#524)
alpeb Jun 5, 2025
6c19648
build(deps): bump actions/checkout in /.github/actions/version-mode (…
dependabot[bot] Jun 9, 2025
e5cba87
Merge branch 'main' into BUG-fix-race-condition
sdickhoven Jun 11, 2025
834ed72
make script stylistically more consistent
sdickhoven Jun 11, 2025
0c4bb3a
address shellcheck complaints
sdickhoven Jun 11, 2025
6b3b0f3
Merge branch 'main' into CHORE-fix-inconsistencies
sdickhoven Jun 11, 2025
3017863
break comment lines consistently
sdickhoven Jun 11, 2025
97c421f
more efficient and concise find logic
sdickhoven Jun 11, 2025
218a121
Merge branch 'main' into CHORE-fix-inconsistencies
sdickhoven Jun 12, 2025
e43a87d
use braces for all variables
sdickhoven Jun 18, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
163 changes: 83 additions & 80 deletions cni-plugin/deployment/scripts/install-cni.sh
Original file line number Diff line number Diff line change
Expand Up @@ -20,18 +20,19 @@
# 2) https://github.com/istio/cni/blob/c63a509539b5ed165a6617548c31b686f13c2133/deployments/kubernetes/install/scripts/install-cni.sh

# Script to install Linkerd CNI on a Kubernetes host.
# - Expects the host CNI binary path to be mounted at /host/opt/cni/bin.
# - Expects the host CNI network config path to be mounted at /host/etc/cni/net.d.
# - Expects the desired CNI config in the CNI_NETWORK_CONFIG env variable.
# - Expects the host CNI binary path to be mounted at /host/opt/cni/bin
# - Expects the host CNI network config path to be mounted at /host/etc/cni/net.d
# - Expects the desired CNI config in the CNI_NETWORK_CONFIG env variable

# Ensure all variables are defined, and that the script fails when an error is hit.
set -u -e -o pipefail
# Ensure all variables are defined, and that the script fails when an error is
# hit.
set -u -e -o pipefail +o noclobber

# Helper function for raising errors
# Usage:
# some_command || exit_with_error "some_command_failed: maybe try..."
exit_with_error() {
log "${1}"
log "$1"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

may i ask, why are we opting to remove the surrounding braces throughout this script?

Copy link
Contributor Author

@sdickhoven sdickhoven Jun 11, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes. good question. i'm just going for consistency.

"$foo" == ${foo}

i'm happy going with either but the script is using both. i'm just trying to add consistency.

this is entirely a stylistic nit. 100% non-functional.

the braces are typically used for two reasons:

  1. to do string manipulation - e.g. ${foo:-default_value} or ${foo//a/b}
  2. to remove ambiguity - e.g. $foobar != ${foo}bar

but there's nothing wrong with always using braces.

however, the script currently uses them sometimes (when they are not actually required for any functional reasons) and sometimes not. that's all.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

got it. my personal vote would be to use braces, rather than remove them. as you mention, these are often used to remove ambiguity or to otherwise avoid accidentally expanding a different variable than intended.

i'll cede to @alpeb's opinion on that, however.

Copy link
Contributor Author

@sdickhoven sdickhoven Jun 16, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

happy to update the pr to switch all variables to using braces.

i have no strong preference one way or the other. i just like consistency. that's all.

...but i realize that most people probably couldn't care less about the consistent use of braces here. 🙂

rest assured that it won't hurt my feelings if y'all decide to simply close this pr. 😌

...though i do think that we should (double)quote all variables that are used as command line arguments:

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

my personal vote would be to use braces, rather than remove them.

i have just pushed a commit that flips all variables to using braces.

exit 1
}

Expand All @@ -54,7 +55,7 @@ CONTAINER_CNI_BIN_DIR=${CONTAINER_CNI_BIN_DIR:-/opt/cni/bin}
# Directory path where CNI configuration should live on the host
HOST_CNI_NET="${CONTAINER_MOUNT_PREFIX}${DEST_CNI_NET_DIR}"
# Location of legacy "interface mode" file, to be automatically deleted
DEFAULT_CNI_CONF_PATH="${HOST_CNI_NET}/01-linkerd-cni.conf"
DEFAULT_CNI_CONF_PATH="$HOST_CNI_NET/01-linkerd-cni.conf"
KUBECONFIG_FILE_NAME=${KUBECONFIG_FILE_NAME:-ZZZ-linkerd-cni-kubeconfig}
SERVICEACCOUNT_PATH=/var/run/secrets/kubernetes.io/serviceaccount

Expand All @@ -66,7 +67,8 @@ SERVICEACCOUNT_PATH=/var/run/secrets/kubernetes.io/serviceaccount
# *conflist files, then linkerd-cni configuration parameters will be removed
# from them.
cleanup() {
# First, kill both 'inotifywait' processes so we don't process any DELETE/CREATE events
# First, kill both 'inotifywait' processes so we don't process any
# DELETE/CREATE events.
pids=$(pgrep inotifywait)
if [ -n "$pids" ]; then
while read -r pid; do
Expand All @@ -80,8 +82,8 @@ cleanup() {
# Find all conflist files and print them out using a NULL separator instead of
# writing each file in a new line. We will subsequently read each string and
# attempt to rm linkerd config from it using jq helper.
local cni_data=''
find "${HOST_CNI_NET}" -maxdepth 1 -type f \( -iname '*conflist' \) -print0 |
local cni_data
find "$HOST_CNI_NET" -maxdepth 1 -type f \( -iname '*conflist' \) -print0 |
while read -r -d $'\0' file; do
log "Removing linkerd-cni config from $file"
cni_data=$(jq 'del( .plugins[]? | select( .type == "linkerd-cni" ))' "$file")
Expand All @@ -91,11 +93,11 @@ cleanup() {
done

# Remove binary and kubeconfig file
if [ -e "${HOST_CNI_NET}/${KUBECONFIG_FILE_NAME}" ]; then
log "Removing linkerd-cni kubeconfig: ${HOST_CNI_NET}/${KUBECONFIG_FILE_NAME}"
rm -f "${HOST_CNI_NET}/${KUBECONFIG_FILE_NAME}"
if [ -e "$HOST_CNI_NET/$KUBECONFIG_FILE_NAME" ]; then
log "Removing linkerd-cni kubeconfig: $HOST_CNI_NET/$KUBECONFIG_FILE_NAME"
rm -f "$HOST_CNI_NET/$KUBECONFIG_FILE_NAME"
fi
if [ -e "${CONTAINER_MOUNT_PREFIX}${DEST_CNI_BIN_DIR}"/linkerd-cni ]; then
if [ -e "${CONTAINER_MOUNT_PREFIX}${DEST_CNI_BIN_DIR}/linkerd-cni" ]; then
log "Removing linkerd-cni binary: ${CONTAINER_MOUNT_PREFIX}${DEST_CNI_BIN_DIR}/linkerd-cni"
rm -f "${CONTAINER_MOUNT_PREFIX}${DEST_CNI_BIN_DIR}/linkerd-cni"
fi
Expand All @@ -113,54 +115,54 @@ trap 'log "ERROR caught, exiting..."; cleanup ' ERR
install_cni_bin() {
# Place the new binaries if the mounted directory is writeable.
dir="${CONTAINER_MOUNT_PREFIX}${DEST_CNI_BIN_DIR}"
if [ ! -w "${dir}" ]; then
exit_with_error "${dir} is non-writeable, failure"
if [ ! -w "$dir" ]; then
exit_with_error "$dir is non-writeable, failure"
fi
for path in "${CONTAINER_CNI_BIN_DIR}"/*; do
cp "${path}" "${dir}"/ || exit_with_error "Failed to copy ${path} to ${dir}."
for path in "$CONTAINER_CNI_BIN_DIR"/*; do
cp "$path" "$dir/" || exit_with_error "Failed to copy $path to $dir."
done

log "Wrote linkerd CNI binaries to ${dir}"
log "Wrote linkerd CNI binaries to $dir"
}

create_kubeconfig() {
KUBE_CA_FILE=${KUBE_CA_FILE:-${SERVICEACCOUNT_PATH}/ca.crt}
KUBE_CA_FILE=${KUBE_CA_FILE:-$SERVICEACCOUNT_PATH/ca.crt}
SKIP_TLS_VERIFY=${SKIP_TLS_VERIFY:-false}
SERVICEACCOUNT_TOKEN=$(cat ${SERVICEACCOUNT_PATH}/token)
SERVICEACCOUNT_TOKEN=$(cat "$SERVICEACCOUNT_PATH/token")
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"dangerous" not to quote command arguments. what if $SERVICEACCOUNT_PATH has a space char in it?


# Check if we're not running as a k8s pod.
if [[ ! -f "${SERVICEACCOUNT_PATH}/token" ]]; then
if [[ ! -f "$SERVICEACCOUNT_PATH/token" ]]; then
return
fi

if [ -z "${KUBERNETES_SERVICE_HOST}" ]; then
if [ -z "$KUBERNETES_SERVICE_HOST" ]; then
log 'KUBERNETES_SERVICE_HOST not set'; exit 1;
fi
if [ -z "${KUBERNETES_SERVICE_PORT}" ]; then
if [ -z "$KUBERNETES_SERVICE_PORT" ]; then
log 'KUBERNETES_SERVICE_PORT not set'; exit 1;
fi

if [ "${SKIP_TLS_VERIFY}" = 'true' ]; then
if [ "$SKIP_TLS_VERIFY" = 'true' ]; then
TLS_CFG='insecure-skip-tls-verify: true'
elif [ -f "${KUBE_CA_FILE}" ]; then
TLS_CFG="certificate-authority-data: $(base64 "${KUBE_CA_FILE}" | tr -d '\n')"
elif [ -f "$KUBE_CA_FILE" ]; then
TLS_CFG="certificate-authority-data: $(base64 "$KUBE_CA_FILE" | tr -d '\n')"
fi

touch "${CONTAINER_MOUNT_PREFIX}${DEST_CNI_NET_DIR}/${KUBECONFIG_FILE_NAME}"
chmod "${KUBECONFIG_MODE:-600}" "${CONTAINER_MOUNT_PREFIX}${DEST_CNI_NET_DIR}/${KUBECONFIG_FILE_NAME}"
cat > "${CONTAINER_MOUNT_PREFIX}${DEST_CNI_NET_DIR}/${KUBECONFIG_FILE_NAME}" <<EOF
touch "${CONTAINER_MOUNT_PREFIX}${DEST_CNI_NET_DIR}/$KUBECONFIG_FILE_NAME"
chmod "${KUBECONFIG_MODE:-600}" "${CONTAINER_MOUNT_PREFIX}${DEST_CNI_NET_DIR}/$KUBECONFIG_FILE_NAME"
cat > "${CONTAINER_MOUNT_PREFIX}${DEST_CNI_NET_DIR}/$KUBECONFIG_FILE_NAME" <<EOF
# Kubeconfig file for linkerd CNI plugin.
apiVersion: v1
kind: Config
clusters:
- name: local
cluster:
server: ${KUBERNETES_SERVICE_PROTOCOL:-https}://[${KUBERNETES_SERVICE_HOST}]:${KUBERNETES_SERVICE_PORT}
${TLS_CFG}
server: ${KUBERNETES_SERVICE_PROTOCOL:-https}://[$KUBERNETES_SERVICE_HOST]:$KUBERNETES_SERVICE_PORT
$TLS_CFG
users:
- name: linkerd-cni
user:
token: ${SERVICEACCOUNT_TOKEN}
token: $SERVICEACCOUNT_TOKEN
contexts:
- name: linkerd-cni-context
context:
Expand All @@ -179,20 +181,20 @@ create_cni_conf() {
CNI_NETWORK_CONFIG="${CNI_NETWORK_CONFIG:-}"

# If the CNI Network Config has been overwritten, then use template from file
if [ -e "${CNI_NETWORK_CONFIG_FILE}" ]; then
log "Using CNI config template from ${CNI_NETWORK_CONFIG_FILE}."
cp "${CNI_NETWORK_CONFIG_FILE}" "${TMP_CONF}"
elif [ "${CNI_NETWORK_CONFIG}" ]; then
if [ -e "$CNI_NETWORK_CONFIG_FILE" ]; then
log "Using CNI config template from $CNI_NETWORK_CONFIG_FILE."
cp "$CNI_NETWORK_CONFIG_FILE" "$TMP_CONF"
elif [ "$CNI_NETWORK_CONFIG" ]; then
log 'Using CNI config template from CNI_NETWORK_CONFIG environment variable.'
cat >"${TMP_CONF}" <<EOF
${CNI_NETWORK_CONFIG}
cat <<EOF > "$TMP_CONF"
$CNI_NETWORK_CONFIG
EOF
fi

# Use alternative command character "~", since these include a "/".
sed -i s~__KUBECONFIG_FILEPATH__~"${DEST_CNI_NET_DIR}/${KUBECONFIG_FILE_NAME}"~g ${TMP_CONF}
sed -i s~__KUBECONFIG_FILEPATH__~"$DEST_CNI_NET_DIR/$KUBECONFIG_FILE_NAME"~g "$TMP_CONF"

log "CNI config: $(cat ${TMP_CONF})"
log "CNI config: $(cat "$TMP_CONF")"
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"dangerous" not to quote command arguments. what if $TMP_CONF has a space char in it?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

...and, yes, bash understands how to correctly interpret "$(... "...")"

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i'm surprised shellcheck doesn't complain about this. it's a common rookie mistake. 🙂

$ file="a b"
$ touch "$file"
$ ls "$file"
a b
$ ls $file
ls: a: No such file or directory
ls: b: No such file or directory

}

install_cni_conf() {
Expand All @@ -209,15 +211,18 @@ install_cni_conf() {

echo "$conf_data" > "$TMP_CONF"

# If the old config filename ends with .conf, rename it to .conflist, because it has changed to be a list
# If the old config filename ends with .conf, rename it to .conflist because
# it has changed to be a list.
local filename
local extension
filename=${cni_conf_path##*/}
extension=${filename##*.}
# When this variable has a file, we must delete it later.
old_file_path=
if [ "${filename}" != '01-linkerd-cni.conf' ] && [ "${extension}" = 'conf' ]; then
old_file_path=${cni_conf_path}
log "Renaming ${cni_conf_path} extension to .conflist"
cni_conf_path="${cni_conf_path}list"
if [ "$filename" != '01-linkerd-cni.conf' ] && [ "$extension" = 'conf' ]; then
old_file_path=$cni_conf_path
log "Renaming $cni_conf_path extension to .conflist"
cni_conf_path=${cni_conf_path}list
fi

# Store SHA of each patched file in global `CNI_CONF_SHA` variable.
Expand All @@ -238,18 +243,19 @@ install_cni_conf() {
CNI_CONF_SHA=$(jq -c --arg f "$cni_conf_path" --arg sha "$new_sha" '. * {$f: $sha}' <<< "$CNI_CONF_SHA")

# Move the temporary CNI config into place.
mv "${TMP_CONF}" "${cni_conf_path}" || exit_with_error 'Failed to mv files.'
[ -n "$old_file_path" ] && rm -f "${old_file_path}" && log "Removing unwanted .conf file"
mv "$TMP_CONF" "$cni_conf_path" || exit_with_error 'Failed to mv files.'
[ -n "$old_file_path" ] && rm -f "$old_file_path" && log "Removing unwanted .conf file"

log "Created CNI config ${cni_conf_path}"
log "Created CNI config $cni_conf_path"
}

# Sync() is responsible for reacting to file system changes. It is used in
# conjunction with inotify events; sync() is called with the event type (which
# can be either 'CREATE', 'MOVED_TO' or 'MODIFY'), and the name of the file that
# `sync()` is responsible for reacting to file system changes. It is used in
# conjunction with inotify events; `sync()` is called with the event type (which
# can be either 'CREATE', 'MOVED_TO', or 'MODIFY') and the name of the file that
# has changed.
#
# Based on the changed file, sync() might re-install the CNI configuration file.
# Based on the changed file, `sync()` might re-install the CNI configuration
# file.
sync() {
local ev=$1
local file=${2//\/\//\/} # replace "//" with "/"
Expand Down Expand Up @@ -286,7 +292,7 @@ sync() {

# monitor_cni_config starts a watch on the host's CNI config directory
monitor_cni_config() {
inotifywait -m "${HOST_CNI_NET}" -e create,moved_to,modify |
inotifywait -m "$HOST_CNI_NET" -e create,moved_to,modify |
while read -r directory action filename; do
sync "$action" "$directory/$filename"
done
Expand All @@ -302,18 +308,19 @@ monitor_cni_config() {
# Indeed, as per atomic writer's Write function docs, in the final steps the
# ..data_tmp symlink points to a new timestamped directory containing the new
# files, which is then atomically renamed to ..data:
# > 8. A symlink to the new timestamped directory ..data_tmp is created that will
# > become the new data directory.
# > 9. The new data directory symlink is renamed to the data directory; rename is atomic.
# > 8. A symlink to the new timestamped directory ..data_tmp is created that
# > will become the new data directory.
# > 9. The new data directory symlink is renamed to the data directory; rename
# > is atomic.
# See https://github.com/kubernetes/kubernetes/blob/release-1.32/pkg/volume/util/atomic_writer.go
monitor_service_account_token() {
inotifywait -m "${SERVICEACCOUNT_PATH}" -e moved_to |
while read -r _ _ filename; do
if [[ "$filename" == "..data" ]]; then
inotifywait -m "$SERVICEACCOUNT_PATH" -e moved_to |
while read -r _ _ filename; do
if [[ "$filename" == "..data" ]]; then
log "Detected change in service account files; recreating kubeconfig file"
create_kubeconfig
fi
done
fi
done
}

log() {
Expand All @@ -326,7 +333,7 @@ log() {

# Delete old "interface mode" file, possibly left over from previous versions
# TODO(alpeb): remove this on stable-2.15
rm -f "${DEFAULT_CNI_CONF_PATH}"
rm -f "$DEFAULT_CNI_CONF_PATH"

install_cni_bin

Expand All @@ -339,24 +346,19 @@ CNI_CONF_SHA='{}'
monitor_cni_config &

# Append our config to any existing config file (*.conflist or *.conf)
config_files=$(find "${HOST_CNI_NET}" -maxdepth 1 -type f \( -iname '*conflist' -o -iname '*conf' \))
config_files=$(find "$HOST_CNI_NET" -maxdepth 1 -type f \( -iname '*conflist' -o -iname '*conf' \) | grep -v linkerd || true)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you explain what this is doing? this seems like a change to the logic of this script, versus the cosmetic/stylistic changes happening elsewhere in this diff.

Copy link
Contributor Author

@sdickhoven sdickhoven Jun 11, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is a simplification.

right now, the script checks if there are any files in /host/etc/cni/net.d and if not it prints "No active CNI configuration files found":

config_files=$(find "${HOST_CNI_NET}" -maxdepth 1 -type f \( -iname '*conflist' -o -iname '*conf' \))
if [ -z "$config_files" ]; then
  log "No active CNI configuration files found"
else

in the else case, the script then removes any files from the $config_files variable that have the (sub)string linkerd in them and counts how many files are left (the sort is completely nonsensical here!).

and if there are no files left (i.e. wc -l == number of lines == 0) then it again prints "No active CNI configuration files found":

  config_file_count=$(echo "$config_files" | grep -v linkerd | sort | wc -l)
  if [ "$config_file_count" -eq 0 ]; then
    log "No active CNI configuration files found"
  else

here are a couple of examples:


/host/etc/cni/net.d has no files:

[ -z "" ] # true

-> first if statement is true .: "No active CNI configuration files found"


/host/etc/cni/net.d has the following file:

  • something-linkerd-something
[ -z "something-linkerd-something" ] # false
echo "something-linkerd-something" | grep -v linkerd | wc -l # 0
[ "0" -eq 0 ] # true

-> first if statement is false, second if statement is true .: "No active CNI configuration files found"


/host/etc/cni/net.d has the following files:

  • something-linkerd-something
  • something-else
[ -z "something-linkerd-something
something-else" ] # false
echo "something-linkerd-something
something-else" | grep -v linkerd | wc -l # 1
[ "1" -eq 0 ] # false

-> find files and patch them


anyway, my logic just finds all files in /host/etc/cni/net.d and immediately removes any files that have the (sub)string linkerd in them and then checks whether any files are left:

config_files=$(find "$HOST_CNI_NET" -maxdepth 1 -type f \( -iname '*conflist' -o -iname '*conf' \) | grep -v linkerd || true)
if [ -z "$config_files" ]; then
...

this is much more straight forward and easier to understand:

  1. find all files that don't have the (sub)string linkerd in them
  2. if what was found is an empty string ... else ...

you may be wondering about the || true at the end.

this is due to the fact that the script configures bash with set -e (== exit immediately when a command exits with a non-zero exit status) and that grep foo or grep -v foo will exit with an exit status of 1 (which would then cause the entire script to exit) when no output is produced.

$ grep foo <<< foobarbaz
foobarbaz
$ echo $?
0

$ grep foo <<< noobarbaz
$ echo $?
1

$ grep -v foo <<< foobarbaz
$ echo $?
1

$ grep -v foo <<< noobarbaz
noobarbaz
$ echo $?
0

the || construct simply means "if exit status is non-zero do this instead" and the command true simply does nothing and exits with a zero exit status.

$ true
$ echo $?
0

$ false
$ echo $?
1

so the || true simply means: if the exit status is non-zero (which is entirely expected and totally fine), change it to zero so that the script doesn't exit (due to set -e).

by the way, the same thing can be accomplished with

config_files=$(find "$HOST_CNI_NET" -maxdepth 1 -type f ! -name '*linkerd*' \( -iname '*conflist' -o -iname '*conf' \))

actually... i like that better. i'll update... because then we don't need that somewhat mysterious || true.

Copy link
Contributor Author

@sdickhoven sdickhoven Jun 11, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok. so now we are trading

  config_file_count=$(echo "$config_files" | grep -v linkerd | sort | wc -l)
  if [ "$config_file_count" -eq 0 ]; then
    log "No active CNI configuration files found"
  else

for

! -name '*linkerd*' in the find command.

to get the same exact behavior.

by the way, it's a bit incongruent that files with linkerd in them are not excluded from the subsequent find that drives the actual patching.

i would personally get rid of that second find as well and just use $config_files but that would actually be a slightly different behavior and this pr is all about not making any functional changes.

if [ -z "$config_files" ]; then
log "No active CNI configuration files found"
log "No active CNI configuration files found"
else
config_file_count=$(echo "$config_files" | grep -v linkerd | sort | wc -l)
if [ "$config_file_count" -eq 0 ]; then
log "No active CNI configuration files found"
else
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

combine the two ifs into one.

find "${HOST_CNI_NET}" -maxdepth 1 -type f \( -iname '*conflist' -o -iname '*conf' \) -print0 |
while read -r -d $'\0' file; do
log "Trigger CNI config detection for $file"
tmp_file="$(mktemp -u /tmp/linkerd-cni.patch-candidate.XXXXXX)"
cp -fp "$file" "$tmp_file"
# The following will trigger the `sync()` function via filesystem event.
# This requires `monitor_cni_config()` to be up and running!
mv "$tmp_file" "$file" || exit_with_error 'Failed to mv files.'
done
fi
find "$HOST_CNI_NET" -maxdepth 1 -type f \( -iname '*conflist' -o -iname '*conf' \) -print0 |
while read -r -d $'\0' file; do
log "Trigger CNI config detection for $file"
tmp_file="$(mktemp -u /tmp/linkerd-cni.patch-candidate.XXXXXX)"
cp -fp "$file" "$tmp_file"
# The following will trigger the `sync()` function via filesystem event.
# This requires `monitor_cni_config()` to be up and running!
mv "$tmp_file" "$file" || exit_with_error 'Failed to mv files.'
done
fi

# Watch in bg so we can receive interrupt signals through 'trap'. From 'man
Expand All @@ -368,5 +370,6 @@ fi
# the wait builtin to return immediately with an exit status greater than 128,
# immediately after which the trap is executed."
monitor_service_account_token &
# uses -n so that we exit when the first background job exits (when there's an error)
# uses -n so that we exit when the first background job exits (when there's an
# error)
wait -n