bixby-agent 0.3.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.
- checksums.yaml +7 -0
- data/.document +5 -0
- data/.testguardrc +1 -0
- data/.travis.yml +25 -0
- data/Gemfile +61 -0
- data/Gemfile.lock +237 -0
- data/LICENSE +21 -0
- data/Rakefile +13 -0
- data/VERSION +1 -0
- data/bin/bixby-agent +15 -0
- data/bixby-agent.gemspec +186 -0
- data/etc/bixby-god.initd +70 -0
- data/etc/bixby.god +20 -0
- data/etc/god.d/bixby-agent.god +71 -0
- data/lib/bixby-agent.rb +16 -0
- data/lib/bixby-agent/agent.rb +98 -0
- data/lib/bixby-agent/agent/config.rb +109 -0
- data/lib/bixby-agent/agent/crypto.rb +73 -0
- data/lib/bixby-agent/agent/handshake.rb +81 -0
- data/lib/bixby-agent/agent/shell_exec.rb +111 -0
- data/lib/bixby-agent/agent_handler.rb +38 -0
- data/lib/bixby-agent/app.rb +208 -0
- data/lib/bixby-agent/app/cli.rb +112 -0
- data/lib/bixby-agent/config_exception.rb +5 -0
- data/lib/bixby-agent/help/system_time.rb +41 -0
- data/lib/bixby-agent/version.rb +8 -0
- data/lib/bixby-agent/websocket/client.rb +186 -0
- data/tasks/cane.rake +14 -0
- data/tasks/coverage.rake +2 -0
- data/tasks/coveralls.rake +11 -0
- data/tasks/jeweler.rake +21 -0
- data/tasks/test.rake +5 -0
- data/tasks/yard.rake +6 -0
- data/test/base.rb +92 -0
- data/test/helper.rb +29 -0
- data/test/stub_eventmachine.rb +38 -0
- data/test/support/root_dir/bixby.yml +8 -0
- data/test/support/root_dir/id_rsa +27 -0
- data/test/support/root_dir/server +27 -0
- data/test/support/root_dir/server.pub +9 -0
- data/test/support/test_bundle/bin/cat +2 -0
- data/test/support/test_bundle/bin/echo +2 -0
- data/test/support/test_bundle/digest +17 -0
- data/test/support/test_bundle/manifest.json +0 -0
- data/test/test_agent.rb +110 -0
- data/test/test_agent_exec.rb +53 -0
- data/test/test_agent_handler.rb +72 -0
- data/test/test_app.rb +138 -0
- data/test/test_bixby_common.rb +18 -0
- data/test/test_crypto.rb +78 -0
- data/test/websocket/test_client.rb +110 -0
- metadata +557 -0
data/etc/bixby-god.initd
ADDED
@@ -0,0 +1,70 @@
|
|
1
|
+
#!/bin/bash
|
2
|
+
|
3
|
+
### BEGIN INIT INFO
|
4
|
+
# Provides: bixby-god
|
5
|
+
# Required-Start: $local_fs $remote_fs $syslog $named $network $time
|
6
|
+
# Required-Stop: $local_fs $remote_fs $syslog $named $network
|
7
|
+
# Should-Start:
|
8
|
+
# Should-Stop:
|
9
|
+
# Default-Start: 2 3 4 5
|
10
|
+
# Default-Stop: 0 1 6
|
11
|
+
# Short-Description: Start/Stop the bixby god daemon
|
12
|
+
### END INIT INFO
|
13
|
+
|
14
|
+
# processname: god
|
15
|
+
# pidfile: /opt/bixby/var/bixby-god.pid
|
16
|
+
|
17
|
+
NAME=bixby
|
18
|
+
DESC=bixby
|
19
|
+
|
20
|
+
export BIXBY_HOME=/opt/bixby
|
21
|
+
GOD_PORT=18165 # actually used in unix socket name
|
22
|
+
GOD_CONF="-c $BIXBY_HOME/etc/bixby.god"
|
23
|
+
GOD_PID=$BIXBY_HOME/var/bixby-god.pid
|
24
|
+
GOD_BIN="$BIXBY_HOME/embedded/bin/god -P $GOD_PID -p $GOD_PORT"
|
25
|
+
|
26
|
+
start() {
|
27
|
+
echo " * Starting bixby"
|
28
|
+
$GOD_BIN $GOD_CONF
|
29
|
+
}
|
30
|
+
stop() {
|
31
|
+
echo " * Stopping bixby"
|
32
|
+
$GOD_BIN terminate >/dev/null
|
33
|
+
}
|
34
|
+
quit() {
|
35
|
+
$GOD_BIN quit >/dev/null
|
36
|
+
}
|
37
|
+
|
38
|
+
case $1 in
|
39
|
+
start)
|
40
|
+
start
|
41
|
+
;;
|
42
|
+
stop)
|
43
|
+
stop
|
44
|
+
;;
|
45
|
+
restart)
|
46
|
+
stop
|
47
|
+
start
|
48
|
+
;;
|
49
|
+
reload)
|
50
|
+
quit
|
51
|
+
start
|
52
|
+
;;
|
53
|
+
status)
|
54
|
+
$GOD_BIN status >/dev/null 2>&1
|
55
|
+
if [[ $? -eq 1 ]]; then
|
56
|
+
echo "Not running"
|
57
|
+
else
|
58
|
+
$GOD_BIN status
|
59
|
+
fi
|
60
|
+
;;
|
61
|
+
god)
|
62
|
+
$GOD_BIN ${@:2}
|
63
|
+
;;
|
64
|
+
*)
|
65
|
+
echo "Usage: $NAME {start|stop|restart|status|reload|god}" >&2
|
66
|
+
exit 1
|
67
|
+
;;
|
68
|
+
esac
|
69
|
+
|
70
|
+
exit 0
|
data/etc/bixby.god
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
|
2
|
+
# Main God config
|
3
|
+
#
|
4
|
+
# usage:
|
5
|
+
#
|
6
|
+
# # god -P var/bixby-god.pid -c etc/bixby.god
|
7
|
+
#
|
8
|
+
# god must be run as root!
|
9
|
+
#
|
10
|
+
# see also: http://godrb.com/
|
11
|
+
|
12
|
+
BIXBY_HOME = ENV["BIXBY_HOME"] || "/opt/bixby"
|
13
|
+
BIXBY_CLIENT = File.join(BIXBY_HOME, "bin", "bixby")
|
14
|
+
|
15
|
+
God.pid_file_directory = File.join(BIXBY_HOME, "var", "pids")
|
16
|
+
|
17
|
+
path = File.join(BIXBY_HOME, "etc", "god.d", "*.god")
|
18
|
+
Dir.glob(path).each do |conf|
|
19
|
+
God.load conf
|
20
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
|
2
|
+
God.watch do |w|
|
3
|
+
w.dir = BIXBY_HOME
|
4
|
+
w.name = "bixby-agent"
|
5
|
+
w.group = "bixby"
|
6
|
+
w.log = "#{BIXBY_HOME}/var/god.#{w.name}.log"
|
7
|
+
w.pid_file = "#{BIXBY_HOME}/var/#{w.name}.pid"
|
8
|
+
|
9
|
+
w.interval = 30.seconds
|
10
|
+
|
11
|
+
w.env = {}
|
12
|
+
w.start = "#{BIXBY_HOME}/bin/bixby-agent start"
|
13
|
+
w.stop = "#{BIXBY_HOME}/bin/bixby-agent stop"
|
14
|
+
|
15
|
+
w.start_grace = 10.seconds
|
16
|
+
w.restart_grace = 10.seconds
|
17
|
+
|
18
|
+
# other scripts may drop privs
|
19
|
+
# w.uid = USER
|
20
|
+
# w.gid = GROUP
|
21
|
+
|
22
|
+
w.behavior(:clean_pid_file)
|
23
|
+
|
24
|
+
# determine the state on startup
|
25
|
+
w.transition(:init, { true => :up, false => :start }) do |on|
|
26
|
+
on.condition(:process_running) do |c|
|
27
|
+
c.running = true
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
# determine when process has finished starting
|
32
|
+
w.transition([:start, :restart], :up) do |on|
|
33
|
+
on.condition(:process_running) do |c|
|
34
|
+
c.running = true
|
35
|
+
end
|
36
|
+
|
37
|
+
# failsafe
|
38
|
+
on.condition(:tries) do |c|
|
39
|
+
c.times = 8
|
40
|
+
c.within = 2.minutes
|
41
|
+
c.transition = :start
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
# start if process is not running
|
46
|
+
w.transition(:up, :start) do |on|
|
47
|
+
on.condition(:process_exits)
|
48
|
+
end
|
49
|
+
|
50
|
+
# restart if memory gets too high
|
51
|
+
w.transition(:up, :restart) do |on|
|
52
|
+
on.condition(:memory_usage) do |c|
|
53
|
+
c.above = 250.megabytes
|
54
|
+
c.times = 2
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
# lifecycle
|
59
|
+
w.lifecycle do |on|
|
60
|
+
on.condition(:flapping) do |c|
|
61
|
+
c.to_state = [:start, :restart]
|
62
|
+
c.times = 5
|
63
|
+
c.within = 5.minute
|
64
|
+
c.transition = :unmonitored
|
65
|
+
c.retry_in = 10.minutes
|
66
|
+
c.retry_times = 5
|
67
|
+
c.retry_within = 2.hours
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
end
|
data/lib/bixby-agent.rb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
|
2
|
+
# Set BIXBY_HOME when in dev environment
|
3
|
+
path = File.expand_path(File.join(File.dirname(__FILE__), ".."))
|
4
|
+
if !ENV["BIXBY_HOME"] && File.directory?(File.join(path, ".git")) &&
|
5
|
+
File.basename($0) == "bixby-agent" then
|
6
|
+
|
7
|
+
ENV["BIXBY_HOME"] = path
|
8
|
+
end
|
9
|
+
|
10
|
+
require "bixby-common"
|
11
|
+
require "bixby-client"
|
12
|
+
|
13
|
+
require "bixby-agent/agent"
|
14
|
+
require "bixby-agent/version"
|
15
|
+
|
16
|
+
Bixby::Agent.setup_env()
|
@@ -0,0 +1,98 @@
|
|
1
|
+
|
2
|
+
require "uri"
|
3
|
+
require "rbconfig"
|
4
|
+
|
5
|
+
require "bixby-agent/config_exception"
|
6
|
+
require "bixby-agent/agent/handshake"
|
7
|
+
require "bixby-agent/agent/shell_exec"
|
8
|
+
require "bixby-agent/agent/config"
|
9
|
+
|
10
|
+
module Bixby
|
11
|
+
|
12
|
+
class << self
|
13
|
+
attr_accessor :agent
|
14
|
+
end
|
15
|
+
|
16
|
+
class Agent
|
17
|
+
|
18
|
+
DEFAULT_ROOT_DIR = "/opt/bixby"
|
19
|
+
|
20
|
+
include Bixby::Log
|
21
|
+
include Config
|
22
|
+
include Handshake
|
23
|
+
include ShellExec
|
24
|
+
|
25
|
+
attr_accessor :manager_uri, :uuid, :mac_address,
|
26
|
+
:access_key, :secret_key, :client
|
27
|
+
|
28
|
+
def self.create(root_dir=nil, use_config = true)
|
29
|
+
|
30
|
+
agent = load_config(root_dir) if use_config
|
31
|
+
if agent.nil? then
|
32
|
+
# create a new one if unable to load
|
33
|
+
agent = new()
|
34
|
+
end
|
35
|
+
|
36
|
+
# pass config to some modules
|
37
|
+
Bixby.agent = agent
|
38
|
+
return agent if agent.new?
|
39
|
+
|
40
|
+
Bixby.manager_uri = agent.manager_uri
|
41
|
+
Bixby.client = Bixby::Client.new(agent.access_key, agent.secret_key)
|
42
|
+
return agent
|
43
|
+
end
|
44
|
+
|
45
|
+
def initialize()
|
46
|
+
#uri, tenant = nil, password = nil, root_dir = nil
|
47
|
+
Bixby::Log.setup_logger()
|
48
|
+
@new = true
|
49
|
+
|
50
|
+
@uuid = create_uuid()
|
51
|
+
@mac_address = get_mac_address()
|
52
|
+
create_keypair()
|
53
|
+
end
|
54
|
+
private_class_method :new
|
55
|
+
|
56
|
+
# Setup the environment for shelling out. Makes sure the correct Ruby
|
57
|
+
# version is on the path and that bixby-agent will be loaded by default
|
58
|
+
def self.setup_env
|
59
|
+
# make sure the correct ruby version is on the path
|
60
|
+
c = begin; ::RbConfig::CONFIG; rescue NameError; ::Config::CONFIG; end
|
61
|
+
ruby_dir = File.expand_path(c['bindir'])
|
62
|
+
|
63
|
+
shell = Mixlib::ShellOut.new("which ruby")
|
64
|
+
shell.run_command
|
65
|
+
if not $?.success? or File.dirname(shell.stdout.strip) != ruby_dir then
|
66
|
+
ENV["PATH"] = ruby_dir + File::PATH_SEPARATOR + ENV["PATH"]
|
67
|
+
end
|
68
|
+
|
69
|
+
# create RUBYLIB paths
|
70
|
+
paths = []
|
71
|
+
if ENV.include? "RUBYLIB" and not ENV["RUBYLIB"].empty? then
|
72
|
+
paths = ENV["RUBYLIB"].split(/:/)
|
73
|
+
end
|
74
|
+
$:.each { |p|
|
75
|
+
if p =~ %r(/gems/) and not paths.include? p then
|
76
|
+
paths << p
|
77
|
+
end
|
78
|
+
}
|
79
|
+
self_lib = File.expand_path(File.join(File.dirname(__FILE__), '../..', 'lib'))
|
80
|
+
paths << self_lib if not paths.include? self_lib
|
81
|
+
|
82
|
+
ENV["RUBYLIB"] = paths.join(":")
|
83
|
+
ENV["RUBYOPT"] = '-rbixby-client/script'
|
84
|
+
end
|
85
|
+
|
86
|
+
# Get the WebSocket API URI
|
87
|
+
#
|
88
|
+
# @return [String] uri
|
89
|
+
def manager_ws_uri
|
90
|
+
# convert manager uri to websocket
|
91
|
+
uri = URI.parse(manager_uri)
|
92
|
+
uri.scheme = (uri.scheme == "https" ? "wss" : "ws")
|
93
|
+
uri.path = "/wsapi"
|
94
|
+
return uri.to_s
|
95
|
+
end
|
96
|
+
|
97
|
+
end # Agent
|
98
|
+
end # Bixby
|
@@ -0,0 +1,109 @@
|
|
1
|
+
|
2
|
+
require 'yaml'
|
3
|
+
require 'fileutils'
|
4
|
+
|
5
|
+
module Bixby
|
6
|
+
class Agent
|
7
|
+
|
8
|
+
module Config
|
9
|
+
|
10
|
+
KEYS = %w{ manager_uri uuid mac_address access_key secret_key log_level }
|
11
|
+
|
12
|
+
module ClassMethods
|
13
|
+
|
14
|
+
def config_dir
|
15
|
+
Bixby.path("etc")
|
16
|
+
end
|
17
|
+
|
18
|
+
def config_file
|
19
|
+
File.join(config_dir, "bixby.yml")
|
20
|
+
end
|
21
|
+
|
22
|
+
def load_config(root_dir)
|
23
|
+
# make sure BIXBY_HOME is set correctly
|
24
|
+
ENV["BIXBY_HOME"] = File.expand_path(root_dir || ENV["BIXBY_HOME"] || Agent::DEFAULT_ROOT_DIR)
|
25
|
+
|
26
|
+
return nil if not File.exists? config_file
|
27
|
+
|
28
|
+
# load it!
|
29
|
+
begin
|
30
|
+
config = YAML.load_file(config_file)
|
31
|
+
if not config.kind_of? Hash or config.empty? then
|
32
|
+
bad_config("corrupted file contents")
|
33
|
+
end
|
34
|
+
|
35
|
+
log_level = config["log_level"]
|
36
|
+
log_level = log_level.strip.downcase if log_level.kind_of? String
|
37
|
+
Bixby::Log.setup_logger(:level => log_level)
|
38
|
+
|
39
|
+
agent = Agent.allocate
|
40
|
+
KEYS.each do |k|
|
41
|
+
m = "#{k}=".to_sym
|
42
|
+
agent.send(m, config[k]) if agent.respond_to? m
|
43
|
+
end
|
44
|
+
agent.new = false
|
45
|
+
|
46
|
+
return agent
|
47
|
+
|
48
|
+
rescue Exception => ex
|
49
|
+
if ex.kind_of? SystemExit then
|
50
|
+
raise ex
|
51
|
+
end
|
52
|
+
bad_config(ex) if ex.message != "exit"
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def bad_config(ex = nil)
|
57
|
+
# TODO should force a reinstall/handshake?
|
58
|
+
$stderr.puts "error loading config from #{config_file}"
|
59
|
+
$stderr.puts "(#{ex})" if ex
|
60
|
+
$stderr.puts "exiting"
|
61
|
+
exit 1
|
62
|
+
end
|
63
|
+
|
64
|
+
end # ClassMethods
|
65
|
+
|
66
|
+
def self.included(clazz)
|
67
|
+
clazz.extend(ClassMethods)
|
68
|
+
end
|
69
|
+
|
70
|
+
def new=(val)
|
71
|
+
@new = val
|
72
|
+
end
|
73
|
+
|
74
|
+
def new?
|
75
|
+
@new
|
76
|
+
end
|
77
|
+
|
78
|
+
def config_dir
|
79
|
+
self.class.config_dir
|
80
|
+
end
|
81
|
+
|
82
|
+
def config_file
|
83
|
+
self.class.config_file
|
84
|
+
end
|
85
|
+
|
86
|
+
def init_config_dir
|
87
|
+
return if File.exists? config_dir
|
88
|
+
begin
|
89
|
+
FileUtils.mkdir_p(config_dir)
|
90
|
+
rescue Exception => ex
|
91
|
+
raise IOError.new(ex.message)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
def save_config
|
96
|
+
init_config_dir()
|
97
|
+
config = {}
|
98
|
+
KEYS.each do |k|
|
99
|
+
m = k.to_sym
|
100
|
+
config[k] = self.send(m) if self.respond_to? m
|
101
|
+
end
|
102
|
+
config["log_level"] = Logging::Logger.root.level
|
103
|
+
File.open(config_file, 'w') { |out| out.write YAML.dump(config) }
|
104
|
+
end
|
105
|
+
|
106
|
+
end # Config
|
107
|
+
|
108
|
+
end # Agent
|
109
|
+
end # Bixby
|
@@ -0,0 +1,73 @@
|
|
1
|
+
|
2
|
+
require "openssl"
|
3
|
+
require "base64"
|
4
|
+
|
5
|
+
module Bixby
|
6
|
+
class Agent
|
7
|
+
|
8
|
+
module Crypto
|
9
|
+
|
10
|
+
# create crypto keypair and save in config folder
|
11
|
+
def create_keypair
|
12
|
+
init_config_dir()
|
13
|
+
pair = OpenSSL::PKey::RSA.generate(2048)
|
14
|
+
File.open(private_key_file, 'w') { |out| out.write(pair.to_s) }
|
15
|
+
end
|
16
|
+
|
17
|
+
def private_key_file
|
18
|
+
File.join(self.config_dir, "id_rsa")
|
19
|
+
end
|
20
|
+
|
21
|
+
def keypair
|
22
|
+
@keypair ||= OpenSSL::PKey::RSA.new(File.read(private_key_file))
|
23
|
+
end
|
24
|
+
|
25
|
+
def public_key
|
26
|
+
@public_key ||= keypair.public_key
|
27
|
+
end
|
28
|
+
|
29
|
+
def private_key
|
30
|
+
keypair
|
31
|
+
end
|
32
|
+
|
33
|
+
def server_key_file
|
34
|
+
File.join(self.config_dir, "server.pub")
|
35
|
+
end
|
36
|
+
|
37
|
+
def have_server_key?
|
38
|
+
File.exists? server_key_file
|
39
|
+
end
|
40
|
+
|
41
|
+
def server_key
|
42
|
+
@server_key ||= OpenSSL::PKey::RSA.new(File.read(server_key_file))
|
43
|
+
end
|
44
|
+
|
45
|
+
def crypto_enabled?
|
46
|
+
b = ENV["BIXBY_NOCRYPTO"]
|
47
|
+
!(b and %w{1 true yes}.include? b)
|
48
|
+
end
|
49
|
+
|
50
|
+
# Encrypt data using the server's public key
|
51
|
+
#
|
52
|
+
# @param [String] data data to encrypt
|
53
|
+
#
|
54
|
+
# @return [String] Base64 result
|
55
|
+
def encrypt_for_server(data)
|
56
|
+
Bixby::CryptoUtil.encrypt(data, self.uuid, server_key, keypair)
|
57
|
+
end
|
58
|
+
|
59
|
+
# Decrypt data that was encrypted with our public key
|
60
|
+
#
|
61
|
+
# @param [String] data Base64 encoded data
|
62
|
+
#
|
63
|
+
# @return [String] unencrypted data
|
64
|
+
def decrypt_from_server(data)
|
65
|
+
data = StringIO.new(data, 'rb')
|
66
|
+
uuid = data.readline.strip # TODO throwaway the uuid for now
|
67
|
+
Bixby::CryptoUtil.decrypt(data, keypair, server_key)
|
68
|
+
end
|
69
|
+
|
70
|
+
end # Crypto
|
71
|
+
|
72
|
+
end # Agent
|
73
|
+
end # Bixby
|