vagrantup 0.1.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 (88) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +11 -0
  3. data/Gemfile +17 -0
  4. data/README.md +45 -0
  5. data/Rakefile +41 -0
  6. data/VERSION +1 -0
  7. data/bin/.gitignore +0 -0
  8. data/bin/vagrant +15 -0
  9. data/bin/vagrant-box +35 -0
  10. data/bin/vagrant-down +28 -0
  11. data/bin/vagrant-halt +29 -0
  12. data/bin/vagrant-init +28 -0
  13. data/bin/vagrant-package +30 -0
  14. data/bin/vagrant-reload +30 -0
  15. data/bin/vagrant-resume +28 -0
  16. data/bin/vagrant-ssh +28 -0
  17. data/bin/vagrant-suspend +28 -0
  18. data/bin/vagrant-up +30 -0
  19. data/config/default.rb +29 -0
  20. data/lib/vagrant.rb +14 -0
  21. data/lib/vagrant/actions/base.rb +93 -0
  22. data/lib/vagrant/actions/box/add.rb +22 -0
  23. data/lib/vagrant/actions/box/destroy.rb +14 -0
  24. data/lib/vagrant/actions/box/download.rb +63 -0
  25. data/lib/vagrant/actions/box/unpackage.rb +49 -0
  26. data/lib/vagrant/actions/runner.rb +128 -0
  27. data/lib/vagrant/actions/vm/destroy.rb +14 -0
  28. data/lib/vagrant/actions/vm/down.rb +12 -0
  29. data/lib/vagrant/actions/vm/export.rb +41 -0
  30. data/lib/vagrant/actions/vm/forward_ports.rb +32 -0
  31. data/lib/vagrant/actions/vm/halt.rb +14 -0
  32. data/lib/vagrant/actions/vm/import.rb +17 -0
  33. data/lib/vagrant/actions/vm/move_hard_drive.rb +53 -0
  34. data/lib/vagrant/actions/vm/package.rb +61 -0
  35. data/lib/vagrant/actions/vm/provision.rb +71 -0
  36. data/lib/vagrant/actions/vm/reload.rb +17 -0
  37. data/lib/vagrant/actions/vm/resume.rb +16 -0
  38. data/lib/vagrant/actions/vm/shared_folders.rb +69 -0
  39. data/lib/vagrant/actions/vm/start.rb +50 -0
  40. data/lib/vagrant/actions/vm/suspend.rb +16 -0
  41. data/lib/vagrant/actions/vm/up.rb +35 -0
  42. data/lib/vagrant/box.rb +129 -0
  43. data/lib/vagrant/busy.rb +73 -0
  44. data/lib/vagrant/commands.rb +174 -0
  45. data/lib/vagrant/config.rb +156 -0
  46. data/lib/vagrant/downloaders/base.rb +13 -0
  47. data/lib/vagrant/downloaders/file.rb +21 -0
  48. data/lib/vagrant/downloaders/http.rb +47 -0
  49. data/lib/vagrant/env.rb +140 -0
  50. data/lib/vagrant/ssh.rb +43 -0
  51. data/lib/vagrant/util.rb +45 -0
  52. data/lib/vagrant/vm.rb +57 -0
  53. data/script/vagrant-ssh-expect.sh +22 -0
  54. data/templates/Vagrantfile +8 -0
  55. data/test/test_helper.rb +91 -0
  56. data/test/vagrant/actions/base_test.rb +32 -0
  57. data/test/vagrant/actions/box/add_test.rb +37 -0
  58. data/test/vagrant/actions/box/destroy_test.rb +18 -0
  59. data/test/vagrant/actions/box/download_test.rb +118 -0
  60. data/test/vagrant/actions/box/unpackage_test.rb +101 -0
  61. data/test/vagrant/actions/runner_test.rb +236 -0
  62. data/test/vagrant/actions/vm/destroy_test.rb +24 -0
  63. data/test/vagrant/actions/vm/down_test.rb +32 -0
  64. data/test/vagrant/actions/vm/export_test.rb +88 -0
  65. data/test/vagrant/actions/vm/forward_ports_test.rb +50 -0
  66. data/test/vagrant/actions/vm/halt_test.rb +27 -0
  67. data/test/vagrant/actions/vm/import_test.rb +36 -0
  68. data/test/vagrant/actions/vm/move_hard_drive_test.rb +108 -0
  69. data/test/vagrant/actions/vm/package_test.rb +155 -0
  70. data/test/vagrant/actions/vm/provision_test.rb +103 -0
  71. data/test/vagrant/actions/vm/reload_test.rb +44 -0
  72. data/test/vagrant/actions/vm/resume_test.rb +27 -0
  73. data/test/vagrant/actions/vm/shared_folders_test.rb +117 -0
  74. data/test/vagrant/actions/vm/start_test.rb +55 -0
  75. data/test/vagrant/actions/vm/suspend_test.rb +27 -0
  76. data/test/vagrant/actions/vm/up_test.rb +76 -0
  77. data/test/vagrant/box_test.rb +92 -0
  78. data/test/vagrant/busy_test.rb +81 -0
  79. data/test/vagrant/commands_test.rb +252 -0
  80. data/test/vagrant/config_test.rb +123 -0
  81. data/test/vagrant/downloaders/base_test.rb +20 -0
  82. data/test/vagrant/downloaders/file_test.rb +32 -0
  83. data/test/vagrant/downloaders/http_test.rb +40 -0
  84. data/test/vagrant/env_test.rb +293 -0
  85. data/test/vagrant/ssh_test.rb +95 -0
  86. data/test/vagrant/util_test.rb +64 -0
  87. data/test/vagrant/vm_test.rb +96 -0
  88. metadata +263 -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