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