405 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Bash
		
	
	
		
			Executable File
		
	
	
			
		
		
	
	
			405 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Bash
		
	
	
		
			Executable File
		
	
	
| #!/usr/bin/env bash
 | |
| 
 | |
| # set -o errexit
 | |
| 
 | |
| 
 | |
| ## Colors
 | |
| readonly COLOR_RED='\033[0;31m'
 | |
| readonly COLOR_GREEN='\033[0;32m'
 | |
| readonly COLOR_YELLOW='\033[0;33m'
 | |
| readonly COLOR_BLUE='\033[0;34m'
 | |
| readonly COLOR_PURPLE='\033[1;35m'
 | |
| readonly COLOR_RESET='\033[0m'
 | |
| 
 | |
| 
 | |
| RETURNCODE_SUCCESS=0
 | |
| RETURNCODE_ERROR=1
 | |
| 
 | |
| 
 | |
| 
 | |
| log()
 | |
| {
 | |
| 	local log_type="$1"  # 'debug', 'info', 'warning' or 'error'
 | |
| 	local message="$2"
 | |
| 	local message_color=''
 | |
| 	case "$log_type" in
 | |
| 		'error')
 | |
| 			message_color="$COLOR_RED"
 | |
| 			;;
 | |
| 		'warning')
 | |
| 			message_color="$COLOR_YELLOW"
 | |
| 			;;
 | |
| 		'info')
 | |
| 			message_color="$COLOR_BLUE"
 | |
| 			;;
 | |
| 		'debug')
 | |
| 			message_color="$COLOR_PURPLE"
 | |
| 			;;
 | |
| 		*)
 | |
| 			echo "unexpected log type $log_type"
 | |
| 			exit "$RETURNCODE_ERROR"
 | |
| 	esac
 | |
| 
 | |
| 	printf "%b : %s\n" "${message_color}${log_type}${COLOR_RESET}" "$message"
 | |
| }
 | |
| 
 | |
| replace_in_file()
 | |
| {
 | |
| 	local file_path="$1"
 | |
| 	local searched_string="$2"
 | |
| 	local replacement_string="$3"
 | |
| 
 | |
| 	log 'debug' "replacing ${searched_string} with ${replacement_string} in $file_path"
 | |
| 
 | |
| 
 | |
| 	local safe_searched_string=$(echo "$searched_string" | tr '"' '\"' | tr '$' '\$')
 | |
| 	local safe_replacement_string=$(echo "$replacement_string" | tr '"' '\"' | tr '$' '\$')
 | |
| 	# log 'debug' "safe_searched_string : $safe_searched_string"
 | |
| 
 | |
| 	printf 'safe_searched_string: %b\n' "$safe_searched_string" 
 | |
| 
 | |
| 	grep -q "$safe_searched_string" "$file_path"
 | |
| 	if [ $? != "$RETURNCODE_SUCCESS" ]
 | |
| 	then
 | |
| 		log 'error' "failed to find $safe_searched_string in $file_path"
 | |
| 		return $RETURNCODE_ERROR
 | |
| 	fi
 | |
| 
 | |
| 	# log 'debug' "searching a separator"
 | |
| 
 | |
| 	local sep=''
 | |
| 	for sep in '/' '|' '@'
 | |
| 	do
 | |
| 		if $(echo "$safe_searched_string" | grep -q "$sep") || $(echo "$safe_replacement_string" | grep -q "$sep")
 | |
| 		then
 | |
| 			sep=''
 | |
| 		else
 | |
| 			# found a suitable separator for use in sed
 | |
| 			log 'debug' "found separator: $sep"
 | |
| 			break
 | |
| 		fi
 | |
| 	done
 | |
| 
 | |
| 
 | |
| 	if [ "$sep" = '' ]
 | |
| 	then
 | |
| 		log 'error' "failed to find a sed separator suitable for $safe_searched_string and $safe_replacement_string"
 | |
| 	fi
 | |
| 	sed -i "s${sep}${safe_searched_string}${sep}${safe_replacement_string}${sep}" "$file_path"
 | |
| 
 | |
| 	grep -q "$safe_replacement_string" "$file_path"
 | |
| 	if [ $? != "$RETURNCODE_SUCCESS" ]
 | |
| 	then
 | |
| 		log 'error' "failed to find $safe_replacement_string in $file_path"
 | |
| 		return $RETURNCODE_ERROR
 | |
| 	fi
 | |
| }
 | |
| 
 | |
| is_version_greater_than()
 | |
| {
 | |
| 	
 | |
| 	first_value="${1}"
 | |
| 	value_to_compare="${2}"
 | |
| 
 | |
| 	## Return False by default
 | |
| 	return_is_version_greater_than="1"
 | |
| 	
 | |
| 	log 'debug' "is_version_greater_than - Is first value (${first_value}) greater than the second value (${value_to_compare})."
 | |
| 	
 | |
| 	if printf '%s\n' "${first_value}" "${value_to_compare}" | sort --check=quiet --version-sort; then
 | |
| 		log 'debug' "${debug_prefix}is_version_greater_than - ${first_value} <= ${value_to_compare} ."
 | |
| 		return_is_version_greater_than="1"
 | |
| 	else
 | |
| 		log 'debug' "${debug_prefix}is_version_greater_than - ${first_value} > ${value_to_compare} ."
 | |
| 		return_is_version_greater_than="0"
 | |
| 	fi
 | |
| 
 | |
| 	unset first_value
 | |
| 	unset value_to_compare
 | |
| 
 | |
| 	return "${return_is_version_greater_than}"
 | |
| }
 | |
| 
 | |
| 
 | |
| controller__get_local_repos_path()
 | |
| {
 | |
| 	local debops_controller_path="$1"
 | |
| 	echo "${debops_controller_path}/ansible.debops.git"
 | |
| }
 | |
| 
 | |
| controller__get_reports_path()
 | |
| {
 | |
| 	local debops_controller_path="$1"
 | |
| 	echo "${debops_controller_path}/reports"
 | |
| }
 | |
| 
 | |
| controller__get_virtualenv_path()
 | |
| {
 | |
| 	local debops_controller_path="$1"
 | |
| 	echo "${debops_controller_path}/debops.venv"
 | |
| }
 | |
| 
 | |
| 
 | |
| controller__get_debops_env_path()
 | |
| {
 | |
| 	local debops_controller_path="$1"
 | |
| 	echo "${debops_controller_path}/debops-environment"
 | |
| }
 | |
| 
 | |
| deboco__init()
 | |
| {
 | |
| 	local debops_controller_path="$1"
 | |
| 	log 'info' "creating debobs controller in $debops_controller_path"
 | |
| 	if [ "$debops_controller_path" = '' ]
 | |
| 	then
 | |
| 		log 'error' "wrong debops controller path: $debops_controller_path"
 | |
| 		return "$RETURNCODE_ERROR"
 | |
| 	fi
 | |
| 	if [ -d "$debops_controller_path" ]
 | |
| 	then
 | |
| 		log 'error' "$debops_controller_path already exists"
 | |
| 		return "$RETURNCODE_ERROR"
 | |
| 	fi
 | |
| 	local local_repos_path=$(controller__get_local_repos_path "$debops_controller_path")
 | |
| 	local ipr_debops_url='https://git.ipr.univ-rennes.fr/cellinfo/ansible.debops'
 | |
| 	log 'info' "cloning $ipr_debops_url into $local_repos_path"
 | |
| 	git clone https://git.ipr.univ-rennes.fr/cellinfo/ansible.debops "$local_repos_path"
 | |
| 
 | |
| 	local debops_env_path=$(controller__get_debops_env_path "$debops_controller_path")
 | |
| 
 | |
| 	local debops_update_script_path="$local_repos_path/bin/update-debops.sh"
 | |
| 
 | |
| 	local from_line='readonly DEBOPS_ENVIRONMENT_FILE="${HOME}/.config/debops/environment"'
 | |
| 	# local from_line='readonly DEBOPS_ENVIRONMENT_FILE'
 | |
| 	local to_line="readonly DEBOPS_ENVIRONMENT_FILE=${debops_env_path}"
 | |
| 	log 'info' "replacing  $from_line with $to_line in $debops_update_script_path so that the script uses a debops environment specific to this controller"
 | |
| 	replace_in_file "$debops_update_script_path" "$from_line" "$to_line"
 | |
| 	if [ $? != "$RETURNCODE_SUCCESS" ]
 | |
| 	then
 | |
| 		return $RETURNCODE_ERROR
 | |
| 	fi
 | |
| 
 | |
| 	local bug_3678_is_fixed='false'
 | |
| 	if [ "$bug_3678_is_fixed" = 'false' ]
 | |
| 	then
 | |
| 		log 'warning' "disabling pip update in $debops_update_script_path because of https://bugzilla.ipr.univ-rennes.fr/show_bug.cgi?id=3678"
 | |
| 		replace_in_file "$debops_update_script_path" \
 | |
| 			'python -m pip install --quiet --requirement "${PIP_PKG_LIST}" --upgrade' \
 | |
| 			'# python -m pip install --quiet --requirement "${PIP_PKG_LIST}" --upgrade'
 | |
| 		if [ $? != "$RETURNCODE_SUCCESS" ]
 | |
| 		then
 | |
| 			return $RETURNCODE_ERROR
 | |
| 		fi
 | |
| 
 | |
| 		replace_in_file "$debops_update_script_path" \
 | |
| 			'|| error_message "Error with pip packages upgrade ' \
 | |
| 			'# || error_message "Error with pip packages upgrade '
 | |
| 		if [ $? != "$RETURNCODE_SUCCESS" ]
 | |
| 		then
 | |
| 			return $RETURNCODE_ERROR
 | |
| 		fi
 | |
| 	fi
 | |
| 
 | |
| 	local bug_3715_is_fixed='false'
 | |
| 	if [ "$bug_3715_is_fixed" = 'false' ]
 | |
| 	then
 | |
| 		# make sure that the python version is at least 3.10 so that ansible>=2.15 is installed (as debops 3.1 requires ansible>=2.15)
 | |
| 		local python_version=$(${PYTHON_PATH} --version | cut --delimiter=" " --fields=2 --)
 | |
| 		log 'debug' "python_version=$python_version"
 | |
| 
 | |
| 		if is_version_greater_than "3.10" "${python_version}"
 | |
| 		then
 | |
| 			log 'error' "debops only works with python version 3.10 or more, and your version of python is $python_version"
 | |
| 			return $RETURNCODE_ERROR
 | |
| 		fi
 | |
| 
 | |
| 		replace_in_file "$debops_update_script_path" \
 | |
| 			'virtualenv --quiet -- ' \
 | |
| 			"${PYTHON_PATH} -m venv "
 | |
| 		if [ $? != "$RETURNCODE_SUCCESS" ]
 | |
| 		then
 | |
| 			return $RETURNCODE_ERROR
 | |
| 		fi
 | |
| 	fi
 | |
| 
 | |
| 
 | |
| 	local virtual_env_path=$(controller__get_virtualenv_path "$debops_controller_path")
 | |
| 	# no need to call update-debops.sh since update-dev.sh does it
 | |
| 	DEBOPS_VENV="$virtual_env_path" "$local_repos_path/bin/update-dev.sh"
 | |
| }
 | |
| 
 | |
| deboco__update()
 | |
| {
 | |
| 	local debops_controller_path="$1"
 | |
| 	log 'info' "updating debobs controller in $debops_controller_path"
 | |
| 
 | |
| 	reports_path="$(controller__get_reports_path "$debops_controller_path")"
 | |
| 	mkdir -p "${reports_path}"
 | |
| 	report_file_path=${reports_path}/$(date --iso=seconds)-init-${target_host_fqdn}
 | |
| 
 | |
| 	local local_repos_path=$(controller__get_local_repos_path "$debops_controller_path")
 | |
| 	local virtual_env_path=$(controller__get_virtualenv_path "$debops_controller_path")
 | |
| 
 | |
| 	log info "updating repository $local_repos_path"
 | |
| 	pushd "$local_repos_path"
 | |
| 		git pull | tee --append ${report_file_path}
 | |
| 	popd
 | |
| 
 | |
| 	log info "updating debops itself in $virtual_env_path"
 | |
| 	DEBOPS_VENV="$virtual_env_path" "$local_repos_path/bin/update-dev.sh" | tee --append ${report_file_path}
 | |
| }
 | |
| 
 | |
| deboco__configure_machine()
 | |
| {
 | |
| 	local debops_controller_path="$1"
 | |
| 	local target_host_fqdn="$2"  # the machine on which we want to install debops bootstrap, eg alambix-108.ipr.univ-rennes.fr
 | |
| 	log 'info' "configuring $target_host_fqdn using debobs controller $debops_controller_path"
 | |
| 
 | |
| 	local error_code=$RETURNCODE_SUCCESS
 | |
| 
 | |
| 	deboco__init_machine "$debops_controller_path" "$target_host_fqdn"
 | |
| 	if [ $? = 0 ]
 | |
| 	then
 | |
| 		deboco__update_machine "$debops_controller_path" "$target_host_fqdn"
 | |
| 		if [ $? != 0 ]
 | |
| 		then
 | |
| 			log 'error' "update_machine failed"
 | |
| 			error_code=$RETURNCODE_ERROR
 | |
| 		fi
 | |
| 	else
 | |
| 		log 'error' "init_machine failed"
 | |
| 		error_code=$RETURNCODE_ERROR
 | |
| 	fi
 | |
| 
 | |
| 	return "$error_code"
 | |
| }
 | |
| 
 | |
| 
 | |
| deboco__init_machine()
 | |
| {
 | |
| 	local debops_controller_path="$1"
 | |
| 	local target_host_fqdn="$2"  # the machine on which we want to install debops bootstrap, eg alambix-108.ipr.univ-rennes.fr
 | |
| 	log 'info' "installing debops bootstrap on $target_host_fqdn using debobs controller $debops_controller_path"
 | |
| 
 | |
| 	local error_code=$RETURNCODE_SUCCESS
 | |
| 
 | |
| 	reports_path="$(controller__get_reports_path "$debops_controller_path")"
 | |
| 	mkdir -p "${reports_path}"
 | |
| 	report_file_path=${reports_path}/$(date --iso=seconds)-init-${target_host_fqdn}
 | |
| 	echo "installing debops bootstrap on ${target_host_fqdn} (report stored in ${report_file_path})"
 | |
| 
 | |
| 	local local_repos_path=$(controller__get_local_repos_path "$debops_controller_path")
 | |
| 	local virtual_env_path=$(controller__get_virtualenv_path "$debops_controller_path")
 | |
| 	local debops_exit_code=''
 | |
| 	pushd "$local_repos_path"
 | |
| 		source "$virtual_env_path/bin/activate"
 | |
| 		ANS_HOST=$(echo ${target_host_fqdn} | sed -E 's/\.univ-rennes[1]?\.fr$//')
 | |
| 		log 'debug' "ANS_HOST=${ANS_HOST}"
 | |
| 		debops run bootstrap-ldap -l "${ANS_HOST:-/dev/null}" | tee --append ${report_file_path}
 | |
| 		debops_exit_code=$?
 | |
| 		echo "return code for debops run bootstrap-ldap -l ${ANS_HOST:-/dev/null} : $debops_exit_code" >> "${report_file_path}"
 | |
| 		log 'debug' "debops_exit_code=$debops_exit_code"
 | |
| 		if [ "$debops_exit_code" != "$RETURNCODE_SUCCESS" ]
 | |
| 		then
 | |
| 			log 'error' 'debops run bootstrap-ldap failed'
 | |
| 			error_code="$RETURNCODE_ERROR"
 | |
| 		fi
 | |
| 		deactivate
 | |
| 	popd
 | |
| 	return "$error_code"
 | |
| }
 | |
| 
 | |
| deboco__update_machine()
 | |
| {
 | |
| 	local debops_controller_path="$1"
 | |
| 	local target_host_fqdn="$2"  # eg alambix-108.ipr.univ-rennes.fr
 | |
| 	log 'info' "updating $target_host_fqdn using debobs controller $debops_controller_path"
 | |
| 
 | |
| 	local error_code=$RETURNCODE_SUCCESS
 | |
| 
 | |
| 	reports_path="$(controller__get_reports_path "$debops_controller_path")"
 | |
| 	mkdir -p "${reports_path}"
 | |
| 	report_file_path=${reports_path}/$(date --iso=seconds)-init-${target_host_fqdn}
 | |
| 	echo "installing debops bootstrap on ${target_host_fqdn} (report stored in ${report_file_path})"
 | |
| 
 | |
| 	local local_repos_path=$(controller__get_local_repos_path "$debops_controller_path")
 | |
| 	local virtual_env_path=$(controller__get_virtualenv_path "$debops_controller_path")
 | |
| 	local debops_exit_code=''
 | |
| 
 | |
| 	pushd "$local_repos_path"
 | |
| 		source "$virtual_env_path/bin/activate"
 | |
| 		ANS_HOST=$(echo ${target_host_fqdn} | sed -E 's/\.univ-rennes[1]?\.fr$//')
 | |
| 		log 'debug' "ANS_HOST=${ANS_HOST}"
 | |
| 		debops run site --limit "${ANS_HOST:-/dev/null}" | tee --append ${report_file_path}
 | |
| 		debops_exit_code=$?
 | |
| 		echo "return code for debops run site --limit ${ANS_HOST:-/dev/null} : $debops_exit_code" >> "${report_file_path}"
 | |
| 		if [ "$debops_exit_code" != "$RETURNCODE_SUCCESS" ]
 | |
| 		then
 | |
| 			log 'error' 'debops run site failed'
 | |
| 			error_code="$RETURNCODE_ERROR"
 | |
| 		fi
 | |
| 		deactivate
 | |
| 	popd
 | |
| 	return "$error_code"
 | |
| }
 | |
| 
 | |
| deboco_print_usage()
 | |
| {
 | |
| 	echo "$0 performs a debops controller operation"
 | |
| 	echo 
 | |
| 	echo "usage:"
 | |
| 	echo " $0 [options] <command>"
 | |
| 	echo 
 | |
| 	echo "where:"
 | |
| 	echo "  [options] is a set of:"
 | |
| 	echo "     --python-path <python-path>"
 | |
| 	echo "        specifies the python executable to use (must be python 3.10 or above). Default is $DEFAULT_PYTHON_PATH"
 | |
| 	echo 
 | |
| 	echo "  <command> is one of:"
 | |
| 	echo "     init <debobs-controller-path>"
 | |
| 	echo "        creates a debops controller in <debobs-controller-path>"
 | |
| 	echo 
 | |
| 	echo "     update <debobs-controller-path>"
 | |
| 	echo "        updates the debops controller in <debobs-controller-path>"
 | |
| 	echo 
 | |
| 	echo "     init_machine <debobs-controller-path> <machine-fqdn>"
 | |
| 	echo "        installs debops bootstrap on the machine <machine-fqdn> using the debops controller in <debobs-controller-path>"
 | |
| 	echo 
 | |
| 	echo "     update_machine <debobs-controller-path> <machine-fqdn>"
 | |
| 	echo "        updates the machine <machine-fqdn> using the debops controller in <debobs-controller-path>"
 | |
| 	echo 
 | |
| 	echo "     configure_machine <debobs-controller-path> <machine-fqdn>"
 | |
| 	echo "        configures the machine <machine-fqdn> using the debops controller in <debobs-controller-path>". Equivivalent to a init_machine followed by update_machine
 | |
| 	echo 
 | |
| 	echo "example:"
 | |
| 	echo "     deboco --python-path /usr/bin/python3.10 init ~/work/debops/constrollers/alambix"
 | |
| }
 | |
| 
 | |
| deboco()
 | |
| {
 | |
| 	PYTHON_PATH="$DEFAULT_PYTHON_PATH"
 | |
| 	while true
 | |
| 	do
 | |
| 		local arg="$1"
 | |
| 		shift
 | |
| 		if [ "$arg" = '--python-path' ]
 | |
| 		then
 | |
| 			PYTHON_PATH="$1"
 | |
| 			shift
 | |
| 		elif type "deboco__${arg}" >/dev/null 2>&1
 | |
| 		then
 | |
| 			"deboco__${arg}" "$@"
 | |
| 			return $?
 | |
| 		else
 | |
| 			log 'error' "unhandled argument: ${arg}"
 | |
| 			deboco_print_usage
 | |
| 			return "$RETURNCODE_ERROR"
 | |
| 		fi
 | |
| 	done
 | |
| }
 | |
| 
 | |
| 
 | |
| deboco $@
 | |
| 
 | |
| 
 |