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