agent_client 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- data/README +1 -0
- data/Rakefile +52 -0
- data/lib/agent_client/base.rb +28 -0
- data/lib/agent_client/errors.rb +9 -0
- data/lib/agent_client/http_client.rb +64 -0
- data/lib/agent_client/version.rb +7 -0
- data/lib/agent_client.rb +25 -0
- data/spec/spec_helper.rb +10 -0
- data/spec/unit/http_agent_client_spec.rb +135 -0
- metadata +83 -0
data/README
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
Agent client for VMware BOSH.
|
data/Rakefile
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
# Copyright (c) 2009-2012 VMware, Inc.
|
2
|
+
|
3
|
+
$:.unshift(File.expand_path("../../rake", __FILE__))
|
4
|
+
|
5
|
+
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __FILE__)
|
6
|
+
|
7
|
+
require "rubygems"
|
8
|
+
require "bundler"
|
9
|
+
Bundler.setup(:default, :test)
|
10
|
+
|
11
|
+
require "rake"
|
12
|
+
require "rake"
|
13
|
+
begin
|
14
|
+
require "rspec/core/rake_task"
|
15
|
+
rescue LoadError
|
16
|
+
end
|
17
|
+
|
18
|
+
require "bundler_task"
|
19
|
+
require "ci_task"
|
20
|
+
|
21
|
+
gem_helper = Bundler::GemHelper.new(Dir.pwd)
|
22
|
+
|
23
|
+
desc "Build BOSH Agent Client gem into the pkg directory"
|
24
|
+
task "build" do
|
25
|
+
gem_helper.build_gem
|
26
|
+
end
|
27
|
+
|
28
|
+
desc "Build and install BOSH Agent Client into system gems"
|
29
|
+
task "install" do
|
30
|
+
Rake::Task["bundler:install"].invoke
|
31
|
+
gem_helper.install_gem
|
32
|
+
end
|
33
|
+
|
34
|
+
BundlerTask.new
|
35
|
+
|
36
|
+
if defined?(RSpec)
|
37
|
+
namespace :spec do
|
38
|
+
desc "Run Unit Tests"
|
39
|
+
rspec_task = RSpec::Core::RakeTask.new(:unit) do |t|
|
40
|
+
t.gemfile = "Gemfile"
|
41
|
+
t.pattern = "spec/unit/**/*_spec.rb"
|
42
|
+
t.rspec_opts = %w(--format progress --colour)
|
43
|
+
end
|
44
|
+
|
45
|
+
CiTask.new do |task|
|
46
|
+
task.rspec_task = rspec_task
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
desc "Install dependencies and run tests"
|
51
|
+
task :spec => %w(bundler:install:test spec:unit)
|
52
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Bosh
|
2
|
+
module Agent
|
3
|
+
class BaseClient
|
4
|
+
|
5
|
+
def run_task(method, *args)
|
6
|
+
task = send(method.to_sym, *args)
|
7
|
+
|
8
|
+
while task["state"] == "running"
|
9
|
+
sleep(1.0)
|
10
|
+
task = get_task(task["agent_task_id"])
|
11
|
+
end
|
12
|
+
|
13
|
+
task
|
14
|
+
end
|
15
|
+
|
16
|
+
def method_missing(method_name, *args)
|
17
|
+
result = handle_method(method_name, args)
|
18
|
+
|
19
|
+
raise HandlerError, result["exception"] if result.has_key?("exception")
|
20
|
+
result["value"]
|
21
|
+
end
|
22
|
+
|
23
|
+
protected
|
24
|
+
def handle_method(method_name, args)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
module Bosh; module Agent; end; end
|
2
|
+
|
3
|
+
require "httpclient"
|
4
|
+
|
5
|
+
module Bosh::Agent
|
6
|
+
class HTTPClient < BaseClient
|
7
|
+
|
8
|
+
IO_TIMEOUT = 86400 * 3
|
9
|
+
CONNECT_TIMEOUT = 30
|
10
|
+
|
11
|
+
def initialize(base_uri, options = {})
|
12
|
+
@base_uri = base_uri
|
13
|
+
@options = options
|
14
|
+
end
|
15
|
+
|
16
|
+
protected
|
17
|
+
|
18
|
+
def handle_method(method, args)
|
19
|
+
payload = {
|
20
|
+
"method" => method, "arguments" => args,
|
21
|
+
"reply_to" => @options["reply_to"] || self.class.name
|
22
|
+
}
|
23
|
+
result = post_json("/agent", Yajl::Encoder.encode(payload))
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def request(method, uri, content_type = nil, payload = nil, headers = {})
|
29
|
+
headers = headers.dup
|
30
|
+
headers["Content-Type"] = content_type if content_type
|
31
|
+
|
32
|
+
http_client = ::HTTPClient.new
|
33
|
+
|
34
|
+
http_client.send_timeout = IO_TIMEOUT
|
35
|
+
http_client.receive_timeout = IO_TIMEOUT
|
36
|
+
http_client.connect_timeout = CONNECT_TIMEOUT
|
37
|
+
|
38
|
+
if @options['user'] && @options['password']
|
39
|
+
http_client.set_auth(@base_uri, @options['user'], @options['password'])
|
40
|
+
end
|
41
|
+
|
42
|
+
http_client.request(method, @base_uri + uri,
|
43
|
+
:body => payload, :header => headers)
|
44
|
+
|
45
|
+
rescue URI::Error, SocketError, Errno::ECONNREFUSED => e
|
46
|
+
raise Error, "cannot access agent (%s)" % [ e.message ]
|
47
|
+
rescue SystemCallError => e
|
48
|
+
raise Error, "System call error while talking to agent: #{e}"
|
49
|
+
rescue ::HTTPClient::BadResponseError => e
|
50
|
+
raise Error, "Received bad HTTP response from agent: #{e}"
|
51
|
+
rescue => e
|
52
|
+
raise Error, "Agent call exception: #{e}"
|
53
|
+
end
|
54
|
+
|
55
|
+
def post_json(url, payload)
|
56
|
+
response = request(:post, url, "application/json", payload)
|
57
|
+
status = response.code
|
58
|
+
raise AuthError, "Authentication failed" if status == 401
|
59
|
+
raise Error, "Agent HTTP #{status}" if status != 200
|
60
|
+
Yajl::Parser.parse(response.body)
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
64
|
+
end
|
data/lib/agent_client.rb
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
module Bosh; module Agent; end; end
|
2
|
+
|
3
|
+
require "agent_client/version"
|
4
|
+
require "agent_client/errors"
|
5
|
+
require "agent_client/base"
|
6
|
+
require "agent_client/http_client"
|
7
|
+
|
8
|
+
require "uri"
|
9
|
+
require "yajl"
|
10
|
+
|
11
|
+
module Bosh
|
12
|
+
module Agent
|
13
|
+
class Client
|
14
|
+
def self.create(uri, options = { })
|
15
|
+
scheme = URI.parse(uri).scheme
|
16
|
+
case scheme
|
17
|
+
when "http"
|
18
|
+
HTTPClient.new(uri, options)
|
19
|
+
else
|
20
|
+
raise "Invalid client scheme, available providers are: 'http'"
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,135 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../spec_helper'
|
2
|
+
|
3
|
+
describe Bosh::Agent::HTTPClient do
|
4
|
+
|
5
|
+
before(:each) do
|
6
|
+
@httpclient = mock("httpclient")
|
7
|
+
HTTPClient.stub!(:new).and_return(@httpclient)
|
8
|
+
end
|
9
|
+
|
10
|
+
describe "options" do
|
11
|
+
|
12
|
+
it "should set up authentication when present" do
|
13
|
+
response = mock("response")
|
14
|
+
response.stub!(:code).and_return(200)
|
15
|
+
response.stub!(:body).and_return('{"value": "pong"}')
|
16
|
+
|
17
|
+
[:send_timeout=, :receive_timeout=, :connect_timeout=].each do |method|
|
18
|
+
@httpclient.should_receive(method)
|
19
|
+
end
|
20
|
+
|
21
|
+
@httpclient.should_receive(:set_auth).with("http://localhost", "john", "smith")
|
22
|
+
@httpclient.should_receive(:request).and_return(response)
|
23
|
+
|
24
|
+
@client = Bosh::Agent::HTTPClient.new("http://localhost",
|
25
|
+
{ "user" => "john",
|
26
|
+
"password" => "smith" })
|
27
|
+
@client.ping
|
28
|
+
end
|
29
|
+
|
30
|
+
it "should encode arguments" do
|
31
|
+
response = mock("response")
|
32
|
+
response.stub!(:code).and_return(200)
|
33
|
+
response.stub!(:body).and_return('{"value": "iam"}')
|
34
|
+
|
35
|
+
[:send_timeout=, :receive_timeout=, :connect_timeout=].each do |method|
|
36
|
+
@httpclient.should_receive(method)
|
37
|
+
end
|
38
|
+
|
39
|
+
headers = { "Content-Type" => "application/json" }
|
40
|
+
payload = '{"method":"shh","arguments":["hunting","wabbits"],"reply_to":"elmer"}'
|
41
|
+
|
42
|
+
@httpclient.should_receive(:request).with(:post, "http://localhost/agent",
|
43
|
+
:body => payload, :header => headers).and_return(response)
|
44
|
+
|
45
|
+
@client = Bosh::Agent::HTTPClient.new("http://localhost", {"reply_to" => "elmer"})
|
46
|
+
|
47
|
+
@client.shh("hunting", "wabbits").should == "iam"
|
48
|
+
end
|
49
|
+
|
50
|
+
it "should receive a message value" do
|
51
|
+
response = mock("response")
|
52
|
+
response.stub!(:code).and_return(200)
|
53
|
+
response.stub!(:body).and_return('{"value": "pong"}')
|
54
|
+
|
55
|
+
[:send_timeout=, :receive_timeout=, :connect_timeout=].each do |method|
|
56
|
+
@httpclient.should_receive(method)
|
57
|
+
end
|
58
|
+
|
59
|
+
headers = { "Content-Type" => "application/json" }
|
60
|
+
payload = '{"method":"ping","arguments":[],"reply_to":"fudd"}'
|
61
|
+
|
62
|
+
@httpclient.should_receive(:request).with(:post, "http://localhost/agent",
|
63
|
+
:body => payload, :header => headers).and_return(response)
|
64
|
+
|
65
|
+
@client = Bosh::Agent::HTTPClient.new("http://localhost", {"reply_to" => "fudd"})
|
66
|
+
|
67
|
+
@client.ping.should == "pong"
|
68
|
+
end
|
69
|
+
|
70
|
+
it "should run_task" do
|
71
|
+
response = mock("response")
|
72
|
+
response.stub!(:code).and_return(200)
|
73
|
+
response.stub!(:body).and_return('{"value": {"state": "running", "agent_task_id": "task_id_foo"}}')
|
74
|
+
|
75
|
+
[:send_timeout=, :receive_timeout=, :connect_timeout=].each do |method|
|
76
|
+
@httpclient.should_receive(method)
|
77
|
+
end
|
78
|
+
|
79
|
+
headers = { "Content-Type" => "application/json" }
|
80
|
+
payload = '{"method":"compile_package","arguments":["id","sha1"],"reply_to":"bugs"}'
|
81
|
+
|
82
|
+
@httpclient.should_receive(:request).with(:post, "http://localhost/agent",
|
83
|
+
:body => payload, :header => headers).and_return(response)
|
84
|
+
|
85
|
+
response2 = mock("response2")
|
86
|
+
response2.stub!(:code).and_return(200)
|
87
|
+
response2.stub!(:body).and_return('{"value": {"state": "done"}')
|
88
|
+
|
89
|
+
payload = '{"method":"get_task","arguments":["task_id_foo"],"reply_to":"bugs"}'
|
90
|
+
|
91
|
+
[:send_timeout=, :receive_timeout=, :connect_timeout=].each do |method|
|
92
|
+
@httpclient.should_receive(method)
|
93
|
+
end
|
94
|
+
|
95
|
+
@httpclient.should_receive(:request).with(:post, "http://localhost/agent",
|
96
|
+
:body => payload, :header => headers).and_return(response2)
|
97
|
+
|
98
|
+
@client = Bosh::Agent::HTTPClient.new("http://localhost", {"reply_to" => "bugs"})
|
99
|
+
|
100
|
+
@client.run_task(:compile_package, "id", "sha1").should == { "state" => "done" }
|
101
|
+
end
|
102
|
+
|
103
|
+
it "should raise handler exception when method is invalid" do
|
104
|
+
response = mock("response")
|
105
|
+
response.stub!(:code).and_return(200)
|
106
|
+
response.stub!(:body).and_return('{"exception": "no such method"}')
|
107
|
+
|
108
|
+
[:send_timeout=, :receive_timeout=, :connect_timeout=].each do |method|
|
109
|
+
@httpclient.should_receive(method)
|
110
|
+
end
|
111
|
+
|
112
|
+
@httpclient.should_receive(:request).and_return(response)
|
113
|
+
|
114
|
+
@client = Bosh::Agent::HTTPClient.new("http://localhost")
|
115
|
+
|
116
|
+
lambda { @client.no_such_method }.should raise_error(Bosh::Agent::HandlerError)
|
117
|
+
|
118
|
+
end
|
119
|
+
|
120
|
+
it "should raise authentication exception when 401 is returned" do
|
121
|
+
response = mock("response")
|
122
|
+
response.stub!(:code).and_return(401)
|
123
|
+
|
124
|
+
[:send_timeout=, :receive_timeout=, :connect_timeout=].each do |method|
|
125
|
+
@httpclient.should_receive(method)
|
126
|
+
end
|
127
|
+
|
128
|
+
@httpclient.should_receive(:request).and_return(response)
|
129
|
+
|
130
|
+
@client = Bosh::Agent::HTTPClient.new("http://localhost")
|
131
|
+
|
132
|
+
lambda { @client.ping }.should raise_error(Bosh::Agent::AuthError)
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
metadata
ADDED
@@ -0,0 +1,83 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: agent_client
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- VMware
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-03-13 00:00:00.000000000Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: httpclient
|
16
|
+
requirement: &18710880 !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: *18710880
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: yajl-ruby
|
27
|
+
requirement: &18710200 !ruby/object:Gem::Requirement
|
28
|
+
none: false
|
29
|
+
requirements:
|
30
|
+
- - ! '>='
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '0'
|
33
|
+
type: :runtime
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: *18710200
|
36
|
+
description: BOSH agent client
|
37
|
+
email: support@vmware.com
|
38
|
+
executables: []
|
39
|
+
extensions: []
|
40
|
+
extra_rdoc_files: []
|
41
|
+
files:
|
42
|
+
- lib/agent_client.rb
|
43
|
+
- lib/agent_client/base.rb
|
44
|
+
- lib/agent_client/errors.rb
|
45
|
+
- lib/agent_client/http_client.rb
|
46
|
+
- lib/agent_client/version.rb
|
47
|
+
- README
|
48
|
+
- Rakefile
|
49
|
+
- spec/spec_helper.rb
|
50
|
+
- spec/unit/http_agent_client_spec.rb
|
51
|
+
homepage: http://www.vmware.com
|
52
|
+
licenses: []
|
53
|
+
post_install_message:
|
54
|
+
rdoc_options: []
|
55
|
+
require_paths:
|
56
|
+
- lib
|
57
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
58
|
+
none: false
|
59
|
+
requirements:
|
60
|
+
- - ! '>='
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
version: '0'
|
63
|
+
segments:
|
64
|
+
- 0
|
65
|
+
hash: 12617920935854716
|
66
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
67
|
+
none: false
|
68
|
+
requirements:
|
69
|
+
- - ! '>='
|
70
|
+
- !ruby/object:Gem::Version
|
71
|
+
version: '0'
|
72
|
+
segments:
|
73
|
+
- 0
|
74
|
+
hash: 12617920935854716
|
75
|
+
requirements: []
|
76
|
+
rubyforge_project:
|
77
|
+
rubygems_version: 1.8.10
|
78
|
+
signing_key:
|
79
|
+
specification_version: 3
|
80
|
+
summary: BOSH agent client
|
81
|
+
test_files:
|
82
|
+
- spec/spec_helper.rb
|
83
|
+
- spec/unit/http_agent_client_spec.rb
|