vagrant 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (87) hide show
  1. data/.gitignore +11 -0
  2. data/Gemfile +17 -0
  3. data/README.md +45 -0
  4. data/Rakefile +41 -0
  5. data/VERSION +1 -0
  6. data/bin/.gitignore +0 -0
  7. data/bin/vagrant +15 -0
  8. data/bin/vagrant-box +35 -0
  9. data/bin/vagrant-down +28 -0
  10. data/bin/vagrant-halt +29 -0
  11. data/bin/vagrant-init +28 -0
  12. data/bin/vagrant-package +30 -0
  13. data/bin/vagrant-reload +30 -0
  14. data/bin/vagrant-resume +28 -0
  15. data/bin/vagrant-ssh +28 -0
  16. data/bin/vagrant-suspend +28 -0
  17. data/bin/vagrant-up +30 -0
  18. data/config/default.rb +29 -0
  19. data/lib/vagrant.rb +14 -0
  20. data/lib/vagrant/actions/base.rb +93 -0
  21. data/lib/vagrant/actions/box/add.rb +22 -0
  22. data/lib/vagrant/actions/box/destroy.rb +14 -0
  23. data/lib/vagrant/actions/box/download.rb +63 -0
  24. data/lib/vagrant/actions/box/unpackage.rb +49 -0
  25. data/lib/vagrant/actions/runner.rb +128 -0
  26. data/lib/vagrant/actions/vm/destroy.rb +14 -0
  27. data/lib/vagrant/actions/vm/down.rb +12 -0
  28. data/lib/vagrant/actions/vm/export.rb +41 -0
  29. data/lib/vagrant/actions/vm/forward_ports.rb +32 -0
  30. data/lib/vagrant/actions/vm/halt.rb +14 -0
  31. data/lib/vagrant/actions/vm/import.rb +17 -0
  32. data/lib/vagrant/actions/vm/move_hard_drive.rb +53 -0
  33. data/lib/vagrant/actions/vm/package.rb +61 -0
  34. data/lib/vagrant/actions/vm/provision.rb +71 -0
  35. data/lib/vagrant/actions/vm/reload.rb +17 -0
  36. data/lib/vagrant/actions/vm/resume.rb +16 -0
  37. data/lib/vagrant/actions/vm/shared_folders.rb +69 -0
  38. data/lib/vagrant/actions/vm/start.rb +50 -0
  39. data/lib/vagrant/actions/vm/suspend.rb +16 -0
  40. data/lib/vagrant/actions/vm/up.rb +35 -0
  41. data/lib/vagrant/box.rb +129 -0
  42. data/lib/vagrant/busy.rb +73 -0
  43. data/lib/vagrant/commands.rb +174 -0
  44. data/lib/vagrant/config.rb +156 -0
  45. data/lib/vagrant/downloaders/base.rb +13 -0
  46. data/lib/vagrant/downloaders/file.rb +21 -0
  47. data/lib/vagrant/downloaders/http.rb +47 -0
  48. data/lib/vagrant/env.rb +140 -0
  49. data/lib/vagrant/ssh.rb +43 -0
  50. data/lib/vagrant/util.rb +45 -0
  51. data/lib/vagrant/vm.rb +57 -0
  52. data/script/vagrant-ssh-expect.sh +22 -0
  53. data/templates/Vagrantfile +8 -0
  54. data/test/test_helper.rb +91 -0
  55. data/test/vagrant/actions/base_test.rb +32 -0
  56. data/test/vagrant/actions/box/add_test.rb +37 -0
  57. data/test/vagrant/actions/box/destroy_test.rb +18 -0
  58. data/test/vagrant/actions/box/download_test.rb +118 -0
  59. data/test/vagrant/actions/box/unpackage_test.rb +101 -0
  60. data/test/vagrant/actions/runner_test.rb +236 -0
  61. data/test/vagrant/actions/vm/destroy_test.rb +24 -0
  62. data/test/vagrant/actions/vm/down_test.rb +32 -0
  63. data/test/vagrant/actions/vm/export_test.rb +88 -0
  64. data/test/vagrant/actions/vm/forward_ports_test.rb +50 -0
  65. data/test/vagrant/actions/vm/halt_test.rb +27 -0
  66. data/test/vagrant/actions/vm/import_test.rb +36 -0
  67. data/test/vagrant/actions/vm/move_hard_drive_test.rb +108 -0
  68. data/test/vagrant/actions/vm/package_test.rb +155 -0
  69. data/test/vagrant/actions/vm/provision_test.rb +103 -0
  70. data/test/vagrant/actions/vm/reload_test.rb +44 -0
  71. data/test/vagrant/actions/vm/resume_test.rb +27 -0
  72. data/test/vagrant/actions/vm/shared_folders_test.rb +117 -0
  73. data/test/vagrant/actions/vm/start_test.rb +55 -0
  74. data/test/vagrant/actions/vm/suspend_test.rb +27 -0
  75. data/test/vagrant/actions/vm/up_test.rb +76 -0
  76. data/test/vagrant/box_test.rb +92 -0
  77. data/test/vagrant/busy_test.rb +81 -0
  78. data/test/vagrant/commands_test.rb +252 -0
  79. data/test/vagrant/config_test.rb +123 -0
  80. data/test/vagrant/downloaders/base_test.rb +20 -0
  81. data/test/vagrant/downloaders/file_test.rb +32 -0
  82. data/test/vagrant/downloaders/http_test.rb +40 -0
  83. data/test/vagrant/env_test.rb +293 -0
  84. data/test/vagrant/ssh_test.rb +95 -0
  85. data/test/vagrant/util_test.rb +64 -0
  86. data/test/vagrant/vm_test.rb +96 -0
  87. metadata +275 -0
@@ -0,0 +1,16 @@
1
+ module Vagrant
2
+ module Actions
3
+ module VM
4
+ class Suspend < Base
5
+ def execute!
6
+ if !@runner.vm.running?
7
+ raise ActionException.new("The vagrant virtual environment you are trying to suspend must be running to be suspended.")
8
+ end
9
+
10
+ logger.info "Saving VM state and suspending execution..."
11
+ @runner.vm.save_state(true)
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,35 @@
1
+ module Vagrant
2
+ module Actions
3
+ module VM
4
+ class Up < Base
5
+ def prepare
6
+ # Up is a "meta-action" so it really just queues up a bunch
7
+ # of other actions in its place:
8
+ steps = [Import, ForwardPorts, SharedFolders, Start]
9
+ steps << Provision if Vagrant.config.chef.enabled
10
+ steps.insert(0, MoveHardDrive) if Vagrant.config.vm.hd_location
11
+
12
+ steps.each do |action_klass|
13
+ @runner.add_action(action_klass)
14
+ end
15
+ end
16
+
17
+ def after_import
18
+ persist
19
+ setup_mac_address
20
+ end
21
+
22
+ def persist
23
+ logger.info "Persisting the VM UUID (#{@runner.vm.uuid})..."
24
+ Env.persist_vm(@runner.vm)
25
+ end
26
+
27
+ def setup_mac_address
28
+ logger.info "Matching MAC addresses..."
29
+ @runner.vm.nics.first.macaddress = Vagrant.config[:vm][:base_mac]
30
+ @runner.vm.save(true)
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,129 @@
1
+ module Vagrant
2
+ # Represents a "box," which is simply a packaged vagrant environment.
3
+ # Boxes are simply `tar` files which contain an exported VirtualBox
4
+ # virtual machine, at the least. They are created with `vagrant package`
5
+ # and may contain additional files if specified by the creator. This
6
+ # class serves to help manage these boxes, although most of the logic
7
+ # is kicked out to actions.
8
+ #
9
+ # What can the {Box} class do?
10
+ #
11
+ # * Find boxes
12
+ # * Add existing boxes (from some URI)
13
+ # * Delete existing boxes
14
+ #
15
+ # # Finding Boxes
16
+ #
17
+ # Using the {Box.find} method, you can search for existing boxes. This
18
+ # method will return `nil` if none is found or an instance of {Box}
19
+ # otherwise.
20
+ #
21
+ # box = Vagrant::Box.find("base")
22
+ # if box.nil?
23
+ # puts "Box not found!"
24
+ # else
25
+ # puts "Box exists at #{box.directory}"
26
+ # end
27
+ #
28
+ # # Adding a Box
29
+ #
30
+ # Boxes can be added from any URI. Some schemas aren't supported; if this
31
+ # is the case, the error will output to the logger.
32
+ #
33
+ # Vagrant::Box.add("foo", "http://myfiles.com/foo.box")
34
+ #
35
+ # # Destroying a box
36
+ #
37
+ # Boxes can be deleted as well. This method is _final_ and there is no way
38
+ # to undo this action once it is completed.
39
+ #
40
+ # box = Vagrant::Box.find("foo")
41
+ # box.destroy
42
+ #
43
+ class Box < Actions::Runner
44
+ # The name of the box.
45
+ attr_accessor :name
46
+
47
+ # The URI for a new box. This is not available for existing boxes.
48
+ attr_accessor :uri
49
+
50
+ # The temporary path to the downloaded or copied box. This should
51
+ # only be used internally.
52
+ attr_accessor :temp_path
53
+
54
+ class <<self
55
+ # Finds a box with the given name. This method searches for a box
56
+ # with the given name, returning `nil` if none is found or returning
57
+ # a {Box} instance otherwise.
58
+ #
59
+ # @param [String] name The name of the box
60
+ # @return [Box] Instance of {Box} representing the box found
61
+ def find(name)
62
+ return nil unless File.directory?(directory(name))
63
+ new(name)
64
+ end
65
+
66
+ # Adds a new box with given name from the given URI. This method
67
+ # begins the process of adding a box from a given URI by setting up
68
+ # the {Box} instance and calling {#add}.
69
+ #
70
+ # @param [String] name The name of the box
71
+ # @param [String] uri URI to the box file
72
+ def add(name, uri)
73
+ box = new
74
+ box.name = name
75
+ box.uri = uri
76
+ box.add
77
+ end
78
+
79
+ # Returns the directory to a box of the given name. The name given
80
+ # as a parameter is not checked for existence; this method simply
81
+ # returns the directory which would be used if the box did exist.
82
+ #
83
+ # @param [String] name Name of the box whose directory you're interested in.
84
+ # @return [String] Full path to the box directory.
85
+ def directory(name)
86
+ File.join(Env.boxes_path, name)
87
+ end
88
+ end
89
+
90
+ # Creates a new box instance. Given an optional `name` parameter,
91
+ # newly created instance will have that name, otherwise it defaults
92
+ # to `nil`.
93
+ #
94
+ # **Note:** This method does not actually _create_ the box, but merely
95
+ # returns a new, abstract representation of it. To add a box, see {#add}.
96
+ def initialize(name=nil)
97
+ @name = name
98
+ end
99
+
100
+ # Returns path to the OVF file of the box. The OVF file is an open
101
+ # virtual machine file which contains specifications of the exported
102
+ # virtual machine this box contains.
103
+ #
104
+ # @return [String]
105
+ def ovf_file
106
+ File.join(directory, Vagrant.config.vm.box_ovf)
107
+ end
108
+
109
+ # Begins the process of adding a box to the vagrant installation. This
110
+ # method requires that `name` and `uri` be set. The logic of this method
111
+ # is kicked out to the {Actions::Box::Add add box} action.
112
+ def add
113
+ execute!(Actions::Box::Add)
114
+ end
115
+
116
+ # Beings the process of destroying this box.
117
+ def destroy
118
+ execute!(Actions::Box::Destroy)
119
+ end
120
+
121
+ # Returns the directory to the location of this boxes content in the local
122
+ # filesystem.
123
+ #
124
+ # @return [String]
125
+ def directory
126
+ self.class.directory(self.name)
127
+ end
128
+ end
129
+ end
@@ -0,0 +1,73 @@
1
+ module Vagrant
2
+ def self.busy?
3
+ Busy.busy?
4
+ end
5
+
6
+ def self.busy(&block)
7
+ Busy.busy(&block)
8
+ end
9
+
10
+ class Busy
11
+ extend Vagrant::Util
12
+
13
+ @@busy = false
14
+ @@mutex = Mutex.new
15
+ @@trap_thread = nil
16
+
17
+ class << self
18
+ def busy?
19
+ @@busy
20
+ end
21
+
22
+ def busy=(val)
23
+ @@busy = val
24
+ end
25
+
26
+ def busy(&block)
27
+ @@mutex.synchronize do
28
+ begin
29
+ Signal.trap("INT") { wait_for_not_busy }
30
+ Busy.busy = true
31
+ runner = Thread.new(block) { block.call }
32
+ runner.join
33
+ ensure
34
+ # In the case an exception is thrown, make sure we restore
35
+ # busy back to some sane state.
36
+ Busy.busy = false
37
+
38
+ # Make sure that the trap thread completes, if it is running
39
+ trap_thread.join if trap_thread
40
+
41
+ # And restore the INT trap to the default
42
+ Signal.trap("INT", "DEFAULT")
43
+ end
44
+ end
45
+ end
46
+
47
+ def wait_for_not_busy(sleeptime=5)
48
+ @@trap_thread ||= Thread.new do
49
+ # Wait while the app is busy
50
+ loop do
51
+ break unless busy?
52
+ logger.info "Waiting for vagrant to clean itself up..."
53
+ sleep sleeptime
54
+ end
55
+
56
+ # Exit out of the entire script
57
+ logger.info "Exiting vagrant..."
58
+ exit
59
+ end
60
+ end
61
+
62
+ # Used for testing
63
+ def reset_trap_thread!
64
+ @@trap_thread = nil
65
+ end
66
+
67
+ # Returns the trap thread
68
+ def trap_thread
69
+ @@trap_thread
70
+ end
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,174 @@
1
+ module Vagrant
2
+ # Contains all the command-line commands invoked by the
3
+ # binaries. Having them all in one location assists with
4
+ # documentation and also takes the commands out of some of
5
+ # the other classes.
6
+ class Commands
7
+ extend Vagrant::Util
8
+
9
+ class << self
10
+ # Initializes a directory for use with vagrant. This command copies an
11
+ # initial `Vagrantfile` into the current working directory so you can
12
+ # begin using vagrant. The configuration file contains some documentation
13
+ # to get you started.
14
+ def init
15
+ rootfile_path = File.join(Dir.pwd, Env::ROOTFILE_NAME)
16
+ if File.exist?(rootfile_path)
17
+ error_and_exit(<<-error)
18
+ It looks like this directory is already setup for vagrant! (A #{Env::ROOTFILE_NAME}
19
+ already exists.)
20
+ error
21
+ end
22
+
23
+ # Copy over the rootfile template into this directory
24
+ File.copy(File.join(PROJECT_ROOT, "templates", Env::ROOTFILE_NAME), rootfile_path)
25
+ end
26
+
27
+ # Bring up a vagrant instance. This handles everything from importing
28
+ # the base VM, setting up shared folders, forwarded ports, etc to
29
+ # provisioning the instance with chef. {up} also starts the instance,
30
+ # running it in the background.
31
+ def up
32
+ Env.load!
33
+
34
+ if Env.persisted_vm
35
+ logger.info "VM already created. Starting VM if its not already running..."
36
+ Env.persisted_vm.start
37
+ else
38
+ Env.require_box
39
+ VM.execute!(Actions::VM::Up)
40
+ end
41
+ end
42
+
43
+ # Tear down a vagrant instance. This not only shuts down the instance
44
+ # (if its running), but also deletes it from the system, including the
45
+ # hard disks associated with it.
46
+ #
47
+ # This command requires that an instance already be brought up with
48
+ # `vagrant up`.
49
+ def down
50
+ Env.load!
51
+ Env.require_persisted_vm
52
+ Env.persisted_vm.destroy
53
+ end
54
+
55
+ # Reload the environment. This is almost equivalent to the {up} command
56
+ # except that it doesn't import the VM and do the initialize bootstrapping
57
+ # of the instance. Instead, it forces a shutdown (if its running) of the
58
+ # VM, updates the metadata (shared folders, forwarded ports), restarts
59
+ # the VM, and then reruns the provisioning if enabled.
60
+ def reload
61
+ Env.load!
62
+ Env.require_persisted_vm
63
+ Env.persisted_vm.execute!(Actions::VM::Reload)
64
+ end
65
+
66
+ # SSH into the vagrant instance. This will setup an SSH connection into
67
+ # the vagrant instance, replacing the running ruby process with the SSH
68
+ # connection.
69
+ #
70
+ # This command requires that an instance already be brought up with
71
+ # `vagrant up`.
72
+ def ssh
73
+ Env.load!
74
+ Env.require_persisted_vm
75
+ SSH.connect
76
+ end
77
+
78
+ # Halts a running vagrant instance. This forcibly halts the instance;
79
+ # it is the equivalent of pulling the power on a machine. The instance
80
+ # can be restarted again with {up}.
81
+ #
82
+ # This command requires than an instance already be brought up with
83
+ # `vagrant up`.
84
+ def halt
85
+ Env.load!
86
+ Env.require_persisted_vm
87
+ Env.persisted_vm.execute!(Actions::VM::Halt)
88
+ end
89
+
90
+ # Suspend a running vagrant instance. This suspends the instance, saving
91
+ # the state of the VM and "pausing" it. The instance can be resumed
92
+ # again with {resume}.
93
+ #
94
+ # This command requires that an instance already be brought up with
95
+ # `vagrant up`.
96
+ def suspend
97
+ Env.load!
98
+ Env.require_persisted_vm
99
+ Env.persisted_vm.suspend
100
+ end
101
+
102
+ # Resume a running vagrant instance. This resumes an already suspended
103
+ # instance (from {suspend}).
104
+ #
105
+ # This command requires that an instance already be brought up with
106
+ # `vagrant up`.
107
+ def resume
108
+ Env.load!
109
+ Env.require_persisted_vm
110
+ Env.persisted_vm.resume
111
+ end
112
+
113
+ # Export and package the current vm
114
+ #
115
+ # This command requires that an instance be powered off
116
+ def package(out_path=nil, include_files=[])
117
+ Env.load!
118
+ Env.require_persisted_vm
119
+ error_and_exit(<<-error) unless Env.persisted_vm.powered_off?
120
+ The vagrant virtual environment you are trying to package must be powered off
121
+ error
122
+
123
+ Env.persisted_vm.package(out_path, include_files)
124
+ end
125
+
126
+ # Manages the `vagrant box` command, allowing the user to add
127
+ # and remove boxes. This single command, given an array, determines
128
+ # which action to take and calls the respective action method
129
+ # (see {box_add} and {box_remove})
130
+ def box(argv)
131
+ Env.load!(:suppress_errors => true)
132
+
133
+ sub_commands = ["add", "remove"]
134
+
135
+ if !sub_commands.include?(argv[0])
136
+ error_and_exit(<<-error)
137
+ Please specify a valid action to take on the boxes, either
138
+ `add` or `remove`. Examples:
139
+
140
+ vagrant box add name uri
141
+ vagrant box remove name
142
+ error
143
+ end
144
+
145
+ send("box_#{argv[0]}", *argv[1..-1])
146
+ end
147
+
148
+ # Adds a box to the local filesystem, given a URI.
149
+ def box_add(name, path)
150
+ Box.add(name, path)
151
+ end
152
+
153
+ # Removes a box.
154
+ def box_remove(name)
155
+ box = Box.find(name)
156
+ if box.nil?
157
+ error_and_exit(<<-error)
158
+ The box you're attempting to remove does not exist!
159
+ error
160
+ return # for tests
161
+ end
162
+
163
+ box.destroy
164
+ end
165
+
166
+ private
167
+
168
+ def act_on_vm(&block)
169
+ yield Env.persisted_vm
170
+ Env.persisted_vm.execute!
171
+ end
172
+ end
173
+ end
174
+ end
@@ -0,0 +1,156 @@
1
+ module Vagrant
2
+ def self.config
3
+ Config.config
4
+ end
5
+
6
+ class Config
7
+ @config = nil
8
+ @config_runners = []
9
+
10
+ class << self
11
+ def reset!
12
+ @config = nil
13
+ config_runners.clear
14
+ end
15
+
16
+ def config
17
+ @config ||= Config::Top.new
18
+ end
19
+
20
+ def config_runners
21
+ @config_runners ||= []
22
+ end
23
+
24
+ def run(&block)
25
+ config_runners << block
26
+ end
27
+
28
+ def execute!
29
+ config_runners.each do |block|
30
+ block.call(config)
31
+ end
32
+
33
+ config.loaded!
34
+ end
35
+ end
36
+ end
37
+
38
+ class Config
39
+ class Base
40
+ def [](key)
41
+ send(key)
42
+ end
43
+
44
+ def to_json
45
+ instance_variables_hash.to_json
46
+ end
47
+
48
+ def instance_variables_hash
49
+ instance_variables.inject({}) do |acc, iv|
50
+ acc[iv.to_s[1..-1].to_sym] = instance_variable_get(iv)
51
+ acc
52
+ end
53
+ end
54
+ end
55
+
56
+ class SSHConfig < Base
57
+ attr_accessor :username
58
+ attr_accessor :password
59
+ attr_accessor :host
60
+ attr_accessor :forwarded_port_key
61
+ attr_accessor :max_tries
62
+ end
63
+
64
+ class VMConfig < Base
65
+ attr_accessor :box
66
+ attr_accessor :box_ovf
67
+ attr_accessor :base_mac
68
+ attr_accessor :project_directory
69
+ attr_reader :forwarded_ports
70
+ attr_accessor :hd_location
71
+ attr_accessor :disk_image_format
72
+
73
+
74
+ def initialize
75
+ @forwarded_ports = {}
76
+ end
77
+
78
+ def forward_port(name, guestport, hostport, protocol="TCP")
79
+ forwarded_ports[name] = {
80
+ :guestport => guestport,
81
+ :hostport => hostport,
82
+ :protocol => protocol
83
+ }
84
+ end
85
+
86
+ def hd_location=(val)
87
+ raise Exception.new "disk_storage must be set to a directory" unless File.directory?(val)
88
+ @hd_location=val
89
+ end
90
+
91
+ def base
92
+ File.expand_path(@base)
93
+ end
94
+ end
95
+
96
+ class PackageConfig < Base
97
+ attr_accessor :name
98
+ attr_accessor :extension
99
+ end
100
+
101
+ class ChefConfig < Base
102
+ attr_accessor :cookbooks_path
103
+ attr_accessor :provisioning_path
104
+ attr_accessor :json
105
+ attr_accessor :enabled
106
+
107
+ def initialize
108
+ @enabled = false
109
+ end
110
+
111
+ def to_json
112
+ # Overridden so that the 'json' key could be removed, since its just
113
+ # merged into the config anyways
114
+ data = instance_variables_hash
115
+ data.delete(:json)
116
+ data.to_json
117
+ end
118
+ end
119
+
120
+ class VagrantConfig < Base
121
+ attr_accessor :dotfile_name
122
+ attr_accessor :log_output
123
+ attr_accessor :home
124
+
125
+ def home
126
+ File.expand_path(@home)
127
+ end
128
+ end
129
+
130
+ class Top < Base
131
+ attr_reader :package
132
+ attr_reader :ssh
133
+ attr_reader :vm
134
+ attr_reader :chef
135
+ attr_reader :vagrant
136
+
137
+ def initialize
138
+ @ssh = SSHConfig.new
139
+ @vm = VMConfig.new
140
+ @chef = ChefConfig.new
141
+ @vagrant = VagrantConfig.new
142
+ @package = PackageConfig.new
143
+
144
+ @loaded = false
145
+ end
146
+
147
+ def loaded?
148
+ @loaded
149
+ end
150
+
151
+ def loaded!
152
+ @loaded = true
153
+ end
154
+ end
155
+ end
156
+ end