vagrant-shell 0.2.3
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +14 -0
- data/CHANGELOG.md +39 -0
- data/Gemfile +3 -0
- data/LICENSE +8 -0
- data/README.md +12 -0
- data/Rakefile +21 -0
- data/lib/vagrant-shell/action/is_created.rb +18 -0
- data/lib/vagrant-shell/action/message_already_created.rb +16 -0
- data/lib/vagrant-shell/action/message_not_created.rb +16 -0
- data/lib/vagrant-shell/action/read_ssh_info.rb +33 -0
- data/lib/vagrant-shell/action/read_state.rb +29 -0
- data/lib/vagrant-shell/action/run_instance.rb +109 -0
- data/lib/vagrant-shell/action/sync_folders.rb +67 -0
- data/lib/vagrant-shell/action/terminate_instance.rb +24 -0
- data/lib/vagrant-shell/action/timed_provision.rb +21 -0
- data/lib/vagrant-shell/action/warn_networks.rb +19 -0
- data/lib/vagrant-shell/action.rb +116 -0
- data/lib/vagrant-shell/config.rb +68 -0
- data/lib/vagrant-shell/errors.rb +31 -0
- data/lib/vagrant-shell/plugin.rb +73 -0
- data/lib/vagrant-shell/provider.rb +50 -0
- data/lib/vagrant-shell/util/timer.rb +17 -0
- data/lib/vagrant-shell/version.rb +5 -0
- data/lib/vagrant-shell.rb +18 -0
- data/locales/en.yml +59 -0
- data/vagrant-shell.gemspec +51 -0
- metadata +73 -0
data/.gitignore
ADDED
data/CHANGELOG.md
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
# 0.2.2 (April 18, 2013)
|
2
|
+
|
3
|
+
* Fix crashing bug with incorrect provisioner arguments.
|
4
|
+
|
5
|
+
# 0.2.1 (April 16, 2013)
|
6
|
+
|
7
|
+
* Got rid of extranneous references to old SSH settings.
|
8
|
+
|
9
|
+
# 0.2.0 (April 16, 2013)
|
10
|
+
|
11
|
+
* Add support for `vagrant ssh -c` [GH-42]
|
12
|
+
* Ability to specify a timeout for waiting for instances to become ready. [GH-44]
|
13
|
+
* Better error message if instance didn't become ready in time.
|
14
|
+
* Connection can now be done using IAM profiles. [GH-41]
|
15
|
+
|
16
|
+
# 0.1.3 (April 9, 2013)
|
17
|
+
|
18
|
+
* The `AWS_ACCESS_KEY` and `AWS_SECRET_KEY` will be used if available
|
19
|
+
and no specific keys are set in the Vagrantfile. [GH-33]
|
20
|
+
* Fix issues with SSH on VPCs, the correct IP is used. [GH-30]
|
21
|
+
* Exclude the ".vagrant" directory from rsync.
|
22
|
+
* Implement `:disabled` flag support for shared folders. [GH-29]
|
23
|
+
* `aws.user_data` to specify user data on the instance. [GH-26]
|
24
|
+
|
25
|
+
# 0.1.2 (March 22, 2013)
|
26
|
+
|
27
|
+
* Choose the proper region when connecting to AWS. [GH-9]
|
28
|
+
* Configurable SSH port. [GH-13]
|
29
|
+
* Support other AWS-compatible API endpoints with `config.endpoint`
|
30
|
+
and `config.version`. [GH-6]
|
31
|
+
* Disable strict host key checking on rsync so known hosts aren't an issue. [GH-7]
|
32
|
+
|
33
|
+
# 0.1.1 (March 18, 2013)
|
34
|
+
|
35
|
+
* Up fog dependency for Vagrant 1.1.1
|
36
|
+
|
37
|
+
# 0.1.0 (March 14, 2013)
|
38
|
+
|
39
|
+
* Initial release.
|
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,8 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
Copyright (c) 2013 Mitchell Hashimoto
|
3
|
+
|
4
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
5
|
+
|
6
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
7
|
+
|
8
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
# Vagrant Shell Provider
|
2
|
+
|
3
|
+
This is a [Vagrant](http://www.vagrantup.com) 1.2+ plugin that adds a
|
4
|
+
shell provider to Vagrant, allowing Vagrant to control and provision
|
5
|
+
machines using shell scripts. It's meant to be customized with scripts
|
6
|
+
for Xen, Docker, etc.
|
7
|
+
|
8
|
+
**NOTE:** This plugin requires Vagrant 1.2+,
|
9
|
+
|
10
|
+
## Usage
|
11
|
+
|
12
|
+
TODO - this gem is a work in progress
|
data/Rakefile
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'bundler/setup'
|
3
|
+
require 'rspec/core/rake_task'
|
4
|
+
|
5
|
+
# Immediately sync all stdout so that tools like buildbot can
|
6
|
+
# immediately load in the output.
|
7
|
+
$stdout.sync = true
|
8
|
+
$stderr.sync = true
|
9
|
+
|
10
|
+
# Change to the directory of this file.
|
11
|
+
Dir.chdir(File.expand_path("../", __FILE__))
|
12
|
+
|
13
|
+
# This installs the tasks that help with gem creation and
|
14
|
+
# publishing.
|
15
|
+
Bundler::GemHelper.install_tasks
|
16
|
+
|
17
|
+
# Install the `spec` task so that we can run tests.
|
18
|
+
RSpec::Core::RakeTask.new
|
19
|
+
|
20
|
+
# Default task is to run the unit tests
|
21
|
+
task :default => "spec"
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module VagrantPlugins
|
2
|
+
module Shell
|
3
|
+
module Action
|
4
|
+
# This can be used with "Call" built-in to check if the machine
|
5
|
+
# is created and branch in the middleware.
|
6
|
+
class IsCreated
|
7
|
+
def initialize(app, env)
|
8
|
+
@app = app
|
9
|
+
end
|
10
|
+
|
11
|
+
def call(env)
|
12
|
+
env[:result] = env[:machine].state.id != :not_created
|
13
|
+
@app.call(env)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module VagrantPlugins
|
2
|
+
module Shell
|
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_shell.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 Shell
|
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_shell.not_created"))
|
11
|
+
@app.call(env)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require "log4r"
|
2
|
+
|
3
|
+
module VagrantPlugins
|
4
|
+
module Shell
|
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_shell::action::read_ssh_info")
|
12
|
+
end
|
13
|
+
|
14
|
+
def call(env)
|
15
|
+
env[:machine_ssh_info] = read_ssh_info(env[:machine])
|
16
|
+
|
17
|
+
@app.call(env)
|
18
|
+
end
|
19
|
+
|
20
|
+
def read_ssh_info(machine)
|
21
|
+
return nil if machine.id.nil?
|
22
|
+
|
23
|
+
# Read the DNS info
|
24
|
+
host,port = `#{machine.provider_config.script} ssh-info #{machine.id}`.split(/\s+/)[0,2]
|
25
|
+
return {
|
26
|
+
:host => host,
|
27
|
+
:port => port
|
28
|
+
}
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require "log4r"
|
2
|
+
|
3
|
+
module VagrantPlugins
|
4
|
+
module Shell
|
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_shell::action::read_state")
|
12
|
+
end
|
13
|
+
|
14
|
+
def call(env)
|
15
|
+
env[:machine_state_id] = read_state(env[:machine])
|
16
|
+
|
17
|
+
@app.call(env)
|
18
|
+
end
|
19
|
+
|
20
|
+
def read_state(machine)
|
21
|
+
return :not_created if machine.id.nil?
|
22
|
+
|
23
|
+
# Return the state
|
24
|
+
`#{machine.provider_config.script} read-state #{machine.id}`
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,109 @@
|
|
1
|
+
require "log4r"
|
2
|
+
require "pp"
|
3
|
+
|
4
|
+
require 'vagrant/util/retryable'
|
5
|
+
|
6
|
+
require 'vagrant-shell/util/timer'
|
7
|
+
|
8
|
+
module VagrantPlugins
|
9
|
+
module Shell
|
10
|
+
module Action
|
11
|
+
# This runs the configured instance.
|
12
|
+
class RunInstance
|
13
|
+
include Vagrant::Util::Retryable
|
14
|
+
|
15
|
+
def initialize(app, env)
|
16
|
+
@app = app
|
17
|
+
@logger = Log4r::Logger.new("vagrant_shell::action::run_instance")
|
18
|
+
end
|
19
|
+
|
20
|
+
def call(env)
|
21
|
+
# Initialize metrics if they haven't been
|
22
|
+
env[:metrics] ||= {}
|
23
|
+
|
24
|
+
# Get the configs
|
25
|
+
provider_config = env[:machine].provider_config
|
26
|
+
ami = provider_config.ami
|
27
|
+
user_data = provider_config.user_data
|
28
|
+
|
29
|
+
# Launch!
|
30
|
+
env[:ui].info(I18n.t("vagrant_shell.launching_instance"))
|
31
|
+
env[:ui].info(" -- AMI: #{ami}")
|
32
|
+
|
33
|
+
begin
|
34
|
+
# Immediately save the ID since it is created at this point.
|
35
|
+
env[:machine].id = `#{env[:machine].provider_config.script} run-instance #{ami}`.split(/\s+/)[0]
|
36
|
+
rescue Errors::ComputeError => e
|
37
|
+
raise Errors::ShellError, :message => e.message
|
38
|
+
end
|
39
|
+
|
40
|
+
|
41
|
+
# Wait for the instance to be ready first
|
42
|
+
env[:metrics]["instance_ready_time"] = Util::Timer.time do
|
43
|
+
tries = provider_config.instance_ready_timeout / 2
|
44
|
+
|
45
|
+
env[:ui].info(I18n.t("vagrant_shell.waiting_for_ready"))
|
46
|
+
begin
|
47
|
+
retryable(:on => Shell::Errors::TimeoutError, :tries => tries) do
|
48
|
+
# If we're interrupted don't worry about waiting
|
49
|
+
next if env[:interrupted]
|
50
|
+
|
51
|
+
# Wait for the server to be ready
|
52
|
+
true
|
53
|
+
end
|
54
|
+
rescue Shell::Errors::TimeoutError
|
55
|
+
# Delete the instance
|
56
|
+
terminate(env)
|
57
|
+
|
58
|
+
# Notify the user
|
59
|
+
raise Errors::InstanceReadyTimeout,
|
60
|
+
timeout: provider_config.instance_ready_timeout
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
@logger.info("Time to instance ready: #{env[:metrics]["instance_ready_time"]}")
|
65
|
+
|
66
|
+
if !env[:interrupted]
|
67
|
+
env[:metrics]["instance_ssh_time"] = Util::Timer.time do
|
68
|
+
# Wait for SSH to be ready.
|
69
|
+
env[:ui].info(I18n.t("vagrant_shell.waiting_for_ssh"))
|
70
|
+
while true
|
71
|
+
# If we're interrupted then just back out
|
72
|
+
break if env[:interrupted]
|
73
|
+
break if env[:machine].communicate.ready?
|
74
|
+
sleep 2
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
@logger.info("Time for SSH ready: #{env[:metrics]["instance_ssh_time"]}")
|
79
|
+
|
80
|
+
# Ready and booted!
|
81
|
+
env[:ui].info(I18n.t("vagrant_shell.ready"))
|
82
|
+
end
|
83
|
+
|
84
|
+
# Terminate the instance if we were interrupted
|
85
|
+
terminate(env) if env[:interrupted]
|
86
|
+
|
87
|
+
@app.call(env)
|
88
|
+
end
|
89
|
+
|
90
|
+
def recover(env)
|
91
|
+
return if env["vagrant.error"].is_a?(Vagrant::Errors::VagrantError)
|
92
|
+
|
93
|
+
if env[:machine].provider.state.id != :not_created
|
94
|
+
# Undo the import
|
95
|
+
terminate(env)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
def terminate(env)
|
100
|
+
destroy_env = env.dup
|
101
|
+
destroy_env.delete(:interrupted)
|
102
|
+
destroy_env[:config_validate] = false
|
103
|
+
destroy_env[:force_confirm_destroy] = true
|
104
|
+
env[:action_runner].run(Action.action_destroy, destroy_env)
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
require "log4r"
|
2
|
+
|
3
|
+
require "vagrant/util/subprocess"
|
4
|
+
|
5
|
+
require "vagrant/util/scoped_hash_override"
|
6
|
+
|
7
|
+
module VagrantPlugins
|
8
|
+
module Shell
|
9
|
+
module Action
|
10
|
+
# This middleware uses `rsync` to sync the folders over to the
|
11
|
+
# Shell instance.
|
12
|
+
class SyncFolders
|
13
|
+
include Vagrant::Util::ScopedHashOverride
|
14
|
+
|
15
|
+
def initialize(app, env)
|
16
|
+
@app = app
|
17
|
+
@logger = Log4r::Logger.new("vagrant_shell::action::sync_folders")
|
18
|
+
end
|
19
|
+
|
20
|
+
def call(env)
|
21
|
+
@app.call(env)
|
22
|
+
|
23
|
+
ssh_info = env[:machine].ssh_info
|
24
|
+
|
25
|
+
env[:machine].config.vm.synced_folders.each do |id, data|
|
26
|
+
data = scoped_hash_override(data, :shell)
|
27
|
+
|
28
|
+
# Ignore disabled shared folders
|
29
|
+
next if data[:disabled]
|
30
|
+
|
31
|
+
hostpath = File.expand_path(data[:hostpath], env[:root_path])
|
32
|
+
guestpath = data[:guestpath]
|
33
|
+
|
34
|
+
# Make sure there is a trailing slash on the host path to
|
35
|
+
# avoid creating an additional directory with rsync
|
36
|
+
hostpath = "#{hostpath}/" if hostpath !~ /\/$/
|
37
|
+
|
38
|
+
env[:ui].info(I18n.t("vagrant_shell.rsync_folder",
|
39
|
+
:hostpath => hostpath,
|
40
|
+
:guestpath => guestpath))
|
41
|
+
|
42
|
+
# Create the guest path
|
43
|
+
env[:machine].communicate.sudo("mkdir -p '#{guestpath}'")
|
44
|
+
env[:machine].communicate.sudo(
|
45
|
+
"chown #{ssh_info[:username]} '#{guestpath}'")
|
46
|
+
|
47
|
+
# Rsync over to the guest path using the SSH info
|
48
|
+
command = [
|
49
|
+
"rsync", "--verbose", "--archive", "-z",
|
50
|
+
"--exclude", ".vagrant/",
|
51
|
+
"-e", "ssh -p #{ssh_info[:port]} -o StrictHostKeyChecking=no -i '#{ssh_info[:private_key_path]}'",
|
52
|
+
hostpath,
|
53
|
+
"#{ssh_info[:username]}@#{ssh_info[:host]}:#{guestpath}"]
|
54
|
+
|
55
|
+
r = Vagrant::Util::Subprocess.execute(*command)
|
56
|
+
if r.exit_code != 0
|
57
|
+
raise Errors::RsyncError,
|
58
|
+
:guestpath => guestpath,
|
59
|
+
:hostpath => hostpath,
|
60
|
+
:stderr => r.stderr
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require "log4r"
|
2
|
+
|
3
|
+
module VagrantPlugins
|
4
|
+
module Shell
|
5
|
+
module Action
|
6
|
+
# This terminates the running instance.
|
7
|
+
class TerminateInstance
|
8
|
+
def initialize(app, env)
|
9
|
+
@app = app
|
10
|
+
@logger = Log4r::Logger.new("vagrant_shell::action::terminate_instance")
|
11
|
+
end
|
12
|
+
|
13
|
+
def call(env)
|
14
|
+
# Destroy the server and remove the tracking ID
|
15
|
+
env[:ui].info(I18n.t("vagrant_shell.terminating"))
|
16
|
+
`#{env[:machine].provider_config.script} terminate-instance #{env[:machine].id}`
|
17
|
+
env[:machine].id = nil
|
18
|
+
|
19
|
+
@app.call(env)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require "vagrant-shell/util/timer"
|
2
|
+
|
3
|
+
module VagrantPlugins
|
4
|
+
module Shell
|
5
|
+
module Action
|
6
|
+
# This is the same as the builtin provision except it times the
|
7
|
+
# provisioner runs.
|
8
|
+
class TimedProvision < Vagrant::Action::Builtin::Provision
|
9
|
+
def run_provisioner(env, name, p)
|
10
|
+
timer = Util::Timer.time do
|
11
|
+
super
|
12
|
+
end
|
13
|
+
|
14
|
+
env[:metrics] ||= {}
|
15
|
+
env[:metrics]["provisioner_times"] ||= []
|
16
|
+
env[:metrics]["provisioner_times"] << [name, timer]
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module VagrantPlugins
|
2
|
+
module Shell
|
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_shell.warn_networks"))
|
12
|
+
end
|
13
|
+
|
14
|
+
@app.call(env)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,116 @@
|
|
1
|
+
require "pathname"
|
2
|
+
|
3
|
+
require "vagrant/action/builder"
|
4
|
+
|
5
|
+
module VagrantPlugins
|
6
|
+
module Shell
|
7
|
+
module Action
|
8
|
+
# Include the built-in modules so we can use them as top-level things.
|
9
|
+
include Vagrant::Action::Builtin
|
10
|
+
|
11
|
+
# This action is called to terminate the remote machine.
|
12
|
+
def self.action_destroy
|
13
|
+
Vagrant::Action::Builder.new.tap do |b|
|
14
|
+
b.use ConfigValidate
|
15
|
+
b.use TerminateInstance
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
# This action is called when `vagrant provision` is called.
|
20
|
+
def self.action_provision
|
21
|
+
Vagrant::Action::Builder.new.tap do |b|
|
22
|
+
b.use ConfigValidate
|
23
|
+
b.use Call, IsCreated do |env, b2|
|
24
|
+
if !env[:result]
|
25
|
+
b2.use MessageNotCreated
|
26
|
+
next
|
27
|
+
end
|
28
|
+
|
29
|
+
b2.use Provision
|
30
|
+
b2.use SyncFolders
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
# This action is called to read the SSH info of the machine. The
|
36
|
+
# resulting state is expected to be put into the `:machine_ssh_info`
|
37
|
+
# key.
|
38
|
+
def self.action_read_ssh_info
|
39
|
+
Vagrant::Action::Builder.new.tap do |b|
|
40
|
+
b.use ConfigValidate
|
41
|
+
b.use ReadSSHInfo
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
# This action is called to read the state of the machine. The
|
46
|
+
# resulting state is expected to be put into the `:machine_state_id`
|
47
|
+
# key.
|
48
|
+
def self.action_read_state
|
49
|
+
Vagrant::Action::Builder.new.tap do |b|
|
50
|
+
b.use ConfigValidate
|
51
|
+
b.use ReadState
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
# This action is called to SSH into the machine.
|
56
|
+
def self.action_ssh
|
57
|
+
Vagrant::Action::Builder.new.tap do |b|
|
58
|
+
b.use ConfigValidate
|
59
|
+
b.use Call, IsCreated do |env, b2|
|
60
|
+
if !env[:result]
|
61
|
+
b2.use MessageNotCreated
|
62
|
+
next
|
63
|
+
end
|
64
|
+
|
65
|
+
b2.use SSHExec
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def self.action_ssh_run
|
71
|
+
Vagrant::Action::Builder.new.tap do |b|
|
72
|
+
b.use ConfigValidate
|
73
|
+
b.use Call, IsCreated do |env, b2|
|
74
|
+
if !env[:result]
|
75
|
+
b2.use MessageNotCreated
|
76
|
+
next
|
77
|
+
end
|
78
|
+
|
79
|
+
b2.use SSHRun
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
# This action is called to bring the box up from nothing.
|
85
|
+
def self.action_up
|
86
|
+
Vagrant::Action::Builder.new.tap do |b|
|
87
|
+
b.use ConfigValidate
|
88
|
+
b.use Call, IsCreated do |env, b2|
|
89
|
+
if env[:result]
|
90
|
+
b2.use MessageAlreadyCreated
|
91
|
+
next
|
92
|
+
end
|
93
|
+
|
94
|
+
b2.use TimedProvision
|
95
|
+
b2.use SyncFolders
|
96
|
+
b2.use WarnNetworks
|
97
|
+
b2.use RunInstance
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
# The autoload farm
|
103
|
+
action_root = Pathname.new(File.expand_path("../action", __FILE__))
|
104
|
+
autoload :IsCreated, action_root.join("is_created")
|
105
|
+
autoload :MessageAlreadyCreated, action_root.join("message_already_created")
|
106
|
+
autoload :MessageNotCreated, action_root.join("message_not_created")
|
107
|
+
autoload :ReadSSHInfo, action_root.join("read_ssh_info")
|
108
|
+
autoload :ReadState, action_root.join("read_state")
|
109
|
+
autoload :RunInstance, action_root.join("run_instance")
|
110
|
+
autoload :SyncFolders, action_root.join("sync_folders")
|
111
|
+
autoload :TimedProvision, action_root.join("timed_provision")
|
112
|
+
autoload :WarnNetworks, action_root.join("warn_networks")
|
113
|
+
autoload :TerminateInstance, action_root.join("terminate_instance")
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
require "vagrant"
|
2
|
+
|
3
|
+
module VagrantPlugins
|
4
|
+
module Shell
|
5
|
+
class Config < Vagrant.plugin("2", :config)
|
6
|
+
# The ID of the AMI to use.
|
7
|
+
#
|
8
|
+
# @return [String]
|
9
|
+
attr_accessor :ami
|
10
|
+
|
11
|
+
# The timeout to wait for an instance to become ready.
|
12
|
+
#
|
13
|
+
# @return [Fixnum]
|
14
|
+
attr_accessor :instance_ready_timeout
|
15
|
+
|
16
|
+
# The user data string
|
17
|
+
#
|
18
|
+
# @return [String]
|
19
|
+
attr_accessor :user_data
|
20
|
+
|
21
|
+
# The shell script implementing some tech
|
22
|
+
#
|
23
|
+
# @return [String]
|
24
|
+
attr_accessor :script
|
25
|
+
|
26
|
+
def initialize
|
27
|
+
@ami = UNSET_VALUE
|
28
|
+
@instance_ready_timeout = UNSET_VALUE
|
29
|
+
@user_data = UNSET_VALUE
|
30
|
+
@script = UNSET_VALUE
|
31
|
+
|
32
|
+
# Internal state (prefix with __ so they aren't automatically
|
33
|
+
# merged)
|
34
|
+
@__finalized = false
|
35
|
+
end
|
36
|
+
|
37
|
+
#-------------------------------------------------------------------
|
38
|
+
# Internal methods.
|
39
|
+
#-------------------------------------------------------------------
|
40
|
+
|
41
|
+
def merge(other)
|
42
|
+
super.tap do |result|
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def finalize!
|
47
|
+
# AMI must be nil, since we can't default that
|
48
|
+
@ami = nil if @ami == UNSET_VALUE
|
49
|
+
|
50
|
+
# Set the default timeout for waiting for an instance to be ready
|
51
|
+
@instance_ready_timeout = 120 if @instance_ready_timeout == UNSET_VALUE
|
52
|
+
|
53
|
+
# User Data is nil by default
|
54
|
+
@user_data = nil if @user_data == UNSET_VALUE
|
55
|
+
|
56
|
+
# No default shell script
|
57
|
+
@script = nil if @script == UNSET_VALUE
|
58
|
+
|
59
|
+
# Mark that we finalized
|
60
|
+
@__finalized = true
|
61
|
+
end
|
62
|
+
|
63
|
+
def validate(machine)
|
64
|
+
{ "Shell Provider" => [ ] }
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require "vagrant"
|
2
|
+
|
3
|
+
module VagrantPlugins
|
4
|
+
module Shell
|
5
|
+
module Errors
|
6
|
+
class VagrantShellError < Vagrant::Errors::VagrantError
|
7
|
+
error_namespace("vagrant_shell.errors")
|
8
|
+
end
|
9
|
+
|
10
|
+
class ShellError < VagrantShellError
|
11
|
+
error_key(:shell_error)
|
12
|
+
end
|
13
|
+
|
14
|
+
class TimeoutError < VagrantShellError
|
15
|
+
error_key(:instance_ready_timeout)
|
16
|
+
end
|
17
|
+
|
18
|
+
class ComputeError < VagrantShellError
|
19
|
+
error_key(:instance_ready_timeout)
|
20
|
+
end
|
21
|
+
|
22
|
+
class InstanceReadyTimeout < VagrantShellError
|
23
|
+
error_key(:instance_ready_timeout)
|
24
|
+
end
|
25
|
+
|
26
|
+
class RsyncError < VagrantShellError
|
27
|
+
error_key(:rsync_error)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
begin
|
2
|
+
require "vagrant"
|
3
|
+
rescue LoadError
|
4
|
+
raise "The Vagrant Shell 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.2.0"
|
10
|
+
raise "The Vagrant Shell plugin is only compatible with Vagrant 1.2+"
|
11
|
+
end
|
12
|
+
|
13
|
+
module VagrantPlugins
|
14
|
+
module Shell
|
15
|
+
class Plugin < Vagrant.plugin("2")
|
16
|
+
name "Shell"
|
17
|
+
description <<-DESC
|
18
|
+
This plugin installs a provider that allows Vagrant to manage
|
19
|
+
machines with shell scripts.
|
20
|
+
DESC
|
21
|
+
|
22
|
+
config(:shell, :provider) do
|
23
|
+
require_relative "config"
|
24
|
+
Config
|
25
|
+
end
|
26
|
+
|
27
|
+
provider(:shell) 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", Shell.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_shell")
|
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 Shell
|
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_shell.states.short_#{state_id}")
|
38
|
+
long = I18n.t("vagrant_shell.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
|
+
"Shell (#{id})"
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module VagrantPlugins
|
2
|
+
module Shell
|
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,18 @@
|
|
1
|
+
require "pathname"
|
2
|
+
|
3
|
+
require "vagrant-shell/plugin"
|
4
|
+
|
5
|
+
module VagrantPlugins
|
6
|
+
module Shell
|
7
|
+
lib_path = Pathname.new(File.expand_path("../vagrant-shell", __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,59 @@
|
|
1
|
+
en:
|
2
|
+
vagrant_shell:
|
3
|
+
already_created: |-
|
4
|
+
The machine is already created.
|
5
|
+
launching_instance: |-
|
6
|
+
Launching an instance with the following settings...
|
7
|
+
not_created: |-
|
8
|
+
Instance is not created. Please run `vagrant up` first.
|
9
|
+
ready: |-
|
10
|
+
Machine is booted and ready for use!
|
11
|
+
rsync_folder: |-
|
12
|
+
Rsyncing folder: %{hostpath} => %{guestpath}
|
13
|
+
terminating: |-
|
14
|
+
Terminating the instance...
|
15
|
+
waiting_for_ready: |-
|
16
|
+
Waiting for instance to become "ready"...
|
17
|
+
waiting_for_ssh: |-
|
18
|
+
Waiting for SSH to become available...
|
19
|
+
warn_networks: |-
|
20
|
+
Warning! The Shell provider doesn't support any of the Vagrant
|
21
|
+
high-level network configurations (`config.vm.network`). They
|
22
|
+
will be silently ignored.
|
23
|
+
|
24
|
+
config:
|
25
|
+
ami_required: |-
|
26
|
+
An AMI must be configured via "ami"
|
27
|
+
private_key_missing: |-
|
28
|
+
The specified private key for Shell could not be found
|
29
|
+
|
30
|
+
errors:
|
31
|
+
shell_error: |-
|
32
|
+
There was an error talking to Shell. The error message is shown
|
33
|
+
below:
|
34
|
+
|
35
|
+
%{message}
|
36
|
+
instance_ready_timeout: |-
|
37
|
+
The instance never became "ready" in Shell. The timeout currently
|
38
|
+
set waiting for the instance to become ready is %{timeout} seconds.
|
39
|
+
Please verify that the machine properly boots. If you need more time
|
40
|
+
set the `instance_ready_timeout` configuration on the Shell provider.
|
41
|
+
rsync_error: |-
|
42
|
+
There was an error when attemping to rsync a share folder.
|
43
|
+
Please inspect the error message below for more info.
|
44
|
+
|
45
|
+
Host path: %{hostpath}
|
46
|
+
Guest path: %{guestpath}
|
47
|
+
Error: %{stderr}
|
48
|
+
|
49
|
+
states:
|
50
|
+
short_not_created: |-
|
51
|
+
not created
|
52
|
+
long_not_created: |-
|
53
|
+
The Shell instance is not created. Run `vagrant up` to create it.
|
54
|
+
|
55
|
+
short_running: |-
|
56
|
+
running
|
57
|
+
long_running: |-
|
58
|
+
The Shell instance is running. To stop this machine, you can run
|
59
|
+
`vagrant halt`. To destroy the machine, you can run `vagrant destroy`.
|
@@ -0,0 +1,51 @@
|
|
1
|
+
$:.unshift File.expand_path("../lib", __FILE__)
|
2
|
+
require "vagrant-shell/version"
|
3
|
+
|
4
|
+
Gem::Specification.new do |s|
|
5
|
+
s.name = "vagrant-shell"
|
6
|
+
s.version = VagrantPlugins::Shell::VERSION
|
7
|
+
s.platform = Gem::Platform::RUBY
|
8
|
+
s.authors = [ "Tom Bombadil", "Mitchell Hashimoto" ]
|
9
|
+
s.email = [ "amanibhavam@destructuring.org", "mitchell@hashicorp.com" ]
|
10
|
+
s.homepage = "http://destructuring.org/vagrant-shell"
|
11
|
+
s.summary = "Enables Vagrant to manage machines using shell scripts"
|
12
|
+
s.description = "Enables Vagrant to manage machines using shell scripts"
|
13
|
+
|
14
|
+
s.required_rubygems_version = ">= 1.3.6"
|
15
|
+
s.rubyforge_project = "vagrant-shell"
|
16
|
+
|
17
|
+
# The following block of code determines the files that should be included
|
18
|
+
# in the gem. It does this by reading all the files in the directory where
|
19
|
+
# this gemspec is, and parsing out the ignored files from the gitignore.
|
20
|
+
# Note that the entire gitignore(5) syntax is not supported, specifically
|
21
|
+
# the "!" syntax, but it should mostly work correctly.
|
22
|
+
root_path = File.dirname(__FILE__)
|
23
|
+
all_files = Dir.chdir(root_path) { Dir.glob("**/{*,.*}") }
|
24
|
+
all_files.reject! { |file| [".", ".."].include?(File.basename(file)) }
|
25
|
+
gitignore_path = File.join(root_path, ".gitignore")
|
26
|
+
gitignore = File.readlines(gitignore_path)
|
27
|
+
gitignore.map! { |line| line.chomp.strip }
|
28
|
+
gitignore.reject! { |line| line.empty? || line =~ /^(#|!)/ }
|
29
|
+
|
30
|
+
unignored_files = all_files.reject do |file|
|
31
|
+
# Ignore any directories, the gemspec only cares about files
|
32
|
+
next true if File.directory?(file)
|
33
|
+
|
34
|
+
# Ignore any paths that match anything in the gitignore. We do
|
35
|
+
# two tests here:
|
36
|
+
#
|
37
|
+
# - First, test to see if the entire path matches the gitignore.
|
38
|
+
# - Second, match if the basename does, this makes it so that things
|
39
|
+
# like '.DS_Store' will match sub-directories too (same behavior
|
40
|
+
# as git).
|
41
|
+
#
|
42
|
+
gitignore.any? do |ignore|
|
43
|
+
File.fnmatch(ignore, file, File::FNM_PATHNAME) ||
|
44
|
+
File.fnmatch(ignore, File.basename(file), File::FNM_PATHNAME)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
s.files = unignored_files
|
49
|
+
s.executables = unignored_files.map { |f| f[/^bin\/(.*)/, 1] }.compact
|
50
|
+
s.require_path = 'lib'
|
51
|
+
end
|
metadata
ADDED
@@ -0,0 +1,73 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: vagrant-shell
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.2.3
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Tom Bombadil
|
9
|
+
- Mitchell Hashimoto
|
10
|
+
autorequire:
|
11
|
+
bindir: bin
|
12
|
+
cert_chain: []
|
13
|
+
date: 2013-05-01 00:00:00.000000000 Z
|
14
|
+
dependencies: []
|
15
|
+
description: Enables Vagrant to manage machines using shell scripts
|
16
|
+
email:
|
17
|
+
- amanibhavam@destructuring.org
|
18
|
+
- mitchell@hashicorp.com
|
19
|
+
executables: []
|
20
|
+
extensions: []
|
21
|
+
extra_rdoc_files: []
|
22
|
+
files:
|
23
|
+
- CHANGELOG.md
|
24
|
+
- Gemfile
|
25
|
+
- lib/vagrant-shell/action/is_created.rb
|
26
|
+
- lib/vagrant-shell/action/message_already_created.rb
|
27
|
+
- lib/vagrant-shell/action/message_not_created.rb
|
28
|
+
- lib/vagrant-shell/action/read_ssh_info.rb
|
29
|
+
- lib/vagrant-shell/action/read_state.rb
|
30
|
+
- lib/vagrant-shell/action/run_instance.rb
|
31
|
+
- lib/vagrant-shell/action/sync_folders.rb
|
32
|
+
- lib/vagrant-shell/action/terminate_instance.rb
|
33
|
+
- lib/vagrant-shell/action/timed_provision.rb
|
34
|
+
- lib/vagrant-shell/action/warn_networks.rb
|
35
|
+
- lib/vagrant-shell/action.rb
|
36
|
+
- lib/vagrant-shell/config.rb
|
37
|
+
- lib/vagrant-shell/errors.rb
|
38
|
+
- lib/vagrant-shell/plugin.rb
|
39
|
+
- lib/vagrant-shell/provider.rb
|
40
|
+
- lib/vagrant-shell/util/timer.rb
|
41
|
+
- lib/vagrant-shell/version.rb
|
42
|
+
- lib/vagrant-shell.rb
|
43
|
+
- LICENSE
|
44
|
+
- locales/en.yml
|
45
|
+
- Rakefile
|
46
|
+
- README.md
|
47
|
+
- vagrant-shell.gemspec
|
48
|
+
- .gitignore
|
49
|
+
homepage: http://destructuring.org/vagrant-shell
|
50
|
+
licenses: []
|
51
|
+
post_install_message:
|
52
|
+
rdoc_options: []
|
53
|
+
require_paths:
|
54
|
+
- lib
|
55
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
56
|
+
none: false
|
57
|
+
requirements:
|
58
|
+
- - ! '>='
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: '0'
|
61
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
62
|
+
none: false
|
63
|
+
requirements:
|
64
|
+
- - ! '>='
|
65
|
+
- !ruby/object:Gem::Version
|
66
|
+
version: 1.3.6
|
67
|
+
requirements: []
|
68
|
+
rubyforge_project: vagrant-shell
|
69
|
+
rubygems_version: 1.8.25
|
70
|
+
signing_key:
|
71
|
+
specification_version: 3
|
72
|
+
summary: Enables Vagrant to manage machines using shell scripts
|
73
|
+
test_files: []
|