Saltstack Official Linux Formula
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

298 line
8.9KB

  1. #!/usr/bin/env bash
  2. ###
  3. # Script requirments:
  4. #apt-get install -y python-yaml virtualenv git
  5. __ScriptVersion="2018.08.16"
  6. __ScriptName="run_tests.sh"
  7. __ScriptFullName="$0"
  8. __ScriptArgs="$*"
  9. set -e
  10. [ -n "$DEBUG" ] && set -x
  11. CURDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
  12. METADATA=${CURDIR}/../metadata.yml
  13. FORMULA_NAME=$(cat $METADATA | python -c "import sys,yaml; print yaml.load(sys.stdin)['name']")
  14. FORMULA_META_DIR=${CURDIR}/../${FORMULA_NAME}/meta
  15. ## Overrideable parameters
  16. PILLARDIR=${PILLARDIR:-${CURDIR}/pillar}
  17. BUILDDIR=${BUILDDIR:-${CURDIR}/build}
  18. VENV_DIR=${VENV_DIR:-${BUILDDIR}/virtualenv}
  19. MOCK_BIN_DIR=${MOCK_BIN_DIR:-${CURDIR}/mock_bin}
  20. DEPSDIR=${BUILDDIR}/deps
  21. SCHEMARDIR=${SCHEMARDIR:-"${CURDIR}/../${FORMULA_NAME}/schemas/"}
  22. SALT_FILE_DIR=${SALT_FILE_DIR:-${BUILDDIR}/file_root}
  23. SALT_PILLAR_DIR=${SALT_PILLAR_DIR:-${BUILDDIR}/pillar_root}
  24. SALT_CONFIG_DIR=${SALT_CONFIG_DIR:-${BUILDDIR}/salt}
  25. SALT_CACHE_DIR=${SALT_CACHE_DIR:-${SALT_CONFIG_DIR}/cache}
  26. SALT_CACHE_EXTMODS_DIR=${SALT_CACHE_EXTMODS_DIR:-${SALT_CONFIG_DIR}/cache_master_extmods}
  27. SALT_OPTS="${SALT_OPTS} --retcode-passthrough --local -c ${SALT_CONFIG_DIR} --log-file=/dev/null"
  28. IGNORE_MODELVALIDATE_MASK=${IGNORE_MODELVALIDATE_MASK:-"novalidate"}
  29. if [ "x${SALT_VERSION}" != "x" ]; then
  30. PIP_SALT_VERSION="==${SALT_VERSION}"
  31. fi
  32. ## Functions
  33. log_info() {
  34. echo -e "[INFO] $*"
  35. }
  36. log_err() {
  37. echo -e "[ERROR] $*" >&2
  38. }
  39. setup_virtualenv() {
  40. log_info "Setting up Python virtualenv"
  41. dependency_check virtualenv
  42. virtualenv $VENV_DIR
  43. source ${VENV_DIR}/bin/activate
  44. python -m pip install salt${PIP_SALT_VERSION}
  45. if [[ -f ${CURDIR}/test-requirements.txt ]]; then
  46. python -m pip install -r ${CURDIR}/test-requirements.txt
  47. fi
  48. }
  49. setup_mock_bin() {
  50. # If some state requires a binary, a lightweight replacement for
  51. # such binary can be put into MOCK_BIN_DIR for test purposes
  52. if [ -d "${MOCK_BIN_DIR}" ]; then
  53. PATH="${MOCK_BIN_DIR}:$PATH"
  54. export PATH
  55. fi
  56. }
  57. setup_pillar() {
  58. [ ! -d ${SALT_PILLAR_DIR} ] && mkdir -p ${SALT_PILLAR_DIR}
  59. echo "base:" > ${SALT_PILLAR_DIR}/top.sls
  60. for pillar in ${PILLARDIR}/*; do
  61. grep ${FORMULA_NAME}: ${pillar} &>/dev/null || continue
  62. state_name=$(basename ${pillar%.sls})
  63. echo -e " ${state_name}:\n - ${state_name}" >> ${SALT_PILLAR_DIR}/top.sls
  64. done
  65. }
  66. setup_salt() {
  67. [ ! -d ${SALT_FILE_DIR} ] && mkdir -p ${SALT_FILE_DIR}
  68. [ ! -d ${SALT_CONFIG_DIR} ] && mkdir -p ${SALT_CONFIG_DIR}
  69. [ ! -d ${SALT_CACHE_DIR} ] && mkdir -p ${SALT_CACHE_DIR}
  70. [ ! -d ${SALT_CACHE_EXTMODS_DIR} ] && mkdir -p ${SALT_CACHE_EXTMODS_DIR}
  71. echo "base:" > ${SALT_FILE_DIR}/top.sls
  72. for pillar in ${PILLARDIR}/*.sls; do
  73. grep ${FORMULA_NAME}: ${pillar} &>/dev/null || continue
  74. state_name=$(basename ${pillar%.sls})
  75. echo -e " ${state_name}:\n - ${FORMULA_NAME}" >> ${SALT_FILE_DIR}/top.sls
  76. done
  77. cat << EOF > ${SALT_CONFIG_DIR}/minion
  78. file_client: local
  79. cachedir: ${SALT_CACHE_DIR}
  80. extension_modules: ${SALT_CACHE_EXTMODS_DIR}
  81. verify_env: False
  82. minion_id_caching: False
  83. file_roots:
  84. base:
  85. - ${SALT_FILE_DIR}
  86. - ${CURDIR}/..
  87. pillar_roots:
  88. base:
  89. - ${SALT_PILLAR_DIR}
  90. - ${PILLARDIR}
  91. EOF
  92. }
  93. fetch_dependency() {
  94. # example: fetch_dependency "linux:https://github.com/salt-formulas/salt-formula-linux"
  95. dep_name="$(echo $1|cut -d : -f 1)"
  96. dep_source="$(echo $1|cut -d : -f 2-)"
  97. dep_root="${DEPSDIR}/$(basename $dep_source .git)"
  98. dep_metadata="${dep_root}/metadata.yml"
  99. dependency_check git
  100. [ -d $dep_root ] && { log_info "Dependency $dep_name already fetched"; return 0; }
  101. log_info "Fetching dependency $dep_name"
  102. [ ! -d ${DEPSDIR} ] && mkdir -p ${DEPSDIR}
  103. git clone $dep_source ${DEPSDIR}/$(basename $dep_source .git)
  104. ln -s ${dep_root}/${dep_name} ${SALT_FILE_DIR}/${dep_name}
  105. METADATA="${dep_metadata}" install_dependencies
  106. }
  107. link_modules(){
  108. # Link modules *.py files to temporary salt-root
  109. local SALT_ROOT=${1:-$SALT_FILE_DIR}
  110. local SALT_ENV=${2:-$DEPSDIR}
  111. mkdir -p "${SALT_ROOT}/_modules/"
  112. # from git, development versions
  113. find ${SALT_ENV} -maxdepth 3 -mindepth 3 -path '*_modules*' -iname "*.py" -type f -print0 | while read -d $'\0' file; do
  114. ln -fs $(readlink -e ${file}) "$SALT_ROOT"/_modules/$(basename ${file}) ;
  115. done
  116. salt_run saltutil.sync_all
  117. }
  118. install_dependencies() {
  119. grep -E "^dependencies:" ${METADATA} >/dev/null || return 0
  120. (python - | while read dep; do fetch_dependency "$dep"; done) << EOF
  121. import sys,yaml
  122. for dep in yaml.load(open('${METADATA}', 'ro'))['dependencies']:
  123. print '%s:%s' % (dep["name"], dep["source"])
  124. EOF
  125. }
  126. clean() {
  127. log_info "Cleaning up ${BUILDDIR}"
  128. [ -d ${BUILDDIR} ] && rm -rf ${BUILDDIR} || exit 0
  129. }
  130. salt_run() {
  131. [ -e ${VENV_DIR}/bin/activate ] && source ${VENV_DIR}/bin/activate
  132. python $(which salt-call) ${SALT_OPTS} $*
  133. }
  134. prepare() {
  135. if [[ -f ${BUILDDIR}/.prepare_done ]]; then
  136. log_info "${BUILDDIR}/.prepare_done exist, not rebuilding BUILDDIR"
  137. return
  138. fi
  139. [[ -d ${BUILDDIR} ]] && mkdir -p ${BUILDDIR}
  140. [[ ! -f "${VENV_DIR}/bin/activate" ]] && setup_virtualenv
  141. setup_mock_bin
  142. setup_pillar
  143. setup_salt
  144. install_dependencies
  145. link_modules
  146. touch ${BUILDDIR}/.prepare_done
  147. }
  148. lint_releasenotes() {
  149. [[ ! -f "${VENV_DIR}/bin/activate" ]] && setup_virtualenv
  150. source ${VENV_DIR}/bin/activate
  151. reno lint ${CURDIR}/../
  152. }
  153. lint() {
  154. # lint_releasenotes
  155. log_err "TODO: lint_releasenotes"
  156. }
  157. run() {
  158. for pillar in ${PILLARDIR}/*.sls; do
  159. grep ${FORMULA_NAME}: ${pillar} &>/dev/null || continue
  160. state_name=$(basename ${pillar%.sls})
  161. salt_run grains.set 'noservices' False force=True
  162. echo "Checking state ${FORMULA_NAME}.${state_name} ..."
  163. salt_run --id=${state_name} state.show_sls ${FORMULA_NAME} || (log_err "Execution of ${FORMULA_NAME}.${state_name} failed"; exit 1)
  164. # Check that all files in 'meta' folder can be rendered using any valid pillar
  165. for meta in `find ${FORMULA_META_DIR} -type f`; do
  166. meta_name=$(basename ${meta})
  167. echo "Checking meta ${meta_name} ..."
  168. salt_run --out=quiet --id=${state_name} cp.get_template ${meta} ${SALT_CACHE_DIR}/${meta_name} \
  169. || { log_err "Failed to render meta ${meta} using pillar ${FORMULA_NAME}.${state_name}"; exit 1; }
  170. cat ${SALT_CACHE_DIR}/${meta_name}
  171. done
  172. done
  173. }
  174. real_run() {
  175. for pillar in ${PILLARDIR}/*.sls; do
  176. state_name=$(basename ${pillar%.sls})
  177. salt_run --id=${state_name} state.sls ${FORMULA_NAME} || { log_err "Execution of ${FORMULA_NAME}.${state_name} failed"; exit 1; }
  178. done
  179. }
  180. run_model_validate(){
  181. # Run modelschema.model_validate validation.
  182. # TEST iterateble, run for `each formula ROLE against each ROLE_PILLARNAME`
  183. # Pillars should be named in conviend ROLE_XXX.sls or ROLE.sls
  184. # Example:
  185. # client.sls client_auth.sls server.sls server_auth.sls
  186. if [ -d ${SCHEMARDIR} ]; then
  187. # model validator require py modules
  188. fetch_dependency "salt:https://github.com/salt-formulas/salt-formula-salt"
  189. link_modules
  190. salt_run saltutil.clear_cache; salt_run saltutil.refresh_pillar; salt_run saltutil.sync_all;
  191. for role in ${SCHEMARDIR}/*.yaml; do
  192. role_name=$(basename "${role%*.yaml}")
  193. for pillar in $(ls pillar/${role_name}*.sls | grep -v ${IGNORE_MODELVALIDATE_MASK} ); do
  194. pillar_name=$(basename "${pillar%*.sls}")
  195. local _message="FORMULA:${FORMULA_NAME} ROLE:${role_name} against PILLAR:${pillar_name}"
  196. log_info "model_validate ${_message}"
  197. # Rendered Example:
  198. # python $(which salt-call) --local -c /test1/maas/tests/build/salt --id=maas_cluster modelschema.model_validate maas cluster
  199. salt_run -m ${DEPSDIR}/salt-formula-salt --id=${pillar_name} modelschema.model_validate ${FORMULA_NAME} ${role_name} || { log_err "Execution of model_validate ${_message} failed"; exit 1 ; }
  200. done
  201. done
  202. else
  203. log_info "${SCHEMARDIR} not found!";
  204. fi
  205. }
  206. dependency_check() {
  207. local DEPENDENCY_COMMANDS=$*
  208. for DEPENDENCY_COMMAND in $DEPENDENCY_COMMANDS; do
  209. which $DEPENDENCY_COMMAND > /dev/null || ( log_err "Command \"$DEPENDENCY_COMMAND\" can not be found in default path."; exit 1; )
  210. done
  211. }
  212. _atexit() {
  213. RETVAL=$?
  214. trap true INT TERM EXIT
  215. if [ $RETVAL -ne 0 ]; then
  216. log_err "Execution failed"
  217. else
  218. log_info "Execution successful"
  219. fi
  220. return $RETVAL
  221. }
  222. ## Main
  223. log_info "Running version: ${__ScriptVersion}"
  224. log_info "Command line: '${__ScriptFullName} ${__ScriptArgs}'"
  225. trap _atexit INT TERM EXIT
  226. case $1 in
  227. clean)
  228. clean
  229. ;;
  230. prepare)
  231. prepare
  232. ;;
  233. lint)
  234. lint
  235. ;;
  236. run)
  237. run
  238. ;;
  239. real-run)
  240. real_run
  241. ;;
  242. model-validate)
  243. prepare
  244. run_model_validate
  245. ;;
  246. *)
  247. prepare
  248. # lint
  249. run
  250. run_model_validate
  251. ;;
  252. esac