vagrant-rackspace 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,126 @@
1
+ require "fog"
2
+ require "log4r"
3
+
4
+ require 'vagrant/util/retryable'
5
+
6
+ module VagrantPlugins
7
+ module Rackspace
8
+ module Action
9
+ # This creates the Rackspace server.
10
+ class CreateServer
11
+ include Vagrant::Util::Retryable
12
+
13
+ def initialize(app, env)
14
+ @app = app
15
+ @logger = Log4r::Logger.new("vagrant_rackspace::action::create_server")
16
+ end
17
+
18
+ def call(env)
19
+ # Get the configs
20
+ config = env[:machine].provider_config
21
+
22
+ # Find the flavor
23
+ env[:ui].info(I18n.t("vagrant_rackspace.finding_flavor"))
24
+ flavor = find_matching(env[:rackspace_compute].flavors.all, config.flavor)
25
+ raise Errors::NoMatchingFlavor if !flavor
26
+
27
+ # Find the image
28
+ env[:ui].info(I18n.t("vagrant_rackspace.finding_image"))
29
+ image = find_matching(env[:rackspace_compute].images.all, config.image)
30
+ raise Errors::NoMatchingImage if !image
31
+
32
+ # Figure out the name for the server
33
+ server_name = config.server_name || env[:machine].name
34
+
35
+ # If we're using the default keypair, then show a warning
36
+ default_key_path = Vagrant.source_root.join("keys/vagrant.pub").to_s
37
+ public_key_path = File.expand_path(config.public_key_path, env[:root_path])
38
+
39
+ if default_key_path == public_key_path
40
+ env[:ui].warn(I18n.t("vagrant_rackspace.warn_insecure_ssh"))
41
+ end
42
+
43
+ # Output the settings we're going to use to the user
44
+ env[:ui].info(I18n.t("vagrant_rackspace.launching_server"))
45
+ env[:ui].info(" -- Flavor: #{flavor.name}")
46
+ env[:ui].info(" -- Image: #{image.name}")
47
+ env[:ui].info(" -- Name: #{server_name}")
48
+
49
+ # Build the options for launching...
50
+ options = {
51
+ :flavor_id => flavor.id,
52
+ :image_id => image.id,
53
+ :name => server_name,
54
+ :personality => [
55
+ {
56
+ :path => "/root/.ssh/authorized_keys",
57
+ :contents => Base64.encode64(File.read(public_key_path))
58
+ }
59
+ ]
60
+ }
61
+
62
+ # Create the server
63
+ server = env[:rackspace_compute].servers.create(options)
64
+
65
+ # Store the ID right away so we can track it
66
+ env[:machine].id = server.id
67
+
68
+ # Wait for the server to finish building
69
+ env[:ui].info(I18n.t("vagrant_rackspace.waiting_for_build"))
70
+ retryable(:on => Fog::Errors::TimeoutError, :tries => 200) do
71
+ # If we're interrupted don't worry about waiting
72
+ next if env[:interrupted]
73
+
74
+ # Set the progress
75
+ env[:ui].clear_line
76
+ env[:ui].report_progress(server.progress, 100, false)
77
+
78
+ # Wait for the server to be ready
79
+ begin
80
+ server.wait_for(5) { ready? }
81
+ rescue RuntimeError => e
82
+ # If we don't have an error about a state transition, then
83
+ # we just move on.
84
+ raise if e.message !~ /should have transitioned/
85
+ raise Errors::CreateBadState, :state => server.state
86
+ end
87
+ end
88
+
89
+ if !env[:interrupted]
90
+ # Clear the line one more time so the progress is removed
91
+ env[:ui].clear_line
92
+
93
+ # Wait for SSH to become available
94
+ env[:ui].info(I18n.t("vagrant_rackspace.waiting_for_ssh"))
95
+ while true
96
+ # If we're interrupted then just back out
97
+ break if env[:interrupted]
98
+ break if env[:machine].communicate.ready?
99
+ sleep 2
100
+ end
101
+
102
+ env[:ui].info(I18n.t("vagrant_rackspace.ready"))
103
+ end
104
+
105
+ @app.call(env)
106
+ end
107
+
108
+ protected
109
+
110
+ # This method finds a matching _thing_ in a collection of
111
+ # _things_. This works matching if the ID or NAME equals to
112
+ # `name`. Or, if `name` is a regexp, a partial match is chosen
113
+ # as well.
114
+ def find_matching(collection, name)
115
+ collection.each do |single|
116
+ return single if single.id == name
117
+ return single if single.name == name
118
+ return single if name.is_a?(Regexp) && name =~ single.name
119
+ end
120
+
121
+ nil
122
+ end
123
+ end
124
+ end
125
+ end
126
+ end
@@ -0,0 +1,26 @@
1
+ require "log4r"
2
+
3
+ module VagrantPlugins
4
+ module Rackspace
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_rackspace::action::delete_server")
11
+ end
12
+
13
+ def call(env)
14
+ if env[:machine].id
15
+ env[:ui].info(I18n.t("vagrant_rackspace.deleting_server"))
16
+ server = env[:rackspace_compute].servers.get(env[:machine].id)
17
+ server.destroy
18
+ env[:machine].id = nil
19
+ end
20
+
21
+ @app.call(env)
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,16 @@
1
+ module VagrantPlugins
2
+ module Rackspace
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,16 @@
1
+ module VagrantPlugins
2
+ module Rackspace
3
+ module Action
4
+ class MessageAlreadyCreated
5
+ def initialize(app, env)
6
+ @app = app
7
+ end
8
+
9
+ def call(env)
10
+ env[:ui].info(I18n.t("vagrant_rackspace.already_created"))
11
+ @app.call(env)
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,16 @@
1
+ module VagrantPlugins
2
+ module Rackspace
3
+ module Action
4
+ class MessageNotCreated
5
+ def initialize(app, env)
6
+ @app = app
7
+ end
8
+
9
+ def call(env)
10
+ env[:ui].info(I18n.t("vagrant_rackspace.not_created"))
11
+ @app.call(env)
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,42 @@
1
+ require "log4r"
2
+
3
+ module VagrantPlugins
4
+ module Rackspace
5
+ module Action
6
+ # This action reads the SSH info for the machine and puts it into the
7
+ # `:machine_ssh_info` key in the environment.
8
+ class ReadSSHInfo
9
+ def initialize(app, env)
10
+ @app = app
11
+ @logger = Log4r::Logger.new("vagrant_rackspace::action::read_ssh_info")
12
+ end
13
+
14
+ def call(env)
15
+ env[:machine_ssh_info] = read_ssh_info(env[:rackspace_compute], env[:machine])
16
+
17
+ @app.call(env)
18
+ end
19
+
20
+ def read_ssh_info(rackspace, machine)
21
+ return nil if machine.id.nil?
22
+
23
+ # Find the machine
24
+ server = rackspace.servers.get(machine.id)
25
+ if server.nil?
26
+ # The machine can't be found
27
+ @logger.info("Machine couldn't be found, assuming it got destroyed.")
28
+ machine.id = nil
29
+ return nil
30
+ end
31
+
32
+ # Read the DNS info
33
+ return {
34
+ :host => server.ipv4_address,
35
+ :port => 22,
36
+ :username => "root"
37
+ }
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,38 @@
1
+ require "log4r"
2
+
3
+ module VagrantPlugins
4
+ module Rackspace
5
+ module Action
6
+ # This action reads the state of the machine and puts it in the
7
+ # `:machine_state_id` key in the environment.
8
+ class ReadState
9
+ def initialize(app, env)
10
+ @app = app
11
+ @logger = Log4r::Logger.new("vagrant_rackspace::action::read_state")
12
+ end
13
+
14
+ def call(env)
15
+ env[:machine_state_id] = read_state(env[:rackspace_compute], env[:machine])
16
+
17
+ @app.call(env)
18
+ end
19
+
20
+ def read_state(rackspace, machine)
21
+ return :not_created if machine.id.nil?
22
+
23
+ # Find the machine
24
+ server = rackspace.servers.get(machine.id)
25
+ if server.nil? || server.state == "DELETED"
26
+ # The machine can't be found
27
+ @logger.info("Machine not found or deleted, assuming it got destroyed.")
28
+ machine.id = nil
29
+ return :not_created
30
+ end
31
+
32
+ # Return the state
33
+ return server.state.downcase.to_sym
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,57 @@
1
+ require "log4r"
2
+
3
+ require "vagrant/util/subprocess"
4
+
5
+ module VagrantPlugins
6
+ module Rackspace
7
+ module Action
8
+ # This middleware uses `rsync` to sync the folders over to the
9
+ # remote instance.
10
+ class SyncFolders
11
+ def initialize(app, env)
12
+ @app = app
13
+ @logger = Log4r::Logger.new("vagrant_rackspace::action::sync_folders")
14
+ end
15
+
16
+ def call(env)
17
+ @app.call(env)
18
+
19
+ ssh_info = env[:machine].ssh_info
20
+
21
+ env[:machine].config.vm.synced_folders.each do |id, data|
22
+ hostpath = File.expand_path(data[:hostpath], env[:root_path])
23
+ guestpath = data[:guestpath]
24
+
25
+ # Make sure there is a trailing slash on the host path to
26
+ # avoid creating an additional directory with rsync
27
+ hostpath = "#{hostpath}/" if hostpath !~ /\/$/
28
+
29
+ env[:ui].info(I18n.t("vagrant_rackspace.rsync_folder",
30
+ :hostpath => hostpath,
31
+ :guestpath => guestpath))
32
+
33
+ # Create the guest path
34
+ env[:machine].communicate.sudo("mkdir -p '#{guestpath}'")
35
+ env[:machine].communicate.sudo(
36
+ "chown #{ssh_info[:username]} '#{guestpath}'")
37
+
38
+ # Rsync over to the guest path using the SSH info
39
+ command = [
40
+ "rsync", "--verbose", "--archive", "-z",
41
+ "-e", "ssh -p #{ssh_info[:port]} -i '#{ssh_info[:private_key_path]}'",
42
+ hostpath,
43
+ "#{ssh_info[:username]}@#{ssh_info[:host]}:#{guestpath}"]
44
+
45
+ r = Vagrant::Util::Subprocess.execute(*command)
46
+ if r.exit_code != 0
47
+ raise Errors::RsyncError,
48
+ :guestpath => guestpath,
49
+ :hostpath => hostpath,
50
+ :stderr => r.stderr
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,19 @@
1
+ module VagrantPlugins
2
+ module Rackspace
3
+ module Action
4
+ class WarnNetworks
5
+ def initialize(app, env)
6
+ @app = app
7
+ end
8
+
9
+ def call(env)
10
+ if env[:machine].config.vm.networks.length > 0
11
+ env[:ui].warn(I18n.t("vagrant_rackspace.warn_networks"))
12
+ end
13
+
14
+ @app.call(env)
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,79 @@
1
+ require "vagrant"
2
+
3
+ module VagrantPlugins
4
+ module Rackspace
5
+ class Config < Vagrant.plugin("2", :config)
6
+ # The API key to access RackSpace.
7
+ #
8
+ # @return [String]
9
+ attr_accessor :api_key
10
+
11
+ # The endpoint to access RackSpace. If nil, it will default
12
+ # to DFW.
13
+ #
14
+ # @return [String]
15
+ attr_accessor :endpoint
16
+
17
+ # The flavor of server to launch, either the ID or name. This
18
+ # can also be a regular expression to partially match a name.
19
+ attr_accessor :flavor
20
+
21
+ # The name or ID of the image to use. This can also be a regular
22
+ # expression to partially match a name.
23
+ attr_accessor :image
24
+
25
+ # The path to the public key to set up on the remote server for SSH.
26
+ # This should match the private key configured with `config.ssh.private_key_path`.
27
+ #
28
+ # @return [String]
29
+ attr_accessor :public_key_path
30
+
31
+ # The name of the server. This defaults to the name of the machine
32
+ # defined by Vagrant (via `config.vm.define`), but can be overriden
33
+ # here.
34
+ attr_accessor :server_name
35
+
36
+ # The username to access RackSpace.
37
+ #
38
+ # @return [String]
39
+ attr_accessor :username
40
+
41
+ def initialize
42
+ @api_key = UNSET_VALUE
43
+ @endpoint = UNSET_VALUE
44
+ @flavor = UNSET_VALUE
45
+ @image = UNSET_VALUE
46
+ @public_key_path = UNSET_VALUE
47
+ @server_name = UNSET_VALUE
48
+ @username = UNSET_VALUE
49
+ end
50
+
51
+ def finalize!
52
+ @api_key = nil if @api_key == UNSET_VALUE
53
+ @endpoint = nil if @endpoint == UNSET_VALUE
54
+ @flavor = /512MB/ if @flavor == UNSET_VALUE
55
+ @image = /Ubuntu/ if @image == UNSET_VALUE
56
+ @server_name = nil if @server_name == UNSET_VALUE
57
+ @username = nil if @username == UNSET_VALUE
58
+
59
+ if @public_key_path == UNSET_VALUE
60
+ @public_key_path = Vagrant.source_root.join("keys/vagrant.pub")
61
+ end
62
+ end
63
+
64
+ def validate(machine)
65
+ errors = []
66
+
67
+ errors << I18n.t("vagrant_rackspace.config.api_key_required") if !@api_key
68
+ errors << I18n.t("vagrant_rackspace.config.username_required") if !@username
69
+
70
+ public_key_path = File.expand_path(@public_key_path, machine.env.root_path)
71
+ if !File.file?(public_key_path)
72
+ errors << I18n.t("vagrant_rackspace.config.public_key_not_found")
73
+ end
74
+
75
+ { "RackSpace Provider" => errors }
76
+ end
77
+ end
78
+ end
79
+ end