vagrant-brightbox 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 (37) hide show
  1. data/.gitignore +15 -0
  2. data/CHANGELOG.md +4 -0
  3. data/Gemfile +10 -0
  4. data/LICENSE +8 -0
  5. data/README.md +217 -0
  6. data/Rakefile +21 -0
  7. data/Vagrantfile.example +105 -0
  8. data/dummy.box +0 -0
  9. data/example_box/README.md +9 -0
  10. data/example_box/metadata.json +3 -0
  11. data/lib/vagrant-brightbox/action/connect_brightbox.rb +53 -0
  12. data/lib/vagrant-brightbox/action/create_server.rb +137 -0
  13. data/lib/vagrant-brightbox/action/delete_server.rb +26 -0
  14. data/lib/vagrant-brightbox/action/forced_halt.rb +27 -0
  15. data/lib/vagrant-brightbox/action/is_created.rb +18 -0
  16. data/lib/vagrant-brightbox/action/is_running.rb +18 -0
  17. data/lib/vagrant-brightbox/action/map_cloud_ip.rb +68 -0
  18. data/lib/vagrant-brightbox/action/message_already_created.rb +16 -0
  19. data/lib/vagrant-brightbox/action/message_not_created.rb +16 -0
  20. data/lib/vagrant-brightbox/action/read_ssh_info.rb +69 -0
  21. data/lib/vagrant-brightbox/action/read_state.rb +38 -0
  22. data/lib/vagrant-brightbox/action/start_server.rb +28 -0
  23. data/lib/vagrant-brightbox/action/sync_folders.rb +58 -0
  24. data/lib/vagrant-brightbox/action/timed_provision.rb +21 -0
  25. data/lib/vagrant-brightbox/action/unsupported.rb +18 -0
  26. data/lib/vagrant-brightbox/action.rb +166 -0
  27. data/lib/vagrant-brightbox/config.rb +268 -0
  28. data/lib/vagrant-brightbox/errors.rb +34 -0
  29. data/lib/vagrant-brightbox/plugin.rb +73 -0
  30. data/lib/vagrant-brightbox/provider.rb +50 -0
  31. data/lib/vagrant-brightbox/util/timer.rb +17 -0
  32. data/lib/vagrant-brightbox/version.rb +5 -0
  33. data/lib/vagrant-brightbox.rb +18 -0
  34. data/locales/en.yml +88 -0
  35. data/spec/vagrant-brightbox/config_spec.rb +179 -0
  36. data/vagrant-brightbox.gemspec +23 -0
  37. metadata +122 -0
@@ -0,0 +1,268 @@
1
+ require "vagrant"
2
+
3
+ module VagrantPlugins
4
+ module Brightbox
5
+ class Config < Vagrant.plugin("2", :config)
6
+ # The API Client ID for accessing Brightbox.
7
+ #
8
+ # @return [String]
9
+ attr_accessor :client_id
10
+
11
+ # The secret access key for accessing Brightbox.
12
+ #
13
+ # @return [String]
14
+ attr_accessor :secret
15
+
16
+ # The URL to use as the API endpoint
17
+ #
18
+ # @return [String]
19
+ attr_accessor :api_url
20
+
21
+ # The URL to use as the API authentication endpoint
22
+ #
23
+ # @return [String]
24
+ attr_accessor :auth_url
25
+
26
+ # Email or user id for user based authentication
27
+ #
28
+ # @return [String]
29
+ attr_accessor :username
30
+
31
+ # Password for user based authentication
32
+ #
33
+ # @return [String]
34
+ attr_accessor :password
35
+
36
+ # The account id of the account on which operations should be performed
37
+ #
38
+ # @return [String]
39
+ attr_accessor :account
40
+
41
+
42
+ # The ID of the Image to use.
43
+ #
44
+ # @return [String]
45
+ attr_accessor :image_id
46
+
47
+ # The zone to launch the server into. If nil, it will
48
+ # use the default for your account.
49
+ #
50
+ # @return [String]
51
+ attr_accessor :zone
52
+
53
+ # The type of server to launch, such as "nano"
54
+ #
55
+ # @return [String]
56
+ attr_accessor :server_type
57
+
58
+ # The name of the server. This defaults to the name of the machine
59
+ # defined by Vagrant (via `config.vm.define`), but can be overriden
60
+ # here.
61
+ attr_accessor :server_name
62
+
63
+ # The name of the Brightbox region in which to create the server.
64
+ #
65
+ # @return [String]
66
+ attr_accessor :region
67
+
68
+ # The server groups to set on the server. This must
69
+ # be a list of group IDs.
70
+ #
71
+ # @return [Array<String>]
72
+ attr_accessor :server_groups
73
+
74
+ # The path to the SSH private key to use with this server.
75
+ # This overrides the `config.ssh.private_key_path` variable.
76
+ #
77
+ # @return [String]
78
+ attr_accessor :ssh_private_key_path
79
+
80
+ # The SSH username to use with this server. This overrides
81
+ # the `config.ssh.username` variable.
82
+ #
83
+ # @return [String]
84
+ attr_accessor :ssh_username
85
+
86
+ def initialize(region_specific=false)
87
+ @client_id = UNSET_VALUE
88
+ @secret = UNSET_VALUE
89
+ @api_url = UNSET_VALUE
90
+ @auth_url = UNSET_VALUE
91
+ @username = UNSET_VALUE
92
+ @password = UNSET_VALUE
93
+ @account = UNSET_VALUE
94
+ @image_id = UNSET_VALUE
95
+ @zone = UNSET_VALUE
96
+ @server_type = UNSET_VALUE
97
+ @server_name = UNSET_VALUE
98
+ @region = UNSET_VALUE
99
+ @server_groups = UNSET_VALUE
100
+ @ssh_private_key_path = UNSET_VALUE
101
+ @ssh_username = UNSET_VALUE
102
+
103
+ # Internal state (prefix with __ so they aren't automatically
104
+ # merged)
105
+ @__compiled_region_configs = {}
106
+ @__finalized = false
107
+ @__region_config = {}
108
+ @__region_specific = region_specific
109
+ end
110
+
111
+ # Allows region-specific overrides of any of the settings on this
112
+ # configuration object. This allows the user to override things like
113
+ # Image and keypair name for regions. Example:
114
+ #
115
+ # brightbox.region_config "gb1" do |region|
116
+ # region.image_id = "img-umqoe"
117
+ # end
118
+ #
119
+ # @param [String] region The region name to configure.
120
+ # @param [Hash] attributes Direct attributes to set on the configuration
121
+ # as a shortcut instead of specifying a full block.
122
+ # @yield [config] Yields a new Brightbox configuration.
123
+ def region_config(region, attributes=nil, &block)
124
+ # Append the block to the list of region configs for that region.
125
+ # We'll evaluate these upon finalization.
126
+ @__region_config[region] ||= []
127
+
128
+ # Append a block that sets attributes if we got one
129
+ if attributes
130
+ attr_block = lambda do |config|
131
+ config.set_options(attributes)
132
+ end
133
+
134
+ @__region_config[region] << attr_block
135
+ end
136
+
137
+ # Append a block if we got one
138
+ @__region_config[region] << block if block_given?
139
+ end
140
+
141
+ #-------------------------------------------------------------------
142
+ # Internal methods.
143
+ #-------------------------------------------------------------------
144
+
145
+ def merge(other)
146
+ super.tap do |result|
147
+ # Copy over the region specific flag. "True" is retained if either
148
+ # has it.
149
+ new_region_specific = other.instance_variable_get(:@__region_specific)
150
+ result.instance_variable_set(
151
+ :@__region_specific, new_region_specific || @__region_specific)
152
+
153
+ # Go through all the region configs and prepend ours onto
154
+ # theirs.
155
+ new_region_config = other.instance_variable_get(:@__region_config)
156
+ @__region_config.each do |key, value|
157
+ new_region_config[key] ||= []
158
+ new_region_config[key] = value + new_region_config[key]
159
+ end
160
+
161
+ # Set it
162
+ result.instance_variable_set(:@__region_config, new_region_config)
163
+
164
+ end
165
+ end
166
+
167
+ def finalize!
168
+ # Default region is UK gb1. Because that's all there is at the moment!
169
+ @region = "gb1" if @region == UNSET_VALUE
170
+
171
+ # The server groups are empty by default.
172
+ @server_groups = [] if @server_groups == UNSET_VALUE
173
+
174
+ # Everything defaults to nil
175
+ @client_id = nil if @client_id == UNSET_VALUE
176
+ @secret = nil if @secret == UNSET_VALUE
177
+ @auth_url = nil if @auth_url == UNSET_VALUE
178
+ @api_url = nil if @api_url == UNSET_VALUE
179
+ @username = nil if @username == UNSET_VALUE
180
+ @password = nil if @password == UNSET_VALUE
181
+ @account = nil if @account == UNSET_VALUE
182
+ @image_id = nil if @image_id == UNSET_VALUE
183
+ @server_type = nil if @server_type == UNSET_VALUE
184
+ @server_name = nil if @server_name == UNSET_VALUE
185
+ @zone = nil if @zone == UNSET_VALUE
186
+
187
+
188
+ # The SSH values by default are nil, and the top-level config
189
+ # `config.ssh` values are used.
190
+ @ssh_private_key_path = nil if @ssh_private_key_path == UNSET_VALUE
191
+ @ssh_username = nil if @ssh_username == UNSET_VALUE
192
+
193
+ # Compile our region specific configurations only within
194
+ # NON-REGION-SPECIFIC configurations.
195
+ if !@__region_specific
196
+ @__region_config.each do |region, blocks|
197
+ config = self.class.new(true).merge(self)
198
+
199
+ # Execute the configuration for each block
200
+ blocks.each { |b| b.call(config) }
201
+
202
+ # The region name of the configuration always equals the
203
+ # region config name:
204
+ config.region = region
205
+
206
+ # Finalize the configuration
207
+ config.finalize!
208
+
209
+ # Store it for retrieval
210
+ @__compiled_region_configs[region] = config
211
+ end
212
+ end
213
+
214
+ # Mark that we finalized
215
+ @__finalized = true
216
+ end
217
+
218
+ def validate(machine)
219
+ errors = []
220
+
221
+ errors << I18n.t("vagrant_brightbox.config.region_required") if @region.nil?
222
+
223
+ errors << I18n.t("vagrant_brightbox.config.region_invalid") unless valid_region?
224
+
225
+ if @region && valid_region?
226
+ # Get the configuration for the region we're using and validate only
227
+ # that region.
228
+ config = get_region_config(@region)
229
+
230
+ # Secret could be in fog config file.
231
+ unless fog_config_file_exists?
232
+ errors << I18n.t("vagrant_brightbox.config.client_id_required") if \
233
+ config.client_id.nil?
234
+ errors << I18n.t("vagrant_brightbox.config.secret_required") if \
235
+ config.secret.nil?
236
+ end
237
+
238
+ if config.ssh_private_key_path && \
239
+ !File.file?(File.expand_path(config.ssh_private_key_path, machine.env.root_path))
240
+ errors << I18n.t("vagrant_brightbox.config.private_key_missing")
241
+ end
242
+ end
243
+
244
+ { "Brightbox Provider" => errors }
245
+ end
246
+
247
+ def fog_config_file_exists?
248
+ File.file?(File.join(ENV['HOME'], '.fog'))
249
+ end
250
+
251
+ def valid_region?
252
+ return false unless @region =~ /gb1/
253
+ @region == 'gb1' || !(@auth_url.nil? || @api_url.nil?)
254
+ end
255
+
256
+ # This gets the configuration for a specific region. It shouldn't
257
+ # be called by the general public and is only used internally.
258
+ def get_region_config(name)
259
+ if !@__finalized
260
+ raise "Configuration must be finalized before calling this method."
261
+ end
262
+
263
+ # Return the compiled region config
264
+ @__compiled_region_configs[name] || self
265
+ end
266
+ end
267
+ end
268
+ end
@@ -0,0 +1,34 @@
1
+ require "vagrant"
2
+
3
+ module VagrantPlugins
4
+ module Brightbox
5
+ module Errors
6
+ class VagrantBrightboxError < Vagrant::Errors::VagrantError
7
+ error_namespace("vagrant_brightbox.errors")
8
+ end
9
+
10
+ class FogError < VagrantBrightboxError
11
+ def initialize(message = nil, *args)
12
+ if message.is_a?(Hash)
13
+ target = message[:message]
14
+ if target.respond_to?(:body)
15
+ puts target.body.inspect
16
+ decode = Fog::JSON.decode(target.body)
17
+ if decode["errors"]
18
+ message[:message] = decode["error_name"]+":\n"+decode["errors"].join("\n")
19
+ elsif decode["error_description"]
20
+ message[:message] = decode["error"] + ": " + decode["error_description"]
21
+ end
22
+ end
23
+ end
24
+ super
25
+ end
26
+ error_key(:fog_error)
27
+ end
28
+
29
+ class RsyncError < VagrantBrightboxError
30
+ error_key(:rsync_error)
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,73 @@
1
+ begin
2
+ require "vagrant"
3
+ rescue LoadError
4
+ raise "The Vagrant Brightbox plugin 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 "The Vagrant Brightbox plugin is only compatible with Vagrant 1.1+"
11
+ end
12
+
13
+ module VagrantPlugins
14
+ module Brightbox
15
+ class Plugin < Vagrant.plugin("2")
16
+ name "Brightbox"
17
+ description <<-DESC
18
+ This plugin installs a provider that allows Vagrant to manage
19
+ servers in the Brightbox Cloud
20
+ DESC
21
+
22
+ config(:brightbox, :provider) do
23
+ require_relative "config"
24
+ Config
25
+ end
26
+
27
+ provider(:brightbox) do
28
+ # Setup logging and i18n
29
+ setup_logging
30
+ setup_i18n
31
+
32
+ # Return the provider
33
+ require_relative "provider"
34
+ Provider
35
+ end
36
+
37
+ # This initializes the internationalization strings.
38
+ def self.setup_i18n
39
+ I18n.load_path << File.expand_path("locales/en.yml", Brightbox.source_root)
40
+ I18n.reload!
41
+ end
42
+
43
+ # This sets up our log level to be whatever VAGRANT_LOG is.
44
+ def self.setup_logging
45
+ require "log4r"
46
+
47
+ level = nil
48
+ begin
49
+ level = Log4r.const_get(ENV["VAGRANT_LOG"].upcase)
50
+ rescue NameError
51
+ # This means that the logging constant wasn't found,
52
+ # which is fine. We just keep `level` as `nil`. But
53
+ # we tell the user.
54
+ level = nil
55
+ end
56
+
57
+ # Some constants, such as "true" resolve to booleans, so the
58
+ # above error checking doesn't catch it. This will check to make
59
+ # sure that the log level is an integer, as Log4r requires.
60
+ level = nil if !level.is_a?(Integer)
61
+
62
+ # Set the logging level on all "vagrant" namespaced
63
+ # logs as long as we have a valid level.
64
+ if level
65
+ logger = Log4r::Logger.new("vagrant_brightbox")
66
+ logger.outputters = Log4r::Outputter.stderr
67
+ logger.level = level
68
+ logger = nil
69
+ end
70
+ end
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,50 @@
1
+ require "log4r"
2
+ require "vagrant"
3
+
4
+ module VagrantPlugins
5
+ module Brightbox
6
+ class Provider < Vagrant.plugin("2", :provider)
7
+ def initialize(machine)
8
+ @machine = machine
9
+ end
10
+
11
+ def action(name)
12
+ # Attempt to get the action method from the Action class if it
13
+ # exists, otherwise return nil to show that we don't support the
14
+ # given action.
15
+ action_method = "action_#{name}"
16
+ return Action.send(action_method) if Action.respond_to?(action_method)
17
+ nil
18
+ end
19
+
20
+ def ssh_info
21
+ # Run a custom action called "read_ssh_info" which does what it
22
+ # says and puts the resulting SSH info into the `:machine_ssh_info`
23
+ # key in the environment.
24
+ env = @machine.action("read_ssh_info")
25
+ env[:machine_ssh_info]
26
+ end
27
+
28
+ def state
29
+ # Run a custom action we define called "read_state" which does
30
+ # what it says. It puts the state in the `:machine_state_id`
31
+ # key in the environment.
32
+ env = @machine.action("read_state")
33
+
34
+ state_id = env[:machine_state_id]
35
+
36
+ # Get the short and long description
37
+ short = I18n.t("vagrant_brightbox.states.short_#{state_id}")
38
+ long = I18n.t("vagrant_brightbox.states.long_#{state_id}")
39
+
40
+ # Return the MachineState object
41
+ Vagrant::MachineState.new(state_id, short, long)
42
+ end
43
+
44
+ def to_s
45
+ id = @machine.id.nil? ? "new" : @machine.id
46
+ "Brightbox (#{id})"
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,17 @@
1
+ module VagrantPlugins
2
+ module Brightbox
3
+ module Util
4
+ class Timer
5
+ # A basic utility method that times the execution of the given
6
+ # block and returns it.
7
+ def self.time
8
+ start_time = Time.now.to_f
9
+ yield
10
+ end_time = Time.now.to_f
11
+
12
+ end_time - start_time
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,5 @@
1
+ module VagrantPlugins
2
+ module Brightbox
3
+ VERSION = "0.1.0"
4
+ end
5
+ end
@@ -0,0 +1,18 @@
1
+ require "pathname"
2
+
3
+ require "vagrant-brightbox/plugin"
4
+
5
+ module VagrantPlugins
6
+ module Brightbox
7
+ lib_path = Pathname.new(File.expand_path("../vagrant-brightbox", __FILE__))
8
+ autoload :Action, lib_path.join("action")
9
+ autoload :Errors, lib_path.join("errors")
10
+
11
+ # This returns the path to the source of this plugin.
12
+ #
13
+ # @return [Pathname]
14
+ def self.source_root
15
+ @source_root ||= Pathname.new(File.expand_path("../../", __FILE__))
16
+ end
17
+ end
18
+ end
data/locales/en.yml ADDED
@@ -0,0 +1,88 @@
1
+ en:
2
+ vagrant_brightbox:
3
+ already_created: |-
4
+ The server is already created.
5
+ deleting_server: |-
6
+ Deleting server...
7
+ finding_image: |-
8
+ Finding image for server...
9
+ launching_server: |-
10
+ Launching a server with the following settings...
11
+ not_created: |-
12
+ The server hasn't been created yet. Run `vagrant up` first.
13
+ ready: |-
14
+ The server is ready!
15
+ rsync_folder: |-
16
+ Rsyncing folder: %{hostpath} => %{guestpath}
17
+ waiting_for_build: |-
18
+ Waiting for the server to be built...
19
+ waiting_for_ssh: |-
20
+ Waiting for SSH to become available...
21
+ mapped_cloud_ip: |-
22
+ Mapped IPv4 Cloud IP '%{cloud_ip}' to server '%{server}'
23
+ starting_server: |-
24
+ Starting server...
25
+ stopping_server: |-
26
+ Stopping server...
27
+ unsupported: |-
28
+ This command is not supported on the Brightbox Provider
29
+
30
+ config:
31
+ region_required: |-
32
+ A region must be specified via "region".
33
+ region_invalid: |-
34
+ You must specify the correct 'auth_url' and 'api_url' with
35
+ anything other than the default 'gb1' region.
36
+ private_key_missing: |-
37
+ The specified private key for Brightbox cloud could not be found.
38
+ client_id_required: |-
39
+ You must include a client_id in the Vagrantfile, or in the file '~/.fog'.
40
+ secret_required: |-
41
+ You must include the secret associated with the client_id in
42
+ the Vagrantfile, or in the file '~/.fog'.
43
+ username_required: |-
44
+ A username is required.
45
+
46
+ errors:
47
+ no_free_cloud_ip: |-
48
+ Warning! Unable to find a free cloup ip from your allocation. IPv4
49
+ public network access will not be available.
50
+ create_bad_state: |-
51
+ While creating the server, it transitioned to an unexpected
52
+ state: '%{state}', instead of properly booting. Run `vagrant status`
53
+ to find out what can be done about this state, or `vagrant destroy`
54
+ if you want to start over.
55
+ no_matching_image: |-
56
+ No matching image was found! Please check your image setting to
57
+ make sure you have a valid image chosen.
58
+ fog_error: |-
59
+ There was an error talking to the Brightbox cloud. The error
60
+ message is shown below:
61
+
62
+ %{message}
63
+ rsync_error: |-
64
+ There was an error when attemping to rsync a share folder.
65
+ Please inspect the error message below for more info.
66
+
67
+ Host path: %{hostpath}
68
+ Guest path: %{guestpath}
69
+ Error: %{stderr}
70
+
71
+ states:
72
+ short_active: |-
73
+ active
74
+ long_active: |-
75
+ The server is up and running. Run `vagrant ssh` to access it.
76
+ short_inactive: |-
77
+ inactive
78
+ long_inactive: |-
79
+ The server has been created but is offline. Run `vagrant up` to restart.
80
+ short_creating: |-
81
+ creating
82
+ long_creating: |-
83
+ The server is currently being built. You must wait for this to
84
+ complete before you can access it or destroy it
85
+ short_not_created: |-
86
+ not created
87
+ long_not_created: |-
88
+ The server is not created. Run `vagrant up` to create it.