vcloud-edge_gateway 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (58) hide show
  1. data/.gitignore +16 -0
  2. data/Gemfile +9 -0
  3. data/LICENSE.txt +20 -0
  4. data/README.md +160 -0
  5. data/Rakefile +23 -0
  6. data/bin/vcloud-edge +12 -0
  7. data/jenkins.sh +11 -0
  8. data/lib/vcloud/config_loader.rb +27 -0
  9. data/lib/vcloud/config_validator.rb +207 -0
  10. data/lib/vcloud/edge_gateway/configuration_differ.rb +17 -0
  11. data/lib/vcloud/edge_gateway/configuration_generator/firewall_service.rb +63 -0
  12. data/lib/vcloud/edge_gateway/configuration_generator/id_ranges.rb +10 -0
  13. data/lib/vcloud/edge_gateway/configuration_generator/load_balancer_service.rb +243 -0
  14. data/lib/vcloud/edge_gateway/configuration_generator/nat_service.rb +54 -0
  15. data/lib/vcloud/edge_gateway/edge_gateway_configuration.rb +41 -0
  16. data/lib/vcloud/edge_gateway/version.rb +6 -0
  17. data/lib/vcloud/edge_gateway.rb +32 -0
  18. data/lib/vcloud/edge_gateway_services.rb +26 -0
  19. data/lib/vcloud/schema/edge_gateway.rb +15 -0
  20. data/lib/vcloud/schema/firewall_service.rb +39 -0
  21. data/lib/vcloud/schema/load_balancer_service.rb +129 -0
  22. data/lib/vcloud/schema/nat_service.rb +35 -0
  23. data/scripts/generate_fog_conf_file.sh +6 -0
  24. data/spec/erb_helper.rb +11 -0
  25. data/spec/integration/edge_gateway/data/firewall_config.yaml.erb +17 -0
  26. data/spec/integration/edge_gateway/data/firewall_config_updated_rule.yaml.erb +17 -0
  27. data/spec/integration/edge_gateway/data/firewall_rule_order_test.yaml.erb +24 -0
  28. data/spec/integration/edge_gateway/data/hairpin_nat_config.yaml.erb +13 -0
  29. data/spec/integration/edge_gateway/data/incorrect_firewall_config.yaml +14 -0
  30. data/spec/integration/edge_gateway/data/nat_and_firewall_config.yaml.erb +32 -0
  31. data/spec/integration/edge_gateway/data/nat_config.yaml.erb +17 -0
  32. data/spec/integration/edge_gateway/edge_gateway_services_spec.rb +132 -0
  33. data/spec/integration/edge_gateway/firewall_service_spec.rb +201 -0
  34. data/spec/integration/edge_gateway/nat_service_spec.rb +208 -0
  35. data/spec/spec_helper.rb +26 -0
  36. data/spec/vcloud/config_loader_spec.rb +112 -0
  37. data/spec/vcloud/config_validator_spec.rb +570 -0
  38. data/spec/vcloud/data/basic_preamble_test.erb +8 -0
  39. data/spec/vcloud/data/basic_preamble_test.erb.OUT +8 -0
  40. data/spec/vcloud/data/working.json +21 -0
  41. data/spec/vcloud/data/working.yaml +22 -0
  42. data/spec/vcloud/data/working_with_defaults.yaml +25 -0
  43. data/spec/vcloud/edge_gateway/configuration_differ_spec.rb +131 -0
  44. data/spec/vcloud/edge_gateway/configuration_generator/data/load_balancer_http-input.yaml +41 -0
  45. data/spec/vcloud/edge_gateway/configuration_generator/data/load_balancer_http-output.yaml +93 -0
  46. data/spec/vcloud/edge_gateway/configuration_generator/data/load_balancer_https-input.yaml +39 -0
  47. data/spec/vcloud/edge_gateway/configuration_generator/data/load_balancer_https-output.yaml +92 -0
  48. data/spec/vcloud/edge_gateway/configuration_generator/data/load_balancer_mixed_complex-input.yaml +65 -0
  49. data/spec/vcloud/edge_gateway/configuration_generator/data/load_balancer_mixed_complex-output.yaml +94 -0
  50. data/spec/vcloud/edge_gateway/configuration_generator/firewall_service_spec.rb +378 -0
  51. data/spec/vcloud/edge_gateway/configuration_generator/load_balancer_service_spec.rb +233 -0
  52. data/spec/vcloud/edge_gateway/configuration_generator/nat_service_spec.rb +360 -0
  53. data/spec/vcloud/edge_gateway/edge_gateway_configuration_spec.rb +182 -0
  54. data/spec/vcloud/edge_gateway/firewall_schema_validation_spec.rb +45 -0
  55. data/spec/vcloud/edge_gateway/load_balancer_schema_validation_spec.rb +153 -0
  56. data/spec/vcloud/edge_gateway/nat_schema_validation_spec.rb +93 -0
  57. data/vcloud-edge_gateway.gemspec +32 -0
  58. metadata +252 -0
@@ -0,0 +1,24 @@
1
+ ---
2
+ gateway: <%= edge_gateway_name %>
3
+ firewall_service:
4
+ firewall_rules:
5
+ - description: 'First Input Rule'
6
+ destination_port_range: '8081'
7
+ destination_ip: 10.10.1.2
8
+ source_ip: Any
9
+ - description: 'Second Input Rule'
10
+ destination_port_range: '8888'
11
+ destination_ip: 10.10.1.2
12
+ source_ip: Any
13
+ - description: 'Third Input Rule'
14
+ destination_port_range: '2222'
15
+ destination_ip: 10.10.1.2
16
+ source_ip: Any
17
+ - description: 'Fourth Input Rule'
18
+ destination_port_range: '9111'
19
+ destination_ip: 10.10.1.2
20
+ source_ip: Any
21
+ - description: 'Fifth Input Rule'
22
+ destination_port_range: '8113'
23
+ destination_ip: 10.10.1.2
24
+ source_ip: Any
@@ -0,0 +1,13 @@
1
+ ---
2
+ gateway: <%= edge_gateway_name %>
3
+ nat_service:
4
+ nat_rules:
5
+ - enabled: true
6
+ description: 'A DNAT rule'
7
+ network_id: <%= org_vdc_network_id %>
8
+ rule_type: 'DNAT'
9
+ translated_ip: '10.10.1.2'
10
+ translated_port: '3412'
11
+ original_ip: <%= original_ip %>
12
+ original_port: '3412'
13
+ protocol: 'tcp'
@@ -0,0 +1,14 @@
1
+ ---
2
+ gateway: 'Test Edgegateway'
3
+ firewall_service:
4
+ policy: drop
5
+ log_default_action: true
6
+ firewall_rules_incorrect:
7
+ - enabled: true
8
+ description: 'A rule'
9
+ policy: allow
10
+ protocols: 'tcp'
11
+ destination_port_range: Any
12
+ destination_ip: Any
13
+ source_port_range: Any
14
+ source_ip: 192.0.2.2
@@ -0,0 +1,32 @@
1
+ ---
2
+ gateway: <%= edge_gateway_name %>
3
+ nat_service:
4
+ nat_rules:
5
+ - enabled: true
6
+ network_id: <%= network_id %>
7
+ rule_type: 'DNAT'
8
+ translated_ip: '10.10.1.2-10.10.1.3'
9
+ translated_port: '3412'
10
+ original_ip: <%= original_ip %>
11
+ original_port: '3412'
12
+ protocol: 'tcp'
13
+ - enabled: true
14
+ network_id: <%= network_id %>
15
+ rule_type: 'SNAT'
16
+ translated_ip: <%= original_ip %>
17
+ original_ip: '10.10.1.2-10.10.1.3'
18
+ firewall_service:
19
+ policy: drop
20
+ log_default_action: true
21
+ firewall_rules:
22
+ - enabled: true
23
+ description: 'A rule'
24
+ policy: allow
25
+ protocols: 'tcp'
26
+ destination_port_range: Any
27
+ destination_ip: 10.10.1.2
28
+ source_port_range: Any
29
+ source_ip: 192.0.2.2
30
+ - enabled: true
31
+ destination_ip: '10.10.1.3-10.10.1.5'
32
+ source_ip: 192.0.2.2/24
@@ -0,0 +1,17 @@
1
+ ---
2
+ gateway: <%= edge_gateway_name %>
3
+ nat_service:
4
+ nat_rules:
5
+ - enabled: true
6
+ network_id: <%= network_id %>
7
+ rule_type: 'DNAT'
8
+ translated_ip: '10.10.1.2-10.10.1.3'
9
+ translated_port: '3412'
10
+ original_ip: <%= original_ip %>
11
+ original_port: '3412'
12
+ protocol: 'tcp'
13
+ - enabled: true
14
+ network_id: <%= network_id %>
15
+ rule_type: 'SNAT'
16
+ translated_ip: <%= original_ip %>
17
+ original_ip: '10.10.1.2-10.10.1.3'
@@ -0,0 +1,132 @@
1
+ require 'spec_helper'
2
+
3
+ module Vcloud
4
+ describe EdgeGatewayServices do
5
+
6
+ required_env = {
7
+ 'VCLOUD_EDGE_GATEWAY' => 'to name of VSE',
8
+ 'VCLOUD_PROVIDER_NETWORK_ID' => 'to ID of VSE external network',
9
+ 'VCLOUD_PROVIDER_NETWORK_IP' => 'to an available IP on VSE external network',
10
+ 'VCLOUD_NETWORK1_ID' => 'to the ID of a VSE internal network',
11
+ 'VCLOUD_NETWORK1_NAME' => 'to the name of the VSE internal network',
12
+ 'VCLOUD_NETWORK1_IP' => 'to an ID on the VSE internal network',
13
+ }
14
+
15
+ error = false
16
+ required_env.each do |var,message|
17
+ unless ENV[var]
18
+ puts "Must set #{var} #{message}" unless ENV[var]
19
+ error = true
20
+ end
21
+ end
22
+ Kernel.exit(2) if error
23
+
24
+ before(:all) do
25
+ @edge_name = ENV['VCLOUD_EDGE_GATEWAY']
26
+ @ext_net_id = ENV['VCLOUD_PROVIDER_NETWORK_ID']
27
+ @ext_net_ip = ENV['VCLOUD_PROVIDER_NETWORK_IP']
28
+ @ext_net_name = ENV['VCLOUD_PROVIDER_NETWORK_NAME']
29
+ @int_net_id = ENV['VCLOUD_NETWORK1_ID']
30
+ @int_net_ip = ENV['VCLOUD_NETWORK1_IP']
31
+ @int_net_name = ENV['VCLOUD_NETWORK1_NAME']
32
+ @files_to_delete = []
33
+ end
34
+
35
+ context "Test EdgeGatewayServices with multiple services" do
36
+
37
+ before(:all) do
38
+ reset_edge_gateway
39
+ @initial_config_file = generate_input_config_file('nat_and_firewall_config.yaml.erb', edge_gateway_erb_input)
40
+ @edge_gateway = Vcloud::Core::EdgeGateway.get_by_name(@edge_name)
41
+ end
42
+
43
+ context "Check update is functional" do
44
+
45
+ before(:all) do
46
+ local_config = ConfigLoader.new.load_config(@initial_config_file, Vcloud::Schema::EDGE_GATEWAY_SERVICES)
47
+ end
48
+
49
+ it "should be starting our tests from an empty EdgeGateway" do
50
+ remote_vcloud_config = @edge_gateway.vcloud_attributes[:Configuration][:EdgeGatewayServiceConfiguration]
51
+ expect(remote_vcloud_config[:FirewallService][:FirewallRule].empty?).to be_true
52
+ expect(remote_vcloud_config[:NatService][:NatRule].empty?).to be_true
53
+ end
54
+
55
+ it "should only need to make one call to Core::EdgeGateway.update_configuration to update configuration" do
56
+ q = Query.new('edgeGateway', :filter => "name==#{@edge_name}")
57
+ result = q.get_all_results
58
+ latest_task = result.first[:task]
59
+
60
+ expect_any_instance_of(Core::EdgeGateway).to receive(:update_configuration).exactly(1).times.and_call_original
61
+ EdgeGatewayServices.new.update(@initial_config_file)
62
+
63
+ test_result = q.get_all_results
64
+ test_latest_task = test_result.first[:task]
65
+
66
+ # confirm that a task has been run on the EdgeGateway
67
+ expect(latest_task == test_latest_task).to be_false
68
+ end
69
+
70
+ it "should now have nat and firewall rules configured" do
71
+ remote_vcloud_config = @edge_gateway.vcloud_attributes[:Configuration][:EdgeGatewayServiceConfiguration]
72
+ expect(remote_vcloud_config[:FirewallService][:FirewallRule].empty?).to be_false
73
+ expect(remote_vcloud_config[:NatService][:NatRule].empty?).to be_false
74
+ end
75
+
76
+ it "should not update the EdgeGateway again if the config hasn't changed" do
77
+ q = Query.new('edgeGateway', :filter => "name==#{@edge_name}")
78
+ result = q.get_all_results
79
+ latest_task = result.first[:task]
80
+
81
+ EdgeGatewayServices.new.update(@initial_config_file)
82
+
83
+ test_result = q.get_all_results
84
+ test_latest_task = result.first[:task]
85
+
86
+ # No task has been run on the EdgeGateway since the one before update was called
87
+ expect(latest_task == test_latest_task).to be_true
88
+ end
89
+
90
+ end
91
+
92
+ after(:all) do
93
+ reset_edge_gateway unless ENV['VCLOUD_NO_RESET_VSE_AFTER']
94
+ remove_temp_config_files
95
+ end
96
+
97
+ def remove_temp_config_files
98
+ FileUtils.rm(@files_to_delete)
99
+ end
100
+
101
+ def reset_edge_gateway
102
+ edge_gateway = Core::EdgeGateway.get_by_name @edge_name
103
+ edge_gateway.update_configuration({
104
+ FirewallService: {IsEnabled: false, FirewallRule: []},
105
+ NatService: {:IsEnabled => "true", :NatRule => []},
106
+ LoadBalancerService: {
107
+ IsEnabled: "false",
108
+ Pool: [],
109
+ VirtualServer: []
110
+ }
111
+ })
112
+ end
113
+
114
+ def generate_input_config_file(data_file, erb_input)
115
+ config_erb = File.expand_path("data/#{data_file}", File.dirname(__FILE__))
116
+ output_file = ErbHelper.convert_erb_template_to_yaml(erb_input, config_erb)
117
+ @files_to_delete << output_file
118
+ output_file
119
+ end
120
+
121
+ def edge_gateway_erb_input
122
+ {
123
+ edge_gateway_name: @edge_name,
124
+ network_id: @ext_net_id,
125
+ original_ip: @ext_net_ip,
126
+ }
127
+ end
128
+
129
+ end
130
+
131
+ end
132
+ end
@@ -0,0 +1,201 @@
1
+ require 'spec_helper'
2
+
3
+ module Vcloud
4
+ describe EdgeGatewayServices do
5
+
6
+ required_env = {
7
+ 'VCLOUD_EDGE_GATEWAY' => 'to name of VSE',
8
+ 'VCLOUD_PROVIDER_NETWORK_ID' => 'to ID of VSE external network',
9
+ 'VCLOUD_PROVIDER_NETWORK_IP' => 'to an available IP on VSE external network',
10
+ 'VCLOUD_NETWORK1_ID' => 'to the ID of a VSE internal network',
11
+ 'VCLOUD_NETWORK1_NAME' => 'to the name of the VSE internal network',
12
+ 'VCLOUD_NETWORK1_IP' => 'to an ID on the VSE internal network',
13
+ }
14
+
15
+ error = false
16
+ required_env.each do |var,message|
17
+ unless ENV[var]
18
+ puts "Must set #{var} #{message}" unless ENV[var]
19
+ error = true
20
+ end
21
+ end
22
+ Kernel.exit(2) if error
23
+
24
+ before(:all) do
25
+ @edge_name = ENV['VCLOUD_EDGE_GATEWAY']
26
+ @ext_net_id = ENV['VCLOUD_PROVIDER_NETWORK_ID']
27
+ @ext_net_ip = ENV['VCLOUD_PROVIDER_NETWORK_IP']
28
+ @ext_net_name = ENV['VCLOUD_PROVIDER_NETWORK_NAME']
29
+ @int_net_id = ENV['VCLOUD_NETWORK1_ID']
30
+ @int_net_ip = ENV['VCLOUD_NETWORK1_IP']
31
+ @int_net_name = ENV['VCLOUD_NETWORK1_NAME']
32
+ @files_to_delete = []
33
+ end
34
+
35
+ context "Test FirewallService specifics of EdgeGatewayServices" do
36
+
37
+ before(:all) do
38
+ reset_edge_gateway
39
+ @initial_firewall_config_file = generate_input_config_file('firewall_config.yaml.erb', edge_gateway_erb_input)
40
+ @edge_gateway = Vcloud::Core::EdgeGateway.get_by_name(@edge_name)
41
+ @firewall_service = {}
42
+ end
43
+
44
+ context "Check input schema checking is working" do
45
+
46
+ it "should raise exception if input yaml does not match with schema" do
47
+ config_yaml = File.expand_path('data/incorrect_firewall_config.yaml', File.dirname(__FILE__))
48
+ expect(Vcloud::EdgeGateway.logger).to receive(:fatal)
49
+ expect { EdgeGatewayServices.new.update(config_yaml) }.to raise_error('Supplied configuration does not match supplied schema')
50
+ end
51
+
52
+ end
53
+
54
+ context "Check update is functional" do
55
+
56
+ before(:all) do
57
+ local_config = ConfigLoader.new.load_config(@initial_firewall_config_file, Vcloud::Schema::EDGE_GATEWAY_SERVICES)
58
+ @local_vcloud_config = EdgeGateway::ConfigurationGenerator::FirewallService.new.generate_fog_config(local_config[:firewall_service])
59
+ end
60
+
61
+ it "should be starting our tests from an empty firewall" do
62
+ remote_vcloud_config = @edge_gateway.vcloud_attributes[:Configuration][:EdgeGatewayServiceConfiguration][:FirewallService]
63
+ expect(remote_vcloud_config[:FirewallRule].empty?).to be_true
64
+ end
65
+
66
+ it "should only need to make one call to Core::EdgeGateway.update_configuration" do
67
+ expect_any_instance_of(Core::EdgeGateway).to receive(:update_configuration).exactly(1).times.and_call_original
68
+ EdgeGatewayServices.new.update(@initial_firewall_config_file)
69
+ end
70
+
71
+ it "should have configured at least one firewall rule" do
72
+ remote_vcloud_config = @edge_gateway.vcloud_attributes[:Configuration][:EdgeGatewayServiceConfiguration][:FirewallService]
73
+ expect(remote_vcloud_config[:FirewallRule].empty?).to be_false
74
+ end
75
+
76
+ it "should have configured the same number of firewall rules as in our configuration" do
77
+ remote_vcloud_config = @edge_gateway.vcloud_attributes[:Configuration][:EdgeGatewayServiceConfiguration][:FirewallService]
78
+ expect(remote_vcloud_config[:FirewallRule].size).
79
+ to eq(@local_vcloud_config[:FirewallRule].size)
80
+ end
81
+
82
+ it "and then should not configure the firewall service if updated again with the same configuration (idempotency)" do
83
+ expect(Vcloud::EdgeGateway.logger).to receive(:info).with('EdgeGatewayServices.update: Configuration is already up to date. Skipping.')
84
+ EdgeGatewayServices.new.update(@initial_firewall_config_file)
85
+ end
86
+
87
+ it "ConfigurationDiffer should return empty if local and remote firewall configs match" do
88
+ remote_vcloud_config = @edge_gateway.vcloud_attributes[:Configuration][:EdgeGatewayServiceConfiguration][:FirewallService]
89
+ differ = EdgeGateway::ConfigurationDiffer.new(@local_vcloud_config, remote_vcloud_config)
90
+ diff_output = differ.diff
91
+ expect(diff_output).to eq([])
92
+ end
93
+
94
+ it "should highlight a difference if local firewall config has been updated" do
95
+ input_config_file = generate_input_config_file('firewall_config_updated_rule.yaml.erb', edge_gateway_erb_input)
96
+
97
+ local_config = ConfigLoader.new.load_config(input_config_file, Vcloud::Schema::EDGE_GATEWAY_SERVICES)
98
+ local_firewall_config = EdgeGateway::ConfigurationGenerator::FirewallService.new.generate_fog_config(local_config[:firewall_service])
99
+
100
+ edge_gateway = Core::EdgeGateway.get_by_name local_config[:gateway]
101
+ remote_config = edge_gateway.vcloud_attributes[:Configuration][:EdgeGatewayServiceConfiguration]
102
+ remote_firewall_config = remote_config[:FirewallService]
103
+
104
+ differ = EdgeGateway::ConfigurationDiffer.new(local_firewall_config, remote_firewall_config)
105
+ diff_output = differ.diff
106
+
107
+ expect(diff_output.empty?).to be_false
108
+ end
109
+
110
+ end
111
+
112
+ context "ensure EdgeGateway FirewallService configuration is as expected" do
113
+ before(:all) do
114
+ @firewall_service = @edge_gateway.vcloud_attributes[:Configuration][:EdgeGatewayServiceConfiguration][:FirewallService]
115
+ end
116
+
117
+ it "should configure firewall rule with destination and source ip addresses" do
118
+ expect(@firewall_service[:FirewallRule].first).to eq({:Id => "1",
119
+ :IsEnabled => "true",
120
+ :MatchOnTranslate => "false",
121
+ :Description => "A rule",
122
+ :Policy => "allow",
123
+ :Protocols => {:Tcp => "true"},
124
+ :Port => "-1",
125
+ :DestinationPortRange => "Any",
126
+ :DestinationIp => "10.10.1.2",
127
+ :SourcePort => "-1",
128
+ :SourcePortRange => "Any",
129
+ :SourceIp => "192.0.2.2",
130
+ :EnableLogging => "false"})
131
+ end
132
+
133
+ it "should configure firewall rule with destination and source ip ranges" do
134
+ expect(@firewall_service[:FirewallRule].last).to eq({:Id => "2",
135
+ :IsEnabled => "true",
136
+ :MatchOnTranslate => "false",
137
+ :Description => "",
138
+ :Policy => "allow",
139
+ :Protocols => {:Tcp => "true"},
140
+ :Port => "-1",
141
+ :DestinationPortRange => "Any",
142
+ :DestinationIp => "10.10.1.3-10.10.1.5",
143
+ :SourcePort => "-1",
144
+ :SourcePortRange => "Any",
145
+ :SourceIp => "192.0.2.2/24",
146
+ :EnableLogging => "false"})
147
+ end
148
+
149
+ end
150
+
151
+ context "Specific FirewallService update tests" do
152
+
153
+ it "should have the same rule order as the input rule order" do
154
+ input_config_file = generate_input_config_file('firewall_rule_order_test.yaml.erb', edge_gateway_erb_input)
155
+ EdgeGatewayServices.new.update(input_config_file)
156
+ remote_rules = @edge_gateway.vcloud_attributes[:Configuration][:EdgeGatewayServiceConfiguration][:FirewallService][:FirewallRule]
157
+ remote_descriptions_list = remote_rules.map {|rule| rule[:Description]}
158
+ expect(remote_descriptions_list).
159
+ to eq([
160
+ "First Input Rule",
161
+ "Second Input Rule",
162
+ "Third Input Rule",
163
+ "Fourth Input Rule",
164
+ "Fifth Input Rule"
165
+ ])
166
+ end
167
+
168
+ end
169
+
170
+
171
+ after(:all) do
172
+ reset_edge_gateway unless ENV['VCLOUD_NO_RESET_VSE_AFTER']
173
+ FileUtils.rm(@files_to_delete)
174
+ end
175
+
176
+ def reset_edge_gateway
177
+ edge_gateway = Core::EdgeGateway.get_by_name @edge_name
178
+ edge_gateway.update_configuration({
179
+ FirewallService: {IsEnabled: false, FirewallRule: []},
180
+ })
181
+ end
182
+
183
+ def generate_input_config_file(data_file, erb_input)
184
+ config_erb = File.expand_path("data/#{data_file}", File.dirname(__FILE__))
185
+ output_file = ErbHelper.convert_erb_template_to_yaml(erb_input, config_erb)
186
+ @files_to_delete << output_file
187
+ output_file
188
+ end
189
+
190
+ def edge_gateway_erb_input
191
+ {
192
+ :edge_gateway_name => @edge_name,
193
+ :edge_gateway_ext_network_id => @ext_net_id,
194
+ :edge_gateway_ext_network_ip => @ext_net_ip,
195
+ }
196
+ end
197
+
198
+ end
199
+
200
+ end
201
+ end
@@ -0,0 +1,208 @@
1
+ require 'spec_helper'
2
+
3
+ module Vcloud
4
+ describe EdgeGatewayServices do
5
+
6
+ required_env = {
7
+ 'VCLOUD_EDGE_GATEWAY' => 'to name of VSE',
8
+ 'VCLOUD_PROVIDER_NETWORK_ID' => 'to ID of VSE external network',
9
+ 'VCLOUD_PROVIDER_NETWORK_IP' => 'to an available IP on VSE external network',
10
+ 'VCLOUD_NETWORK1_ID' => 'to the ID of a VSE internal network',
11
+ 'VCLOUD_NETWORK1_NAME' => 'to the name of the VSE internal network',
12
+ 'VCLOUD_NETWORK1_IP' => 'to an ID on the VSE internal network',
13
+ }
14
+
15
+ error = false
16
+ required_env.each do |var,message|
17
+ unless ENV[var]
18
+ puts "Must set #{var} #{message}" unless ENV[var]
19
+ error = true
20
+ end
21
+ end
22
+ Kernel.exit(2) if error
23
+
24
+ before(:all) do
25
+ @edge_name = ENV['VCLOUD_EDGE_GATEWAY']
26
+ @ext_net_id = ENV['VCLOUD_PROVIDER_NETWORK_ID']
27
+ @ext_net_ip = ENV['VCLOUD_PROVIDER_NETWORK_IP']
28
+ @ext_net_name = ENV['VCLOUD_PROVIDER_NETWORK_NAME']
29
+ @int_net_id = ENV['VCLOUD_NETWORK1_ID']
30
+ @int_net_ip = ENV['VCLOUD_NETWORK1_IP']
31
+ @int_net_name = ENV['VCLOUD_NETWORK1_NAME']
32
+ @files_to_delete = []
33
+ end
34
+
35
+ context "Test NatService specifics of EdgeGatewayServices" do
36
+
37
+ before(:all) do
38
+ reset_edge_gateway
39
+ @initial_nat_config_file = generate_input_config_file(
40
+ 'nat_config.yaml.erb', {
41
+ edge_gateway_name: @edge_name,
42
+ network_id: @ext_net_id,
43
+ original_ip: @ext_net_ip,
44
+ }
45
+ )
46
+ @edge_gateway = Vcloud::Core::EdgeGateway.get_by_name(@edge_name)
47
+ end
48
+
49
+ context "Check update is functional" do
50
+
51
+ before(:all) do
52
+ local_config = ConfigLoader.new.load_config(@initial_nat_config_file, Vcloud::Schema::EDGE_GATEWAY_SERVICES)
53
+ @local_vcloud_config = EdgeGateway::ConfigurationGenerator::NatService.new(@edge_name, local_config[:nat_service]).generate_fog_config
54
+ end
55
+
56
+ it "should be starting our tests from an empty NatService" do
57
+ remote_vcloud_config = @edge_gateway.vcloud_attributes[:Configuration][:EdgeGatewayServiceConfiguration][:NatService]
58
+ expect(remote_vcloud_config[:NatRule].empty?).to be_true
59
+ end
60
+
61
+ it "should only make one EdgeGateway update task, to minimise EdgeGateway reload events" do
62
+ start_time = DateTime.now()
63
+ task_list_before_update = get_all_edge_gateway_update_tasks_ordered_by_start_date_since_time(start_time)
64
+ EdgeGatewayServices.new.update(@initial_nat_config_file)
65
+ task_list_after_update = get_all_edge_gateway_update_tasks_ordered_by_start_date_since_time(start_time)
66
+ expect(task_list_after_update.size - task_list_before_update.size).to be(1)
67
+ end
68
+
69
+ it "should have configured at least one NAT rule" do
70
+ remote_vcloud_config = @edge_gateway.vcloud_attributes[:Configuration][:EdgeGatewayServiceConfiguration][:NatService]
71
+ expect(remote_vcloud_config[:NatRule].empty?).to be_false
72
+ end
73
+
74
+ it "should have configured the same number of nat rules as in our configuration" do
75
+ remote_vcloud_config = @edge_gateway.vcloud_attributes[:Configuration][:EdgeGatewayServiceConfiguration][:NatService]
76
+ expect(remote_vcloud_config[:NatRule].size).
77
+ to eq(@local_vcloud_config[:NatRule].size)
78
+ end
79
+
80
+ it "ConfigurationDiffer should return empty if local and remote nat configs match" do
81
+ remote_vcloud_config = @edge_gateway.vcloud_attributes[:Configuration][:EdgeGatewayServiceConfiguration][:NatService]
82
+ differ = EdgeGateway::ConfigurationDiffer.new(@local_vcloud_config, remote_vcloud_config)
83
+ diff_output = differ.diff
84
+ expect(diff_output).to eq([])
85
+ end
86
+
87
+ it "and then should not configure the firewall service if updated again with the same configuration (idempotency)" do
88
+ expect(Vcloud::EdgeGateway.logger).to receive(:info).with('EdgeGatewayServices.update: Configuration is already up to date. Skipping.')
89
+ EdgeGatewayServices.new.update(@initial_nat_config_file)
90
+ end
91
+
92
+ end
93
+
94
+ context "ensure updated EdgeGateway NatService configuration is as expected" do
95
+ before(:all) do
96
+ @nat_service = @edge_gateway.vcloud_attributes[:Configuration][:EdgeGatewayServiceConfiguration][:NatService]
97
+ end
98
+
99
+ it "should configure DNAT rule" do
100
+ dnat_rule = @nat_service[:NatRule].first
101
+ expect(dnat_rule).not_to be_nil
102
+ expect(dnat_rule[:RuleType]).to eq('DNAT')
103
+ expect(dnat_rule[:Id]).to eq('65537')
104
+ expect(dnat_rule[:IsEnabled]).to eq('true')
105
+ expect(dnat_rule[:GatewayNatRule][:Interface][:href]).to include(@ext_net_id)
106
+ expect(dnat_rule[:GatewayNatRule][:OriginalIp]).to eq(@ext_net_ip)
107
+ expect(dnat_rule[:GatewayNatRule][:OriginalPort]).to eq('3412')
108
+ expect(dnat_rule[:GatewayNatRule][:TranslatedIp]).to eq('10.10.1.2-10.10.1.3')
109
+ expect(dnat_rule[:GatewayNatRule][:TranslatedPort]).to eq('3412')
110
+ expect(dnat_rule[:GatewayNatRule][:Protocol]).to eq('tcp')
111
+ end
112
+
113
+ it "should configure SNAT rule" do
114
+ snat_rule = @nat_service[:NatRule].last
115
+ expect(snat_rule).not_to be_nil
116
+ expect(snat_rule[:RuleType]).to eq('SNAT')
117
+ expect(snat_rule[:Id]).to eq('65538')
118
+ expect(snat_rule[:IsEnabled]).to eq('true')
119
+ expect(snat_rule[:GatewayNatRule][:Interface][:href]).to include(@ext_net_id)
120
+ expect(snat_rule[:GatewayNatRule][:OriginalIp]).to eq('10.10.1.2-10.10.1.3')
121
+ expect(snat_rule[:GatewayNatRule][:TranslatedIp]).to eq(@ext_net_ip)
122
+ end
123
+
124
+ end
125
+
126
+ context "ensure hairpin NAT rules are specifiable" do
127
+
128
+ it "and then should configure hairpin NATting with orgVdcNetwork" do
129
+ input_config_file = generate_input_config_file('hairpin_nat_config.yaml.erb', {
130
+ edge_gateway_name: @edge_name,
131
+ org_vdc_network_id: @int_net_id,
132
+ original_ip: @int_net_ip,
133
+ })
134
+
135
+ EdgeGatewayServices.new.update(input_config_file)
136
+
137
+ edge_gateway = Vcloud::Core::EdgeGateway.get_by_name(@edge_name)
138
+ nat_service = edge_gateway.vcloud_attributes[:Configuration][:EdgeGatewayServiceConfiguration][:NatService]
139
+ expected_rule = nat_service[:NatRule].first
140
+ expect(expected_rule).not_to be_nil
141
+ expect(expected_rule[:RuleType]).to eq('DNAT')
142
+ expect(expected_rule[:Id]).to eq('65537')
143
+ expect(expected_rule[:RuleType]).to eq('DNAT')
144
+ expect(expected_rule[:IsEnabled]).to eq('true')
145
+ expect(expected_rule[:GatewayNatRule][:Interface][:name]).to eq(@int_net_name)
146
+ expect(expected_rule[:GatewayNatRule][:OriginalIp]).to eq(@int_net_ip)
147
+ expect(expected_rule[:GatewayNatRule][:OriginalPort]).to eq('3412')
148
+ expect(expected_rule[:GatewayNatRule][:TranslatedIp]).to eq('10.10.1.2')
149
+ expect(expected_rule[:GatewayNatRule][:TranslatedPort]).to eq('3412')
150
+ expect(expected_rule[:GatewayNatRule][:Protocol]).to eq('tcp')
151
+ end
152
+
153
+ it "should raise error if network provided in rule does not exist" do
154
+ random_network_id = SecureRandom.uuid
155
+ input_config_file = generate_input_config_file('nat_config.yaml.erb', {
156
+ edge_gateway_name: @edge_name,
157
+ network_id: random_network_id,
158
+ original_ip: @int_net_ip,
159
+ })
160
+ expect{EdgeGatewayServices.new.update(input_config_file)}.
161
+ to raise_error("unable to find gateway network interface with id #{random_network_id}")
162
+ end
163
+ end
164
+
165
+ after(:all) do
166
+ reset_edge_gateway unless ENV['VCLOUD_NO_RESET_VSE_AFTER']
167
+ remove_temp_config_files
168
+ end
169
+
170
+ def remove_temp_config_files
171
+ FileUtils.rm(@files_to_delete)
172
+ end
173
+
174
+ def reset_edge_gateway
175
+ edge_gateway = Core::EdgeGateway.get_by_name @edge_name
176
+ edge_gateway.update_configuration({
177
+ NatService: {:IsEnabled => "true", :NatRule => []},
178
+ })
179
+ end
180
+
181
+ def generate_input_config_file(data_file, erb_input)
182
+ config_erb = File.expand_path("data/#{data_file}", File.dirname(__FILE__))
183
+ output_file = ErbHelper.convert_erb_template_to_yaml(erb_input, config_erb)
184
+ @files_to_delete << output_file
185
+ output_file
186
+ end
187
+
188
+ def edge_gateway_erb_input
189
+ {
190
+ :edge_gateway_name => @edge_name,
191
+ :edge_gateway_ext_network_id => @ext_net_id,
192
+ :edge_gateway_ext_network_ip => @ext_net_ip,
193
+ }
194
+ end
195
+
196
+ def get_all_edge_gateway_update_tasks_ordered_by_start_date_since_time(timestamp)
197
+ vcloud_time = timestamp.strftime('%FT%T.000Z')
198
+ q = Query.new('task',
199
+ :filter => "name==networkConfigureEdgeGatewayServices;objectName==#{@edge_name};startDate=ge=#{vcloud_time}",
200
+ :sortDesc => 'startDate',
201
+ )
202
+ q.get_all_results
203
+ end
204
+
205
+ end
206
+
207
+ end
208
+ end
@@ -0,0 +1,26 @@
1
+ require 'simplecov'
2
+ require 'erb_helper'
3
+
4
+ SimpleCov.profiles.define 'gem' do
5
+ add_filter '/spec/'
6
+ add_filter '/features/'
7
+ add_filter '/vendor/'
8
+
9
+ add_group 'Libraries', '/lib/'
10
+ end
11
+
12
+ SimpleCov.start 'gem'
13
+
14
+ require 'bundler/setup'
15
+ require 'vcloud/edge_gateway'
16
+
17
+
18
+ SimpleCov.at_exit do
19
+ SimpleCov.result.format!
20
+ # do not change the coverage percentage, instead add more unit tests to fix coverage failures.
21
+ if SimpleCov.result.covered_percent < 60
22
+ print "ERROR::BAD_COVERAGE\n"
23
+ print "Coverage is less than acceptable limit(71%). Please add more tests to improve the coverage"
24
+ exit(1)
25
+ end
26
+ end