locd 0.1.3

Sign up to get free protection for your applications and to get access to all the features.
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