Tools: unitc Docker mode.

Introduces a new remote host scheme docker:// that specifies a local
container ID. By default, the control socket is assumed to be in the default
location, as per the Docker Official Images for Unit. If not, the path to
the control socket can be appended to the container ID.
This commit is contained in:
Liam Crilly
2023-10-16 10:32:19 +01:00
parent e78ada0140
commit 43f140dfd3
2 changed files with 43 additions and 21 deletions

View File

@@ -55,10 +55,9 @@ The error log is monitored; when changes occur, new log entries are shown.
|---------|-| |---------|-|
| `-l` \| `--nolog` | Do not monitor the error log after configuration changes. | `-l` \| `--nolog` | Do not monitor the error log after configuration changes.
#### Examples #### Local Examples
```shell ```shell
unitc /config unitc /config
unitc /control/applications/my_app/restart
unitc /config < unitconf.json unitc /config < unitconf.json
echo '{"*:8080": {"pass": "routes"}}' | unitc /config/listeners echo '{"*:8080": {"pass": "routes"}}' | unitc /config/listeners
unitc /config/applications/my_app DELETE unitc /config/applications/my_app DELETE
@@ -68,10 +67,12 @@ unitc /certificates/bundle cert.pem key.pem
### Remote Configuration ### Remote Configuration
For remote instances of NGINX Unit, the control socket on the remote host can For remote instances of NGINX Unit, the control socket on the remote host can
be set with the `$UNIT_CTRL` environment variable. The remote control socket be set with the `$UNIT_CTRL` environment variable. The remote control socket
can be accessed over TCP or SSH, depending on the type of control socket: can be accessed over TCP, SSH, or Docker containers on the host, depending on
the type of control socket:
* `ssh://[user@]remote_host[:ssh_port]/path/to/control.socket` * `ssh://[user@]remote_host[:ssh_port]/path/to/control.socket`
* `http://remote_host:unit_control_port` * `http://remote_host:unit_control_port`
* `docker://container_ID[/path/to/control.socket]`
> **Note:** SSH is recommended for remote confguration. Consider the > **Note:** SSH is recommended for remote confguration. Consider the
> [security implications](https://unit.nginx.org/howto/security/#secure-socket-and-state) > [security implications](https://unit.nginx.org/howto/security/#secure-socket-and-state)
@@ -81,8 +82,9 @@ can be accessed over TCP or SSH, depending on the type of control socket:
|---------|-| |---------|-|
| `ssh://…` | Specify the remote Unix control socket on the command line. | `ssh://…` | Specify the remote Unix control socket on the command line.
| `http://…`*URI* | For remote TCP control sockets, the URI may include the protocol, hostname, and port. | `http://…`*URI* | For remote TCP control sockets, the URI may include the protocol, hostname, and port.
| `docker://…` | Specify the local container ID/name. The default Unix control socket can be overridden.
#### Examples #### Remote Examples
```shell ```shell
unitc http://192.168.0.1:8080/status unitc http://192.168.0.1:8080/status
UNIT_CTRL=http://192.168.0.1:8080 unitc /status UNIT_CTRL=http://192.168.0.1:8080 unitc /status
@@ -93,4 +95,12 @@ cat catchall_route.json | unitc POST /config/routes
echo '{"match":{"uri":"/wp-admin/*"},"action":{"return":403}}' | unitc INSERT /config/routes echo '{"match":{"uri":"/wp-admin/*"},"action":{"return":403}}' | unitc INSERT /config/routes
``` ```
#### Docker Examples
```shell
unitc docker://d43251184c54 /config
echo '{"http": {"log_route": true}}' | unitc docker://d43251184c54 /settings
unitc docker://f4f3d9e918e6/root/unit.sock /control/applications/my_app/restart
UNIT_CTRL=docker://4d0431488982 unitc /status/requests/total
```
--- ---

View File

@@ -12,7 +12,7 @@ NOLOG=0
QUIET=0 QUIET=0
CONVERT=0 CONVERT=0
URI="" URI=""
SSH_CMD="" RPC_CMD=""
METHOD=PUT METHOD=PUT
CONF_FILES=() CONF_FILES=()
@@ -82,6 +82,9 @@ while [ $# -gt 0 ]; do
elif [ "${1:0:6}" = "ssh://" ]; then elif [ "${1:0:6}" = "ssh://" ]; then
UNIT_CTRL=$1 UNIT_CTRL=$1
shift shift
elif [ "${1:0:9}" = "docker://" ]; then
UNIT_CTRL=$1
shift
else else
echo "${0##*/}: ERROR: Invalid option ($1)" echo "${0##*/}: ERROR: Invalid option ($1)"
exit 1 exit 1
@@ -115,9 +118,10 @@ Local options
Remote options Remote options
ssh://[user@]remote_host[:port]/path/to/control.socket # Remote Unix socket ssh://[user@]remote_host[:port]/path/to/control.socket # Remote Unix socket
http://remote_host:port/URI # Remote TCP socket http://remote_host:port/URI # Remote TCP socket
docker://container_ID[/non-default/control.socket] # Container on host
A remote Unit control socket may also be defined with the \$UNIT_CTRL A remote Unit instance may also be defined with the \$UNIT_CTRL environment
environment variable as http://remote_host:port -OR- ssh://… (as above) variable as http://remote_host:port or ssh://… or docker://… (as above).
__EOF__ __EOF__
exit 1 exit 1
@@ -133,8 +137,16 @@ if [ "$UNIT_CTRL" = "" ]; then
fi fi
elif [ "${UNIT_CTRL:0:6}" = "ssh://" ]; then elif [ "${UNIT_CTRL:0:6}" = "ssh://" ]; then
REMOTE=1 REMOTE=1
SSH_CMD="ssh $(echo $UNIT_CTRL | cut -f1-3 -d/)" RPC_CMD="ssh $(echo $UNIT_CTRL | cut -f1-3 -d/)"
UNIT_CTRL="--unix-socket /$(echo $UNIT_CTRL | cut -f4- -d/) _" UNIT_CTRL="--unix-socket /$(echo $UNIT_CTRL | cut -f4- -d/) _"
elif [ "${UNIT_CTRL:0:9}" = "docker://" ]; then
RPC_CMD="docker exec -i $(echo $UNIT_CTRL | cut -f3 -d/)"
DOCKSOCK=/$(echo "$UNIT_CTRL" | cut -f4- -d/)
if [ "$DOCKSOCK" = "/" ]; then
DOCKSOCK="/var/run/control.unit.sock" # Use default location if no path
fi
UNIT_CTRL="--unix-socket $DOCKSOCK _"
REMOTE=1
elif [ "${URI:0:1}" = "/" ]; then elif [ "${URI:0:1}" = "/" ]; then
REMOTE=1 REMOTE=1
fi fi
@@ -241,11 +253,11 @@ fi
# #
if [ -t 0 ] && [ ${#CONF_FILES[@]} -eq 0 ]; then if [ -t 0 ] && [ ${#CONF_FILES[@]} -eq 0 ]; then
if [ "$METHOD" = "DELETE" ]; then if [ "$METHOD" = "DELETE" ]; then
$SSH_CMD curl -X $METHOD $UNIT_CTRL$URI 2> /tmp/${0##*/}.$$ | $OUTPUT $RPC_CMD curl -X $METHOD $UNIT_CTRL$URI 2> /tmp/${0##*/}.$$ | $OUTPUT
elif [ "$METHOD" = "EDIT" ]; then elif [ "$METHOD" = "EDIT" ]; then
EDITOR=$(test "$EDITOR" && printf '%s' "$EDITOR" || command -v editor || command -v vim || echo vi) EDITOR=$(test "$EDITOR" && printf '%s' "$EDITOR" || command -v editor || command -v vim || echo vi)
EDIT_FILENAME=/tmp/${0##*/}.$$${URI//\//_} EDIT_FILENAME=/tmp/${0##*/}.$$${URI//\//_}
$SSH_CMD curl -fsS $UNIT_CTRL$URI > $EDIT_FILENAME || exit 2 $RPC_CMD curl -fsS $UNIT_CTRL$URI > $EDIT_FILENAME || exit 2
if [ "${URI:0:12}" = "/js_modules/" ]; then if [ "${URI:0:12}" = "/js_modules/" ]; then
if ! hash jq 2> /dev/null; then if ! hash jq 2> /dev/null; then
echo "${0##*/}: ERROR: jq(1) is required to edit JavaScript modules; install at <https://stedolan.github.io/jq/>" echo "${0##*/}: ERROR: jq(1) is required to edit JavaScript modules; install at <https://stedolan.github.io/jq/>"
@@ -255,23 +267,23 @@ if [ -t 0 ] && [ ${#CONF_FILES[@]} -eq 0 ]; then
EDIT_FILE=$EDIT_FILENAME.js EDIT_FILE=$EDIT_FILENAME.js
$EDITOR $EDIT_FILENAME.js || exit 2 $EDITOR $EDIT_FILENAME.js || exit 2
# Remove the references, delete old config, push new config+reference # Remove the references, delete old config, push new config+reference
$SSH_CMD curl -fsS $UNIT_CTRL/config/settings/js_module > /tmp/${0##*/}.$$_js_module && \ $RPC_CMD curl -fsS $UNIT_CTRL/config/settings/js_module > /tmp/${0##*/}.$$_js_module && \
$SSH_CMD curl -X DELETE $UNIT_CTRL/config/settings/js_module && \ $RPC_CMD curl -X DELETE $UNIT_CTRL/config/settings/js_module && \
$SSH_CMD curl -fsSX DELETE $UNIT_CTRL$URI 2> /tmp/${0##*/}.$$ && \ $RPC_CMD curl -fsSX DELETE $UNIT_CTRL$URI 2> /tmp/${0##*/}.$$ && \
printf "%s" "$(< $EDIT_FILENAME.js)" | $SSH_CMD curl -fX PUT --data-binary @- $UNIT_CTRL$URI 2> /tmp/${0##*/}.$$ && \ printf "%s" "$(< $EDIT_FILENAME.js)" | $RPC_CMD curl -fX PUT --data-binary @- $UNIT_CTRL$URI 2> /tmp/${0##*/}.$$ && \
$SSH_CMD curl -X PUT --data-binary @/tmp/${0##*/}.$$_js_module $UNIT_CTRL/config/settings/js_module 2> /tmp/${0##*/}.$$ $RPC_CMD curl -X PUT --data-binary @/tmp/${0##*/}.$$_js_module $UNIT_CTRL/config/settings/js_module 2> /tmp/${0##*/}.$$
elif [ $CONVERT -eq 1 ]; then elif [ $CONVERT -eq 1 ]; then
$CONVERT_FROM_JSON < $EDIT_FILENAME > $EDIT_FILENAME.yaml $CONVERT_FROM_JSON < $EDIT_FILENAME > $EDIT_FILENAME.yaml
$EDITOR $EDIT_FILENAME.yaml || exit 2 $EDITOR $EDIT_FILENAME.yaml || exit 2
$CONVERT_TO_JSON < $EDIT_FILENAME.yaml | $SSH_CMD curl -X PUT --data-binary @- $UNIT_CTRL$URI 2> /tmp/${0##*/}.$$ | $OUTPUT $CONVERT_TO_JSON < $EDIT_FILENAME.yaml | $RPC_CMD curl -X PUT --data-binary @- $UNIT_CTRL$URI 2> /tmp/${0##*/}.$$ | $OUTPUT
else else
tr -d '\r' < $EDIT_FILENAME > $EDIT_FILENAME.json # Remove carriage-return from newlines tr -d '\r' < $EDIT_FILENAME > $EDIT_FILENAME.json # Remove carriage-return from newlines
$EDITOR $EDIT_FILENAME.json || exit 2 $EDITOR $EDIT_FILENAME.json || exit 2
$SSH_CMD curl -X PUT --data-binary @$EDIT_FILENAME.json $UNIT_CTRL$URI 2> /tmp/${0##*/}.$$ | $OUTPUT $RPC_CMD curl -X PUT --data-binary @$EDIT_FILENAME.json $UNIT_CTRL$URI 2> /tmp/${0##*/}.$$ | $OUTPUT
fi fi
else else
SHOW_LOG=$(echo $URI | grep -c ^/control/) SHOW_LOG=$(echo $URI | grep -c ^/control/)
$SSH_CMD curl $UNIT_CTRL$URI 2> /tmp/${0##*/}.$$ | $OUTPUT $RPC_CMD curl $UNIT_CTRL$URI 2> /tmp/${0##*/}.$$ | $OUTPUT
fi fi
else else
if [ "$METHOD" = "INSERT" ]; then if [ "$METHOD" = "INSERT" ]; then
@@ -281,9 +293,9 @@ else
fi fi
NEW_ELEMENT=$(cat ${CONF_FILES[@]}) NEW_ELEMENT=$(cat ${CONF_FILES[@]})
echo $NEW_ELEMENT | jq > /dev/null || exit $? # Test the input is valid JSON before proceeding echo $NEW_ELEMENT | jq > /dev/null || exit $? # Test the input is valid JSON before proceeding
OLD_ARRAY=$($SSH_CMD curl -s $UNIT_CTRL$URI) OLD_ARRAY=$($RPC_CMD curl -s $UNIT_CTRL$URI)
if [ "$(echo $OLD_ARRAY | jq -r type)" = "array" ]; then if [ "$(echo $OLD_ARRAY | jq -r type)" = "array" ]; then
echo $OLD_ARRAY | jq ". |= [$NEW_ELEMENT] + ." | $SSH_CMD curl -X PUT --data-binary @- $UNIT_CTRL$URI 2> /tmp/${0##*/}.$$ | $OUTPUT echo $OLD_ARRAY | jq ". |= [$NEW_ELEMENT] + ." | $RPC_CMD curl -X PUT --data-binary @- $UNIT_CTRL$URI 2> /tmp/${0##*/}.$$ | $OUTPUT
else else
echo "${0##*/}: ERROR: the INSERT method expects an array" echo "${0##*/}: ERROR: the INSERT method expects an array"
exit 3 exit 3
@@ -293,7 +305,7 @@ else
cat ${CONF_FILES[@]} | $CONVERT_TO_JSON > /tmp/${0##*/}.$$_json cat ${CONF_FILES[@]} | $CONVERT_TO_JSON > /tmp/${0##*/}.$$_json
CONF_FILES=(/tmp/${0##*/}.$$_json) CONF_FILES=(/tmp/${0##*/}.$$_json)
fi fi
cat ${CONF_FILES[@]} | $SSH_CMD curl -X $METHOD --data-binary @- $UNIT_CTRL$URI 2> /tmp/${0##*/}.$$ | $OUTPUT cat ${CONF_FILES[@]} | $RPC_CMD curl -X $METHOD --data-binary @- $UNIT_CTRL$URI 2> /tmp/${0##*/}.$$ | $OUTPUT
fi fi
fi fi