bosh_cpi 1.2539.0 → 1.2546.0

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.
@@ -7,6 +7,7 @@ require "forwardable"
7
7
  require "cloud/config"
8
8
  require "cloud/errors"
9
9
  require "cloud/provider"
10
+ require "cloud/external_cpi"
10
11
 
11
12
  module Bosh
12
13
 
@@ -0,0 +1,140 @@
1
+ require 'membrane'
2
+ require 'open3'
3
+
4
+ module Bosh::Clouds
5
+ class ExternalCpi
6
+ ##
7
+ # Raised when the external CPI executable returns an error unknown to director
8
+ #
9
+ class UnknownError < StandardError; end
10
+
11
+ ##
12
+ # Raised when the external CPI executable returns nil or invalid JSON format to director
13
+ class InvalidResponse < StandardError; end
14
+
15
+ ##
16
+ # Raised when the external CPI bin/cpi is not executable
17
+ class NonExecutable < StandardError; end
18
+
19
+ KNOWN_RPC_ERRORS = %w(
20
+ Bosh::Clouds::VMCreationFailed
21
+ Bosh::Clouds::DiskNotFound
22
+ Bosh::Clouds::DiskNotAttached
23
+ Bosh::Clouds::NoDiskSpace
24
+ Bosh::Clouds::CloudError
25
+ Bosh::Clouds::CpiError
26
+ ).freeze
27
+
28
+ KNOWN_RPC_METHODS = %w(
29
+ current_vm_id
30
+ create_stemcell
31
+ delete_stemcell
32
+ create_vm
33
+ delete_vm
34
+ has_vm?
35
+ reboot_vm
36
+ set_vm_metadata
37
+ configure_networks
38
+ create_disk
39
+ delete_disk
40
+ attach_disk
41
+ detach_disk
42
+ snapshot_disk
43
+ delete_snapshot
44
+ get_disks
45
+ ping
46
+ ).freeze
47
+
48
+ RESPONSE_SCHEMA = Membrane::SchemaParser.parse do
49
+ {
50
+ 'result' => any,
51
+ 'error' => enum(nil,
52
+ { 'type' => String,
53
+ 'message' => String,
54
+ 'ok_to_retry' => bool
55
+ }
56
+ )
57
+ }
58
+ end
59
+
60
+ def initialize(cpi_path, director_uuid)
61
+ @cpi_path = cpi_path
62
+ @director_uuid = director_uuid
63
+ @logger = Config.logger
64
+ end
65
+
66
+ KNOWN_RPC_METHODS.each do |method_name|
67
+ define_method method_name do |*arguments|
68
+ request = JSON.dump({
69
+ 'method' => method_name.gsub(/\?$/,''),
70
+ 'arguments' => arguments,
71
+ 'context' => {
72
+ 'director_uuid' => @director_uuid
73
+ }
74
+ })
75
+
76
+ env = {'PATH' => '/usr/sbin:/usr/bin:/sbin:/bin', 'TMPDIR' => ENV['TMPDIR']}
77
+ cpi_exec_path = checked_cpi_exec_path
78
+
79
+ @logger.debug("External CPI sending request: #{request} with command: #{cpi_exec_path}")
80
+ cpi_response, stderr, exit_status = Open3.capture3(env, cpi_exec_path, stdin_data: request)
81
+ @logger.debug("External CPI got response: #{cpi_response}, err: #{stderr}, exit_status: #{exit_status}")
82
+
83
+ parsed_response = parsed_response(cpi_response)
84
+ validate_response(parsed_response)
85
+
86
+ if parsed_response['error']
87
+ handle_error(parsed_response['error'])
88
+ end
89
+
90
+ parsed_response['result']
91
+ end
92
+ end
93
+
94
+ private
95
+
96
+ def checked_cpi_exec_path
97
+ unless File.executable?(@cpi_path)
98
+ raise NonExecutable, "Failed to run cpi: `#{@cpi_path}' is not executable"
99
+ end
100
+ @cpi_path
101
+ end
102
+
103
+ def handle_error(error_response)
104
+ error_type = error_response['type']
105
+ error_message = error_response['message']
106
+ unless KNOWN_RPC_ERRORS.include?(error_type)
107
+ raise UnknownError, "Received unknown error from cpi: #{error_type} with message #{error_message}"
108
+ end
109
+
110
+ error_class = constantize(error_type)
111
+
112
+ if error_class <= RetriableCloudError
113
+ error = error_class.new(error_response['ok_to_retry'])
114
+ else
115
+ error = error_class.new(error_message)
116
+ end
117
+
118
+ raise error, error_message
119
+ end
120
+
121
+ def parsed_response(input)
122
+ begin
123
+ JSON.load(input)
124
+ rescue JSON::ParserError => e
125
+ raise InvalidResponse, "Received invalid response from cpi with error #{e.message}"
126
+ end
127
+ end
128
+
129
+ def validate_response(response)
130
+ RESPONSE_SCHEMA.validate(response)
131
+ rescue Membrane::SchemaValidationError => e
132
+ raise InvalidResponse, "Received invalid response from cpi with error #{e.message}"
133
+ end
134
+
135
+ def constantize(camel_cased_word)
136
+ error_name = camel_cased_word.split('::').last
137
+ Bosh::Clouds.const_get(error_name)
138
+ end
139
+ end
140
+ end
@@ -1,6 +1,17 @@
1
1
  module Bosh::Clouds
2
2
  class Provider
3
+ def self.create(cloud_config, director_uuid)
4
+ if cloud_config.fetch('external_cpi',{}).fetch('enabled', false)
5
+ ExternalCpiProvider.create(cloud_config['external_cpi'], director_uuid)
6
+ else
7
+ PluginCloudProvider.create(cloud_config['plugin'], cloud_config['properties'])
8
+ end
9
+ end
10
+ end
3
11
 
12
+ private
13
+
14
+ class PluginCloudProvider
4
15
  def self.create(plugin, options)
5
16
  begin
6
17
  require "cloud/#{plugin}"
@@ -10,6 +21,11 @@ module Bosh::Clouds
10
21
 
11
22
  Bosh::Clouds.const_get(plugin.capitalize).new(options)
12
23
  end
24
+ end
13
25
 
26
+ class ExternalCpiProvider
27
+ def self.create(external_cpi_config, director_uuid)
28
+ ExternalCpi.new(external_cpi_config['cpi_path'], director_uuid)
29
+ end
14
30
  end
15
31
  end
@@ -1,5 +1,5 @@
1
1
  module Bosh
2
2
  module Clouds
3
- VERSION = '1.2539.0'
3
+ VERSION = '1.2546.0'
4
4
  end
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bosh_cpi
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2539.0
4
+ version: 1.2546.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2014-05-10 00:00:00.000000000 Z
12
+ date: 2014-05-13 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: bosh_common
@@ -18,7 +18,7 @@ dependencies:
18
18
  requirements:
19
19
  - - ~>
20
20
  - !ruby/object:Gem::Version
21
- version: 1.2539.0
21
+ version: 1.2546.0
22
22
  type: :runtime
23
23
  prerelease: false
24
24
  version_requirements: !ruby/object:Gem::Requirement
@@ -26,10 +26,10 @@ dependencies:
26
26
  requirements:
27
27
  - - ~>
28
28
  - !ruby/object:Gem::Version
29
- version: 1.2539.0
29
+ version: 1.2546.0
30
30
  description: ! 'BOSH CPI
31
31
 
32
- 69090d'
32
+ e8aedb'
33
33
  email: support@cloudfoundry.com
34
34
  executables: []
35
35
  extensions: []
@@ -44,6 +44,7 @@ files:
44
44
  - lib/cloud.rb
45
45
  - lib/cloud/config.rb
46
46
  - lib/cloud/errors.rb
47
+ - lib/cloud/external_cpi.rb
47
48
  - lib/cloud/provider.rb
48
49
  - lib/cloud/version.rb
49
50
  - README
@@ -68,7 +69,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
68
69
  version: '0'
69
70
  segments:
70
71
  - 0
71
- hash: -52753533641108904
72
+ hash: -941851252333260636
72
73
  requirements: []
73
74
  rubyforge_project:
74
75
  rubygems_version: 1.8.23