diff options
author | Ippolitov Igor <iippolitov@nginx.com> | 2023-10-19 12:50:39 +0100 |
---|---|---|
committer | Ippolitov Igor <iippolitov@nginx.com> | 2023-10-19 12:50:39 +0100 |
commit | c43629880472bba8d389dfb0b7ae6d883b0ba499 (patch) | |
tree | acecdcb36cfb85fac3d8cdbfbe473c26ac2e2686 /tools | |
parent | 8c4425ccb9a413e8d0506e0254f0e84bd89a32a6 (diff) | |
parent | fb33ec86a3b6ca6a844dfa6980bb9e083094abec (diff) | |
download | unit-1.31.1-1.tar.gz unit-1.31.1-1.tar.bz2 |
Merged with the default branch.1.31.1-1
Diffstat (limited to 'tools')
-rw-r--r-- | tools/README.md | 22 | ||||
-rwxr-xr-x | tools/unitc | 104 |
2 files changed, 98 insertions, 28 deletions
diff --git a/tools/README.md b/tools/README.md index 1a631e10..883bc107 100644 --- a/tools/README.md +++ b/tools/README.md @@ -37,12 +37,16 @@ web page with NGINX Unit. | _HTTP method_ | It is usually not required to specify a HTTP method. `GET` is used to read the configuration. `PUT` is used when making configuration changes unless a specific method is provided. | `edit` | Opens **URI** in the default editor for interactive configuration. The [jq](https://stedolan.github.io/jq/) tool is required for this option. | `INSERT` | A _virtual_ HTTP method that prepends data when the URI specifies an existing array. The [jq](https://stedolan.github.io/jq/) tool is required for this option. +| `-f` \| `--format YAML` | Convert configuration data to/from YAML format. The [yq](https://github.com/mikefarah/yq) tool is required for this option. | `-q` \| `--quiet` | No output to stdout. Options are case insensitive and can appear in any order. For example, a redundant part of the configuration can be identified by its URI, and followed by `delete` in a subsequent command. +Options may be combined. For example, `edit -f yaml` will open the +configuration URI in a text editor, in YAML format. + ### Local Configuration For local instances of Unit, the control socket is automatically detected. The error log is monitored; when changes occur, new log entries are shown. @@ -51,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. -#### Examples +#### Local Examples ```shell unitc /config -unitc /control/applications/my_app/restart unitc /config < unitconf.json echo '{"*:8080": {"pass": "routes"}}' | unitc /config/listeners unitc /config/applications/my_app DELETE @@ -64,10 +67,12 @@ unitc /certificates/bundle cert.pem key.pem ### Remote Configuration 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 -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` * `http://remote_host:unit_control_port` + * `docker://container_ID[/path/to/control.socket]` > **Note:** SSH is recommended for remote confguration. Consider the > [security implications](https://unit.nginx.org/howto/security/#secure-socket-and-state) @@ -77,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. | `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 unitc http://192.168.0.1:8080/status UNIT_CTRL=http://192.168.0.1:8080 unitc /status @@ -89,4 +95,12 @@ cat catchall_route.json | unitc POST /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 +``` + --- diff --git a/tools/unitc b/tools/unitc index 877e11d4..4ab5f663 100755 --- a/tools/unitc +++ b/tools/unitc @@ -10,14 +10,39 @@ REMOTE=0 SHOW_LOG=1 NOLOG=0 QUIET=0 +CONVERT=0 URI="" -SSH_CMD="" +RPC_CMD="" METHOD=PUT CONF_FILES=() while [ $# -gt 0 ]; do OPTION=$(echo $1 | tr '[a-z]' '[A-Z]') case $OPTION in + "-F" | "--FORMAT") + case $(echo $2 | tr '[a-z]' '[A-Z]') in + "YAML") + CONVERT=1 + if hash yq 2> /dev/null; then + CONVERT_TO_JSON="yq eval -P --output-format=json" + CONVERT_FROM_JSON="yq eval -P --output-format=yaml" + else + echo "${0##*/}: ERROR: yq(1) is required to use YAML format; install at <https://github.com/mikefarah/yq#install>" + exit 1 + fi + ;; + "") + echo "${0##*/}: ERROR: Must specify configuration format" + exit 1 + ;; + *) + echo "${0##*/}: ERROR: Invalid format ($2)" + exit 1 + ;; + esac + shift; shift + ;; + "-H" | "--HELP") shift ;; @@ -45,15 +70,25 @@ while [ $# -gt 0 ]; do *) if [ -f $1 ] && [ -r $1 ]; then CONF_FILES+=($1) + if [ "${1##*.}" = "yaml" ]; then + echo "${0##*/}: INFO: converting $1 to JSON" + shift; set -- "--format" "yaml" "$@" # Apply the command line option + else + shift + fi elif [ "${1:0:1}" = "/" ] || [ "${1:0:4}" = "http" ] && [ "$URI" = "" ]; then URI=$1 + shift elif [ "${1:0:6}" = "ssh://" ]; then UNIT_CTRL=$1 + shift + elif [ "${1:0:9}" = "docker://" ]; then + UNIT_CTRL=$1 + shift else echo "${0##*/}: ERROR: Invalid option ($1)" exit 1 fi - shift ;; esac done @@ -67,23 +102,26 @@ USAGE: ${0##*/} [options] URI • URI is for Unit's control API target, e.g. /config • A local Unit control socket is detected unless a remote one is specified. • Configuration data is read from stdin. +• All options are case-insensitive (excluding filenames and URIs). General options - filename … # Read configuration data from files instead of stdin - HTTP method # Default=GET, or PUT with config data (case-insensitive) - EDIT # Opens the URI contents in \$EDITOR - INSERT # Virtual HTTP method to prepend data to an existing array - -q | --quiet # No output to stdout + filename … # Read configuration data from files instead of stdin + HTTP method # Default=GET, or PUT when config data is present + EDIT # Opens the URI contents in \$EDITOR + INSERT # Virtual HTTP method; prepend data to an array + -f | --format YAML # Convert configuration data to/from YAML format + -q | --quiet # No output to stdout Local options - -l | --nolog # Do not monitor the error log after applying config changes + -l | --nolog # Do not monitor the Unit log file after config changes Remote options ssh://[user@]remote_host[:port]/path/to/control.socket # Remote Unix 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 - environment variable as http://remote_host:port -OR- ssh://… (as above) + A remote Unit instance may also be defined with the \$UNIT_CTRL environment + variable as http://remote_host:port or ssh://… or docker://… (as above). __EOF__ exit 1 @@ -99,8 +137,16 @@ if [ "$UNIT_CTRL" = "" ]; then fi elif [ "${UNIT_CTRL:0:6}" = "ssh://" ]; then 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/) _" +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 REMOTE=1 fi @@ -186,7 +232,9 @@ fi # Choose presentation style # if [ $QUIET -eq 1 ]; then - OUTPUT="head -c 0" # Equivalent to >/dev/null + OUTPUT="tail -c 0" # Equivalent to >/dev/null +elif [ $CONVERT -eq 1 ]; then + OUTPUT=$CONVERT_FROM_JSON elif hash jq 2> /dev/null; then OUTPUT="jq" else @@ -205,11 +253,11 @@ fi # if [ -t 0 ] && [ ${#CONF_FILES[@]} -eq 0 ]; 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 EDITOR=$(test "$EDITOR" && printf '%s' "$EDITOR" || command -v editor || command -v vim || echo vi) 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 ! hash jq 2> /dev/null; then echo "${0##*/}: ERROR: jq(1) is required to edit JavaScript modules; install at <https://stedolan.github.io/jq/>" @@ -219,19 +267,23 @@ if [ -t 0 ] && [ ${#CONF_FILES[@]} -eq 0 ]; then EDIT_FILE=$EDIT_FILENAME.js $EDITOR $EDIT_FILENAME.js || exit 2 # Remove the references, delete old config, push new config+reference - $SSH_CMD curl -fsS $UNIT_CTRL/config/settings/js_module > /tmp/${0##*/}.$$_js_module && \ - $SSH_CMD curl -X DELETE $UNIT_CTRL/config/settings/js_module && \ - $SSH_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##*/}.$$ && \ - $SSH_CMD curl -X PUT --data-binary @/tmp/${0##*/}.$$_js_module $UNIT_CTRL/config/settings/js_module 2> /tmp/${0##*/}.$$ + $RPC_CMD curl -fsS $UNIT_CTRL/config/settings/js_module > /tmp/${0##*/}.$$_js_module && \ + $RPC_CMD curl -X DELETE $UNIT_CTRL/config/settings/js_module && \ + $RPC_CMD curl -fsSX DELETE $UNIT_CTRL$URI 2> /tmp/${0##*/}.$$ && \ + printf "%s" "$(< $EDIT_FILENAME.js)" | $RPC_CMD curl -fX PUT --data-binary @- $UNIT_CTRL$URI 2> /tmp/${0##*/}.$$ && \ + cat /tmp/${0##*/}.$$_js_module | $RPC_CMD curl -X PUT --data-binary @- $UNIT_CTRL/config/settings/js_module 2> /tmp/${0##*/}.$$ + elif [ $CONVERT -eq 1 ]; then + $CONVERT_FROM_JSON < $EDIT_FILENAME > $EDIT_FILENAME.yaml + $EDITOR $EDIT_FILENAME.yaml || exit 2 + $CONVERT_TO_JSON < $EDIT_FILENAME.yaml | $RPC_CMD curl -X PUT --data-binary @- $UNIT_CTRL$URI 2> /tmp/${0##*/}.$$ | $OUTPUT else tr -d '\r' < $EDIT_FILENAME > $EDIT_FILENAME.json # Remove carriage-return from newlines $EDITOR $EDIT_FILENAME.json || exit 2 - $SSH_CMD curl -X PUT --data-binary @$EDIT_FILENAME.json $UNIT_CTRL$URI 2> /tmp/${0##*/}.$$ | $OUTPUT + cat $EDIT_FILENAME.json | $RPC_CMD curl -X PUT --data-binary @- $UNIT_CTRL$URI 2> /tmp/${0##*/}.$$ | $OUTPUT fi else 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 else if [ "$METHOD" = "INSERT" ]; then @@ -241,15 +293,19 @@ else fi NEW_ELEMENT=$(cat ${CONF_FILES[@]}) 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 - 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 echo "${0##*/}: ERROR: the INSERT method expects an array" exit 3 fi else - cat ${CONF_FILES[@]} | $SSH_CMD curl -X $METHOD --data-binary @- $UNIT_CTRL$URI 2> /tmp/${0##*/}.$$ | $OUTPUT + if [ $CONVERT -eq 1 ]; then + cat ${CONF_FILES[@]} | $CONVERT_TO_JSON > /tmp/${0##*/}.$$_json + CONF_FILES=(/tmp/${0##*/}.$$_json) + fi + cat ${CONF_FILES[@]} | $RPC_CMD curl -X $METHOD --data-binary @- $UNIT_CTRL$URI 2> /tmp/${0##*/}.$$ | $OUTPUT fi fi |