hula 0.7.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,69 @@
1
+ # Copyright (c) 2014-2015 Pivotal Software, Inc.
2
+ # All rights reserved.
3
+ # THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
4
+ # INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
5
+ # PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
6
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
7
+ # TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
8
+ # USE OR OTHER DEALINGS IN THE SOFTWARE.
9
+ #
10
+
11
+ require 'hula/service_broker/api'
12
+
13
+ require 'forwardable'
14
+
15
+ module Hula
16
+ module ServiceBroker
17
+ class Client
18
+
19
+ extend Forwardable
20
+ def_delegators :api,
21
+ :catalog,
22
+ :debug,
23
+ :deprovision_instance,
24
+ :bind_instance,
25
+ :unbind_instance,
26
+ :url
27
+
28
+ def initialize(args = {})
29
+ api_args = args.reject { |k, _v| k == :api }
30
+ @api = args.fetch(:api) { Api.new(api_args) }
31
+ end
32
+
33
+ def provision_and_bind(service_name, plan_name, &block)
34
+ raise Error, 'no block given' unless block_given?
35
+ provision_instance(service_name, plan_name) do |service_instance|
36
+ bind_instance(service_instance, &block)
37
+ end
38
+ end
39
+
40
+ def provision_instance(service_name, plan_name, &block)
41
+ plan = catalog.service_plan(service_name, plan_name)
42
+ service_instance = api.provision_instance(plan)
43
+ return service_instance unless block
44
+
45
+ begin
46
+ block.call(service_instance)
47
+ ensure
48
+ api.deprovision_instance(service_instance)
49
+ end
50
+ end
51
+
52
+ def bind_instance(service_instance, &block)
53
+ binding = api.bind_instance(service_instance)
54
+ return binding unless block
55
+
56
+ begin
57
+ block.call(binding, service_instance)
58
+ ensure
59
+ api.unbind_instance(binding)
60
+ sleep 1
61
+ end
62
+ end
63
+
64
+ private
65
+
66
+ attr_reader :api
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,24 @@
1
+ # Copyright (c) 2014-2015 Pivotal Software, Inc.
2
+ # All rights reserved.
3
+ # THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
4
+ # INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
5
+ # PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
6
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
7
+ # TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
8
+ # USE OR OTHER DEALINGS IN THE SOFTWARE.
9
+ #
10
+
11
+ module Hula
12
+ module ServiceBroker
13
+ class Error < StandardError; end
14
+
15
+ class NotInCatalog < Error; end
16
+ class PlanNotFoundError < NotInCatalog; end
17
+ class ServiceNotFoundError < NotInCatalog; end
18
+
19
+ class JsonParseError < Error; end
20
+ class TimeoutError < Error; end
21
+ class HTTPError < Error; end
22
+
23
+ end
24
+ end
@@ -0,0 +1,90 @@
1
+ # Copyright (c) 2014-2015 Pivotal Software, Inc.
2
+ # All rights reserved.
3
+ # THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
4
+ # INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
5
+ # PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
6
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
7
+ # TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
8
+ # USE OR OTHER DEALINGS IN THE SOFTWARE.
9
+ #
10
+
11
+ require 'net/http'
12
+
13
+ require 'hula/service_broker/errors'
14
+
15
+ module Hula
16
+ module ServiceBroker
17
+
18
+ class HttpProxyNull
19
+ def http_host
20
+ nil
21
+ end
22
+ def http_port
23
+ nil
24
+ end
25
+ end
26
+
27
+ class HttpJsonClient
28
+ def initialize(http_proxy: HttpProxyNull.new)
29
+ @http_proxy = http_proxy
30
+ end
31
+
32
+ def get(uri, auth: nil)
33
+ request = Net::HTTP::Get.new(uri)
34
+ request.basic_auth auth.fetch(:username), auth.fetch(:password) unless auth.nil?
35
+ send_request(request)
36
+ end
37
+
38
+ def put(uri, body: nil, auth: nil)
39
+ request = Net::HTTP::Put.new(uri)
40
+ request.body = JSON.generate(body) if body
41
+ request.basic_auth auth.fetch(:username), auth.fetch(:password) unless auth.nil?
42
+ send_request(request)
43
+ end
44
+
45
+ def delete(uri, auth: nil)
46
+ request = Net::HTTP::Delete.new(uri)
47
+ request.basic_auth auth.fetch(:username), auth.fetch(:password) unless auth.nil?
48
+ send_request(request)
49
+ end
50
+
51
+ private
52
+
53
+ attr_reader :http_proxy
54
+
55
+ def send_request(request)
56
+ uri = request.uri
57
+ make_request(uri.hostname, uri.port, uri.scheme, request)
58
+ end
59
+
60
+ def make_request(host, port, scheme, request)
61
+ response = Net::HTTP.start(
62
+ host,
63
+ port,
64
+ http_proxy.http_host,
65
+ http_proxy.http_port,
66
+ use_ssl: scheme == 'https',
67
+ verify_mode: OpenSSL::SSL::VERIFY_NONE
68
+ ) { |http| http.request(request) }
69
+
70
+ handle(response)
71
+ rescue Timeout::Error => e
72
+ raise TimeoutError, e
73
+ end
74
+
75
+ def handle(response)
76
+ unless response.is_a?(Net::HTTPSuccess)
77
+ fail HTTPError, [
78
+ response.uri.to_s,
79
+ response.code,
80
+ response.body
81
+ ].join("\n\n")
82
+ end
83
+
84
+ JSON.parse(response.body, symbolize_names: true)
85
+ rescue JSON::ParserError => e
86
+ raise JsonParseError, e
87
+ end
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,30 @@
1
+ # Copyright (c) 2014-2015 Pivotal Software, Inc.
2
+ # All rights reserved.
3
+ # THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
4
+ # INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
5
+ # PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
6
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
7
+ # TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
8
+ # USE OR OTHER DEALINGS IN THE SOFTWARE.
9
+ #
10
+
11
+ module Hula
12
+ module ServiceBroker
13
+ class InstanceBinding
14
+ attr_reader :id, :credentials, :service_instance
15
+
16
+ def initialize(id:, credentials:, service_instance:)
17
+ @id = id
18
+ @credentials = credentials
19
+ @service_instance = service_instance
20
+ end
21
+
22
+ def ==(other)
23
+ is_a?(other.class) &&
24
+ id == other.id &&
25
+ credentials == other.credentials &&
26
+ service_instance == other.service_instance
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,32 @@
1
+ # Copyright (c) 2014-2015 Pivotal Software, Inc.
2
+ # All rights reserved.
3
+ # THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
4
+ # INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
5
+ # PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
6
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
7
+ # TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
8
+ # USE OR OTHER DEALINGS IN THE SOFTWARE.
9
+ #
10
+
11
+ module Hula
12
+ module ServiceBroker
13
+ class Plan
14
+ def initialize(args = {})
15
+ @id = args.fetch(:id)
16
+ @name = args.fetch(:name)
17
+ @description = args.fetch(:description)
18
+ @service_id = args.fetch(:service_id)
19
+ end
20
+
21
+ attr_reader :id, :name, :description, :service_id
22
+
23
+ def ==(other)
24
+ is_a?(other.class) &&
25
+ id == other.id &&
26
+ name == other.name &&
27
+ description == other.description &&
28
+ service_id == other.service_id
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,47 @@
1
+ # Copyright (c) 2014-2015 Pivotal Software, Inc.
2
+ # All rights reserved.
3
+ # THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
4
+ # INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
5
+ # PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
6
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
7
+ # TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
8
+ # USE OR OTHER DEALINGS IN THE SOFTWARE.
9
+ #
10
+
11
+ require 'hula/service_broker/errors'
12
+ require 'hula/service_broker/plan'
13
+
14
+ module Hula
15
+ module ServiceBroker
16
+ class Service
17
+ def initialize(args = {})
18
+ @id = args.fetch(:id)
19
+ @name = args.fetch(:name)
20
+ @description = args.fetch(:description)
21
+ @bindable = !!args.fetch(:bindable)
22
+ @plans = args.fetch(:plans).map { |p| Plan.new(p.merge(service_id: self.id)) }
23
+ end
24
+
25
+ attr_reader :id, :name, :description, :bindable, :plans
26
+
27
+ def ==(other)
28
+ is_a?(other.class) &&
29
+ id == other.id &&
30
+ name == other.name &&
31
+ description == other.description &&
32
+ bindable == other.bindable &&
33
+ plans == other.plans
34
+ end
35
+
36
+ def plan(plan_name)
37
+ plans.find { |p| p.name == plan_name } or
38
+ fail(PlanNotFoundError, [
39
+ %{Unknown plan with name: #{plan_name.inspect}},
40
+ " Known plan names are: #{plans.map(&:name).inspect}"
41
+ ].join("\n")
42
+ )
43
+ end
44
+
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,24 @@
1
+ # Copyright (c) 2014-2015 Pivotal Software, Inc.
2
+ # All rights reserved.
3
+ # THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
4
+ # INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
5
+ # PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
6
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
7
+ # TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
8
+ # USE OR OTHER DEALINGS IN THE SOFTWARE.
9
+ #
10
+
11
+ module Hula
12
+ module ServiceBroker
13
+ class ServiceInstance
14
+ def initialize(id:)
15
+ @id = id
16
+ end
17
+ attr_reader :id
18
+
19
+ def ==(other)
20
+ is_a?(other.class) && id == other.id
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,118 @@
1
+ # Copyright (c) 2014-2015 Pivotal Software, Inc.
2
+ # All rights reserved.
3
+ # THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
4
+ # INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
5
+ # PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
6
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
7
+ # TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
8
+ # USE OR OTHER DEALINGS IN THE SOFTWARE.
9
+ #
10
+
11
+ require 'hula/helpers/socket_tools'
12
+ require 'pty'
13
+ require 'expect'
14
+
15
+ module Hula
16
+ class Socks4ProxySsh
17
+ class FailedToStart < StandardError; end
18
+ class DieSignal < StandardError; end
19
+
20
+ include Helpers::SocketTools
21
+
22
+ def initialize(
23
+ ssh_host:,
24
+ ssh_username:,
25
+ ssh_password:,
26
+ socks_host: 'localhost',
27
+ socks_port: free_port,
28
+ ssh_bin: 'ssh',
29
+ retry_count: 10
30
+ )
31
+ @ssh_bin = String(ssh_bin)
32
+ @ssh_host = String(ssh_host)
33
+ @ssh_username = String(ssh_username)
34
+ @ssh_password = String(ssh_password)
35
+ @socks_port = String(socks_port)
36
+ @socks_host = String(socks_host)
37
+ @retry_count = retry_count
38
+ end
39
+
40
+ attr_reader :socks_port, :socks_host
41
+
42
+ def stop
43
+ return unless @thread
44
+ @thread.raise(DieSignal)
45
+ @thread.join
46
+ @thread = nil
47
+ end
48
+
49
+ def start
50
+ @thread ||= begin
51
+ thread = start_ssh_socks_thread
52
+ wait_for_port(:host => socks_host, :port => socks_port, timeout_seconds: 30)
53
+ thread
54
+ end
55
+ end
56
+
57
+ private
58
+
59
+ attr_reader :ssh_host, :ssh_username, :ssh_password, :ssh_bin
60
+
61
+ def start_ssh_socks_thread
62
+ # We need to shell out to ssh as this has a SOCKS proxy facility
63
+ # However, there's a problem. SSH does not provide an argument for passing in a password
64
+ # We don't have keys available so we need to use expect to feed the password into the prompt
65
+ Thread.new(
66
+ ssh_command,
67
+ ssh_password
68
+ ) do |ssh_command, ssh_password|
69
+ tries_remaining = @retry_count
70
+
71
+ while true
72
+ puts "--- SSH Gateway attempt #{@retry_count + 1 - tries_remaining}"
73
+ sleep 1
74
+
75
+ begin
76
+ ssh_out, ssh_in, pid = PTY.spawn(*ssh_command)
77
+ # On BOSH lite a password is used, however on aws an identity is used
78
+ # The below statment will wait on a aws run and the pid is never returned
79
+ ssh_out.expect(/[Pp]assword\:/) { |r| ssh_in.print("#{ssh_password}\n") }
80
+ Process.wait(pid)
81
+
82
+ tries_remaining -= 1
83
+ if tries_remaining < 0
84
+ raise FailedToStart, "SSH finished early - SSH Socks Proxy could not be setup or failed"
85
+ end
86
+ rescue Errno::EIO
87
+ tries_remaining -= 1
88
+ # Can't read or write to dead process
89
+
90
+ if tries_remaining < 0
91
+ raise FailedToStart, "SSH finished early EIO - SSH Socks Proxy could not be setup or failed"
92
+ end
93
+ rescue DieSignal
94
+ Process.kill('KILL', pid) rescue Errno::ESRCH
95
+ Process.wait(pid) rescue Errno::ECHILD
96
+ break
97
+ end
98
+ end
99
+ end.tap do |thread|
100
+ thread.abort_on_exception = true
101
+ end
102
+ end
103
+
104
+ def ssh_command
105
+ [
106
+ ssh_bin,
107
+ '-D', "#{socks_host}:#{socks_port}",
108
+ '-N',
109
+ "#{ssh_username}@#{ssh_host}",
110
+ '-o', 'UserKnownHostsFile=/dev/null',
111
+ '-o', 'StrictHostKeyChecking=no',
112
+ '-o', 'NumberOfPasswordPrompts=1',
113
+ '-o', 'StrictHostKeyChecking=no'
114
+ ]
115
+ end
116
+
117
+ end
118
+ end
@@ -0,0 +1,13 @@
1
+ # Copyright (c) 2014-2015 Pivotal Software, Inc.
2
+ # All rights reserved.
3
+ # THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
4
+ # INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
5
+ # PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
6
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
7
+ # TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
8
+ # USE OR OTHER DEALINGS IN THE SOFTWARE.
9
+ #
10
+
11
+ module Hula
12
+ VERSION = '0.7.1'
13
+ end
data/lib/hula.rb ADDED
@@ -0,0 +1,16 @@
1
+ # Copyright (c) 2014-2015 Pivotal Software, Inc.
2
+ # All rights reserved.
3
+ # THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
4
+ # INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
5
+ # PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
6
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
7
+ # TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
8
+ # USE OR OTHER DEALINGS IN THE SOFTWARE.
9
+ #
10
+
11
+ require 'hula/version'
12
+ require 'hula/bosh_director'
13
+ require 'hula/cloud_foundry'
14
+
15
+ module Hula
16
+ end