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