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.
Files changed (3) hide show
  1. data/lib/uae/common.rb +111 -0
  2. data/lib/uae/component.rb +171 -0
  3. 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: []