truex-skylight 0.6.0

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.
Files changed (122) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +277 -0
  3. data/CLA.md +9 -0
  4. data/CONTRIBUTING.md +1 -0
  5. data/LICENSE.md +79 -0
  6. data/README.md +4 -0
  7. data/bin/skylight +3 -0
  8. data/ext/extconf.rb +186 -0
  9. data/ext/libskylight.yml +6 -0
  10. data/ext/skylight_memprof.c +115 -0
  11. data/ext/skylight_native.c +416 -0
  12. data/ext/skylight_native.h +20 -0
  13. data/lib/skylight.rb +2 -0
  14. data/lib/skylight/api.rb +79 -0
  15. data/lib/skylight/cli.rb +146 -0
  16. data/lib/skylight/compat.rb +47 -0
  17. data/lib/skylight/config.rb +498 -0
  18. data/lib/skylight/core.rb +122 -0
  19. data/lib/skylight/data/cacert.pem +3894 -0
  20. data/lib/skylight/formatters/http.rb +17 -0
  21. data/lib/skylight/gc.rb +107 -0
  22. data/lib/skylight/helpers.rb +137 -0
  23. data/lib/skylight/instrumenter.rb +290 -0
  24. data/lib/skylight/middleware.rb +75 -0
  25. data/lib/skylight/native.rb +69 -0
  26. data/lib/skylight/normalizers.rb +133 -0
  27. data/lib/skylight/normalizers/action_controller/process_action.rb +35 -0
  28. data/lib/skylight/normalizers/action_controller/send_file.rb +76 -0
  29. data/lib/skylight/normalizers/action_view/render_collection.rb +18 -0
  30. data/lib/skylight/normalizers/action_view/render_partial.rb +18 -0
  31. data/lib/skylight/normalizers/action_view/render_template.rb +18 -0
  32. data/lib/skylight/normalizers/active_record/sql.rb +79 -0
  33. data/lib/skylight/normalizers/active_support/cache.rb +50 -0
  34. data/lib/skylight/normalizers/active_support/cache_clear.rb +16 -0
  35. data/lib/skylight/normalizers/active_support/cache_decrement.rb +16 -0
  36. data/lib/skylight/normalizers/active_support/cache_delete.rb +16 -0
  37. data/lib/skylight/normalizers/active_support/cache_exist.rb +16 -0
  38. data/lib/skylight/normalizers/active_support/cache_fetch_hit.rb +16 -0
  39. data/lib/skylight/normalizers/active_support/cache_generate.rb +16 -0
  40. data/lib/skylight/normalizers/active_support/cache_increment.rb +16 -0
  41. data/lib/skylight/normalizers/active_support/cache_read.rb +16 -0
  42. data/lib/skylight/normalizers/active_support/cache_read_multi.rb +16 -0
  43. data/lib/skylight/normalizers/active_support/cache_write.rb +16 -0
  44. data/lib/skylight/normalizers/default.rb +21 -0
  45. data/lib/skylight/normalizers/moped/query.rb +141 -0
  46. data/lib/skylight/probes.rb +91 -0
  47. data/lib/skylight/probes/excon.rb +25 -0
  48. data/lib/skylight/probes/excon/middleware.rb +65 -0
  49. data/lib/skylight/probes/net_http.rb +44 -0
  50. data/lib/skylight/probes/redis.rb +30 -0
  51. data/lib/skylight/probes/sequel.rb +30 -0
  52. data/lib/skylight/probes/sinatra.rb +74 -0
  53. data/lib/skylight/probes/tilt.rb +27 -0
  54. data/lib/skylight/railtie.rb +122 -0
  55. data/lib/skylight/sinatra.rb +4 -0
  56. data/lib/skylight/subscriber.rb +92 -0
  57. data/lib/skylight/trace.rb +191 -0
  58. data/lib/skylight/util.rb +16 -0
  59. data/lib/skylight/util/allocation_free.rb +17 -0
  60. data/lib/skylight/util/clock.rb +53 -0
  61. data/lib/skylight/util/gzip.rb +15 -0
  62. data/lib/skylight/util/hostname.rb +17 -0
  63. data/lib/skylight/util/http.rb +218 -0
  64. data/lib/skylight/util/inflector.rb +110 -0
  65. data/lib/skylight/util/logging.rb +87 -0
  66. data/lib/skylight/util/multi_io.rb +21 -0
  67. data/lib/skylight/util/native_ext_fetcher.rb +205 -0
  68. data/lib/skylight/util/platform.rb +67 -0
  69. data/lib/skylight/util/ssl.rb +50 -0
  70. data/lib/skylight/vendor/active_support/notifications.rb +207 -0
  71. data/lib/skylight/vendor/active_support/notifications/fanout.rb +159 -0
  72. data/lib/skylight/vendor/active_support/notifications/instrumenter.rb +72 -0
  73. data/lib/skylight/vendor/active_support/per_thread_registry.rb +52 -0
  74. data/lib/skylight/vendor/cli/highline.rb +1034 -0
  75. data/lib/skylight/vendor/cli/highline/color_scheme.rb +134 -0
  76. data/lib/skylight/vendor/cli/highline/compatibility.rb +16 -0
  77. data/lib/skylight/vendor/cli/highline/import.rb +41 -0
  78. data/lib/skylight/vendor/cli/highline/menu.rb +381 -0
  79. data/lib/skylight/vendor/cli/highline/question.rb +481 -0
  80. data/lib/skylight/vendor/cli/highline/simulate.rb +48 -0
  81. data/lib/skylight/vendor/cli/highline/string_extensions.rb +111 -0
  82. data/lib/skylight/vendor/cli/highline/style.rb +181 -0
  83. data/lib/skylight/vendor/cli/highline/system_extensions.rb +242 -0
  84. data/lib/skylight/vendor/cli/thor.rb +473 -0
  85. data/lib/skylight/vendor/cli/thor/actions.rb +318 -0
  86. data/lib/skylight/vendor/cli/thor/actions/create_file.rb +105 -0
  87. data/lib/skylight/vendor/cli/thor/actions/create_link.rb +60 -0
  88. data/lib/skylight/vendor/cli/thor/actions/directory.rb +119 -0
  89. data/lib/skylight/vendor/cli/thor/actions/empty_directory.rb +137 -0
  90. data/lib/skylight/vendor/cli/thor/actions/file_manipulation.rb +314 -0
  91. data/lib/skylight/vendor/cli/thor/actions/inject_into_file.rb +109 -0
  92. data/lib/skylight/vendor/cli/thor/base.rb +652 -0
  93. data/lib/skylight/vendor/cli/thor/command.rb +136 -0
  94. data/lib/skylight/vendor/cli/thor/core_ext/hash_with_indifferent_access.rb +80 -0
  95. data/lib/skylight/vendor/cli/thor/core_ext/io_binary_read.rb +12 -0
  96. data/lib/skylight/vendor/cli/thor/core_ext/ordered_hash.rb +100 -0
  97. data/lib/skylight/vendor/cli/thor/error.rb +28 -0
  98. data/lib/skylight/vendor/cli/thor/group.rb +282 -0
  99. data/lib/skylight/vendor/cli/thor/invocation.rb +172 -0
  100. data/lib/skylight/vendor/cli/thor/parser.rb +4 -0
  101. data/lib/skylight/vendor/cli/thor/parser/argument.rb +74 -0
  102. data/lib/skylight/vendor/cli/thor/parser/arguments.rb +171 -0
  103. data/lib/skylight/vendor/cli/thor/parser/option.rb +121 -0
  104. data/lib/skylight/vendor/cli/thor/parser/options.rb +218 -0
  105. data/lib/skylight/vendor/cli/thor/rake_compat.rb +72 -0
  106. data/lib/skylight/vendor/cli/thor/runner.rb +322 -0
  107. data/lib/skylight/vendor/cli/thor/shell.rb +88 -0
  108. data/lib/skylight/vendor/cli/thor/shell/basic.rb +393 -0
  109. data/lib/skylight/vendor/cli/thor/shell/color.rb +148 -0
  110. data/lib/skylight/vendor/cli/thor/shell/html.rb +127 -0
  111. data/lib/skylight/vendor/cli/thor/util.rb +270 -0
  112. data/lib/skylight/vendor/cli/thor/version.rb +3 -0
  113. data/lib/skylight/vendor/thread_safe.rb +126 -0
  114. data/lib/skylight/vendor/thread_safe/non_concurrent_cache_backend.rb +133 -0
  115. data/lib/skylight/vendor/thread_safe/synchronized_cache_backend.rb +76 -0
  116. data/lib/skylight/version.rb +4 -0
  117. data/lib/skylight/vm/gc.rb +70 -0
  118. data/lib/sql_lexer.rb +6 -0
  119. data/lib/sql_lexer/lexer.rb +579 -0
  120. data/lib/sql_lexer/string_scanner.rb +11 -0
  121. data/lib/sql_lexer/version.rb +3 -0
  122. metadata +179 -0
@@ -0,0 +1,20 @@
1
+ #ifndef __SKYLIGHT_NATIVE__
2
+ #define __SKYLIGHT_NATIVE__
3
+
4
+ #include <stdint.h>
5
+
6
+ void sky_activate_memprof(void);
7
+
8
+ void sky_deactivate_memprof(void);
9
+
10
+ uint64_t sky_allocation_count(void);
11
+
12
+ uint64_t sky_consume_allocations();
13
+
14
+ void sky_clear_allocation_count(void);
15
+
16
+ int sky_have_memprof(void);
17
+
18
+ #define UNUSED(x) (void)(x)
19
+
20
+ #endif
@@ -0,0 +1,2 @@
1
+ require 'skylight/core'
2
+ require 'skylight/probes'
@@ -0,0 +1,79 @@
1
+ require 'uri'
2
+
3
+ module Skylight
4
+ # @api private
5
+ class Api
6
+ attr_reader :config, :http
7
+
8
+ class CreateFailed < StandardError
9
+ attr_reader :res
10
+
11
+ def initialize(res)
12
+ @res = res
13
+ super "failed with status #{res.status}"
14
+ end
15
+
16
+ def errors
17
+ return unless res.respond_to?(:body) && res.body.is_a?(Hash)
18
+ res.body['errors']
19
+ end
20
+
21
+ def to_s
22
+ if errors
23
+ errors.inspect
24
+ elsif res
25
+ "#{res.class.to_s}: #{res.to_s}"
26
+ else
27
+ super
28
+ end
29
+ end
30
+ end
31
+
32
+ def initialize(config, service = :auth)
33
+ @config = config
34
+ @http = Util::HTTP.new(config, service)
35
+ end
36
+
37
+ def authentication
38
+ @http.authentication
39
+ end
40
+
41
+ def authentication=(token)
42
+ @http.authentication = token
43
+ end
44
+
45
+ def validate_authentication
46
+ url = URI.parse(config[:auth_url])
47
+
48
+ res = @http.get(url.path)
49
+
50
+ case res.status
51
+ when 200...300
52
+ :ok
53
+ when 400...500
54
+ :invalid
55
+ else
56
+ :unknown
57
+ end
58
+ rescue
59
+ :unknown
60
+ end
61
+
62
+ def login(email, password)
63
+ res = http.get('/me', 'X-Email' => email, 'X-Password' => password)
64
+
65
+ if res && res.success?
66
+ res.get('me.authentication_token')
67
+ end
68
+ end
69
+
70
+ def create_app(name, token=nil)
71
+ params = { app: { name: name } }
72
+ params[:token] = token if token
73
+ res = @http.post('/apps', params)
74
+ raise CreateFailed, res unless res.success?
75
+ res
76
+ end
77
+
78
+ end
79
+ end
@@ -0,0 +1,146 @@
1
+ $:.unshift File.expand_path('../vendor/cli', __FILE__)
2
+
3
+ require 'skylight'
4
+ require 'thor'
5
+ require 'yaml'
6
+ require 'highline'
7
+ require 'active_support/inflector'
8
+
9
+ module Skylight
10
+ # @api private
11
+ class CLI < Thor
12
+
13
+ desc "setup TOKEN", "Sets up a new app using the provided token"
14
+ def setup(token=nil)
15
+ if File.exist?(config_path)
16
+ say "Your app is already on Skylight. http://www.skylight.io", :green
17
+ return
18
+ end
19
+
20
+ unless token
21
+ api.authentication = login
22
+ end
23
+
24
+ res = api.create_app(app_name, token)
25
+
26
+ config[:application] = res.get('app.id')
27
+ config[:authentication] = res.get('app.token')
28
+ config.write(config_path)
29
+
30
+ say "Congratulations. Your application is on Skylight! http://www.skylight.io", :green
31
+ say <<-OUT
32
+
33
+ The application was registered for you and we generated a config file
34
+ containing your API token at:
35
+
36
+ #{relative_config_path}
37
+
38
+ The next step is for you to deploy your application to production. The
39
+ easiest way is to just commit the config file to your source control
40
+ repository and deploy from there. You can learn more about the process at:
41
+
42
+ http://docs.skylight.io/getting-started/#deploy
43
+
44
+ If you want to specify the authentication token as an environment variable,
45
+ you should set the `SKYLIGHT_AUTHENTICATION` variable to:
46
+
47
+ #{config[:authentication]}
48
+
49
+ OUT
50
+ rescue Api::CreateFailed => e
51
+ say "Could not create the application", :red
52
+ say e.to_s, :yellow
53
+ rescue Interrupt
54
+ end
55
+
56
+ private
57
+
58
+ def app_name
59
+ @app_name ||=
60
+ begin
61
+ name = nil
62
+
63
+ if File.exist?("config/application.rb")
64
+ # This looks like a Rails app, lets make sure we have the railtie loaded
65
+ # skylight.rb checks for Rails, but when running the CLI, Skylight loads before Rails does
66
+ begin
67
+ require "skylight/railtie"
68
+ rescue LoadError => e
69
+ error "Unable to load Railtie. Please notify support@skylight.io."
70
+ end
71
+
72
+ # Get the name in a process so that we don't pollute our environment here
73
+ # This is especially important since users may have things like WebMock that
74
+ # will prevent us from communicating with the Skylight API
75
+ begin
76
+ namefile = Tempfile.new('skylight-app-name')
77
+ # Windows appears to need double quotes for `rails runner`
78
+ `rails runner "File.open('#{namefile.path}', 'w') {|f| f.write(Rails.application.class.name) rescue '' }"`
79
+ name = namefile.read.split("::").first.underscore.titleize
80
+ name = nil if name.empty?
81
+ rescue => e
82
+ if ENV['DEBUG']
83
+ puts e.class.name
84
+ puts e.to_s
85
+ puts e.backtrace.join("\n")
86
+ end
87
+ ensure
88
+ namefile.close
89
+ namefile.unlink
90
+ end
91
+
92
+ unless name
93
+ warn "Unable to determine Rails application name. Using directory name."
94
+ end
95
+ end
96
+
97
+ unless name
98
+ name = File.basename(File.expand_path('.')).titleize
99
+ end
100
+
101
+ name
102
+ end
103
+ end
104
+
105
+ def login
106
+ say "Please enter your email and password below or get a token from https://www.skylight.io/app/setup.", :cyan
107
+
108
+ 10.times do
109
+ email = highline.ask("Email: ")
110
+ password = highline.ask("Password: ") { |q| q.echo = "*" }
111
+
112
+ if token = api.login(email, password)
113
+ return token
114
+ end
115
+
116
+ say "Sorry. That email and password was invalid. Please try again", :red
117
+ puts
118
+ end
119
+
120
+ say "Could not login", :red
121
+ return
122
+ end
123
+
124
+ def relative_config_path
125
+ 'config/skylight.yml'
126
+ end
127
+
128
+ def config_path
129
+ File.expand_path(relative_config_path)
130
+ end
131
+
132
+ def api
133
+ @api ||= Api.new(config)
134
+ end
135
+
136
+ def highline
137
+ @highline ||= HighLine.new
138
+ end
139
+
140
+ def config
141
+ # Calling .load checks ENV variables
142
+ @config ||= Config.load
143
+ end
144
+
145
+ end
146
+ end
@@ -0,0 +1,47 @@
1
+ module Skylight
2
+ # Ensure the version of AS:N being used is recent enough
3
+ begin
4
+ # Attempt to reference an internal class only present in the new AS::Notifications
5
+ ActiveSupport::Notifications::Fanout::Subscribers
6
+ rescue NameError
7
+
8
+ # The things we do...
9
+ class ::ActiveSupport::Notifications::Fanout
10
+ attr_reader :subscribers
11
+
12
+ class Subscriber
13
+ attr_reader :pattern, :delegate
14
+ end
15
+ end
16
+
17
+ notifier = ActiveSupport::Notifications.notifier
18
+
19
+ # If the class is missing, require our vendored AS::N
20
+ require 'skylight/vendor/active_support/notifications'
21
+
22
+ if notifier.subscribers.respond_to?(:each)
23
+ notifier.subscribers.each do |sub|
24
+ pattern = sub.respond_to?(:pattern) && sub.pattern
25
+ delegate = sub.respond_to?(:delegate) && sub.delegate
26
+
27
+ if pattern && delegate
28
+ ActiveSupport::Notifications.subscribe(pattern, delegate)
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
34
+
35
+ if defined?(ActiveSupport::Notifications::Fanout::Subscribers::Evented)
36
+ # Handle early RCs of rails 4.0
37
+ # @api private
38
+ class ActiveSupport::Notifications::Fanout::Subscribers::Evented
39
+ unless method_defined?(:publish)
40
+ def publish(name, *args)
41
+ if @delegate.respond_to?(:publish)
42
+ @delegate.publish name, *args
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,498 @@
1
+ require 'uri'
2
+ require 'yaml'
3
+ require 'fileutils'
4
+ require 'thread'
5
+ require 'openssl'
6
+ require 'skylight/util/hostname'
7
+ require 'skylight/util/logging'
8
+ require 'skylight/util/platform'
9
+ require 'skylight/util/ssl'
10
+
11
+ module Skylight
12
+ class Config
13
+ # @api private
14
+ MUTEX = Mutex.new
15
+
16
+ # Map environment variable keys with Skylight configuration keys
17
+ ENV_TO_KEY = {
18
+ # == Authentication ==
19
+ 'AUTHENTICATION' => :'authentication',
20
+
21
+ # == Version ==
22
+ 'VERSION' => :'version',
23
+
24
+ # == App settings ==
25
+ 'ROOT' => :'root',
26
+ 'APPLICATION' => :'application',
27
+ 'HOSTNAME' => :'hostname',
28
+ 'SESSION_TOKEN' => :'session_token',
29
+
30
+ # == Logging ==
31
+ 'LOG_FILE' => :'log_file',
32
+ 'LOG_LEVEL' => :'log_level',
33
+ 'ALERT_LOG_FILE' => :'alert_log_file',
34
+
35
+ # == Proxy ==
36
+ 'PROXY_URL' => :'proxy_url',
37
+
38
+ # == Instrumenter ==
39
+ "IGNORED_ENDPOINT" => :'ignored_endpoint',
40
+
41
+ # == Skylight Remote ==
42
+ "AUTH_URL" => :'auth_url',
43
+ "AUTH_HTTP_DEFLATE" => :'auth_http_deflate',
44
+ "AUTH_HTTP_CONNECT_TIMEOUT" => :'auth_http_connect_timeout',
45
+ "AUTH_HTTP_READ_TIMEOUT" => :'auth_http_read_timeout',
46
+ "REPORT_URL" => :'report_url',
47
+ "REPORT_HTTP_DEFLATE" => :'report_http_deflate',
48
+ "REPORT_HTTP_CONNECT_TIMEOUT" => :'report_http_connect_timeout',
49
+ "REPORT_HTTP_READ_TIMEOUT" => :'report_http_read_timeout',
50
+
51
+ # == Native agent settings ==
52
+ #
53
+ "LAZY_START" => :'daemon.lazy_start',
54
+ "DAEMON_EXEC_PATH" => :'daemon.exec_path',
55
+ "DAEMON_LIB_PATH" => :'daemon.lib_path',
56
+ "PIDFILE_PATH" => :'daemon.pidfile_path',
57
+ "SOCKDIR_PATH" => :'daemon.sockdir_path',
58
+ "BATCH_QUEUE_DEPTH" => :'daemon.batch_queue_depth',
59
+ "BATCH_SAMPLE_SIZE" => :'daemon.batch_sample_size',
60
+ "BATCH_FLUSH_INTERVAL" => :'daemon.batch_flush_interval',
61
+ "DAEMON_TICK_INTERVAL" => :'daemon.tick_interval',
62
+ "DAEMON_SANITY_CHECK_INTERVAL" => :'daemon.sanity_check_interval',
63
+ "DAEMON_INACTIVITY_TIMEOUT" => :'daemon.inactivity_timeout',
64
+ "CLIENT_MAX_TRIES" => :'daemon.max_connect_tries',
65
+ "CLIENT_CONN_TRY_WIN" => :'daemon.connect_try_window',
66
+ "MAX_PRESPAWN_JITTER" => :'daemon.max_prespawn_jitter',
67
+ "DAEMON_WAIT_TIMEOUT" => :'daemon.wait_timeout',
68
+ "CLIENT_CHECK_INTERVAL" => :'daemon.client_check_interval',
69
+ "CLIENT_QUEUE_DEPTH" => :'daemon.client_queue_depth',
70
+ "CLIENT_WRITE_TIMEOUT" => :'daemon.client_write_timeout',
71
+ "SSL_CERT_PATH" => :'daemon.ssl_cert_path',
72
+ "SSL_CERT_DIR" => :'daemon.ssl_cert_dir',
73
+
74
+ # == Legacy env vars ==
75
+ #
76
+ 'AGENT_LOCKFILE' => :'agent.lockfile',
77
+ 'AGENT_SOCKFILE_PATH' => :'agent.sockfile_path',
78
+ }
79
+
80
+ # Default values for Skylight configuration keys
81
+ DEFAULTS = {
82
+ :'version' => VERSION,
83
+ :'auth_url' => 'https://auth.skylight.io/agent',
84
+ :'daemon.lazy_start' => true,
85
+ :'daemon.ssl_cert_path' => Util::SSL.ca_cert_file_or_default,
86
+ :'daemon.ssl_cert_dir' => Util::SSL.ca_cert_dir,
87
+
88
+ # == Legacy ==
89
+ :'log_file' => '-'.freeze,
90
+ :'log_level' => 'INFO'.freeze,
91
+ :'alert_log_file' => '-'.freeze,
92
+ :'hostname' => Util::Hostname.default_hostname,
93
+ :'agent.keepalive' => 60,
94
+ :'agent.interval' => 5,
95
+ :'agent.sample' => 200,
96
+ :'agent.max_memory' => 256, # MB
97
+ :'report.host' => 'agent.skylight.io'.freeze,
98
+ :'report.port' => 443,
99
+ :'report.ssl' => true,
100
+ :'report.deflate' => true,
101
+ :'accounts.host' => 'www.skylight.io'.freeze,
102
+ :'accounts.port' => 443,
103
+ :'accounts.ssl' => true,
104
+ :'accounts.deflate' => false,
105
+ :'me.credentials_path' => '~/.skylight',
106
+ :'metrics.report_interval' => 60
107
+ }
108
+
109
+ if Skylight.native?
110
+ native_path = Skylight.libskylight_path
111
+
112
+ DEFAULTS[:'daemon.lib_path'] = native_path
113
+ DEFAULTS[:'daemon.exec_path'] = File.join(native_path, 'skylightd')
114
+ end
115
+
116
+ DEFAULTS.freeze
117
+
118
+ REQUIRED = {
119
+ :'authentication' => "authentication token",
120
+ :'hostname' => "server hostname",
121
+ :'report.host' => "skylight remote host",
122
+ :'report.port' => "skylight remote port" }
123
+
124
+ ALWAYS_INCLUDE_IN_ENV = [
125
+ :version,
126
+ :'daemon.lazy_start',
127
+ :'daemon.lib_path',
128
+ :'daemon.exec_path',
129
+ :'daemon.ssl_cert_dir',
130
+ :'daemon.ssl_cert_path' ]
131
+
132
+ # Maps legacy config keys to new config keys
133
+ LEGACY = {
134
+ :'agent.sockfile_path' => :'daemon.sockdir_path',
135
+ :'agent.pidfile_path' => :'agent.lockfile',
136
+ }
137
+
138
+ VALIDATORS = {
139
+ :'agent.interval' => [lambda { |v, c| Integer === v && v > 0 }, "must be an integer greater than 0"]
140
+ }
141
+
142
+ # @api private
143
+ attr_reader :environment
144
+
145
+ # @api private
146
+ def initialize(*args)
147
+ attrs = {}
148
+
149
+ if Hash === args.last
150
+ attrs = args.pop.dup
151
+ end
152
+
153
+ @values = {}
154
+ @priority = {}
155
+ @regexp = nil
156
+
157
+ p = attrs.delete(:priority)
158
+
159
+ if @environment = args[0]
160
+ @regexp = /^#{Regexp.escape(@environment)}\.(.+)$/
161
+ end
162
+
163
+ attrs.each do |k, v|
164
+ self[k] = v
165
+ end
166
+
167
+ if p
168
+ p.each do |k, v|
169
+ @priority[Config.remap_key(k)] = v
170
+ end
171
+ end
172
+ end
173
+
174
+ def self.load(opts = {}, env = ENV)
175
+ attrs = {}
176
+ version = nil
177
+
178
+ path = opts.delete(:file)
179
+ environment = opts.delete(:environment)
180
+
181
+ if path
182
+ error = nil
183
+ begin
184
+ attrs = YAML.load_file(path)
185
+ error = "empty file" unless attrs
186
+ error = "invalid format" if attrs && !attrs.is_a?(Hash)
187
+ rescue Exception => e
188
+ error = e.message
189
+ end
190
+
191
+ raise ConfigError, "could not load config file; msg=#{error}" if error
192
+
193
+ version = File.mtime(path).to_i
194
+ end
195
+
196
+ if env
197
+ attrs[:priority] = remap_env(env)
198
+ end
199
+
200
+ config = new(environment, attrs)
201
+
202
+ opts.each do |k, v|
203
+ config[k] = v
204
+ end
205
+
206
+ config
207
+ end
208
+
209
+ def self.remap_key(key)
210
+ key = key.to_sym
211
+ LEGACY[key] || key
212
+ end
213
+
214
+ # @api private
215
+ def self.remap_env(env)
216
+ ret = {}
217
+
218
+ return ret unless env
219
+
220
+ ret[:proxy_url] = detect_proxy_url(env)
221
+
222
+ env.each do |k, val|
223
+ # Support deprecated SK_ key prefix
224
+ next unless k =~ /^(?:SK|SKYLIGHT)_(.+)$/
225
+
226
+ if key = ENV_TO_KEY[$1]
227
+ ret[key] =
228
+ case val
229
+ when /^false$/i then false
230
+ when /^true$/i then true
231
+ when /^(nil|null)$/i then nil
232
+ when /^\d+$/ then val.to_i
233
+ when /^\d+\.\d+$/ then val.to_f
234
+ else val
235
+ end
236
+ end
237
+ end
238
+
239
+ ret
240
+ end
241
+
242
+ def self.detect_proxy_url(env)
243
+ if u = env['HTTP_PROXY'] || env['http_proxy']
244
+ u = "http://#{u}" unless u =~ %r[://]
245
+ u
246
+ end
247
+ end
248
+
249
+ # @api private
250
+ def skip_validation?
251
+ !!get(:skip_validation)
252
+ end
253
+
254
+ # @api private
255
+ def validate!
256
+ return true if skip_validation?
257
+
258
+ REQUIRED.each do |k, v|
259
+ unless get(k)
260
+ raise ConfigError, "#{v} required"
261
+ end
262
+ end
263
+
264
+ sockdir_path = self[:'daemon.sockdir_path'] || File.expand_path('.')
265
+ pidfile_path = self[:'daemon.pidfile_path'] || File.expand_path('skylight.pid', sockdir_path)
266
+
267
+ check_permissions(pidfile_path, sockdir_path)
268
+
269
+ true
270
+ end
271
+
272
+ def check_permissions(pidfile, sockdir_path)
273
+ pidfile_root = File.dirname(pidfile)
274
+
275
+ FileUtils.mkdir_p pidfile_root
276
+ FileUtils.mkdir_p sockdir_path
277
+
278
+ if File.exist?(pidfile)
279
+ if !FileTest.writable?(pidfile)
280
+ raise ConfigError, "File `#{pidfile}` not writable. Please set daemon.pidfile_path or daemon.sockdir_path in your config to a writable path"
281
+ end
282
+ else
283
+ if !FileTest.writable?(pidfile_root)
284
+ raise ConfigError, "Directory `#{pidfile_root}` not writable. Please set daemon.pidfile_path or daemon.sockdir_path in your config to a writable path"
285
+ end
286
+ end
287
+
288
+ unless FileTest.writable?(sockdir_path)
289
+ raise ConfigError, "Directory `#{sockdir_path}` not writable. Please set daemon.sockdir_path in your config to a writable path"
290
+ end
291
+ end
292
+
293
+ def key?(key)
294
+ key = Config.remap_key(key)
295
+ @priority.key?(key) || @values.key?(key)
296
+ end
297
+
298
+ def get(key, default = nil, &blk)
299
+ key = Config.remap_key(key)
300
+
301
+ return @priority[key] if @priority.key?(key)
302
+ return @values[key] if @values.key?(key)
303
+ return DEFAULTS[key] if DEFAULTS.key?(key)
304
+
305
+ if default
306
+ return default
307
+ elsif blk
308
+ return blk.call(key)
309
+ end
310
+
311
+ nil
312
+ end
313
+
314
+ alias [] get
315
+
316
+ def set(key, val, scope = nil)
317
+ if scope
318
+ key = [scope, key].join('.')
319
+ end
320
+
321
+ if Hash === val
322
+ val.each do |k, v|
323
+ set(k, v, key)
324
+ end
325
+ else
326
+ k = Config.remap_key(key)
327
+
328
+ if validator = VALIDATORS[k]
329
+ blk, msg = validator
330
+
331
+ unless blk.call(val, self)
332
+ error_msg = "invalid value for #{k} (#{val})"
333
+ error_msg << ", #{msg}" if msg
334
+ raise ConfigError, error_msg
335
+ end
336
+ end
337
+
338
+ if @regexp && k =~ @regexp
339
+ @priority[$1.to_sym] = val
340
+ end
341
+
342
+ @values[k] = val
343
+ end
344
+ end
345
+
346
+ alias []= set
347
+
348
+ def duration_ms(key, default = nil)
349
+ if (v = self[key]) && v.to_s =~ /^\s*(\d+)(s|sec|ms|micros|nanos)?\s*$/
350
+ v = $1.to_i
351
+ case $2
352
+ when "ms"
353
+ v
354
+ when "micros"
355
+ v / 1_000
356
+ when "nanos"
357
+ v / 1_000_000
358
+ else # "s", "sec", nil
359
+ v * 1000
360
+ end
361
+ else
362
+ default
363
+ end
364
+ end
365
+
366
+ def to_env
367
+ ret = []
368
+
369
+ ENV_TO_KEY.each do |k, v|
370
+ next if LEGACY[v]
371
+ c = get(v)
372
+ # Always need to pass daemon lib_path config even when default
373
+ if c != DEFAULTS[v] || ALWAYS_INCLUDE_IN_ENV.include?(v)
374
+ ret << "SKYLIGHT_#{k}" << cast_for_env(c) if c
375
+ end
376
+ end
377
+
378
+ ret << "SKYLIGHT_VALIDATE_AUTHENTICATION"
379
+ ret << "false"
380
+
381
+ ret
382
+ end
383
+
384
+ def write(path)
385
+ FileUtils.mkdir_p(File.dirname(path))
386
+
387
+ File.open(path, 'w') do |f|
388
+ f.puts <<-YAML
389
+ ---
390
+ # The authentication token for the application.
391
+ authentication: #{self[:authentication]}
392
+ YAML
393
+ end
394
+ end
395
+
396
+ #
397
+ #
398
+ # ===== Helpers =====
399
+ #
400
+ #
401
+
402
+ # @api private
403
+ def gc
404
+ @gc ||= GC.new(self, get('gc.profiler', VM::GC.new))
405
+ end
406
+
407
+ # @api private
408
+ def ignore_token?
409
+ get('test.ignore_token')
410
+ end
411
+
412
+ # @api private
413
+ def ignored_endpoints
414
+ @ignored_endpoints ||=
415
+ begin
416
+ val = Array(get(:'ignored_endpoint'))
417
+ val.concat(Array(get(:'ignored_endpoints')))
418
+ val
419
+ end
420
+ end
421
+
422
+ def root
423
+ self[:root] || Dir.pwd
424
+ end
425
+
426
+ def logger
427
+ @logger ||=
428
+ MUTEX.synchronize do
429
+ load_logger
430
+ end
431
+ end
432
+
433
+ def logger=(logger)
434
+ @logger = logger
435
+ end
436
+
437
+ def alert_logger
438
+ @alert_logger ||=
439
+ begin
440
+ MUTEX.synchronize do
441
+ unless l = @alert_logger
442
+ out = get(:'alert_log_file')
443
+
444
+ if out == '-'
445
+ out = Util::AlertLogger.new(load_logger)
446
+ elsif !(IO === out)
447
+ out = File.expand_path(out, root)
448
+ FileUtils.mkdir_p(File.dirname(out))
449
+ end
450
+
451
+ l = Logger.new(out)
452
+ l.level = Logger::DEBUG
453
+ end
454
+
455
+ l
456
+ end
457
+ end
458
+ end
459
+
460
+ def alert_logger=(logger)
461
+ @alert_logger = logger
462
+ end
463
+
464
+ private
465
+
466
+ def load_logger
467
+ unless l = @logger
468
+ out = get(:'log_file')
469
+ out = STDOUT if out == '-'
470
+
471
+ unless IO === out
472
+ out = File.expand_path(out, root)
473
+ FileUtils.mkdir_p(File.dirname(out))
474
+ end
475
+
476
+ l = Logger.new(out)
477
+ l.level =
478
+ case get(:'log_level')
479
+ when /^debug$/i then Logger::DEBUG
480
+ when /^info$/i then Logger::INFO
481
+ when /^warn$/i then Logger::WARN
482
+ when /^error$/i then Logger::ERROR
483
+ end
484
+ end
485
+
486
+ l
487
+ end
488
+
489
+ def cast_for_env(v)
490
+ case v
491
+ when true then 'true'
492
+ when false then 'false'
493
+ when nil then 'nil'
494
+ else v.to_s
495
+ end
496
+ end
497
+ end
498
+ end