This is an automated email from the ASF dual-hosted git repository. fgerlits pushed a commit to branch main in repository https://gitbox.apache.org/repos/asf/nifi-minifi-cpp.git
commit 3a78be611b59f381941c30803a833dbf4abdfa2c Author: Marton Szasz <[email protected]> AuthorDate: Fri Aug 11 05:08:15 2023 +0200 MINIFICPP-2181 Use systemd service management on Linux I generated small parts of this PR with GitHub Copilot. I believe all of the generated snippets are trivial enough to not be copyrightable in their own right, and I would have written the same myself. For this reason, I believe that I own the copyright for the whole code, and it can be licensed under the Apache License 2.0. Signed-off-by: Ferenc Gerlits <[email protected]> This closes #1627 --- .gitignore | 1 - CMakeLists.txt | 2 +- README.md | 16 +- bin/minifi.service | 16 ++ bin/minifi.sh | 356 +++--------------------- docker/test/integration/features/steps/steps.py | 2 +- 6 files changed, 69 insertions(+), 324 deletions(-) diff --git a/.gitignore b/.gitignore index b917c99b8..38cd99d85 100644 --- a/.gitignore +++ b/.gitignore @@ -49,7 +49,6 @@ cmake-build-debug build /*build* bt_state -bin target thirdparty/**/*.o thirdparty/**/*.a diff --git a/CMakeLists.txt b/CMakeLists.txt index 17ea231da..13a2b9924 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -541,7 +541,7 @@ if (NOT WIN32) DESTINATION minifi-python COMPONENT bin) - install(PROGRAMS bin/minifi.sh + install(PROGRAMS bin/minifi.sh bin/minifi.service DESTINATION bin COMPONENT bin) endif() diff --git a/README.md b/README.md index 060eacddf..df1a6ad72 100644 --- a/README.md +++ b/README.md @@ -501,12 +501,18 @@ $ cd nifi-minifi-cpp-* ### Configuring The 'conf' directory in the installation root contains a template config.yml document, minifi.properties, and minifi-log.properties. Please see our [Configuration document](CONFIGURE.md) for details on how to configure agents. +### Installing as a service + +MiNiFi can also be installed as a system service using minifi.sh: + + $ ./bin/minifi.sh install + ### Running -After completing the [installation](#installation), the application can be run by issuing the following command from the installation directory: +After completing the [installation](#installing-as-a-service), the application can be run by issuing the following command from the installation directory: $ ./bin/minifi.sh start -By default, this will make use of a config.yml located in the conf directory. This configuration file location can be altered by adjusting the property `nifi.flow.configuration.file` in minifi.properties located in the conf directory. +By default, this will make use of a config.yml located in the conf directory. This configuration file location can be altered by adjusting the property `nifi.flow.configuration.file` in minifi.properties located in the conf directory. ### Stopping @@ -514,12 +520,6 @@ MiNiFi can then be stopped by issuing: $ ./bin/minifi.sh stop -### Installing as a service - -MiNiFi can also be installed as a system service using minifi.sh with an optional "service name" (default: minifi) - - $ ./bin/minifi.sh install [service name] - ### Running as a docker container You can use the officially released image pulled from the [apache/nifi-minifi-cpp](https://hub.docker.com/r/apache/nifi-minifi-cpp) repository on dockerhub or you can use your locally built image. The container can be run with a specific configuration by mounting the locally edited configuration files to your docker container. diff --git a/bin/minifi.service b/bin/minifi.service new file mode 100644 index 000000000..6f3c43908 --- /dev/null +++ b/bin/minifi.service @@ -0,0 +1,16 @@ +[Unit] +Description=Starts and stops the MiNiFi C++ Agent +After=network.target + +[Service] +Type=simple +Environment=MINIFI_HOME=/opt/minifi-cpp +ExecStart=/opt/minifi-cpp/bin/minifi +Restart=on-failure +RestartSec=3 +KillSignal=SIGTERM +TimeoutStopSec=20 +RestartForceExitStatus=3 + +[Install] +WantedBy=multi-user.target diff --git a/bin/minifi.sh b/bin/minifi.sh index 2e074e4c5..33a395a53 100755 --- a/bin/minifi.sh +++ b/bin/minifi.sh @@ -23,7 +23,6 @@ MINIFI_HOME="$(dirname "${SCRIPTPATH}")" export MINIFI_HOME bin_dir=${MINIFI_HOME}/bin minifi_executable=${bin_dir}/minifi -pid_file=${bin_dir}/.minifi.pid warn() { echo "${PROGNAME}: $*" @@ -57,352 +56,83 @@ detectOS() { fi } -init() { - # Determine if there is special OS handling we must perform - detectOS -} - -# determines the pid -get_pid() { - # Default to a -1 for pid - pid=-1 - # Check to see if we have a pid file - if [ -f "${pid_file}" ]; then - pid=$(cat "${pid_file}") - fi - echo "${pid}" -} - -# Performs a check to see if the provided pid is one that currently exists -active_pid() { - pid=${1} - if [ "${pid}" -eq -1 ]; then - echo 1 - elif [ "${pid}" -eq $$ ]; then - echo 1 - else - kill -s 0 "${pid}" > /dev/null 2>&1 - echo $? - fi -} - -endnow() { - echo "Killing MiNiFi..." - kill -9 "${saved_pid}" > /dev/null 2>&1 - -} - install() { detectOS - if [ "${darwin}" = "true" ] || [ "${cygwin}" = "true" ]; then echo 'Installing Apache MiNiFi as a service is not supported on OS X or Cygwin.' exit 1 fi - SVC_NAME=minifi - if [ "x$2" != "x" ] ; then - SVC_NAME=$2 - fi + echo "Uninstalling any previous versions" + uninstall + + target_dir="/usr/local/lib/systemd/system" + mkdir -p "${target_dir}" || die "Unable to create ${target_dir}. Cannot install MiNiFi as a service." - initd_dir='/etc/init.d' - SVC_FILE="${initd_dir}/${SVC_NAME}" + echo "Installing MiNiFi as a systemd service to ${target_dir}" + cp -fv "${bin_dir}"/minifi.service "${target_dir}" + chmod 644 "${target_dir}"/minifi.service + sed -i "s|/opt/minifi-cpp|${MINIFI_HOME}|g" "${target_dir}"/minifi.service + systemctl daemon-reload + systemctl enable minifi.service +} - if [ ! -w "${initd_dir}" ]; then - echo "Current user does not have write permissions to ${initd_dir}. Cannot install MiNiFi as a service." +uninstall() { + detectOS + if [ "${darwin}" = "true" ] || [ "${cygwin}" = "true" ]; then + echo 'Apache MiNiFi as a service is not supported on OS X or Cygwin.' exit 1 fi -# Create the init script, overwriting anything currently present -cat <<SERVICEDESCRIPTOR > "${SVC_FILE}" -#!/bin/sh -# -# Licensed to the Apache Software Foundation (ASF) under one or more -# contributor license agreements. See the NOTICE file distributed with -# this work for additional information regarding copyright ownership. -# The ASF licenses this file to You under the Apache License, Version 2.0 -# (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -# chkconfig: 2345 20 80 -# description: Apache NiFi MiNiFi is a subproject of Apache nifi to collect data where it originates. -# -# Make use of the configured MINIFI_HOME directory and pass service requests to the minifi executable -export MINIFI_HOME=${MINIFI_HOME} -bin_dir=\${MINIFI_HOME}/bin -minifi_executable=\${bin_dir}/minifi -pid_file="\${bin_dir}/.minifi.pid" - -# determines the pid -get_pid() { - # Default to a -1 for pid - pid=-1 - # Check to see if we have a pid file - if [ -f \${pid_file} ]; then - pid=\$(cat "\${pid_file}") - fi - echo \${pid} -} + # Uninstall legacy init.d service files, if exists + rm -fv "/etc/init.d/minifi" || : + rm -fv "/etc/rc2.d/S65minifi" || : + rm -fv "/etc/rc2.d/K65minifi" || : -# Performs a check to see if the provided pid is one that currently exists -active_pid() { - pid=\${1} - if [ \${pid} -eq -1 ]; then - echo 1 - elif [ "${pid}" -eq $$ ]; then - echo 1 - else - kill -s 0 \${pid} > /dev/null 2>&1 - echo \$? - fi + # Uninstall systemd service files, if exists + rm -fv "/usr/local/lib/systemd/system/minifi.service" || : + systemctl daemon-reload } -saved_pid=\$(get_pid) - -case "\$1" in - start) - if [ "\${saved_pid}" -gt 0 ]; then - if [ \$(active_pid \${saved_pid}) -ne 0 ]; then - echo "PID \${saved_pid} is stale, removing pid file at \${pid_file}"; - if ! rm -f \${pid_file}; then - echo "Could not remove \${pid_file}. File will need to be manually removed." - exit 1; - fi - else - echo "MINIFI is currently running (PID: \${saved_pid}) with pid file \${pid_file}." - exit 0; - fi - fi - \${minifi_executable} & - pid=\$! - echo \${pid} > \${pid_file} - echo Starting MiNiFi with PID \${pid} and pid file \${pid_file} - ;; - stop) - if [ \$(active_pid \${saved_pid}) -ne 0 ]; then - echo "MiNiFi is not currently running." - else - echo "Stopping MiNiFi (PID: \${saved_pid})." - # Send a SIGTERM to MiNiFi so that the handler begins shutdown. - kill -15 \${saved_pid} > /dev/null 2>&1 - if [ \$? -ne 0 ]; then - echo "Could not successfully send termination signal to MiNiFi (PID: \${saved_pid})" - exit 1; - else - # Clean up our pid file - rm -f \${pid_file} - fi - fi - ;; - run) - if [ "\${saved_pid}" -gt 0 ]; then - if ! active_pid \${saved_pid}; then - echo "PID \${saved_pid} is stale, removing pid file at \${pid_file}"; - if ! rm -f \${pid_file}; then - echo "Could not remove \${pid_file}. File will need to be manually removed." - exit 1; - fi - else - echo "MINIFI is currently running (PID: \${saved_pid}) with pid file \${pid_file}." - exit 0; - fi - fi - pid=$$ - echo ${pid} > "${pid_file}" - exec "\${minifi_executable}" - ;; - status) - # interpret status as per LSB specifications - # see: http://refspecs.linuxbase.org/LSB_3.1.0/LSB-Core-generic/LSB-Core-generic/iniscrptact.html - - if [ "\${saved_pid}" -gt 0 ]; then - if [ \$(active_pid \${saved_pid}) -ne 0 ]; then - # program is dead and pid file exists - echo "Program is not currently running but stale pid file (\${pid_file}) exists."; - exit 1 - else - # pid is correct, program is running - echo "MINIFI is currently running (PID: \${saved_pid}) with pid file \${pid_file}." - exit 0; - fi - else - # program is not running - echo "MiNiFi is not currently running." - exit 3; - fi - ;; - update) - if [ -f \${bin_dir}/minifi.update ]; then - \${bin_dir}/minifi.sh stop - cp \${bin_dir}/minifi \${bin_dir}/minifi.bak - cp \${bin_dir}/minifi.update \${bin_dir}/minifi - # ensure that the command is now running - \${bin_dir}/minifi.sh start - saved_pid=\$(get_pid) - if [ "\${saved_pid}" -gt 0 ]; then - if [ \$(active_pid \${saved_pid}) -ne 0 ]; then - cp \${bin_dir}/minifi.bak \${bin_dir}/minifi - \${bin_dir}/minifi.sh start - fi - fi - fi - ;; - restart) - echo "Restarting the MiNiFi service. Hit CTRL+C at any time to forcibly terminate MiNiFi" - "\${bin_dir}/minifi.sh" stop - ticks=1 - printf "Waiting for process to terminate." - trap endnow INT - if [ "\${saved_pid}" -gt 0 ]; then - while [ "\$(active_pid \${saved_pid})" -eq 0 ]; do - sleep 1 - ticks="\$((ticks+1))" - numticks="\$((ticks % 5))" - if [ "\${numticks}" -eq 0 ]; then - printf "." - fi - done - fi - "\${bin_dir}/minifi.sh" start - ;; - *) - echo "Usage: service minifi {start|stop|restart|status}" - ;; -esac - -SERVICEDESCRIPTOR - - if [ ! -f "${SVC_FILE}" ]; then - echo "Could not create service file ${SVC_FILE}" +check_service_installed() { + if [ ! -f "/usr/local/lib/systemd/system/minifi.service" ]; then + echo "MiNiFi is not installed as a service. Please run 'minifi.sh install' first." exit 1 fi - - # Provide the user execute access on the file - chmod u+x "${SVC_FILE}" - - rm -f "/etc/rc2.d/S65${SVC_NAME}" - ln -s "/etc/init.d/${SVC_NAME}" "/etc/rc2.d/S65${SVC_NAME}" || { echo "Could not create link /etc/rc2.d/S65${SVC_NAME}"; exit 1; } - rm -f "/etc/rc2.d/K65${SVC_NAME}" - ln -s "/etc/init.d/${SVC_NAME}" "/etc/rc2.d/K65${SVC_NAME}" || { echo "Could not create link /etc/rc2.d/K65${SVC_NAME}"; exit 1; } - echo "Service ${SVC_NAME} installed" } -saved_pid=$(get_pid) - case "$1" in start) - if [ "${saved_pid}" -gt 0 ]; then - if [ "$(active_pid "${saved_pid}")" -ne 0 ]; then - echo "PID ${saved_pid} is stale, removing pid file at ${pid_file}"; - if ! rm -f "${pid_file}"; then - echo "Could not remove ${pid_file}. File will need to be manually removed." - exit 1; - fi - else - echo "MINIFI is currently running (PID: ${saved_pid}) with pid file ${pid_file}." - exit 0; - fi - fi - ${minifi_executable} & - pid=$! - echo ${pid} > "${pid_file}" - echo "Starting MiNiFi with PID ${pid} and pid file ${pid_file}" + check_service_installed + systemctl start minifi.service + echo "MiNiFi started" ;; stop) - if [ "$(active_pid "${saved_pid}")" -ne 0 ]; then - echo "MiNiFi is not currently running." - else - echo "Stopping MiNiFi (PID: ${saved_pid})." - # Send a SIGTERM to MiNiFi so that the handler begins shutdown. - if ! kill -15 "${saved_pid}" > /dev/null 2>&1; then - echo "Could not successfully send termination signal to MiNiFi (PID: ${saved_pid})" - exit 1; - else - # Clean up our pid file - rm -f "${pid_file}" - fi - fi + check_service_installed + systemctl stop minifi.service + echo "MiNiFi stopped" ;; run) - if [ "${saved_pid}" -gt 0 ]; then - if [ "$(active_pid "${saved_pid}")" -ne 0 ]; then - echo "PID ${saved_pid} is stale, removing pid file at ${pid_file}"; - if ! rm -f "${pid_file}"; then - echo "Could not remove ${pid_file}. File will need to be manually removed." - exit 1; - fi - else - echo "MINIFI is currently running (PID: ${saved_pid}) with pid file ${pid_file}." - exit 0; - fi - fi - pid=$$ - echo ${pid} > "${pid_file}" exec "${minifi_executable}" ;; status) - if [ "${saved_pid}" -gt 0 ]; then - if [ "$(active_pid "${saved_pid}")" -ne 0 ]; then - # program is dead and pid file exists - echo "Program is not currently running but stale pid file (${pid_file}) exists."; - exit 1 - else - # pid is correct, program is running - echo "MINIFI is currently running (PID: ${saved_pid}) with pid file ${pid_file}." - exit 0; - fi - else - # program is not running - echo "MiNiFi is not currently running." - exit 0; - fi + check_service_installed + systemctl status minifi.service ;; - update) - if [ -f "${bin_dir}"/minifi.update ]; then - "${bin_dir}"/minifi.sh stop - cp "${bin_dir}"/minifi "${bin_dir}"/minifi.bak - cp "${bin_dir}"/minifi.update "${bin_dir}"/minifi - # ensure that the command is now running - "${bin_dir}"/minifi.sh start - saved_pid=$(get_pid) - if [ "${saved_pid}" -gt 0 ]; then - if [ "$(active_pid "${saved_pid}")" -ne 0 ]; then - cp "${bin_dir}"/minifi.bak "${bin_dir}"/minifi - "${bin_dir}"/minifi.sh start - fi - fi - fi - ;; restart) - echo "Restarting MiNiFi. Hit CTRL+C at any time to forcibly terminate MiNiFi" - "${bin_dir}"/minifi.sh stop - ticks=1 - printf "Waiting for process to terminate." - trap endnow INT - if [ "${saved_pid}" -gt 0 ]; then - while [ "$(active_pid "${saved_pid}")" -eq 0 ]; do - sleep 1 - ticks=$((ticks+1)) - numticks=$((ticks % 5)) - if [ "${numticks}" -eq 0 ]; then - printf "." - fi - done - fi - "${bin_dir}"/minifi.sh start + check_service_installed + systemctl restart minifi.service + echo "MiNiFi restarted" ;; install) install "$@" + echo "Service minifi installed. Please start it using 'minifi.sh start' or 'systemctl start minifi.service'" + ;; + uninstall) + uninstall "$@" + echo "Service minifi uninstalled. Please remove the ${MINIFI_HOME} directory manually." ;; *) - echo "Usage: minifi.sh {start|stop|run|restart|status|install}" + echo "Usage: minifi.sh {start|stop|run|restart|status|install|uninstall}" ;; esac diff --git a/docker/test/integration/features/steps/steps.py b/docker/test/integration/features/steps/steps.py index b52862ffa..cb4094d48 100644 --- a/docker/test/integration/features/steps/steps.py +++ b/docker/test/integration/features/steps/steps.py @@ -335,7 +335,7 @@ def step_impl(context, flow_name): @given("a transient MiNiFi flow with the name \"{flow_name}\" is set up") def step_impl(context, flow_name): - context.test.acquire_container(context=context, name=flow_name, command=["/bin/sh", "-c", "./bin/minifi.sh start && sleep 10 && ./bin/minifi.sh stop && sleep 100"]) + context.test.acquire_container(context=context, name=flow_name, command=["/bin/sh", "-c", "timeout 10s ./bin/minifi.sh run && sleep 100"]) @given("the provenance repository is enabled in MiNiFi")
