123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445 |
- #!/bin/bash
-
- shellfu import jat
- shellfu import pretty
- shellfu import jat_dump
-
- #
- # fake upgrade path generator
- #
- # This beakerlib module helps generate mock upgrade paths for purposes
- # of testing preupgrade-assistant.
- #
- #
- # =head1 HELLO WORLD
- #
- # {
- # echo '[preupgrade]'
- # echo 'description = hello world module'
- # echo '[MODULE]'
- # echo 'GROUP = foo'
- # echo 'NAME = bar'
- # echo 'LANG = py'
- # echo 'CODE = log_info('hello world')'
- # echo 'CODE = exit_informational()'
- # } > one.mdef
- # preupg_fupath RHEL6_7 one.mdef
- # preupg__upath=RHEL6_7/all-xccdf.xml
- # preupg__run1
- #
- # would generate:
- #
- # RHEL6_7
- # └── foo
- # └── bar
- # ├── module.ini
- # └── check
- #
- #
- # =head1 OVERVIEW
- #
- # The workflow is following:
- #
- # 1. Per each upgrade path module, generate a pseudo-INI
- # file, defining properties and content of the module.
- #
- # 2. Call preupg_fupath() to generate the
- # upgrade path for you, using standard tools under the
- # hood.
- #
- # 3. Call preupg (or better, preupg__run1()
- # from preupgrade-assistant/main) to use the upgrade path.
- #
- #
- # =head1 FORMAT
- #
- # A fuller example:
- #
- # [preupgrade]
- # content_description = test module bob
- # content_title = test module bob
- #
- # [MODULE]
- # GROUP = system
- # NAME = foo
- # LANG = sh
- # CODE = log_medium_risk foo
- # CODE = exit_pass
- #
- # [FILES]
- # solution.txt = Some text
- # solution.txt = Some more text
- # myfile.txt = first line of an arbitrary file
- # myfile.txt = second line of of an arbitrary file
- #
- # * Fields from "preupgrade" section are directly copied to
- # used the actual module.ini.
- #
- # * MODULE section should contain GROUP, NAME and a multi-line
- # CODE of the module. GROUP and NAME define placement of
- # the module in the tree, CODE will be appended to the
- # auto-generated script.
- #
- # LANG key is 'sh' for Bash (default) and 'py' for Python.
- #
- # * FILES section can be used to include any number of plain
- # text files (such as a hook scripts or config files) inside
- # the module. Here key name is name of the file and key
- # content is content of the file.
- #
- # Virtually any field may be omitted; the generator will try to
- # fill in as much as possible (even generate names from random
- # chars).
- #
- # FILES section may be used to fully overwrite any other files
- # (even module.ini file!). This is useful to test "extreme" cases such
- # as empty or malformed module.ini file, while retaining feature of
- # auto-generation mentioned above.
- #
- #
- # =head2 Indentation
- #
- # Multi-line values (CODE and values from FILES section) are
- # read in a way that preserves indentation by removing as many
- # spaces from first column as possible without removing other
- # characters. For example, following sections:
- #
- # CODE = def blah():
- # CODE = pass
- # CODE =
- # CODE = for p in get_dist_native_list():
- # CODE = blah()
- #
- # CODE = def blah():
- # CODE = pass
- # CODE =
- # CODE = for p in get_dist_native_list():
- # CODE = blah()
- #
- # CODE =def blah():
- # CODE = pass
- # CODE =
- # CODE =for p in get_dist_native_list():
- # CODE = blah()
- #
- # would result in exactly the same code:
- #
- # def blah():
- # pass
- #
- # for p in get_dist_native_list():
- # blah()
- #
- # This is in order to enable generation of valid Python scripts
- # while enabling maximum readability of your test code.
- #
-
-
- preupg_fupath() {
- #
- # Create custom test upgrade path from pseudo-INI files
- #
- # Usage:
- # preupg_fupath UPATH [./FILE]...
- #
- # UPATH must be in form CCCN_N, where C is letter from alphabet,
- # N is a decimal integer number and '_' is literally underscore.
- #
- # Note: folders UPATH, UPATH-raw and UPATH-results in current
- # folder will be silently deleted!
- #
- #
- # If FILE argument starts with "at sign" (`@`), it will be interpreted
- # as name of a built-in .mdef file included within this library; look
- # into "builtins" subfolder; the alias can be constructed as path
- # to .mdef file relative to that folder, with .mdef suffix stripped.
- #
- # For example,
- #
- # preupg_fupath RHEL6_7 @pass @failed some/other.mdef
- #
- # will construct upgrade path named RHEL6_7 with three modules based on
- # two built-in files and one custom file.
- #
- # To launch generated upgrade path, specify it to preupg using
- # `-c` parameter pointing to file all-xccdf.xml under folder
- # <UPATH>, for example:
- #
- # preupg --force -c RHEL6_7/all-xccdf.xml
- #
- # Recommended practice, though, is to set preupg__upath
- # to the path as above and use preupg__run1:
- #
- # shellfu import preupg
- # ...
- # preupg_fupath RHEL6_7 foo.mdef bar.mdef
- # preupg__upath=RHEL6_7/all-xccdf.xml
- # preupg__run1
- #
- # which will enable lot of additional checks.
- #
-
- local UPName=$1; shift # upgrade path name
- local MdefFile # each Mdef file
- local Adding=false # true: we're adding into existing upath (iteration 2+)
- local b_file="" # built-in .mdef file
- local MdefFileNick # Shorter .mdef File reference (usable if built-in)
- local Tmp # our temp dir
- Tmp=$(mktemp -dt preupg_fupath.XXXXXXXX)
- jat__cmd rm -rf "$UPName" "$UPName-raw" "$UPName-results"
- for MdefFile in "$@";
- do
- MdefFileNick=$MdefFile
- test "${MdefFile:0:1}" == @ && {
- b_file=$(_preupg_fupath__builtin "$MdefFile") || return 2
- jat__log_info "using built-in .mdef file: $MdefFile=${b_file#$_PREUPG_FUPATH__HOMEDIR/}"
- MdefFile=$b_file
- }
- _preupg_fupath__mdef2module
- Adding=true
- done
- _preupg_fupath__cook_tree "$UPName"
- rm -r "$Tmp"
- }
-
- # # don't jump around too much beyond this line #
- # INTERNAL # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ #
- # # it's not a safe path #
-
- #
- # Library data dir (set during build)
- #
- _PREUPG_FUPATH__HOMEDIR=__SHELLFU_MODHOME__
-
- _preupg_fupath__builtin() {
- #
- # Show path of built-in .mdef file (if exists, else fail)
- #
- local b_file=$_PREUPG_FUPATH__HOMEDIR/builtins/${1:1}.mdef
- test -f "$b_file" || {
- jat__log_error "no such built-in: $b_file"
- return 2
- }
- echo "$b_file"
- }
-
- _preupg_fupath__cook_tree() {
- #
- # Create proper compose from raw tree
- #
- local tree=$1
- jat__cmd -h "build upgrade path" \
- preupg-xccdf-compose "$tree"
- jat__eval -h "put upgrade path in place" \
- "mv '$tree' '$tree-raw' && mv '$tree-results' '$tree'"
- find "$tree-raw" "$tree" | jat_dump__pipe -D FOREST
- }
-
- _preupg_fupath__draw_token() {
- #
- # Get a unique token
- #
- head -c 100 /dev/urandom | md5sum | head -c 5
- }
-
- _preupg_fupath__mdef2module() {
- #
- # Process single .mdef
- #
- local m_afile # additional file
- local m_code_body # module code body (after auto-header)
- local m_description # module description
- local m_srcmaj=6 # source OS version #FIXME: don't hard-code this
- local m_dstmaj=7 # destination OS version #FIXME: don't hard-code this
- local m_group # module group name
- local m_name # module name
- local m_lang # module language ('sh' for Bash or 'py' for Python)
- local m_title # module title
- local newmod_path # new module's subpath (derived from other parts)
-
- # Read starting values from $MdefFile or make them up
- #
- jat_dump__file -D "$MdefFile"
- m_group=$(_preupg_fupath__ini 1value "MODULE:GROUP")
- m_name=$(_preupg_fupath__ini 1value "MODULE:NAME")
- m_lang=$(_preupg_fupath__ini 1value "MODULE:LANG")
- m_code_body=$(_preupg_fupath__ini values "MODULE:CODE")
- m_title=$(_preupg_fupath__ini 1value "preupgrade:content_title")
- m_description=$(_preupg_fupath__ini 1value "preupgrade:content_description")
- test -n "$m_group" || m_group="group_$(_preupg_fupath__draw_token)"
- test -n "$m_name" || m_name="module_$(_preupg_fupath__draw_token)"
- test -n "$m_lang" || m_lang="sh"
- test -n "$m_code_body" || m_code_body="exit_pass"
- test -n "$m_title" || m_title="Mock module named $m_name"
- test -n "$m_description" || m_description="This is just a testing module named $m_name"
- debug -v m_group m_name m_lang m_code_body m_title m_description
-
- # create new module (most fields will be overwritten)
- #
- {
- echo "$UPName" # upgrade path name (also relative path)
- $Adding && echo y # we need to confirm if we want to add into existing
- $Adding || echo "$m_srcmaj" # source OS version
- $Adding || echo "$m_dstmaj" # destination OS version
- echo "$m_group" # module group
- echo "$m_name" # module name
- echo "$m_lang" # module language
- echo "$m_title" # module title
- echo "$m_description" # module description
- } >"$Tmp/answers"
- jat_dump__file -D "$Tmp/answers"
- jat__eval -h "create module: $m_name in group $m_group from $MdefFileNick" \
- "preupg-content-creator <$Tmp/answers"
- newmod_path="$UPName/$m_group/$m_name"
-
- # add arbitrary values from $MdefFile
- #
- local extra_keys
- extra_keys=$(
- for key in $(_preupg_fupath__ini lskeys "preupgrade");
- do
- value=$(_preupg_fupath__ini 1value "preupgrade:$key")
- grep "^$key *=" "$newmod_path/module.ini" && continue
- test -n "$value" && echo "$key = $value"
- done
- )
- debug -v extra_keys
- echo "$extra_keys" >> "$newmod_path/module.ini"
-
- # append CODE to module script
- echo "$m_code_body" >> "$newmod_path/check"
-
- # dump to keep context clear
- #
- jat_dump__file -D \
- "$newmod_path/module.ini" \
- "$newmod_path/check"
-
- # add any arbitrary files stored in FILES section
- #
- for m_afile in $(_preupg_fupath__ini lskeys FILES);
- do
- test -n "$m_code_body" \
- && test "$m_afile" == "check" \
- && jat__log_warning "overwriting CODE by file from FILES section: $m_afile"
- mkdir -p "$(dirname "$newmod_path/$m_afile")"
- _preupg_fupath__ini values "FILES:$m_afile" \
- > "$newmod_path/$m_afile"
- done
-
- debug -c find "$UPName"
- }
-
- _preupg_fupath__ini() {
- #
- # do ini operation
- #
- local op=$1
- local arg=$2
- local fn
- local flt=_preupg_fupath__ini_cat
- case $op in
- lskeys) fn=_preupg_fupath__ini_lskeys ;;
- sec) fn=_preupg_fupath__ini_grepsec ;;
- values) fn=_preupg_fupath__ini_greppath
- flt=_preupg_fupath__ini_unind ;;
- 1value) fn=_preupg_fupath__ini_greppath
- flt=_preupg_fupath__ini_strip ;;
- *) jat__log_error "incorrect use of \`_preupg_fupath__ini()\`"
- esac
- <"$MdefFile" $fn "$arg" | $flt
- }
-
- _preupg_fupath__ini_strip() {
- #
- # Strip a simple value
- #
- head -1 | sed 's/^ *//; s/ *$//'
- }
-
- _preupg_fupath__ini_unind() {
- #
- # Unindent multi-line value
- #
- local remove=0
- local tmp
- tmp=$(mktemp -dt _preupg_fupath__ini_unind.XXXXXXXX)
- cat >"$tmp/body"
- grep . "$tmp/body" | sed 's/[^ ].*$//' > "$tmp/air"
- remove=$(sort "$tmp/air" | head -1 | wc -c)
- ((remove--)) # newline
- case $remove in
- 0) cat "$tmp/body" ;;
- *) colrm 1 "$remove" < "$tmp/body" ;;
- esac
- rm -rf "$tmp"
- }
-
- _preupg_fupath__ini_cat() {
- #
- # A no-op for text stream
- #
- while IFS= read -r line;
- do
- printf -- "%s\n" "$line"
- done
- }
-
- _preupg_fupath__ini_grepkey() {
- #
- # Read key from a section
- #
- local wnt=$1
- grep -v '\s*#' \
- | sed -e 's/ *=/=/;' \
- | grep -e "^$wnt=" \
- | cut -d= -f2-
- }
-
- _preupg_fupath__ini_greppath() {
- #
- # Read key from the right section
- #
- # E.g. `files:share:my/lib.sh` should read
- #
- # [files:share]
- # my/lib.sh = proj/my/lib.sh
- #
- local wnt="$1"
- local wntkey="${wnt##*:}"
- local wntsec="${wnt%:$wntkey}"
- _preupg_fupath__ini_grepsec "$wntsec" \
- | _preupg_fupath__ini_grepkey "$wntkey"
- }
-
- _preupg_fupath__ini_grepsec() {
- #
- # Read one INI section
- #
- local wnt="$1"
- local ok=false
- grep -v '\s*#' \
- | while IFS= read -r line;
- do
- case "$line" in
- \[$wnt\]) ok=true; continue ;;
- \[*\]) ok=false; continue ;;
- esac
- $ok || continue
- printf -- "%s\n" "$line"
- done \
- | sed -e 's/ *=/=/;'
- }
-
- _preupg_fupath__ini_lskeys() {
- #
- # List keys from a section
- #
- local sct="$1"
- _preupg_fupath__ini_grepsec "$sct" \
- | cut -d= -f1 \
- | sort \
- | uniq
- }
-
- #shellfu module-version=__MKIT_PROJ_VERSION__
|