locd 0.1.3

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.
Files changed (50) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +108 -0
  3. data/.gitmodules +9 -0
  4. data/.qb-options.yml +4 -0
  5. data/.rspec +3 -0
  6. data/.travis.yml +5 -0
  7. data/.yardopts +7 -0
  8. data/Gemfile +10 -0
  9. data/LICENSE.txt +21 -0
  10. data/README.md +72 -0
  11. data/Rakefile +6 -0
  12. data/VERSION +1 -0
  13. data/config/default.yml +24 -0
  14. data/doc/files/design/domains_and_labels.md +117 -0
  15. data/doc/files/topics/agents.md +16 -0
  16. data/doc/files/topics/launchd.md +15 -0
  17. data/doc/files/topics/plists.md +39 -0
  18. data/doc/include/plist.md +3 -0
  19. data/exe/locd +24 -0
  20. data/lib/locd.rb +75 -0
  21. data/lib/locd/agent.rb +1186 -0
  22. data/lib/locd/agent/job.rb +142 -0
  23. data/lib/locd/agent/proxy.rb +111 -0
  24. data/lib/locd/agent/rotate_logs.rb +82 -0
  25. data/lib/locd/agent/site.rb +174 -0
  26. data/lib/locd/agent/system.rb +270 -0
  27. data/lib/locd/cli.rb +4 -0
  28. data/lib/locd/cli/command.rb +9 -0
  29. data/lib/locd/cli/command/agent.rb +310 -0
  30. data/lib/locd/cli/command/base.rb +243 -0
  31. data/lib/locd/cli/command/job.rb +110 -0
  32. data/lib/locd/cli/command/main.rb +201 -0
  33. data/lib/locd/cli/command/proxy.rb +177 -0
  34. data/lib/locd/cli/command/rotate_logs.rb +152 -0
  35. data/lib/locd/cli/command/site.rb +47 -0
  36. data/lib/locd/cli/table.rb +157 -0
  37. data/lib/locd/config.rb +237 -0
  38. data/lib/locd/config/base.rb +93 -0
  39. data/lib/locd/errors.rb +65 -0
  40. data/lib/locd/label.rb +61 -0
  41. data/lib/locd/launchctl.rb +209 -0
  42. data/lib/locd/logging.rb +360 -0
  43. data/lib/locd/newsyslog.rb +402 -0
  44. data/lib/locd/pattern.rb +193 -0
  45. data/lib/locd/proxy.rb +272 -0
  46. data/lib/locd/proxymachine.rb +34 -0
  47. data/lib/locd/util.rb +49 -0
  48. data/lib/locd/version.rb +26 -0
  49. data/locd.gemspec +66 -0
  50. metadata +262 -0
@@ -0,0 +1,142 @@
1
+ # encoding: UTF-8
2
+ # frozen_string_literal: true
3
+
4
+ # Refinements
5
+ # =======================================================================
6
+
7
+ using NRSER
8
+ using NRSER::Types
9
+
10
+
11
+ # Definitions
12
+ # =======================================================================
13
+
14
+ # A user-defined agent that runs periodically to perform a task.
15
+ #
16
+ class Locd::Agent::Job < Locd::Agent
17
+
18
+ # Modules
19
+ # ============================================================================
20
+
21
+ module Types
22
+ def self.start_calendar_interval
23
+ t.and(
24
+ t.shape(
25
+ minute: t.maybe( t.non_neg_int ),
26
+ hour: t.maybe( t.non_neg_int ),
27
+ day: t.maybe( t.and( t.int, 0..31 ) ),
28
+ weekday: t.maybe( t.and( t.int, 0..7 ) ),
29
+ month: t.maybe( t.and( t.int, 0..11 ) ),
30
+ ),
31
+ # All values can't be `nil`
32
+ t.not( t.hash_( values: nil ) ),
33
+ )
34
+ end
35
+
36
+ def self.start_calendar_intervals
37
+ t.array start_calendar_interval
38
+ end
39
+ end
40
+
41
+
42
+
43
+ # Class Methods
44
+ # ======================================================================
45
+
46
+ # @todo Document plist? method.
47
+ #
48
+ # @param [type] arg_name
49
+ # @todo Add name param description.
50
+ #
51
+ # @return [return_type]
52
+ # @todo Document return value.
53
+ #
54
+ def self.plist? plist
55
+ super( plist ) && ( plist.key?( 'StartInterval' ) ||
56
+ plist.key?( 'StartCalendarInterval' ) )
57
+ end # .plist?
58
+
59
+
60
+ # @!group Creating Servers
61
+ # --------------------------------------------------------------------------
62
+
63
+ # Create the `launchd` property list data for a new {Locd::Agent::Site}, which
64
+ # has an additional `port:` keyword versus {Locd::Agent.create_plist_data}.
65
+ #
66
+ # @param cmd_template (see Locd::Agent.create_plist_data)
67
+ # @param label (see Locd::Agent.create_plist_data)
68
+ # @param workdir (see Locd::Agent.create_plist_data)
69
+ # @param log_path (see Locd::Agent.create_plist_data)
70
+ # @param keep_alive (see Locd::Agent.create_plist_data)
71
+ # @param run_at_load (see Locd::Agent.create_plist_data)
72
+ #
73
+ # @param [Fixnum | Hash] start_interval:
74
+ # How often to start the job:
75
+ #
76
+ # 1. {Fixnum} - Run about every X number of seconds (positive integer).
77
+ # 2. {Hash}
78
+ #
79
+ # @return (see Locd::Agent.create_plist_data)
80
+ #
81
+ # @example Run a job about every minute
82
+ # Locd::Agent::Job.create_plist_date \
83
+ # cmd_template: 'myexe do-some-job',
84
+ # label: 'myexe.do-some-job',
85
+ # workdir: '~',
86
+ # start_interval: 60
87
+ #
88
+ # @example Run a job every hour, on the hour
89
+ # Locd::Agent::Job.create_plist_date \
90
+ # cmd_template: 'myexe do-some-job',
91
+ # label: 'myexe.do-some-job',
92
+ # workdir: '~',
93
+ # start_interval: {
94
+ # minute: 0,
95
+ # }
96
+ #
97
+ def self.create_plist_data cmd_template:,
98
+ label:,
99
+ workdir:,
100
+ start_interval:,
101
+ log_path: nil,
102
+ keep_alive: false,
103
+ run_at_load: false,
104
+ **extras
105
+
106
+ plist_data = super cmd_template: cmd_template,
107
+ label: label,
108
+ workdir: workdir,
109
+ log_path: log_path,
110
+ keep_alive: keep_alive,
111
+ run_at_load: run_at_load,
112
+ **extras
113
+
114
+ start_interval_data = t.match start_interval,
115
+ t.pos_int,
116
+ {"StartInterval" => start_interval},
117
+
118
+ Types.start_calendar_interval,
119
+ ->( interval ) {
120
+ interval.map { |key, value| [key.to_s.capitalize, value] }.to_h
121
+ },
122
+
123
+ Types.start_calendar_intervals,
124
+ ->( intervals ) {
125
+ intervals.map { |interval|
126
+ interval.map { |key, value| [key.to_s.capitalize, value] }.to_h
127
+ }
128
+ }
129
+
130
+ plist_data.merge start_interval_data
131
+ end
132
+
133
+
134
+ # Attributes
135
+ # ======================================================================
136
+
137
+
138
+ # Instance Methods
139
+ # ======================================================================
140
+
141
+
142
+ end # class Locd::Agent::Job
@@ -0,0 +1,111 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Requirements
4
+ # =======================================================================
5
+
6
+ # Stdlib
7
+ # -----------------------------------------------------------------------
8
+
9
+ # Deps
10
+ # -----------------------------------------------------------------------
11
+ require 'plist'
12
+ require 'fileutils'
13
+
14
+ # Project / Package
15
+ # -----------------------------------------------------------------------
16
+
17
+
18
+ # Refinements
19
+ # =======================================================================
20
+
21
+ using NRSER
22
+ using NRSER::Types
23
+
24
+
25
+ # Definitions
26
+ # =======================================================================
27
+
28
+
29
+ # An server {Locd::Agent} (HTTP only at the moment) that the proxy can
30
+ # route requests to.
31
+ #
32
+ class Locd::Agent::Proxy < Locd::Agent
33
+
34
+ # Mixins
35
+ # ============================================================================
36
+
37
+ include Locd::Agent::System
38
+
39
+
40
+ # Constants
41
+ # ==========================================================================
42
+
43
+ # Attribute / method names that {#to_h} uses.
44
+ #
45
+ # @return [Hamster::SortedSet<Symbol>]
46
+ #
47
+ TO_H_NAMES = Locd::Agent::TO_H_NAMES.union [:port, :url]
48
+
49
+
50
+ # Class Methods
51
+ # ==========================================================================
52
+
53
+ # @return [String]
54
+ # The non-namespace part of the proxy's label.
55
+ #
56
+ def self.label_name
57
+ "proxy"
58
+ end
59
+
60
+
61
+ # @todo Document cmd_template method.
62
+ #
63
+ # @param [type] arg_name
64
+ # @todo Add name param description.
65
+ #
66
+ # @return [return_type]
67
+ # @todo Document return value.
68
+ #
69
+ def self.default_cmd_template
70
+ "{bin} proxy run"
71
+ end # .cmd_template
72
+
73
+
74
+ def self.default_write_kwds cmd_template: self.default_cmd_template,
75
+ **kwds
76
+ super(
77
+ keep_alive: true,
78
+ run_at_load: true,
79
+ port: Locd.config[:proxy, :port],
80
+ cmd_template: cmd_template,
81
+ **kwds,
82
+ label: self.label
83
+ )
84
+ end
85
+
86
+
87
+ # Instance Methods
88
+ # ============================================================================
89
+
90
+ # @!group Instance Methods: Attribute Readers
91
+ # ----------------------------------------------------------------------------
92
+ #
93
+ # Methods to read proxied and computed attributes.
94
+ #
95
+
96
+ # @return [Fixnum]
97
+ # Port service runs on.
98
+ def port
99
+ config['port']
100
+ end
101
+
102
+
103
+ # @return [String]
104
+ # The URL the agent can be reached at through the proxy.
105
+ def url
106
+ "http://#{ label }:#{ Locd.get_port }"
107
+ end
108
+
109
+ # @!endgroup Instance Methods: Attribute Readers
110
+
111
+ end # class Locd::Agent::Proxy
@@ -0,0 +1,82 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Requirements
4
+ # =======================================================================
5
+
6
+ # Stdlib
7
+ # -----------------------------------------------------------------------
8
+
9
+ # Deps
10
+ # -----------------------------------------------------------------------
11
+ require 'plist'
12
+ require 'fileutils'
13
+
14
+ # Project / Package
15
+ # -----------------------------------------------------------------------
16
+
17
+
18
+ # Refinements
19
+ # =======================================================================
20
+
21
+ using NRSER
22
+ using NRSER::Types
23
+
24
+
25
+ # Definitions
26
+ # =======================================================================
27
+
28
+
29
+ # An server {Locd::Agent} (HTTP only at the moment) that the proxy can
30
+ # route requests to.
31
+ #
32
+ class Locd::Agent::RotateLogs < Locd::Agent::Job
33
+
34
+ # Mixins
35
+ # ============================================================================
36
+
37
+ include Locd::Agent::System
38
+
39
+
40
+ # Class Methods
41
+ # ==========================================================================
42
+
43
+ # @return [String]
44
+ # The non-namespace part of the log rotator agent's label.
45
+ #
46
+ def self.label_name
47
+ "rotate_logs"
48
+ end
49
+
50
+
51
+ # Not much to explain here.
52
+ #
53
+ # @return [String]
54
+ #
55
+ def self.default_cmd_template
56
+ "{bin} rotate-logs run"
57
+ end # .cmd_template
58
+
59
+
60
+ def self.default_start_interval
61
+ NRSER.deep_symbolize_keys Locd.config[:rotate_logs, :start_interval]
62
+ end
63
+
64
+
65
+ # Patch in {.default_cmd_template} as, well, the default for `cmd_template`.
66
+ #
67
+ # @param cmd_template: (see Locd::Agent.render_cmd)
68
+ #
69
+ # @param **kwds (see Locd::Agent::System::ClassMethods#default_write_kwds)
70
+ #
71
+ # @return (see Locd::Agent::System::ClassMethods#default_write_kwds)
72
+ #
73
+ def self.default_write_kwds cmd_template: self.default_cmd_template,
74
+ start_interval: self.default_start_interval,
75
+ **kwds
76
+ super \
77
+ cmd_template: cmd_template,
78
+ start_interval: start_interval,
79
+ **kwds
80
+ end
81
+
82
+ end # class Locd::Agent::RotateLogs
@@ -0,0 +1,174 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Requirements
4
+ # =======================================================================
5
+
6
+ # Stdlib
7
+ # -----------------------------------------------------------------------
8
+
9
+ # Deps
10
+ # -----------------------------------------------------------------------
11
+ require 'plist'
12
+ require 'fileutils'
13
+
14
+ # Project / Package
15
+ # -----------------------------------------------------------------------
16
+
17
+
18
+ # Refinements
19
+ # =======================================================================
20
+
21
+ using NRSER
22
+ using NRSER::Types
23
+
24
+
25
+ # Definitions
26
+ # =======================================================================
27
+
28
+
29
+ # An site is a {Locd::Agent} that the proxy can route HTTP requests to.
30
+ #
31
+ # If you send an HTTP request to the proxy with the site's label as the
32
+ # host, the proxy will start the site if it isn't running and route the
33
+ # request to it's {#port}.
34
+ #
35
+ # Combined with DNSMasq pointing the site's label to the proxy (and using
36
+ # the port the proxy is running on) this allows you to open the site's
37
+ # {#url} in the browser (or make a request to it from anything that resolves
38
+ # DNS properly through DNSMasq... should work fine for APIs and other HTTP
39
+ # services) and get through to the agent.
40
+ #
41
+ class Locd::Agent::Site < Locd::Agent
42
+
43
+ # Constants
44
+ # ==========================================================================
45
+
46
+ # Address we expect servers to bind to so we can reach them. Provided
47
+ # during command rendering as `{bind}` so it can be used when running
48
+ # service commands.
49
+ #
50
+ # For the moment at least this seems to need to be `127.0.0.1` 'cause I
51
+ # think ProxyMachine had trouble connecting to `localhost`, so services
52
+ # need to bind to it or be reachable at it.
53
+ #
54
+ # @return [String]
55
+ #
56
+ BIND = '127.0.0.1'.freeze
57
+
58
+
59
+ # Attribute / method names that {#to_h} uses.
60
+ #
61
+ # @return [Hamster::SortedSet<Symbol>]
62
+ #
63
+ TO_H_NAMES = Locd::Agent::TO_H_NAMES.union [:port, :url]
64
+
65
+
66
+ # Class Methods
67
+ # ==========================================================================
68
+
69
+
70
+ # See if a parsed plist looks like a site.
71
+ #
72
+ # @param [Hash<String, Object>] plist
73
+ # Property list parsed by {Plist.parse_xml}.
74
+ #
75
+ # @return [Boolean]
76
+ # `true` if we think this `plist` belongs to a site.
77
+ #
78
+ def self.plist? plist
79
+ !! plist.dig( Locd::Agent::CONFIG_KEY, 'port' )
80
+ end # .plist?
81
+
82
+
83
+
84
+ # @!group Querying
85
+ # --------------------------------------------------------------------------
86
+
87
+ # Sorted set of all ports configured for installed {Locd::Agent::Site}.
88
+ #
89
+ # Used to determine available ports to allocate when creating new services.
90
+ #
91
+ # @return [Hamster::SortedSet<Fixnum>]
92
+ #
93
+ def self.ports
94
+ Hamster::SortedSet.new all.values.map( &:port )
95
+ end
96
+
97
+ # @!endgroup
98
+
99
+
100
+ # @!group Creating Servers
101
+ # --------------------------------------------------------------------------
102
+
103
+ # Create the `launchd` property list data for a new {Locd::Agent::Site}, which
104
+ # has an additional `port:` keyword versus {Locd::Agent.create_plist_data}.
105
+ #
106
+ # @param cmd_template (see Locd::Agent.create_plist_data)
107
+ # @param label (see Locd::Agent.create_plist_data)
108
+ # @param workdir (see Locd::Agent.create_plist_data)
109
+ # @param log_path (see Locd::Agent.create_plist_data)
110
+ # @param keep_alive (see Locd::Agent.create_plist_data)
111
+ # @param run_at_load (see Locd::Agent.create_plist_data)
112
+ #
113
+ # @param [nil | Fixnum | String] port
114
+ # Port to run the service on. If you don't provide one, one will be
115
+ # provided for you (see {Locd::Proxy.allocate_port}).
116
+ #
117
+ # @param [String] bind
118
+ # Address that the server should bind to.
119
+ #
120
+ # @return (see Locd::Agent.create_plist_data)
121
+ #
122
+ def self.create_plist_data cmd_template:,
123
+ label:,
124
+ workdir:,
125
+ log_path: nil,
126
+ keep_alive: false,
127
+ run_at_load: false,
128
+ port: nil,
129
+ bind: Locd.config[:site, :bind]
130
+ # Allocate a port if one was not provided
131
+ port = if port.nil?
132
+ Locd::Proxy.allocate_port
133
+ else
134
+ # or just normalize to {Fixnum}
135
+ port.to_i
136
+ end
137
+
138
+ super cmd_template: cmd_template,
139
+ label: label,
140
+ workdir: workdir,
141
+ log_path: log_path,
142
+ keep_alive: keep_alive,
143
+ run_at_load: run_at_load,
144
+ # Extras specific to {Locd::Agent::Site}:
145
+ port: port,
146
+ bind: bind
147
+ end # .create_plist_data
148
+
149
+
150
+ # Instance Methods
151
+ # ============================================================================
152
+
153
+ # @!group Instance Methods: Attribute Readers
154
+ # ----------------------------------------------------------------------------
155
+ #
156
+ # Methods to read proxied and computed attributes.
157
+ #
158
+
159
+ # @return [Fixnum]
160
+ # Port service runs on.
161
+ def port
162
+ config['port']
163
+ end
164
+
165
+
166
+ # @return [String]
167
+ # The URL the agent can be reached at through the proxy.
168
+ def url
169
+ "http://#{ label }:#{ Locd::Proxy.port }"
170
+ end
171
+
172
+ # @!endgroup Instance Methods: Attribute Readers
173
+
174
+ end # class Locd::Launchd