vcloud-core 0.3.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore CHANGED
@@ -1,3 +1,5 @@
1
1
  /Gemfile.lock
2
2
  /coverage/
3
3
  .idea
4
+ spec/integration/vcloud_tools_testing_config.yaml
5
+ .bundle
data/CHANGELOG.md CHANGED
@@ -1,3 +1,20 @@
1
+ ## 0.4.0 (2014-05-23)
2
+
3
+ Features:
4
+
5
+ - Add a 'warnings' variable/method to ConfigValidator.
6
+ - Support simple parameter deprecations in ConfigValidator.
7
+ - Log schema warnings encountered in ConfigLoader.
8
+
9
+ API changes:
10
+
11
+ - Breaking changes to the order and name of arguments for VappTemplate#get
12
+ - Remove unused methods Vcloud::Fog::ServiceInterface#get_catalog and
13
+ Vcloud::Fog::ServiceInterface#get_catalog_item, plus associated
14
+ Vcloud::Fog::ContentTypes constants.
15
+ - Restrict variable scope available to preamble ERB templates so that they
16
+ cannot access or modify the Vm object.
17
+
1
18
  ## 0.3.0 (2014-05-13)
2
19
 
3
20
  Features:
data/README.md CHANGED
@@ -153,18 +153,9 @@ Run the integration tests (slower and requires a real environment):
153
153
 
154
154
  bundle exec rake integration
155
155
 
156
- ### setting up and describing your environment for test runs
157
-
158
- You need access to a suitable vCloud Director organization to run the integration tests - it also needs some basic
159
- configuration: an Edge Gateway, and a routed network.
160
- It is not necessarily safe to run them against an existing environment, unless care is taken with the entities being
161
- tested.
162
-
163
- A number of ENV vars specifying items under test in the environment need to be set for the tests to run successfully.
164
-
165
- - `VCLOUD_EDGE_GATEWAY`: _name of edge gateway under test_
166
- - `VCLOUD_NETWORK1_ID`: _Id of network under test_
167
- - `VCLOUD_PROVIDER_NETWORK_ID`: _Id of the uplink network (or external network) of the VCLOUD_EDGE_GATEWAY under test_
156
+ You need access to a suitable vCloud Director organization to run the
157
+ integration tests. See the [integration tests README](/spec/integration/README.md) for
158
+ further details.
168
159
 
169
160
  ## Contributing
170
161
 
data/Rakefile CHANGED
@@ -1,7 +1,7 @@
1
1
  require 'rspec/core/rake_task'
2
2
  require 'cucumber/rake/task'
3
3
 
4
- task :default => [:spec, :features]
4
+ task :default => [:rubocop, :spec, :features]
5
5
 
6
6
  RSpec::Core::RakeTask.new(:spec) do |task|
7
7
  # Set a bogus Fog credential, otherwise it's possible for the unit
data/jenkins.sh CHANGED
@@ -1,11 +1,14 @@
1
1
  #!/bin/bash -x
2
2
  set -e
3
3
 
4
- rm -f Gemfile.lock
5
- git clean -fdx
6
-
4
+ git clean -ffdx
7
5
  bundle install --path "${HOME}/bundles/${JOB_NAME}"
8
6
 
7
+ # Obtain the integration test parameters
8
+ git clone git@github.gds:gds/vcloud-tools-testing-config.git
9
+ mv vcloud-tools-testing-config/vcloud_tools_testing_config.yaml spec/integration/
10
+ rm -rf vcloud-tools-testing-config
11
+
9
12
  bundle exec rake
10
13
  RUBYOPT="-r ./tools/fog_credentials" bundle exec rake integration
11
14
  bundle exec rake publish_gem
@@ -1,4 +1,12 @@
1
1
  #!/bin/bash -x
2
2
  set -e
3
+
4
+ git clean -ffdx
3
5
  bundle install --path "${HOME}/bundles/${JOB_NAME}"
6
+
7
+ # Obtain the integration test parameters
8
+ git clone git@github.gds:gds/vcloud-tools-testing-config.git
9
+ mv vcloud-tools-testing-config/vcloud_tools_testing_config.yaml spec/integration/
10
+ rm -rf vcloud-tools-testing-config
11
+
4
12
  RUBYOPT="-r ./tools/fog_credentials" bundle exec rake integration
@@ -21,6 +21,9 @@ module Vcloud
21
21
 
22
22
  if schema
23
23
  validation = Core::ConfigValidator.validate(:base, config, schema)
24
+ validation.warnings.each do |warning|
25
+ Vcloud::Core.logger.warn(warning)
26
+ end
24
27
  unless validation.valid?
25
28
  validation.errors.each do |error|
26
29
  Vcloud::Core.logger.fatal(error)
@@ -1,18 +1,42 @@
1
1
  require 'ipaddr'
2
2
 
3
+ ##
4
+ # self::validate is entry point; this class method is called to
5
+ # instantiate ConfigValidator. For example:
6
+ #
7
+ # Core::ConfigValidator.validate(key, data, schema)
8
+ #
9
+ # = Recursion in this class
10
+ #
11
+ # Note that this class will recursively call itself in order to validate deep
12
+ # hash and array structures.
13
+ #
14
+ # The +data+ variable is usually either an array or hash and so will pass
15
+ # through the ConfigValidator#validate_array and
16
+ # ConfigValidator#validate_hash methods respectively.
17
+ #
18
+ # These methods then recursively instantiate this class by calling
19
+ # ConfigValidator::validate again (ConfigValidator#validate_hash calls this
20
+ # indirectly via the ConfigValidator#check_hash_parameter method).
21
+
3
22
  module Vcloud
4
23
  module Core
5
24
  class ConfigValidator
6
25
 
7
- attr_reader :key, :data, :schema, :type, :errors
26
+ attr_reader :key, :data, :schema, :type, :errors, :warnings
8
27
 
9
28
  VALID_ALPHABETICAL_VALUES_FOR_IP_RANGE = %w(Any external internal)
10
29
 
30
+ def self.validate(key, data, schema)
31
+ new(key, data, schema)
32
+ end
33
+
11
34
  def initialize(key, data, schema)
12
35
  raise "Nil schema" unless schema
13
36
  raise "Invalid schema" unless schema.key?(:type)
14
37
  @type = schema[:type].to_s.downcase
15
- @errors = []
38
+ @errors = []
39
+ @warnings = []
16
40
  @data = data
17
41
  @schema = schema
18
42
  @key = key
@@ -23,47 +47,147 @@ module Vcloud
23
47
  @errors.empty?
24
48
  end
25
49
 
26
- def self.validate(key, data, schema)
27
- new(key, data, schema)
28
- end
29
-
30
50
  private
31
51
 
52
+ # Call the corresponding function in this class (dependant on schema[:type])
32
53
  def validate
33
54
  self.send("validate_#{type}".to_sym)
34
55
  end
35
56
 
36
- def validate_string
37
- unless @data.is_a? String
38
- errors << "#{key}: #{@data} is not a string"
57
+ def validate_array
58
+ unless data.is_a? Array
59
+ @errors << "#{key} is not an array"
39
60
  return
40
61
  end
41
62
  return unless check_emptyness_ok
42
- return unless check_matcher_matches
63
+ if schema.key?(:each_element_is)
64
+ element_schema = schema[:each_element_is]
65
+ data.each do |element|
66
+ sub_validator = ConfigValidator.validate(key, element, element_schema)
67
+ @warnings = warnings + sub_validator.warnings
68
+ unless sub_validator.valid?
69
+ @errors = errors + sub_validator.errors
70
+ end
71
+ end
72
+ end
43
73
  end
44
74
 
45
- def validate_string_or_number
46
- unless data.is_a?(String) || data.is_a?(Numeric)
47
- @errors << "#{key}: #{@data} is not a string_or_number"
75
+ def validate_hash
76
+ unless data.is_a? Hash
77
+ @errors << "#{key}: is not a hash"
48
78
  return
49
79
  end
80
+ return unless check_emptyness_ok
81
+ check_for_unknown_parameters
82
+
83
+ if schema.key?(:internals)
84
+ check_for_invalid_deprecations
85
+ deprecations_used = get_deprecations_used
86
+ warn_on_deprecations_used(deprecations_used)
87
+
88
+ schema[:internals].each do |param_key,param_schema|
89
+ ignore_required = (
90
+ param_schema[:deprecated_by] ||
91
+ deprecations_used.key?(param_key)
92
+ )
93
+ check_hash_parameter(param_key, param_schema, ignore_required)
94
+ end
95
+ end
50
96
  end
51
97
 
52
- def validate_ip_address
53
- unless data.is_a?(String)
54
- @errors << "#{key}: #{@data} is not a valid ip_address"
55
- return
98
+ # Return a hash of deprecated params referenced in @data. Where the
99
+ # structure is: `{ :deprecator => :deprecatee }`
100
+ def get_deprecations_used
101
+ used = {}
102
+ schema[:internals].each do |param_key,param_schema|
103
+ deprecated_by = param_schema[:deprecated_by]
104
+ if deprecated_by && data[param_key]
105
+ used[deprecated_by.to_sym] = param_key
106
+ end
56
107
  end
57
- @errors << "#{key}: #{@data} is not a valid ip_address" unless valid_ip_address?(data)
108
+
109
+ used
58
110
  end
59
111
 
60
- def validate_ip_address_range
61
- unless data.is_a?(String)
62
- @errors << "#{key}: #{@data} is not a valid IP address range. Valid values can be IP address, CIDR, IP range, 'Any','internal' and 'external'."
63
- return
112
+ # Append warnings for any deprecations used. Takes the output of
113
+ # `#get_deprecations_used`.
114
+ def warn_on_deprecations_used(deprecations_used)
115
+ deprecations_used.each do |deprecator, deprecatee|
116
+ @warnings << "#{deprecatee}: is deprecated by '#{deprecator}'"
117
+ end
118
+ end
119
+
120
+ def check_emptyness_ok
121
+ unless schema.key?(:allowed_empty) && schema[:allowed_empty]
122
+ if data.empty?
123
+ @errors << "#{key}: cannot be empty #{type}"
124
+ return false
125
+ end
126
+ end
127
+ true
128
+ end
129
+
130
+ # Raise an exception if any `deprecated_by` params refer to params
131
+ # that don't exist in the schema.
132
+ def check_for_invalid_deprecations
133
+ schema[:internals].each do |param_key,param_schema|
134
+ deprecated_by = param_schema[:deprecated_by]
135
+ if deprecated_by && !schema[:internals].key?(deprecated_by.to_sym)
136
+ raise "#{param_key}: deprecated_by target '#{deprecated_by}' not found in schema"
137
+ end
138
+ end
139
+ end
140
+
141
+ def check_for_unknown_parameters
142
+ internals = schema[:internals]
143
+ # if there are no parameters specified, then assume all are ok.
144
+ return true unless internals
145
+ return true if schema[:permit_unknown_parameters]
146
+ data.keys.each do |k|
147
+ @errors << "#{key}: parameter '#{k}' is invalid" unless internals[k]
148
+ end
149
+ end
150
+
151
+ def check_hash_parameter(sub_key, sub_schema, ignore_required=false)
152
+ unless data.key?(sub_key)
153
+ if sub_schema[:required] == false || ignore_required
154
+ return true
155
+ end
156
+
157
+ @errors << "#{key}: missing '#{sub_key}' parameter"
158
+ return false
159
+ end
160
+
161
+ sub_validator = ConfigValidator.validate(
162
+ sub_key,
163
+ data[sub_key],
164
+ sub_schema
165
+ )
166
+ @warnings = warnings + sub_validator.warnings
167
+ unless sub_validator.valid?
168
+ @errors = errors + sub_validator.errors
169
+ end
170
+ end
171
+
172
+ def check_matcher_matches
173
+ regex = schema[:matcher]
174
+ return unless regex
175
+ raise "#{key}: #{regex} is not a Regexp" unless regex.is_a? Regexp
176
+ unless data =~ regex
177
+ @errors << "#{key}: #{data} does not match"
178
+ return false
179
+ end
180
+ true
181
+ end
182
+
183
+ def valid_alphabetical_ip_range?
184
+ VALID_ALPHABETICAL_VALUES_FOR_IP_RANGE.include?(data)
185
+ end
186
+
187
+ def validate_boolean
188
+ unless [true, false].include?(data)
189
+ @errors << "#{key}: #{data} is not a valid boolean value."
64
190
  end
65
- valid = valid_cidr_or_ip_address? || valid_alphabetical_ip_range? || valid_ip_range?
66
- @errors << "#{key}: #{@data} is not a valid IP address range. Valid values can be IP address, CIDR, IP range, 'Any','internal' and 'external'." unless valid
67
191
  end
68
192
 
69
193
  def valid_cidr_or_ip_address?
@@ -75,8 +199,21 @@ module Vcloud
75
199
  end
76
200
  end
77
201
 
78
- def valid_alphabetical_ip_range?
79
- VALID_ALPHABETICAL_VALUES_FOR_IP_RANGE.include?(data)
202
+ def validate_enum
203
+ acceptable_values = schema[:acceptable_values]
204
+ raise "Must set :acceptable_values for type 'enum'" unless acceptable_values.is_a?(Array)
205
+ unless acceptable_values.include?(data)
206
+ acceptable_values_string = acceptable_values.collect {|v| "'#{v}'" }.join(', ')
207
+ @errors << "#{key}: #{@data} is not a valid value. Acceptable values are #{acceptable_values_string}."
208
+ end
209
+ end
210
+
211
+ def validate_ip_address
212
+ unless data.is_a?(String)
213
+ @errors << "#{key}: #{@data} is not a valid ip_address"
214
+ return
215
+ end
216
+ @errors << "#{key}: #{@data} is not a valid ip_address" unless valid_ip_address?(data)
80
217
  end
81
218
 
82
219
  def valid_ip_address? ip_address
@@ -91,6 +228,15 @@ module Vcloud
91
228
  end
92
229
  end
93
230
 
231
+ def validate_ip_address_range
232
+ unless data.is_a?(String)
233
+ @errors << "#{key}: #{@data} is not a valid IP address range. Valid values can be IP address, CIDR, IP range, 'Any','internal' and 'external'."
234
+ return
235
+ end
236
+ valid = valid_cidr_or_ip_address? || valid_alphabetical_ip_range? || valid_ip_range?
237
+ @errors << "#{key}: #{@data} is not a valid IP address range. Valid values can be IP address, CIDR, IP range, 'Any','internal' and 'external'." unless valid
238
+ end
239
+
94
240
  def valid_ip_range?
95
241
  range_parts = data.split('-')
96
242
  return false if range_parts.size != 2
@@ -104,101 +250,20 @@ module Vcloud
104
250
  IPAddr.new(start_address) < IPAddr.new(end_address)
105
251
  end
106
252
 
107
- def validate_hash
108
- unless data.is_a? Hash
109
- @errors << "#{key}: is not a hash"
253
+ def validate_string
254
+ unless @data.is_a? String
255
+ errors << "#{key}: #{@data} is not a string"
110
256
  return
111
257
  end
112
258
  return unless check_emptyness_ok
113
- check_for_unknown_parameters
114
- if schema.key?(:internals)
115
- internals = schema[:internals]
116
- internals.each do |param_key,param_schema|
117
- check_hash_parameter(param_key, param_schema)
118
- end
119
- end
259
+ return unless check_matcher_matches
120
260
  end
121
261
 
122
- def validate_array
123
- unless data.is_a? Array
124
- @errors << "#{key} is not an array"
262
+ def validate_string_or_number
263
+ unless data.is_a?(String) || data.is_a?(Numeric)
264
+ @errors << "#{key}: #{@data} is not a string_or_number"
125
265
  return
126
266
  end
127
- return unless check_emptyness_ok
128
- if schema.key?(:each_element_is)
129
- element_schema = schema[:each_element_is]
130
- data.each do |element|
131
- sub_validator = ConfigValidator.validate(key, element, element_schema)
132
- unless sub_validator.valid?
133
- @errors = errors + sub_validator.errors
134
- end
135
- end
136
- end
137
- end
138
-
139
- def validate_enum
140
- acceptable_values = schema[:acceptable_values]
141
- raise "Must set :acceptable_values for type 'enum'" unless acceptable_values.is_a?(Array)
142
- unless acceptable_values.include?(data)
143
- acceptable_values_string = acceptable_values.collect {|v| "'#{v}'" }.join(', ')
144
- @errors << "#{key}: #{@data} is not a valid value. Acceptable values are #{acceptable_values_string}."
145
- end
146
- end
147
-
148
- def validate_boolean
149
- unless [true, false].include?(data)
150
- @errors << "#{key}: #{data} is not a valid boolean value."
151
- end
152
- end
153
-
154
- def check_emptyness_ok
155
- unless schema.key?(:allowed_empty) && schema[:allowed_empty]
156
- if data.empty?
157
- @errors << "#{key}: cannot be empty #{type}"
158
- return false
159
- end
160
- end
161
- true
162
- end
163
-
164
- def check_matcher_matches
165
- regex = schema[:matcher]
166
- return unless regex
167
- raise "#{key}: #{regex} is not a Regexp" unless regex.is_a? Regexp
168
- unless data =~ regex
169
- @errors << "#{key}: #{data} does not match"
170
- return false
171
- end
172
- true
173
- end
174
-
175
- def check_hash_parameter(sub_key, sub_schema)
176
- if sub_schema.key?(:required) && sub_schema[:required] == false
177
- # short circuit out if we do not have the key, but it's not required.
178
- return true unless data.key?(sub_key)
179
- end
180
- unless data.key?(sub_key)
181
- @errors << "#{key}: missing '#{sub_key}' parameter"
182
- return false
183
- end
184
- sub_validator = ConfigValidator.validate(
185
- sub_key,
186
- data[sub_key],
187
- sub_schema
188
- )
189
- unless sub_validator.valid?
190
- @errors = errors + sub_validator.errors
191
- end
192
- end
193
-
194
- def check_for_unknown_parameters
195
- internals = schema[:internals]
196
- # if there are no parameters specified, then assume all are ok.
197
- return true unless internals
198
- return true if schema[:permit_unknown_parameters]
199
- data.keys.each do |k|
200
- @errors << "#{key}: parameter '#{k}' is invalid" unless internals[k]
201
- end
202
267
  end
203
268
  end
204
269
  end
@@ -6,7 +6,7 @@ module Vcloud
6
6
 
7
7
  def initialize(gateway_interface_hash)
8
8
  if gateway_interface_hash.nil?
9
- raise "EdgeGatewayInterface: gateway_interface_hash cannot be nil"
9
+ raise "EdgeGatewayInterface: gateway_interface_hash cannot be nil"
10
10
  end
11
11
  unless gateway_interface_hash[:Name] && gateway_interface_hash[:Network]
12
12
  raise "EdgeGatewayInterface: bad input: #{gateway_interface_hash}"
@@ -33,11 +33,11 @@ module Vcloud
33
33
  end
34
34
  end
35
35
 
36
- def self.get catalog_name, catalog_item_name
37
- ids = self.get_ids_by_name_and_catalog(catalog_item_name, catalog_name)
36
+ def self.get vapp_template_name, catalog_name
37
+ ids = self.get_ids_by_name_and_catalog(vapp_template_name, catalog_name)
38
38
  raise 'Could not find template vApp' if ids.size == 0
39
39
  if ids.size > 1
40
- raise "Template #{catalog_item_name} is not unique in catalog #{catalog_name}"
40
+ raise "Template #{vapp_template_name} is not unique in catalog #{catalog_name}"
41
41
  end
42
42
  return self.new(ids.first)
43
43
  end
@@ -1,5 +1,5 @@
1
1
  module Vcloud
2
2
  module Core
3
- VERSION = '0.3.0'
3
+ VERSION = '0.4.0'
4
4
  end
5
5
  end
@@ -112,15 +112,21 @@ module Vcloud
112
112
  Vcloud::Fog::ServiceInterface.new.put_guest_customization_section(id, name, interpolated_preamble)
113
113
  end
114
114
 
115
- def generate_preamble(script_path, script_post_processor, vars)
116
- script = ERB.new(File.read(File.expand_path(script_path)), nil, '>-').result(binding)
115
+ def generate_preamble(script_path, script_post_processor, preamble_vars)
116
+ erb_vars = OpenStruct.new({
117
+ vapp_name: vapp_name,
118
+ vars: preamble_vars
119
+ })
120
+ erb_vars_binding_object = erb_vars.instance_eval { binding }
121
+ erb_output = interpolate_erb_file(script_path, erb_vars_binding_object)
117
122
  if script_post_processor
118
- script = Open3.capture2(File.expand_path(script_post_processor),
119
- stdin_data: script).first
123
+ post_process_erb_output(erb_output, script_post_processor) if script_post_processor
124
+ else
125
+ erb_output
120
126
  end
121
- script
122
127
  end
123
128
 
129
+
124
130
  def update_storage_profile storage_profile
125
131
  storage_profile_href = get_storage_profile_href_by_name(storage_profile, @vapp.name)
126
132
  Vcloud::Fog::ServiceInterface.new.put_vm(id, name, {
@@ -132,6 +138,18 @@ module Vcloud
132
138
  end
133
139
 
134
140
  private
141
+
142
+ def interpolate_erb_file(erb_file, binding_object)
143
+ ERB.new(File.read(File.expand_path(erb_file)), nil, '>-').result(binding_object)
144
+ end
145
+
146
+ def post_process_erb_output(data_to_process, post_processor_script)
147
+ # Open3.capture2, as we just need to return STDOUT of the post_processor_script
148
+ Open3.capture2(
149
+ File.expand_path(post_processor_script),
150
+ stdin_data: data_to_process).first
151
+ end
152
+
135
153
  def virtual_hardware_section
136
154
  vcloud_attributes[:'ovf:VirtualHardwareSection'][:'ovf:Item']
137
155
  end
@@ -1,10 +1,8 @@
1
1
  module Vcloud
2
2
  module Fog
3
3
  module ContentTypes
4
- CATALOG = 'application/vnd.vmware.vcloud.catalog+xml'
5
4
  ORG = 'application/vnd.vmware.vcloud.org+xml'
6
5
  VDC = 'application/vnd.vmware.vcloud.vdc+xml'
7
- CATALOG_ITEM = 'application/vnd.vmware.vcloud.catalogItem+xml'
8
6
  NETWORK = 'application/vnd.vmware.vcloud.network+xml'
9
7
  METADATA = 'application/vnd.vmware.vcloud.metadata.value+xml'
10
8
  end
@@ -35,10 +35,6 @@ module Vcloud
35
35
  @vcloud.get_vapps_in_lease_from_query(options).body
36
36
  end
37
37
 
38
- def get_catalog_item(id)
39
- @vcloud.get_catalog_item(id).body
40
- end
41
-
42
38
  def post_instantiate_vapp_template(vdc, template, name, params)
43
39
  Vcloud::Core.logger.debug("instantiating #{name} vapp in #{vdc[:name]}")
44
40
  vapp = @vcloud.post_instantiate_vapp_template(extract_id(vdc), template, name, params).body
@@ -151,10 +147,6 @@ module Vcloud
151
147
  @vcloud.process_task(task)
152
148
  end
153
149
 
154
- def get_catalog(id)
155
- @vcloud.get_catalog(id).body
156
- end
157
-
158
150
  def put_vapp_metadata_value(id, k, v)
159
151
  Vcloud::Core.logger.debug("putting metadata pair '#{k}'=>'#{v}' to #{id}")
160
152
  # need to convert key to_s since Fog 0.17 borks on symbol key
@@ -0,0 +1,36 @@
1
+ # Running vCloud Core Integration Tests
2
+
3
+ ## Prerequisites
4
+
5
+ - Access to a suitable vCloud Director organisation.
6
+
7
+ **NB** It is not safe to run them against an environment that is in use
8
+ (e.g. production, preview) as many of the tests clear down all config at
9
+ the beginning and/or end to ensure the environment is as the tests expect.
10
+
11
+ - A config file with the settings configured.
12
+
13
+ There is a [template file](spec/integration/vcloud_tools_testing_config.yaml.template) to
14
+ help with this. Copy the template file to `spec/integration/vcloud_tools_testing_config.yaml`
15
+ and update with parameters suitable for your environment.
16
+
17
+ - You need to include the set-up for your testing environment in your
18
+ [fog file](https://github.com/alphagov/vcloud-core#credentials).
19
+
20
+ - The tests use the [vCloud Tools Tester](http://rubygems.org/gems/vcloud-tools-tester) gem.
21
+ You do not need to install this, `bundler` will do this for you.
22
+
23
+ ## Parameters
24
+
25
+ ````
26
+ default: # This is the fog credential that refers to your testing environment, e.g. `test_credential`
27
+ vdc_1_name: # The name of a VDC
28
+ catalog: # A catalog
29
+ vapp_template: # A vApp Template within that catalog
30
+ network_1: # The name of the primary network
31
+ network_1_ip: # The IP address of the primary network
32
+ ````
33
+
34
+ ## To run the tests
35
+
36
+ `FOG_CREDENTIAL=test_credential bundle exec integration`
@@ -4,28 +4,12 @@ module Vcloud
4
4
  module Core
5
5
  describe QueryRunner do
6
6
 
7
- required_env = {
8
- 'VCLOUD_VDC_NAME' =>
9
- 'to the name of an orgVdc to use to instantiate vApps into',
10
- 'VCLOUD_TEMPLATE_NAME' =>
11
- 'to the name of a vAppTemplate to use create vApps in tests',
12
- 'VCLOUD_CATALOG_NAME' =>
13
- 'to the name of the catalog that VCLOUD_VAPP_TEMPLATE_NAME is stored in',
14
- }
15
-
16
- error = false
17
- required_env.each do |var,message|
18
- unless ENV[var]
19
- puts "Must set #{var} #{message}" unless ENV[var]
20
- error = true
21
- end
22
- end
23
- Kernel.exit(2) if error
24
-
25
7
  before(:all) do
26
- @vapp_template_name = ENV['VCLOUD_TEMPLATE_NAME']
27
- @vapp_template_catalog_name = ENV['VCLOUD_CATALOG_NAME']
28
- @vdc_name = ENV['VCLOUD_VDC_NAME']
8
+ config_file = File.join(File.dirname(__FILE__), "../vcloud_tools_testing_config.yaml")
9
+ test_data = Vcloud::Tools::Tester::TestParameters.new(config_file)
10
+ @vapp_template_name = test_data.vapp_template
11
+ @vapp_template_catalog_name = test_data.catalog
12
+ @vdc_name = test_data.vdc_1_name
29
13
  end
30
14
 
31
15
  context "#available_query_types" do
@@ -92,11 +76,13 @@ module Vcloud
92
76
 
93
77
  before(:all) do
94
78
  @number_of_vapps_to_create = 2
95
- @test_case_vapps = create_test_case_vapps(
79
+ @test_case_vapps = IntegrationHelper.create_test_case_vapps(
96
80
  @number_of_vapps_to_create,
97
81
  @vdc_name,
98
82
  @vapp_template_catalog_name,
99
83
  @vapp_template_name,
84
+ [],
85
+ "vcloud-core-query-tests"
100
86
  )
101
87
  end
102
88
 
@@ -194,27 +180,7 @@ module Vcloud
194
180
  end
195
181
 
196
182
  after(:all) do
197
- fsi = Vcloud::Fog::ServiceInterface.new()
198
- @test_case_vapps.each do |vapp|
199
- fsi.delete_vapp(vapp.id)
200
- end
201
- end
202
-
203
- def create_test_case_vapps(quantity, vdc_name, catalog_name, vapp_template_name)
204
- vapp_template = VappTemplate.get(catalog_name, vapp_template_name)
205
- timestamp_in_s = Time.new.to_i
206
- base_vapp_name = "vcloud-core-query-tests-#{timestamp_in_s}-"
207
- network_names = []
208
- vapp_list = []
209
- quantity.times do |index|
210
- vapp_list << Vapp.instantiate(
211
- base_vapp_name + index.to_s,
212
- network_names,
213
- vapp_template.id,
214
- vdc_name
215
- )
216
- end
217
- vapp_list
183
+ IntegrationHelper.delete_vapps(@test_case_vapps)
218
184
  end
219
185
 
220
186
  end
@@ -0,0 +1,6 @@
1
+ default:
2
+ vdc_1_name:
3
+ catalog:
4
+ vapp_template:
5
+ network_1:
6
+ network_1_ip:
data/spec/spec_helper.rb CHANGED
@@ -14,15 +14,17 @@ end
14
14
 
15
15
  require 'bundler/setup'
16
16
  require 'vcloud/core'
17
+ require 'vcloud/tools/tester'
17
18
  require 'support/stub_fog_interface.rb'
19
+ require 'support/integration_helper'
18
20
 
19
21
  if ENV['COVERAGE']
20
22
  SimpleCov.at_exit do
21
23
  SimpleCov.result.format!
22
24
  # do not change the coverage percentage, instead add more unit tests to fix coverage failures.
23
- if SimpleCov.result.covered_percent < 71
25
+ if SimpleCov.result.covered_percent < 81
24
26
  print "ERROR::BAD_COVERAGE\n"
25
- print "Coverage is less than acceptable limit(71%). Please add more tests to improve the coverage"
27
+ print "Coverage is less than acceptable limit(81%). Please add more tests to improve the coverage"
26
28
  exit(1)
27
29
  end
28
30
  end
@@ -0,0 +1,32 @@
1
+ module IntegrationHelper
2
+
3
+ def self.create_test_case_vapps(number_of_vapps,
4
+ vdc_name,
5
+ catalog_name,
6
+ vapp_template_name,
7
+ network_names = [],
8
+ prefix = "vcloud-core-tests"
9
+ )
10
+ vapp_template = Vcloud::Core::VappTemplate.get(vapp_template_name, catalog_name)
11
+ timestamp_in_s = Time.new.to_i
12
+ base_vapp_name = "#{prefix}-#{timestamp_in_s}-"
13
+ vapp_list = []
14
+ number_of_vapps.times do |index|
15
+ vapp_list << Vcloud::Core::Vapp.instantiate(
16
+ base_vapp_name + index.to_s,
17
+ network_names,
18
+ vapp_template.id,
19
+ vdc_name
20
+ )
21
+ end
22
+ vapp_list
23
+ end
24
+
25
+ def self.delete_vapps(vapp_list)
26
+ fsi = Vcloud::Fog::ServiceInterface.new()
27
+ vapp_list.each do |vapp|
28
+ fsi.delete_vapp(vapp.id)
29
+ end
30
+ end
31
+
32
+ end
@@ -31,7 +31,7 @@ class StubFogInterface
31
31
  end
32
32
 
33
33
  def get_edge_gateway(id)
34
- {
34
+ {
35
35
  :name => 'test-edgegw-1',
36
36
  :href => "/#{id}",
37
37
  }
@@ -57,6 +57,32 @@ module Vcloud
57
57
  expect { loader.load_config(input_file, invalid_schema) }.
58
58
  to raise_error('Supplied configuration does not match supplied schema')
59
59
  end
60
+
61
+ it "should not log warnings if there are none" do
62
+ input_file = "#{@data_dir}/working_with_defaults.yaml"
63
+ loader = ConfigLoader.new
64
+
65
+ Vcloud::Core.logger.should_not_receive(:warn)
66
+ loader.load_config(input_file, vapp_config_schema)
67
+ end
68
+
69
+ it "should log warnings if checked against a deprecated schema" do
70
+ input_file = "#{@data_dir}/working_with_defaults.yaml"
71
+ loader = ConfigLoader.new
72
+
73
+ Vcloud::Core.logger.should_receive(:warn).with("vapps: is deprecated by 'vapps_new'")
74
+ loader.load_config(input_file, deprecated_schema)
75
+ end
76
+
77
+ it "should log warning before raising error against an invalid and deprecated schema" do
78
+ input_file = "#{@data_dir}/working_with_defaults.yaml"
79
+ loader = ConfigLoader.new
80
+
81
+ Vcloud::Core.logger.should_receive(:warn).with("vapps: is deprecated by 'vapps_new'")
82
+ Vcloud::Core.logger.should_receive(:fatal).with("vapps: is not a hash")
83
+ expect { loader.load_config(input_file, invalid_and_deprecated_schema) }.
84
+ to raise_error('Supplied configuration does not match supplied schema')
85
+ end
60
86
  end
61
87
 
62
88
  def vapp_config_schema
@@ -84,13 +110,35 @@ module Vcloud
84
110
  }
85
111
  end
86
112
 
113
+ def invalid_and_deprecated_schema
114
+ {
115
+ type: 'hash',
116
+ permit_unknown_parameters: true,
117
+ internals: {
118
+ vapps: { type: Hash, deprecated_by: 'vapps_new' },
119
+ vapps_new: { type: 'array' },
120
+ }
121
+ }
122
+ end
123
+
124
+ def deprecated_schema
125
+ {
126
+ type: 'hash',
127
+ permit_unknown_parameters: true,
128
+ internals: {
129
+ vapps: { type: 'array', deprecated_by: 'vapps_new' },
130
+ vapps_new: { type: 'array' },
131
+ }
132
+ }
133
+ end
134
+
87
135
  def valid_config
88
136
  {
89
137
  :vapps=>[{
90
138
  :name=>"vapp-vcloud-tools-tests",
91
139
  :vdc_name=>"VDC_NAME",
92
140
  :catalog=>"CATALOG_NAME",
93
- :catalog_item=>"CATALOG_ITEM",
141
+ :vapp_template=>"VAPP_TEMPLATE",
94
142
  :vm=>{
95
143
  :hardware_config=>{:memory=>"4096", :cpu=>"2"},
96
144
  :extra_disks=>[{:size=>"8192"}],
@@ -564,7 +564,125 @@ module Vcloud
564
564
  expect(v.valid?).to be_true
565
565
  end
566
566
  end
567
+ end
568
+
569
+ context "deprecated_by" do
570
+ # For clarification:
571
+ #
572
+ # - deprecatee: is the old param with a `deprecated_by` field.
573
+ # - deprecator: is the new param listed in the `deprecated_by` field.
574
+ #
575
+ context "deprecatee is provided and deprecator is not" do
576
+ let(:data) {{ name: "santa" }}
577
+
578
+ it "should validate and warn when deprecator is required" do
579
+ schema = {
580
+ type: "Hash",
581
+ internals: {
582
+ name: { type: 'string', deprecated_by: 'full_name' },
583
+ full_name: { type: 'string', required: true },
584
+ }
585
+ }
586
+ v = ConfigValidator.validate(:base, data, schema)
587
+ expect(v.valid?).to be_true
588
+ expect(v.warnings).to eq(["name: is deprecated by 'full_name'"])
589
+ end
590
+
591
+ it "should validate and warn when deprecator appears before deprecatee in schema" do
592
+ schema = {
593
+ type: "Hash",
594
+ internals: {
595
+ full_name: { type: 'string', required: true },
596
+ name: { type: 'string', deprecated_by: 'full_name' },
597
+ }
598
+ }
599
+ v = ConfigValidator.validate(:base, data, schema)
600
+ expect(v.valid?).to be_true
601
+ expect(v.warnings).to eq(["name: is deprecated by 'full_name'"])
602
+ end
603
+
604
+ it "should warn about deprecatee even when both are not required" do
605
+ schema = {
606
+ type: "Hash",
607
+ internals: {
608
+ name: { type: 'string', required: false, deprecated_by: 'full_name' },
609
+ full_name: { type: 'string', required: false },
610
+ }
611
+ }
612
+ v = ConfigValidator.validate(:base, data, schema)
613
+ expect(v.valid?).to be_true
614
+ expect(v.warnings).to eq(["name: is deprecated by 'full_name'"])
615
+ end
616
+ end
617
+
618
+ context "neither deprecatee or deprecator are provided" do
619
+ let(:data) {{ bogus: "blah" }}
620
+
621
+ it "should return error for deprecator but not deprecatee if neither are set" do
622
+ schema = {
623
+ type: "Hash",
624
+ internals: {
625
+ name: { type: 'string', deprecated_by: 'full_name' },
626
+ full_name: { type: 'string', required: true },
627
+ bogus: { type: 'string' },
628
+ }
629
+ }
630
+ v = ConfigValidator.validate(:base, data, schema)
631
+ expect(v.valid?).to be_false
632
+ expect(v.errors).to eq(["base: missing 'full_name' parameter"])
633
+ end
634
+
635
+ it "should raise exception if deprecator does not exist in schema" do
636
+ schema = {
637
+ type: "Hash",
638
+ internals: {
639
+ name: { type: 'string', deprecated_by: 'does_not_exist' },
640
+ bogus: { type: 'string' },
641
+ }
642
+ }
643
+ expect {
644
+ ConfigValidator.validate(:base, data, schema)
645
+ }.to raise_error("name: deprecated_by target 'does_not_exist' not found in schema")
646
+ end
647
+ end
648
+
649
+ context "deprecatee and deprecator are provided and of the wrong type" do
650
+ let(:data) {{ name: 123, full_name: 123 }}
651
+
652
+ it "should return an error for each and a warning for deprecatee" do
653
+ schema = {
654
+ type: "Hash",
655
+ internals: {
656
+ name: { type: 'string', deprecated_by: 'full_name' },
657
+ full_name: { type: 'string', required: true },
658
+ }
659
+ }
660
+ v = ConfigValidator.validate(:base, data, schema)
661
+ expect(v.valid?).to be_false
662
+ expect(v.errors).to eq([
663
+ "name: 123 is not a string",
664
+ "full_name: 123 is not a string",
665
+ ])
666
+ expect(v.warnings).to eq(["name: is deprecated by 'full_name'"])
667
+ end
668
+ end
567
669
 
670
+ it "should not honour deprecation across nested structures" do
671
+ data = { bogus: "blah" }
672
+ schema = {
673
+ type: "Hash",
674
+ internals: {
675
+ name: { type: 'string', deprecated_by: 'full_name' },
676
+ nested: { type: 'hash', internals: {
677
+ full_name: { type: 'string', required: true },
678
+ }},
679
+ bogus: { type: 'string' },
680
+ }
681
+ }
682
+ expect {
683
+ ConfigValidator.validate(:base, data, schema)
684
+ }.to raise_error("name: deprecated_by target 'full_name' not found in schema")
685
+ end
568
686
  end
569
687
 
570
688
  end
@@ -4,6 +4,10 @@
4
4
 
5
5
  (
6
6
  echo "in $*"
7
+ echo "env_var: <%= ENV['TEST_INTERPOLATED_ENVVAR'] -%>"
7
8
  echo "vapp_name: <%= vapp_name -%>"
8
9
  echo "message: <%= vars[:message] -%>"
10
+ <% vars[:array_test].each_with_index do |element, index| -%>
11
+ echo "index<%= index -%>: <%= element -%>"
12
+ <% end -%>
9
13
  ) >> /PREAMBLE_OUTPUT
@@ -4,6 +4,9 @@
4
4
 
5
5
  (
6
6
  echo "in $*"
7
+ echo "env_var: test_interpolated_env"
7
8
  echo "vapp_name: test-vapp-1"
8
9
  echo "message: hello world"
10
+ echo "index0: foo"
11
+ echo "index1: bar"
9
12
  ) >> /PREAMBLE_OUTPUT
@@ -0,0 +1,7 @@
1
+ #!/bin/bash
2
+
3
+ # This is a sample test file that is really just
4
+ # to confirm that when we run this through a
5
+ # postprocessor script, that the output is
6
+ # consistent.
7
+
@@ -3,7 +3,7 @@
3
3
  "name":"vapp-vcloud-tools-tests",
4
4
  "vdc_name":"VDC_NAME",
5
5
  "catalog":"CATALOG_NAME",
6
- "catalog_item":"CATALOG_ITEM",
6
+ "vapp_template":"VAPP_TEMPLATE",
7
7
  "vm":{
8
8
  "hardware_config":{"memory":"4096", "cpu":"2"},
9
9
  "extra_disks":[{"size":"8192"}],
@@ -3,7 +3,7 @@ vapps:
3
3
  - name: vapp-vcloud-tools-tests
4
4
  vdc_name: VDC_NAME
5
5
  catalog: CATALOG_NAME
6
- catalog_item: CATALOG_ITEM
6
+ vapp_template: VAPP_TEMPLATE
7
7
  vm:
8
8
  hardware_config:
9
9
  memory: '4096'
@@ -3,7 +3,7 @@ vapps:
3
3
  - name: vapp-vcloud-tools-tests
4
4
  vdc_name: VDC_NAME
5
5
  catalog: CATALOG_NAME
6
- catalog_item: CATALOG_ITEM
6
+ vapp_template: VAPP_TEMPLATE
7
7
  vm:
8
8
  hardware_config:
9
9
  memory: '4096'
@@ -6,7 +6,7 @@ vapps:
6
6
  - name: vapp-vcloud-tools-tests
7
7
  vdc_name: *VDC_NAME
8
8
  catalog: CATALOG_NAME
9
- catalog_item: CATALOG_ITEM
9
+ vapp_template: VAPP_TEMPLATE
10
10
  vm:
11
11
  hardware_config:
12
12
  memory: '4096'
@@ -8,10 +8,6 @@ module Vcloud
8
8
  @id = 'vappTemplate-12345678-1234-1234-1234-000000234121'
9
9
  @mock_fog_interface = StubFogInterface.new
10
10
  Vcloud::Fog::ServiceInterface.stub(:new).and_return(@mock_fog_interface)
11
- @test_config = {
12
- :catalog => 'test_catalog',
13
- :catalog_item => 'test_template'
14
- }
15
11
  end
16
12
 
17
13
  context "Class public interface" do
@@ -55,7 +51,7 @@ module Vcloud
55
51
  mock_query.should_receive(:run).
56
52
  with('vAppTemplate', :filter => "name==test_template;catalogName==test_catalog").
57
53
  and_return(q_results)
58
- expect { VappTemplate.get('test_catalog', 'test_template') }.
54
+ expect { VappTemplate.get('test_template', 'test_catalog') }.
59
55
  to raise_error('Could not find template vApp')
60
56
  end
61
57
 
@@ -71,7 +67,7 @@ module Vcloud
71
67
  mock_query.should_receive(:run).
72
68
  with('vAppTemplate', :filter => "name==test_template;catalogName==test_catalog").
73
69
  and_return(q_results)
74
- expect { VappTemplate.get('test_catalog', 'test_template') }.
70
+ expect { VappTemplate.get('test_template', 'test_catalog') }.
75
71
  to raise_error('Template test_template is not unique in catalog test_catalog')
76
72
  end
77
73
 
@@ -85,7 +81,7 @@ module Vcloud
85
81
  mock_query.should_receive(:run).
86
82
  with('vAppTemplate', :filter => "name==test_template;catalogName==test_catalog").
87
83
  and_return(q_results)
88
- test_template = VappTemplate.get('test_catalog', 'test_template')
84
+ test_template = VappTemplate.get('test_template', 'test_catalog')
89
85
  test_template.id.should == 'vappTemplate-12345678-90ab-cdef-0123-4567890abcde'
90
86
  end
91
87
 
@@ -181,18 +181,26 @@ module Vcloud
181
181
  end
182
182
 
183
183
  context '#generate_preamble' do
184
- it "should interpolate vars hash and vapp_name into template" do
185
- vars = {:message => 'hello world'}
184
+ it "should interpolate vars hash, ENV, and vapp_name into template" do
185
+ vars = {
186
+ :message => 'hello world',
187
+ :array_test => [ 'foo', 'bar' ],
188
+ }
189
+ stub_const('ENV', {'TEST_INTERPOLATED_ENVVAR' => 'test_interpolated_env'})
186
190
  erbfile = "#{@data_dir}/basic_preamble_test.erb"
187
191
  expected_output = File.read("#{erbfile}.OUT")
188
192
  @vm.generate_preamble(erbfile, nil, vars).should == expected_output
189
193
  end
190
194
 
191
- it "should interpolate vars hash and post-process template" do
192
- vars = {:message => 'hello world'}
193
- erbfile = "#{@data_dir}/basic_preamble_test.erb"
194
- expected_output = File.read("#{erbfile}.OUT")
195
- @vm.generate_preamble(erbfile, '/bin/cat', vars).should == expected_output
195
+ it "passes the output of ERB through an optional post-processor tool" do
196
+ # we use 'wc' as post processor since it will give us the character
197
+ # count in the file, which we can easily match on, and is common
198
+ # across most OSes.
199
+ vars = {}
200
+ erbfile = "#{@data_dir}/preamble_post_processor_test_input.erb"
201
+ characters_in_file = File.read(erbfile).size
202
+ expect(@vm.generate_preamble(erbfile, '/usr/bin/wc', vars)).
203
+ to match(/^\s+\d+\s+\d+\s+#{characters_in_file}\s/)
196
204
  end
197
205
  end
198
206
 
data/vcloud-core.gemspec CHANGED
@@ -32,4 +32,5 @@ Gem::Specification.new do |s|
32
32
  s.add_development_dependency 'rubocop'
33
33
  s.add_development_dependency 'simplecov', '~> 0.8.2'
34
34
  s.add_development_dependency 'gem_publisher', '1.2.0'
35
+ s.add_development_dependency 'vcloud-tools-tester', '0.0.3'
35
36
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: vcloud-core
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.4.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-14 00:00:00.000000000 Z
12
+ date: 2014-05-23 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: fog
@@ -171,6 +171,22 @@ dependencies:
171
171
  - - '='
172
172
  - !ruby/object:Gem::Version
173
173
  version: 1.2.0
174
+ - !ruby/object:Gem::Dependency
175
+ name: vcloud-tools-tester
176
+ requirement: !ruby/object:Gem::Requirement
177
+ none: false
178
+ requirements:
179
+ - - '='
180
+ - !ruby/object:Gem::Version
181
+ version: 0.0.3
182
+ type: :development
183
+ prerelease: false
184
+ version_requirements: !ruby/object:Gem::Requirement
185
+ none: false
186
+ requirements:
187
+ - - '='
188
+ - !ruby/object:Gem::Version
189
+ version: 0.0.3
174
190
  description: Core tools for interacting with VMware vCloud Director. Includes VCloud
175
191
  Query, a light wrapper round the vCloud Query API.
176
192
  email:
@@ -213,15 +229,19 @@ files:
213
229
  - lib/vcloud/fog/model_interface.rb
214
230
  - lib/vcloud/fog/relation.rb
215
231
  - lib/vcloud/fog/service_interface.rb
232
+ - spec/integration/README.md
233
+ - spec/integration/core/query_runner_spec.rb
216
234
  - spec/integration/edge_gateway/configure_edge_gateway_services_spec.rb
217
235
  - spec/integration/edge_gateway/edge_gateway_spec.rb
218
- - spec/integration/query/query_runner_spec.rb
236
+ - spec/integration/vcloud_tools_testing_config.yaml.template
219
237
  - spec/spec_helper.rb
238
+ - spec/support/integration_helper.rb
220
239
  - spec/support/stub_fog_interface.rb
221
240
  - spec/vcloud/core/config_loader_spec.rb
222
241
  - spec/vcloud/core/config_validator_spec.rb
223
242
  - spec/vcloud/core/data/basic_preamble_test.erb
224
243
  - spec/vcloud/core/data/basic_preamble_test.erb.OUT
244
+ - spec/vcloud/core/data/preamble_post_processor_test_input.erb
225
245
  - spec/vcloud/core/data/working.json
226
246
  - spec/vcloud/core/data/working.yaml
227
247
  - spec/vcloud/core/data/working_template.yaml
@@ -262,7 +282,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
262
282
  version: '0'
263
283
  segments:
264
284
  - 0
265
- hash: 2968796492213479520
285
+ hash: -2177023225627992680
266
286
  requirements: []
267
287
  rubyforge_project:
268
288
  rubygems_version: 1.8.23
@@ -272,15 +292,19 @@ summary: Core tools for interacting with VMware vCloud Director
272
292
  test_files:
273
293
  - features/support/env.rb
274
294
  - features/vcloud-query.feature
295
+ - spec/integration/README.md
296
+ - spec/integration/core/query_runner_spec.rb
275
297
  - spec/integration/edge_gateway/configure_edge_gateway_services_spec.rb
276
298
  - spec/integration/edge_gateway/edge_gateway_spec.rb
277
- - spec/integration/query/query_runner_spec.rb
299
+ - spec/integration/vcloud_tools_testing_config.yaml.template
278
300
  - spec/spec_helper.rb
301
+ - spec/support/integration_helper.rb
279
302
  - spec/support/stub_fog_interface.rb
280
303
  - spec/vcloud/core/config_loader_spec.rb
281
304
  - spec/vcloud/core/config_validator_spec.rb
282
305
  - spec/vcloud/core/data/basic_preamble_test.erb
283
306
  - spec/vcloud/core/data/basic_preamble_test.erb.OUT
307
+ - spec/vcloud/core/data/preamble_post_processor_test_input.erb
284
308
  - spec/vcloud/core/data/working.json
285
309
  - spec/vcloud/core/data/working.yaml
286
310
  - spec/vcloud/core/data/working_template.yaml