1
0

refactor environment handling

also:
- drop "njs" directories (not actually used nor useful)
- rework jinja filters: more functions, shorter names, etc.
This commit is contained in:
Konstantin Demin 2024-07-23 00:02:19 +03:00
parent 1dda7066c9
commit a0a4531501
Signed by: krd
GPG Key ID: 4D56F87A8BA65FD0
22 changed files with 487 additions and 230 deletions

View File

@ -37,7 +37,7 @@ RUN python3 -m compileall -q -j 2 /usr/local/lib/j2cfg/
RUN libpython="${PYTHON_SITE_PACKAGES%/*}" ; \ RUN libpython="${PYTHON_SITE_PACKAGES%/*}" ; \
find "${libpython}/" -mindepth 1 -maxdepth 1 -printf '%P\0' \ find "${libpython}/" -mindepth 1 -maxdepth 1 -printf '%P\0' \
| sed -zEn \ | sed -zEn \
-e '/^(asyncio|collections|concurrent|encodings|html|importlib|json|logging|multiprocessing|re|urllib|xml)$/p' \ -e '/^(collections|importlib|json|re)$/p' \
| sort -zV \ | sort -zV \
| env -C "${libpython}" xargs -0r \ | env -C "${libpython}" xargs -0r \
python3 -m compileall -q -j 2 ; \ python3 -m compileall -q -j 2 ; \
@ -141,7 +141,7 @@ RUN install -d -o angie -g angie -m 03777 /angie /run/angie ; \
ln -sv /run/angie/lock lock.d ; \ ln -sv /run/angie/lock lock.d ; \
ln -sv ${ANGIE_MODULES_DIR} modules.dist ; \ ln -sv ${ANGIE_MODULES_DIR} modules.dist ; \
## hyper-modular paths: ## hyper-modular paths:
data='conf j2cfg mod modules njs site snip static' ; \ data='conf j2cfg mod modules site snip static' ; \
vardata='cache lib log' ; \ vardata='cache lib log' ; \
for n in ${data} ; do \ for n in ${data} ; do \
for d in "$n" "$n.dist" ; do \ for d in "$n" "$n.dist" ; do \

View File

@ -1,19 +0,0 @@
{#- prologue -#}
{%- set preserve_env = ( j2cfg.core_preserve_environment or [] )|env_any_to_str_list -%}
{%- set have_tz = preserve_env|is_str_list_re_match('TZ(=|$)') -%}
{%- set have_malloc_arena = preserve_env|is_str_list_re_match('MALLOC_ARENA_MAX(=|$)') -%}
{#- main part -#}
{%- if not have_tz -%}
env TZ;
{% endif %}
{%- if not have_malloc_arena -%}
env MALLOC_ARENA_MAX;
{% endif %}
{%- for v in preserve_env -%}
{%- if re.search("(\"|'|\\s)", v) %}
{#- TODO: investigate corrent escape behavior for Angie/nginx -#}
env {{ v.__repr__() }};
{%- else %}
env {{ v }};
{%- endif %}
{%- endfor -%}

View File

@ -0,0 +1,28 @@
{#- prologue -#}
{#- NB: "TZ" is always provided by Angie itself -#}
{%- set s_vars = ['MALLOC_ARENA_MAX', 'GLIBC_TUNABLES', 'MALLOC_CONF'] -%}
{%- set c_env = ( j2cfg.core_worker_env or [] ) | any_to_env_dict -%}
{%- set c_vars = c_env | dict_keys -%}
{%- set c_vars_preserve = c_env | dict_empty_keys -%}
{%- set c_vars_override = c_env | dict_non_empty_keys -%}
{%- set vars_preserve = ( c_vars_preserve + ( s_vars | list_diff(c_vars) )) | sort -%}
{#- main part -#}
{%- if vars_preserve %}
## preserve
{%- for k in vars_preserve %}
env {{ k }};
{%- endfor %}
{% endif %}
{%- if c_vars_override %}
## WARNING!
## explicit environment variables are NOT implemented
## reason: envs are supported only for http_perl but not for http_js/stream_js
## solution: provide environment variables explicitly
## and then list them in "core_worker_env" key in config
{%- for k in c_vars_override %}
{%- set v = c_env[k] -%}
# env {{ k }}={{ v.__repr__() }};
{%- endif %}
{%- endfor %}
{% endif %}

View File

@ -1,7 +0,0 @@
{#- prologue -#}
{%- set preserve_env = ( j2cfg.core_preserve_environment or [] )|env_any_to_str_list -%}
{%- set preserve_vars = preserve_env|str_list_re_fullmatch('[^=]+') -%}
{#- main part -#}
{% for v in preserve_vars -%}
{{ v }}
{% endfor -%}

View File

@ -0,0 +1,10 @@
{#- prologue -#}
{%- set s_vars = ['MALLOC_ARENA_MAX', 'GLIBC_TUNABLES', 'MALLOC_CONF'] -%}
{%- set c_env = ( j2cfg.core_worker_env or [] ) | any_to_env_dict -%}
{%- set c_vars = c_env | dict_keys -%}
{%- set c_vars_preserve = c_env | dict_empty_keys -%}
{%- set vars_preserve = ( c_vars_preserve + ( s_vars | list_diff(c_vars) )) | sort -%}
{#- main part -#}
{%- for k in vars_preserve -%}
{{ k }}
{% endfor -%}

View File

@ -12,7 +12,7 @@ have_envvar() {
## unexporting variable in (POSIX) sh is PITA =/ ## unexporting variable in (POSIX) sh is PITA =/
unexport() { unexport() {
unset ___k ___v local ___k ___v
for ___k ; do for ___k ; do
[ -n "${___k}" ] || continue [ -n "${___k}" ] || continue
have_envvar "${___k}" || continue have_envvar "${___k}" || continue
@ -20,9 +20,7 @@ unexport() {
___v=$(eval printf '%s' "\"\${${___k}}\"") ___v=$(eval printf '%s' "\"\${${___k}}\"")
eval "unset ${___k}" eval "unset ${___k}"
eval "${___k}=$(env printf '%s' \"\${___v}\")" eval "${___k}=$(env printf '%s' \"\${___v}\")"
unset ___v
done done
unset ___k
} }
## likely the same as in https://pkg.go.dev/strconv#ParseBool ## likely the same as in https://pkg.go.dev/strconv#ParseBool
@ -38,31 +36,33 @@ gobool_to_int() {
[ -n "${__IEP_SRC:-}" ] || __IEP_SRC="$0" [ -n "${__IEP_SRC:-}" ] || __IEP_SRC="$0"
IEP_TRACE=$(gobool_to_int "${IEP_TRACE:-0}" 0)
export IEP_TRACE
log_always() { log_always() {
if [ "${IEP_TRACE}" = 1 ] ; then if [ "${IEP_DEBUG}" = 1 ] ; then
echo "# $(date +'%Y-%m-%d %H:%M:%S.%03N %z'): ${__IEP_SRC}${*:+: $*}" echo "# $(date +'%Y-%m-%d %H:%M:%S.%03N %z'): ${__IEP_SRC}${*:+: $*}"
else else
echo "# ${__IEP_SRC}${*:+: $*}" echo "# ${__IEP_SRC}${*:+: $*}"
fi >&2 fi >&2
} }
IEP_VERBOSE=$(gobool_to_int "${IEP_VERBOSE:-${IEP_TRACE}}" "${IEP_TRACE}")
export IEP_VERBOSE
log() { log() {
[ "${IEP_VERBOSE}" = 0 ] || log_always "$@" [ "${IEP_VERBOSE}" = 0 ] || log_always "$@"
} }
log_file() { sed -E '/^./s,^, ,' < "$1" >&2 ; } log_file() { sed -E '/^./s,^, ,' < "$1" >&2 ; }
if [ "${IEP_VERBOSE}" = 0 ] ; then ln_s() {
ln_s() { ln -s "$@" || return; } if [ "${IEP_VERBOSE}" = 0 ] ; then
cp_a() { cp -a "$@" || return; } ln -s "$@" || return
else else
ln_s() { ln -sv "$@" || return; } ln -sv "$@" || return
cp_a() { cp -av "$@" || return; } fi
fi }
cp_a() {
if [ "${IEP_VERBOSE}" = 0 ] ; then
cp -a "$@" || return
else
cp -av "$@" || return
fi
}
ln_cp() { ln_cp() {
if [ -h "$1" ] ; then if [ -h "$1" ] ; then
@ -82,7 +82,7 @@ untemplate_path() {
"${volume_root}"/* | /etc/angie/run/* ) "${volume_root}"/* | /etc/angie/run/* )
strip_suffix "$1" "$2" strip_suffix "$1" "$2"
;; ;;
/etc/angie/conf.d/* | /etc/angie/j2cfg.d/* | /etc/angie/mod.d/* | /etc/angie/modules.d/* | /etc/angie/njs.d/* | /etc/angie/site.d/* | /etc/angie/snip.d/* ) /etc/angie/conf.d/* | /etc/angie/j2cfg.d/* | /etc/angie/mod.d/* | /etc/angie/modules.d/* | /etc/angie/site.d/* | /etc/angie/snip.d/* )
strip_suffix "$1" "$2" strip_suffix "$1" "$2"
;; ;;
/etc/angie/static.d/* ) /etc/angie/static.d/* )
@ -118,12 +118,14 @@ install_userdir() {
} }
expand_file_envsubst() { expand_file_envsubst() {
__r=0 local __ret __src __dst
__ret=0
for __src ; do for __src ; do
[ -n "${__src}" ] || continue [ -n "${__src}" ] || continue
if ! [ -f "${__src}" ] ; then if ! [ -f "${__src}" ] ; then
__r=1 __ret=1
log_always "file not found: ${__src}" log_always "file not found: ${__src}"
continue continue
fi fi
@ -131,24 +133,23 @@ expand_file_envsubst() {
case "${__src}" in case "${__src}" in
*.in ) ;; *.in ) ;;
* ) * )
__r=1 __ret=1
log "expand_file_envsubst: file name extension mismatch: ${__src}" log "expand_file_envsubst: file name extension mismatch: ${__src}"
continue continue
;; ;;
esac esac
__dest=$(strip_suffix "${__src}" '.in') __dst=$(strip_suffix "${__src}" '.in')
if [ -e "${__dest}" ] ; then if [ -e "${__dst}" ] ; then
__r=1 __ret=1
log "expand_file_envsubst: destination file already exists: ${__dest}" log "expand_file_envsubst: destination file already exists: ${__dst}"
continue continue
fi fi
log "Running envsubst: ${__src} -> ${__dest}" log "Running envsubst: ${__src} -> ${__dst}"
envsubst.sh < "${__src}" > "${__dest}" || __r=1 envsubst.sh < "${__src}" > "${__dst}" || __ret=1
done done
unset __src __dest return ${__ret}
return ${__r}
} }
expand_file_j2cfg() { expand_file_j2cfg() {
@ -156,6 +157,8 @@ expand_file_j2cfg() {
} }
expand_dir_envsubst() { expand_dir_envsubst() {
local __template_list __have_args __ret __orig_file
__template_list=$(mktemp) || return __template_list=$(mktemp) || return
find "$@" -follow -type f -name '*.in' \ find "$@" -follow -type f -name '*.in' \
@ -175,7 +178,6 @@ expand_dir_envsubst() {
[ -n "${__orig_file}" ] || continue [ -n "${__orig_file}" ] || continue
expand_file_envsubst "${__orig_file}" || __ret=1 expand_file_envsubst "${__orig_file}" || __ret=1
done < "${__template_list}" done < "${__template_list}"
unset __orig_file
if [ -z "${__have_args}" ] ; then if [ -z "${__have_args}" ] ; then
rm -f "${ENVSUBST_ARGS}" ; unset ENVSUBST_ARGS rm -f "${ENVSUBST_ARGS}" ; unset ENVSUBST_ARGS
@ -188,6 +190,8 @@ expand_dir_envsubst() {
} }
expand_dir_j2cfg() { expand_dir_j2cfg() {
local __template_list __ret
__template_list=$(mktemp) || return __template_list=$(mktemp) || return
find "$@" -follow -type f -name '*.j2' -printf '%p\0' \ find "$@" -follow -type f -name '*.j2' -printf '%p\0' \
@ -224,10 +228,6 @@ remap_path() {
/etc/angie/modules.dist/* ) echo "${2:-/etc/angie/modules.d}${1#/etc/angie/modules.dist}" ;; /etc/angie/modules.dist/* ) echo "${2:-/etc/angie/modules.d}${1#/etc/angie/modules.dist}" ;;
/etc/angie/modules/* ) echo "${2:-/etc/angie/modules.d}${1#/etc/angie/modules}" ;; /etc/angie/modules/* ) echo "${2:-/etc/angie/modules.d}${1#/etc/angie/modules}" ;;
/angie/modules/* ) echo "${2:-/etc/angie/modules.d}${1#/angie/modules}" ;; /angie/modules/* ) echo "${2:-/etc/angie/modules.d}${1#/angie/modules}" ;;
## njs
/etc/angie/njs.dist/* ) echo "${2:-/etc/angie/njs.d}${1#/etc/angie/njs.dist}" ;;
/etc/angie/njs/* ) echo "${2:-/etc/angie/njs.d}${1#/etc/angie/njs}" ;;
/angie/njs/* ) echo "${2:-/etc/angie/njs.d}${1#/angie/njs}" ;;
## site ## site
/etc/angie/site.dist/* ) echo "${2:-/etc/angie/site.d}${1#/etc/angie/site.dist}" ;; /etc/angie/site.dist/* ) echo "${2:-/etc/angie/site.d}${1#/etc/angie/site.dist}" ;;
/etc/angie/site/* ) echo "${2:-/etc/angie/site.d}${1#/etc/angie/site}" ;; /etc/angie/site/* ) echo "${2:-/etc/angie/site.d}${1#/etc/angie/site}" ;;

View File

@ -19,7 +19,7 @@ if [ "${NGX_HTTP}${NGX_MAIL}${NGX_STREAM}" = '000' ] ; then
fi fi
unset default_dirs_merge default_dirs_link unset default_dirs_merge default_dirs_link
default_dirs_merge='conf j2cfg mod modules njs site snip' default_dirs_merge='conf j2cfg mod modules site snip'
default_dirs_link='' default_dirs_link=''
if [ "${NGX_PROCESS_STATIC}" = 1 ] ; then if [ "${NGX_PROCESS_STATIC}" = 1 ] ; then

View File

@ -0,0 +1,25 @@
#!/bin/sh
## allow these addresses to be provided in case of:
## - local development/testing
## - `hostname -I' random failures or misbehavior
if [ -z "${NGX_IP_ADDRESSES:-}" ] ; then
NGX_IP_ADDRESSES=$(hostname -I)
fi
NGX_IP_ADDRESSES=$(printf '%s' "${NGX_IP_ADDRESSES}" | sed -zE 's/^\s+//;s/\s+$//;s/\s+/ /g')
export NGX_IP_ADDRESSES
unset NGX_IPV4_ADDRESSES NGX_IPV6_ADDRESSES
for i in ${NGX_IP_ADDRESSES} ; do
case "$i" in
*:* )
NGX_IPV6_ADDRESSES="${NGX_IPV6_ADDRESSES:-}${NGX_IPV6_ADDRESSES:+ }$i"
;;
* )
NGX_IPV4_ADDRESSES="${NGX_IPV4_ADDRESSES:-}${NGX_IPV4_ADDRESSES:+ }$i"
;;
esac
done
unset i
export NGX_IPV4_ADDRESSES NGX_IPV6_ADDRESSES

View File

@ -10,9 +10,16 @@ _NGX_WORKER_CONNECTIONS=4096
[ -n "${NGX_WORKER_PROCESSES:-}" ] || NGX_WORKER_PROCESSES=${_NGX_WORKER_PROCESSES} [ -n "${NGX_WORKER_PROCESSES:-}" ] || NGX_WORKER_PROCESSES=${_NGX_WORKER_PROCESSES}
case "${NGX_WORKER_PROCESSES}" in case "${NGX_WORKER_PROCESSES}" in
"${_NGX_WORKER_PROCESSES}" ) ;; "${_NGX_WORKER_PROCESSES}" ) ;;
[1-9] | [1-9][0-9] ) ;; ## allow values within [1;999]
0 | [Aa][Uu][Tt][Oo] ) [1-9] | [1-9][0-9] | [1-9][0-9][0-9] ) ;;
log_always "NGX_WORKER_PROCESSES=${NGX_WORKER_PROCESSES} isn't supported yet" [Aa][Uu][Tt][Oo] )
## adjust
NGX_WORKER_PROCESSES=auto
log_always "NGX_WORKER_PROCESSES: \"auto\" isn't supported by container yet"
log_always "offloading decision to Angie (this could be a problem!)"
;;
0 )
log_always "NGX_WORKER_PROCESSES: \"0\" isn't supported by container yet"
log_always "setting NGX_WORKER_PROCESSES=${_NGX_WORKER_PROCESSES}" log_always "setting NGX_WORKER_PROCESSES=${_NGX_WORKER_PROCESSES}"
NGX_WORKER_PROCESSES=${_NGX_WORKER_PROCESSES} NGX_WORKER_PROCESSES=${_NGX_WORKER_PROCESSES}
;; ;;
@ -28,6 +35,11 @@ case "${NGX_WORKER_PRIORITY}" in
"${_NGX_WORKER_PRIORITY}" ) ;; "${_NGX_WORKER_PRIORITY}" ) ;;
-[1-9] | -1[0-9] | -20 ) ;; -[1-9] | -1[0-9] | -20 ) ;;
[0-9] | 1[0-9] | 20 ) ;; [0-9] | 1[0-9] | 20 ) ;;
-0 )
log_always "NGX_WORKER_PRIORITY: likely an error: '-0'"
log_always "adjusting NGX_WORKER_PRIORITY=0"
NGX_WORKER_PRIORITY=0
;;
* ) * )
log_always "NGX_WORKER_PRIORITY: unrecognized value: ${NGX_WORKER_PRIORITY}" log_always "NGX_WORKER_PRIORITY: unrecognized value: ${NGX_WORKER_PRIORITY}"
log_always "setting NGX_WORKER_PRIORITY=${_NGX_WORKER_PRIORITY}" log_always "setting NGX_WORKER_PRIORITY=${_NGX_WORKER_PRIORITY}"
@ -38,7 +50,7 @@ esac
[ -n "${NGX_WORKER_RLIMIT_NOFILE:-}" ] || NGX_WORKER_RLIMIT_NOFILE=${_NGX_WORKER_RLIMIT_NOFILE} [ -n "${NGX_WORKER_RLIMIT_NOFILE:-}" ] || NGX_WORKER_RLIMIT_NOFILE=${_NGX_WORKER_RLIMIT_NOFILE}
case "${NGX_WORKER_RLIMIT_NOFILE}" in case "${NGX_WORKER_RLIMIT_NOFILE}" in
"${_NGX_WORKER_RLIMIT_NOFILE}" ) ;; "${_NGX_WORKER_RLIMIT_NOFILE}" ) ;;
[1-9] | [1-9][0-9] ) [0-9] | [1-9][0-9] )
log_always "NGX_WORKER_RLIMIT_NOFILE: too low: ${NGX_WORKER_RLIMIT_NOFILE}" log_always "NGX_WORKER_RLIMIT_NOFILE: too low: ${NGX_WORKER_RLIMIT_NOFILE}"
log_always "setting NGX_WORKER_RLIMIT_NOFILE=${_NGX_WORKER_RLIMIT_NOFILE}" log_always "setting NGX_WORKER_RLIMIT_NOFILE=${_NGX_WORKER_RLIMIT_NOFILE}"
NGX_WORKER_RLIMIT_NOFILE=${_NGX_WORKER_RLIMIT_NOFILE} NGX_WORKER_RLIMIT_NOFILE=${_NGX_WORKER_RLIMIT_NOFILE}
@ -59,7 +71,7 @@ esac
[ -n "${NGX_WORKER_CONNECTIONS:-}" ] || NGX_WORKER_CONNECTIONS=${_NGX_WORKER_CONNECTIONS} [ -n "${NGX_WORKER_CONNECTIONS:-}" ] || NGX_WORKER_CONNECTIONS=${_NGX_WORKER_CONNECTIONS}
case "${NGX_WORKER_CONNECTIONS}" in case "${NGX_WORKER_CONNECTIONS}" in
"${_NGX_WORKER_CONNECTIONS}" ) ;; "${_NGX_WORKER_CONNECTIONS}" ) ;;
[1-9] | [1-9][0-9] ) [0-9] | [1-9][0-9] )
log_always "NGX_WORKER_CONNECTIONS: too low: ${NGX_WORKER_CONNECTIONS}" log_always "NGX_WORKER_CONNECTIONS: too low: ${NGX_WORKER_CONNECTIONS}"
log_always "setting NGX_WORKER_CONNECTIONS=${_NGX_WORKER_CONNECTIONS}" log_always "setting NGX_WORKER_CONNECTIONS=${_NGX_WORKER_CONNECTIONS}"
NGX_WORKER_CONNECTIONS=${_NGX_WORKER_CONNECTIONS} NGX_WORKER_CONNECTIONS=${_NGX_WORKER_CONNECTIONS}
@ -82,7 +94,7 @@ nofile_hard=$(ulimit -Hn)
if [ "${nofile_hard}" = unlimited ] ; then if [ "${nofile_hard}" = unlimited ] ; then
## minor hack (if applicable) :) ## minor hack (if applicable) :)
nofile_hard=${NGX_WORKER_RLIMIT_NOFILE} nofile_hard=$((NGX_WORKER_RLIMIT_NOFILE * 2))
fi fi
nofile_ok=0 nofile_ok=0
@ -104,7 +116,7 @@ if [ ${nofile_ok} = 0 ] ; then
nofile_hard=$(ulimit -Hn) nofile_hard=$(ulimit -Hn)
fi fi
if [ ${nofile_hard} -lt ${NGX_WORKER_RLIMIT_NOFILE} ] ; then if [ ${nofile_hard} -lt ${NGX_WORKER_RLIMIT_NOFILE} ] ; then
log_always "lowering NGX_WORKER_RLIMIT_NOFILE to ${nofile_hard}" log_always "lowering NGX_WORKER_RLIMIT_NOFILE to ${nofile_hard} due to hard limit"
NGX_WORKER_RLIMIT_NOFILE=${nofile_hard} NGX_WORKER_RLIMIT_NOFILE=${nofile_hard}
fi fi
@ -120,3 +132,15 @@ unset nofile_soft nofile_hard nofile_ok
export NGX_WORKER_PROCESSES NGX_WORKER_PRIORITY NGX_WORKER_RLIMIT_NOFILE NGX_WORKER_CONNECTIONS export NGX_WORKER_PROCESSES NGX_WORKER_PRIORITY NGX_WORKER_RLIMIT_NOFILE NGX_WORKER_CONNECTIONS
unset _NGX_WORKER_PROCESSES _NGX_WORKER_PRIORITY _NGX_WORKER_RLIMIT_NOFILE _NGX_WORKER_CONNECTIONS unset _NGX_WORKER_PROCESSES _NGX_WORKER_PRIORITY _NGX_WORKER_RLIMIT_NOFILE _NGX_WORKER_CONNECTIONS
if [ ${NGX_WORKER_RLIMIT_NOFILE} -lt ${NGX_WORKER_CONNECTIONS} ] ; then
log_always "WARNING: NGX_WORKER_RLIMIT_NOFILE is less than NGX_WORKER_CONNECTIONS (${NGX_WORKER_RLIMIT_NOFILE} < ${NGX_WORKER_CONNECTIONS})"
else
ratio=$(mawk -v "a=${NGX_WORKER_RLIMIT_NOFILE}" -v "b=${NGX_WORKER_CONNECTIONS}" 'BEGIN{print a/b;exit;}' </dev/null)
case "${ratio}" in
1 | 1.* )
log_always "WARNING: \"NGX_WORKER_RLIMIT_NOFILE/NGX_WORKER_CONNECTIONS\" ratio is too low (=${ratio})"
;;
esac
unset ratio
fi

View File

@ -10,9 +10,10 @@ else
[ -n "${NGX_HTTP_MAX_RANGES:-}" ] || NGX_HTTP_MAX_RANGES=${_NGX_HTTP_MAX_RANGES} [ -n "${NGX_HTTP_MAX_RANGES:-}" ] || NGX_HTTP_MAX_RANGES=${_NGX_HTTP_MAX_RANGES}
case "${NGX_HTTP_MAX_RANGES}" in case "${NGX_HTTP_MAX_RANGES}" in
"${_NGX_HTTP_MAX_RANGES}" ) ;; "${_NGX_HTTP_MAX_RANGES}" ) ;;
## allow values within [1;999]
[1-9] | [1-9][0-9] | [1-9][0-9][0-9] ) ;; [1-9] | [1-9][0-9] | [1-9][0-9][0-9] ) ;;
0 ) 0 )
log "HTTP: Range/If-Range/Accept-Ranges support is disabled" log_always "HTTP: Range/If-Range/Accept-Ranges support is disabled by NGX_HTTP_MAX_RANGES=0"
;; ;;
* ) * )
log_always "NGX_HTTP_MAX_RANGES: unrecognized value: ${NGX_HTTP_MAX_RANGES}" log_always "NGX_HTTP_MAX_RANGES: unrecognized value: ${NGX_HTTP_MAX_RANGES}"

View File

@ -3,12 +3,15 @@ set -f
. /image-entry.d/00-common.envsh . /image-entry.d/00-common.envsh
## Angie: unset core variable
unset ANGIE ANGIE_BPF_MAPS
[ "${NGX_STRICT_LOAD}" = 0 ] || set -e [ "${NGX_STRICT_LOAD}" = 0 ] || set -e
cd "${merged_root}/" cd "${merged_root}/"
expand_error_delim() { expand_error_delim() {
IEP_TRACE=0 log_always ' ----------------------------------- ' IEP_DEBUG=0 log_always ' ----------------------------------- '
} }
expand_error() { expand_error() {
[ "${expand_error_seen:-}" != 1 ] || return [ "${expand_error_seen:-}" != 1 ] || return

View File

@ -22,7 +22,7 @@ rm -f "$t" ; unset t
[ "${NGX_STRICT_LOAD}" = 0 ] || set -e [ "${NGX_STRICT_LOAD}" = 0 ] || set -e
load_error_delim() { load_error_delim() {
IEP_TRACE=0 log_always ' ----------------------------------- ' IEP_DEBUG=0 log_always ' ----------------------------------- '
} }
load_error() { load_error() {
[ "${load_error_seen:-}" != 1 ] || return [ "${load_error_seen:-}" != 1 ] || return

View File

@ -3,7 +3,7 @@ set -f
. /image-entry.d/00-common.envsh . /image-entry.d/00-common.envsh
if [ "${IEP_TRACE}" = 1 ] ; then if [ "${IEP_RETAIN_MERGED_TREE}" = 1 ] ; then
log_always "NOT removing merged tree: ${merged_root}/" log_always "NOT removing merged tree: ${merged_root}/"
else else
log "removing merged tree: ${merged_root}/" log "removing merged tree: ${merged_root}/"

View File

@ -3,6 +3,9 @@ set -f
. /image-entry.d/00-common.envsh . /image-entry.d/00-common.envsh
## Angie: unset core variable
unset ANGIE ANGIE_BPF_MAPS
## merely debug test ## merely debug test
log_always 'test Angie configuration:' log_always 'test Angie configuration:'
log_always '=========================' log_always '========================='

View File

@ -1,41 +1,64 @@
#!/bin/sh #!/bin/sh
__set="$-" ## Angie: unset core variable
set +e unset ANGIE ANGIE_BPF_MAPS
if [ "${IEP_TRACE}" = 1 ] ; then
log_always "NOT going to unset following variables:" if [ "${IEP_RETAIN_ENV}" = 1 ] ; then
sed -E '/^./s,^,- ,' >&2 log_always "NOT removing following variables:"
sed -E '/^./s,^, ,' >&2
echo >&2
else else
unset __env __set="$-"
set +e
unset __env __env_print
while read -r __env ; do while read -r __env ; do
[ -n "${__env}" ] || continue [ -n "${__env}" ] || continue
case "${__env}" in case "${__env}" in
*\'* ) \'* | \"* )
log "skipping variable (malformed): ${__env}" >&2 log "skipping variable (malformed): ${__env}" >&2
continue continue
;; ;;
esac esac
if [ "${IEP_DEBUG}" = 1 ] ; then
__env_print="${__env}="$(printenv "${__env}")
__env_print=$(env printf '%q' "${__env_print}")
log_always "unsetting variable: ${__env_print}"
else
log "unsetting variable: ${__env}" log "unsetting variable: ${__env}"
fi
unset "${__env}" unset "${__env}"
done done
unset __env unset __env __env_print
[ -z "${__set}" ] || set -"${__set}"
unset __set
fi <<-EOF fi <<-EOF
$( $(
set +e
cat /proc/self/environ \ cat /proc/self/environ \
| sed -zEn '/^([^=]+).*$/s//\1/p' \ | sed -zEn '/^([^=]+).*$/s//\1/p' \
| xargs -0r printf '%q\n' \ | xargs -0r printf '%q\n' \
| { | {
f="${target_root}/j2cfg/core-preserve-environment.txt" ## retain variables defined in ".core_worker_env" configuration key
## (if it was specified somewhere in dictionaries - either yaml or json)
f="${target_root}/j2cfg/core-worker-env.txt"
[ -s "$f" ] || exec cat [ -s "$f" ] || exec cat
grep -Fxv -f "$f" grep -Fxv -f "$f"
} \ } \
| grep -E \ | {
-e '^(NGX|PYTHON)' \ ## remove environment variables:
## 1. variables starting with "NGX" as they are used by configuration templates
## 2. variables containing "_SERVICE" or "_PORT" as they are came from
## container orchestration
grep -E \
-e '^NGX' \
-e '_(SERVICE|PORT)' \
} \
| sort -uV | sort -uV
) )
EOF EOF
[ -z "${__set}" ] || set -"${__set}"
unset __set

View File

@ -1,35 +1,77 @@
#!/bin/sh #!/bin/sh
set -f set -f
[ -n "${IEP_TRACE}" ] || IEP_TRACE=0
[ "${IEP_TRACE}" = 1 ] || IEP_TRACE=0
[ "${IEP_TRACE}" = 0 ] || echo "# trace: $(date +'%Y-%m-%d %H:%M:%S.%03N %z'): start" >&2
iep_preserve_env() { iep_preserve_env() {
## preserve LD_PRELOAD ## preserve LD_PRELOAD
unset __IEP_LD_PRELOAD unset __IEP_LD_PRELOAD
__IEP_LD_PRELOAD="${LD_PRELOAD:-}" __IEP_LD_PRELOAD="${LD_PRELOAD:-}"
unset LD_PRELOAD unset LD_PRELOAD
## glibc: preserve GLIBC_TUNABLES
unset __IEP_GLIBC_TUNABLES
__IEP_GLIBC_TUNABLES="${GLIBC_TUNABLES:-}"
unset GLIBC_TUNABLES
## glibc: preserve MALLOC_ARENA_MAX ## glibc: preserve MALLOC_ARENA_MAX
unset __IEP_MALLOC_ARENA_MAX unset __IEP_MALLOC_ARENA_MAX
__IEP_MALLOC_ARENA_MAX=${MALLOC_ARENA_MAX:-2} __IEP_MALLOC_ARENA_MAX="${MALLOC_ARENA_MAX:-2}"
export MALLOC_ARENA_MAX=2 export MALLOC_ARENA_MAX=2
## jemalloc: preserve MALLOC_CONF
unset __IEP_MALLOC_CONF
__IEP_MALLOC_CONF="${MALLOC_CONF:-}"
unset MALLOC_CONF
}
iep_prepare_env() {
## Angie: unset core variable
unset ANGIE ANGIE_BPF_MAPS
## dumb-init: preserve args
unset IEP_DUMB_INIT_ARGS
IEP_DUMB_INIT_ARGS="${DUMB_INIT_ARGS:-}"
unset DUMB_INIT_ARGS
if [ "${DUMB_INIT_SETSID:-}" = 0 ] ; then
IEP_DUMB_INIT_ARGS="-c${IEP_DUMB_INIT_ARGS:+ }${IEP_DUMB_INIT_ARGS}"
fi
unset DUMB_INIT_SETSID
} }
iep_restore_env() { iep_restore_env() {
unset IEP_VERBOSE IEP_TRACE IEP_ROOT IEP_LOCAL_OVERRIDE unset IEP_DEBUG IEP_VERBOSE IEP_TRACE IEP_ROOT
unset IEP_LOCAL_OVERRIDE IEP_RETAIN_MERGED_TREE IEP_RETAIN_ENV
## restore LD_PRELOAD ## restore LD_PRELOAD
if [ -n "${__IEP_LD_PRELOAD}" ] ; then if [ -n "${__IEP_LD_PRELOAD:-}" ] ; then
export LD_PRELOAD="${__IEP_LD_PRELOAD}" export LD_PRELOAD="${__IEP_LD_PRELOAD}"
fi fi
unset __IEP_LD_PRELOAD unset __IEP_LD_PRELOAD
## glibc: restore GLIBC_TUNABLES
if [ -n "${__IEP_GLIBC_TUNABLES:-}" ] ; then
export GLIBC_TUNABLES="${__IEP_GLIBC_TUNABLES}"
fi
unset __IEP_GLIBC_TUNABLES
## glibc: restore MALLOC_ARENA_MAX ## glibc: restore MALLOC_ARENA_MAX
if [ "${MALLOC_ARENA_MAX}" = 2 ] ; then if [ -n "${__IEP_MALLOC_ARENA_MAX:-}" ] ; then
export MALLOC_ARENA_MAX="${__IEP_MALLOC_ARENA_MAX}" export MALLOC_ARENA_MAX="${__IEP_MALLOC_ARENA_MAX}"
fi fi
unset __IEP_MALLOC_ARENA_MAX unset __IEP_MALLOC_ARENA_MAX
## jemalloc: restore MALLOC_CONF
if [ -n "${__IEP_MALLOC_CONF:-}" ] ; then
export MALLOC_CONF="${__IEP_MALLOC_CONF}"
fi
unset __IEP_MALLOC_CONF
} }
iep_preserve_env iep_preserve_env
iep_prepare_env
## early setup TMPDIR (affects "mktemp") ## early setup TMPDIR (affects "mktemp")
export TMPDIR=/run/angie/tmp export TMPDIR=/run/angie/tmp
@ -39,7 +81,6 @@ export TMPDIR=/run/angie/tmp
# case "$1" in # case "$1" in
# angie | */angie ) ;; # angie | */angie ) ;;
# * ) # * )
# unset IEP_INIT DUMB_INIT_ARGS
# iep_restore_env # iep_restore_env
# exec "$@" # exec "$@"
# ;; # ;;
@ -48,31 +89,46 @@ export TMPDIR=/run/angie/tmp
unset __IEP_SRC ; __IEP_SRC="${0##*/}" unset __IEP_SRC ; __IEP_SRC="${0##*/}"
. /image-entry.d/00-common.envsh . /image-entry.d/00-common.envsh
unexport IEP_INIT DUMB_INIT_ARGS IEP_INIT=$(gobool_to_int "${IEP_INIT:-0}" 0)
# unexport IEP_INIT
unset x ; x="${IEP_INIT}" ; unset IEP_INIT ; IEP_INIT="$x" ; unset x
IEP_RETAIN_MERGED_TREE=$(gobool_to_int "${IEP_RETAIN_MERGED_TREE:-0}" 0)
IEP_RETAIN_ENV=$(gobool_to_int "${IEP_RETAIN_ENV:-0}" 0)
export IEP_RETAIN_MERGED_TREE IEP_RETAIN_ENV
# IEP_TRACE=$(gobool_to_int "${IEP_TRACE:-0}" 0)
IEP_DEBUG=$(gobool_to_int "${IEP_DEBUG:-0}" 0)
IEP_VERBOSE=$(gobool_to_int "${IEP_VERBOSE:-${IEP_DEBUG}}" "${IEP_DEBUG}")
export IEP_TRACE IEP_DEBUG IEP_VERBOSE
## run parts (if any) ## run parts (if any)
while read -r f ; do unset __IEP_SCRIPT
[ -n "$f" ] || continue while read -r __IEP_SCRIPT ; do
[ -f "$f" ] || continue [ -n "${__IEP_SCRIPT}" ] || continue
[ -f "${__IEP_SCRIPT}" ] || continue
case "$f" in
case "${__IEP_SCRIPT}" in
*.envsh ) *.envsh )
if ! [ -x "$f" ] ; then if ! [ -x "${__IEP_SCRIPT}" ] ; then
log "NOT sourcing $f - not executable" log "NOT sourcing ${__IEP_SCRIPT} - not executable"
continue continue
fi fi
log_always "sourcing $f" [ "${IEP_TRACE}" = 0 ] || echo "# trace: $(date +'%Y-%m-%d %H:%M:%S.%03N %z'): source ${__IEP_SCRIPT}" >&2
__IEP_SRC="$f" log "sourcing ${__IEP_SCRIPT}"
. "$f" __IEP_SRC="${__IEP_SCRIPT}"
. "${__IEP_SCRIPT}"
__IEP_SRC="${0##*/}" __IEP_SRC="${0##*/}"
;; ;;
* ) * )
if ! [ -x "$f" ] ; then if ! [ -x "${__IEP_SCRIPT}" ] ; then
log "NOT running $f - not executable" log "NOT running ${__IEP_SCRIPT} - not executable"
continue continue
fi fi
log_always "running $f" [ "${IEP_TRACE}" = 0 ] || echo "# trace: $(date +'%Y-%m-%d %H:%M:%S.%03N %z'): run ${__IEP_SCRIPT}" >&2
"$f" log "running ${__IEP_SCRIPT}"
"${__IEP_SCRIPT}"
;; ;;
esac esac
done <<EOF done <<EOF
@ -85,11 +141,30 @@ $(
) )
EOF EOF
[ "${IEP_TRACE}" = 0 ] || echo "# trace: $(date +'%Y-%m-%d %H:%M:%S.%03N %z'): end" >&2
if [ "${IEP_DEBUG}" = 1 ] ; then
log_always "ready to run application: $*"
else
log_always "ready to run application"
fi
echo >&2
iep_restore_env iep_restore_env
IEP_INIT=$(gobool_to_int "${IEP_INIT:-0}" 0) ## variables that are not so easily unsettable
unset __IEP_ENV
for i in '_' 'SHLVL' ; do
__IEP_ENV="${__IEP_ENV:-}${__IEP_ENV:+ }-u $i"
done
if [ "${IEP_INIT}" = 0 ] ; then if [ "${IEP_INIT}" = 0 ] ; then
exec "$@" exec \
${__IEP_ENV:+ env ${__IEP_ENV} } \
"$@"
else else
exec dumb-init ${DUMB_INIT_ARGS} "$@" exec \
${__IEP_ENV:+ env ${__IEP_ENV} } \
dumb-init ${IEP_DUMB_INIT_ARGS} \
"$@"
fi fi

View File

@ -5,31 +5,7 @@ import re
import jinja2 import jinja2
from .settings import is_env_banned
def uniq_list(a: list) -> list:
return list(dict.fromkeys(a))
def list_remove_non_str(a: list) -> list:
return list(itertools.filterfalse(
lambda x: not isinstance(x, str), a
))
def list_remove_empty_str(a: list) -> list:
return list(itertools.filterfalse(
lambda x: len(x) == 0, a
))
def uniq_str_list(a: list) -> list:
return uniq_list(list_remove_empty_str(a))
def str_split_to_list(s: str, sep=r'\s+') -> list:
return list_remove_empty_str(
re.split(sep, s)
)
def is_sequence(x) -> bool: def is_sequence(x) -> bool:
@ -40,33 +16,74 @@ def is_mapping(x) -> bool:
return isinstance(x, collections.abc.Mapping) return isinstance(x, collections.abc.Mapping)
def any_to_str_list(k) -> list: def uniq(a: (list, set)) -> list:
if isinstance(k, str): return list(dict.fromkeys(a))
return [k]
if is_sequence(k):
return [str(e) for e in k]
return [str(k)]
def is_str_list_re_match(a: list, pattern, flags=0) -> bool: def remove_non_str(a: (list, set)) -> list:
return list(filter(lambda x: isinstance(x, str), a))
def remove_empty_str(a: (list, set)) -> list:
return list(filter(None, a))
def uniq_str_list(a: (list, set)) -> (list, set):
return remove_empty_str(uniq(a))
def str_split_to_list(s: str, sep=r'\s+') -> list:
return remove_empty_str(re.split(sep, s))
def dict_to_env_str_list(x: dict) -> list:
r = []
for k in sorted(x.keys()):
if x[k] is None:
r.append(f'{k}')
else:
r.append(f'{k}={str(x[k])}')
return r
def any_to_str_list(x) -> list:
if isinstance(x, str):
return [x]
if is_sequence(x):
return [str(e) for e in x]
if is_mapping(x):
return dict_to_env_str_list(x)
return [str(x)]
def is_re_match(a: (list, set), pattern, flags=0) -> bool:
return any(re.match(pattern, x, flags) for x in a) return any(re.match(pattern, x, flags) for x in a)
def is_str_list_re_fullmatch(a: list, pattern, flags=0) -> bool: def is_re_fullmatch(a: (list, set), pattern, flags=0) -> bool:
return any(re.fullmatch(pattern, x, flags) for x in a) return any(re.fullmatch(pattern, x, flags) for x in a)
def str_list_re_match(a: list, pattern, flags=0) -> list: def re_match(a: (list, set), pattern, flags=0) -> list:
return [x for x in a if re.match(pattern, x, flags)] return [x for x in a if re.match(pattern, x, flags)]
def str_list_re_fullmatch(a: list, pattern, flags=0) -> list: def re_fullmatch(a: (list, set), pattern, flags=0) -> list:
return [x for x in a if re.fullmatch(pattern, x, flags)] return [x for x in a if re.fullmatch(pattern, x, flags)]
def str_list_re_sub(a: list, pattern, repl, count=0, flags=0) -> list: def re_match_negate(a: (list, set), pattern, flags=0) -> list:
return [x for x in a if not re.match(pattern, x, flags)]
def re_fullmatch_negate(a: (list, set), pattern, flags=0) -> list:
return [x for x in a if not re.fullmatch(pattern, x, flags)]
def re_sub(a: (list, set), pattern, repl, count=0, flags=0) -> list:
return [re.sub(pattern, repl, x, count, flags) for x in a] return [re.sub(pattern, repl, x, count, flags) for x in a]
@ -84,9 +101,10 @@ def as_cgi_header(s: str) -> str:
return 'HTTP_' + re.sub('[^A-Z0-9]+', '_', s.upper()).strip('_') return 'HTTP_' + re.sub('[^A-Z0-9]+', '_', s.upper()).strip('_')
def env_any_to_str_list(x) -> list: def any_to_env_dict(x) -> dict:
if x is None: if x is None:
return [] return {}
h = {} h = {}
def feed(k, v=None): def feed(k, v=None):
@ -96,16 +114,13 @@ def env_any_to_str_list(x) -> list:
if m == '=': if m == '=':
k = k2 k = k2
v = v2 v = v2
if len(k) == 0:
return
if not re.fullmatch(r'[a-zA-Z_][a-zA-Z0-9_]*', k): if not re.fullmatch(r'[a-zA-Z_][a-zA-Z0-9_]*', k):
return return
if is_env_banned(k):
return
if k in h: if k in h:
return return
if v is None: h[k] = v if v is None else str(v)
h[k] = v
else:
h[k] = str(v)
if isinstance(x, str): if isinstance(x, str):
feed(x) feed(x)
@ -113,35 +128,57 @@ def env_any_to_str_list(x) -> list:
for e in x: for e in x:
feed(e) feed(e)
elif is_mapping(x): elif is_mapping(x):
for k, v in x.items(): for k in x:
feed(k, v) feed(k, x[k])
else: else:
return [] return {}
r = [] return h
for k in sorted(h.keys()):
if h[k] is None:
r.append(k) def dict_keys(x: dict) -> list:
else: return sorted([k for k in x.keys()])
r.append(f'{k}={h[k]}')
return r
def dict_empty_keys(x: dict) -> list:
return sorted([k for k in x.keys() if x[k] is None])
def dict_non_empty_keys(x: dict) -> list:
return sorted([k for k in x.keys() if x[k] is not None])
def list_diff(a: (list, set), b: (list, set)) -> list:
return list(set(a) - set(b))
def list_intersect(a: (list, set), b: (list, set)) -> list:
return list(set(a) & set(b))
J2CFG_FILTERS = [ J2CFG_FILTERS = [
any_to_env_dict,
any_to_str_list, any_to_str_list,
as_cgi_header, as_cgi_header,
env_any_to_str_list, dict_empty_keys,
dict_keys,
dict_non_empty_keys,
dict_to_env_str_list,
is_mapping, is_mapping,
is_re_fullmatch,
is_re_match,
is_sequence, is_sequence,
is_str_list_re_fullmatch, list_diff,
is_str_list_re_match, list_intersect,
list_remove_empty_str, re_fullmatch,
list_remove_non_str, re_fullmatch_negate,
re_match,
re_match_negate,
re_sub,
remove_empty_str,
remove_non_str,
sh_like_file_to_list, sh_like_file_to_list,
str_list_re_fullmatch,
str_list_re_match,
str_list_re_sub,
str_split_to_list, str_split_to_list,
uniq_list, uniq,
uniq_str_list, uniq_str_list,
] ]

View File

@ -1,3 +1,6 @@
import re
J2CFG_TEMPLATE_EXT = '.j2' J2CFG_TEMPLATE_EXT = '.j2'
J2CFG_PATH = [ J2CFG_PATH = [
@ -24,3 +27,15 @@ J2CFG_JINJA_EXTENSIONS = [
'jinja2.ext.do', 'jinja2.ext.do',
'jinja2.ext.loopcontrols', 'jinja2.ext.loopcontrols',
] ]
J2CFG_BANNED_ENVS = [
r'ANGIE(=|$)',
r'ANGIE_BPF_MAPS(=|$)'
]
def is_env_banned(k: str) -> bool:
for r in J2CFG_BANNED_ENVS:
if re.match(r, k):
return True
return False

View File

@ -1,20 +1,40 @@
j2cfg: j2cfg:
{{ j2cfg }} {{ j2cfg }}
{% set x = [1,2,3,4] %}
x = {{ x }}
is_sequence:
{{ x | is_sequence }}
{% set x = {1:2,3:4} %}
x = {{ x }}
is_sequence:
{{ x | is_sequence }}
{% set x = [1,2,3,4] %}
x = {{ x }}
is_mapping:
{{ x | is_mapping }}
{% set x = {1:2,3:4} %}
x = {{ x }}
is_mapping:
{{ x | is_mapping }}
{% set x = [2,3,1,2] %} {% set x = [2,3,1,2] %}
x = {{ x }} x = {{ x }}
uniq_list: uniq:
{{ x | uniq_list }} {{ x | uniq }}
{% set x = ['2',3,'1','2'] %} {% set x = ['2',3,'1','2'] %}
x = {{ x }} x = {{ x }}
list_remove_non_str: remove_non_str:
{{ x | list_remove_non_str }} {{ x | remove_non_str }}
{% set x = ['2','','1','2'] %} {% set x = ['2','','1','2'] %}
x = {{ x }} x = {{ x }}
list_remove_empty_str: remove_empty_str:
{{ x | list_remove_empty_str }} {{ x | remove_empty_str }}
{% set x = ['2','3','1','2'] %} {% set x = ['2','3','1','2'] %}
x = {{ x }} x = {{ x }}
@ -31,25 +51,10 @@ str_split_to_list:
str_split_to_list(':'): str_split_to_list(':'):
{{ x | str_split_to_list(':') }} {{ x | str_split_to_list(':') }}
{% set x = [1,2,3,4] %} {% set x = { 'VAR1': 'Etc/UTC', 'VAR2': '', 'VAR3': None, '4VAR4': 'yeah', 'VAR5=not': 'yeah', 'VAR5=real yeah': None, 'VAR6': {'pi': 3.1415926}, 'VAR7': ['pi', 3.1415926] } %}
x = {{ x }} x = {{ x }}
is_sequence: dict_to_env_str_list:
{{ x | is_sequence }} {{ x | dict_to_env_str_list }}
{% set x = {1:2,3:4} %}
x = {{ x }}
is_sequence:
{{ x | is_sequence }}
{% set x = [1,2,3,4] %}
x = {{ x }}
is_mapping:
{{ x | is_mapping }}
{% set x = {1:2,3:4} %}
x = {{ x }}
is_mapping:
{{ x | is_mapping }}
{% set x = '1 2 3 4' %} {% set x = '1 2 3 4' %}
"x = {{ x }}" "x = {{ x }}"
@ -68,38 +73,52 @@ any_to_str_list:
{% set x = ['a2','b3','c1','d2'] %} {% set x = ['a2','b3','c1','d2'] %}
x = {{ x }} x = {{ x }}
is_str_list_re_match('[ab]'): is_re_match('[ab]'):
{{ x | is_str_list_re_match('[ab]') }} {{ x | is_re_match('[ab]') }}
is_str_list_re_match('[mn]'): is_re_match('[mn]'):
{{ x | is_str_list_re_match('[mn]') }} {{ x | is_re_match('[mn]') }}
{% set x = ['a2','b3','c1','d2'] %} {% set x = ['a2','b3','c1','d2'] %}
x = {{ x }} x = {{ x }}
is_str_list_re_fullmatch('[ab]'): is_re_fullmatch('[ab]'):
{{ x | is_str_list_re_fullmatch('[ab]') }} {{ x | is_re_fullmatch('[ab]') }}
is_str_list_re_fullmatch('[ab][12]'): is_re_fullmatch('[ab][12]'):
{{ x | is_str_list_re_fullmatch('[ab][12]') }} {{ x | is_re_fullmatch('[ab][12]') }}
{% set x = ['a2','b3','c1','d2'] %} {% set x = ['a2','b3','c1','d2'] %}
x = {{ x }} x = {{ x }}
str_list_re_match('[ab]'): re_match('[ab]'):
{{ x | str_list_re_match('[ab]') }} {{ x | re_match('[ab]') }}
str_list_re_match('[mn]'): re_match('[mn]'):
{{ x | str_list_re_match('[mn]') }} {{ x | re_match('[mn]') }}
{% set x = ['a2','b3','c1','d2'] %} {% set x = ['a2','b3','c1','d2'] %}
x = {{ x }} x = {{ x }}
str_list_re_fullmatch('[ab]'): re_fullmatch('[ab]'):
{{ x | str_list_re_fullmatch('[ab]') }} {{ x | re_fullmatch('[ab]') }}
str_list_re_fullmatch('[ab][12]'): re_fullmatch('[ab][12]'):
{{ x | str_list_re_fullmatch('[ab][12]') }} {{ x | re_fullmatch('[ab][12]') }}
{% set x = ['a2','b3','c1','d2'] %}
x = {{ x }}
re_match_negate('[ab]'):
{{ x | re_match_negate('[ab]') }}
re_match_negate('[mn]'):
{{ x | re_match_negate('[mn]') }}
{% set x = ['a2','b3','c1','d2'] %}
x = {{ x }}
re_fullmatch_negate('[ab]'):
{{ x | re_fullmatch_negate('[ab]') }}
re_fullmatch_negate('[ab][12]'):
{{ x | re_fullmatch_negate('[ab][12]') }}
{% set x = ['a2b','b3b','c1f','d2g'] %} {% set x = ['a2b','b3b','c1f','d2g'] %}
x = {{ x }} x = {{ x }}
str_list_re_sub('[ab]', '_'): re_sub('[ab]', '_'):
{{ x | str_list_re_sub('[ab]', '_') }} {{ x | re_sub('[ab]', '_') }}
str_list_re_sub('[mn]', '_'): re_sub('[mn]', '_'):
{{ x | str_list_re_sub('[mn]', '_') }} {{ x | re_sub('[mn]', '_') }}
{% set x = 'j2cfg-multi.py' %} {% set x = 'j2cfg-multi.py' %}
x = {{ x }} x = {{ x }}
@ -118,15 +137,35 @@ as_cgi_header:
{% set x = 'VAR1=Etc/UTC' %} {% set x = 'VAR1=Etc/UTC' %}
x = {{ x }} x = {{ x }}
env_any_to_str_list: any_to_env_dict:
{{ x | env_any_to_str_list }} {{ x | any_to_env_dict }}
{% set x = ['VAR1=Etc/UTC', 'VAR2=', 'VAR3', '4VAR4=yeah', 'VAR5=yeah', 'VAR5=not-yeah'] %} {% set x = ['VAR1=Etc/UTC', 'VAR2=', 'VAR3', '4VAR4=yeah', 'VAR5=yeah', 'VAR5=not-yeah'] %}
x = {{ x }} x = {{ x }}
env_any_to_str_list: any_to_env_dict:
{{ x | env_any_to_str_list }} {{ x | any_to_env_dict }}
{% set x = { 'VAR1': 'Etc/UTC', 'VAR2': '', 'VAR3': None, '4VAR4': 'yeah', 'VAR5=not': 'yeah', 'VAR5=real yeah': None, 'VAR6': {'pi': 3.1415926}, 'VAR7': ['pi', 3.1415926] } %} {% set x = { 'VAR1': 'Etc/UTC', 'VAR2': '', 'VAR3': None, '4VAR4': 'yeah', 'VAR5=not': 'yeah', 'VAR5=real yeah': None, 'VAR6': {'pi': 3.1415926}, 'VAR7': ['pi', 3.1415926] } %}
x = {{ x }} x = {{ x }}
env_any_to_str_list: any_to_env_dict:
{{ x | env_any_to_str_list }} {{ x | any_to_env_dict }}
{% set x = { 'VAR1': 'Etc/UTC', 'VAR2': '', 'VAR3': None, '4VAR4': 'yeah', 'VAR5=not': 'yeah', 'VAR5=real yeah': None, 'VAR6': {'pi': 3.1415926}, 'VAR7': ['pi', 3.1415926] } %}
x = {{ x }}
dict_keys:
{{ x | dict_keys }}
dict_empty_keys:
{{ x | dict_empty_keys }}
dict_non_empty_keys:
{{ x | dict_non_empty_keys }}
{% set x = [1,2,3,4] %}
{% set y = [3,4,5,6] %}
list_diff(x, y):
{{ x | list_diff(y) }}
list_diff(y, x):
{{ y | list_diff(x) }}
list_intersect(x, y):
{{ x | list_intersect(y) }}
list_intersect(y, x):
{{ y | list_intersect(x) }}

View File

@ -4,7 +4,7 @@ set -f
sed -znE '/^([^=]+)=.*$/s,,\1,p' /proc/self/environ \ sed -znE '/^([^=]+)=.*$/s,,\1,p' /proc/self/environ \
| sed -zE \ | sed -zE \
-e '/^_$/d;/^ENVSUBST_/d;' \ -e '/^_$/d;/^ENVSUBST_/d;' \
-e '/^__IEP/d;/^IEP_(INIT|VERBOSE|TRACE)$/d' \ -e '/^__IEP_/d;/^IEP_$/d' \
| { | {
if [ -n "${ENVSUBST_EXCLUDE_REGEX:-}" ] ; then if [ -n "${ENVSUBST_EXCLUDE_REGEX:-}" ] ; then
grep -zEv -e "${ENVSUBST_EXCLUDE_REGEX}" grep -zEv -e "${ENVSUBST_EXCLUDE_REGEX}"

View File

@ -1,7 +1,7 @@
#!/bin/sh #!/bin/sh
[ "${IEP_VERBOSE}" = 0 ] || { [ "${IEP_VERBOSE:-}" = 0 ] || {
pfx= pfx=
[ "${IEP_TRACE}" = 0 ] || pfx="$(date +'%Y-%m-%d %H:%M:%S.%03N %z'): " [ "${IEP_DEBUG:-}" = 0 ] || pfx="$(date +'%Y-%m-%d %H:%M:%S.%03N %z'): "
echo "# ${pfx}${0##*/}:" >&2 echo "# ${pfx}${0##*/}:" >&2
printf ' - %s\n' "$@" >&2 printf ' - %s\n' "$@" >&2
} }

View File

@ -1,7 +1,7 @@
#!/bin/sh #!/bin/sh
[ "${IEP_VERBOSE}" = 0 ] || { [ "${IEP_VERBOSE:-}" = 0 ] || {
pfx= pfx=
[ "${IEP_TRACE}" = 0 ] || pfx="$(date +'%Y-%m-%d %H:%M:%S.%03N %z'): " [ "${IEP_DEBUG:-}" = 0 ] || pfx="$(date +'%Y-%m-%d %H:%M:%S.%03N %z'): "
echo "# ${pfx}${0##*/}:${*:+ $*}" >&2 echo "# ${pfx}${0##*/}:${*:+ $*}" >&2
} }
exec python3 "/usr/local/lib/j2cfg/${0##*/}.py" "$@" exec python3 "/usr/local/lib/j2cfg/${0##*/}.py" "$@"