diff --git a/conf/errors.conf.sample b/conf/errors.conf.sample index 2129246..1c24443 100644 --- a/conf/errors.conf.sample +++ b/conf/errors.conf.sample @@ -1,69 +1,81 @@ -# Config parsing errors. +# Deployer config parsing errors. err_deployer_sock_empty=11 err_deployer_sock_dir_not_directory=12 err_deployer_sock_dir_not_writable=13 -err_deployer_sock_already_in_use=14 -err_deployer_sock_not_exist=15 -err_deployer_pid_empty=16 -err_deployer_pid_dir_not_directory=17 -err_deployer_pid_dir_not_writable=18 -err_repos_dir_empty=19 -err_repos_dir_not_directory=20 -err_repos_dir_not_accessible=21 -err_rpms_dir_empty=22 -err_rpms_dir_not_directory=23 -err_rpms_dir_not_accessible=24 -err_runner_username_empty=25 -err_runner_user_not_exist=26 -err_deployer_timeout_empty=27 -err_deployer_timeout_not_valid=28 -err_runner_sock_empty=29 -err_runner_sock_dir_not_directory=30 -err_runner_sock_dir_not_writable=31 -err_runner_sock_already_in_use=32 -err_runner_sock_not_exist=33 -err_deployer_username_empty=34 -err_deployer_user_not_exist=35 -err_runner_cloning_dir_empty=36 -err_runner_cloning_dir_not_directory=37 -err_runner_cloning_dir_not_writable=38 -err_runner_timeout_empty=39 -err_runner_timeout_not_valid=40 -err_git_runner_groupname_empty=41 -err_git_runner_group_not_exist=42 -err_runner_deployer_groupname_empty=43 -err_runner_deployer_group_not_exist=44 -err_pipeline_sock_dir_empty=45 -err_pipeline_sock_dir_not_directory=46 -err_pipeline_sock_dir_not_writable=47 +err_deployer_sock_dir_not_accessible=14 +err_deployer_sock_already_in_use=15 +err_deployer_sock_not_exist=16 +err_deployer_pid_empty=17 +err_deployer_pid_dir_not_directory=18 +err_deployer_pid_dir_not_writable=19 +err_deployer_timeout_empty=20 +err_deployer_timeout_not_valid=21 +err_deployer_username_empty=22 +err_deployer_user_not_exist=23 -# JSON parsing errors -err_json_bad_format=51 -err_pkg_name_missing=52 -err_pkg_name_empty=53 -err_repo_dir_not_exist=54 -err_pkg_version_missing=55 -err_pkg_version_empty=56 -err_rpm_path_not_exist=57 -err_repo_name_missing=58 -err_repo_name_empty=59 -err_repo_hash_missing=60 -err_repo_hash_empty=61 -err_repo_tag_empty=62 +# Runner config parsing errors. +err_runner_username_empty=41 +err_runner_user_not_exist=42 +err_runner_sock_empty=43 +err_runner_sock_dir_not_directory=44 +err_runner_sock_dir_not_writable=45 +err_runner_sock_already_in_use=46 +err_runner_sock_not_exist=47 +err_runner_pid_empty=48 +err_runner_pid_dir_not_directory=49 +err_runner_pid_dir_not_writable=50 +err_runner_pid_not_exist=51 +err_runner_pid_not_readable=52 +err_runner_process_not_running=53 +err_runner_process_not_killed=54 +err_runner_cloning_dir_empty=55 +err_runner_cloning_dir_not_directory=56 +err_runner_cloning_dir_not_writable=57 +err_runner_timeout_empty=58 +err_runner_timeout_not_valid=59 +err_runner_deployer_groupname_empty=60 +err_runner_deployer_group_not_exist=61 +err_repos_dir_empty=62 +err_repos_dir_not_directory=63 +err_repos_dir_not_accessible=64 +err_rpms_dir_empty=65 +err_rpms_dir_not_directory=66 +err_rpms_dir_not_accessible=67 + +# Pipeline config parsing errors. +err_git_runner_groupname_empty=81 +err_git_runner_group_not_exist=82 +err_pipeline_sock_dir_empty=83 +err_pipeline_sock_dir_not_directory=84 +err_pipeline_sock_dir_not_writable=85 + +# JSON parsing errors. +err_json_bad_format=101 +err_pkg_name_missing=102 +err_pkg_name_empty=103 +err_repo_dir_not_exist=104 +err_pkg_version_missing=105 +err_pkg_version_empty=106 +err_rpm_path_not_exist=107 +err_repo_name_missing=108 +err_repo_name_empty=109 +err_repo_hash_missing=110 +err_repo_hash_empty=111 +err_repo_tag_empty=112 # Operationnal errors. -err_clone_repo=101 -err_checkout_hash=102 -err_check_format=103 -err_check_linting=104 -err_unit_test=105 -err_add_tag=106 -err_make_tarball=107 -err_rpm_build=108 -err_rpm_upgrade=109 -err_rpm_install=110 -err_deployer_pid_not_exist=111 -err_deployer_pid_not_readable=112 -err_deployer_process_not_running=113 -err_deployer_process_not_running=114 -err_deployer_process_not_killed=115 +err_clone_repo=131 +err_checkout_hash=132 +err_check_format=133 +err_check_linting=134 +err_unit_test=135 +err_add_tag=136 +err_make_tarball=137 +err_rpm_build=138 +err_rpm_upgrade=139 +err_rpm_install=140 +err_deployer_pid_not_exist=141 +err_deployer_pid_not_readable=142 +err_deployer_process_not_running=143 +err_deployer_process_not_running=144 +err_deployer_process_not_killed=145 diff --git a/conf/netoik-cicd.conf.sample b/conf/netoik-cicd.conf.sample index dfef18a..60db50a 100644 --- a/conf/netoik-cicd.conf.sample +++ b/conf/netoik-cicd.conf.sample @@ -23,6 +23,9 @@ runner_username="netoik-cicd-runner" # Location of unixsock file used to send requests to the runner server. runner_sock="/run/netoik-cicd/runner/runner.sock" +# Location of runner pid. +runner_pid="/run/netoik-cicd/pids/runner.pid" + # Directory in which to clone git repositories. runner_cloning_dir="/var/tmp/netoik-cicd/repositories" diff --git a/netoik-cicd.spec b/netoik-cicd.spec index 26ee179..448a3d5 100644 --- a/netoik-cicd.spec +++ b/netoik-cicd.spec @@ -63,9 +63,7 @@ systemctl daemon-reload systemctl restart %{name}-deployer.service systemctl enable %{name}-deployer.service # Restart runner service. -systemctl stop %{name}-runner.service -rm --force %{_rundir}/%{name}/runner/runner.sock -systemctl start %{name}-runner.service +systemctl restart %{name}-runner.service systemctl enable %{name}-runner.service %preun @@ -73,7 +71,6 @@ systemctl enable %{name}-runner.service if [ $1 -eq 0 ]; then systemctl disable --now %{name}-deployer.service systemctl disable --now %{name}-runner.service - rm --force %{_rundir}/%{name}/runner/runner.sock fi %postun diff --git a/src/runner.sh b/src/runner.sh index dc47114..3f1b7c5 100755 --- a/src/runner.sh +++ b/src/runner.sh @@ -70,7 +70,7 @@ fail() ( ) usage() ( - echo "Usage: ${PROGRAM_NAME} [OPTION]... + echo "Usage: ${PROGRAM_NAME} [OPTION]... ACTION Start runner server, wait for unixsock requests to clone git repo, run tests and build rpm package. @@ -83,10 +83,14 @@ Mandatory argumentes for long options are mandatory for short options too. ${DEFAULT_ERRORS_FILE} -o, --once stop listening after first request process -q, --quiet set level verbosity to WARN, default to INFO - -t, --test just test config file and do not run start loop -v, --verbose set level verbosity to DEBUG, default to INFO -h, --help display this help message and exit + +Positional argument ACTION + start start runner server + stop stop runner server + test tests configuration and exit " ) @@ -313,15 +317,14 @@ process_loop() ( main() ( # Parse arguments. daemonize="false" - testing="false" keep_open="true" config_file="${DEFAULT_CONFIG_FILE}" errors_file="${DEFAULT_ERRORS_FILE}" verbosity_option="" verbosity_level="${DEFAULT_VERBOSITY_LEVEL}" if ! args="$(getopt --name "${PROGRAM_NAME}" \ - --options c:de:hoqtv \ - --longoptions conf:,daemon,errs:,help,once,quiet,test,verbose \ + --options c:de:hoqv \ + --longoptions conf:,daemon,errs:,help,once,quiet,verbose \ -- "$@")"; then usage fail "Bad arguments." @@ -359,10 +362,6 @@ main() ( ((verbosity_level -= 10)) shift ;; - -t | --test) - testing="true" - shift - ;; -v | --verbose) if [ "${verbosity_option}" ]; then usage @@ -382,10 +381,19 @@ main() ( ;; esac done - if [ "$@" ]; then + if [ $# -ne 1 ]; then usage - fail "Unexpected extra arguments '$*'." + fail "Missing positional argument ACTION." fi + case "$1" in + start | stop | test) + action="$1" + ;; + *) + usage + fail "Bad postional argument ACTION '$1'." + ;; + esac # Load config file. if [ ! -r "${config_file}" ]; then @@ -407,9 +415,13 @@ main() ( if [ -z "${deployer_sock}" ]; then fail "Variable deployer_sock is empty." "${err_deployer_sock_empty}" fi - if [ ! -S "${deployer_sock}" ]; then - fail "Sock deployer_sock='${deployer_sock}' does not exist." \ - "${err_deployer_sock_not_exist}" + if [ ! -d "$(dirname "${deployer_sock}")" ]; then + fail "Dirname of deployer_sock='${deployer_sock}' is not a directory." \ + "${err_deployer_sock_dir_not_directory}" + fi + if [ ! -x "$(dirname "${deployer_sock}")" ]; then + fail "Dirname of deployer_sock='${deployer_sock}' is not accessible." \ + "${err_deployer_sock_dir_not_accessible}" fi if [ -z "${deployer_timeout}" ]; then @@ -485,28 +497,72 @@ main() ( "${err_git_runner_group_not_exist}" fi - # Stop now if we are only testing config. - if "${testing}"; then + if [ -z "${runner_pid}" ]; then + fail "Variable runner_pid is empty." "${err_runner_pid_empty}" + fi + if [ ! -d "$(dirname "${runner_pid}")" ]; then + fail "Dirname of runner_pid='${runner_pid}' is not a directory." \ + "${err_runner_pid_dir_not_directory}" + fi + if [ ! -w "$(dirname "${runner_pid}")" ]; then + fail "Dirname of runner_pid='${runner_pid}' is not writable." \ + "${err_runner_pid_dir_not_writable}" + fi + + # Run chosen action. + case "${action}" in + start) + if [ ! -S "${deployer_sock}" ]; then + fail "Sock deployer_sock='${deployer_sock}' does not exist." \ + "${err_deployer_sock_not_exist}" + fi + + # Set right access in background after nc listen. + ( + inotifywait --timeout 1 --quiet --quiet \ + --event create "$(dirname "${runner_sock}")" || true + chmod 775 "${runner_sock}" + chgrp "${git_runner_groupname}" "${runner_sock}" + ) & + + # Run process loop in background or foreground. + if "${daemonize}"; then + log_info "Run process loop in background." + process_loop & + echo "$!" >"${runner_pid}" + else + log_info "Run process loop in foreground." + echo "$$" >"${runner_pid}" + process_loop + jobs -p | xargs kill 2>/dev/null || true + fi + ;; + stop) + if [ ! -f "${runner_pid}" ]; then + fail "File runner_pid='${runner_pid}' does not exist." \ + "${err_runner_pid_not_exist}" + fi + if [ ! -r "${runner_pid}" ]; then + fail "File runner_pid='${runner_pid}' is not readable." \ + "${err_runner_pid_not_readable}" + fi + pid="$(cat "${runner_pid}")" + rm --force "${runner_pid}" + if ! ps -p "${pid}"; then + fail "Runner process with pid='${pid}' is not running." \ + "${err_runner_process_not_running}" + fi + if ! kill "${pid}" || kill --signal KILL "${pid}"; then + fail "Cannot kill runner process." "${err_runner_process_not_killed}" + fi + rm --force "${runner_sock}" + ;; + test) + # Stop now if we are only testing. + log_info "Configuration OK." exit 0 - fi - - # Set right access in background after nc listen. - ( - inotifywait --timeout 1 --quiet --quiet \ - --event create "$(dirname "${runner_sock}")" || true - chmod 775 "${runner_sock}" - chgrp "${git_runner_groupname}" "${runner_sock}" - ) & - - # Run process loop in background or foreground. - if "${daemonize}"; then - log_info "Run process loop in background." - process_loop & - else - log_info "Run process loop in foreground." - process_loop - jobs -p | xargs kill 2>/dev/null || true - fi + ;; + esac ) main "$@" diff --git a/systemd/runner.service b/systemd/runner.service index 6c11886..736ac3c 100644 --- a/systemd/runner.service +++ b/systemd/runner.service @@ -5,7 +5,8 @@ After=network.target netoik-cicd-deployer.service [Service] User=netoik-cicd-runner Group=netoik-cicd-runner-deployer -ExecStart=/usr/bin/netoik-cicd-runner +ExecStart=/usr/bin/netoik-cicd-runner start +ExecStop=/usr/bin/netoik-cicd-runner stop Restart=always [Install] diff --git a/tests/runner.bats b/tests/runner.bats index 1b02d2b..8bf3b56 100644 --- a/tests/runner.bats +++ b/tests/runner.bats @@ -37,7 +37,7 @@ teardown() { @test "run without loop" { ncat --listen --unixsock "${deployer_sock}" & - "${runner_bin}" --conf="${conf_file}" --errs="${errs_file}" --test --verbose + "${runner_bin}" --conf="${conf_file}" --errs="${errs_file}" --verbose test echo "done" | ncat --unixsock "${deployer_sock}" } @@ -46,10 +46,11 @@ send_request() ( ncat --listen --unixsock "${deployer_sock}" & "${runner_bin}" --conf="${conf_file}" --errs="${errs_file}" \ - --daemon --once --verbose + --daemon --once --verbose start code=$? if [ "${code}" -ne 0 ]; then echo "fail" | ncat --unixsock "${deployer_sock}" + "${runner_bin}" --conf="${conf_file}" --errs="${errs_file}" stop return "${code}" fi ( @@ -58,7 +59,9 @@ send_request() ( echo "${request}" | ncat --unixsock "${runner_sock}" ) & echo "done" | ncat --unixsock "${deployer_sock}" - return "$(ncat --listen --unixsock "${response_sock}" | jq .code)" + code="$(ncat --listen --unixsock "${response_sock}" | jq .code)" + "${runner_bin}" --conf="${conf_file}" --errs="${errs_file}" stop + return "${code}" ) @test "run with err_repo_name_missing" { diff --git a/tests/tests.conf b/tests/tests.conf index ea2cb73..b378742 100644 --- a/tests/tests.conf +++ b/tests/tests.conf @@ -38,6 +38,9 @@ runner_username="$(id --user --name)" # Location of unixsock file used to send requests to the runner server. runner_sock="${temp}${run_dir}/${name}/runner/runner.sock" +# Location of deployer pid. +runner_pid="${temp}${run_dir}/${name}/pids/runner.pid" + # Directory in which to clone git repositories. runner_cloning_dir="${temp}${tmp_dir}/${name}/repositories"