vagrant-chassis-digitalocean 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) hide show
  1. data/.gitignore +19 -0
  2. data/CHANGELOG.md +8 -0
  3. data/Gemfile +5 -0
  4. data/LICENSE.txt +23 -0
  5. data/README.md +95 -0
  6. data/Rakefile +22 -0
  7. data/box/digital_ocean.box +0 -0
  8. data/box/metadata.json +3 -0
  9. data/lib/vagrant-chassis-digitalocean.rb +20 -0
  10. data/lib/vagrant-chassis-digitalocean/actions.rb +160 -0
  11. data/lib/vagrant-chassis-digitalocean/actions/check_state.rb +19 -0
  12. data/lib/vagrant-chassis-digitalocean/actions/create.rb +96 -0
  13. data/lib/vagrant-chassis-digitalocean/actions/destroy.rb +35 -0
  14. data/lib/vagrant-chassis-digitalocean/actions/modify_provision_path.rb +38 -0
  15. data/lib/vagrant-chassis-digitalocean/actions/power_off.rb +33 -0
  16. data/lib/vagrant-chassis-digitalocean/actions/power_on.rb +34 -0
  17. data/lib/vagrant-chassis-digitalocean/actions/rebuild.rb +52 -0
  18. data/lib/vagrant-chassis-digitalocean/actions/reload.rb +31 -0
  19. data/lib/vagrant-chassis-digitalocean/actions/setup_key.rb +58 -0
  20. data/lib/vagrant-chassis-digitalocean/actions/setup_sudo.rb +41 -0
  21. data/lib/vagrant-chassis-digitalocean/actions/setup_user.rb +64 -0
  22. data/lib/vagrant-chassis-digitalocean/actions/sync_folders.rb +91 -0
  23. data/lib/vagrant-chassis-digitalocean/commands/rebuild.rb +23 -0
  24. data/lib/vagrant-chassis-digitalocean/config.rb +62 -0
  25. data/lib/vagrant-chassis-digitalocean/errors.rb +37 -0
  26. data/lib/vagrant-chassis-digitalocean/helpers/client.rb +88 -0
  27. data/lib/vagrant-chassis-digitalocean/helpers/result.rb +40 -0
  28. data/lib/vagrant-chassis-digitalocean/plugin.rb +26 -0
  29. data/lib/vagrant-chassis-digitalocean/provider.rb +100 -0
  30. data/lib/vagrant-chassis-digitalocean/version.rb +5 -0
  31. data/locales/en.yml +84 -0
  32. data/test/Vagrantfile +38 -0
  33. data/test/cookbooks/test/recipes/default.rb +1 -0
  34. data/test/scripts/provision.sh +3 -0
  35. data/test/test.sh +14 -0
  36. data/test/test_id_rsa +27 -0
  37. data/test/test_id_rsa.pub +1 -0
  38. data/vagrant-chassis-digitalocean.gemspec +21 -0
  39. metadata +137 -0
@@ -0,0 +1,35 @@
1
+ require 'vagrant-digitalocean/helpers/client'
2
+
3
+ module VagrantPlugins
4
+ module DigitalOcean
5
+ module Actions
6
+ class Destroy
7
+ include Helpers::Client
8
+
9
+ def initialize(app, env)
10
+ @app = app
11
+ @machine = env[:machine]
12
+ @client = client
13
+ @logger = Log4r::Logger.new('vagrant::digitalocean::destroy')
14
+ end
15
+
16
+ def call(env)
17
+ # submit destroy droplet request
18
+ result = @client.request("/droplets/#{@machine.id}/destroy")
19
+
20
+ env[:ui].info I18n.t('vagrant_digital_ocean.info.destroying')
21
+
22
+ # wait for the destroy progress to start
23
+ @client.wait_for_event(env, result['event_id']) do |response|
24
+ break if response['event']['percentage'] != nil
25
+ end
26
+
27
+ # set the machine id to nil to cleanup local vagrant state
28
+ @machine.id = nil
29
+
30
+ @app.call(env)
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,38 @@
1
+ module VagrantPlugins
2
+ module DigitalOcean
3
+ module Actions
4
+ class ModifyProvisionPath
5
+ def initialize(app, env)
6
+ @app = app
7
+ @machine = env[:machine]
8
+ @logger =
9
+ Log4r::Logger.new('vagrant::digitalocean::modify_provision_path')
10
+ end
11
+
12
+ def call(env)
13
+ # check if provisioning is enabled
14
+ enabled = true
15
+ enabled = env[:provision_enabled] if env.has_key?(:provision_enabled)
16
+ return @app.call(env) if !enabled
17
+
18
+ username = @machine.ssh_info()[:username]
19
+
20
+ # change ownership of the provisioning path recursively to the
21
+ # ssh user
22
+ #
23
+ # TODO submit patch to vagrant to set appropriate permissions
24
+ # based on ssh username
25
+ @machine.config.vm.provisioners.each do |provisioner|
26
+ cfg = provisioner.config
27
+ path = cfg.upload_path if cfg.respond_to? :upload_path
28
+ path = cfg.provisioning_path if cfg.respond_to? :provisioning_path
29
+ @machine.communicate.sudo("chown -R #{username} #{path}",
30
+ :error_check => false)
31
+ end
32
+
33
+ @app.call(env)
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,33 @@
1
+ require 'vagrant-digitalocean/helpers/client'
2
+
3
+ module VagrantPlugins
4
+ module DigitalOcean
5
+ module Actions
6
+ class PowerOff
7
+ include Helpers::Client
8
+
9
+ def initialize(app, env)
10
+ @app = app
11
+ @machine = env[:machine]
12
+ @client = client
13
+ @logger = Log4r::Logger.new('vagrant::digitalocean::power_off')
14
+ end
15
+
16
+ def call(env)
17
+ # submit power off droplet request
18
+ result = @client.request("/droplets/#{@machine.id}/power_off")
19
+
20
+ # wait for request to complete
21
+ env[:ui].info I18n.t('vagrant_digital_ocean.info.powering_off')
22
+ @client.wait_for_event(env, result['event_id'])
23
+
24
+ # refresh droplet state with provider
25
+ Provider.droplet(@machine, :refresh => true)
26
+
27
+ @app.call(env)
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
33
+
@@ -0,0 +1,34 @@
1
+ require 'vagrant-digitalocean/helpers/client'
2
+
3
+ module VagrantPlugins
4
+ module DigitalOcean
5
+ module Actions
6
+ class PowerOn
7
+ include Helpers::Client
8
+
9
+ def initialize(app, env)
10
+ @app = app
11
+ @machine = env[:machine]
12
+ @client = client
13
+ @logger = Log4r::Logger.new('vagrant::digitalocean::power_on')
14
+ end
15
+
16
+ def call(env)
17
+ # submit power on droplet request
18
+ result = @client.request("/droplets/#{@machine.id}/power_on")
19
+
20
+ # wait for request to complete
21
+ env[:ui].info I18n.t('vagrant_digital_ocean.info.powering_on')
22
+ @client.wait_for_event(env, result['event_id'])
23
+
24
+ # refresh droplet state with provider
25
+ Provider.droplet(@machine, :refresh => true)
26
+
27
+ @app.call(env)
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
33
+
34
+
@@ -0,0 +1,52 @@
1
+ require 'vagrant-digitalocean/helpers/client'
2
+
3
+ module VagrantPlugins
4
+ module DigitalOcean
5
+ module Actions
6
+ class Rebuild
7
+ include Helpers::Client
8
+ include Vagrant::Util::Retryable
9
+
10
+ def initialize(app, env)
11
+ @app = app
12
+ @machine = env[:machine]
13
+ @client = client
14
+ @logger = Log4r::Logger.new('vagrant::digitalocean::rebuild')
15
+ end
16
+
17
+ def call(env)
18
+ # look up image id
19
+ image_id = @client
20
+ .request('/images')
21
+ .find_id(:images, :name => @machine.provider_config.image)
22
+
23
+ # submit rebuild request
24
+ result = @client.request("/droplets/#{@machine.id}/rebuild", {
25
+ :image_id => image_id
26
+ })
27
+
28
+ # wait for request to complete
29
+ env[:ui].info I18n.t('vagrant_digital_ocean.info.rebuilding')
30
+ @client.wait_for_event(env, result['event_id'])
31
+
32
+ # refresh droplet state with provider
33
+ Provider.droplet(@machine, :refresh => true)
34
+
35
+ # wait for ssh to be ready
36
+ switch_user = @machine.provider_config.setup?
37
+ user = @machine.config.ssh.username
38
+ @machine.config.ssh.username = 'root' if switch_user
39
+
40
+ retryable(:tries => 120, :sleep => 10) do
41
+ next if env[:interrupted]
42
+ raise 'not ready' if !@machine.communicate.ready?
43
+ end
44
+
45
+ @machine.config.ssh.username = user
46
+
47
+ @app.call(env)
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,31 @@
1
+ require 'vagrant-digitalocean/helpers/client'
2
+
3
+ module VagrantPlugins
4
+ module DigitalOcean
5
+ module Actions
6
+ class Reload
7
+ include Helpers::Client
8
+
9
+ def initialize(app, env)
10
+ @app = app
11
+ @machine = env[:machine]
12
+ @client = client
13
+ @logger = Log4r::Logger.new('vagrant::digitalocean::reload')
14
+ end
15
+
16
+ def call(env)
17
+ # submit reboot droplet request
18
+ result = @client.request("/droplets/#{@machine.id}/reboot")
19
+
20
+ # wait for request to complete
21
+ env[:ui].info I18n.t('vagrant_digital_ocean.info.reloading')
22
+ @client.wait_for_event(env, result['event_id'])
23
+
24
+ @app.call(env)
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
30
+
31
+
@@ -0,0 +1,58 @@
1
+ require 'vagrant-digitalocean/helpers/client'
2
+
3
+ module VagrantPlugins
4
+ module DigitalOcean
5
+ module Actions
6
+ class SetupKey
7
+ include Helpers::Client
8
+
9
+ def initialize(app, env)
10
+ @app = app
11
+ @machine = env[:machine]
12
+ @client = client
13
+ @logger = Log4r::Logger.new('vagrant::digitalocean::setup_key')
14
+ end
15
+
16
+ # TODO check the content of the key to see if it has changed
17
+ def call(env)
18
+ ssh_key_name = @machine.provider_config.ssh_key_name
19
+
20
+ begin
21
+ # assigns existing ssh key id to env for use by other commands
22
+ env[:ssh_key_id] = @client
23
+ .request('/ssh_keys/')
24
+ .find_id(:ssh_keys, :name => ssh_key_name)
25
+
26
+ env[:ui].info I18n.t('vagrant_digital_ocean.info.using_key', {
27
+ :name => ssh_key_name
28
+ })
29
+ rescue Errors::ResultMatchError
30
+ env[:ssh_key_id] = create_ssh_key(ssh_key_name, env)
31
+ end
32
+
33
+ @app.call(env)
34
+ end
35
+
36
+ private
37
+
38
+ def create_ssh_key(name, env)
39
+ # assumes public key exists on the same path as private key with .pub ext
40
+ path = @machine.config.ssh.private_key_path
41
+ path = path[0] if path.is_a?(Array)
42
+ path = File.expand_path(path, @machine.env.root_path)
43
+ pub_key = DigitalOcean.public_key(path)
44
+
45
+ env[:ui].info I18n.t('vagrant_digital_ocean.info.creating_key', {
46
+ :name => name
47
+ })
48
+
49
+ result = @client.request('/ssh_keys/new', {
50
+ :name => name,
51
+ :ssh_pub_key => pub_key
52
+ })
53
+ result['ssh_key']['id']
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,41 @@
1
+ module VagrantPlugins
2
+ module DigitalOcean
3
+ module Actions
4
+ class SetupSudo
5
+ def initialize(app, env)
6
+ @app = app
7
+ @machine = env[:machine]
8
+ @logger = Log4r::Logger.new('vagrant::digitalocean::setup_sudo')
9
+ end
10
+
11
+ def call(env)
12
+ # check if setup is enabled
13
+ return @app.call(env) unless @machine.provider_config.setup?
14
+
15
+ # override ssh username to root
16
+ user = @machine.config.ssh.username
17
+ @machine.config.ssh.username = 'root'
18
+
19
+ # check for guest name available in Vagrant 1.2 first
20
+ guest_name = @machine.guest.name if @machine.guest.respond_to?(:name)
21
+ guest_name ||= @machine.guest.to_s.downcase
22
+
23
+ case guest_name
24
+ when /redhat/
25
+ env[:ui].info I18n.t('vagrant_digital_ocean.info.modifying_sudo')
26
+
27
+ # disable tty requirement for sudo
28
+ @machine.communicate.execute(<<-'BASH')
29
+ sed -i'.bk' -e 's/\(Defaults\s\+requiretty\)/# \1/' /etc/sudoers
30
+ BASH
31
+ end
32
+
33
+ # reset ssh username
34
+ @machine.config.ssh.username = user
35
+
36
+ @app.call(env)
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,64 @@
1
+ module VagrantPlugins
2
+ module DigitalOcean
3
+ module Actions
4
+ class SetupUser
5
+ def initialize(app, env)
6
+ @app = app
7
+ @machine = env[:machine]
8
+ @logger = Log4r::Logger.new('vagrant::digitalocean::setup_user')
9
+ end
10
+
11
+ def call(env)
12
+ # check if setup is enabled
13
+ return @app.call(env) unless @machine.provider_config.setup?
14
+
15
+ # check if a username has been specified
16
+ return @app.call(env) unless @machine.config.ssh.username
17
+
18
+ # override ssh username to root temporarily
19
+ user = @machine.config.ssh.username
20
+ @machine.config.ssh.username = 'root'
21
+
22
+ env[:ui].info I18n.t('vagrant_digital_ocean.info.creating_user', {
23
+ :user => user
24
+ })
25
+
26
+ # create user account
27
+ @machine.communicate.execute(<<-BASH)
28
+ if ! (grep ^#{user}: /etc/passwd); then
29
+ useradd -m -s /bin/bash #{user};
30
+ fi
31
+ BASH
32
+
33
+ # grant user sudo access with no password requirement
34
+ @machine.communicate.execute(<<-BASH)
35
+ if ! (grep #{user} /etc/sudoers); then
36
+ echo "#{user} ALL=(ALL:ALL) NOPASSWD: ALL" >> /etc/sudoers;
37
+ else
38
+ sed -i -e "/#{user}/ s/=.*/=(ALL:ALL) NOPASSWD: ALL/" /etc/sudoers;
39
+ fi
40
+ BASH
41
+
42
+ # create the .ssh directory in the users home
43
+ @machine.communicate.execute("su #{user} -c 'mkdir -p ~/.ssh'")
44
+
45
+ # add the specified key to the authorized keys file
46
+ path = @machine.config.ssh.private_key_path
47
+ path = path[0] if path.is_a?(Array)
48
+ path = File.expand_path(path, @machine.env.root_path)
49
+ pub_key = DigitalOcean.public_key(path)
50
+ @machine.communicate.execute(<<-BASH)
51
+ if ! grep '#{pub_key}' /home/#{user}/.ssh/authorized_keys; then
52
+ echo '#{pub_key}' >> /home/#{user}/.ssh/authorized_keys;
53
+ fi
54
+ BASH
55
+
56
+ # reset username
57
+ @machine.config.ssh.username = user
58
+
59
+ @app.call(env)
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,91 @@
1
+ require 'vagrant/util/subprocess'
2
+ require 'vagrant/util/which'
3
+
4
+ module VagrantPlugins
5
+ module DigitalOcean
6
+ module Actions
7
+ class SyncFolders
8
+ def initialize(app, env)
9
+ @app = app
10
+ @machine = env[:machine]
11
+ @logger = Log4r::Logger.new('vagrant::digitalocean::sync_folders')
12
+ end
13
+
14
+ def call(env)
15
+ ssh_info = @machine.ssh_info
16
+
17
+ @machine.config.vm.synced_folders.each do |id, data|
18
+ next if data[:disabled]
19
+
20
+ unless Vagrant::Util::Which.which('rsync')
21
+ env[:ui].warn I18n.t('vagrant_digital_ocean.info.rsync_missing')
22
+ break
23
+ end
24
+
25
+ hostpath = File.expand_path(data[:hostpath], env[:root_path])
26
+ guestpath = data[:guestpath]
27
+
28
+ # make sure there is a trailing slash on the host path to
29
+ # avoid creating an additional directory with rsync
30
+ hostpath = "#{hostpath}/" if hostpath !~ /\/$/
31
+
32
+ # on windows rsync.exe requires cygdrive-style paths
33
+ if Vagrant::Util::Platform.windows?
34
+ hostpath = hostpath.gsub(/^(\w):/) { "/cygdrive/#{$1}" }
35
+ end
36
+
37
+ env[:ui].info I18n.t('vagrant_digital_ocean.info.rsyncing', {
38
+ :hostpath => hostpath,
39
+ :guestpath => guestpath
40
+ })
41
+
42
+ # create the guest path
43
+ @machine.communicate.sudo("mkdir -p #{guestpath}")
44
+ @machine.communicate.sudo(
45
+ "chown -R #{ssh_info[:username]} #{guestpath}")
46
+
47
+ key = ssh_info[:private_key_path]
48
+ key = key[0] if key.is_a?(Array)
49
+
50
+
51
+ # build exclude rules
52
+ excludes = ['.vagrant/']
53
+ excludes += Array(data[:rsync__exclude]).map(&:to_s) if data[:rsync__exclude]
54
+ excludes.uniq!
55
+
56
+
57
+
58
+ # rsync over to the guest path using the ssh info
59
+ command = [
60
+ "rsync", "--verbose", "--progress", "--archive", "-z", "--delete", excludes.map { |e| "--exclude=#{e}" },
61
+ "-e", "ssh -p #{ssh_info[:port]} -o StrictHostKeyChecking=no -i '#{key}'",
62
+ hostpath,
63
+ "#{ssh_info[:username]}@#{ssh_info[:host]}:#{guestpath}",
64
+ { :notify => [:stdout, :stderr] } ].flatten
65
+
66
+ # we need to fix permissions when using rsync.exe on windows, see
67
+ # http://stackoverflow.com/questions/5798807/rsync-permission-denied-created-directories-have-no-permissions
68
+ if Vagrant::Util::Platform.windows?
69
+ command.insert(1, "--chmod", "ugo=rwX")
70
+ end
71
+
72
+ r = Vagrant::Util::Subprocess.execute(*command) do |type, data|
73
+ if type == :stdout || type == :stderr
74
+ @machine.env.ui.info(data, :new_line => false, :prefix => false)
75
+ end
76
+ end
77
+
78
+ if r.exit_code != 0
79
+ raise Errors::RsyncError,
80
+ :guestpath => guestpath,
81
+ :hostpath => hostpath,
82
+ :stderr => r.stderr
83
+ end
84
+ end
85
+
86
+ @app.call(env)
87
+ end
88
+ end
89
+ end
90
+ end
91
+ end