vagrant-profitbricks 1.0.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 (57) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +25 -0
  3. data/Appraisals +35 -0
  4. data/CHANGELOG.md +3 -0
  5. data/Gemfile +18 -0
  6. data/LICENSE +202 -0
  7. data/README.md +268 -0
  8. data/RELEASE.md +15 -0
  9. data/Rakefile +21 -0
  10. data/Vagrantfile +30 -0
  11. data/bootstrap.cmd +16 -0
  12. data/dummy.box +36 -0
  13. data/example_box/README.md +13 -0
  14. data/example_box/metadata.json +3 -0
  15. data/features/provision.feature +36 -0
  16. data/features/steps/sdk_steps.rb +13 -0
  17. data/features/steps/server_steps.rb +25 -0
  18. data/features/support/env.rb +35 -0
  19. data/features/support/fog_mock.rb +17 -0
  20. data/features/vagrant-profitbricks.feature +66 -0
  21. data/lib/vagrant-profitbricks.rb +53 -0
  22. data/lib/vagrant-profitbricks/action.rb +166 -0
  23. data/lib/vagrant-profitbricks/action/connect_profitbricks.rb +39 -0
  24. data/lib/vagrant-profitbricks/action/create_image.rb +53 -0
  25. data/lib/vagrant-profitbricks/action/create_server.rb +194 -0
  26. data/lib/vagrant-profitbricks/action/delete_server.rb +43 -0
  27. data/lib/vagrant-profitbricks/action/is_created.rb +16 -0
  28. data/lib/vagrant-profitbricks/action/list_flavors.rb +20 -0
  29. data/lib/vagrant-profitbricks/action/list_images.rb +20 -0
  30. data/lib/vagrant-profitbricks/action/list_keypairs.rb +20 -0
  31. data/lib/vagrant-profitbricks/action/list_networks.rb +20 -0
  32. data/lib/vagrant-profitbricks/action/list_servers.rb +21 -0
  33. data/lib/vagrant-profitbricks/action/message_already_created.rb +16 -0
  34. data/lib/vagrant-profitbricks/action/message_not_created.rb +16 -0
  35. data/lib/vagrant-profitbricks/action/read_ssh_info.rb +49 -0
  36. data/lib/vagrant-profitbricks/action/read_state.rb +39 -0
  37. data/lib/vagrant-profitbricks/action/run_init_script.rb +28 -0
  38. data/lib/vagrant-profitbricks/command/create_image.rb +21 -0
  39. data/lib/vagrant-profitbricks/command/flavors.rb +21 -0
  40. data/lib/vagrant-profitbricks/command/images.rb +33 -0
  41. data/lib/vagrant-profitbricks/command/keypairs.rb +21 -0
  42. data/lib/vagrant-profitbricks/command/list_images.rb +21 -0
  43. data/lib/vagrant-profitbricks/command/networks.rb +21 -0
  44. data/lib/vagrant-profitbricks/command/root.rb +79 -0
  45. data/lib/vagrant-profitbricks/command/servers.rb +21 -0
  46. data/lib/vagrant-profitbricks/config.rb +152 -0
  47. data/lib/vagrant-profitbricks/errors.rb +39 -0
  48. data/lib/vagrant-profitbricks/plugin.rb +44 -0
  49. data/lib/vagrant-profitbricks/provider.rb +50 -0
  50. data/lib/vagrant-profitbricks/version.rb +5 -0
  51. data/locales/en.yml +124 -0
  52. data/spec/spec_helper.rb +20 -0
  53. data/spec/vagrant-profitbricks/actions/list_flavors_spec.rb +48 -0
  54. data/spec/vagrant-profitbricks/actions/list_images_spec.rb +48 -0
  55. data/spec/vagrant-profitbricks/config_spec.rb +150 -0
  56. data/vagrant-profitbricks.gemspec +25 -0
  57. metadata +164 -0
@@ -0,0 +1,39 @@
1
+ require "fog/profitbricks"
2
+ require "log4r"
3
+
4
+ module VagrantPlugins
5
+ module ProfitBricks
6
+ module Action
7
+ # This action connects to ProfitBricks and verifies that the credentials work
8
+ class ConnectProfitBricks
9
+ def initialize(app, env)
10
+ @app = app
11
+ @logger = Log4r::Logger.new("vagrant_profitbricks::action::connect_profitbricks")
12
+ end
13
+
14
+ def call(env)
15
+ # Get the configs
16
+ config = env[:machine].provider_config
17
+ password = config.password
18
+ username = config.username
19
+
20
+ params = {
21
+ :provider => 'ProfitBricks',
22
+ :profitbricks_password => password,
23
+ :profitbricks_username => username
24
+ }
25
+
26
+ if config.profitbricks_url
27
+ @logger.info("Connecting to ProfitBricks profitbricks_url...")
28
+ params[:profitbricks_url] = config.profitbricks_url
29
+ end
30
+
31
+ env[:profitbricks_compute] = Fog::Compute.new params
32
+ @logger.info("env[:profitbricks_compute]: " + env[:profitbricks_compute].to_s)
33
+
34
+ @app.call(env)
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,53 @@
1
+ require "fog/profitbricks"
2
+ require "log4r"
3
+
4
+ require 'vagrant/util/retryable'
5
+
6
+ module VagrantPlugins
7
+ module ProfitBricks
8
+ module Action
9
+ # Creates an Image
10
+ class CreateImage
11
+ include Vagrant::Util::Retryable
12
+
13
+ attr_reader :env
14
+
15
+ def initialize(app, env)
16
+ @app, @env = app, env
17
+ end
18
+
19
+ def call(env)
20
+ env[:ui].info(I18n.t("vagrant_profitbricks.creating_image"))
21
+
22
+ server = env[:profitbricks_compute].servers.get(env[:machine].id)
23
+
24
+ config = env[:machine].provider_config
25
+ image_name = config.server_name || env[:machine].name
26
+
27
+ image = server.create_image(image_name)
28
+
29
+ retryable(:on => Fog::Errors::TimeoutError, :tries => 200) do
30
+ # If we're interrupted don't worry about waiting
31
+ next if env[:interrupted]
32
+
33
+ env[:ui].clear_line
34
+ env[:ui].report_progress(image.progress, 100, false)
35
+
36
+ begin
37
+ image.wait_for(5) { ready? }
38
+ rescue RuntimeError => e
39
+ # If we don't have an error about a state transition, then
40
+ # we just move on.
41
+ raise if e.message !~ /should have transitioned/
42
+ raise Errors::CreateBadState, :state => server.state
43
+ end
44
+ end
45
+
46
+ env[:ui].info(I18n.t("vagrant_profitbricks.image_ready"))
47
+
48
+ @app.call(env)
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,194 @@
1
+ require "fog/profitbricks"
2
+ require "log4r"
3
+
4
+ require 'vagrant/util/retryable'
5
+
6
+ module VagrantPlugins
7
+ module ProfitBricks
8
+ module Action
9
+ # This creates the ProfitBricks server.
10
+ class CreateServer
11
+ include Vagrant::Util::Retryable
12
+
13
+ def initialize(app, env)
14
+ @app = app
15
+ @compute = env[:profitbricks_compute]
16
+ @logger = Log4r::Logger.new("vagrant_profitbricks::action::create_server")
17
+ end
18
+
19
+ def call(env)
20
+ # Get the ProfitBricks configs
21
+ config = env[:machine].provider_config
22
+ machine_config = env[:machine].config
23
+ begin
24
+ communicator = machine_config.vm.communicator ||= :ssh
25
+ rescue NoMethodError
26
+ communicator = :ssh
27
+ end
28
+
29
+ # Figure out the Data Center ID,
30
+ datacenter = find_matching(@compute.datacenters, config.datacenter_id)
31
+ # in case datacenter is not found try to create new one.
32
+ # New datacenter is created if provided with name that is not regex or id
33
+ if config.datacenter_id && !datacenter && !config.datacenter_id.is_a?(Regexp) && (config.datacenter_id =~ /[a-fA-F0-9]{8}(-[a-fA-F0-9]{4}){3}-[a-fA-F0-9]{12}/).nil?
34
+ env[:ui].info(I18n.t("vagrant_profitbricks.creating_datacenter", :name => config.datacenter_id, :location => config.location))
35
+ datacenter = @compute.datacenters.create(:name => config.datacenter_id, :location => config.location)
36
+
37
+ end
38
+ raise Errors::NoMatchingDatacenter if !datacenter
39
+
40
+ # Find the flavor
41
+ if config.flavor
42
+ env[:ui].info(I18n.t("vagrant_profitbricks.finding_flavor"))
43
+ flavor = find_matching(@compute.flavors, config.flavor)
44
+ raise Errors::NoMatchingFlavor if !flavor
45
+ end
46
+
47
+ # Find the image
48
+ env[:ui].info(I18n.t("vagrant_profitbricks.finding_image"))
49
+ env[:ui].info(I18n.t("config.image: #{config.image}"))
50
+ env[:ui].info(I18n.t("datacenter.location: #{datacenter.location}"))
51
+ images = @compute.images.all
52
+ image = find_matching_image(images, config.image, datacenter.location)
53
+
54
+ # Figure out the name for the server
55
+ server_name = config.server_name || env[:machine].name
56
+
57
+ # Prepare volume values
58
+ vol_name = server_name.to_s + "_volume" if server_name
59
+ vol_size = config.profitbricks_volume_size ? config.profitbricks_volume_size : 5
60
+ vol_image = image.id if image
61
+ vol_type = config.volume_type
62
+ vol_licence_type = config.volume_licence_type if config.volume_licence_type
63
+ vol_ssh_keys = config.volume_ssh_keys if config.volume_ssh_keys
64
+ vol_availability_zone = config.volume_availability_zone
65
+ vol_image_password = config.image_password if config.image_password
66
+
67
+ volume = {
68
+ :name => vol_name,
69
+ :size => vol_size,
70
+ :type => vol_type,
71
+ :availability_zone => vol_availability_zone
72
+ }
73
+
74
+ if vol_image
75
+ volume[:image] = vol_image
76
+ elsif vol_licence_type
77
+ volume[:licence_type] = vol_licence_type
78
+ else
79
+ raise Errors::ImageOrLicenceTypeMustBeProvided
80
+ end
81
+
82
+ if vol_ssh_keys
83
+ volume[:ssh_keys] = vol_ssh_keys
84
+ elsif vol_image_password
85
+ volume[:image_password] = vol_image_password
86
+ else
87
+ raise Errors::ImageOrLicenceTypeMustBeProvided
88
+ end
89
+
90
+
91
+
92
+ # create LAN if it does not exist, and create a NIC on that LAN
93
+ lan_id = config.lan_id
94
+ lan = @compute.lans.get(datacenter.id, config.lan_id)
95
+
96
+ if !lan || lan.id.end_with?('/lans')
97
+ lan_name = server_name.to_s + "_lan" if server_name
98
+ lan = @compute.lans.create(:datacenter_id => datacenter.id, :name => lan_name, :public => true)
99
+ lan_id = lan.id
100
+ end
101
+
102
+ nic_name = server_name.to_s + "_nic" if server_name
103
+ nic = {
104
+ :name => nic_name,
105
+ :lan => lan_id,
106
+ :dhcp => true,
107
+ :nat => config.nat
108
+ }
109
+
110
+ # Output the settings we're going to use to the user
111
+ env[:ui].info(I18n.t("vagrant_profitbricks.launching_server"))
112
+ env[:ui].info(" -- Data Center ID: #{datacenter.id}")
113
+ env[:ui].info(" -- Name: #{server_name}")
114
+ env[:ui].info(" -- Volume Name: #{vol_name}")
115
+ env[:ui].info(" -- Image: #{image.name}") if image
116
+
117
+ server_cores = nil
118
+ server_ram = nil
119
+
120
+ if flavor
121
+ env[:ui].info(" -- Flavor: #{flavor.name}")
122
+ server_cores = flavor.cores
123
+ server_ram = flavor.ram
124
+ elsif config.profitbricks_ram && config.profitbricks_cores
125
+ env[:ui].info(" -- Server cores: #{config.profitbricks_cores}")
126
+ env[:ui].info(" -- Server ram: #{config.profitbricks_ram } MB")
127
+ server_cores = config.profitbricks_cores
128
+ server_ram = config.profitbricks_ram
129
+ else
130
+ raise Errors::NoMatchingFlavor
131
+ end
132
+
133
+ # Create the server
134
+ env[:ui].info(I18n.t("vagrant_profitbricks.creating_server"))
135
+ server = @compute.servers.create(:datacenter_id => datacenter.id,
136
+ :name => server_name,
137
+ :cores => server_cores,
138
+ :ram => server_ram,
139
+ :cpu_family => config.cpu_family,
140
+ :volumes => [volume],
141
+ :nics => [nic]
142
+ )
143
+ server.wait_for { ready? }
144
+
145
+ # Store the ID right away so we can track it
146
+ env[:machine].id = server.id
147
+
148
+ env[:ui].info(I18n.t("vagrant_profitbricks.ready"))
149
+ @app.call(env)
150
+ end
151
+
152
+ protected
153
+
154
+ # This method finds a matching _thing_ in a collection of
155
+ # _things_. This works matching if the ID or NAME equals to
156
+ # `name`. Or, if `name` is a regexp, a partial match is chosen
157
+ # as well.
158
+ def find_matching(collection, name)
159
+ item = collection.find do |single|
160
+ single.id == name ||
161
+ single.name == name ||
162
+ (name.is_a?(Regexp) && name =~ single.name)
163
+ end
164
+
165
+ item
166
+ end
167
+
168
+ # This method finds a matching _thing_ in a collection of
169
+ # _things_. This works matching if the ID or NAME equals to
170
+ # `name`. Or, if `name` is a regexp, a partial match is chosen
171
+ # as well.
172
+ def find_matching_image(collection, name, location)
173
+ location = location ? location : 'us/las'
174
+ item = collection.find do |image|
175
+ image.location == location &&
176
+ image.image_type == 'HDD' &&
177
+ (image.name == name || image.id == name ||
178
+ (name.is_a?(Regexp) && image.name =~ name))
179
+ end
180
+
181
+ item
182
+ end
183
+
184
+ # Ported from Vagrant::UI, but scoped to the machine's UI
185
+ def report_server_progress(machine, progress)
186
+ machine.ui.clear_line
187
+ line = "Progress: #{progress}"
188
+
189
+ machine.ui.info(line, new_line: false)
190
+ end
191
+ end
192
+ end
193
+ end
194
+ end
@@ -0,0 +1,43 @@
1
+ require "log4r"
2
+
3
+ module VagrantPlugins
4
+ module ProfitBricks
5
+ module Action
6
+ # This deletes the running server, if there is one.
7
+ class DeleteServer
8
+ def initialize(app, env)
9
+ @app = app
10
+ @logger = Log4r::Logger.new("vagrant_profitbricks::action::delete_server")
11
+ end
12
+
13
+ def call(env)
14
+ compute = env[:profitbricks_compute]
15
+ server_id = env[:machine].id
16
+ server = begin
17
+ compute.servers.get(server_id)
18
+ rescue
19
+ nil
20
+ end
21
+
22
+ if server.nil?
23
+ @logger.warn "Unable to find server #{server_id}..."
24
+ env[:ui].warn(I18n.t('vagrant_profitbricks.errors.server_not_found'))
25
+ env[:machine].id = nil
26
+ else
27
+ @logger.info "Deleting server #{server_id}..."
28
+ env[:ui].info(I18n.t("vagrant_profitbricks.deleting_server"))
29
+
30
+ server.attached_volumes.each do |vol_info|
31
+ volume = compute.volumes.get(vol_info['id'])
32
+ volume.delete if volume
33
+ end
34
+ server.delete
35
+ env[:machine].id = nil
36
+
37
+ @app.call(env)
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,16 @@
1
+ module VagrantPlugins
2
+ module ProfitBricks
3
+ module Action
4
+ class IsCreated
5
+ def initialize(app, env)
6
+ @app = app
7
+ end
8
+
9
+ def call(env)
10
+ env[:result] = env[:machine].state.id != :not_created
11
+ @app.call(env)
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,20 @@
1
+ module VagrantPlugins
2
+ module ProfitBricks
3
+ module Action
4
+ class ListFlavors
5
+ def initialize(app, env)
6
+ @app = app
7
+ end
8
+
9
+ def call(env)
10
+ compute_service = env[:profitbricks_compute]
11
+ env[:ui].info ('%-36s %s' % ['Flavor ID', 'Flavor Name'])
12
+ compute_service.flavors.sort_by(&:id).each do |flavor|
13
+ env[:ui].info ('%-36s %s' % [flavor.id, flavor.name])
14
+ end
15
+ @app.call(env)
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,20 @@
1
+ module VagrantPlugins
2
+ module ProfitBricks
3
+ module Action
4
+ class ListImages
5
+ def initialize(app, env)
6
+ @app = app
7
+ end
8
+
9
+ def call(env)
10
+ compute_service = env[:profitbricks_compute]
11
+ env[:ui].info ('%-36s %s' % ['Image ID', 'Image Name'])
12
+ compute_service.images.sort_by(&:name).each do |image|
13
+ env[:ui].info ('%-36s %s' % [image.id.to_s, image.name])
14
+ end
15
+ @app.call(env)
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,20 @@
1
+ module VagrantPlugins
2
+ module ProfitBricks
3
+ module Action
4
+ class ListKeyPairs
5
+ def initialize(app, env)
6
+ @app = app
7
+ end
8
+
9
+ def call(env)
10
+ compute_service = env[:profitbricks_compute]
11
+ env[:ui].info ('%s' % ['KeyPair Name'])
12
+ compute_service.key_pairs.sort_by(&:name).each do |keypair|
13
+ env[:ui].info ('%s' % [keypair.name])
14
+ end
15
+ @app.call(env)
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,20 @@
1
+ module VagrantPlugins
2
+ module ProfitBricks
3
+ module Action
4
+ class ListInterfaces
5
+ def initialize(app, env)
6
+ @app = app
7
+ end
8
+
9
+ def call(env)
10
+ compute_service = env[:profitbricks_compute]
11
+ env[:ui].info ('%-36s %-24s %s' % ['Lan Id', 'NIC IPs', 'Internet'])
12
+ compute_service.interfaces.sort_by(&:server_id).each do |network|
13
+ env[:ui].info ('%-36s %-24s %s' % [network.lan_id, network.ips, network.internet_access])
14
+ end
15
+ @app.call(env)
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,21 @@
1
+ module VagrantPlugins
2
+ module ProfitBricks
3
+ module Action
4
+ class ListServers
5
+ def initialize(app, env)
6
+ @app = app
7
+ end
8
+
9
+ def call(env)
10
+ compute_service = env[:profitbricks_compute]
11
+ env[:ui].info ('%-20s %-20s %s' % ['Server Name', 'State', 'IPv4 address'])
12
+ compute_service.servers.sort_by(&:name).each do |server|
13
+ ip = server.interfaces[0] ? server.interfaces[0].ips : "-"
14
+ env[:ui].info ('%-20s %-20s %s' % [server.name, server.state, ip])
15
+ end
16
+ @app.call(env)
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end