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
data/lib/locd/config.rb
ADDED
@@ -0,0 +1,237 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
# Requirements
|
5
|
+
# =======================================================================
|
6
|
+
|
7
|
+
# Stdlib
|
8
|
+
# -----------------------------------------------------------------------
|
9
|
+
require 'pathname'
|
10
|
+
|
11
|
+
# Deps
|
12
|
+
# -----------------------------------------------------------------------
|
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
|
+
# @todo document Locd::Config class.
|
29
|
+
class Locd::Config
|
30
|
+
|
31
|
+
# Constants
|
32
|
+
# ===================================================================
|
33
|
+
|
34
|
+
# Absolute path to the default config file (`//config/default.yml`).
|
35
|
+
#
|
36
|
+
# Has to be defined as a constant because no other config is loaded
|
37
|
+
# before it and it contains the ENV var prefix.
|
38
|
+
#
|
39
|
+
# @return [Pathname]
|
40
|
+
#
|
41
|
+
DEFAULT_CONFIG_PATH = Locd::ROOT / 'config' / 'default.yml'
|
42
|
+
|
43
|
+
|
44
|
+
# Absolute path to the dev override config, which will be loaded
|
45
|
+
# last if it exists.
|
46
|
+
#
|
47
|
+
# @return [Pathname]
|
48
|
+
#
|
49
|
+
DEV_CONFIG_PATH = Locd::ROOT / 'dev' / 'config.yml'
|
50
|
+
|
51
|
+
|
52
|
+
# What to split string keys into key path segments on.
|
53
|
+
#
|
54
|
+
# @return [String]
|
55
|
+
#
|
56
|
+
KEY_SEPARATOR = '.'
|
57
|
+
|
58
|
+
|
59
|
+
# Attributes
|
60
|
+
# ============================================================================
|
61
|
+
|
62
|
+
# Absolute path to the built-in default configuration file in use.
|
63
|
+
#
|
64
|
+
# @return [Pathname]
|
65
|
+
#
|
66
|
+
attr_reader :default_config_path
|
67
|
+
|
68
|
+
|
69
|
+
# Constructor
|
70
|
+
# ===================================================================
|
71
|
+
|
72
|
+
# Instantiate a new `Locd::Config`.
|
73
|
+
def initialize default_config_path: DEFAULT_CONFIG_PATH,
|
74
|
+
dev_config_path: DEV_CONFIG_PATH
|
75
|
+
@default_config_path = default_config_path.to_pn
|
76
|
+
@dev_config_path = dev_config_path.to_pn
|
77
|
+
|
78
|
+
@from_files = {}
|
79
|
+
|
80
|
+
load_file default_config_path
|
81
|
+
|
82
|
+
if user_config_path.exist?
|
83
|
+
load_file user_config_path
|
84
|
+
end
|
85
|
+
|
86
|
+
if dev_config_path && dev_config_path.exist?
|
87
|
+
load_file dev_config_path
|
88
|
+
end
|
89
|
+
end # #initialize
|
90
|
+
|
91
|
+
|
92
|
+
# Instance Methods
|
93
|
+
# ===================================================================
|
94
|
+
|
95
|
+
protected
|
96
|
+
# ========================================================================
|
97
|
+
|
98
|
+
|
99
|
+
# @todo Document load_file method.
|
100
|
+
#
|
101
|
+
# @param [type] arg_name
|
102
|
+
# @todo Add name param description.
|
103
|
+
#
|
104
|
+
# @return [return_type]
|
105
|
+
# @todo Document return value.
|
106
|
+
#
|
107
|
+
def load_file path
|
108
|
+
bnd = binding
|
109
|
+
|
110
|
+
path.
|
111
|
+
to_pn.
|
112
|
+
read.
|
113
|
+
thru { |contents|
|
114
|
+
YAML.load contents
|
115
|
+
}.
|
116
|
+
map_leaves { |path, leaf|
|
117
|
+
case leaf
|
118
|
+
when String
|
119
|
+
bnd.erb leaf
|
120
|
+
else
|
121
|
+
leaf
|
122
|
+
end
|
123
|
+
}.
|
124
|
+
thru { |tree|
|
125
|
+
@from_files.deep_merge! tree
|
126
|
+
}
|
127
|
+
|
128
|
+
nil
|
129
|
+
end # #load_file
|
130
|
+
|
131
|
+
public # end protected ***************************************************
|
132
|
+
|
133
|
+
|
134
|
+
def key_path_for *key
|
135
|
+
key.flat_map { |k| k.to_s.split KEY_SEPARATOR }
|
136
|
+
end
|
137
|
+
|
138
|
+
|
139
|
+
def from_files *key, type: nil
|
140
|
+
key_path = key_path_for *key
|
141
|
+
value = @from_files.dig Locd::GEM_NAME, *key_path
|
142
|
+
|
143
|
+
if value.nil?
|
144
|
+
nil
|
145
|
+
else
|
146
|
+
parse_and_check value, type: type
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
|
151
|
+
def env_key_for *key
|
152
|
+
[
|
153
|
+
from_files( :namespace, :env, type: t.non_empty_str ),
|
154
|
+
*key_path_for( *key )
|
155
|
+
].join( '_' ).upcase
|
156
|
+
end
|
157
|
+
|
158
|
+
|
159
|
+
def parse_and_check value, type: nil
|
160
|
+
return value if type.nil?
|
161
|
+
|
162
|
+
if value.is_a?( String ) && type.has_from_s?
|
163
|
+
type.from_s value
|
164
|
+
else
|
165
|
+
type.check value
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
|
170
|
+
def from_env *key, type: nil
|
171
|
+
env_key = env_key_for *key
|
172
|
+
|
173
|
+
value = ENV[env_key]
|
174
|
+
|
175
|
+
case value
|
176
|
+
when nil, ''
|
177
|
+
nil
|
178
|
+
else
|
179
|
+
parse_and_check value, type: type
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
|
184
|
+
def get *key, type: nil, default: nil
|
185
|
+
env_value = from_env *key, type: type
|
186
|
+
return env_value unless env_value.nil?
|
187
|
+
|
188
|
+
files_value = from_files *key, type: type
|
189
|
+
return files_value unless files_value.nil?
|
190
|
+
|
191
|
+
parse_and_check default, type: type
|
192
|
+
end
|
193
|
+
|
194
|
+
alias_method :[], :get
|
195
|
+
|
196
|
+
|
197
|
+
def to_h
|
198
|
+
@from_files.merge \
|
199
|
+
"locd" => @from_files["locd"].map_leaves { |key_path, value|
|
200
|
+
env_value = from_env *key_path
|
201
|
+
|
202
|
+
if env_value.nil?
|
203
|
+
value
|
204
|
+
else
|
205
|
+
env_value
|
206
|
+
end
|
207
|
+
}
|
208
|
+
end
|
209
|
+
|
210
|
+
|
211
|
+
# @return [Pathname]
|
212
|
+
# Absolute path to Loc'd home directory (it's workspace).
|
213
|
+
# Directory may not exist, or may be something else like a file.
|
214
|
+
#
|
215
|
+
def home_dir
|
216
|
+
self[:home, type: t.abs_path].to_pn
|
217
|
+
end
|
218
|
+
|
219
|
+
|
220
|
+
def log_dir
|
221
|
+
self[
|
222
|
+
:log_dir,
|
223
|
+
type: t.abs_path,
|
224
|
+
default: (home_dir / 'log')
|
225
|
+
]
|
226
|
+
end
|
227
|
+
|
228
|
+
|
229
|
+
# @return [Pathname]
|
230
|
+
# Absolute path to the user config file, which may not exist, but
|
231
|
+
# will be loaded if it does.
|
232
|
+
#
|
233
|
+
def user_config_path
|
234
|
+
home_dir / 'config.yml'
|
235
|
+
end
|
236
|
+
|
237
|
+
end # class Locd::Config
|
@@ -0,0 +1,93 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
# Requirements
|
5
|
+
# =======================================================================
|
6
|
+
|
7
|
+
# Stdlib
|
8
|
+
# -----------------------------------------------------------------------
|
9
|
+
|
10
|
+
# Deps
|
11
|
+
# -----------------------------------------------------------------------
|
12
|
+
|
13
|
+
# Project / Package
|
14
|
+
# -----------------------------------------------------------------------
|
15
|
+
|
16
|
+
|
17
|
+
# Refinements
|
18
|
+
# =======================================================================
|
19
|
+
|
20
|
+
|
21
|
+
|
22
|
+
# Definitions
|
23
|
+
# =======================================================================
|
24
|
+
|
25
|
+
|
26
|
+
# @todo document Locd::Config::Base class.
|
27
|
+
class Locd::Config::Base
|
28
|
+
|
29
|
+
# Constants
|
30
|
+
# ======================================================================
|
31
|
+
|
32
|
+
|
33
|
+
# Class Methods
|
34
|
+
# ======================================================================
|
35
|
+
|
36
|
+
|
37
|
+
# Attributes
|
38
|
+
# ======================================================================
|
39
|
+
|
40
|
+
|
41
|
+
# TODO document `key_separator` attribute.
|
42
|
+
#
|
43
|
+
# @return [attr_type]
|
44
|
+
#
|
45
|
+
attr_reader :key_separator
|
46
|
+
|
47
|
+
|
48
|
+
# Constructor
|
49
|
+
# ======================================================================
|
50
|
+
|
51
|
+
# Instantiate a new `Locd::Config::Base`.
|
52
|
+
def initialize source:, parent: nil, key_separator: '.'
|
53
|
+
@source = source
|
54
|
+
@parent = parent
|
55
|
+
@key_separator = key_separator
|
56
|
+
end # #initialize
|
57
|
+
|
58
|
+
|
59
|
+
# Instance Methods
|
60
|
+
# ======================================================================
|
61
|
+
|
62
|
+
def key_path_for *key
|
63
|
+
key.flat_map { |k| k.to_s.split key_separator }
|
64
|
+
end
|
65
|
+
|
66
|
+
|
67
|
+
def dig *key_path
|
68
|
+
if key_path.length == 1
|
69
|
+
if source.key? key_path[0]
|
70
|
+
return source[key_path[0]]
|
71
|
+
end
|
72
|
+
else
|
73
|
+
just_before = source.dig key_path[0...-1]
|
74
|
+
if just_before.respond_to?( :key? ) &&
|
75
|
+
just_before.key? key_path[-1]
|
76
|
+
return just_before[key_path[-1]]
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
if parent
|
81
|
+
parent.dig key_path
|
82
|
+
else
|
83
|
+
nil
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
|
88
|
+
def get *key
|
89
|
+
dig key_path_for( *key )
|
90
|
+
end
|
91
|
+
|
92
|
+
|
93
|
+
end # class Locd::Config::Base
|
data/lib/locd/errors.rb
ADDED
@@ -0,0 +1,65 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Refinements
|
4
|
+
# =======================================================================
|
5
|
+
|
6
|
+
using NRSER
|
7
|
+
|
8
|
+
|
9
|
+
# Definitions
|
10
|
+
# =======================================================================
|
11
|
+
|
12
|
+
module Locd
|
13
|
+
|
14
|
+
# Classes
|
15
|
+
# ----------------------------------------------------------------------------
|
16
|
+
|
17
|
+
# Base class for errors that are to be expected and should be handled
|
18
|
+
# gracefully.
|
19
|
+
#
|
20
|
+
class HandledError < StandardError; end
|
21
|
+
|
22
|
+
|
23
|
+
# Base class for {HandledError} that happen during request processing which
|
24
|
+
# should result in useful error responses to the user.
|
25
|
+
#
|
26
|
+
class RequestError < HandledError
|
27
|
+
STATUS_CODE = 400
|
28
|
+
STATUS_MESSAGE = "Bad Request"
|
29
|
+
|
30
|
+
def self.status
|
31
|
+
"#{ self::STATUS_CODE } #{ self::STATUS_MESSAGE }"
|
32
|
+
end
|
33
|
+
|
34
|
+
|
35
|
+
# TODO document `text` attribute.
|
36
|
+
#
|
37
|
+
# @return [attr_type]
|
38
|
+
#
|
39
|
+
attr_reader :text
|
40
|
+
|
41
|
+
|
42
|
+
def initialize text = self.class::STATUS_MESSAGE
|
43
|
+
@text = text
|
44
|
+
super "#{ self.class.status }\n\n#{ text }"
|
45
|
+
end
|
46
|
+
|
47
|
+
def to_http_response
|
48
|
+
Locd.http_response_for self.class.status, text
|
49
|
+
end
|
50
|
+
|
51
|
+
def to_proxy_machine_cmd
|
52
|
+
{ close: to_http_response }
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
|
57
|
+
# Raised when we couldn't find what the request seems to be looking for
|
58
|
+
# (`404 Not Found` in HTTP).
|
59
|
+
#
|
60
|
+
class NotFoundError < RequestError
|
61
|
+
STATUS_CODE = 404
|
62
|
+
STATUS_MESSAGE = "Not Found"
|
63
|
+
end
|
64
|
+
|
65
|
+
end # module Locd
|
data/lib/locd/label.rb
ADDED
@@ -0,0 +1,61 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Refinements
|
4
|
+
# =======================================================================
|
5
|
+
|
6
|
+
using NRSER
|
7
|
+
|
8
|
+
|
9
|
+
# Definitions
|
10
|
+
# =======================================================================
|
11
|
+
|
12
|
+
# Static method namespace for dealing with labels.
|
13
|
+
#
|
14
|
+
module Locd::Label
|
15
|
+
|
16
|
+
def self.url_for label, tld:, port:
|
17
|
+
"http://#{ label }:#{ port }"
|
18
|
+
end
|
19
|
+
|
20
|
+
|
21
|
+
# Simple transformation of "glob-style" label patterns to {Regexp}.
|
22
|
+
#
|
23
|
+
# **_Right now only handles the `*` wildcard!_**
|
24
|
+
#
|
25
|
+
# @param [String] string
|
26
|
+
# Glob-style pattern to search for in {Locd::Agent#label} strings.
|
27
|
+
#
|
28
|
+
# @param [Boolean] exact:
|
29
|
+
# When `false`, the returned Regexp will match any part of label
|
30
|
+
# strings.
|
31
|
+
#
|
32
|
+
# When `true`, the generated Regexp will only match if `pattern` matches
|
33
|
+
# entire label strings.
|
34
|
+
#
|
35
|
+
# @param [Boolean] ignore_case:
|
36
|
+
# Makes the Regexp case-insensitive.
|
37
|
+
#
|
38
|
+
#
|
39
|
+
def self.regexp_for_glob string, exact: false, ignore_case: false
|
40
|
+
string.
|
41
|
+
# Uncommon option: `-1` causes empty segments to be included, which
|
42
|
+
# fixes the issue of `*` at start or end
|
43
|
+
split( '*', -1 ).
|
44
|
+
map( &Regexp.method( :escape ) ).
|
45
|
+
join( '.*' ).
|
46
|
+
thru { |regexp_string|
|
47
|
+
# Add `\A` and `\z` caps if `exact` is set
|
48
|
+
regexp_string = "\\A#{ regexp_string }\\z" if exact
|
49
|
+
|
50
|
+
Regexp.new regexp_string, ignore_case
|
51
|
+
}
|
52
|
+
end
|
53
|
+
|
54
|
+
|
55
|
+
def self.compare label_a, label_b
|
56
|
+
[label_a, label_b].
|
57
|
+
map { |label| label.split( '.' ).reverse }.
|
58
|
+
thru { |a, b| a <=> b }
|
59
|
+
end
|
60
|
+
|
61
|
+
end # module Locd::Label
|