bixby-agent 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|