diff --git a/CHANGELOG.md b/CHANGELOG.md index a2fd2ec..85e38ed 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,12 @@ +# v0.4.1 + +Released 2023-07-21 + +- add additional packages to container image +- support executing arbitrary pre/post scripts during reconcile (should be + idempotent) +- bump docker base image to `hydrogen-bookworm-slim` + # v0.4.0 Released 2023-07-20 @@ -5,6 +14,7 @@ Released 2023-07-20 - remove management of `TABLE_NAME` in `/etc/iproute2/rt_tables` - better management of watches to unsubscribe when appropriate - more robust reconcile logic to prevent race conditions +- support `RULE_FWMARK` # v0.3.2 diff --git a/Dockerfile b/Dockerfile index 60dca4e..5879d58 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM node:gallium-bullseye-slim AS build +FROM node:hydrogen-bookworm-slim AS build ENV DEBIAN_FRONTEND=noninteractive @@ -19,7 +19,7 @@ COPY . . ###################### # actual image ###################### -FROM node:gallium-bullseye-slim +FROM node:hydrogen-bookworm-slim LABEL org.opencontainers.image.source https://github.com/travisghansen/metallb-node-route-agent @@ -31,7 +31,17 @@ ARG BUILDPLATFORM RUN echo "I am running on final $BUILDPLATFORM, building for $TARGETPLATFORM" -RUN apt-get update && apt-get install -y iproute2 && rm -rf /var/lib/apt/lists/* +RUN apt-get update && \ + cd ~ && \ + apt-get install -y iproute2 xz-utils conntrack ipset iptables wget curl jq && \ + wget -c https://xyne.dev/projects/idemptables/src/idemptables-2012.tar.xz -O - | tar -Jxv && \ + install -o root -g root -m 0755 idemptables-2012/idemptables /usr/sbin/idemptables && \ + rm -rf idemptables-2012/ && \ + sed -i 's:#!/bin/sh:#!/bin/bash:g' /usr/sbin/idemptables && \ + curl -LO https://dl.k8s.io/release/v1.27.3/bin/linux/amd64/kubectl && \ + install -o root -g root -m 0755 kubectl /usr/local/bin/kubectl && \ + rm -rf kubectl && \ + rm -rf /var/lib/apt/lists/* COPY --from=build /app /app diff --git a/README.md b/README.md index 0c8f6ce..9b8e75f 100644 --- a/README.md +++ b/README.md @@ -41,8 +41,12 @@ your nodes have the sysctl `net.ipv4.fib_multipath_use_neigh` set to `1`. - `RULE_FWMARK` - the fwmark to give to the managed rules (show be provided in hex format exactly as the `ip` output shows) - default: unset -- `DESTINATION` - the `dst` network of the rule +- `DESTINATION` - the `dst` network of the route - default: `default` +- `PRE_RECONCILE_SCRIPT_PATH` - path to script (must be marked executable) to + run _before_ the reonciliation happens +- `POST_RECONCILE_SCRIPT_PATH` - path to script (must be marked executable) to + run _after_ the reonciliation happens - `METALLB_NAMESPACE` - namespace where `metallb` is running - default: `""` - will fallback to the value in @@ -53,6 +57,8 @@ your nodes have the sysctl `net.ipv4.fib_multipath_use_neigh` set to `1`. - `METALLB_STATIC_FILE` - a static file on the filesystem to monitor (mostly for development purposes). If set the k8s watch is disabled entirely. - `METALLB_USE_CRDS` - prefer `CRDs` over configmap +- `LOG_LEVEL` - `error|warn|info|verbose|debug|silly` + - default: `info` - `CLEANANDEXIT` - if equals `1` then all rules/tables will be deleted and the process will exit - `ONESHOT` - if equals `1` then then reconciliation will complete once and the diff --git a/agent.js b/agent.js index 4ff2c7b..fa679ad 100644 --- a/agent.js +++ b/agent.js @@ -1,4 +1,5 @@ const _ = require('lodash'); +const cp = require('child_process'); const AsyncMutex = require('async-mutex'); const fs = require('fs'); const { IpAddress, IpRange } = require('cidr-calc'); @@ -58,6 +59,9 @@ const METALLB_USE_CRDS = process.env.METALLB_USE_CRDS; const NODE_NAME = process.env.NODE_NAME; +const PRE_RECONCILE_SCRIPT_PATH = process.env.PRE_RECONCILE_SCRIPT_PATH; +const POST_RECONCILE_SCRIPT_PATH = process.env.POST_RECONCILE_SCRIPT_PATH; + // globals let metallb_loaded = false; @@ -128,6 +132,22 @@ async function reconcile() { return; } + if (PRE_RECONCILE_SCRIPT_PATH) { + logger.verbose( + `executing PRE_RECONCILE_SCRIPT_PATH: ${PRE_RECONCILE_SCRIPT_PATH}` + ); + let res = cp.spawnSync(PRE_RECONCILE_SCRIPT_PATH, [], { + timeout: 10 * 1000 + }); + + let message = `executed ${PRE_RECONCILE_SCRIPT_PATH}: code=${res.status}, signal=${res.signal}, stdout=${res.stdout}, stderr=${res.stderr}`; + if (res.status != 0) { + logger.warn(message); + } else { + logger.debug(message); + } + } + let args = []; //logger.info('skipping reconcile, development debug'); @@ -351,6 +371,22 @@ async function reconcile() { } } + if (POST_RECONCILE_SCRIPT_PATH) { + logger.verbose( + `executing POST_RECONCILE_SCRIPT_PATH: ${POST_RECONCILE_SCRIPT_PATH}` + ); + let res = cp.spawnSync(POST_RECONCILE_SCRIPT_PATH, [], { + timeout: 10 * 1000 + }); + + let message = `executed ${POST_RECONCILE_SCRIPT_PATH}: code=${res.status}, signal=${res.signal}, stdout=${res.stdout}, stderr=${res.stderr}`; + if (res.status != 0) { + logger.warn(message); + } else { + logger.debug(message); + } + } + logger.verbose('reconcile finished'); if (process.env.ONESHOT == '1') { logger.info('exiting due to ONESHOT'); diff --git a/charts/metallb-node-route-agent/Chart.yaml b/charts/metallb-node-route-agent/Chart.yaml index a2b0876..5c2baa8 100644 --- a/charts/metallb-node-route-agent/Chart.yaml +++ b/charts/metallb-node-route-agent/Chart.yaml @@ -15,10 +15,10 @@ type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 0.4.0 +version: 0.4.1 # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to # follow Semantic Versioning. They should reflect the version the application is using. # It is recommended to use it with quotes. -appVersion: "0.4.0" +appVersion: "0.4.1" diff --git a/charts/metallb-node-route-agent/templates/configmap.yaml b/charts/metallb-node-route-agent/templates/configmap.yaml new file mode 100644 index 0000000..3bdcd4d --- /dev/null +++ b/charts/metallb-node-route-agent/templates/configmap.yaml @@ -0,0 +1,13 @@ +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ include "metallb-node-route-agent.fullname" . }} + labels: + {{- include "metallb-node-route-agent.labels" . | nindent 4 }} +data: + config.yaml: | + foo: bar +{{- with .Values.configMapData }} +{{- toYaml . | nindent 2 }} +{{- end }} diff --git a/charts/metallb-node-route-agent/templates/daemonset.yaml b/charts/metallb-node-route-agent/templates/daemonset.yaml index 657acd5..5fd3fab 100644 --- a/charts/metallb-node-route-agent/templates/daemonset.yaml +++ b/charts/metallb-node-route-agent/templates/daemonset.yaml @@ -39,6 +39,9 @@ spec: volumeMounts: - name: iproute2 mountPath: /etc/iproute2 + - name: configmap + mountPath: /tmp/config + readOnly: true env: - name: NODE_NAME valueFrom: @@ -66,3 +69,8 @@ spec: hostPath: path: /etc/iproute2 type: Directory + - name: configmap + configMap: + name: {{ include "metallb-node-route-agent.fullname" . }} + # 770 in unix decimal + defaultMode: 504 diff --git a/charts/metallb-node-route-agent/templates/rbac.yaml b/charts/metallb-node-route-agent/templates/rbac.yaml index e723cbd..49af589 100644 --- a/charts/metallb-node-route-agent/templates/rbac.yaml +++ b/charts/metallb-node-route-agent/templates/rbac.yaml @@ -17,6 +17,8 @@ apiVersion: rbac.authorization.k8s.io/v1 metadata: name: {{ include "metallb-node-route-agent.serviceAccountName" . }} namespace: {{ .Release.Namespace | quote }} + labels: + {{- include "metallb-node-route-agent.labels" . | nindent 4 }} rules: - apiGroups: - "" @@ -41,6 +43,8 @@ apiVersion: rbac.authorization.k8s.io/v1 metadata: name: {{ include "metallb-node-route-agent.serviceAccountName" . }} namespace: {{ .Release.Namespace | quote }} + labels: + {{- include "metallb-node-route-agent.labels" . | nindent 4 }} subjects: - kind: ServiceAccount name: {{ include "metallb-node-route-agent.serviceAccountName" . }} @@ -54,6 +58,8 @@ kind: ClusterRole apiVersion: rbac.authorization.k8s.io/v1 metadata: name: {{ include "metallb-node-route-agent.serviceAccountName" . }} + labels: + {{- include "metallb-node-route-agent.labels" . | nindent 4 }} rules: - apiGroups: - "" @@ -68,6 +74,8 @@ kind: ClusterRoleBinding apiVersion: rbac.authorization.k8s.io/v1 metadata: name: {{ include "metallb-node-route-agent.serviceAccountName" . }} + labels: + {{- include "metallb-node-route-agent.labels" . | nindent 4 }} roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole diff --git a/charts/metallb-node-route-agent/values.yaml b/charts/metallb-node-route-agent/values.yaml index 287ec51..a5ecb33 100644 --- a/charts/metallb-node-route-agent/values.yaml +++ b/charts/metallb-node-route-agent/values.yaml @@ -15,6 +15,15 @@ imagePullSecrets: [] nameOverride: "" fullnameOverride: "" +# all files get mounted to /tmp/config 770 perms +configMapData: {} +# pre.sh: | +# #/bin/bash +# echo hello world +# post.sh: | +# #/bin/bash +# echo hello world + rbac: enabled: true diff --git a/lib/ip.js b/lib/ip.js index d07abdf..aa3c167 100644 --- a/lib/ip.js +++ b/lib/ip.js @@ -223,7 +223,13 @@ class IpCommand { }); child.on('close', function (code) { - const result = { code, stdout, stderr, timeout: false }; + const result = { + code, + stdout, + stderr, + timeout: false, + command: `${command} ${args.join(' ')}` + }; ip.options.logger.debug('ip command result:', result); // timeout scenario