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.
- data/.gitignore +19 -0
- data/CHANGELOG.md +8 -0
- data/Gemfile +5 -0
- data/LICENSE.txt +23 -0
- data/README.md +95 -0
- data/Rakefile +22 -0
- data/box/digital_ocean.box +0 -0
- data/box/metadata.json +3 -0
- data/lib/vagrant-chassis-digitalocean.rb +20 -0
- data/lib/vagrant-chassis-digitalocean/actions.rb +160 -0
- data/lib/vagrant-chassis-digitalocean/actions/check_state.rb +19 -0
- data/lib/vagrant-chassis-digitalocean/actions/create.rb +96 -0
- data/lib/vagrant-chassis-digitalocean/actions/destroy.rb +35 -0
- data/lib/vagrant-chassis-digitalocean/actions/modify_provision_path.rb +38 -0
- data/lib/vagrant-chassis-digitalocean/actions/power_off.rb +33 -0
- data/lib/vagrant-chassis-digitalocean/actions/power_on.rb +34 -0
- data/lib/vagrant-chassis-digitalocean/actions/rebuild.rb +52 -0
- data/lib/vagrant-chassis-digitalocean/actions/reload.rb +31 -0
- data/lib/vagrant-chassis-digitalocean/actions/setup_key.rb +58 -0
- data/lib/vagrant-chassis-digitalocean/actions/setup_sudo.rb +41 -0
- data/lib/vagrant-chassis-digitalocean/actions/setup_user.rb +64 -0
- data/lib/vagrant-chassis-digitalocean/actions/sync_folders.rb +91 -0
- data/lib/vagrant-chassis-digitalocean/commands/rebuild.rb +23 -0
- data/lib/vagrant-chassis-digitalocean/config.rb +62 -0
- data/lib/vagrant-chassis-digitalocean/errors.rb +37 -0
- data/lib/vagrant-chassis-digitalocean/helpers/client.rb +88 -0
- data/lib/vagrant-chassis-digitalocean/helpers/result.rb +40 -0
- data/lib/vagrant-chassis-digitalocean/plugin.rb +26 -0
- data/lib/vagrant-chassis-digitalocean/provider.rb +100 -0
- data/lib/vagrant-chassis-digitalocean/version.rb +5 -0
- data/locales/en.yml +84 -0
- data/test/Vagrantfile +38 -0
- data/test/cookbooks/test/recipes/default.rb +1 -0
- data/test/scripts/provision.sh +3 -0
- data/test/test.sh +14 -0
- data/test/test_id_rsa +27 -0
- data/test/test_id_rsa.pub +1 -0
- data/vagrant-chassis-digitalocean.gemspec +21 -0
- 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
|