vagrant-chassis-digitalocean 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 (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