inception 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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