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
@@ -0,0 +1,81 @@
|
|
1
|
+
|
2
|
+
require 'facter'
|
3
|
+
require 'uuidtools'
|
4
|
+
|
5
|
+
require "bixby-agent/agent/crypto"
|
6
|
+
|
7
|
+
module Bixby
|
8
|
+
class Agent
|
9
|
+
|
10
|
+
module Handshake
|
11
|
+
|
12
|
+
include Crypto
|
13
|
+
|
14
|
+
# Register the agent with the server
|
15
|
+
#
|
16
|
+
# @param [String] url Bixby manager URL
|
17
|
+
# @param [String] tenant Tenant name
|
18
|
+
# @param [String] password Tenant registration password
|
19
|
+
# @param [String] tags Comma-separated list of tags (e.g., "foo,bar")
|
20
|
+
#
|
21
|
+
# @return [JsonResponse] response from server
|
22
|
+
def register_agent(url, tenant, password, tags=nil)
|
23
|
+
Bixby.manager_uri = @manager_uri = url
|
24
|
+
ret = Bixby::Inventory.register_agent({
|
25
|
+
:uuid => @uuid,
|
26
|
+
:public_key => self.public_key.to_s,
|
27
|
+
:hostname => get_hostname(),
|
28
|
+
:tenant => tenant,
|
29
|
+
:password => password,
|
30
|
+
:tags => tags,
|
31
|
+
:version => Bixby::Agent::VERSION
|
32
|
+
})
|
33
|
+
|
34
|
+
if ret.fail? then
|
35
|
+
return ret
|
36
|
+
end
|
37
|
+
|
38
|
+
@access_key = ret.data["access_key"]
|
39
|
+
@secret_key = ret.data["secret_key"]
|
40
|
+
Bixby.client = Bixby::Client.new(access_key, secret_key)
|
41
|
+
|
42
|
+
# success, store server's pub key
|
43
|
+
File.open(self.server_key_file, 'w') do |f|
|
44
|
+
f.puts(ret.data["server_key"])
|
45
|
+
end
|
46
|
+
|
47
|
+
return ret
|
48
|
+
end
|
49
|
+
|
50
|
+
def mac_changed?
|
51
|
+
(not @mac_address.nil? and (@mac_address != get_mac_address()))
|
52
|
+
end
|
53
|
+
|
54
|
+
def get_hostname
|
55
|
+
`hostname`.strip
|
56
|
+
end
|
57
|
+
|
58
|
+
# Get the mac address of the system's primary interface
|
59
|
+
def get_mac_address
|
60
|
+
return @mac if not @mac.nil?
|
61
|
+
Facter.collection.fact(:ipaddress).value # force value to be loaded now (usually lazy-loaded)
|
62
|
+
Facter.collection.fact(:interfaces).value
|
63
|
+
Facter.collection.fact(:macaddress).value
|
64
|
+
vals = Facter.collection.to_hash
|
65
|
+
ip = vals["ipaddress"]
|
66
|
+
raise "Unable to find IP address" if ip.nil?
|
67
|
+
# use the primary IP of the system to find the associated interface name (e.g., en0 or eth0)
|
68
|
+
int = vals.find{ |k,v| v == ip && k != "ipaddress" }.first.to_s.split(/_/)[1]
|
69
|
+
raise "Unable to find primary interface" if int.nil? or int.empty?
|
70
|
+
# finally, get the mac address
|
71
|
+
@mac = vals["macaddress_#{int}"]
|
72
|
+
end
|
73
|
+
|
74
|
+
def create_uuid
|
75
|
+
UUIDTools::UUID.random_create.hexdigest
|
76
|
+
end
|
77
|
+
|
78
|
+
end # Handshake
|
79
|
+
|
80
|
+
end # Agent
|
81
|
+
end # Bixby
|
@@ -0,0 +1,111 @@
|
|
1
|
+
|
2
|
+
require 'mixlib/shellout'
|
3
|
+
|
4
|
+
module Bixby
|
5
|
+
class Agent
|
6
|
+
|
7
|
+
module ShellExec
|
8
|
+
|
9
|
+
# Shell exec a local command with the given params
|
10
|
+
#
|
11
|
+
# @param [Hash] params CommandSpec hash
|
12
|
+
# @option params [String] :repo
|
13
|
+
# @option params [String] :bundle
|
14
|
+
# @option params [String] :command
|
15
|
+
# @option params [String] :args
|
16
|
+
# @option params [String] :stdin
|
17
|
+
# @option params [String] :digest Expected bundle digest
|
18
|
+
# @option params [Hash] :env Hash of extra ENV key/values to pass to sub-shell
|
19
|
+
# @option params [String] :user User to run as
|
20
|
+
# @option params [String] :group Group to run as
|
21
|
+
#
|
22
|
+
# @return [CommandResponse]
|
23
|
+
#
|
24
|
+
# @raise [BundleNotFound] If bundle doesn't exist or digest does not match
|
25
|
+
# @raise [CommandNotFound] If command doesn't exist
|
26
|
+
def shell_exec(params)
|
27
|
+
digest = params.delete("digest") || params.delete(:digest)
|
28
|
+
|
29
|
+
spec = CommandSpec.new(params)
|
30
|
+
log.debug { "shell_exec:\n" + spec.to_s + "\n" }
|
31
|
+
spec.validate(digest)
|
32
|
+
|
33
|
+
cmd = "#{spec.command_file} #{spec.args}"
|
34
|
+
|
35
|
+
# Cleanup the ENV and execute
|
36
|
+
old_env = {}
|
37
|
+
%W{BUNDLE_BIN_PATH BUNDLE_GEMFILE}.each{ |r|
|
38
|
+
old_env[r] = ENV.delete(r) if ENV.include?(r) }
|
39
|
+
|
40
|
+
logger.debug("exec: #{cmd}")
|
41
|
+
shell = Mixlib::ShellOut.new(cmd, :input => spec.stdin,
|
42
|
+
:env => spec.env,
|
43
|
+
:user => uid(spec.user),
|
44
|
+
:group => gid(spec.group))
|
45
|
+
|
46
|
+
shell.run_command
|
47
|
+
|
48
|
+
old_env.each{ |k,v| ENV[k] = v } # reset the ENV
|
49
|
+
|
50
|
+
return CommandResponse.new({ :status => shell.exitstatus,
|
51
|
+
:stdout => shell.stdout,
|
52
|
+
:stderr => shell.stderr })
|
53
|
+
end
|
54
|
+
|
55
|
+
|
56
|
+
private
|
57
|
+
|
58
|
+
# Return uid of 'bixby' user, if it exists
|
59
|
+
#
|
60
|
+
# @param [String] user username to run as [Optional, default=bixby]
|
61
|
+
# @return [Fixnum]
|
62
|
+
def uid(user)
|
63
|
+
if Process.uid != 0 then
|
64
|
+
logger.warn("Can't change effective uid unless running as root")
|
65
|
+
return nil
|
66
|
+
end
|
67
|
+
|
68
|
+
if user then
|
69
|
+
begin
|
70
|
+
return Etc.getpwnam(user).uid
|
71
|
+
rescue ArgumentError => ex
|
72
|
+
logger.warn("Username '#{user}' was invalid: #{ex.message}")
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
begin
|
77
|
+
return Etc.getpwnam("bixby").uid
|
78
|
+
rescue ArgumentError
|
79
|
+
end
|
80
|
+
return nil
|
81
|
+
end
|
82
|
+
|
83
|
+
# Return uid of 'bixby' group, if it exists
|
84
|
+
#
|
85
|
+
# @param [String] group group to run as [Optional, default=bixby]
|
86
|
+
# @return [Fixnum]
|
87
|
+
def gid(group)
|
88
|
+
if Process.uid != 0 then
|
89
|
+
logger.warn("Can't change effective gid unless running as root")
|
90
|
+
return nil
|
91
|
+
end
|
92
|
+
|
93
|
+
if group then
|
94
|
+
begin
|
95
|
+
return Etc.getgrnam(group).gid
|
96
|
+
rescue ArgumentError => ex
|
97
|
+
logger.warn("Group '#{group}' was invalid: #{ex.message}")
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
begin
|
102
|
+
return Etc.getgrnam("bixby").gid
|
103
|
+
rescue ArgumentError
|
104
|
+
end
|
105
|
+
return nil
|
106
|
+
end
|
107
|
+
|
108
|
+
end # Exec
|
109
|
+
|
110
|
+
end # Agent
|
111
|
+
end # Bixby
|
@@ -0,0 +1,38 @@
|
|
1
|
+
|
2
|
+
module Bixby
|
3
|
+
|
4
|
+
class AgentHandler < Bixby::RpcHandler
|
5
|
+
|
6
|
+
include Bixby::Log
|
7
|
+
# include Bixby::CryptoUti
|
8
|
+
|
9
|
+
def initialize(request)
|
10
|
+
@request = request
|
11
|
+
end
|
12
|
+
|
13
|
+
def handle(json_req)
|
14
|
+
|
15
|
+
begin
|
16
|
+
cmd_res = Bixby.agent.shell_exec(json_req.params)
|
17
|
+
log.debug { cmd_res.to_s + "\n---\n\n\n" }
|
18
|
+
return cmd_res.to_json_response
|
19
|
+
|
20
|
+
rescue Exception => ex
|
21
|
+
if ex.kind_of? BundleNotFound then
|
22
|
+
log.debug(ex.message)
|
23
|
+
return JsonResponse.bundle_not_found(ex.message)
|
24
|
+
|
25
|
+
elsif ex.kind_of? CommandNotFound then
|
26
|
+
log.debug(ex.message)
|
27
|
+
return JsonResponse.command_not_found(ex.message)
|
28
|
+
end
|
29
|
+
|
30
|
+
log.error(ex)
|
31
|
+
return JsonResponse.new("fail", ex.message, nil, 500)
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
@@ -0,0 +1,208 @@
|
|
1
|
+
|
2
|
+
require 'bixby-agent/websocket/client'
|
3
|
+
require 'bixby-agent/agent_handler'
|
4
|
+
require 'bixby-agent/app/cli'
|
5
|
+
require 'bixby-agent/help/system_time'
|
6
|
+
|
7
|
+
require 'daemons'
|
8
|
+
require 'highline/import'
|
9
|
+
|
10
|
+
module Bixby
|
11
|
+
class App
|
12
|
+
|
13
|
+
include CLI
|
14
|
+
include Bixby::Log
|
15
|
+
|
16
|
+
# Load Agent
|
17
|
+
#
|
18
|
+
# Load the agent from $BIXBY_HOME. If no existing configuration was found,
|
19
|
+
# try to register with the server if we have the correct parameters.
|
20
|
+
def load_agent
|
21
|
+
|
22
|
+
begin
|
23
|
+
agent = Agent.create(@config[:directory])
|
24
|
+
rescue Exception => ex
|
25
|
+
if ex.message =~ /manager URI/ then
|
26
|
+
# if unable to load from config and no/bad uri passed, bail!
|
27
|
+
$stderr.puts "ERROR: a valid manager URI is required on first run"
|
28
|
+
$stderr.puts
|
29
|
+
$stderr.puts @opt_parser.help()
|
30
|
+
exit 1
|
31
|
+
end
|
32
|
+
raise ex
|
33
|
+
end
|
34
|
+
|
35
|
+
# TODO disable mac detection for now; it doesn't work in certain cases
|
36
|
+
# e.g., when you stop/start an instance on EC2 a new mac is issued
|
37
|
+
#
|
38
|
+
# if not agent.new? and agent.mac_changed? then
|
39
|
+
# # loaded from config and mac has changed
|
40
|
+
# agent = Agent.create(opts, false)
|
41
|
+
# end
|
42
|
+
|
43
|
+
if agent.new? then
|
44
|
+
|
45
|
+
if !@config[:register] then
|
46
|
+
# --register not passed, bail out
|
47
|
+
if File.exists? agent.config_file then
|
48
|
+
$stderr.puts "Unable to load agent config from #{agent.config_file}; pass --register to reinitialize"
|
49
|
+
else
|
50
|
+
$stderr.puts "Unable to load agent from BIXBY_HOME=#{ENV['BIXBY_HOME']}; pass --register to initialize"
|
51
|
+
end
|
52
|
+
exit 1
|
53
|
+
end
|
54
|
+
|
55
|
+
# validate uri
|
56
|
+
uri = @argv.shift || @config[:register]
|
57
|
+
begin
|
58
|
+
if uri.nil? or URI.parse(uri).nil? or URI.join(uri, "/api").nil? then
|
59
|
+
raise ConfigException, "Missing manager URI", caller
|
60
|
+
end
|
61
|
+
rescue URI::Error => ex
|
62
|
+
raise ConfigException, "Bad manager URI: '#{uri}'"
|
63
|
+
end
|
64
|
+
|
65
|
+
tenant = @config[:tenant] || HighLine.new.ask("Tenant: ")
|
66
|
+
password = @config[:password] || HighLine.new.ask("Enter agent registration password: ") { |q| q.echo = "*" }
|
67
|
+
|
68
|
+
# register
|
69
|
+
$stdout.puts "Going to register with manager: #{uri}"
|
70
|
+
if (ret = agent.register_agent(uri, tenant, password, @config[:tags])).fail? then
|
71
|
+
$stderr.puts "error: failed to register with manager!"
|
72
|
+
$stderr.puts "reason:"
|
73
|
+
if ret.message =~ /900 seconds old/ then
|
74
|
+
Help::SystemTime.print()
|
75
|
+
else
|
76
|
+
$stderr.puts " #{ret.message}"
|
77
|
+
end
|
78
|
+
exit 1
|
79
|
+
end
|
80
|
+
agent.save_config()
|
81
|
+
ARGV.clear # make sure it's empty so daemon starts properly
|
82
|
+
$stdout.puts "Registration successful; launching bixby-agent into background"
|
83
|
+
end
|
84
|
+
agent
|
85
|
+
end
|
86
|
+
|
87
|
+
# Run the agent app!
|
88
|
+
#
|
89
|
+
# This is the main method. Will boot and configure the agent, connect to the
|
90
|
+
# server and start the daemon.
|
91
|
+
def run!
|
92
|
+
# load agent from config or cli opts
|
93
|
+
agent = load_agent()
|
94
|
+
|
95
|
+
fix_ownership()
|
96
|
+
|
97
|
+
# debug mode, stay in front
|
98
|
+
if @config[:debug] then
|
99
|
+
Logging::Logger.root.add_appenders("stdout")
|
100
|
+
return start_websocket_client()
|
101
|
+
end
|
102
|
+
|
103
|
+
# start daemon
|
104
|
+
validate_argv()
|
105
|
+
daemon_dir = Bixby.path("var")
|
106
|
+
ensure_state_dir(daemon_dir)
|
107
|
+
close_fds()
|
108
|
+
|
109
|
+
daemon_opts = {
|
110
|
+
:dir => daemon_dir,
|
111
|
+
:dir_mode => :normal,
|
112
|
+
:log_output => true,
|
113
|
+
:stop_proc => lambda { logger.info "Agent shutdown on service stop command" }
|
114
|
+
}
|
115
|
+
|
116
|
+
Daemons.run_proc("bixby-agent", daemon_opts) do
|
117
|
+
Logging.logger.root.clear_appenders
|
118
|
+
start_websocket_client()
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
# Open the WebSocket channel with the Manager
|
123
|
+
#
|
124
|
+
# NOTE: this call will not return!
|
125
|
+
def start_websocket_client
|
126
|
+
# make sure log level is still set correctly here
|
127
|
+
Bixby::Log.setup_logger(:level => Logging.appenders["file"].level)
|
128
|
+
logger.info "Started Bixby Agent"
|
129
|
+
@client = Bixby::WebSocket::Client.new(Bixby.agent.manager_ws_uri, AgentHandler)
|
130
|
+
trap_signals()
|
131
|
+
@client.start
|
132
|
+
end
|
133
|
+
|
134
|
+
def trap_signals
|
135
|
+
%w{INT QUIT TERM}.each do |sig|
|
136
|
+
Kernel.trap(sig) do
|
137
|
+
@client.stop()
|
138
|
+
puts # to get a blank line after the ^C in the term
|
139
|
+
reason = sig + (sig == "INT" ? " (^C)" : "")
|
140
|
+
logger.warn "caught #{reason} signal; exiting"
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
# If running as root, fix ownership of var and etc dirs
|
146
|
+
def fix_ownership
|
147
|
+
return if Process.uid != 0
|
148
|
+
begin
|
149
|
+
uid = Etc.getpwnam("bixby").uid
|
150
|
+
gid = Etc.getgrnam("bixby").gid
|
151
|
+
# user/group exists, chown
|
152
|
+
File.chown(uid, gid, Bixby.path("var"), Bixby.path("etc"))
|
153
|
+
rescue ArgumentError
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
# Validate ARGV
|
158
|
+
#
|
159
|
+
# If empty, default to "start", otherwise make sure we have a valid option
|
160
|
+
# for daemons.
|
161
|
+
#
|
162
|
+
# @raise [SystemExit] on invalid arg
|
163
|
+
def validate_argv
|
164
|
+
if ARGV.empty? then
|
165
|
+
ARGV << "start"
|
166
|
+
else
|
167
|
+
if not %w{start stop restart zap status}.include? ARGV.first then
|
168
|
+
$stderr.puts "ERROR: invalid command '#{ARGV.first}'"
|
169
|
+
$stderr.puts
|
170
|
+
$stderr.puts @opt_parser.help()
|
171
|
+
exit 1
|
172
|
+
end
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
# Ensure that the var dir exists and is writable
|
177
|
+
#
|
178
|
+
# @raise [SystemExit] on error
|
179
|
+
def ensure_state_dir(daemon_dir)
|
180
|
+
if not File.directory? daemon_dir then
|
181
|
+
begin
|
182
|
+
Dir.mkdir(daemon_dir)
|
183
|
+
rescue Exception => ex
|
184
|
+
$stderr.puts "Failed to create state dir: #{daemon_dir}; message:\n" + ex.message
|
185
|
+
exit 1
|
186
|
+
end
|
187
|
+
end
|
188
|
+
if not File.writable? daemon_dir then
|
189
|
+
$stderr.puts "State dir is not writable: #{daemon_dir}"
|
190
|
+
exit 1
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
# Copied from daemons gem. We hit a bug where closing FDs failed,
|
195
|
+
# probably related to prompting for a password. So close them
|
196
|
+
# all cleanly before daemonizing.
|
197
|
+
def close_fds
|
198
|
+
# don't close stdin/out/err (0/1/2)
|
199
|
+
3.upto(8192).each do |i|
|
200
|
+
begin
|
201
|
+
IO.for_fd(i).close
|
202
|
+
rescue Exception
|
203
|
+
end
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
207
|
+
end # App
|
208
|
+
end # Bixby
|
@@ -0,0 +1,112 @@
|
|
1
|
+
|
2
|
+
require 'mixlib/cli'
|
3
|
+
require 'highline/import'
|
4
|
+
|
5
|
+
module Bixby
|
6
|
+
class App
|
7
|
+
|
8
|
+
module CLI
|
9
|
+
|
10
|
+
include Mixlib::CLI
|
11
|
+
|
12
|
+
def self.included(receiver)
|
13
|
+
receiver.extend(Mixlib::CLI::ClassMethods)
|
14
|
+
receiver.instance_variable_set(:@options, @options)
|
15
|
+
receiver.instance_variable_set(:@banner, @banner)
|
16
|
+
receiver.instance_variable_set(:@opt_parser, @opt_parser)
|
17
|
+
end
|
18
|
+
|
19
|
+
banner <<-EOF
|
20
|
+
Usage: #{$0} <command>
|
21
|
+
|
22
|
+
Run bixby-agent as a background daemon.
|
23
|
+
|
24
|
+
Where <command> is one of:
|
25
|
+
start start the agent
|
26
|
+
stop stop the agent
|
27
|
+
restart stop and start the agent
|
28
|
+
zap reset the PID file
|
29
|
+
status show status (PID) of the agent
|
30
|
+
|
31
|
+
To register with the manager:
|
32
|
+
|
33
|
+
#{$0} --register [URL] -t TENANT -P [PASSWORD] [--tags TAG1,TAG2]
|
34
|
+
|
35
|
+
Options:
|
36
|
+
|
37
|
+
EOF
|
38
|
+
|
39
|
+
# :nocov:
|
40
|
+
|
41
|
+
option :register,
|
42
|
+
:on => :head,
|
43
|
+
:long => "--register [URL]",
|
44
|
+
:description => "Register with the management server (optional URL, default: https://bixby.io)",
|
45
|
+
:proc => Proc.new { |url| url || "https://bixby.io" } # default URL
|
46
|
+
|
47
|
+
option :tenant,
|
48
|
+
:on => :head,
|
49
|
+
:short => "-t TENANT",
|
50
|
+
:long => "--tenant TENANT",
|
51
|
+
:description => "Tenant name"
|
52
|
+
|
53
|
+
option :password,
|
54
|
+
:on => :head,
|
55
|
+
:short => "-P [PASSWORD]",
|
56
|
+
:long => "--password [PASSWORD]",
|
57
|
+
:description => "Agent registration password (prompt if not supplied)",
|
58
|
+
:proc => Proc.new { |c|
|
59
|
+
if c then
|
60
|
+
c
|
61
|
+
else
|
62
|
+
HighLine.new.ask("Enter agent registration password: ") { |q| q.echo = "*" }
|
63
|
+
end
|
64
|
+
}
|
65
|
+
|
66
|
+
option :tags,
|
67
|
+
:on => :head,
|
68
|
+
:long => "--tags TAGS",
|
69
|
+
:description => "Comma separated tags to assign to this host (optional)"
|
70
|
+
|
71
|
+
option :directory,
|
72
|
+
:short => "-d DIRECTORY",
|
73
|
+
:long => "--directory DIRECTORY",
|
74
|
+
:description => "Root directory for Bixby (optional, default: /opt/bixby)"
|
75
|
+
|
76
|
+
option :debug,
|
77
|
+
:on => :tail,
|
78
|
+
:long => "--debug",
|
79
|
+
:description => "Enable debug mode (don't daemonize, extra logging)",
|
80
|
+
:boolean => true,
|
81
|
+
:proc => Proc.new { ENV["BIXBY_LOG"] = "debug" }
|
82
|
+
|
83
|
+
option :help,
|
84
|
+
:on => :tail,
|
85
|
+
:short => "-h",
|
86
|
+
:long => "--help",
|
87
|
+
:description => "Print this help",
|
88
|
+
:boolean => true,
|
89
|
+
:show_options => true,
|
90
|
+
:exit => 0
|
91
|
+
|
92
|
+
option :version,
|
93
|
+
:on => :tail,
|
94
|
+
:short => "-v",
|
95
|
+
:long => "--version",
|
96
|
+
:description => "Show version",
|
97
|
+
:proc => Proc.new { puts "Bixby v" + Bixby::Agent::VERSION },
|
98
|
+
:exit => 0
|
99
|
+
|
100
|
+
# :nocov:
|
101
|
+
|
102
|
+
def initialize
|
103
|
+
super
|
104
|
+
@argv = parse_options()
|
105
|
+
ARGV.clear << @argv
|
106
|
+
ARGV.flatten!
|
107
|
+
end
|
108
|
+
|
109
|
+
end # CLI
|
110
|
+
|
111
|
+
end # App
|
112
|
+
end # Bixby
|