inception 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (74) hide show
  1. data/.chef/knife.rb +4 -0
  2. data/.gitignore +20 -0
  3. data/.kitchen.yml +42 -0
  4. data/.rspec +3 -0
  5. data/.travis.yml +23 -0
  6. data/Berksfile +8 -0
  7. data/Berksfile.lock +9 -0
  8. data/Gemfile +18 -0
  9. data/LICENSE.txt +22 -0
  10. data/README.md +139 -0
  11. data/Rakefile +66 -0
  12. data/TODO.md +26 -0
  13. data/bin/bosh-inception +8 -0
  14. data/config/ssh/kitchen-aws +23 -0
  15. data/cookbooks/bosh_inception/README.md +15 -0
  16. data/cookbooks/bosh_inception/attributes/default.rb +20 -0
  17. data/cookbooks/bosh_inception/files/default/Gemfile.cf +4 -0
  18. data/cookbooks/bosh_inception/files/default/Gemfile.micro +5 -0
  19. data/cookbooks/bosh_inception/metadata.rb +32 -0
  20. data/cookbooks/bosh_inception/recipes/default.rb +15 -0
  21. data/cookbooks/bosh_inception/recipes/install_bosh.rb +37 -0
  22. data/cookbooks/bosh_inception/recipes/install_ruby.rb +10 -0
  23. data/cookbooks/bosh_inception/recipes/mount_store_volume.rb +24 -0
  24. data/cookbooks/bosh_inception/recipes/packages.rb +23 -0
  25. data/cookbooks/bosh_inception/recipes/setup_git.rb +34 -0
  26. data/cookbooks/bosh_inception/recipes/useful_dirs.rb +13 -0
  27. data/inception.gemspec +42 -0
  28. data/lib/bosh/providers.rb +41 -0
  29. data/lib/bosh/providers/README.md +5 -0
  30. data/lib/bosh/providers/cli/aws_provider_cli.rb +58 -0
  31. data/lib/bosh/providers/cli/openstack_provider_cli.rb +47 -0
  32. data/lib/bosh/providers/cli/provider_cli.rb +17 -0
  33. data/lib/bosh/providers/clients/aws_provider_client.rb +168 -0
  34. data/lib/bosh/providers/clients/fog_provider_client.rb +161 -0
  35. data/lib/bosh/providers/clients/openstack_provider_client.rb +65 -0
  36. data/lib/bosh/providers/constants/aws_constants.rb +25 -0
  37. data/lib/bosh/providers/constants/openstack_constants.rb +12 -0
  38. data/lib/inception.rb +9 -0
  39. data/lib/inception/cli.rb +136 -0
  40. data/lib/inception/cli_helpers/display.rb +26 -0
  41. data/lib/inception/cli_helpers/infrastructure.rb +157 -0
  42. data/lib/inception/cli_helpers/interactions.rb +15 -0
  43. data/lib/inception/cli_helpers/prepare_deploy_settings.rb +89 -0
  44. data/lib/inception/cli_helpers/provider.rb +14 -0
  45. data/lib/inception/cli_helpers/settings.rb +47 -0
  46. data/lib/inception/inception_server.rb +305 -0
  47. data/lib/inception/inception_server_cookbook.rb +89 -0
  48. data/lib/inception/next_deploy_actions.rb +20 -0
  49. data/lib/inception/version.rb +3 -0
  50. data/nodes/.gitkeep +0 -0
  51. data/spec/assets/.gitkeep +0 -0
  52. data/spec/assets/gitconfig +5 -0
  53. data/spec/assets/settings/aws-before-server.yml +14 -0
  54. data/spec/assets/settings/aws-created-server.yml +31 -0
  55. data/spec/integration/.gitkeep +0 -0
  56. data/spec/integration/aws/aws_basic_spec.rb +39 -0
  57. data/spec/spec_helper.rb +50 -0
  58. data/spec/support/aws/aws_helpers.rb +73 -0
  59. data/spec/support/settings_helper.rb +20 -0
  60. data/spec/support/stdout_capture.rb +17 -0
  61. data/spec/unit/.gitkeep +0 -0
  62. data/spec/unit/bosh/providers/aws_spec.rb +199 -0
  63. data/spec/unit/cli_delete_spec.rb +39 -0
  64. data/spec/unit/cli_deploy_aws_spec.rb +84 -0
  65. data/spec/unit/cli_ssh_spec.rb +82 -0
  66. data/spec/unit/inception_server_cookbook_spec.rb +61 -0
  67. data/spec/unit/inception_server_spec.rb +58 -0
  68. data/test/integration/default/bats/discover_user.bash +2 -0
  69. data/test/integration/default/bats/install_ruby.bats +8 -0
  70. data/test/integration/default/bats/useful_dirs.bats +8 -0
  71. data/test/integration/default/bats/user.bats +9 -0
  72. data/test/integration/default/bats/verify_bosh.bats +13 -0
  73. data/test/integration/default/bats/verify_git.bats +18 -0
  74. metadata +342 -0
@@ -0,0 +1,89 @@
1
+ module Inception
2
+ # Perform converge chef cookbooks upon inception server
3
+ class InceptionServerCookbook
4
+ include FileUtils
5
+
6
+ attr_reader :server, :settings, :project_dir
7
+
8
+ class InvalidTarget < StandardError; end
9
+
10
+ def initialize(inception_server, settings, project_dir)
11
+ @server = inception_server
12
+ @settings = settings
13
+ @project_dir = project_dir
14
+ end
15
+
16
+ def prepare
17
+ FileUtils.chdir(project_dir) do
18
+ prepare_project_dir
19
+ knife_solo :prepare unless ignore_chef_preparations?
20
+ end
21
+ end
22
+
23
+ # To be invoked within the settings_dir
24
+ def converge
25
+ FileUtils.chdir(project_dir) do
26
+ knife_solo :cook
27
+ end
28
+ end
29
+
30
+ def ignore_chef_preparations?
31
+ @settings.exists?("cookbook.prepared")
32
+ end
33
+
34
+ def user_host; server.user_host; end
35
+ def key_path; server.private_key_path; end
36
+
37
+ def knife_solo(command)
38
+ attributes = cookbook_attributes_for_inception.to_json
39
+ sh %Q{knife solo #{command} #{user_host} -i #{key_path} -j '#{attributes}' -r 'bosh_inception'}
40
+ end
41
+
42
+ protected
43
+ def prepare_project_dir
44
+ prepare_cookbook
45
+ prepare_knife_config
46
+ prepare_berksfile
47
+ end
48
+
49
+ def prepare_cookbook
50
+ mkdir_p("cookbooks")
51
+ rm_rf("cookbooks/bosh_inception")
52
+ cp_r(inception_cookbook_path, "cookbooks/")
53
+ end
54
+
55
+ def prepare_knife_config
56
+ mkdir_p("nodes") # needed for knife solo
57
+ end
58
+
59
+ def prepare_berksfile
60
+ unless File.exists?("Berksfile")
61
+ cp_r(File.join(gem_root_path, "Berksfile"), "Berksfile")
62
+ end
63
+ end
64
+
65
+ def cookbook_attributes_for_inception
66
+ {
67
+ "disk" => {
68
+ "mounted" => true,
69
+ "device" => settings.inception.provisioned.disk_device.internal
70
+ },
71
+ "git" => {
72
+ "name" => settings.git.name,
73
+ "email" => settings.git.email
74
+ },
75
+ "user" => {
76
+ "username" => settings.inception.provisioned.username
77
+ }
78
+ }
79
+ end
80
+
81
+ def gem_root_path
82
+ File.expand_path("../../..", __FILE__)
83
+ end
84
+
85
+ def inception_cookbook_path
86
+ File.join(gem_root_path, "cookbooks/bosh_inception")
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,20 @@
1
+ module Inception
2
+
3
+ class NextDeployActions
4
+ def initialize(attributes, cli_options)
5
+ @attributes = attributes.is_a?(Hash) ? Settingslogic.new(attributes) : attributes
6
+ raise "@attributes must be Settingslogic (or Hash)" unless @attributes.is_a?(Settingslogic)
7
+ raise "@cli_options must be Hash" unless cli_options.is_a?(Hash)
8
+ apply_cli_options(cli_options)
9
+ end
10
+
11
+ def skip_chef_converge?
12
+ @attributes["no_converge"] || @attributes["no-converge"] || @attributes["skip_chef_converge"]
13
+ end
14
+
15
+ protected
16
+ def apply_cli_options(cli_options)
17
+ @attributes.merge(cli_options)
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,3 @@
1
+ module Inception
2
+ VERSION = "0.1.0"
3
+ end
File without changes
File without changes
@@ -0,0 +1,5 @@
1
+ [user]
2
+ name = Dr Nic Williams
3
+ email = drnicwilliams@gmail.com
4
+ [github]
5
+ user = drnic
@@ -0,0 +1,14 @@
1
+ ---
2
+ provider:
3
+ name: aws
4
+ region: us-west-2
5
+ credentials:
6
+ aws_access_key_id: MOCK_AWS_ACCESS_KEY_ID
7
+ aws_secret_access_key: MOCK_AWS_SECRET_ACCESS_KEY
8
+ inception:
9
+ ip_address: 54.214.15.178
10
+ key_pair:
11
+ name: inception
12
+ private_key: private_key
13
+ extensions:
14
+ - mosh
@@ -0,0 +1,31 @@
1
+ ---
2
+ provider:
3
+ name: aws
4
+ region: us-west-2
5
+ credentials:
6
+ aws_access_key_id: MOCK_AWS_ACCESS_KEY_ID
7
+ aws_secret_access_key: MOCK_AWS_SECRET_ACCESS_KEY
8
+ git:
9
+ name: Dr Nic Williams
10
+ email: drnicwilliams@gmail.com
11
+ inception:
12
+ key_pair:
13
+ name: inception
14
+ private_key: private_key
15
+ extensions:
16
+ - mosh
17
+ size: m1.small
18
+ disk_size: 16
19
+ provisioned:
20
+ ip_address: 54.214.15.178
21
+ image_id: ami-123456
22
+ server_id: i-e7f005d2
23
+ security_groups:
24
+ - ssh
25
+ - mosh
26
+ username: vcap
27
+ disk_device:
28
+ volume_id: vol-123456
29
+ external: /dev/sdf
30
+ internal: /dev/xvdf
31
+ host: ec2-54-214-15-178.us-west-2.compute.amazonaws.com
File without changes
@@ -0,0 +1,39 @@
1
+ require File.expand_path("../../../spec_helper", __FILE__)
2
+ require File.expand_path("../../../support/aws/aws_helpers", __FILE__)
3
+
4
+ describe "AWS deployment without Chef run" do
5
+ include FileUtils
6
+ include AwsHelpers
7
+
8
+ if AwsHelpers.aws_credentials?
9
+ before do
10
+ prepare_aws("basic", aws_region, "next_deploy_actions.no_converge" => true)
11
+ end
12
+ after(:all) do
13
+ destroy_test_constructs unless keep_after_test?
14
+ end
15
+
16
+ def aws_region
17
+ ENV['AWS_REGION'] || "us-west-2"
18
+ end
19
+
20
+ it "creates an EC2 inception/microbosh with the associated resources" do
21
+ create_manifest
22
+
23
+ manifest_file = home_file(".bosh_inception", "settings.yml")
24
+ File.should be_exists(manifest_file)
25
+
26
+ cmd.deploy
27
+
28
+ inception_servers = fog.servers.select { |s| s.tags["Name"] == test_server_name && s.ready? }
29
+ inception_servers.size.should == 1
30
+
31
+ server = inception_servers.first
32
+ server.volumes.size.should == 2
33
+ named_volume = server.volumes.select { |s| s.tags["Name"] == test_server_name }
34
+ named_volume.should_not be_nil
35
+ end
36
+ else
37
+ it "no AWS integration specs run; missing $AWS_ACCESS_KEY_ID etc"
38
+ end
39
+ end
@@ -0,0 +1,50 @@
1
+ # Copyright (c) 2012-2013 Stark & Wayne, LLC
2
+
3
+ ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile", __FILE__)
4
+
5
+ require "rubygems"
6
+ require "bundler"
7
+ Bundler.setup(:default, :test)
8
+
9
+ $:.unshift(File.expand_path("../../lib", __FILE__))
10
+
11
+ require "rspec/core"
12
+ require "bosh/providers"
13
+ require "inception"
14
+ require "inception/cli"
15
+
16
+ # for the #sh helper
17
+ require "rake"
18
+ require "rake/file_utils"
19
+
20
+ # load all files in spec/support/* (but not lower down)
21
+ Dir[File.dirname(__FILE__) + '/support/*'].each do |path|
22
+ require path unless File.directory?(path)
23
+ end
24
+
25
+ def spec_asset(filename)
26
+ File.expand_path("../assets/#{filename}", __FILE__)
27
+ end
28
+
29
+ def setup_home_dir
30
+ home_dir = File.expand_path("../../tmp/home", __FILE__)
31
+ FileUtils.rm_rf(home_dir)
32
+ FileUtils.mkdir_p(home_dir)
33
+ FileUtils.cp_r(spec_asset("gitconfig"), home_file(".gitconfig"))
34
+ ENV['HOME'] = home_dir
35
+ end
36
+
37
+ # returns the file path to a file
38
+ # in the fake $HOME folder
39
+ def home_file(*path)
40
+ File.join(ENV['HOME'], *path)
41
+ end
42
+
43
+ RSpec.configure do |c|
44
+ c.before do
45
+ setup_home_dir
46
+ Fog::Mock.reset
47
+ end
48
+
49
+ c.color_enabled = true
50
+ end
@@ -0,0 +1,73 @@
1
+ require "active_support/core_ext/hash/keys"
2
+ module AwsHelpers
3
+ extend self
4
+ include SettingsHelper
5
+
6
+ def keep_after_test?
7
+ ENV['KEEP_AFTER_TEST']
8
+ end
9
+
10
+ def region
11
+ @region ||= "us-west-2"
12
+ end
13
+
14
+ def fog
15
+ @fog ||= Fog::Compute.new(fog_credentials.merge(:region => region))
16
+ end
17
+
18
+ def aws_credentials?
19
+ access_key = ENV['AWS_ACCESS_KEY_ID']
20
+ secret_key = ENV["AWS_SECRET_ACCESS_KEY"]
21
+ access_key && secret_key
22
+ end
23
+
24
+ def fog_credentials
25
+ @fog_credentials ||= begin
26
+ access_key = ENV['AWS_ACCESS_KEY_ID']
27
+ secret_key = ENV["AWS_SECRET_ACCESS_KEY"]
28
+ unless access_key && secret_key
29
+ raise "Please provided $AWS_ACCESS_KEY_ID and $AWS_SECRET_ACCESS_KEY"
30
+ end
31
+ credentials = {
32
+ :provider => 'AWS',
33
+ :aws_access_key_id => access_key,
34
+ :aws_secret_access_key => secret_key
35
+ }
36
+ end
37
+ end
38
+
39
+ def prepare_aws(spec_name, aws_region, options={})
40
+ setup_home_dir
41
+ @cmd = nil
42
+ @fog = nil
43
+ create_manifest(options)
44
+ destroy_test_constructs
45
+ end
46
+
47
+ def unique_number
48
+ ENV['UNIQUE_NUMBER'] || Random.rand(100000)
49
+ end
50
+
51
+ def test_server_name
52
+ "test-inception"
53
+ end
54
+
55
+ def create_manifest(options = {})
56
+ credentials = options.delete(:credentials) || fog_credentials
57
+ setting "provider.name", "aws"
58
+ setting "provider.credentials", credentials.stringify_keys
59
+ setting "provider.region", region
60
+ setting "inception.name", test_server_name
61
+ options.each { |key, value| setting(key, value) }
62
+ cmd.save_settings!
63
+ end
64
+
65
+ def destroy_test_constructs
66
+ puts "Destroying everything created by previous test..."
67
+ # destroy servers using inception-vm SG
68
+ provider.delete_servers_with_name(test_server_name)
69
+ provider.delete_volumes_with_name(test_server_name)
70
+ provider.delete_key_pair_if_exists(test_server_name)
71
+ provider.cleanup_unused_ip_addresses
72
+ end
73
+ end
@@ -0,0 +1,20 @@
1
+ # assumes @cmd is Inception::Cli instance
2
+ module SettingsHelper
3
+ def cmd
4
+ @cmd ||= Inception::Cli.new
5
+ end
6
+
7
+ def provider
8
+ cmd.provider_client
9
+ end
10
+
11
+ # Set a nested setting with "key1.key2.key3" notation
12
+ def setting(nested_key, value)
13
+ settings.set(nested_key, value)
14
+ end
15
+
16
+ # used by +SettingsSetter+ to access the settings
17
+ def settings
18
+ cmd.settings
19
+ end
20
+ end
@@ -0,0 +1,17 @@
1
+ module StdoutCapture
2
+ # Captures stdout within the block
3
+ # Usage:
4
+ #
5
+ # out = capture_stdout do
6
+ # puts "this will not be shown"
7
+ # end
8
+ # out.should == "this will not be shown"
9
+ def capture_stdout(&block)
10
+ out = StringIO.new
11
+ $stdout = out
12
+ yield
13
+ return out
14
+ ensure
15
+ $stdout = STDOUT
16
+ end
17
+ end
File without changes
@@ -0,0 +1,199 @@
1
+ # Copyright (c) 2012-2013 Stark & Wayne, LLC
2
+
3
+ require File.expand_path("../../../../spec_helper", __FILE__)
4
+ require "fog"
5
+
6
+ # Specs for the aws provider
7
+ describe Bosh::Providers do
8
+ include FileUtils
9
+ include StdoutCapture
10
+
11
+ describe "AWS" do
12
+ before { Fog.mock! }
13
+ let(:provider_attributes) do
14
+ {
15
+ "name" => "aws",
16
+ "region" => "us-west-2",
17
+ "credentials" => {
18
+ "aws_access_key_id" => 'MOCK_AWS_ACCESS_KEY_ID',
19
+ "aws_secret_access_key" => 'MOCK_AWS_SECRET_ACCESS_KEY'
20
+ }
21
+ }
22
+ end
23
+ subject { Bosh::Providers.provider_client(provider_attributes) }
24
+ let(:fog_compute) { subject.fog_compute }
25
+
26
+ describe "create security group" do
27
+ it "should open a single TCP port on a security group" do
28
+ capture_stdout do
29
+ ports = { ssh: 22 }
30
+ subject.create_security_group("sg1-name", "sg1-desc", ports)
31
+ created_sg = fog_compute.security_groups.get("sg1-name")
32
+ created_sg.name.should == "sg1-name"
33
+ created_sg.description.should == "sg1-desc"
34
+ created_sg.ip_permissions.should == [
35
+ {
36
+ "ipProtocol"=>"tcp",
37
+ "fromPort"=>22,
38
+ "toPort"=>22,
39
+ "groups"=>[],
40
+ "ipRanges"=>[ { "cidrIp"=>"0.0.0.0/0" } ]
41
+ }
42
+ ]
43
+ end
44
+ end
45
+ it "should open a range of TCP ports" do
46
+ capture_stdout do
47
+ ports = { ssh: (22..30) }
48
+ subject.create_security_group("sg-range-name", "sg-range-desc", ports)
49
+ created_sg = fog_compute.security_groups.get("sg-range-name")
50
+ created_sg.ip_permissions.should == [
51
+ {
52
+ "ipProtocol"=>"tcp",
53
+ "fromPort"=>22,
54
+ "toPort"=>30,
55
+ "groups"=>[],
56
+ "ipRanges"=>[ { "cidrIp"=>"0.0.0.0/0" } ]
57
+ }
58
+ ]
59
+ end
60
+ end
61
+ it "should open a range of UDP ports" do
62
+ capture_stdout do
63
+ ports = { ssh: { protocol: "udp", ports: (60000..600050) } }
64
+ subject.create_security_group("sg-range-udp-name", "sg-range-udp-name", ports)
65
+ created_sg = fog_compute.security_groups.get("sg-range-udp-name")
66
+ created_sg.ip_permissions.should == [
67
+ {
68
+ "ipProtocol"=>"udp",
69
+ "fromPort"=>60000,
70
+ "toPort"=>600050,
71
+ "groups"=>[],
72
+ "ipRanges"=>[ { "cidrIp"=>"0.0.0.0/0" } ]
73
+ }
74
+ ]
75
+ end
76
+ end
77
+ it "should open a range of ICMP ports" do
78
+ capture_stdout do
79
+ ports = { ping: { protocol: "icmp", ports: (3..4) } }
80
+ subject.create_security_group("sg-range-icmp-name", "sg-range-icmp-name", ports)
81
+ created_sg = fog_compute.security_groups.get("sg-range-icmp-name")
82
+ created_sg.ip_permissions.should == [
83
+ {
84
+ "ipProtocol"=>"icmp",
85
+ "fromPort"=>3,
86
+ "toPort"=>4,
87
+ "groups"=>[],
88
+ "ipRanges"=>[ { "cidrIp"=>"0.0.0.0/0" } ]
89
+ }
90
+ ]
91
+ end
92
+ end
93
+ it "should open not open ports if they are already open" do
94
+ capture_stdout do
95
+ subject.create_security_group("sg2", "", { ssh: { protocol: "udp", ports: (60000..600050) } })
96
+ subject.create_security_group("sg2", "", { ssh: { protocol: "udp", ports: (60010..600040) } })
97
+ subject.create_security_group("sg2", "", { ssh: { protocol: "udp", ports: (60000..600050) } })
98
+ created_sg = fog_compute.security_groups.get("sg2")
99
+ created_sg.ip_permissions.should == [
100
+ {
101
+ "ipProtocol"=>"udp",
102
+ "fromPort"=>60000,
103
+ "toPort"=>600050,
104
+ "groups"=>[],
105
+ "ipRanges"=>[ { "cidrIp"=>"0.0.0.0/0" } ]
106
+ }
107
+ ]
108
+ end
109
+ end
110
+ it "should open ports even if they are already open for a different protocol" do
111
+ capture_stdout do
112
+ subject.create_security_group("sg3", "", { ssh: { protocol: "udp", ports: (60000..600050) } })
113
+ subject.create_security_group("sg3", "", { ssh: { protocol: "tcp", ports: (60000..600050) } })
114
+ created_sg = fog_compute.security_groups.get("sg3")
115
+ created_sg.ip_permissions.should == [
116
+ {
117
+ "ipProtocol"=>"udp",
118
+ "fromPort"=>60000,
119
+ "toPort"=>600050,
120
+ "groups"=>[],
121
+ "ipRanges"=>[ { "cidrIp"=>"0.0.0.0/0" } ]
122
+ },
123
+ {
124
+ "ipProtocol"=>"tcp",
125
+ "fromPort"=>60000,
126
+ "toPort"=>600050,
127
+ "groups"=>[],
128
+ "ipRanges"=>[ { "cidrIp"=>"0.0.0.0/0" } ]
129
+ }
130
+ ]
131
+ end
132
+ end
133
+ it "should open ports even if they are already open for a different ip_range" do
134
+ capture_stdout do
135
+ default_ports = {
136
+ all_internal_tcp: { protocol: "tcp", ip_range: "1.1.1.1/32", ports: (0..65535) }
137
+ }
138
+ subject.create_security_group("sg6", "sg6", default_ports)
139
+ subject.create_security_group("sg6", "sg6", { mosh: { protocol: "tcp", ports: (15..30) } })
140
+ created_sg = fog_compute.security_groups.get("sg6")
141
+ created_sg.ip_permissions.should == [
142
+ {
143
+ "ipProtocol"=>"tcp",
144
+ "fromPort"=>0,
145
+ "toPort"=>65535,
146
+ "groups"=>[],
147
+ "ipRanges"=>[ { "cidrIp"=>"1.1.1.1/32" } ]
148
+ },
149
+ {
150
+ "ipProtocol"=>"tcp",
151
+ "fromPort"=>15,
152
+ "toPort"=>30,
153
+ "groups"=>[],
154
+ "ipRanges"=>[ { "cidrIp"=>"0.0.0.0/0" } ]
155
+ }
156
+ ]
157
+ end
158
+ end
159
+ it "should open ports on the default sg" do
160
+ capture_stdout do
161
+ subject.create_security_group("default", "default", { mosh: { protocol: "tcp", ports: (15..30) } })
162
+ created_sg = fog_compute.security_groups.get("default")
163
+ expected_rule = {
164
+ "ipProtocol"=>"tcp",
165
+ "fromPort"=>15,
166
+ "toPort"=>30,
167
+ "groups"=>[],
168
+ "ipRanges"=>[ { "cidrIp"=>"0.0.0.0/0" } ]
169
+ }
170
+ created_sg.ip_permissions.should include expected_rule
171
+ end
172
+ end
173
+ #AWS allows overlapping port ranges, and it makes it easier to see the separate "rules" that were added
174
+ it "should create overlapping port ranges" do
175
+ capture_stdout do
176
+ subject.create_security_group("sg4", "", { ssh: { protocol: "udp", ports: (10..20) } })
177
+ subject.create_security_group("sg4", "", { ssh: { protocol: "udp", ports: (15..30) } })
178
+ created_sg = fog_compute.security_groups.get("sg4")
179
+ created_sg.ip_permissions.should == [
180
+ {
181
+ "ipProtocol"=>"udp",
182
+ "fromPort"=>10,
183
+ "toPort"=>20,
184
+ "groups"=>[],
185
+ "ipRanges"=>[ { "cidrIp"=>"0.0.0.0/0" } ]
186
+ },
187
+ {
188
+ "ipProtocol"=>"udp",
189
+ "fromPort"=>15,
190
+ "toPort"=>30,
191
+ "groups"=>[],
192
+ "ipRanges"=>[ { "cidrIp"=>"0.0.0.0/0" } ]
193
+ }
194
+ ]
195
+ end
196
+ end
197
+ end
198
+ end
199
+ end