vcloud-edge_gateway 0.0.1
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.
- data/.gitignore +16 -0
- data/Gemfile +9 -0
- data/LICENSE.txt +20 -0
- data/README.md +160 -0
- data/Rakefile +23 -0
- data/bin/vcloud-edge +12 -0
- data/jenkins.sh +11 -0
- data/lib/vcloud/config_loader.rb +27 -0
- data/lib/vcloud/config_validator.rb +207 -0
- data/lib/vcloud/edge_gateway/configuration_differ.rb +17 -0
- data/lib/vcloud/edge_gateway/configuration_generator/firewall_service.rb +63 -0
- data/lib/vcloud/edge_gateway/configuration_generator/id_ranges.rb +10 -0
- data/lib/vcloud/edge_gateway/configuration_generator/load_balancer_service.rb +243 -0
- data/lib/vcloud/edge_gateway/configuration_generator/nat_service.rb +54 -0
- data/lib/vcloud/edge_gateway/edge_gateway_configuration.rb +41 -0
- data/lib/vcloud/edge_gateway/version.rb +6 -0
- data/lib/vcloud/edge_gateway.rb +32 -0
- data/lib/vcloud/edge_gateway_services.rb +26 -0
- data/lib/vcloud/schema/edge_gateway.rb +15 -0
- data/lib/vcloud/schema/firewall_service.rb +39 -0
- data/lib/vcloud/schema/load_balancer_service.rb +129 -0
- data/lib/vcloud/schema/nat_service.rb +35 -0
- data/scripts/generate_fog_conf_file.sh +6 -0
- data/spec/erb_helper.rb +11 -0
- data/spec/integration/edge_gateway/data/firewall_config.yaml.erb +17 -0
- data/spec/integration/edge_gateway/data/firewall_config_updated_rule.yaml.erb +17 -0
- data/spec/integration/edge_gateway/data/firewall_rule_order_test.yaml.erb +24 -0
- data/spec/integration/edge_gateway/data/hairpin_nat_config.yaml.erb +13 -0
- data/spec/integration/edge_gateway/data/incorrect_firewall_config.yaml +14 -0
- data/spec/integration/edge_gateway/data/nat_and_firewall_config.yaml.erb +32 -0
- data/spec/integration/edge_gateway/data/nat_config.yaml.erb +17 -0
- data/spec/integration/edge_gateway/edge_gateway_services_spec.rb +132 -0
- data/spec/integration/edge_gateway/firewall_service_spec.rb +201 -0
- data/spec/integration/edge_gateway/nat_service_spec.rb +208 -0
- data/spec/spec_helper.rb +26 -0
- data/spec/vcloud/config_loader_spec.rb +112 -0
- data/spec/vcloud/config_validator_spec.rb +570 -0
- data/spec/vcloud/data/basic_preamble_test.erb +8 -0
- data/spec/vcloud/data/basic_preamble_test.erb.OUT +8 -0
- data/spec/vcloud/data/working.json +21 -0
- data/spec/vcloud/data/working.yaml +22 -0
- data/spec/vcloud/data/working_with_defaults.yaml +25 -0
- data/spec/vcloud/edge_gateway/configuration_differ_spec.rb +131 -0
- data/spec/vcloud/edge_gateway/configuration_generator/data/load_balancer_http-input.yaml +41 -0
- data/spec/vcloud/edge_gateway/configuration_generator/data/load_balancer_http-output.yaml +93 -0
- data/spec/vcloud/edge_gateway/configuration_generator/data/load_balancer_https-input.yaml +39 -0
- data/spec/vcloud/edge_gateway/configuration_generator/data/load_balancer_https-output.yaml +92 -0
- data/spec/vcloud/edge_gateway/configuration_generator/data/load_balancer_mixed_complex-input.yaml +65 -0
- data/spec/vcloud/edge_gateway/configuration_generator/data/load_balancer_mixed_complex-output.yaml +94 -0
- data/spec/vcloud/edge_gateway/configuration_generator/firewall_service_spec.rb +378 -0
- data/spec/vcloud/edge_gateway/configuration_generator/load_balancer_service_spec.rb +233 -0
- data/spec/vcloud/edge_gateway/configuration_generator/nat_service_spec.rb +360 -0
- data/spec/vcloud/edge_gateway/edge_gateway_configuration_spec.rb +182 -0
- data/spec/vcloud/edge_gateway/firewall_schema_validation_spec.rb +45 -0
- data/spec/vcloud/edge_gateway/load_balancer_schema_validation_spec.rb +153 -0
- data/spec/vcloud/edge_gateway/nat_schema_validation_spec.rb +93 -0
- data/vcloud-edge_gateway.gemspec +32 -0
- 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
|
data/spec/spec_helper.rb
ADDED
@@ -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
|