Saltstack Official Home Assistant 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.

275 lines
8.0KB

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