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 "vagrant"
2
+
3
+ module VagrantPlugins
4
+ module ProfitBricks
5
+ module Errors
6
+ class VagrantProfitBricksError < Vagrant::Errors::VagrantError
7
+ error_namespace("vagrant_profitbricks.errors")
8
+ end
9
+
10
+ class CreateBadState < VagrantProfitBricksError
11
+ error_key(:create_bad_state)
12
+ end
13
+
14
+ class NoMatchingFlavor < VagrantProfitBricksError
15
+ error_key(:no_matching_flavor)
16
+ end
17
+
18
+ class NoMatchingImage < VagrantProfitBricksError
19
+ error_key(:no_matching_image)
20
+ end
21
+
22
+ class NoMatchingDatacenter < VagrantProfitBricksError
23
+ error_key(:no_matching_datacenter)
24
+ end
25
+
26
+ class RsyncError < VagrantProfitBricksError
27
+ error_key(:rsync_error)
28
+ end
29
+
30
+ class ImageOrLicenceTypeMustBeProvided < VagrantProfitBricksError
31
+ error_key(:image_or_licence_type_must_be_provided)
32
+ end
33
+
34
+ class ImagePasswordOrSSHKeysMustBeProvided < VagrantProfitBricksError
35
+ error_key(:image_password_or_ssh_keys_must_be_provided)
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,44 @@
1
+ begin
2
+ require "vagrant"
3
+ rescue LoadError
4
+ raise "The ProfitBricks Cloud provider must be run within Vagrant."
5
+ end
6
+
7
+ # This is a sanity check to make sure no one is attempting to install
8
+ # this into an early Vagrant version.
9
+ if Vagrant::VERSION < "1.1.0"
10
+ raise "ProfitBricks Cloud provider is only compatible with Vagrant 1.1+"
11
+ end
12
+
13
+ module VagrantPlugins
14
+ module ProfitBricks
15
+ class Plugin < Vagrant.plugin("2")
16
+ name "ProfitBricks Cloud"
17
+ description <<-DESC
18
+ This plugin enables Vagrant to manage machines in the ProfitBricks Cloud.
19
+ DESC
20
+
21
+ config(:profitbricks, :provider) do
22
+ require_relative "config"
23
+ Config
24
+ end
25
+
26
+ provider(:profitbricks, { :box_optional => true, parallel: true}) do
27
+ # Setup localization and logging
28
+ ProfitBricks.init_i18n
29
+ ProfitBricks.init_logging
30
+
31
+ # Load the actual provider
32
+ require_relative "provider"
33
+ Provider
34
+ end
35
+
36
+ command('profitbricks') do
37
+ ProfitBricks.init_i18n
38
+
39
+ require_relative "command/root"
40
+ Command::Root
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,50 @@
1
+ require "vagrant"
2
+
3
+ require "vagrant-profitbricks/action"
4
+
5
+ module VagrantPlugins
6
+ module ProfitBricks
7
+ class Provider < Vagrant.plugin("2", :provider)
8
+ def initialize(machine)
9
+ @machine = machine
10
+ end
11
+
12
+ def action(name)
13
+ # Attempt to get the action method from the Action class if it
14
+ # exists, otherwise return nil to show that we don't support the
15
+ # given action.
16
+ action_method = "action_#{name}"
17
+ return Action.send(action_method) if Action.respond_to?(action_method)
18
+ nil
19
+ end
20
+
21
+ def ssh_info
22
+ # Run a custom action called "read_ssh_info" which does what it
23
+ # says and puts the resulting SSH info into the `:machine_ssh_info`
24
+ # key in the environment.
25
+ env = @machine.action("read_ssh_info")
26
+ env[:machine_ssh_info]
27
+ end
28
+
29
+ def state
30
+ # Run a custom action we define called "read_state" which does
31
+ # what it says. It puts the state in the `:machine_state_id`
32
+ # key in the environment.
33
+ env = @machine.action("read_state")
34
+
35
+ state_id = env[:machine_state_id]
36
+
37
+ # Get the short and long description
38
+ short = I18n.t("vagrant_profitbricks.states.short_#{state_id}")
39
+ long = I18n.t("vagrant_profitbricks.states.long_#{state_id}")
40
+
41
+ # Return the MachineState object
42
+ Vagrant::MachineState.new(state_id, short, long)
43
+ end
44
+
45
+ def to_s
46
+ "ProfitBricks Cloud"
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,5 @@
1
+ module VagrantPlugins
2
+ module ProfitBricks
3
+ VERSION = "1.0.0"
4
+ end
5
+ end
data/locales/en.yml ADDED
@@ -0,0 +1,124 @@
1
+ en:
2
+ vagrant_profitbricks:
3
+ already_created: |-
4
+ The server is already created.
5
+ creating_image: |-
6
+ Creating image...
7
+ deleting_server: |-
8
+ Deleting server and attached volumes...
9
+ finding_flavor: |-
10
+ Finding flavor for server...
11
+ finding_image: |-
12
+ Finding image for server...
13
+ creating_volume: |-
14
+ Creating volume for server...
15
+ creating_server: |-
16
+ Creating server...
17
+ attaching_volume: |-
18
+ Attaching volume to server...
19
+ creating_interface: |-
20
+ Creating nic interface with internet access...
21
+ image_ready: |-
22
+ Image created
23
+ launching_server: |-
24
+ Launching a server with the following settings...
25
+ not_created: |-
26
+ The server hasn't been created yet. Run `vagrant up` first.
27
+ ready: |-
28
+ The server is ready!
29
+ rsync_folder: |-
30
+ Rsyncing folder: %{hostpath} => %{guestpath}
31
+ waiting_for_build: |-
32
+ Waiting for the server to be built...
33
+ waiting_for_profitbricks_connect: |-
34
+ Waiting for ProfitBricks Connection to complete...
35
+ waiting_for_communicator: |-
36
+ Waiting for %{communicator} to become available at %{address}...
37
+ warn_insecure_ssh: |-
38
+ Warning! By not specifying a custom public/private keypair,
39
+ Vagrant is defaulting to use the insecure keypair that ships with
40
+ Vagrant. While this isn't much of a big deal for local development,
41
+ this is quite insecure for remote servers. Please specify a custom
42
+ public/private keypair.
43
+ warn_insecure_winrm: |-
44
+ Warning! Vagrant is using plaintext communication for WinRM. While
45
+ this isn't much of a big deal for local development, this is quite
46
+ insecure for remote servers. Please configure WinRM to use SSL.
47
+ will_not_destroy: |-
48
+ The server will not be deleted.
49
+ sync_folders: |-
50
+ Profitbricks support for Vagrant 1.3 has been deprecated. Please
51
+ upgrade to the latest version of vagrant for continued support.
52
+ creating_datacenter: |-
53
+ Creating datacenter %{name}, in location %{location}.
54
+ config:
55
+ password_required: |-
56
+ An API password is required.
57
+ public_key_not_found: |-
58
+ The public key file could not be found. Please make sure
59
+ you specified a valid path.
60
+ username_required: |-
61
+ A username is required.
62
+ datacenter_required: |-
63
+ A datacenter id or name (can be regex) is required.
64
+ invalid_uri: |-
65
+ The value for %{key} is not a valid URI: %{uri}
66
+ metadata_must_be_hash: |-
67
+ Metadata must be a hash.
68
+ command:
69
+ available_subcommands: |-
70
+ Available subcommands:
71
+ help_subcommands: |-
72
+ For help on any individual subcommand run
73
+ synopsis: |-
74
+ ProfitBricks provider specific commands
75
+ errors:
76
+ server_not_found: |-
77
+ Vagrant was unable to find the ProfitBricks server used for your vagrant machine.
78
+ It might be already deleted.
79
+ create_bad_state: |-
80
+ While creating the server, it transitioned to an unexpected
81
+ state: '%{state}', instead of properly booting. Run `vagrant status`
82
+ to find out what can be done about this state, or `vagrant destroy`
83
+ if you want to start over.
84
+ no_matching_flavor: |-
85
+ No matching flavor was found! Please check your flavor setting
86
+ to make sure you have a valid flavor chosen.
87
+ no_matching_image: |-
88
+ No matching image was found! Please check your image setting to
89
+ make sure you have a valid image chosen.
90
+ no_matching_datacenter: |-
91
+ No matching datacenter was found! Please check your datacenter id to
92
+ make sure you have a valid id chosen.
93
+ rsync_error: |-
94
+ There was an error when attemping to rsync a share folder.
95
+ Please inspect the error message below for more info.
96
+
97
+ Host path: %{hostpath}
98
+ Guest path: %{guestpath}
99
+ Error: %{stderr}
100
+
101
+ states:
102
+ short_active: |-
103
+ active
104
+ long_active: |-
105
+ The server is up and running. Run `vagrant ssh` to access it.
106
+ short_build: |-
107
+ building
108
+ long_build: |-
109
+ The server is currently being built. You must wait for this to
110
+ complete before you can access it. You can delete the server, however,
111
+ by running `vagrant destroy`.
112
+ short_error: |-
113
+ error
114
+ long_error: |-
115
+ The server is in an erroneous state. Contact ProfitBricks support
116
+ or destroy the machine with `vagrant destroy`.
117
+ short_not_created: |-
118
+ not created
119
+ long_not_created: |-
120
+ The server is not created. Run `vagrant up` to create it.
121
+ long_available: |-
122
+ The server is up and running. Run `vagrant ssh` to access it.
123
+ short_available: |-
124
+ available
@@ -0,0 +1,20 @@
1
+ if ENV['COVERAGE'] != 'false'
2
+ require 'simplecov'
3
+ require 'coveralls'
4
+ SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter[
5
+ SimpleCov::Formatter::HTMLFormatter,
6
+ Coveralls::SimpleCov::Formatter
7
+ ]
8
+ SimpleCov.start
9
+
10
+ # Normally classes are lazily loaded, so any class without a test
11
+ # is missing from the report. This ensures they show up so we can
12
+ # see uncovered methods.
13
+ require 'vagrant'
14
+ Dir["lib/**/*.rb"].each do|file|
15
+ require_string = file.match(/lib\/(.*)\.rb/)[1]
16
+ require require_string
17
+ end
18
+ end
19
+
20
+ require 'fog/profitbricks'
@@ -0,0 +1,48 @@
1
+ require 'spec_helper'
2
+ require "vagrant-profitbricks/action/list_flavors"
3
+
4
+ describe VagrantPlugins::ProfitBricks::Action::ListFlavors do
5
+ let(:app) { lambda { |env| } }
6
+ let(:ui) { Vagrant::UI::Silent.new }
7
+ let(:flavors) {
8
+ Fog.mock!
9
+ Fog::Compute.new({
10
+ :provider => :profitbricks,
11
+ :profitbricks_username => 'anything',
12
+ :profitbricks_password => 'anything'
13
+ }).flavors
14
+ }
15
+ let(:compute_connection) { double('fog connection') }
16
+ let(:env) do
17
+ {
18
+ :profitbricks_compute => compute_connection,
19
+ :ui => ui
20
+ }
21
+ end
22
+
23
+ subject(:action) { described_class.new(app, env) }
24
+
25
+ before do
26
+ allow(compute_connection).to receive(:flavors).and_return flavors
27
+ end
28
+
29
+ it 'get flavors from Fog' do
30
+ expect(compute_connection).to receive(:flavors).and_return flavors
31
+ action.call(env)
32
+ end
33
+
34
+ it 'writes a sorted, formatted flavor table to Vagrant::UI' do
35
+ header_line = '%-36s %s' % ['Flavor ID', 'Flavor Name']
36
+ expect(ui).to receive(:info).with(header_line)
37
+ flavors.sort_by(&:id).each do |flavor|
38
+ formatted_line = '%-36s %s' % [flavor.id, flavor.name]
39
+ expect(ui).to receive(:info).with formatted_line
40
+ end
41
+ action.call(env)
42
+ end
43
+
44
+ it 'continues the middleware chain' do
45
+ expect(app).to receive(:call).with(env)
46
+ action.call(env)
47
+ end
48
+ end
@@ -0,0 +1,48 @@
1
+ require 'spec_helper'
2
+ require "vagrant-profitbricks/action/list_images"
3
+
4
+ describe VagrantPlugins::ProfitBricks::Action::ListImages do
5
+ let(:app) { lambda { |env| } }
6
+ let(:ui) { Vagrant::UI::Silent.new }
7
+ let(:images) {
8
+ Fog.mock!
9
+ Fog::Compute.new({
10
+ :provider => :profitbricks,
11
+ :profitbricks_password => 'anything',
12
+ :profitbricks_username => 'anything',
13
+ }).images
14
+ }
15
+ let(:compute_connection) { double('fog connection') }
16
+ let(:env) do
17
+ {
18
+ :profitbricks_compute => compute_connection,
19
+ :ui => ui
20
+ }
21
+ end
22
+
23
+ subject(:action) { described_class.new(app, env) }
24
+
25
+ before do
26
+ allow(compute_connection).to receive(:images).and_return images
27
+ end
28
+
29
+ it 'get images from Fog' do
30
+ expect(compute_connection).to receive(:images).and_return images
31
+ action.call(env)
32
+ end
33
+
34
+ it 'writes a sorted, formatted image table to Vagrant::UI' do
35
+ header_line = '%-36s %s' % ['Image ID', 'Image Name']
36
+ expect(ui).to receive(:info).with(header_line)
37
+ images.sort_by(&:name).each do |image|
38
+ formatted_line = '%-36s %s' % [image.id.to_s, image.name]
39
+ expect(ui).to receive(:info).with formatted_line
40
+ end
41
+ action.call(env)
42
+ end
43
+
44
+ it 'continues the middleware chain' do
45
+ expect(app).to receive(:call).with(env)
46
+ action.call(env)
47
+ end
48
+ end
@@ -0,0 +1,150 @@
1
+ require 'spec_helper'
2
+ require "vagrant-profitbricks/config"
3
+
4
+ describe VagrantPlugins::ProfitBricks::Config do
5
+ describe "defaults" do
6
+ let(:vagrant_public_key) { Vagrant.source_root.join("keys/vagrant.pub") }
7
+
8
+ subject do
9
+ super().tap do |o|
10
+ o.finalize!
11
+ end
12
+ end
13
+
14
+ its(:password) { should be_nil }
15
+ its(:profitbricks_url) { should be_nil }
16
+ its(:profitbricks_cores) { should eq(1) }
17
+ its(:profitbricks_ram) { should eq(256) }
18
+ its(:image) { should eq(/ubuntu/) }
19
+ its(:server_name) { should be_nil }
20
+ its(:username) { should be_nil }
21
+ its(:networks) { should be_nil }
22
+ its(:admin_password) { should be_nil }
23
+ end
24
+
25
+ describe "overriding defaults" do
26
+ [:password,
27
+ :profitbricks_url,
28
+ :profitbricks_cores,
29
+ :profitbricks_ram,
30
+ :image,
31
+ :server_name,
32
+ :username,
33
+ :admin_password
34
+ ].each do |attribute|
35
+ it "should not default #{attribute} if overridden" do
36
+ subject.send("#{attribute}=".to_sym, "foo")
37
+ subject.finalize!
38
+ subject.send(attribute).should == "foo"
39
+ end
40
+ end
41
+
42
+ it "should not default networks if overridden" do
43
+ net_id = "deadbeef-0000-0000-0000-000000000000"
44
+ subject.send(:network, net_id)
45
+ subject.finalize!
46
+ subject.send(:networks).should include(net_id)
47
+ subject.send(:networks).should include(VagrantPlugins::ProfitBricks::Config::PUBLIC_NET_ID)
48
+ subject.send(:networks).should include(VagrantPlugins::ProfitBricks::Config::SERVICE_NET_ID)
49
+ end
50
+
51
+ it "should not default rsync_includes if overridden" do
52
+ inc = "core"
53
+ subject.send(:rsync_include, inc)
54
+ subject.finalize!
55
+ subject.send(:rsync_includes).should include(inc)
56
+ end
57
+ end
58
+
59
+ describe "validation" do
60
+ let(:machine) { double("machine") }
61
+ let(:validation_errors) { subject.validate(machine)['ProfitBricks Provider'] }
62
+ let(:error_message) { double("error message") }
63
+
64
+ before(:each) do
65
+ machine.stub_chain(:env, :root_path).and_return '/'
66
+ subject.username = 'foo'
67
+ subject.password = 'bar'
68
+ subject.datacenter_id = 'dc'
69
+ end
70
+
71
+ subject do
72
+ super().tap do |o|
73
+ o.finalize!
74
+ end
75
+ end
76
+
77
+ context "with invalid key" do
78
+ it "should raise an error" do
79
+ subject.nonsense1 = true
80
+ subject.nonsense2 = false
81
+ I18n.should_receive(:t).with('vagrant.config.common.bad_field',
82
+ { :fields => 'nonsense1, nonsense2' })
83
+ .and_return error_message
84
+ validation_errors.first.should == error_message
85
+ end
86
+ end
87
+ context "with good values" do
88
+ it "should validate" do
89
+ validation_errors.should be_empty
90
+ end
91
+ end
92
+
93
+ context "the API password" do
94
+ it "should error if not given" do
95
+ subject.password = nil
96
+ I18n.should_receive(:t).with('vagrant_profitbricks.config.password_required').and_return error_message
97
+ validation_errors.first.should == error_message
98
+ end
99
+ end
100
+
101
+ # context "the public key path" do
102
+ # it "should have errors if the key doesn't exist" do
103
+ # subject.public_key_path = 'missing'
104
+ # I18n.should_receive(:t).with('vagrant_profitbricks.config.public_key_not_found').and_return error_message
105
+ # validation_errors.first.should == error_message
106
+ # end
107
+ # it "should not have errors if the key exists with an absolute path" do
108
+ # subject.public_key_path = File.expand_path 'locales/en.yml', Dir.pwd
109
+ # validation_errors.should be_empty
110
+ # end
111
+ # it "should not have errors if the key exists with a relative path" do
112
+ # machine.stub_chain(:env, :root_path).and_return '.'
113
+ # subject.public_key_path = 'locales/en.yml'
114
+ # validation_errors.should be_empty
115
+ # end
116
+ # end
117
+
118
+ context "the username" do
119
+ it "should error if not given" do
120
+ subject.username = nil
121
+ I18n.should_receive(:t).with('vagrant_profitbricks.config.username_required').and_return error_message
122
+ validation_errors.first.should == error_message
123
+ end
124
+ end
125
+
126
+ [:profitbricks_url].each do |url|
127
+ context "the #{url}" do
128
+ it "should not validate if the URL is invalid" do
129
+ subject.send "#{url}=", 'baz'
130
+ I18n.should_receive(:t).with('vagrant_profitbricks.config.invalid_uri', {:key => url, :uri => 'baz'}).and_return error_message
131
+ validation_errors.first.should == error_message
132
+ end
133
+ end
134
+ end
135
+ end
136
+
137
+ describe "network" do
138
+ it "should remove SERVICE_NET_ID if :service_net is detached" do
139
+ subject.send(:network, :service_net, :attached => false)
140
+ subject.send(:networks).should_not include(VagrantPlugins::ProfitBricks::Config::SERVICE_NET_ID)
141
+ end
142
+
143
+ it "should not allow duplicate networks" do
144
+ net_id = "deadbeef-0000-0000-0000-000000000000"
145
+ subject.send(:network, net_id)
146
+ subject.send(:network, net_id)
147
+ subject.send(:networks).count(net_id).should == 1
148
+ end
149
+ end
150
+ end