mirror of
https://github.com/unraid/webgui.git
synced 2026-04-27 21:40:20 -05:00
660 lines
23 KiB
Docker
Executable File
660 lines
23 KiB
Docker
Executable File
#!/bin/bash
|
|
|
|
# Short-Description: Create lightweight, portable, self-sufficient containers.
|
|
# Description:
|
|
# Docker is an open-source project to easily create lightweight, portable,
|
|
# self-sufficient containers from any application. The same container that a
|
|
# developer builds and tests on a laptop can run at scale, in production, on
|
|
# VMs, bare metal, OpenStack clusters, public clouds and more.
|
|
|
|
# modified for unRAID
|
|
|
|
PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin
|
|
UNSHARE=/usr/bin/unshare
|
|
SYSTEM=/sys/class/net
|
|
CONF6=/proc/sys/net/ipv6/conf
|
|
ACTIVE=$(ls --indicator-style=none $SYSTEM|awk '/^(bond|br|eth)[0-9]/' ORS=' ')
|
|
NICS=$(ls --indicator-style=none $SYSTEM|awk '/^eth[0-9]+$/')
|
|
|
|
DOCKERD=dockerd
|
|
DOCKER=/usr/bin/$DOCKERD
|
|
DOCKER_PIDFILE=/var/run/$DOCKERD.pid
|
|
DOCKER_LOG=/var/log/docker.log
|
|
DOCKER_ROOT=/var/lib/docker
|
|
DOCKER_CFG=/boot/config/docker.cfg
|
|
STOCK="eth br bond"
|
|
|
|
# network file references
|
|
INI=/var/local/emhttp/network.ini
|
|
TMP=/var/tmp/network.tmp
|
|
|
|
# determine active port name
|
|
[[ -e $SYSTEM/bond0 ]] && PORT=bond0 || PORT=eth0
|
|
[[ -e $SYSTEM/br0 ]] && PORT=br0
|
|
|
|
# Set defaults used by the docker daemon
|
|
if [[ -f $DOCKER_CFG ]]; then
|
|
for NIC in $NICS; do
|
|
if [[ -e $SYSTEM/${NIC/eth/br} ]]; then
|
|
NIC=${NIC/eth/br}
|
|
elif [[ -e $SYSTEM/${NIC/eth/bond} ]]; then
|
|
NIC=${NIC/eth/bond}
|
|
fi
|
|
if ! grep -qPm1 "_${NIC^^}(_[0-9]+)?=" $DOCKER_CFG; then
|
|
# interface has changed, update configuration
|
|
X=${NIC//[^0-9]/}
|
|
sed -ri "s/_(BR|BOND|ETH)$X(_[0-9]+)?=/_${NIC^^}\2=/; s/(br|bond|eth)$X(\.[0-9]+)? /$NIC\2 /g" $DOCKER_CFG
|
|
fi
|
|
done
|
|
# Read (updated) unRAID docker configuration file
|
|
. $DOCKER_CFG
|
|
fi
|
|
|
|
# Set storage driver appropriate for backing filesystem, override user setting
|
|
BACKINGFS=$(findmnt --output FSTYPE --noheadings $DOCKER_ROOT)
|
|
if [[ $BACKINGFS == btrfs ]]; then
|
|
DOCKER_OPTS="$DOCKER_OPTS --storage-driver=btrfs"
|
|
elif [[ $BACKINGFS == xfs ]]; then
|
|
DOCKER_OPTS="$DOCKER_OPTS --storage-driver=overlay2"
|
|
elif [[ $BACKINGFS == zfs ]]; then
|
|
DOCKER_OPTS="$DOCKER_OPTS --storage-driver=zfs"
|
|
fi
|
|
|
|
# Less verbose logging by default
|
|
DOCKER_OPTS="--log-level=fatal $DOCKER_OPTS"
|
|
|
|
# Enable global docker LOG rotation
|
|
if [[ $DOCKER_LOG_ROTATION == yes ]]; then
|
|
[[ -z $DOCKER_LOG_SIZE ]] && DOCKER_LOG_SIZE=10m
|
|
[[ -z $DOCKER_LOG_FILES ]] && DOCKER_LOG_FILES=1
|
|
DOCKER_OPTS="--log-opt max-size=$DOCKER_LOG_SIZE --log-opt max-file=$DOCKER_LOG_FILES $DOCKER_OPTS"
|
|
fi
|
|
|
|
# Adjust MTU size if non-default
|
|
MTU=$(ip link show $PORT|grep -Po 'mtu \K\d+')
|
|
[[ -n $MTU && $MTU -ne 1500 ]] && DOCKER_OPTS="--mtu=$MTU $DOCKER_OPTS"
|
|
|
|
# Enable IPv6 for docker bridge network
|
|
if [[ -n $(ip -6 route show dev $PORT default) ]]; then
|
|
DOCKER0='fd17::/64'
|
|
DOCKER_OPTS="--ipv6 --fixed-cidr-v6=$DOCKER0 $DOCKER_OPTS"
|
|
# create IPv6 NAT rule for docker0
|
|
[[ -z $(ip6tables -t nat -S|grep -o "$DOCKER0") ]] && ip6tables -t nat -A POSTROUTING -s $DOCKER0 ! -o docker0 -j MASQUERADE
|
|
else
|
|
# ipv6 disabled
|
|
[[ -e $SYSTEM/docker0 ]] && echo 1 > $CONF6/docker0/disable_ipv6
|
|
fi
|
|
|
|
export DOCKER_RAMDISK=true
|
|
|
|
# Get docker daemon PID (if existing)
|
|
docker_pid() {
|
|
cat $DOCKER_PIDFILE 2>/dev/null
|
|
}
|
|
|
|
# Verify if docker daemon running
|
|
is_docker_running(){
|
|
[[ -S /var/run/docker.sock ]] || return 1
|
|
[[ $(docker info 2>&1) =~ "Cannot connect to the Docker daemon" ]] && return 1 || return 0
|
|
}
|
|
|
|
# Wait max 30s to daemon start
|
|
wait_daemon(){
|
|
for n in {1..30}; do
|
|
if is_docker_running; then return 0; else sleep 1; fi
|
|
done
|
|
return 1
|
|
}
|
|
|
|
# Containers Management
|
|
# All existing containers
|
|
all_containers(){
|
|
docker ps -a --format='{{.Names}}' 2>/dev/null
|
|
}
|
|
|
|
# Running containers
|
|
running_containers(){
|
|
docker ps --format='{{.Names}}' 2>/dev/null
|
|
}
|
|
|
|
max6() {
|
|
# ipv6 addresses in long notation
|
|
f=:ffff:
|
|
for x in $*; do
|
|
read a m < <(IFS=/; echo $x)
|
|
[[ $a =~ $f && $a =~ '.' ]] && b=${a#*$f} a=${a%$f*}$f:0 || b=
|
|
c=${a//[^:]/}
|
|
[[ ${a:0:1} == : ]] && a=0${a}
|
|
[[ ${a:${#a}-1} == : ]] && a=${a}0
|
|
a=${a/::/:$(for((i=1;i<=$((8-${#c}));i++)); do printf "0:"; done)}
|
|
d= a=$(for q in ${a//:/ }; do printf "$d%04x" "0x$q"; d=:; done)
|
|
[[ -n $b ]] && d= a=${a%$f*}${f}$(for q in ${b//./ }; do printf "$d%03x" "0x$q"; d=.; done)
|
|
[[ -z $m ]] && echo $a || echo $a/$m
|
|
done
|
|
}
|
|
|
|
min6() {
|
|
# ipv6 address in short notation
|
|
f=:ffff:
|
|
[[ -n $1 ]] && read a m < <(IFS=/; echo $1) || return
|
|
[[ $a =~ $f && $a =~ '.' ]] && b=${a#*$f} a=${a%$f*}$f || b=
|
|
d= a=:$(for q in ${a//:/ }; do printf "$d%x" "0x$q"; d=:; done)
|
|
a=${a/$(grep -Po ':(0(:|$)){2,8}' <<< $a|sort|tail -1)/::}
|
|
[[ ${a:0:2} != :: ]] && a=${a:1}
|
|
[[ -n $b ]] && d= a=${a%$f*}:$(for q in ${b//./ }; do printf "$d%x" "0x$q"; d=.; done)
|
|
[[ -z $m ]] && echo $a || echo $a/$m
|
|
}
|
|
|
|
wipe() {
|
|
wet=($*)
|
|
# remove temporary (privacy extensions) and host ipv6 addresses
|
|
for tmp in $(ip -br -6 addr show scope global temporary dev $wet 2>/dev/null|awk '{$1=$2="";print}'); do
|
|
for i in ${!wet[@]}; do
|
|
[[ ${wet[$i]} == $tmp || ${wet[$i]#*/} == 128 ]] && unset 'wet[i]'
|
|
done
|
|
done
|
|
# return cleaned-up list without interface name
|
|
echo ${wet[@]/$wet}
|
|
}
|
|
|
|
# Network driver
|
|
driver() {
|
|
# user selection when bridge is enabled
|
|
if [[ -z $DOCKER_NETWORK_TYPE ]]; then
|
|
DETACH='ipvlan'
|
|
ATTACH='macvlan'
|
|
MODE='bridge'
|
|
else
|
|
DETACH='macvlan'
|
|
ATTACH='ipvlan'
|
|
MODE='l2 bridge'
|
|
fi
|
|
# fixed selection when bridge is disabled
|
|
if [[ $1 != br ]]; then
|
|
DETACH='ipvlan'
|
|
ATTACH='macvlan'
|
|
MODE='bridge'
|
|
fi
|
|
}
|
|
|
|
# Custom networks
|
|
network(){
|
|
docker network ls --filter driver="$1" --format='{{.Name}}' 2>/dev/null|grep -P "^[a-z]+$2(\$|\.)"|tr '\n' ' '
|
|
}
|
|
|
|
# Is container running?
|
|
is_container_running(){
|
|
local CONTAINER
|
|
for CONTAINER in $(running_containers); do
|
|
[[ $CONTAINER == $1 ]] && return 0
|
|
done
|
|
return 1
|
|
}
|
|
|
|
# Does the container exist?
|
|
container_exist(){
|
|
local CONTAINER
|
|
for CONTAINER in $(all_containers); do
|
|
[[ $CONTAINER == $1 ]] && return 0
|
|
done
|
|
return 1
|
|
}
|
|
|
|
container_paths_exist(){
|
|
local CONTAINER=$1
|
|
while IFS=| read -r HOSTPATH; do
|
|
# Make sure hostpath exists
|
|
if [[ ! -e "$HOSTPATH" ]]; then
|
|
echo "container \"$CONTAINER\" hostpath \"$HOSTPATH\" does not exist"
|
|
return 1
|
|
fi
|
|
done <<< $(docker inspect --format='{{range .Mounts}}{{.Source}}|{{end}}' $CONTAINER)
|
|
return 0
|
|
}
|
|
|
|
read_dom(){
|
|
local IFS=\>
|
|
read -d \< ENTITY CONTENT
|
|
}
|
|
|
|
add_route(){
|
|
local CT=($(docker inspect --format='{{.State.Pid}} {{.NetworkSettings.Networks}}' $1))
|
|
local PID=${CT[0]}
|
|
local NET=${CT[1]#*[}
|
|
if [[ $PID -gt 0 && ${NET%%:*} == br0 ]]; then
|
|
local THISIP=$(sed -n '/^\[eth0\]$/,/^TYPE/p' $INI|grep -Pom1 '^IPADDR:0="\K[^"]+')
|
|
for CFG in /etc/wireguard/wg*.cfg ; do
|
|
local NETWORK=$(grep -Pom1 '^Network:0="\K[^"]+' $CFG)
|
|
[[ -n $NETWORK ]] && nsenter -n -t $PID ip -4 route add $NETWORK via $THISIP dev br0 2>/dev/null
|
|
done
|
|
fi
|
|
}
|
|
|
|
# Add custom networks
|
|
start_network(){
|
|
# create list of possible custom networks
|
|
EXCLUDE=; INCLUDE=$(ls --indicator-style=none $SYSTEM|awk '/^br[0-9]+/' ORS=' ')
|
|
while IFS=$'\n' read -r NETWORK; do
|
|
if [[ ${NETWORK:0:4} == bond ]]; then
|
|
if [[ $INCLUDE =~ "${NETWORK/bond/br} " ]]; then
|
|
EXCLUDE="${EXCLUDE}${NETWORK} ${NETWORK/bond/eth} "
|
|
else
|
|
INCLUDE="${INCLUDE}${NETWORK} "
|
|
EXCLUDE="${EXCLUDE}${NETWORK/bond/eth} "
|
|
fi
|
|
else
|
|
if [[ $INCLUDE =~ "${NETWORK/eth/br} " || $INCLUDE =~ "${NETWORK/eth/bond} " ]]; then
|
|
[[ $EXCLUDE =~ "$NETWORK " ]] || EXCLUDE="${EXCLUDE}${NETWORK} "
|
|
else
|
|
INCLUDE="${INCLUDE}${NETWORK} "
|
|
fi
|
|
fi
|
|
done <<< $(ls --indicator-style=none $SYSTEM|grep -P '^(bond|eth)[0-9]+')
|
|
wait_daemon
|
|
if ! is_docker_running; then return 1; fi
|
|
# get container settings for custom networks to reconnect later
|
|
declare -A NETRESTORE CTRESTORE
|
|
CONTAINERS=$(docker container ls -a --format='{{.Names}}'|tr '\n' ' ')
|
|
for CONTAINER in $CONTAINERS; do
|
|
# the file case (due to fat32) might be different so use find to match
|
|
XMLFILE=$(find /boot/config/plugins/dockerMan/templates-user -maxdepth 1 -iname my-${CONTAINER}.xml)
|
|
if [[ -n $XMLFILE ]]; then
|
|
REBUILD=
|
|
# update custom network reference (if changed)
|
|
for NIC in $NICS; do
|
|
if [[ -e $SYSTEM/${NIC/eth/br} ]]; then
|
|
NIC=${NIC/eth/br}
|
|
elif [[ -e $SYSTEM/${NIC/eth/bond} ]]; then
|
|
NIC=${NIC/eth/bond}
|
|
fi
|
|
X=${NIC//[^0-9]/}
|
|
REF=$(grep -Pom1 "<Network>\K(br|bond|eth)$X" $XMLFILE)
|
|
if [[ -n $REF && $REF != $NIC ]]; then
|
|
sed -ri "s/<Network>(br|bond|eth)$X(\.[0-9]+)?<\/Network>/<Network>$NIC\2<\/Network>/" $XMLFILE
|
|
# flag container for later rebuild
|
|
REBUILD=1
|
|
fi
|
|
done
|
|
MY_NETWORK= MY_IP=
|
|
while read_dom; do
|
|
[[ $ENTITY == Network ]] && MY_NETWORK=$CONTENT
|
|
[[ $ENTITY == MyIP ]] && MY_IP=${CONTENT// /,} && MY_IP=$(echo "$MY_IP"|tr -s "," ";")
|
|
done <$XMLFILE
|
|
# only restore valid networks
|
|
if [[ -n $MY_NETWORK ]]; then
|
|
NETRESTORE[$MY_NETWORK]="$CONTAINER,$MY_IP ${NETRESTORE[$MY_NETWORK]}"
|
|
# save container name for later rebuild
|
|
CTRESTORE[$MY_NETWORK]=$REBUILD
|
|
fi
|
|
fi
|
|
# restore user defined networks
|
|
USER_NETWORKS=$(docker inspect --format='{{range $key, $value := .NetworkSettings.Networks}}{{$key}};{{if $value.IPAMConfig}}{{if $value.IPAMConfig.IPv4Address}}{{$value.IPAMConfig.IPv4Address}}{{end}}{{if $value.IPAMConfig.IPv6Address}},{{$value.IPAMConfig.IPv6Address}}{{end}}{{end}} {{end}}' $CONTAINER)
|
|
for ROW in $USER_NETWORKS; do
|
|
ROW=(${ROW/;/ })
|
|
MY_NETWORK=${ROW[0]}
|
|
MY_IP=${ROW[1]/,/;}
|
|
if [[ -n $MY_NETWORK && $MY_NETWORK != $MY_NETWORK ]]; then
|
|
LABEL=${MY_NETWORK//[0-9.]/}
|
|
if [[ "br bond eth" =~ $LABEL && $LABEL != ${PORT:0:-1} ]]; then
|
|
MY_NETWORK=${MY_NETWORK/$LABEL/${PORT:0:-1}}
|
|
fi
|
|
logger -t $(basename $0) "container $CONTAINER has an additional network that will be restored: $MY_NETWORK"
|
|
NETRESTORE[$MY_NETWORK]="$CONTAINER,$MY_IP ${NETRESTORE[$MY_NETWORK]}"
|
|
fi
|
|
done
|
|
done
|
|
# detach custom networks
|
|
for NIC in $NICS; do
|
|
if [[ -e $SYSTEM/${NIC/eth/br} ]]; then
|
|
NIC=${NIC/eth/br}
|
|
elif [[ -e $SYSTEM/${NIC/eth/bond} ]]; then
|
|
NIC=${NIC/eth/bond}
|
|
fi
|
|
X=${NIC//[^0-9]/}
|
|
driver ${NIC//[0-9]/}
|
|
for NETWORK in $(network $DETACH $X); do
|
|
[[ $STOCK =~ ${NETWORK%%[0-9]*} || $DOCKER_USER_NETWORKS != preserve ]] && docker network rm $NETWORK >/dev/null
|
|
done
|
|
# get existing custom networks
|
|
for NETWORK in $(network $ATTACH $X); do
|
|
if [[ $STOCK =~ ${NETWORK%%[0-9]*} ]]; then
|
|
[[ $EXCLUDE =~ "$NETWORK " || ! $ACTIVE =~ "$NETWORK " ]] && docker network rm $NETWORK >/dev/null
|
|
else
|
|
[[ $DOCKER_USER_NETWORKS != preserve ]] && docker network rm $NETWORK >/dev/null
|
|
fi
|
|
done
|
|
NETWORKS=$(network $ATTACH $X)
|
|
done
|
|
# add or remove custom network
|
|
for NETWORK in $INCLUDE; do
|
|
if [[ ! $DOCKER_CUSTOM_NETWORKS =~ "$NETWORK " ]]; then
|
|
# automatic assignment
|
|
AUTO=${NETWORK/./_}
|
|
AUTO=DOCKER_AUTO_${AUTO^^}
|
|
if [[ ${!AUTO} == no ]]; then
|
|
[[ $NETWORKS =~ "$NETWORK " ]] && docker network rm $NETWORK >/dev/null
|
|
continue
|
|
fi
|
|
# add auto defined networks
|
|
SUBNET=; GATEWAY=; SERVER=; RANGE=;
|
|
IPV4=$(ip -4 addr show $NETWORK|awk '/^ +inet /{print $2;exit}')
|
|
if [[ -z $IPV4 ]]; then
|
|
# wait for DHCP to assign IPv4 address
|
|
KEY=${NETWORK%.*}
|
|
KEY=${KEY/br/eth}
|
|
KEY=${KEY/bond/eth}
|
|
# get network configuration section
|
|
sed -n "/^\[$KEY\]$/,/^\[/p" $INI >$TMP
|
|
VLAN=${NETWORK#*.}
|
|
if [[ -z $VLAN ]]; then
|
|
USE_DHCP="USE_DHCP:0"
|
|
else
|
|
VLAN=$(grep -Pom1 "^VLANID:\d+=\"$VLAN\"$" $TMP)
|
|
VLAN=${VLAN%=*}
|
|
USE_DHCP=${VLAN/VLANID/USE_DHCP}
|
|
fi
|
|
# only wait when DHCP is used (max 10 seconds)
|
|
[[ $(grep -Pom1 "^$USE_DHCP=.\K[^\"]+" $TMP) == yes ]] && LOOP=10 || LOOP=0
|
|
while [[ -z $IPV4 && $LOOP -gt 0 ]]; do
|
|
sleep 1
|
|
IPV4=$(ip -4 addr show $NETWORK|awk '/^ +inet /{print $2;exit}')
|
|
((LOOP--))
|
|
done
|
|
fi
|
|
if [[ -n $IPV4 ]]; then
|
|
SUBNET=$(ip -4 route show dev $NETWORK $IPV4|awk '{print $1;exit}')
|
|
SERVER=${IPV4%/*}
|
|
DHCP=${NETWORK/./_}
|
|
DHCP=DOCKER_DHCP_${DHCP^^}
|
|
RANGE=${!DHCP}
|
|
GATEWAY=$(ip -4 route show dev $NETWORK default|awk '{print $3;exit}')
|
|
fi
|
|
SUBNET6=; GATEWAY6=; SERVER6=; RANGE6=;
|
|
IPV6=$(min6 $(max6 $(wipe $(ip -br -6 addr show $NETWORK scope global|awk '{$2="";print;exit}'))|sort|head -1))
|
|
if [[ -n $IPV6 ]]; then
|
|
SUBNET6=$(ip -6 route show dev $NETWORK $IPV6|awk '{print $1;exit}')
|
|
SERVER6=${IPV6%/*}
|
|
DHCP6=${NETWORK/./_}
|
|
DHCP6=DOCKER_DHCP6_${DHCP6^^}
|
|
RANGE6=${!DHCP6}
|
|
GATEWAY6=$(ip -6 route show dev $NETWORK default|awk '{print $3;exit}')
|
|
# replace link local address for first address in subnet
|
|
[[ ${GATEWAY6:0:4} == fe80 ]] && GATEWAY6=${SUBNET6%%/*}1
|
|
fi
|
|
else
|
|
# add user defined networks
|
|
IPV4=; IPV6=;
|
|
DEVICE=${NETWORK/./_}
|
|
DEVICE=${DEVICE^^}
|
|
SUBNET=DOCKER_SUBNET_$DEVICE
|
|
SUBNET=${!SUBNET}
|
|
GATEWAY=DOCKER_GATEWAY_$DEVICE
|
|
GATEWAY=${!GATEWAY}
|
|
SERVER=;
|
|
RANGE=DOCKER_RANGE_$DEVICE
|
|
RANGE=${!RANGE}
|
|
SUBNET6=DOCKER_SUBNET6_$DEVICE
|
|
SUBNET6=${!SUBNET6}
|
|
GATEWAY6=DOCKER_GATEWAY6_$DEVICE
|
|
GATEWAY6=${!GATEWAY6}
|
|
SERVER6=;
|
|
RANGE6=DOCKER_RANGE6_$DEVICE
|
|
RANGE6=${!RANGE6}
|
|
fi
|
|
# custom network already existing and changed?
|
|
if [[ $NETWORKS =~ "$NETWORK " ]]; then
|
|
UPDATE=;
|
|
SUBNETS=($(docker network inspect --format='{{range .IPAM.Config}}{{.Subnet}} {{end}}' $NETWORK 2>/dev/null))
|
|
RANGES=($(docker network inspect --format='{{range .IPAM.Config}}{{.IPRange}} {{end}}' $NETWORK 2>/dev/null))
|
|
GATEWAYS=($(docker network inspect --format='{{range .IPAM.Config}}{{.Gateway}} {{end}}' $NETWORK 2>/dev/null))
|
|
SERVERS=($(docker network inspect --format='{{range .IPAM.Config}}{{range $IPAddr := .AuxiliaryAddresses}}{{$IPAddr}}{{end}} {{end}}' $NETWORK 2>/dev/null))
|
|
# distribute ipv4 and ipv6 assignments
|
|
[[ ${SUBNETS[0]} =~ '.' ]] && SUBNET0=${SUBNETS[0]} || SUBNET1=${SUBNETS[0]}
|
|
[[ -n ${SUBNETS[1]} && ${SUBNETS[1]} =~ '.' ]] && SUBNET0=${SUBNETS[1]} || SUBNET1=${SUBNETS[1]}
|
|
[[ ${RANGES[0]} =~ '.' ]] && RANGE0=${RANGES[0]} || RANGE1=${RANGES[0]}
|
|
[[ -n ${RANGES[1]} && ${RANGES[1]} =~ '.' ]] && RANGE0=${RANGES[1]} || RANGE1=${RANGES[1]}
|
|
[[ ${GATEWAYS[0]} =~ '.' ]] && GATEWAY0=${GATEWAYS[0]} || GATEWAY1=${GATEWAYS[0]}
|
|
[[ -n ${GATEWAYS[1]} && ${GATEWAYS[1]} =~ '.' ]] && GATEWAY0=${GATEWAYS[1]} || GATEWAY1=${GATEWAYS[1]}
|
|
[[ ${SERVERS[0]} =~ '.' ]] && SERVER0=${SERVERS[0]} || SERVER1=${SERVERS[0]}
|
|
[[ -n ${SERVERS[1]} && ${SERVERS[1]} =~ '.' ]] && SERVER0=${SERVERS[1]} || SERVER1=${SERVERS[1]}
|
|
# check for changes
|
|
[[ $SUBNET != $SUBNET0 || $SUBNET6 != $SUBNET1 ]] && UPDATE=1
|
|
[[ $RANGE != $RANGE0 || $RANGE6 != $RANGE1 ]] && UPDATE=1
|
|
[[ (-n $GATEWAY && $GATEWAY != $GATEWAY0) || (-n $GATEWAY6 && $GATEWAY6 != $GATEWAY1) ]] && UPDATE=1
|
|
[[ (-n $SERVER && $SERVER != $SERVER0) || (-n $SERVER6 && $SERVER6 != $SERVER1) ]] && UPDATE=1
|
|
if [[ -z $UPDATE ]]; then
|
|
# no changes, ignore
|
|
SUBNET=; SUBNET6=;
|
|
else
|
|
# changed, remove first
|
|
docker network rm $NETWORK >/dev/null
|
|
fi
|
|
fi
|
|
# set parameters for custom network creation
|
|
N4=$SUBNET; R4=$RANGE;
|
|
N6=$SUBNET6; R6=$RANGE6;
|
|
[[ -n $SUBNET && -n $GATEWAY ]] && GATEWAY="--gateway=$GATEWAY" || GATEWAY=;
|
|
[[ -n $SUBNET && -n $SERVER ]] && SERVER="--aux-address=server=$SERVER" || SERVER=;
|
|
[[ -n $SUBNET && -n $RANGE ]] && RANGE="--ip-range=$RANGE" || RANGE=;
|
|
[[ -n $SUBNET ]] && SUBNET="--subnet=$SUBNET"
|
|
[[ -n $SUBNET6 && -n $GATEWAY6 ]] && GATEWAY6="--gateway=$GATEWAY6" || GATEWAY6=;
|
|
[[ -n $SUBNET6 && -n $SERVER6 ]] && SERVER6="--aux-address=server6=$SERVER6" || SERVER6=;
|
|
[[ -n $SUBNET6 && -n $RANGE6 ]] && RANGE6="--ip-range=$RANGE6" || RANGE6=;
|
|
[[ -n $SUBNET6 ]] && SUBNET6="--ipv6 --subnet=$SUBNET6"
|
|
if [[ -n $SUBNET || -n $SUBNET6 ]]; then
|
|
SHIM_BASE=;
|
|
# hack to let containers talk to host
|
|
if [[ $DOCKER_ALLOW_ACCESS == yes && -n $IPV4 ]]; then
|
|
BASE=${N4%/*}
|
|
MASK=${N4#*/}
|
|
IP=(${BASE//./ })
|
|
IP=$(((${IP[0]}<<24)+(${IP[1]}<<16)+(${IP[2]}<<8)+${IP[3]}+2**(31-MASK)))
|
|
SHIM_BASE=$BASE/$((MASK+1))
|
|
SHIM_HIGH=$((IP>>24)).$((IP>>16&255)).$((IP>>8&255)).$((IP&255))/$((MASK+1))
|
|
if [[ -z $SERVER ]]; then
|
|
[[ -n $RANGE ]] && SERVER="--aux-address=server=${R4%/*}" || SERVER="--aux-address=server=${SHIM_HIGH%/*}"
|
|
fi
|
|
fi
|
|
TYPE=${NETWORK//[0-9.]/}
|
|
driver $TYPE
|
|
[[ $TYPE == br ]] && VHOST=$NETWORK || VHOST=vhost${NETWORK//[^0-9.]/}
|
|
docker network create -d $ATTACH $SUBNET $GATEWAY $SERVER $RANGE $SUBNET6 $GATEWAY6 $SERVER6 $RANGE6 -o parent=$VHOST $NETWORK | xargs docker network inspect -f "created network $ATTACH {{.Name}} with subnets: {{range .IPAM.Config}}{{.Subnet}}; {{end}}" 2>/dev/null | logger -t $(basename $0)
|
|
# connect containers to this new network
|
|
for CONNECT in ${NETRESTORE[$NETWORK]}; do
|
|
CONTAINER=${CONNECT%,*}
|
|
MY_TT=${CONNECT#*,}
|
|
MY_IP=
|
|
for IP in ${MY_TT//;/ }; do
|
|
[[ $IP =~ ':' ]] && MY_IP="$MY_IP --ip6 $IP" || MY_IP="$MY_IP --ip $IP"
|
|
done
|
|
logger -t $(basename $0) "connecting $CONTAINER to network $NETWORK"
|
|
docker network connect $MY_IP $NETWORK $CONTAINER >/dev/null
|
|
if [[ -n ${CTRESTORE[$NETWORK]} ]]; then
|
|
# rebuild the container to use changed network
|
|
logger -t $(basename $0) "rebuild container $CONTAINER"
|
|
/usr/local/emhttp/plugins/dynamix.docker.manager/scripts/rebuild_container $CONTAINER
|
|
fi
|
|
done
|
|
LINK=shim-$NETWORK
|
|
# hack to let containers talk to host
|
|
if [[ $DOCKER_ALLOW_ACCESS == yes && -n $IPV4 ]]; then
|
|
if [[ ! -e $SYSTEM/$LINK && -n $SHIM_BASE ]]; then
|
|
# create shim network
|
|
ip link add $LINK link $NETWORK type $ATTACH mode $MODE
|
|
# disable ipv6 on shim network
|
|
echo 1 > $CONF6/$LINK/disable_ipv6
|
|
ip link set $LINK up
|
|
logger -t $(basename $0) "created network $LINK for host access"
|
|
fi
|
|
[[ -n $SHIM_BASE ]] && shim_network ${SERVER##*=} $SHIM_BASE $SHIM_HIGH
|
|
elif [[ -e $SYSTEM/$LINK ]]; then
|
|
# remove shim network
|
|
ip -4 addr flush dev $LINK
|
|
ip -4 route flush dev $LINK
|
|
ip link set $LINK down
|
|
ip link del $LINK
|
|
fi
|
|
fi
|
|
done
|
|
}
|
|
|
|
# shim network creation
|
|
shim_network(){
|
|
ip -4 addr flush dev $LINK
|
|
ip -4 addr add $1 dev $LINK
|
|
ip -4 route flush dev $LINK
|
|
ip -4 route add $2 src $1 dev $LINK
|
|
ip -4 route add $3 src $1 dev $LINK
|
|
DEFAULT=($(ip -4 route show dev $NETWORK default|awk '{print $3,$5}'))
|
|
if [[ -n $DEFAULT ]]; then
|
|
if [[ -z ${DEFAULT[1]} ]]; then
|
|
METRIC=1
|
|
METRICS=$(ip -4 route show default|grep -Po 'metric \K\d+')
|
|
while [[ " $METRICS " =~ " $METRIC " ]]; do ((METRIC++)); done
|
|
ip -4 route del default via $DEFAULT dev $NETWORK
|
|
ip -4 route add default via $DEFAULT dev $NETWORK metric $METRIC
|
|
fi
|
|
ip -4 route add default via $DEFAULT dev $LINK metric 0
|
|
fi
|
|
}
|
|
|
|
# Remove custom networks
|
|
stop_network(){
|
|
for NIC in $NICS; do
|
|
if [[ -e $SYSTEM/${NIC/eth/br} ]]; then
|
|
NIC=${NIC/eth/br}
|
|
elif [[ -e $SYSTEM/${NIC/eth/bond} ]]; then
|
|
NIC=${NIC/eth/bond}
|
|
fi
|
|
X=${NIC//[^0-9]/}
|
|
driver ${NIC//[0-9]/}
|
|
for NETWORK in $(network $ATTACH $X); do
|
|
[[ $STOCK =~ ${NETWORK%%[0-9]*} || $DOCKER_USER_NETWORKS != preserve ]] && docker network rm $NETWORK >/dev/null
|
|
LINK=shim-$NETWORK
|
|
if [[ -e $SYSTEM/$LINK ]]; then
|
|
ip -4 addr flush dev $LINK
|
|
ip -4 route flush dev $LINK
|
|
ip link set $LINK down
|
|
ip link del $LINK
|
|
fi
|
|
done
|
|
done
|
|
}
|
|
|
|
# Start containers
|
|
start_containers(){
|
|
local CONTAINER
|
|
if ! is_docker_running; then return 1; fi
|
|
if [[ -f $DOCKER_ROOT/unraid-autostart ]]; then
|
|
while read -r CONTAINER; do
|
|
CONTAINER=($CONTAINER)
|
|
WAIT=${CONTAINER[1]}
|
|
if container_exist $CONTAINER && ! is_container_running $CONTAINER && container_paths_exist $CONTAINER; then
|
|
OUT=$(docker start $CONTAINER 2>&1)
|
|
if [[ $OUT =~ "Error:" ]]; then
|
|
echo "$CONTAINER: $OUT" |& logger -t $(basename $0) &
|
|
else
|
|
add_route $CONTAINER
|
|
echo "$CONTAINER: started succesfully!" |& logger -t $(basename $0) &
|
|
if [[ $WAIT -gt 0 ]]; then
|
|
echo "$CONTAINER: wait $WAIT seconds" |& logger -t $(basename $0) &
|
|
sleep $WAIT
|
|
fi
|
|
fi
|
|
fi
|
|
done <$DOCKER_ROOT/unraid-autostart
|
|
fi
|
|
}
|
|
|
|
# Stop containers
|
|
stop_containers(){
|
|
[[ ! -z $(running_containers) ]] && docker stop $(running_containers) >/dev/null
|
|
}
|
|
|
|
# Start docker
|
|
start_docker(){
|
|
if is_docker_running; then
|
|
echo "$DOCKER is already running"
|
|
return 1
|
|
fi
|
|
if ! mountpoint $DOCKER_ROOT &>/dev/null; then
|
|
echo "no image mounted at $DOCKER_ROOT"
|
|
exit 1
|
|
fi
|
|
echo "starting $DOCKERD ..."
|
|
if [[ -x $DOCKER ]]; then
|
|
# If there is an old PID file (no docker running), clean it up:
|
|
if [[ -r $DOCKER_PIDFILE ]]; then
|
|
if ! is_docker_running; then
|
|
echo "Cleaning up old $DOCKER_PIDFILE."
|
|
rm -f $DOCKER_PIDFILE
|
|
fi
|
|
fi
|
|
nohup $UNSHARE --propagation slave -- $DOCKER -p $DOCKER_PIDFILE $DOCKER_OPTS >>$DOCKER_LOG 2>&1 &
|
|
fi
|
|
}
|
|
|
|
# Stop docker
|
|
stop_docker(){
|
|
echo "stopping $DOCKERD ..."
|
|
# If there is no PID file, ignore this request...
|
|
if [[ -r $DOCKER_PIDFILE ]]; then
|
|
kill $(docker_pid) 2>/dev/null
|
|
# must ensure daemon has exited
|
|
for n in {1..15}; do
|
|
sleep 1
|
|
if [[ $(ps -p $(docker_pid) -o comm= 2>/dev/null) != $DOCKERD ]]; then
|
|
rm -f $DOCKER_PIDFILE
|
|
# tear down the bridge
|
|
if ip link show docker0 >/dev/null 2>&1; then
|
|
ip link set docker0 down
|
|
ip link del docker0
|
|
fi
|
|
return
|
|
fi
|
|
echo "waiting for docker to die ..."
|
|
done
|
|
echo "docker will not die!"
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
case "$1" in
|
|
start)
|
|
start_docker
|
|
start_network
|
|
start_containers 1>/dev/null 2>&1 &
|
|
disown
|
|
;;
|
|
stop)
|
|
stop_containers
|
|
stop_network
|
|
stop_docker
|
|
;;
|
|
force_stop)
|
|
stop_docker
|
|
;;
|
|
restart)
|
|
stop_containers
|
|
stop_network
|
|
stop_docker
|
|
sleep 1
|
|
start_docker
|
|
start_network
|
|
start_containers 1>/dev/null 2>&1 &
|
|
disown
|
|
;;
|
|
status)
|
|
if is_docker_running; then
|
|
echo "status of $DOCKERD: running"
|
|
echo "running containers:" $(running_containers)
|
|
else
|
|
echo "status of $DOCKERD: stopped"
|
|
exit 1
|
|
fi
|
|
;;
|
|
*)
|
|
echo "usage $0 start|stop|force_stop|restart|status"
|
|
esac
|
|
exit 0
|