Class: ServiceInstaller
- Inherits:
-
Object
- Object
- ServiceInstaller
- Defined in:
- lib/service_installer.rb
Overview
Generates and installs the systemd unit for the collector.
The unit is generated rather than shipped because the correct paths are only
knowable on the target host: under rvm/rbenv/asdf the executable does not live
in /usr/local/bin, and systemd's minimal PATH does not include the gem bin
directory that passenger-status is installed into.
Defined Under Namespace
Classes: Error
Constant Summary collapse
- UNIT_NAME =
'passenger-datadog.service'- UNIT_DIR =
'/etc/systemd/system'- BASE_PATH =
systemd starts services with a minimal PATH; these are its usual defaults, which we extend with the directories the resolved binaries actually live in.
%w[/usr/local/sbin /usr/local/bin /usr/sbin /usr/bin /sbin /bin].freeze
- UNIT_TEMPLATE =
<<~UNIT # Generated by `passenger-datadog install-service` on this host. # Paths are resolved for this machine's Ruby; regenerate after a Ruby upgrade. [Unit] Description=Send Passenger stats to Datadog After=network-online.target Wants=network-online.target [Service] Type=notify NotifyAccess=main ExecStart=%<exec_start>s Environment=PATH=%<path>s # systemd inherits no gem environment, so pin the one this Ruby is using. Environment=GEM_HOME=%<gem_home>s Environment=GEM_PATH=%<gem_path>s Restart=on-failure RestartSec=5s # Passenger recommends running passenger-status as root so it can see every # Passenger instance on the host. User=%<user>s Group=%<group>s [Install] WantedBy=multi-user.target UNIT
Instance Attribute Summary collapse
-
#group ⇒ Object
readonly
Returns the value of attribute group.
-
#unit_dir ⇒ Object
readonly
Returns the value of attribute unit_dir.
-
#user ⇒ Object
readonly
Returns the value of attribute user.
Instance Method Summary collapse
-
#exec_start ⇒ Object
Invoke Ruby explicitly so the unit does not depend on the shebang line or on systemd's PATH resolving the executable.
- #executable ⇒ Object
-
#gem_home ⇒ Object
Where this Ruby installs and looks up gems.
- #gem_path ⇒ Object
-
#initialize(user: 'root', group: 'root', unit_dir: UNIT_DIR, executable: nil, passenger_status: nil, search_paths: nil, env: ENV) ⇒ ServiceInstaller
constructor
A new instance of ServiceInstaller.
-
#install(force: false) ⇒ Object
Writes the unit and reloads systemd.
- #passenger_status ⇒ Object
-
#search_paths ⇒ Object
passengeris a runtime dependency, so passenger-status is installed alongside this executable. -
#service_path ⇒ Object
Everything systemd would normally have, plus the directories holding the resolved Ruby and passenger-status, so the backtick call in the collector can find passenger-status.
- #uninstall ⇒ Object
- #unit ⇒ Object
- #unit_path ⇒ Object
Constructor Details
#initialize(user: 'root', group: 'root', unit_dir: UNIT_DIR, executable: nil, passenger_status: nil, search_paths: nil, env: ENV) ⇒ ServiceInstaller
Returns a new instance of ServiceInstaller.
50 51 52 53 54 55 56 57 58 59 |
# File 'lib/service_installer.rb', line 50 def initialize(user: 'root', group: 'root', unit_dir: UNIT_DIR, executable: nil, passenger_status: nil, search_paths: nil, env: ENV) @user = user @group = group @unit_dir = unit_dir @executable = executable @passenger_status = passenger_status @search_paths = search_paths @env = env end |
Instance Attribute Details
#group ⇒ Object (readonly)
Returns the value of attribute group.
48 49 50 |
# File 'lib/service_installer.rb', line 48 def group @group end |
#unit_dir ⇒ Object (readonly)
Returns the value of attribute unit_dir.
48 49 50 |
# File 'lib/service_installer.rb', line 48 def unit_dir @unit_dir end |
#user ⇒ Object (readonly)
Returns the value of attribute user.
48 49 50 |
# File 'lib/service_installer.rb', line 48 def user @user end |
Instance Method Details
#exec_start ⇒ Object
Invoke Ruby explicitly so the unit does not depend on the shebang line or on systemd's PATH resolving the executable.
67 68 69 |
# File 'lib/service_installer.rb', line 67 def exec_start "#{RbConfig.ruby} #{executable}" end |
#executable ⇒ Object
71 72 73 |
# File 'lib/service_installer.rb', line 71 def executable @executable ||= File.($PROGRAM_NAME) end |
#gem_home ⇒ Object
Where this Ruby installs and looks up gems. Under rvm/rbenv these are set by shell integration that systemd never runs, so they have to be baked in.
96 97 98 |
# File 'lib/service_installer.rb', line 96 def gem_home Gem.dir end |
#gem_path ⇒ Object
100 101 102 |
# File 'lib/service_installer.rb', line 100 def gem_path Gem.path.uniq.join(File::PATH_SEPARATOR) end |
#install(force: false) ⇒ Object
Writes the unit and reloads systemd. Returns the path written.
110 111 112 113 114 115 116 117 118 |
# File 'lib/service_installer.rb', line 110 def install(force: false) require_root raise Error, "#{unit_path} already exists; pass --force to overwrite" if File.exist?(unit_path) && !force File.write(unit_path, unit) File.chmod(0o644, unit_path) daemon_reload unit_path end |
#passenger_status ⇒ Object
82 83 84 85 |
# File 'lib/service_installer.rb', line 82 def passenger_status @passenger_status ||= which('passenger-status') || raise(Error, 'passenger-status not found; is the passenger gem installed for this ruby?') end |
#search_paths ⇒ Object
passenger is a runtime dependency, so passenger-status is installed
alongside this executable. Looking there first means the command still works
under sudo, whose secure_path strips rvm/rbenv directories from PATH.
90 91 92 |
# File 'lib/service_installer.rb', line 90 def search_paths @search_paths ||= [File.dirname(executable), Gem.bindir] + @env.fetch('PATH', '').split(File::PATH_SEPARATOR) end |
#service_path ⇒ Object
Everything systemd would normally have, plus the directories holding the resolved Ruby and passenger-status, so the backtick call in the collector can find passenger-status.
78 79 80 |
# File 'lib/service_installer.rb', line 78 def service_path ([File.dirname(passenger_status), File.dirname(RbConfig.ruby)] + BASE_PATH).uniq.join(':') end |
#uninstall ⇒ Object
120 121 122 123 124 125 126 127 128 |
# File 'lib/service_installer.rb', line 120 def uninstall require_root return nil unless File.exist?(unit_path) system('systemctl', 'disable', '--now', UNIT_NAME, out: File::NULL, err: File::NULL) File.delete(unit_path) daemon_reload unit_path end |
#unit ⇒ Object
104 105 106 107 |
# File 'lib/service_installer.rb', line 104 def unit format(UNIT_TEMPLATE, exec_start: exec_start, path: service_path, gem_home: gem_home, gem_path: gem_path, user: user, group: group) end |
#unit_path ⇒ Object
61 62 63 |
# File 'lib/service_installer.rb', line 61 def unit_path File.join(unit_dir, UNIT_NAME) end |