Class: Locd::Agent
- Inherits:
-
Object
- Object
- Locd::Agent
- Includes:
- Comparable, NRSER::Log::Mixin
- Defined in:
- lib/locd/agent.rb
Overview
Represents a backend agent that the proxy can route to.
Agents are managed by launchd
, macOS's system service manager, and
created on-demand by generating "property list" files (.plist
, XML format)
and installing them in the user's ~/Library/LaunchAgents
directory.
From there they can be managed directly with macOS's launchctl
utility,
with the lunchy
gem, etc.
Defined Under Namespace
Modules: System Classes: Job, Proxy, RotateLogs, Site, Status
Constant Summary collapse
- TO_H_NAMES =
Attribute / method names that #to_h uses.
Hamster::SortedSet[:label, :path, :plist, :status]
Creating Agents collapse
-
#path ⇒ Pathname
readonly
Absolute path to the agent's
.plist
file. -
#plist ⇒ Hash<String, V>
readonly
Hash of the agent's
.plist
file (keys and values from the top-level<dict>
element).
Computing Paths collapse
-
.default_log_path(workdir, label) ⇒ Pathname?
Default path for a Agent output file.
-
.plist_abs_path(label) ⇒ Pathname
Absolute path for the plist file given it's label, equivalent to expanding
~/Library/LaunchAgents/<label>.plist
. -
.resolve_log_path(log_path:, workdir:, label:) ⇒ Pathname?
Get the path to log
STDOUT
andSTDERR
to given the option value and other options we need to figure it out if that doesn't suffice. -
.user_plist_abs_dir ⇒ Pathname
Absolute path to
launchd
plist directory for the current user, which is an expansion of~/Library/LaunchAgents
.
Instantiating collapse
-
.class_for(plist) ⇒ Class<Locd::Agent>?
Get the agent class that a plist should be instantiated as.
-
.from_path(plist_path) ⇒ Locd::Agent
Instantiate a Agent from the path to it's
.plist
file.
Querying collapse
-
.all ⇒ Hamster::Hash<String, Locd::Agent>
All Agent that are instances of
self
. -
.exists?(label) ⇒ Boolean
Does this agent exist?.
-
.find_all(pattern, **options) ⇒ Hamster::Hash<String, Locd::Agent>
Find all the agents that match a pattern.
-
.find_all!(pattern, **options) ⇒ Hamster::Hash<String, Locd::Agent>
Just like Agent.find_all but raises if result is empty.
-
.find_only!(*find_all_args) ⇒ Locd::Agent
Find a single Agent matching
pattern
or raise. -
.get(label) ⇒ Locd::Agent?
Get a Loc'd agent by it's label.
-
.labels ⇒ Hamster::Vector<String>
Labels for all installed Loc'd agents.
-
.plists ⇒ Hamster::Hash<String, Locd::Agent>
All installed Loc'd agents.
Creating Agents collapse
-
.add(label:, force: false, workdir: Pathname.getwd, **kwds) ⇒ Locd::Agent
Add an agent, writing a
.plist
to~/Library/LaunchAgents
. -
.add_or_update(label:, **values) ⇒ Array<((:add | :update), Locd::Agent)] Whether the agent was added or updated, followed by the agent instance.
Pretty much what the name says.
-
.create_plist_data(cmd_template:, label:, workdir:, log_path: nil, keep_alive: false, run_at_load: false, **extras) ⇒ Hash<String, Object>
Create the
launchd
property list data for a new Agent. -
.render_cmd(cmd_template:, label:, workdir:, **extras) ⇒ String
Render a command string by substituting any
{name}
parts for their values. -
#default_log_path? ⇒ Boolean
true
if the #log_path is the default one we generate. -
#initialize(plist:, path:) ⇒ Agent
constructor
Constructor ============================================================================.
Instance Methods: Attribute Readers collapse
- #cmd_template ⇒ Object
- #config ⇒ Object
-
#err_path ⇒ Pathname?
Path the agent is logging
STDERR
to. - #label ⇒ String
- #last_exit_code(refresh: false) ⇒ nil, Fixnum
-
#log_paths ⇒ Array<Pathname>
Get a list of all unique log paths.
-
#out_path ⇒ Pathname?
(also: #log_path)
Path the agent is logging
STDOUT
to. -
#pid(refresh: false) ⇒ nil, Fixnum
Current process ID of the agent (if running).
- #running?(refresh: false) ⇒ Boolean
- #stopped?(refresh: false) ⇒ Boolean
-
#workdir ⇒ Pathname
The working directory of the agent.
Instance Methods: `launchctl` Interface collapse
-
#load(force: false, enable: false) ⇒ self
Load the agent by executing
launchctl load [OPTIONS] LABEL
. - #reload(force: false, enable: false) ⇒ self
-
#remove(logs: false) ⇒ self
Remove the agent by removing it's #path file.
- #restart(reload: true, force: true, enable: false) ⇒ self
-
#start(load: true, force: false, enable: false) ⇒ self
Start the agent.
-
#status(refresh: false) ⇒ Status
The agent's status from parsing
launchctl list
. -
#stop(unload: true, disable: false) ⇒ self
Stop the agent.
-
#unload(disable: false) ⇒ self
Unload the agent by executing
launchctl unload [OPTIONS] LABEL
.
Modifying Agents collapse
-
#update(force: false, **values) ⇒ Locd::Agent
Update specific values on the agent, which may change it's file path if a different label is provided.
Instance Methods: Language Integration collapse
-
#<=>(other) ⇒ Fixnum
Compare to another agent by their labels.
- #to_h ⇒ Object
- #to_json(*args) ⇒ Object
-
#to_yaml(*args) ⇒ Object
TODO Doesn't work!.
Class Method Summary collapse
-
.plist?(plist) ⇒ Boolean
Test if the parse of a property list is for a Loc'd agent by seeing if it has the config key (from
Locd.config[:agent, :config_key]
) as a key.
Constructor Details
#initialize(plist:, path:) ⇒ Agent
Constructor
733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 |
# File 'lib/locd/agent.rb', line 733 def initialize plist:, path: @path = path.to_pn. @plist = plist @status = nil # Sanity check... unless plist.key? Locd.config[:agent, :config_key] raise ArgumentError.new binding.erb <<~END Not a Loc'd plist (no <%= Locd.config[:agent, :config_key] %> key) path: <%= path %> plist: <%= plist.pretty_inspect %> END end unless @path.basename( '.plist' ).to_s == label raise ArgumentError.new binding.erb <<~END Label and filename don't match. Filename should be `<label>.plist`, found label: <%= label %> filename: <%= @path.basename %> path: <%= path %> END end init_ensure_out_dirs_exist end |
Instance Attribute Details
#path ⇒ Pathname (readonly)
Absolute path to the agent's .plist
file.
727 728 729 |
# File 'lib/locd/agent.rb', line 727 def path @path end |
#plist ⇒ Hash<String, V> (readonly)
Hash of the agent's .plist
file (keys and values from the top-level
<dict>
element).
720 721 722 |
# File 'lib/locd/agent.rb', line 720 def plist @plist end |
Class Method Details
.add(label:, force: false, workdir: Pathname.getwd, **kwds) ⇒ Locd::Agent
Add an agent, writing a .plist
to ~/Library/LaunchAgents
.
Does not start the agent.
641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 |
# File 'lib/locd/agent.rb', line 641 def self.add label:, force: false, workdir: Pathname.getwd, **kwds logger.debug "Creating {Agent}...", label: label, force: force, workdir: workdir, **kwds plist_abs_path = self.plist_abs_path label # Handle file already existing if File.exists? plist_abs_path logger.debug "Agent already exists!", label: label, path: plist_abs_path if force logger.info "Forcing agent creation (overwrite)", label: label, path: plist_abs_path else raise binding.erb <<~END Agent <%= label %> already exists at: <%= plist_abs_path %> END end end plist_data = create_plist_data label: label, workdir: workdir, **kwds logger.debug "Property list created", data: plist_data plist_string = Plist::Emit.dump plist_data plist_abs_path.write plist_string logger.debug "Property list written", path: plist_abs_path from_path( plist_abs_path ).tap { |agent| agent.send :log_info, "added" } end |
.add_or_update(label:, **values) ⇒ Array<((:add | :update), Locd::Agent)] Whether the agent was added or updated, followed by the agent instance.
Pretty much what the name says.
700 701 702 703 704 705 706 |
# File 'lib/locd/agent.rb', line 700 def self.add_or_update label:, **values if exists? label [:update, get( label ).update( **values )] else [:add, add( label: label, **values )] end end |
.all ⇒ Hamster::Hash<String, Locd::Agent>
All Locd::Agent that are instances of self
.
So, when invoked through all returns all agents.
When invoked from a Locd::Agent subclass, returns all agents that are instances of that subclass - Locd::Agent::Site.all returns all Locd::Agent that are Site instances.
328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 |
# File 'lib/locd/agent.rb', line 328 def self.all # If we're being invoked through {Locd::Agent} itself actually find and # load everyone if self == Locd::Agent plists.each_pair.map { |path, plist| begin agent_class = class_for plist if agent_class.nil? nil else agent = agent_class.new path: path, plist: plist [agent.label, agent] end rescue Exception => error logger.error "Failed to parse Loc'd Agent plist", path: path, error: error, backtrace: error.backtrace nil end }. compact. thru( &Hamster::Hash.method( :new ) ) else # We're being invoked through a {Locd::Agent} subclass, so invoke # through {Locd::Agent} and filter the results to instance of `self`. Locd::Agent.all.select { |label, agent| agent.is_a? self } end end |
.class_for(plist) ⇒ Class<Locd::Agent>?
Get the agent class that a plist should be instantiated as.
233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 |
# File 'lib/locd/agent.rb', line 233 def self.class_for plist # 1. See if it's a Loc'd system agent plist if system_class = Locd::Agent::System.class_for( plist ) return system_class end # 2. See if it's a Loc'd user agent plist if user_class = [ Locd::Agent::Site, Locd::Agent::Job, ].find_bounded( max: 1 ) { |cls| cls.plist? plist }.first return user_class end # 3. Return a vanilla agent if it's a plist for one # # Really, not sure when this would happen... # if Locd::Agent.plist?( plist ) return Locd::Agent end # Nada nil end |
.create_plist_data(cmd_template:, label:, workdir:, log_path: nil, keep_alive: false, run_at_load: false, **extras) ⇒ Hash<String, Object>
Create the launchd
property list data for a new Locd::Agent.
521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 |
# File 'lib/locd/agent.rb', line 521 def self.create_plist_data cmd_template:, label:, workdir:, log_path: nil, keep_alive: false, run_at_load: false, **extras # Configure daemon variables... # Normalize `workdir` to an expanded {Pathname} workdir = workdir.to_pn. # Resolve the log (`STDOUT` & `STDERR`) path log_path = resolve_log_path( log_path: log_path, workdir: workdir, label: label, ).to_s # Interpolate variables into command template cmd = render_cmd( cmd_template: cmd_template, label: label, workdir: workdir, **extras ) # Form the property list hash { # Unique label, format: `locd.<owner>.<name>.<agent.path...>` 'Label' => label, # What to run 'ProgramArguments' => [ # TODO Maybe this should be configurable or smarter in some way? 'bash', # *login* shell... need this to source the user profile and set up # all the ENV vars '-l', # Run the command in the login shell '-c', cmd, ], # Directory to run the command in 'WorkingDirectory' => workdir.to_s, # Where to send STDOUT 'StandardOutPath' => log_path, # Where to send STDERR (we send both to the same file) 'StandardErrorPath' => log_path, # Bring the process back up if it goes down (has backoff and stuff # built-in) 'KeepAlive' => keep_alive, # Start the process when the plist is loaded 'RunAtLoad' => run_at_load, 'ProcessType' => 'Interactive', # Extras we need... `launchd` complains in the system log about this # but it's the easiest way to handle it at the moment Locd.config[:agent, :config_key] => { # Save this too why the hell not, might help debuging at least cmd_template: cmd_template, # Add subclass-specific extras **extras, }.str_keys, # Stuff that *doesn't* work... so you don't try it again, because # Apple's online docs seems totally out of date. # # Not allowed for user agents # 'UserName' => ENV['USER'], # # "The Debug key is no longer respected. Please remove it." # 'Debug' => true, # # Yeah, it would have been nice to just use the plist to store the port, # but this runs into all sorts of impenetrable security mess... gotta # put it somewhere else! Weirdly enough it just totally works outside # of here, so I'm not what the security is really stopping..? # # 'Sockets' => { # 'Listeners' => { # 'SockNodeName' => BIND, # 'SockServiceName' => port, # 'SockType' => 'stream', # 'SockFamily' => 'IPv4', # }, # }, }.reject { |key, value| value.nil? } # Drop any `nil` values end |
.default_log_path(workdir, label) ⇒ Pathname?
Default path for a Locd::Agent output file.
153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 |
# File 'lib/locd/agent.rb', line 153 def self.default_log_path workdir, label tmp_dir = workdir.find_up 'tmp', test: :directory?, result: :path if tmp_dir.nil? logger.warn "Unable to find `tmp` dir, output will not be redirected", workdir: workdir, label: label, stream: stream return nil end unless tmp_dir.writable? logger.warn \ "Found `tmp` dir, but not writable. Output will not be redirected", workdir: workdir, label: label, stream: stream return nil end tmp_dir / 'locd' / "#{ label }.log" end |
.exists?(label) ⇒ Boolean
Does this agent exist?
310 311 312 |
# File 'lib/locd/agent.rb', line 310 def self.exists? label File.exists? plist_abs_path( label ) end |
.find_all(pattern, **options) ⇒ Hamster::Hash<String, Locd::Agent>
Find all the agents that match a pattern.
421 422 423 424 |
# File 'lib/locd/agent.rb', line 421 def self.find_all pattern, ** pattern = Locd::Pattern.from pattern, ** all.select { |label, agent| pattern.match? agent } end |
.find_all!(pattern, **options) ⇒ Hamster::Hash<String, Locd::Agent>
Just like find_all but raises if result is empty.
437 438 439 440 441 442 443 444 445 446 447 448 449 |
# File 'lib/locd/agent.rb', line 437 def self.find_all! pattern, ** pattern = Locd::Pattern.from pattern, ** find_all( pattern ).tap { |agents| if agents.empty? raise NRSER::CountError.new( "No agents found for pattern from #{ pattern.source.inspect }", subject: agents, expected: '#count > 0', ) end } end |
.find_only!(*find_all_args) ⇒ Locd::Agent
Find a single Locd::Agent matching pattern
or raise.
Parameters are passed to Label.regexp_for_glob and the resulting Regexp is matched against each agent's #label.
402 403 404 |
# File 'lib/locd/agent.rb', line 402 def self.find_only! *find_all_args find_all( *find_all_args ).values.to_a.only! end |
.from_path(plist_path) ⇒ Locd::Agent
Instantiate a Locd::Agent from the path to it's .plist
file.
267 268 269 270 271 |
# File 'lib/locd/agent.rb', line 267 def self.from_path plist_path plist = Plist.parse_xml plist_path.to_s class_for( plist ).new plist: plist, path: plist_path end |
.get(label) ⇒ Locd::Agent?
Get a Loc'd agent by it's label.
382 383 384 385 386 387 388 |
# File 'lib/locd/agent.rb', line 382 def self.get label path = plist_abs_path label if path.file? from_path path end end |
.labels ⇒ Hamster::Vector<String>
Labels for all installed Loc'd agents.
366 367 368 |
# File 'lib/locd/agent.rb', line 366 def self.labels all.keys.sort end |
.plist?(plist) ⇒ Boolean
Test if the parse of a property list is for a Loc'd agent by seeing if
it has the config key (from Locd.config[:agent, :config_key]
) as a key.
108 109 110 |
# File 'lib/locd/agent.rb', line 108 def self.plist? plist plist.key? Locd.config[:agent, :config_key] end |
.plist_abs_path(label) ⇒ Pathname
Absolute path for the plist file given it's label, equivalent to expanding
~/Library/LaunchAgents/<label>.plist
.
134 135 136 |
# File 'lib/locd/agent.rb', line 134 def self.plist_abs_path label user_plist_abs_dir / "#{ label }.plist" end |
.plists ⇒ Hamster::Hash<String, Locd::Agent>
All installed Loc'd agents.
284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 |
# File 'lib/locd/agent.rb', line 284 def self.plists Pathname.glob( user_plist_abs_dir / '*.plist' ). map { |path| begin [path, Plist.parse_xml( path.to_s )] rescue Exception => error logger.trace "{Plist.parse_xml} failed to parse plist", path: path.to_s, error: error, backtrace: error.backtrace nil end }. compact. select { |path, plist| plist? plist }. thru( &Hamster::Hash.method( :new ) ) end |
.render_cmd(cmd_template:, label:, workdir:, **extras) ⇒ String
Render a command string by substituting any {name}
parts for their
values.
485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 |
# File 'lib/locd/agent.rb', line 485 def self.render_cmd cmd_template:, label:, workdir:, **extras t.match cmd_template, Array, ->( array ) { array.map {|arg| render_cmd \ cmd_template: arg, label: label, workdir: workdir, **extras }.shelljoin }, String, ->( string ) { { label: label, workdir: workdir, **extras, }.reduce( string ) do |cmd, (key, value)| cmd.gsub "{#{ key }}", value.to_s.shellescape end } end |
.resolve_log_path(log_path:, workdir:, label:) ⇒ Pathname?
Get the path to log STDOUT
and STDERR
to given the option value and other
options we need to figure it out if that doesn't suffice.
Note that we might not figure it out at all (returning nil
), and that
needs to be ok too (though it's not expected to happen all too often).
201 202 203 204 205 206 207 |
# File 'lib/locd/agent.rb', line 201 def self.resolve_log_path log_path:, workdir:, label: if log_path.nil? default_log_path workdir, label else log_path.to_pn. workdir end end |
.user_plist_abs_dir ⇒ Pathname
Absolute path to launchd
plist directory for the current user, which is
an expansion of ~/Library/LaunchAgents
.
121 122 123 |
# File 'lib/locd/agent.rb', line 121 def self.user_plist_abs_dir Pathname.new( '~/Library/LaunchAgents' ). end |
Instance Method Details
#<=>(other) ⇒ Fixnum
Compare to another agent by their labels.
1191 1192 1193 |
# File 'lib/locd/agent.rb', line 1191 def <=> other Locd::Label.compare label, other.label end |
#cmd_template ⇒ Object
834 835 836 |
# File 'lib/locd/agent.rb', line 834 def cmd_template config['cmd_template'] end |
#config ⇒ Object
829 830 831 |
# File 'lib/locd/agent.rb', line 829 def config plist[Locd.config[:agent, :config_key]] end |
#default_log_path? ⇒ Boolean
Returns true
if the #log_path is the default one we generate.
774 775 776 |
# File 'lib/locd/agent.rb', line 774 def default_log_path? log_path == self.class.default_log_path( workdir, label ) end |
#err_path ⇒ Pathname?
Path the agent is logging STDERR
to.
869 870 871 |
# File 'lib/locd/agent.rb', line 869 def err_path plist['StandardErrorPath'].to_pn if plist['StandardErrorPath'] end |
#label ⇒ String
787 788 789 |
# File 'lib/locd/agent.rb', line 787 def label plist['Label'].freeze end |
#last_exit_code(refresh: false) ⇒ nil, Fixnum
824 825 826 |
# File 'lib/locd/agent.rb', line 824 def last_exit_code refresh: false status( refresh: refresh ) && status[:last_exit_code] end |
#load(force: false, enable: false) ⇒ self
Load the agent by executing launchctl load [OPTIONS] LABEL
.
This is a bit low-level; you probably want to use #start.
937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 |
# File 'lib/locd/agent.rb', line 937 def load force: false, enable: false logger.debug "Loading #{ label } agent...", force: force, enable: enable result = Locd::Launchctl.load! path, force: force, write: enable = if result.err =~ /service\ already\ loaded/ "already loaded" else "LOADED" end log_info , status: status self end |
#log_paths ⇒ Array<Pathname>
Get a list of all unique log paths.
878 879 880 881 882 883 |
# File 'lib/locd/agent.rb', line 878 def log_paths [ out_path, err_path, ].compact.uniq end |
#out_path ⇒ Pathname? Also known as: log_path
Path the agent is logging STDOUT
to.
854 855 856 |
# File 'lib/locd/agent.rb', line 854 def out_path plist['StandardOutPath'].to_pn if plist['StandardOutPath'] end |
#pid(refresh: false) ⇒ nil, Fixnum
Current process ID of the agent (if running).
802 803 804 |
# File 'lib/locd/agent.rb', line 802 def pid refresh: false status( refresh: refresh ) && status[:pid] end |
#reload(force: false, enable: false) ⇒ self
979 980 981 982 |
# File 'lib/locd/agent.rb', line 979 def reload force: false, enable: false unload load force: force, enable: enable end |
#remove(logs: false) ⇒ self
1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 |
# File 'lib/locd/agent.rb', line 1061 def remove logs: false stop unload: true if logs log_paths.each { |log_path| if log_path.exists? FileUtils.rm log_path log_info "Removed log", path: log_path.to_s else log_info "Log path does not exist", path: log_path.to_s end } end FileUtils.rm path log_info "REMOVED" self end |
#restart(reload: true, force: true, enable: false) ⇒ self
1047 1048 1049 1050 |
# File 'lib/locd/agent.rb', line 1047 def restart reload: true, force: true, enable: false stop unload: reload start load: reload, force: force, enable: enable end |
#running?(refresh: false) ⇒ Boolean
807 808 809 |
# File 'lib/locd/agent.rb', line 807 def running? refresh: false status( refresh: refresh )[:running] end |
#start(load: true, force: false, enable: false) ⇒ self
Start the agent.
If load
is true
, calls #load first, and defaults it's force
keyword
argument to true
(the idea being that you actually want the agent to
start, even if it's #disabled?).
999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 |
# File 'lib/locd/agent.rb', line 999 def start load: true, force: false, enable: false logger.trace "Starting `#{ label }` agent...", load: load, force: force, enable: enable self.load( force: force, enable: enable ) if load Locd::Launchctl.start! label log_info "STARTED" self end |
#status(refresh: false) ⇒ Status
The agent's status from parsing launchctl list
.
Status is read on demand and cached on the instance.
901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 |
# File 'lib/locd/agent.rb', line 901 def status refresh: false if refresh || @status.nil? raw_status = Locd::Launchctl.status[label] # Happens when the agent is not loaded @status = if raw_status.nil? Status.new \ loaded: false, running: false, pid: nil, last_exit_code: nil else Status.new \ loaded: true, running: !raw_status[:pid].nil?, pid: raw_status[:pid], last_exit_code: raw_status[:status] end end @status end |
#stop(unload: true, disable: false) ⇒ self
Stop the agent.
1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 |
# File 'lib/locd/agent.rb', line 1023 def stop unload: true, disable: false logger.debug "Stopping `#{ label } agent...`", unload: unload, disable: disable Locd::Launchctl.stop label log_info "STOPPED" self.unload( disable: disable ) if unload self end |
#stopped?(refresh: false) ⇒ Boolean
812 813 814 |
# File 'lib/locd/agent.rb', line 812 def stopped? refresh: false !running?( refresh: refresh ) end |
#to_h ⇒ Object
1196 1197 1198 1199 1200 |
# File 'lib/locd/agent.rb', line 1196 def to_h self.class::TO_H_NAMES.map { |name| [name, send( name )] }.to_h end |
#to_json(*args) ⇒ Object
1203 1204 1205 |
# File 'lib/locd/agent.rb', line 1203 def to_json *args to_h.to_json *args end |
#to_yaml(*args) ⇒ Object
TODO Doesn't work!
1210 1211 1212 |
# File 'lib/locd/agent.rb', line 1210 def to_yaml *args to_h.to_yaml *args end |
#unload(disable: false) ⇒ self
Unload the agent by executing launchctl unload [OPTIONS] LABEL
.
This is a bit low-level; you probably want to use #stop.
963 964 965 966 967 968 969 970 971 |
# File 'lib/locd/agent.rb', line 963 def unload disable: false logger.debug "Unloading #{ label } agent...", disable: disable result = Locd::Launchctl.unload! path, write: disable log_info "UNLOADED" self end |
#update(force: false, **values) ⇒ Locd::Agent
Update specific values on the agent, which may change it's file path if a different label is provided.
Does not mutate this instance! Returns a new Locd::Agent with the updated values.
1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 |
# File 'lib/locd/agent.rb', line 1102 def update force: false, **values logger.trace "Updating `#{ label }` agent", **values # Remove the `cmd_template` if it's nil of an empty array so that # we use the current one if values[:cmd_template].nil? || (values[:cmd_template].is_a?( Array ) && values[:cmd_template].empty?) values.delete :cmd_template end # Make a new plist new_plist_data = self.class.create_plist_data( label: label, workdir: workdir, log_path: ( values.key?( :log_path ) ? values.key?( :log_path ) : ( default_log_path? ? nil : log_path ) ), # Include the config values, which have the `cmd_template` as well as # any extras. Need to symbolize the keys to make the kwds call work **config.sym_keys, # Now merge over with the values we received **values.except( :log_path ) ) new_label = new_plist_data['Label'] new_plist_abs_path = self.class.plist_abs_path new_label if new_label == label # We maintained the same label, overwrite the file path.write Plist::Emit.dump( new_plist_data ) # Load up the new agent from the same path, reload and return it self.class.from_path( path ).reload else # The label has changed, so we want to make sure we're not overwriting # another agent if File.exists? new_plist_abs_path # There's someone already there! # Bail out unless we are forcing the operation if force logger.info "Overwriting agent #{ new_label } with update to " \ "agent #{ label } (force: `true`)" else raise binding.erb <<-END A different agent already exists at: <%= new_plist_abs_path %> Remove that agent first if you really want to replace it with an updated version of this one or provide `force: true`. END end end # Ok, we're in the clear (save for the obvious race condition, but, # hey, it's a development tool, so fuck it... it's not even clear it's # possible to do an atomic file add from Ruby) new_plist_abs_path.write Plist::Emit.dump( new_plist_data ) # Remove this agent remove # And instantiate and load a new agent from the new path self.class.from_path( new_plist_abs_path ).load end end |
#workdir ⇒ Pathname
Returns The working directory of the agent.
841 842 843 |
# File 'lib/locd/agent.rb', line 841 def workdir plist['WorkingDirectory'].to_pn end |