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.
- checksums.yaml +7 -0
- data/.gitignore +108 -0
- data/.gitmodules +9 -0
- data/.qb-options.yml +4 -0
- data/.rspec +3 -0
- data/.travis.yml +5 -0
- data/.yardopts +7 -0
- data/Gemfile +10 -0
- data/LICENSE.txt +21 -0
- data/README.md +72 -0
- data/Rakefile +6 -0
- data/VERSION +1 -0
- data/config/default.yml +24 -0
- data/doc/files/design/domains_and_labels.md +117 -0
- data/doc/files/topics/agents.md +16 -0
- data/doc/files/topics/launchd.md +15 -0
- data/doc/files/topics/plists.md +39 -0
- data/doc/include/plist.md +3 -0
- data/exe/locd +24 -0
- data/lib/locd.rb +75 -0
- data/lib/locd/agent.rb +1186 -0
- data/lib/locd/agent/job.rb +142 -0
- data/lib/locd/agent/proxy.rb +111 -0
- data/lib/locd/agent/rotate_logs.rb +82 -0
- data/lib/locd/agent/site.rb +174 -0
- data/lib/locd/agent/system.rb +270 -0
- data/lib/locd/cli.rb +4 -0
- data/lib/locd/cli/command.rb +9 -0
- data/lib/locd/cli/command/agent.rb +310 -0
- data/lib/locd/cli/command/base.rb +243 -0
- data/lib/locd/cli/command/job.rb +110 -0
- data/lib/locd/cli/command/main.rb +201 -0
- data/lib/locd/cli/command/proxy.rb +177 -0
- data/lib/locd/cli/command/rotate_logs.rb +152 -0
- data/lib/locd/cli/command/site.rb +47 -0
- data/lib/locd/cli/table.rb +157 -0
- data/lib/locd/config.rb +237 -0
- data/lib/locd/config/base.rb +93 -0
- data/lib/locd/errors.rb +65 -0
- data/lib/locd/label.rb +61 -0
- data/lib/locd/launchctl.rb +209 -0
- data/lib/locd/logging.rb +360 -0
- data/lib/locd/newsyslog.rb +402 -0
- data/lib/locd/pattern.rb +193 -0
- data/lib/locd/proxy.rb +272 -0
- data/lib/locd/proxymachine.rb +34 -0
- data/lib/locd/util.rb +49 -0
- data/lib/locd/version.rb +26 -0
- data/locd.gemspec +66 -0
- 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
|