Skip to content

Instantly share code, notes, and snippets.

@Andrei-Pozolotin
Last active October 2, 2020 04:40
Show Gist options
  • Save Andrei-Pozolotin/6bc4f2caa18700cdd94d910e588a555c to your computer and use it in GitHub Desktop.
Save Andrei-Pozolotin/6bc4f2caa18700cdd94d910e588a555c to your computer and use it in GitHub Desktop.
cni-bash-plugin
cni-bash-plugin
files:
/etc/rkt/net.d/bridge.sh.conf
/etc/rkt/net.d/bridge-dhcp.sh
/etc/rkt/net.d/bridge.sh
usage:
rkt run \
--insecure-options=all \
--net=bridge.sh;machine_name=aci-serv;manchine_mac=name;dhcp=enable \
docker://carrotgarden/aci-serv
see:
https://github.com/coreos/rkt/issues/2895
https://github.com/containernetworking/cni/issues/265
#!/bin/bash
# {{ ansible_managed }}
# udhcpc script
# https://udhcp.busybox.net/README.udhcpc
# https://github.com/mirror/busybox/tree/master/examples/udhcp
log() {
logger -t "rkt" "$DHCP_RUNNER :: $@"
}
route_create() {
[[ "$interface" ]] || { log "missing interface" ; return ; }
[[ "$router" ]] || { log "missing router" ; return ; }
local entry=
for entry in $router ; do
ip route add default via $entry dev $interface
done
}
route_delete() {
[[ "$interface" ]] || { log "missing interface" ; return ; }
while ip route del dev $interface 2> /dev/null ; do
:
done
}
address_create() {
[[ "$interface" ]] || { log "missing interface" ; return ; }
[[ "$subnet" ]] || { log "missing subnet" ; return ; }
[[ "$broadcast" ]] || { log "missing broadcast" ; return ; }
ip address add $ip/$subnet broadcast $broadcast dev $interface
}
address_delete() {
[[ "$interface" ]] || { log "missing interface" ; return ; }
ip address flush dev $interface
}
dhcp_create() {
log "create"
address_create
route_create
env | sort > "$DHCP_STATUS"
}
dhcp_ensure() {
log "ensure"
dhcp_delete
dhcp_create
}
dhcp_delete() {
log "delete"
rm -f "$DHCP_STATUS"
route_delete
address_delete
}
dhcp_error() {
log "error: message=$message"
}
arguments() {
readonly DHCP_RUNNER="$BASH_SOURCE"
readonly DHCP_STATUS="$DHCP_RUNNER.status"
}
main() {
arguments
case "$command" in
bound) dhcp_create ;;
renew) dhcp_ensure ;;
deconfig) dhcp_delete ;;
nak|leasefail) dhcp_error ;;
*) log "wrong command $command" ;;
esac
}
###
export readonly command="$1"
main
#!/bin/bash
# {{ ansible_managed }}
# cni bridge+dhcp plugin
# https://github.com/containernetworking/cni
log() {
logger -t "rkt" "$SCRIPT_NAME :: $@"
}
log_cni() {
log "CNI_VERSION $CNI_VERSION"
log "CNI_COMMAND $CNI_COMMAND"
log "CNI_CONTAINERID $CNI_CONTAINERID"
log "CNI_NETNS $CNI_NETNS"
log "CNI_IFNAME $CNI_IFNAME"
log "CNI_ARGS $CNI_ARGS"
log "CNI_PATH $CNI_PATH"
}
json_list() {
local list=$(echo "$1" | tr ' ' '","')
[[ "$list" ]] && echo '[ "'$list'" ]' || echo '[]'
}
make_face() {
local prefix="$1" ; [[ "$prefix" ]] || prefix="rkt"
echo "$CNI_CONTAINERID" | sed -r "s/(........).*/$prefix-\1/"
}
make_mac_uuid() {
local "$@"
[[ "$uuid" ]] || local uuid="00000000-0000-0000-0000-000000000000"
echo "$uuid" | sed -r "s/(..)(..)(..)(..)-(..).*/02:\1:\2:\3:\4:\5/"
}
make_mac_name() {
local "$@"
[[ "$name" ]] || local name="machine"
echo "$name" | md5sum | sed -r "s/(..)(..)(..)(..)(..).*/12:\1:\2:\3:\4:\5/"
}
make_mac_convert() {
local "$@"
[[ "$mac" ]] || local mac="12-34-56-78-90-ab"
echo "$mac" | tr '-' ':'
}
netns_exec() {
ip netns exec $NETNS_NAME $@
}
result_ip() {
local "$@"
netns_exec ip -oneline -family "$family" addr show dev $MACHINE_FACE | awk '{ print $4 }'
}
result_ipv4() {
result_ip family=inet | head --lines 1
}
result_ipv6() {
result_ip family=inet6 | head --lines 1
}
result_dns() {
has_dhcp_status && local $(read_dhcp_staus) && echo $dns || return 0
}
result_domain() {
has_dhcp_status && local $(read_dhcp_staus) && echo $domain || return 0
}
face_create() {
netns_exec ip link add $MACHINE_FACE type veth peer name $BRIDGE_FACE
netns_exec ip link set $MACHINE_FACE address $MACHINE_MAC
netns_exec ip link set $MACHINE_FACE up
netns_exec ip link set lo up
netns_exec ip link set $BRIDGE_FACE netns 1
}
face_delete() {
netns_exec ip link set $MACHINE_FACE down
netns_exec ip link del $MACHINE_FACE
netns_exec ip link set lo down
}
is_dhcp_enable() {
[[ "$DHCP" == "enable" ]]
}
has_dhcp_status() {
[[ -e "$DHCP_STATUS" ]]
}
read_dhcp_staus() {
cat "$DHCP_STATUS"
}
dhcp_create() {
is_dhcp_enable || return 0
#
mkdir -p "$DHCP_DIR"
rm -f "$DHCP_STATUS"
ln -s "$DHCP_SCRIPT" "$DHCP_RUNNER"
#
systemd-run \
--property="SyslogIdentifier=$DHCP_NAME" \
--description="CNI Service $DHCP_NAME" \
--unit="$DHCP_UNIT" \
--no-block \
$(dhcp_command)
}
dhcp_delete() {
is_dhcp_enable || return 0
#
systemctl stop "$DHCP_UNIT"
#
rm -f "$DHCP_RUNNER"
rm -f "$DHCP_STATUS"
}
dhcp_await() {
is_dhcp_enable || return 0
local step=0 size=5
while ! has_dhcp_status ; do
[[ $(( step % size )) == 0 ]] && log "dhcp await: $step"
step=$(( step + 1 ))
sleep 1
done
}
exec_path() {
type -P $1
}
dhcp_command() {
echo "\
$(exec_path ip) netns exec $NETNS_NAME \
$(exec_path busybox) udhcpc \
--fqdn $MACHINE_NAME \
--interface $MACHINE_FACE \
--script $DHCP_RUNNER \
--retries 0 \
--timeout $DHCP_TIMEOUT \
--tryagain $DHCP_TIMEOUT \
--release \
--foreground \
"
}
join_create() {
brctl addif $BRIDGE_ROOT $BRIDGE_FACE
}
join_delete() {
brctl delif $BRIDGE_ROOT $BRIDGE_FACE
}
main_create() {
face_create
join_create
dhcp_create
dhcp_await
report_success \
ip4=$(result_ipv4) ip6=$(result_ipv6) \
dns=$(result_dns) domain=$(result_domain)
}
main_delete() {
dhcp_delete
join_delete
face_delete
report_success
}
is_bridge_present() {
local "$@"
brctl show | grep -q -E "^$name\s"
}
make_bridge_root() {
local "$@"
case "$bridge" in
ensure) # make when missing
log "TODO $bridge not implemented. CNI_ARGS: $CNI_ARGS" ; return 1
;;
discover) # find best matching
log "TODO $bridge not implemented. CNI_ARGS: $CNI_ARGS" ; return 1
;;
existing) # user config or default
[[ "$bridge_root" ]] && echo $bridge_root || echo br0
;;
*) log "wrong bridge $bridge. CNI_ARGS: $CNI_ARGS" ; return 1
;;
esac
}
make_machine_mac() {
local "$@"
case "$mac" in
"") make_mac_uuid uuid=$uuid ;;
name) make_mac_name name=$name ;;
*) make_mac_convert mac=$mac ;;
esac
}
arguments() {
local _args_=
_args_=$(echo "$CNI_ARGS" | tr ';' ' ')
[[ "${_args_}" ]] && local ${_args_} || true
#
readonly NETNS_NAME=$(basename "$CNI_NETNS")
#
readonly BRIDGE=$([[ "$bridge" ]] && echo $bridge || echo existing)
readonly BRIDGE_FACE=$([[ "$bridge_face" ]] && echo $bridge_face || make_face)
readonly BRIDGE_ROOT=$(make_bridge_root bridge=$BRIDGE bridge_root=$bridge_root)
is_bridge_present name="$BRIDGE_ROOT" || \
report_failure code=110 message="invalid bridge" details="CNI_ARGS: $CNI_ARGS"
#
readonly MACHINE_NAME=$([[ "$machine_name" ]] && echo $machine_name || echo $BRIDGE_FACE)
readonly MACHINE_FACE=$([[ "$machine_face" ]] && echo $machine_face || echo $CNI_IFNAME)
readonly MACHINE_MAC=$(make_machine_mac mac=$machine_mac name=$MACHINE_NAME uuid=$CNI_CONTAINERID)
#
readonly DHCP=$([[ "$dhcp" ]] && echo $dhcp || echo disable)
readonly DHCP_DIR=$([[ "$dhcp_dir" ]] && echo $dhcp_dir || echo $SCRIPT_SPACE)
readonly DHCP_NAME=$([[ "$dhcp_name" ]] && echo $dhcp_name || echo dhcp-$MACHINE_NAME)
readonly DHCP_UNIT=$([[ "$dhcp_unit" ]] && echo $dhcp_unit || echo $DHCP_NAME-$BRIDGE_FACE)
readonly DHCP_TIMEOUT=$([[ "$dhcp_timeout" ]] && echo $dhcp_timeout || echo 1)
readonly DHCP_SCRIPT=$([[ "$dhcp_script" ]] && echo $dhcp_script || echo ${BASH_SOURCE%.*}-dhcp.sh)
readonly DHCP_RUNNER="$DHCP_DIR/$DHCP_UNIT.sh"
readonly DHCP_STATUS="$DHCP_RUNNER.status"
}
defaults() {
readonly REPORT_VERSION="0.3.0"
readonly UNDEFINED_IP4="0.0.0.0/32"
readonly UNDEFINED_IP6="::/128"
}
flush_output() {
stdbuf --output=0 $@
}
switch_output() {
local "$@"
mkdir -p $(dirname "$SCRIPT_OUTPUT")
case "$mode" in
external) # redirect stdout and stderr
exec 11>&1 22>&2 # save stdout/stderr
exec &> "$SCRIPT_OUTPUT" # collect output to file
;;
original) # restore stdout and stderr
flush_output echo >> "$SCRIPT_OUTPUT"
exec 1>&11 2>&22 # restore stdout/stderr
exec 11>&- 22>&- # release file descriptors
;;
*)
exit 123 # program error
;;
esac
}
define() {
IFS='\n' read -r -d '' ${1} || true
}
report_success() {
local "$@" result= code=0
[[ "$ip4" ]] || local ip4="$UNDEFINED_IP4"
[[ "$ip6" ]] || local ip6="$UNDEFINED_IP6"
define result << EOF_SUCCESS
{
"cniVersion" : "$REPORT_VERSION",
"ip4" : {
"ip" : "$ip4"
},
"ip6" : {
"ip" : "$ip6"
},
"dns" : {
"domain" : "$domain",
"nameservers" : $(json_list $dns)
}
}
EOF_SUCCESS
main_exit code=$code result="$result"
}
report_failure() {
local code=200 "$@" result=
define result << EOF_FAILURE
{
"cniVersion": "$REPORT_VERSION",
"code": $code,
"msg": "$message",
"details": "$details"
}
EOF_FAILURE
main_exit code=$code result="$result"
}
main_exit() {
local "$@"
[[ "$BASH_SUBSHELL" == "0" ]] || exit $code
switch_output mode=original
flush_output echo "$result"
[[ $code == 0 ]] && rm -f "$SCRIPT_OUTPUT" || true
exit $code
}
main() {
switch_output mode=external
defaults
arguments
case "$CNI_COMMAND" in
ADD) main_create ;;
DEL) main_delete ;;
*) log_cni ; report_failure code=101 message="wrong command" ;;
esac
}
error() {
local variables=$(set -o posix ; set | sort)
flush_output echo "\n### VARIABLES ###\n\n$variables" >> "$SCRIPT_OUTPUT"
local message="script error: $SCRIPT_NAME :: $@"
local details="script output: $SCRIPT_OUTPUT"
log "$message"
report_failure code=102 message="$message" details="$details"
}
###
set -o errexit
set -o errtrace
readonly SCRIPT_NAME=$(basename $BASH_SOURCE)
readonly SCRIPT_SPACE="/run/rkt/cni/$SCRIPT_NAME"
readonly SCRIPT_OUTPUT="$SCRIPT_SPACE/output-$CNI_CONTAINERID.log"
trap 'error line=["$LINENO"] function=["$FUNCNAME"] command=["$BASH_COMMAND"]' ERR
main
{
"name": "bridge.sh",
"type": "bridge.sh"
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment