bixby-client 0.1.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/.document +5 -0
- data/Gemfile +38 -0
- data/Gemfile.lock +160 -0
- data/LICENSE.txt +20 -0
- data/README.rdoc +19 -0
- data/Rakefile +14 -0
- data/VERSION +1 -0
- data/bin/bixby +0 -0
- data/bixby-client.gemspec +134 -0
- data/lib/bixby-client.rb +47 -0
- data/lib/bixby-client/client.rb +104 -0
- data/lib/bixby-client/command.rb +36 -0
- data/lib/bixby-client/modules.rb +4 -0
- data/lib/bixby-client/modules/inventory.rb +23 -0
- data/lib/bixby-client/modules/metrics.rb +43 -0
- data/lib/bixby-client/modules/repository.rb +63 -0
- data/lib/bixby-client/patch/shellout.rb +16 -0
- data/lib/bixby-client/platform_util.rb +25 -0
- data/lib/bixby-client/script.rb +18 -0
- data/lib/bixby-client/script_util.rb +73 -0
- data/tasks/jeweler.rake +15 -0
- data/tasks/test.rake +16 -0
- data/tasks/yard.rake +2 -0
- data/test/base.rb +16 -0
- data/test/bixby-client/test_command.rb +45 -0
- data/test/bixby-client/test_platform_util.rb +22 -0
- data/test/helper.rb +28 -0
- data/test/support/root_dir/etc/bixby.yml +8 -0
- data/test/support/root_dir/etc/id_rsa +27 -0
- data/test/support/root_dir/etc/server +27 -0
- data/test/support/root_dir/etc/server.pub +9 -0
- data/test/test_bixby-client.rb +21 -0
- metadata +434 -0
@@ -0,0 +1,104 @@
|
|
1
|
+
|
2
|
+
require "curb"
|
3
|
+
require "httpi"
|
4
|
+
require "api-auth"
|
5
|
+
|
6
|
+
module Bixby
|
7
|
+
|
8
|
+
# Implements the Bixby client API
|
9
|
+
class Client
|
10
|
+
|
11
|
+
# Create a new Client
|
12
|
+
#
|
13
|
+
# @param [String] access_key
|
14
|
+
# @param [String] secret_key
|
15
|
+
def initialize(access_key, secret_key)
|
16
|
+
@access_key = access_key
|
17
|
+
@secret_key = secret_key
|
18
|
+
end
|
19
|
+
|
20
|
+
# Execute the given API request on the manager
|
21
|
+
#
|
22
|
+
# @param [String] operation Name of operation
|
23
|
+
# @param [Array] params Array of parameters; must ve valid JSON types
|
24
|
+
#
|
25
|
+
# @return [JsonResponse]
|
26
|
+
def exec(op, params)
|
27
|
+
exec_api(JsonRequest.new(op, params))
|
28
|
+
end
|
29
|
+
|
30
|
+
# Execute the given API download request
|
31
|
+
#
|
32
|
+
# @param [String] download_path Absolute filename to download requested file to
|
33
|
+
# @param [String] operation Name of operation
|
34
|
+
# @param [Array] params Array of parameters; must ve valid JSON types
|
35
|
+
#
|
36
|
+
# @return [JsonResponse]
|
37
|
+
def exec_download(download_path, op, params)
|
38
|
+
exec_api_download(JsonRequest.new(op, params), download_path)
|
39
|
+
end
|
40
|
+
|
41
|
+
# Execute the given API request on the manager
|
42
|
+
#
|
43
|
+
# @param [JsonRequest] json_req
|
44
|
+
# @return [JsonResponse]
|
45
|
+
def exec_api(json_req)
|
46
|
+
begin
|
47
|
+
req = sign_request(json_req)
|
48
|
+
res = HTTPI.post(req).body
|
49
|
+
return JsonResponse.from_json(res)
|
50
|
+
rescue Curl::Err::CurlError => ex
|
51
|
+
return JsonResponse.new("fail", ex.message)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
# Execute the given API download request
|
56
|
+
#
|
57
|
+
# @param [JsonRequest] json_req Request to download a file
|
58
|
+
# @param [String] download_path Absolute filename to download requested file to
|
59
|
+
# @return [JsonResponse]
|
60
|
+
def exec_api_download(json_req, download_path)
|
61
|
+
begin
|
62
|
+
req = sign_request(json_req)
|
63
|
+
File.open(download_path, "w") do |io|
|
64
|
+
req.on_body { |d| io << d; d.length }
|
65
|
+
HTTPI.post(req)
|
66
|
+
end
|
67
|
+
return JsonResponse.new("success")
|
68
|
+
rescue Curl::Err::CurlError => ex
|
69
|
+
return JsonResponse.new("fail", ex.message)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
|
74
|
+
private
|
75
|
+
|
76
|
+
# Create a signed request
|
77
|
+
#
|
78
|
+
# @param [JsonRequest] json_req
|
79
|
+
#
|
80
|
+
# @return [HTTPI::Request]
|
81
|
+
def sign_request(json_req)
|
82
|
+
post = json_req.to_json
|
83
|
+
req = HTTPI::Request.new(:url => api_uri, :body => post)
|
84
|
+
req.headers["Content-Type"] = "application/json"
|
85
|
+
|
86
|
+
if crypto_enabled? and not @secret_key.nil? then
|
87
|
+
ApiAuth.sign!(req, @access_key, @secret_key)
|
88
|
+
end
|
89
|
+
|
90
|
+
return req
|
91
|
+
end
|
92
|
+
|
93
|
+
def api_uri
|
94
|
+
URI.join(Bixby.manager_uri, "/api").to_s
|
95
|
+
end
|
96
|
+
|
97
|
+
def crypto_enabled?
|
98
|
+
b = ENV["BIXBY_NOCRYPTO"]
|
99
|
+
!(b and %w{1 true yes}.include? b)
|
100
|
+
end
|
101
|
+
|
102
|
+
end # Client
|
103
|
+
|
104
|
+
end # Bixby
|
@@ -0,0 +1,36 @@
|
|
1
|
+
|
2
|
+
require "digest"
|
3
|
+
require "fileutils"
|
4
|
+
|
5
|
+
require "mixlib/shellout"
|
6
|
+
|
7
|
+
module Bixby
|
8
|
+
class Command
|
9
|
+
|
10
|
+
include Bixby::Log
|
11
|
+
include Bixby::PlatformUtil
|
12
|
+
include Bixby::ScriptUtil
|
13
|
+
|
14
|
+
def initialize()
|
15
|
+
end
|
16
|
+
|
17
|
+
# retrieve all loaded subclasses of this classs
|
18
|
+
#
|
19
|
+
# @return [Array<Class>] List of subclasses
|
20
|
+
def self.subclasses
|
21
|
+
@subclasses
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def self.inherited(subclass)
|
27
|
+
if superclass.respond_to? :inherited
|
28
|
+
superclass.inherited(subclass)
|
29
|
+
end
|
30
|
+
@subclasses ||= []
|
31
|
+
@subclasses << subclass
|
32
|
+
end
|
33
|
+
|
34
|
+
end # Command
|
35
|
+
end # Bixby
|
36
|
+
|
@@ -0,0 +1,23 @@
|
|
1
|
+
|
2
|
+
module Bixby
|
3
|
+
module Inventory
|
4
|
+
|
5
|
+
# Register an agent with the manager
|
6
|
+
#
|
7
|
+
# @param [String] uuid
|
8
|
+
# @param [String] public_key
|
9
|
+
# @param [String] hostname
|
10
|
+
# @param [FixNum] port
|
11
|
+
# @param [String] tenant Name of the tenant
|
12
|
+
# @param [String] password Password for registering an Agent with the server
|
13
|
+
#
|
14
|
+
# @return [JsonResponse]
|
15
|
+
def self.register_agent(uuid, public_key, hostname, port, tenant, password)
|
16
|
+
# TODO these params should probably be a keyed hash instead
|
17
|
+
params = [ uuid, public_key, hostname, port, tenant, password ]
|
18
|
+
req = JsonRequest.new("inventory:register_agent", params)
|
19
|
+
return Bixby::Client.new(nil, nil).exec_api(req)
|
20
|
+
end
|
21
|
+
|
22
|
+
end # Inventory
|
23
|
+
end # Bixby
|
@@ -0,0 +1,43 @@
|
|
1
|
+
|
2
|
+
module Bixby
|
3
|
+
module Metrics
|
4
|
+
|
5
|
+
# Store the results of one or more Checks. Each result may contain multiple metrics.
|
6
|
+
#
|
7
|
+
# Fires the :put_check_result hook on completion, passing results as the only param.
|
8
|
+
#
|
9
|
+
# @param [Array<Hash>] results An array of results from one or more checks
|
10
|
+
# @option results [Fixnum] :check_id
|
11
|
+
# @option results [String] :key base key name
|
12
|
+
# @option results [String] :status OK, WARNING, CRITICAL, UNKNOWN, TIMEOUT
|
13
|
+
# @option results [Fixnum] :timestamp
|
14
|
+
# @option results [Array] :metrics
|
15
|
+
# * [Hash] :metrics key/value pairs
|
16
|
+
# * [Hash] :metadata key/value pairs
|
17
|
+
# @option results [Array<String>] :errors list of errors, if any
|
18
|
+
#
|
19
|
+
# @return [void]
|
20
|
+
#
|
21
|
+
# Example input:
|
22
|
+
#
|
23
|
+
# {
|
24
|
+
# "status" => "OK",
|
25
|
+
# "timestamp" => 1329775841,
|
26
|
+
# "key" => "hardware.storage.disk",
|
27
|
+
# "check_id" => "77",
|
28
|
+
# "metrics" => [
|
29
|
+
# {
|
30
|
+
# "metrics" => { "size"=>297, "used"=>202, "free"=>94, "usage"=>69 },
|
31
|
+
# "metadata" => { "mount"=>"/", "type"=>"hfs" }
|
32
|
+
# }
|
33
|
+
# ],
|
34
|
+
# "errors"=>[]
|
35
|
+
# }
|
36
|
+
#
|
37
|
+
def self.put_check_result(results)
|
38
|
+
req = JsonRequest.new("metrics:put_check_result", [ results ])
|
39
|
+
return Bixby.client.exec_api(req)
|
40
|
+
end
|
41
|
+
|
42
|
+
end # Metrics
|
43
|
+
end # Bixby
|
@@ -0,0 +1,63 @@
|
|
1
|
+
|
2
|
+
module Bixby
|
3
|
+
module Repository
|
4
|
+
|
5
|
+
# Retrieve a file listing for the given Bundle
|
6
|
+
#
|
7
|
+
# @param [CommandSpec] cmd CommandSpec representing the Bundle to list
|
8
|
+
#
|
9
|
+
# @return [Array<Hash>]
|
10
|
+
# * file [String] Relative path of file
|
11
|
+
# * digest [String] SHA256 digest of file
|
12
|
+
def self.list_files(cmd)
|
13
|
+
req = JsonRequest.new("provisioning:list_files", cmd.to_hash)
|
14
|
+
res = Bixby.client.exec_api(req)
|
15
|
+
return res.data
|
16
|
+
end
|
17
|
+
|
18
|
+
# Download athe given list of files
|
19
|
+
#
|
20
|
+
# @param [CommandSpec] cmd CommandSpec representing the Bundle to which the files belong
|
21
|
+
# @param [Hash] files Hash, returned from #list_files
|
22
|
+
def self.download_files(cmd, files)
|
23
|
+
return if files.nil? or files.empty?
|
24
|
+
|
25
|
+
local_path = cmd.bundle_dir
|
26
|
+
digest = cmd.load_digest
|
27
|
+
files.each do |f|
|
28
|
+
|
29
|
+
fetch = true
|
30
|
+
if not digest then
|
31
|
+
fetch = true
|
32
|
+
elsif df = digest["files"].find{ |h| h["file"] == f["file"] } then
|
33
|
+
# compare digest w/ stored one if we have it
|
34
|
+
fetch = (df["digest"] != f["digest"])
|
35
|
+
else
|
36
|
+
fetch = true
|
37
|
+
end
|
38
|
+
|
39
|
+
next if not fetch
|
40
|
+
|
41
|
+
params = cmd.to_hash
|
42
|
+
params.delete(:digest)
|
43
|
+
|
44
|
+
filename = File.join(local_path, f['file'])
|
45
|
+
path = File.dirname(filename)
|
46
|
+
if not File.exist? path then
|
47
|
+
FileUtils.mkdir_p(path)
|
48
|
+
end
|
49
|
+
|
50
|
+
req = JsonRequest.new("provisioning:fetch_file", [ params, f['file'] ])
|
51
|
+
Bixby.client.exec_api_download(req, filename)
|
52
|
+
if f['file'] =~ /^bin/ then
|
53
|
+
# correct permissions for executables
|
54
|
+
FileUtils.chmod(0755, filename)
|
55
|
+
end
|
56
|
+
end # files.each
|
57
|
+
|
58
|
+
cmd.update_digest
|
59
|
+
end
|
60
|
+
|
61
|
+
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
|
2
|
+
module Bixby
|
3
|
+
module PlatformUtil
|
4
|
+
|
5
|
+
def uname
|
6
|
+
RUBY_PLATFORM
|
7
|
+
end
|
8
|
+
|
9
|
+
def osx?
|
10
|
+
uname =~ /darwin/
|
11
|
+
end
|
12
|
+
alias :darwin? :osx?
|
13
|
+
alias :mac? :osx?
|
14
|
+
|
15
|
+
def linux?
|
16
|
+
uname =~ /linux/
|
17
|
+
end
|
18
|
+
|
19
|
+
def win?
|
20
|
+
|
21
|
+
end
|
22
|
+
alias :windows? :win?
|
23
|
+
|
24
|
+
end # PlatformUtil
|
25
|
+
end # Bixby
|
@@ -0,0 +1,18 @@
|
|
1
|
+
|
2
|
+
require "bixby-client"
|
3
|
+
require "fileutils"
|
4
|
+
require "mixlib/shellout"
|
5
|
+
|
6
|
+
Bixby::Log.setup_logger()
|
7
|
+
|
8
|
+
class Object
|
9
|
+
include Bixby::PlatformUtil
|
10
|
+
include Bixby::ScriptUtil
|
11
|
+
include Bixby::Log
|
12
|
+
|
13
|
+
# override to create logger based on script name
|
14
|
+
def log
|
15
|
+
@log ||= Logging.logger[File.basename($0)]
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
|
2
|
+
require "bixby-client/patch/shellout"
|
3
|
+
|
4
|
+
module Bixby
|
5
|
+
|
6
|
+
module ScriptUtil
|
7
|
+
|
8
|
+
module UseBundle
|
9
|
+
|
10
|
+
# Load the libraries for the given bundle. Searches all available
|
11
|
+
# repositories.
|
12
|
+
def use_bundle(name)
|
13
|
+
repos = Dir.glob(File.join(Bixby.repo_path, "*"))
|
14
|
+
repos.each do |repo|
|
15
|
+
next if not File.directory? repo
|
16
|
+
|
17
|
+
lib = File.join(repo, name, "lib")
|
18
|
+
$: << lib
|
19
|
+
if File.directory? lib then
|
20
|
+
Dir.glob(File.join(lib, "*.rb")).each{ |f| require f }
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
include UseBundle
|
27
|
+
|
28
|
+
# Reads JSON data from STDIN
|
29
|
+
#
|
30
|
+
# @return [Object] data found on STDIN (can be Hash, Array, String, etc)
|
31
|
+
def get_json_input
|
32
|
+
input = read_stdin()
|
33
|
+
input.strip! if input
|
34
|
+
(input.nil? or input.empty?) ? {} : MultiJson.load(input)
|
35
|
+
end
|
36
|
+
|
37
|
+
# Read all available data on STDIN without blocking
|
38
|
+
# (i.e., if no data is available, none will be returned)
|
39
|
+
#
|
40
|
+
# @return [String] data
|
41
|
+
def read_stdin
|
42
|
+
buff = []
|
43
|
+
while true do
|
44
|
+
begin
|
45
|
+
buff << STDIN.read_nonblock(64000)
|
46
|
+
rescue
|
47
|
+
break
|
48
|
+
end
|
49
|
+
end
|
50
|
+
return buff.join('')
|
51
|
+
end
|
52
|
+
|
53
|
+
# Simple wrapper around Mixlib::ShellOut
|
54
|
+
#
|
55
|
+
# @param [Array] args
|
56
|
+
#
|
57
|
+
# @return [Mixlib::ShellOut]
|
58
|
+
def systemu(*args)
|
59
|
+
cmd = Mixlib::ShellOut.new(*args)
|
60
|
+
cmd.run_command
|
61
|
+
cmd
|
62
|
+
end
|
63
|
+
|
64
|
+
end # ScriptUtil
|
65
|
+
end # Bixby
|
66
|
+
|
67
|
+
module Bixby
|
68
|
+
extend Bixby::ScriptUtil::UseBundle
|
69
|
+
end
|
70
|
+
|
71
|
+
class Object
|
72
|
+
include Bixby::ScriptUtil::UseBundle
|
73
|
+
end
|
data/tasks/jeweler.rake
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
|
2
|
+
require 'jeweler'
|
3
|
+
|
4
|
+
Jeweler::Tasks.new do |gemspec|
|
5
|
+
gemspec.name = "bixby-client"
|
6
|
+
gemspec.summary = "Bixby Client"
|
7
|
+
gemspec.description = "Bixby Client"
|
8
|
+
gemspec.email = "chetan@pixelcop.net"
|
9
|
+
gemspec.homepage = "http://github.com/chetan/bixby-client"
|
10
|
+
gemspec.authors = ["Chetan Sarva"]
|
11
|
+
|
12
|
+
gemspec.executables = %w{ bixby }
|
13
|
+
|
14
|
+
end
|
15
|
+
Jeweler::RubygemsDotOrgTasks.new
|
data/tasks/test.rake
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
|
2
|
+
require 'rake/testtask'
|
3
|
+
Rake::TestTask.new(:test) do |test|
|
4
|
+
test.libs << 'lib' << 'test'
|
5
|
+
test.pattern = 'test/**/test_*.rb'
|
6
|
+
end
|
7
|
+
|
8
|
+
task :default => :test
|
9
|
+
|
10
|
+
begin
|
11
|
+
require 'single_test'
|
12
|
+
SingleTest.load_tasks
|
13
|
+
|
14
|
+
rescue LoadError
|
15
|
+
warn "single_test not available"
|
16
|
+
end
|