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
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
|