locd 0.1.3 → 0.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/VERSION +1 -1
- data/exe/locd +1 -9
- data/lib/locd/agent.rb +108 -78
- data/lib/locd/agent/job.rb +3 -2
- data/lib/locd/agent/site.rb +1 -1
- data/lib/locd/agent/system.rb +1 -1
- data/lib/locd/cli/command/agent.rb +260 -53
- data/lib/locd/cli/command/job.rb +38 -30
- data/lib/locd/cli/command/main.rb +3 -10
- data/lib/locd/cli/command/proxy.rb +2 -2
- data/lib/locd/cli/command/rotate_logs.rb +24 -7
- data/lib/locd/label.rb +3 -3
- data/lib/locd/launchctl.rb +38 -1
- data/lib/locd/logging.rb +49 -10
- data/lib/locd/newsyslog.rb +1 -4
- data/lib/locd/pattern.rb +41 -5
- data/lib/locd/version.rb +1 -1
- data/locd.gemspec +3 -3
- metadata +8 -8
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 630061e7d172448491a1da49c70cfac510519d26
|
4
|
+
data.tar.gz: 417ac04a01d1bc9e6e888f3e8a1e847407d7b017
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f3bf11b09b944869c254175fd90984f54959596c9506220d9a79ec49fe6a4ee7d7beb355bff2773b6db552d7bec74f69d3e1c3fb7a313550790ff3334384f72e
|
7
|
+
data.tar.gz: 5c03efd66d67973b72a2608ee21c695b2e9aa473c7f9659c713da8a13736799e8941406a2c4d5b2f24ddcfff56915badb785c5ef7c949c580c728d3b49e3d5e5
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.1.
|
1
|
+
0.1.4
|
data/exe/locd
CHANGED
data/lib/locd/agent.rb
CHANGED
@@ -40,14 +40,6 @@ class Locd::Agent
|
|
40
40
|
# Constants
|
41
41
|
# ==========================================================================
|
42
42
|
|
43
|
-
# Extra plist key we store Loc'd config under (this is where the port is
|
44
|
-
# stored). We also use it's presence to detect that a plist is Loc'd.
|
45
|
-
#
|
46
|
-
# @return [String]
|
47
|
-
#
|
48
|
-
CONFIG_KEY = 'locd_config'
|
49
|
-
|
50
|
-
|
51
43
|
# Attribute / method names that {#to_h} uses.
|
52
44
|
#
|
53
45
|
# @return [Hamster::SortedSet<Symbol>]
|
@@ -69,7 +61,7 @@ class Locd::Agent
|
|
69
61
|
# ==========================================================================
|
70
62
|
|
71
63
|
# Test if the parse of a property list is for a Loc'd agent by seeing if
|
72
|
-
# it has
|
64
|
+
# it has the config key (from `Locd.config[:agent, :config_key]`) as a key.
|
73
65
|
#
|
74
66
|
# @param [Hash<String, Object>] plist
|
75
67
|
# {include:file:doc/include/plist.md}
|
@@ -78,7 +70,7 @@ class Locd::Agent
|
|
78
70
|
# `true` if the plist looks like it's from Loc'd.
|
79
71
|
#
|
80
72
|
def self.plist? plist
|
81
|
-
plist.key?
|
73
|
+
plist.key? Locd.config[:agent, :config_key]
|
82
74
|
end # .plist?
|
83
75
|
|
84
76
|
|
@@ -351,7 +343,7 @@ class Locd::Agent
|
|
351
343
|
# Parameters are passed to {Locd::Label.regexp_for_glob} and the resulting
|
352
344
|
# {Regexp} is matched against each agent's {#label}.
|
353
345
|
#
|
354
|
-
# @see Locd::
|
346
|
+
# @see Locd::Pattern
|
355
347
|
#
|
356
348
|
# @param (see .find_all)
|
357
349
|
#
|
@@ -364,7 +356,7 @@ class Locd::Agent
|
|
364
356
|
|
365
357
|
# Find all the agents that match a pattern.
|
366
358
|
#
|
367
|
-
# @see Locd::
|
359
|
+
# @see Locd::Pattern
|
368
360
|
#
|
369
361
|
# @param [String | Pattern] pattern
|
370
362
|
# Pattern to match against agent.
|
@@ -539,7 +531,7 @@ class Locd::Agent
|
|
539
531
|
|
540
532
|
# Extras we need... `launchd` complains in the system log about this
|
541
533
|
# but it's the easiest way to handle it at the moment
|
542
|
-
|
534
|
+
Locd.config[:agent, :config_key] => {
|
543
535
|
# Save this too why the hell not, might help debuging at least
|
544
536
|
cmd_template: cmd_template,
|
545
537
|
|
@@ -636,27 +628,11 @@ class Locd::Agent
|
|
636
628
|
logger.debug "Property list written", path: plist_abs_path
|
637
629
|
|
638
630
|
from_path( plist_abs_path ).tap { |agent|
|
639
|
-
|
631
|
+
agent.send :log_info, "added"
|
640
632
|
}
|
641
633
|
end # .add
|
642
634
|
|
643
635
|
|
644
|
-
# @!group Removing Agents
|
645
|
-
# ----------------------------------------------------------------------------
|
646
|
-
|
647
|
-
# Find an agent using a label pattern and call {#remove} on it.
|
648
|
-
#
|
649
|
-
# @param (see .find_only!)
|
650
|
-
# @return (see #remove)
|
651
|
-
# @raise See {.find_only!} and {#remove}
|
652
|
-
#
|
653
|
-
def self.remove *find_only_args
|
654
|
-
Locd::Agent.find_only!( *find_only_args ).remove
|
655
|
-
end # .remove
|
656
|
-
|
657
|
-
# @!endgroup
|
658
|
-
|
659
|
-
|
660
636
|
# Attributes
|
661
637
|
# ============================================================================
|
662
638
|
|
@@ -689,9 +665,9 @@ class Locd::Agent
|
|
689
665
|
|
690
666
|
# Sanity check...
|
691
667
|
|
692
|
-
unless plist.key?
|
668
|
+
unless plist.key? Locd.config[:agent, :config_key]
|
693
669
|
raise ArgumentError.new binding.erb <<~END
|
694
|
-
Not a Loc'd plist (no <%= Locd
|
670
|
+
Not a Loc'd plist (no <%= Locd.config[:agent, :config_key] %> key)
|
695
671
|
|
696
672
|
path: <%= path %>
|
697
673
|
plist:
|
@@ -779,7 +755,7 @@ class Locd::Agent
|
|
779
755
|
|
780
756
|
|
781
757
|
def config
|
782
|
-
plist[
|
758
|
+
plist[Locd.config[:agent, :config_key]]
|
783
759
|
end
|
784
760
|
|
785
761
|
|
@@ -822,6 +798,18 @@ class Locd::Agent
|
|
822
798
|
plist['StandardErrorPath'].to_pn if plist['StandardErrorPath']
|
823
799
|
end
|
824
800
|
|
801
|
+
|
802
|
+
# Get a list of all unique log paths.
|
803
|
+
#
|
804
|
+
# @return [Array<Pathname>]
|
805
|
+
#
|
806
|
+
def log_paths
|
807
|
+
[
|
808
|
+
out_path,
|
809
|
+
err_path,
|
810
|
+
].compact.uniq
|
811
|
+
end
|
812
|
+
|
825
813
|
# @!endgroup Instance Methods: Attribute Readers
|
826
814
|
|
827
815
|
|
@@ -875,15 +863,15 @@ class Locd::Agent
|
|
875
863
|
# @param [Boolean] force:
|
876
864
|
# Force the loading of the plist. Ignore the `launchd` *Disabled* key.
|
877
865
|
#
|
878
|
-
# @param [Boolean]
|
866
|
+
# @param [Boolean] enable:
|
879
867
|
# Overrides the `launchd` *Disabled* key and sets it to `false`.
|
880
868
|
#
|
881
869
|
# @return [self]
|
882
870
|
#
|
883
|
-
def load force: false,
|
884
|
-
logger.debug "Loading #{ label } agent...", force: force,
|
871
|
+
def load force: false, enable: false
|
872
|
+
logger.debug "Loading #{ label } agent...", force: force, enable: enable
|
885
873
|
|
886
|
-
result = Locd::Launchctl.load! path, force: force, write:
|
874
|
+
result = Locd::Launchctl.load! path, force: force, write: enable
|
887
875
|
|
888
876
|
message = if result.err =~ /service\ already\ loaded/
|
889
877
|
"already loaded"
|
@@ -891,7 +879,7 @@ class Locd::Agent
|
|
891
879
|
"LOADED"
|
892
880
|
end
|
893
881
|
|
894
|
-
|
882
|
+
log_info message, status: status
|
895
883
|
|
896
884
|
self
|
897
885
|
end
|
@@ -901,17 +889,17 @@ class Locd::Agent
|
|
901
889
|
#
|
902
890
|
# This is a bit low-level; you probably want to use {#stop}.
|
903
891
|
#
|
904
|
-
# @param [Boolean]
|
892
|
+
# @param [Boolean] disable:
|
905
893
|
# Overrides the `launchd` *Disabled* key and sets it to `true`.
|
906
894
|
#
|
907
895
|
# @return [self]
|
908
896
|
#
|
909
|
-
def unload
|
910
|
-
logger.debug "Unloading #{ label } agent...",
|
897
|
+
def unload disable: false
|
898
|
+
logger.debug "Unloading #{ label } agent...", disable: disable
|
911
899
|
|
912
|
-
result = Locd::Launchctl.unload! path, write:
|
900
|
+
result = Locd::Launchctl.unload! path, write: disable
|
913
901
|
|
914
|
-
|
902
|
+
log_info "UNLOADED"
|
915
903
|
|
916
904
|
self
|
917
905
|
end
|
@@ -922,9 +910,9 @@ class Locd::Agent
|
|
922
910
|
# @param (see #load)
|
923
911
|
# @return (see #load)
|
924
912
|
#
|
925
|
-
def reload force: false,
|
913
|
+
def reload force: false, enable: false
|
926
914
|
unload
|
927
|
-
load force: force,
|
915
|
+
load force: force, enable: enable
|
928
916
|
end
|
929
917
|
|
930
918
|
|
@@ -935,23 +923,23 @@ class Locd::Agent
|
|
935
923
|
# start, even if it's {#disabled?}).
|
936
924
|
#
|
937
925
|
# @param [Boolean] load:
|
938
|
-
# Call {#load} first, passing it `
|
926
|
+
# Call {#load} first, passing it `enable` and `force`.
|
939
927
|
#
|
940
|
-
# @param force
|
941
|
-
# @param
|
928
|
+
# @param force (see #load)
|
929
|
+
# @param enable (see #load)
|
942
930
|
#
|
943
931
|
# @return [self]
|
944
932
|
#
|
945
|
-
def start load: true, force:
|
933
|
+
def start load: true, force: false, enable: false
|
946
934
|
logger.trace "Starting `#{ label }` agent...",
|
947
935
|
load: load,
|
948
936
|
force: force,
|
949
|
-
|
937
|
+
enable: enable
|
950
938
|
|
951
|
-
self.load( force: force,
|
939
|
+
self.load( force: force, enable: enable ) if load
|
952
940
|
|
953
941
|
Locd::Launchctl.start! label
|
954
|
-
|
942
|
+
log_info "STARTED"
|
955
943
|
|
956
944
|
self
|
957
945
|
end
|
@@ -962,19 +950,19 @@ class Locd::Agent
|
|
962
950
|
# @param [Boolean] unload:
|
963
951
|
# Call {#unload} first, passing it `write`.
|
964
952
|
#
|
965
|
-
# @param
|
953
|
+
# @param disable (see #unload)
|
966
954
|
#
|
967
955
|
# @return [self]
|
968
956
|
#
|
969
|
-
def stop unload: true,
|
957
|
+
def stop unload: true, disable: false
|
970
958
|
logger.debug "Stopping `#{ label } agent...`",
|
971
959
|
unload: unload,
|
972
|
-
|
960
|
+
disable: disable
|
973
961
|
|
974
962
|
Locd::Launchctl.stop label
|
975
|
-
|
963
|
+
log_info "STOPPED"
|
976
964
|
|
977
|
-
self.unload(
|
965
|
+
self.unload( disable: disable ) if unload
|
978
966
|
|
979
967
|
self
|
980
968
|
end
|
@@ -982,28 +970,44 @@ class Locd::Agent
|
|
982
970
|
|
983
971
|
# Restart the agent ({#stop} then {#start}).
|
984
972
|
#
|
985
|
-
# @param
|
973
|
+
# @param [Boolean] reload:
|
974
|
+
# Unload then load the agent in `launchd`.
|
975
|
+
#
|
986
976
|
# @param force (see #start)
|
987
|
-
# @param
|
977
|
+
# @param enable (see #start)
|
988
978
|
#
|
989
979
|
# @return [self]
|
990
980
|
#
|
991
|
-
def restart
|
992
|
-
stop unload:
|
993
|
-
start load:
|
981
|
+
def restart reload: true, force: true, enable: false
|
982
|
+
stop unload: reload
|
983
|
+
start load: reload, force: force, enable: enable
|
994
984
|
end # #restart
|
995
985
|
|
996
986
|
|
997
987
|
# Remove the agent by removing it's {#path} file. Will {#stop} and
|
998
988
|
# {#unloads} the agent first.
|
999
989
|
#
|
990
|
+
# @param [Boolean] logs:
|
991
|
+
# When `true` remove all logs as well.
|
992
|
+
#
|
1000
993
|
# @return [self]
|
1001
994
|
#
|
1002
|
-
def remove
|
995
|
+
def remove logs: false
|
1003
996
|
stop unload: true
|
1004
997
|
|
998
|
+
if logs
|
999
|
+
log_paths.each { |log_path|
|
1000
|
+
if log_path.exists?
|
1001
|
+
FileUtils.rm log_path
|
1002
|
+
log_info "Removed log", path: log_path.to_s
|
1003
|
+
else
|
1004
|
+
log_info "Log path does not exist", path: log_path.to_s
|
1005
|
+
end
|
1006
|
+
}
|
1007
|
+
end
|
1008
|
+
|
1005
1009
|
FileUtils.rm path
|
1006
|
-
|
1010
|
+
log_info "REMOVED"
|
1007
1011
|
|
1008
1012
|
self
|
1009
1013
|
end
|
@@ -1011,18 +1015,25 @@ class Locd::Agent
|
|
1011
1015
|
# @!endgroup Instance Methods: `launchctl` Interface
|
1012
1016
|
|
1013
1017
|
|
1018
|
+
# @!group Modifying Agents
|
1019
|
+
# --------------------------------------------------------------------------
|
1020
|
+
|
1014
1021
|
# Update specific values on the agent, which *may* change it's file path if
|
1015
1022
|
# a different label is provided.
|
1016
1023
|
#
|
1017
1024
|
# **_Does not mutate this instance! Returns a new {Locd::Agent} with the
|
1018
1025
|
# updated values._**
|
1019
1026
|
#
|
1020
|
-
# @param
|
1027
|
+
# @param [Boolean] force:
|
1028
|
+
# Overwrite any existing agent with the same label.
|
1029
|
+
#
|
1030
|
+
# @param [Hash<Symbol, Object>] **values
|
1031
|
+
# See {.create_plist_data}.
|
1021
1032
|
#
|
1022
1033
|
# @return [Locd::Agent]
|
1023
1034
|
# A new instance with the updated values.
|
1024
1035
|
#
|
1025
|
-
def update **values
|
1036
|
+
def update force: false, **values
|
1026
1037
|
logger.trace "Updating `#{ label }` agent", **values
|
1027
1038
|
|
1028
1039
|
# Remove the `cmd_template` if it's nil of an empty array so that
|
@@ -1066,16 +1077,22 @@ class Locd::Agent
|
|
1066
1077
|
# another agent
|
1067
1078
|
|
1068
1079
|
if File.exists? new_plist_abs_path
|
1069
|
-
# There's someone already there!
|
1070
|
-
|
1071
|
-
|
1072
|
-
|
1073
|
-
|
1074
|
-
|
1075
|
-
|
1076
|
-
|
1077
|
-
|
1078
|
-
|
1080
|
+
# There's someone already there!
|
1081
|
+
# Bail out unless we are forcing the operation
|
1082
|
+
if force
|
1083
|
+
logger.info "Overwriting agent #{ new_label } with update to " \
|
1084
|
+
"agent #{ label } (force: `true`)"
|
1085
|
+
else
|
1086
|
+
raise binding.erb <<-END
|
1087
|
+
A different agent already exists at:
|
1088
|
+
|
1089
|
+
<%= new_plist_abs_path %>
|
1090
|
+
|
1091
|
+
Remove that agent first if you really want to replace it with an
|
1092
|
+
updated version of this one or provide `force: true`.
|
1093
|
+
|
1094
|
+
END
|
1095
|
+
end
|
1079
1096
|
end
|
1080
1097
|
|
1081
1098
|
# Ok, we're in the clear (save for the obvious race condition, but,
|
@@ -1092,6 +1109,7 @@ class Locd::Agent
|
|
1092
1109
|
end
|
1093
1110
|
end # #update
|
1094
1111
|
|
1112
|
+
# @!endgroup Modifying Agents
|
1095
1113
|
|
1096
1114
|
|
1097
1115
|
# @!group Instance Methods: Language Integration
|
@@ -1167,8 +1185,20 @@ class Locd::Agent
|
|
1167
1185
|
end
|
1168
1186
|
|
1169
1187
|
|
1170
|
-
|
1171
|
-
|
1188
|
+
# Log a message with a details payload, prefixing the agent's label.
|
1189
|
+
#
|
1190
|
+
# Used to relay info to the user in the CLI.
|
1191
|
+
#
|
1192
|
+
# @param [#to_s] message
|
1193
|
+
# Message to log.
|
1194
|
+
#
|
1195
|
+
# @param [Hash<Symbol, Object>] **payload
|
1196
|
+
# Optional values to dump.
|
1197
|
+
#
|
1198
|
+
# @return [void]
|
1199
|
+
#
|
1200
|
+
def log_info message, **payload
|
1201
|
+
logger.info "Agent `#{ label }` #{ message }", **payload
|
1172
1202
|
end
|
1173
1203
|
|
1174
1204
|
# end protected
|
data/lib/locd/agent/job.rb
CHANGED
@@ -113,11 +113,12 @@ class Locd::Agent::Job < Locd::Agent
|
|
113
113
|
|
114
114
|
start_interval_data = t.match start_interval,
|
115
115
|
t.pos_int,
|
116
|
-
{"StartInterval" => start_interval},
|
116
|
+
{ "StartInterval" => start_interval },
|
117
117
|
|
118
118
|
Types.start_calendar_interval,
|
119
119
|
->( interval ) {
|
120
|
-
|
120
|
+
{ 'StartCalendarInterval' =>
|
121
|
+
interval.map { |key, value| [key.to_s.capitalize, value] }.to_h }
|
121
122
|
},
|
122
123
|
|
123
124
|
Types.start_calendar_intervals,
|
data/lib/locd/agent/site.rb
CHANGED
data/lib/locd/agent/system.rb
CHANGED
@@ -117,18 +117,36 @@ class Locd::CLI::Command::Agent < Locd::CLI::Command::Base
|
|
117
117
|
# Options when provided a `PATTERN` argument used to find agents by label.
|
118
118
|
#
|
119
119
|
|
120
|
-
shared_option :
|
120
|
+
shared_option :full,
|
121
121
|
groups: :pattern,
|
122
|
-
desc: "
|
123
|
-
aliases: '-
|
122
|
+
desc: "Require label PATTERN to match entire string",
|
123
|
+
aliases: '-u',
|
124
|
+
type: :boolean
|
125
|
+
|
126
|
+
shared_option :ignore_case,
|
127
|
+
groups: :pattern,
|
128
|
+
desc: "Make label PATTERN case-insensitive",
|
129
|
+
aliases: '-i',
|
124
130
|
type: :boolean
|
125
131
|
|
126
132
|
shared_option :recursive,
|
127
133
|
groups: :pattern,
|
128
|
-
desc: "
|
134
|
+
desc: "Make workdir PATTERN match all subdirs too",
|
129
135
|
aliases: '-r',
|
130
136
|
type: :boolean
|
131
137
|
|
138
|
+
# NOTE We don't expose the workdir pattern's `:cwd` option...
|
139
|
+
# If you want to match a directory from the CLI, just provide that
|
140
|
+
# directory... no reason to specify the `:cwd` in an option then
|
141
|
+
# provide a relative PATTERN
|
142
|
+
#
|
143
|
+
|
144
|
+
# `:multi` Group
|
145
|
+
# ----------------------------------------------------------------------------
|
146
|
+
#
|
147
|
+
# For commands that can match multiple agents.
|
148
|
+
#
|
149
|
+
|
132
150
|
shared_option :all,
|
133
151
|
groups: :multi,
|
134
152
|
desc: "Apply to ALL agents that PATTERN matches",
|
@@ -161,11 +179,25 @@ class Locd::CLI::Command::Agent < Locd::CLI::Command::Base
|
|
161
179
|
aliases: ['--log'],
|
162
180
|
type: :string
|
163
181
|
|
182
|
+
shared_option :keep_alive,
|
183
|
+
groups: :write,
|
184
|
+
desc: "Try to keep the agent running",
|
185
|
+
type: :boolean,
|
186
|
+
default: false
|
187
|
+
|
188
|
+
shared_option :run_at_load,
|
189
|
+
groups: :write,
|
190
|
+
desc: "Start the agent when loading it",
|
191
|
+
type: :boolean,
|
192
|
+
default: false
|
193
|
+
|
194
|
+
|
195
|
+
# `:add` Group
|
196
|
+
# ----------------------------------------------------------------------------
|
164
197
|
|
165
198
|
shared_option :force,
|
166
199
|
groups: :add,
|
167
200
|
desc: "Overwrite any existing agent",
|
168
|
-
aliases: '-f',
|
169
201
|
type: :boolean,
|
170
202
|
default: false
|
171
203
|
|
@@ -177,12 +209,6 @@ class Locd::CLI::Command::Agent < Locd::CLI::Command::Base
|
|
177
209
|
default: true
|
178
210
|
|
179
211
|
|
180
|
-
shared_option :unload,
|
181
|
-
groups: :start,
|
182
|
-
desc: "Unload the agent from `launchd` after stopping",
|
183
|
-
type: :boolean,
|
184
|
-
default: true
|
185
|
-
|
186
212
|
# Commands
|
187
213
|
# ============================================================================
|
188
214
|
|
@@ -191,9 +217,12 @@ class Locd::CLI::Command::Agent < Locd::CLI::Command::Base
|
|
191
217
|
|
192
218
|
desc "ls [PATTERN]",
|
193
219
|
"List agents"
|
220
|
+
|
194
221
|
map list: :ls
|
222
|
+
|
195
223
|
include_options :long,
|
196
224
|
groups: :pattern
|
225
|
+
|
197
226
|
def ls pattern = nil
|
198
227
|
results = if pattern.nil?
|
199
228
|
agent_class.all
|
@@ -205,20 +234,74 @@ class Locd::CLI::Command::Agent < Locd::CLI::Command::Base
|
|
205
234
|
end
|
206
235
|
|
207
236
|
|
208
|
-
|
237
|
+
desc "plist PATTERN",
|
238
|
+
"Print an agent's launchd property list"
|
239
|
+
|
240
|
+
include_options groups: :pattern
|
241
|
+
|
242
|
+
def plist pattern
|
243
|
+
agent = find_only! pattern
|
244
|
+
|
245
|
+
if options[:json] || options[:yaml]
|
246
|
+
respond agent.plist
|
247
|
+
else
|
248
|
+
respond agent.path.read
|
249
|
+
end
|
250
|
+
end
|
251
|
+
|
252
|
+
|
253
|
+
desc 'status PATTERN',
|
254
|
+
"Print agent status"
|
255
|
+
|
256
|
+
include_options groups: :pattern
|
257
|
+
|
258
|
+
def status pattern
|
259
|
+
agent = find_only! pattern
|
260
|
+
respond \
|
261
|
+
label: agent.label,
|
262
|
+
port: agent.port,
|
263
|
+
status: agent.status
|
264
|
+
end
|
265
|
+
|
266
|
+
|
267
|
+
# Commands for Manipulating Agent Definitions
|
209
268
|
# ----------------------------------------------------------------------------
|
210
269
|
|
211
270
|
desc "add CMD_TEMPLATE...",
|
212
|
-
"Add
|
271
|
+
"Add an agent that runs a command in the current directory"
|
272
|
+
|
213
273
|
include_options groups: [:write, :add, :respond_with_agents]
|
214
|
-
|
215
|
-
|
274
|
+
|
275
|
+
def add *cmd_template, **kwds
|
276
|
+
logger.trace __method__.to_s,
|
216
277
|
cmd_template: cmd_template,
|
217
|
-
|
278
|
+
kwds: kwds,
|
279
|
+
options: options
|
218
280
|
|
219
|
-
|
281
|
+
# Merge all the keywords together into the format needed to call
|
282
|
+
# {Locd::Agent.add}
|
283
|
+
kwds.merge! **option_kwds( :force, groups: [:write] ),
|
284
|
+
cmd_template: cmd_template
|
220
285
|
|
221
|
-
|
286
|
+
# Check args
|
287
|
+
|
288
|
+
# `:cmd_template` can not be empty at this point
|
289
|
+
if kwds[:cmd_template].empty? || kwds[:cmd_template].all?( &:empty? )
|
290
|
+
raise Thor::RequiredArgumentMissingError,
|
291
|
+
"CMD_TEMPLATE argument is required to add an agent"
|
292
|
+
end
|
293
|
+
|
294
|
+
# Need a `:label` too
|
295
|
+
unless t.non_empty_str === kwds[:label]
|
296
|
+
raise Thor::RequiredArgumentMissingError,
|
297
|
+
"--label=LABEL option is required to add an agent"
|
298
|
+
end
|
299
|
+
|
300
|
+
# Do the add
|
301
|
+
agent = agent_class.add **kwds
|
302
|
+
|
303
|
+
# Reload (unless we were told not to... usually you want to reload)
|
304
|
+
agent.reload if options[:load]
|
222
305
|
|
223
306
|
respond agent
|
224
307
|
end
|
@@ -226,7 +309,9 @@ class Locd::CLI::Command::Agent < Locd::CLI::Command::Base
|
|
226
309
|
|
227
310
|
desc "update PATTERN [OPTIONS] [-- CMD_TEMPLATE...]",
|
228
311
|
"Update an existing agent"
|
312
|
+
|
229
313
|
include_options groups: [:pattern, :write, :respond_with_agents]
|
314
|
+
|
230
315
|
def update pattern, *cmd_template
|
231
316
|
agent = find_only! pattern
|
232
317
|
|
@@ -240,71 +325,193 @@ class Locd::CLI::Command::Agent < Locd::CLI::Command::Base
|
|
240
325
|
end
|
241
326
|
|
242
327
|
|
243
|
-
desc "open PATTERN",
|
244
|
-
"Open an agent's URL in the browser"
|
245
|
-
include_options groups: [:pattern, :multi]
|
246
|
-
def open pattern
|
247
|
-
find_multi!( pattern ).each do |agent|
|
248
|
-
Cmds! "open %s", agent.url
|
249
|
-
logger.info "Opened agent `#{ agent.label }` at #{ agent.url }"
|
250
|
-
end
|
251
|
-
end
|
252
|
-
|
253
|
-
|
254
328
|
desc "rm PATTERN",
|
255
329
|
"Remove (uninstall, delete) a agent"
|
330
|
+
|
256
331
|
map remove: :rm
|
332
|
+
|
257
333
|
include_options groups: [:pattern, :multi]
|
334
|
+
|
335
|
+
option :logs,
|
336
|
+
desc: "Remove logs too",
|
337
|
+
type: :boolean,
|
338
|
+
default: false
|
339
|
+
|
258
340
|
def rm pattern
|
259
|
-
|
341
|
+
kwds = option_kwds :logs
|
342
|
+
find_multi!( pattern ).each { |agent| agent.remove **kwds }
|
260
343
|
end
|
261
344
|
|
262
345
|
|
263
|
-
|
264
|
-
|
265
|
-
include_options groups: :pattern
|
266
|
-
def plist pattern
|
267
|
-
# TODO Why doesn't this use {#find_only!}?
|
268
|
-
agent = agent_class.find_only! pattern, **option_kwds( :exact )
|
269
|
-
|
270
|
-
if options[:json] || options[:yaml]
|
271
|
-
respond agent.plist
|
272
|
-
else
|
273
|
-
respond agent.path.read
|
274
|
-
end
|
275
|
-
end
|
346
|
+
# Commands for Manipulating Agent State
|
347
|
+
# --------------------------------------------------------------------------
|
276
348
|
|
277
|
-
|
278
349
|
desc "start PATTERN",
|
279
350
|
"Start an agent"
|
351
|
+
|
280
352
|
include_options groups: :pattern
|
281
|
-
|
353
|
+
|
354
|
+
option :load,
|
355
|
+
desc: "Load the agent before starting",
|
356
|
+
type: :boolean,
|
357
|
+
default: true
|
358
|
+
|
359
|
+
option :force,
|
360
|
+
desc: "Force loading of agent even if it's disabled",
|
361
|
+
type: :boolean,
|
362
|
+
default: false
|
363
|
+
|
364
|
+
option :enable,
|
282
365
|
desc: "Set `launchd` *Disabled* key to `false`",
|
283
|
-
type: :boolean
|
366
|
+
type: :boolean,
|
367
|
+
default: false
|
368
|
+
|
284
369
|
def start pattern
|
285
|
-
find_only!( pattern ).start
|
370
|
+
find_only!( pattern ).start **option_kwds( :load, :force, :enable )
|
286
371
|
end
|
287
372
|
|
288
373
|
|
289
374
|
desc "stop PATTERN",
|
290
375
|
"Stop an agent"
|
376
|
+
|
291
377
|
include_options groups: [:pattern, :stop]
|
292
|
-
|
378
|
+
|
379
|
+
option :unload,
|
380
|
+
desc: "Unload the agent from `launchd` after stopping",
|
381
|
+
type: :boolean,
|
382
|
+
default: true
|
383
|
+
|
384
|
+
option :disable,
|
293
385
|
desc: "Set `launchd` *Disabled* key to `true`",
|
294
|
-
type: :boolean
|
386
|
+
type: :boolean,
|
387
|
+
default: false
|
388
|
+
|
295
389
|
def stop pattern
|
296
|
-
find_only!( pattern ).stop **option_kwds( :unload )
|
390
|
+
find_only!( pattern ).stop **option_kwds( :unload, :disable )
|
297
391
|
end
|
298
392
|
|
299
393
|
|
300
394
|
desc "restart PATTERN",
|
301
395
|
"Restart an agent"
|
396
|
+
|
302
397
|
include_options groups: [:pattern, :stop]
|
303
|
-
|
398
|
+
|
399
|
+
option :reload,
|
400
|
+
desc: "Unload and reload the agent in `launchd`",
|
401
|
+
type: :boolean,
|
402
|
+
default: true
|
403
|
+
|
404
|
+
option :force,
|
405
|
+
desc: "Force loading of agent even if it's disabled",
|
406
|
+
type: :boolean,
|
407
|
+
default: false
|
408
|
+
|
409
|
+
option :enable,
|
304
410
|
desc: "Set `launchd` *Disabled* key to `false`",
|
305
|
-
type: :boolean
|
411
|
+
type: :boolean,
|
412
|
+
default: false
|
413
|
+
|
306
414
|
def restart pattern
|
307
|
-
find_only!( pattern ).restart
|
415
|
+
find_only!( pattern ).restart **option_kwds( :reload, :force, :enable )
|
308
416
|
end
|
309
417
|
|
418
|
+
|
419
|
+
# Assorted Other Commands
|
420
|
+
# ----------------------------------------------------------------------------
|
421
|
+
|
422
|
+
desc "open PATTERN",
|
423
|
+
"Open an agent's URL in the browser"
|
424
|
+
|
425
|
+
include_options groups: [:pattern, :multi]
|
426
|
+
|
427
|
+
def open pattern
|
428
|
+
find_multi!( pattern ).each do |agent|
|
429
|
+
Cmds! "open %s", agent.url
|
430
|
+
logger.info "Opened agent `#{ agent.label }` at #{ agent.url }"
|
431
|
+
end
|
432
|
+
end
|
433
|
+
|
434
|
+
|
435
|
+
desc 'truncate_logs PATTERN',
|
436
|
+
"Truncate agent log file(s)."
|
437
|
+
|
438
|
+
include_options groups: [:pattern, :multi]
|
439
|
+
|
440
|
+
option :restart,
|
441
|
+
desc: "Restart the agent after truncation",
|
442
|
+
type: :boolean,
|
443
|
+
default: true
|
444
|
+
|
445
|
+
def truncate_logs pattern
|
446
|
+
find_multi!( pattern ).each do |agent|
|
447
|
+
log_paths = [agent.out_path, agent.err_path].compact.uniq
|
448
|
+
|
449
|
+
unless log_paths.empty?
|
450
|
+
restart = options[:restart] && agent.running?
|
451
|
+
|
452
|
+
agent.stop if restart
|
453
|
+
|
454
|
+
log_paths.each do |log_path|
|
455
|
+
begin
|
456
|
+
log_path.open( 'w' ) { |f| f.truncate 0 }
|
457
|
+
rescue Exception => error
|
458
|
+
logger.error "Failed to truncate #{ log_path }", error
|
459
|
+
else
|
460
|
+
logger.info "Truncated",
|
461
|
+
'file' => log_path.to_s,
|
462
|
+
'agent.label' => agent.label
|
463
|
+
end
|
464
|
+
end # each log_path
|
465
|
+
|
466
|
+
agent.start if restart
|
467
|
+
end # unless log_paths.empty?
|
468
|
+
end # each agent
|
469
|
+
end # #truncate_logs
|
470
|
+
|
471
|
+
|
472
|
+
desc 'tail [OPTIONS] PATTERN [-- TAIL_OPTIONS]',
|
473
|
+
"Tail agent logs"
|
474
|
+
|
475
|
+
include_options groups: :pattern
|
476
|
+
|
477
|
+
option :stream,
|
478
|
+
desc: "Stream to tail. May omit if uses single log file.",
|
479
|
+
aliases: ['-s'],
|
480
|
+
type: :string,
|
481
|
+
enum: ['out', 'err']
|
482
|
+
|
483
|
+
def tail pattern, *tail_options
|
484
|
+
agent = find_only! pattern
|
485
|
+
|
486
|
+
path = case options[:stream]
|
487
|
+
when nil
|
488
|
+
paths = agent.log_paths
|
489
|
+
|
490
|
+
unless paths.length == 1
|
491
|
+
raise Thor::RequiredArgumentMissingError.new binding.erb <<~END
|
492
|
+
Agent `<%= agent.label %>` has multiple log files.
|
493
|
+
|
494
|
+
out: <%= agent.out_path.to_s %>
|
495
|
+
err: <%= agent.err_path.to_s %>
|
496
|
+
|
497
|
+
Must specify one via the `--stream` option.
|
498
|
+
|
499
|
+
END
|
500
|
+
end
|
501
|
+
|
502
|
+
paths[0]
|
503
|
+
|
504
|
+
when 'out'
|
505
|
+
agent.out_path
|
506
|
+
|
507
|
+
when 'err'
|
508
|
+
agent.err_path
|
509
|
+
|
510
|
+
else
|
511
|
+
raise "WTF"
|
512
|
+
end
|
513
|
+
|
514
|
+
exec "tail", *tail_options, path.to_s
|
515
|
+
end # #tail
|
516
|
+
|
310
517
|
end # class Locd::CLI::Command::Agent
|