uae-common 0.0.1
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.
- data/lib/uae/common.rb +111 -0
- data/lib/uae/component.rb +171 -0
- metadata +47 -0
data/lib/uae/common.rb
ADDED
@@ -0,0 +1,111 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
require 'socket'
|
3
|
+
|
4
|
+
module UAE
|
5
|
+
|
6
|
+
# 获取本机ip
|
7
|
+
def self.local_ip(route)
|
8
|
+
orig, Socket.do_not_reverse_lookup = Socket.do_not_reverse_lookup, true
|
9
|
+
UDPSocket.open {|s| s.connect(route, 1); s.addr.last }
|
10
|
+
ensure
|
11
|
+
Socket.do_not_reverse_lookup = orig
|
12
|
+
end
|
13
|
+
|
14
|
+
# 获取一个uuid
|
15
|
+
def self.secure_uuid
|
16
|
+
result = File.open('/dev/urandom') { |x| x.read(16).unpack('H*')[0] }
|
17
|
+
end
|
18
|
+
|
19
|
+
# 获取一个随机端口
|
20
|
+
def self.grab_ephemeral_port
|
21
|
+
socket = TCPServer.new('0.0.0.0', 0)
|
22
|
+
socket.setsockopt(Socket::SOL_SOCKET, Socket::SO_REUSEADDR, true)
|
23
|
+
Socket.do_not_reverse_lookup = true
|
24
|
+
port = socket.addr[1]
|
25
|
+
socket.close
|
26
|
+
return port
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.process_running?(pid)
|
30
|
+
return false unless pid && (pid > 0)
|
31
|
+
output = %x[ps -o rss= -p #{pid}]
|
32
|
+
return true if ($? == 0 && !output.empty?)
|
33
|
+
# fail otherwise..
|
34
|
+
return false
|
35
|
+
end
|
36
|
+
|
37
|
+
class PidFile
|
38
|
+
class ProcessRunningError < StandardError
|
39
|
+
end
|
40
|
+
|
41
|
+
def initialize(pid_file, create_parents=true)
|
42
|
+
@pid_file = pid_file
|
43
|
+
@dirty = true
|
44
|
+
write(create_parents)
|
45
|
+
end
|
46
|
+
|
47
|
+
# Removes the created pidfile
|
48
|
+
def unlink()
|
49
|
+
return unless @dirty
|
50
|
+
|
51
|
+
# Swallowing exception here is fine. Removing the pid files is a courtesy.
|
52
|
+
begin
|
53
|
+
File.unlink(@pid_file)
|
54
|
+
@dirty = false
|
55
|
+
rescue
|
56
|
+
end
|
57
|
+
self
|
58
|
+
end
|
59
|
+
|
60
|
+
# Removes the created pidfile upon receipt of the supplied signals
|
61
|
+
def unlink_on_signals(*sigs)
|
62
|
+
return unless @dirty
|
63
|
+
|
64
|
+
sigs.each do |s|
|
65
|
+
Signal.trap(s) { unlink() }
|
66
|
+
end
|
67
|
+
self
|
68
|
+
end
|
69
|
+
|
70
|
+
def unlink_at_exit()
|
71
|
+
at_exit { unlink() }
|
72
|
+
self
|
73
|
+
end
|
74
|
+
|
75
|
+
def to_s()
|
76
|
+
@pid_file
|
77
|
+
end
|
78
|
+
|
79
|
+
protected
|
80
|
+
|
81
|
+
# Atomically writes the pidfile.
|
82
|
+
# NB: This throws exceptions if the pidfile contains the pid of another running process.
|
83
|
+
#
|
84
|
+
# +create_parents+ If true, all parts of the path up to the file's dirname will be created.
|
85
|
+
#
|
86
|
+
def write(create_parents=true)
|
87
|
+
FileUtils.mkdir_p(File.dirname(@pid_file)) if create_parents
|
88
|
+
|
89
|
+
# Protip from Wilson: binary mode keeps things sane under Windows
|
90
|
+
# Closing the fd releases our lock
|
91
|
+
File.open(@pid_file, 'a+b', 0644) do |f|
|
92
|
+
f.flock(File::LOCK_EX)
|
93
|
+
|
94
|
+
# Check if process is already running
|
95
|
+
pid = f.read().strip().to_i()
|
96
|
+
if pid == Process.pid()
|
97
|
+
return
|
98
|
+
elsif UAE.process_running?(pid)
|
99
|
+
raise ProcessRunningError.new("Process already running (pid=%d)." % (pid))
|
100
|
+
end
|
101
|
+
|
102
|
+
# We're good to go, write our pid
|
103
|
+
f.truncate(0)
|
104
|
+
f.rewind()
|
105
|
+
f.write("%d\n" % (Process.pid()))
|
106
|
+
f.flush()
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end # class PidFile
|
110
|
+
|
111
|
+
end
|
@@ -0,0 +1,171 @@
|
|
1
|
+
# Copyright (c) 2009-2011 VMware, Inc.
|
2
|
+
require "eventmachine"
|
3
|
+
require 'thin'
|
4
|
+
require "yajl"
|
5
|
+
require "nats/client"
|
6
|
+
require "base64"
|
7
|
+
require 'set'
|
8
|
+
|
9
|
+
module UAE
|
10
|
+
|
11
|
+
RACK_JSON_HDR = { 'Content-Type' => 'application/json' }
|
12
|
+
RACK_TEXT_HDR = { 'Content-Type' => 'text/plaintext' }
|
13
|
+
|
14
|
+
class Varz
|
15
|
+
def initialize(logger)
|
16
|
+
@logger = logger
|
17
|
+
end
|
18
|
+
|
19
|
+
def call(env)
|
20
|
+
@logger.debug "varz access"
|
21
|
+
varz = Yajl::Encoder.encode(Component.updated_varz, :pretty => true, :terminator => "\n")
|
22
|
+
[200, { 'Content-Type' => 'application/json', 'Content-Length' => varz.length.to_s }, varz]
|
23
|
+
rescue => e
|
24
|
+
@logger.error "varz error #{e.inspect} #{e.backtrace.join("\n")}"
|
25
|
+
raise e
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
class Healthz
|
30
|
+
def initialize(logger)
|
31
|
+
@logger = logger
|
32
|
+
end
|
33
|
+
|
34
|
+
def call(env)
|
35
|
+
@logger.debug "healthz access"
|
36
|
+
healthz = Component.updated_healthz
|
37
|
+
[200, { 'Content-Type' => 'application/json', 'Content-Length' => healthz.length.to_s }, healthz]
|
38
|
+
rescue => e
|
39
|
+
@logger.error "healthz error #{e.inspect} #{e.backtrace.join("\n")}"
|
40
|
+
raise e
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
# Common component setup for discovery and monitoring
|
45
|
+
class Component
|
46
|
+
|
47
|
+
# We will suppress these from normal varz reporting by default.
|
48
|
+
CONFIG_SUPPRESS = Set.new([:mbus, :service_mbus, :keys, :database_environment, :mysql, :password])
|
49
|
+
|
50
|
+
class << self
|
51
|
+
|
52
|
+
attr_reader :varz
|
53
|
+
attr_accessor :healthz
|
54
|
+
|
55
|
+
def updated_varz
|
56
|
+
@last_varz_update ||= 0
|
57
|
+
if Time.now.to_f - @last_varz_update >= 1
|
58
|
+
# Snapshot uptime
|
59
|
+
@varz[:uptime] = UAE.uptime_string(Time.now - @varz[:start])
|
60
|
+
|
61
|
+
# Grab current cpu and memory usage.
|
62
|
+
rss, pcpu = `ps -o rss=,pcpu= -p #{Process.pid}`.split
|
63
|
+
@varz[:mem] = rss.to_i
|
64
|
+
@varz[:cpu] = pcpu.to_f
|
65
|
+
|
66
|
+
@last_varz_update = Time.now.to_f
|
67
|
+
end
|
68
|
+
varz
|
69
|
+
end
|
70
|
+
|
71
|
+
def updated_healthz
|
72
|
+
@last_healthz_update ||= 0
|
73
|
+
if Time.now.to_f - @last_healthz_update >= 1
|
74
|
+
# ...
|
75
|
+
@last_healthz_update = Time.now.to_f
|
76
|
+
end
|
77
|
+
|
78
|
+
healthz
|
79
|
+
end
|
80
|
+
|
81
|
+
def start_http_server(host, port, auth, logger)
|
82
|
+
http_server = Thin::Server.new(host, port, :signals => false) do
|
83
|
+
Thin::Logging.silent = true
|
84
|
+
use Rack::Auth::Basic do |username, password|
|
85
|
+
[username, password] == auth
|
86
|
+
end
|
87
|
+
map '/healthz' do
|
88
|
+
run Healthz.new(logger)
|
89
|
+
end
|
90
|
+
map '/varz' do
|
91
|
+
run Varz.new(logger)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
http_server.start!
|
95
|
+
end
|
96
|
+
|
97
|
+
def uuid
|
98
|
+
@discover[:uuid]
|
99
|
+
end
|
100
|
+
|
101
|
+
def register(opts)
|
102
|
+
uuid = UAE.secure_uuid
|
103
|
+
type = opts[:type]
|
104
|
+
index = opts[:index]
|
105
|
+
uuid = "#{index}-#{uuid}" if index
|
106
|
+
host = opts[:host] || UAE.local_ip
|
107
|
+
port = opts[:port] || UAE.grab_ephemeral_port
|
108
|
+
nats = opts[:nats] || NATS
|
109
|
+
auth = [opts[:user] || UAE.secure_uuid, opts[:password] || UAE.secure_uuid]
|
110
|
+
logger = opts[:logger] || Logger.new(nil)
|
111
|
+
|
112
|
+
# Discover message limited
|
113
|
+
@discover = {
|
114
|
+
:type => type,
|
115
|
+
:index => index,
|
116
|
+
:uuid => uuid,
|
117
|
+
:host => "#{host}:#{port}",
|
118
|
+
:credentials => auth,
|
119
|
+
:start => Time.now
|
120
|
+
}
|
121
|
+
|
122
|
+
# Varz is customizable
|
123
|
+
@varz = @discover.dup
|
124
|
+
@varz[:config] = sanitize_config(opts[:config]) if opts[:config]
|
125
|
+
|
126
|
+
@healthz = "ok\n".freeze
|
127
|
+
|
128
|
+
# Next steps require EM
|
129
|
+
raise "EventMachine reactor needs to be running" if !EventMachine.reactor_running?
|
130
|
+
|
131
|
+
# Startup the http endpoint for /varz and /healthz
|
132
|
+
start_http_server(host, port, auth, logger)
|
133
|
+
|
134
|
+
# Listen for discovery requests
|
135
|
+
nats.subscribe('vcap.component.discover') do |msg, reply|
|
136
|
+
update_discover_uptime
|
137
|
+
nats.publish(reply, @discover.to_json)
|
138
|
+
end
|
139
|
+
|
140
|
+
# Also announce ourselves on startup..
|
141
|
+
nats.publish('vcap.component.announce', @discover.to_json)
|
142
|
+
end
|
143
|
+
|
144
|
+
def update_discover_uptime
|
145
|
+
@discover[:uptime] = UAE.uptime_string(Time.now - @discover[:start])
|
146
|
+
end
|
147
|
+
|
148
|
+
def clear_level(h)
|
149
|
+
h.each do |k, v|
|
150
|
+
if CONFIG_SUPPRESS.include?(k.to_sym)
|
151
|
+
h.delete(k)
|
152
|
+
else
|
153
|
+
clear_level(h[k]) if v.instance_of? Hash
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
def sanitize_config(config)
|
159
|
+
# Can't Marshal/Deep Copy logger instances that services use
|
160
|
+
if config[:logger]
|
161
|
+
config = config.dup
|
162
|
+
config.delete(:logger)
|
163
|
+
end
|
164
|
+
# Deep copy
|
165
|
+
config = Marshal.load(Marshal.dump(config))
|
166
|
+
clear_level(config)
|
167
|
+
config
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|
171
|
+
end
|
metadata
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: uae-common
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- zhonggy
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2013-01-15 00:00:00.000000000 Z
|
13
|
+
dependencies: []
|
14
|
+
description: UAE common library
|
15
|
+
email:
|
16
|
+
- zhonggy@ucweb.com
|
17
|
+
executables: []
|
18
|
+
extensions: []
|
19
|
+
extra_rdoc_files: []
|
20
|
+
files:
|
21
|
+
- lib/uae/common.rb
|
22
|
+
- lib/uae/component.rb
|
23
|
+
homepage: ''
|
24
|
+
licenses: []
|
25
|
+
post_install_message:
|
26
|
+
rdoc_options: []
|
27
|
+
require_paths:
|
28
|
+
- lib
|
29
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
30
|
+
none: false
|
31
|
+
requirements:
|
32
|
+
- - ! '>='
|
33
|
+
- !ruby/object:Gem::Version
|
34
|
+
version: '0'
|
35
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
36
|
+
none: false
|
37
|
+
requirements:
|
38
|
+
- - ! '>='
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
requirements: []
|
42
|
+
rubyforge_project:
|
43
|
+
rubygems_version: 1.8.23
|
44
|
+
signing_key:
|
45
|
+
specification_version: 3
|
46
|
+
summary: It works for UAE project
|
47
|
+
test_files: []
|