vagrant-aws 0.0.1

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 ADDED
@@ -0,0 +1,5 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
5
+ .rvmrc
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in vagrant-aws.gemspec
4
+ gemspec
data/README.md ADDED
@@ -0,0 +1,65 @@
1
+ # vagrant-aws
2
+
3
+ `vagrant-aws` is a plugin for [Vagrant](http://vagrantup.com) which allows the user
4
+ to instantiate the Vagrant environment on Amazon AWS (using EC2). This document assumes
5
+ you are familiar with Vagrant, if not, the project has excellent [documentation](http://vagrantup.com/docs/index.html).
6
+
7
+ **NOTE:** This plugin is "pre-alpha", see below for the caveats
8
+
9
+ ## Installing / Getting Started
10
+
11
+ To use this plugin, first install Vagrant, then install the plugin gem. It should be
12
+ picked up automatically by vagrant. You can then use the `vagrant aws` commands.
13
+
14
+ `vagrant-aws` uses [fog](https://github.com/geemus/fog) internally, and you will need to
15
+ specify your Amazon AWS credentials in a "fog" file. Create `~/.fog` with:
16
+
17
+ ---
18
+ default:
19
+ aws_access_key_id: <YOUR ACCESS KEY>
20
+ aws_secret_access_key: <YOUR SECRET KEY>
21
+
22
+ If you already have an Amazon key pair (created when you signed-up, or
23
+ someother time), you can specify the key name and the path the associated
24
+ private key. Alternately, if no key name is specified, `vagrant-aws` will
25
+ automatically create and register a key pair for you with the name
26
+ `vagrant_<MAC ADDRESS>`. If you want to use your pre-existing key, you can
27
+ specify the key name and path on a per-environment basis (i.e., in each
28
+ Vagrantfile) or in a single Vagrantfile in your `~/.vagrant` directory. In the
29
+ latter case, create `~/.vagrant/Vagrantfile` with:
30
+
31
+ Vagrant::Config.run do |config|
32
+ config.aws.key_name = "<KEY NAME>"
33
+ config.aws.private_key_path = "<PATH/TO/KEY>"
34
+ end
35
+
36
+ With the above in place you should be ready instantiate your Vagrant environment on
37
+ Amazon AWS. See below for additional information on configuration, caveats, etc..
38
+
39
+ ## Configuration and Image Boxes
40
+
41
+ `vagrant-aws` defines a new configuration class for use in your Vagrantfile. An example
42
+ usage (showing the defaults) would be:
43
+
44
+ Vagrant::Config.run do |config|
45
+ config.aws.region = "us-east-1"
46
+ config.aws.availability_zone = nil # Let AWS choose
47
+ config.aws.image = "ami-2ec83147" # EBS-backed Ubuntu 10.04 64-bit
48
+ config.aws.username = "ubuntu"
49
+ config.aws.security_groups = ["default"]
50
+ config.aws.flavor = "t1.micro"
51
+ end
52
+
53
+ Alternately you can work with "image boxes" using the `vagrant aws box_*` commands. These are
54
+ similar to Vagrant's native boxes, and have a similar API, but wrap AWS ami IDs. The major
55
+ difference is that box creation and removal can optionally reregister and deregister AMIs
56
+ with AWS. Note that AMI creation is only supported for EBS-backed instances.
57
+
58
+ ## Caveats
59
+
60
+ `vagrant-aws` is "pre-alpha" and currently only supports creation, suspension, resumption
61
+ and descruction of the Vagrant environment. Provisioning should be supported for shell,
62
+ chef-server and chef-solo, but has only been tested with chef-solo and on an Ubuntu guest.
63
+ Only a subset of Vagrant features are supported. Currently port forwarding and shared
64
+ directories are not implemented, nor is host networking (although that is less relevant for AWS).
65
+ `vagrant-aws` in general has only been tested for a single VM, on OSX 10.6, with chef-solo.
data/Rakefile ADDED
@@ -0,0 +1,10 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rake/testtask'
3
+
4
+ task :default => :test
5
+
6
+ Rake::TestTask.new do |t|
7
+ t.libs << "test"
8
+ t.pattern = 'test/**/*_test.rb'
9
+ end
10
+
@@ -0,0 +1,14 @@
1
+ require 'vagrant'
2
+ require 'vagrant-aws/version'
3
+ require 'vagrant-aws/errors'
4
+ require 'vagrant-aws/environment'
5
+ require 'vagrant-aws/vm'
6
+ require 'vagrant-aws/config'
7
+ require 'vagrant-aws/middleware'
8
+ require 'vagrant-aws/command'
9
+ require 'vagrant-aws/system'
10
+ require 'vagrant-aws/box'
11
+ require 'vagrant-aws/box_collection'
12
+
13
+ # Add our custom translations to the load path
14
+ I18n.load_path << File.expand_path("../../locales/en.yml", __FILE__)
@@ -0,0 +1,56 @@
1
+ module VagrantAWS
2
+ class Action
3
+ class Create
4
+ def initialize(app, env)
5
+ @app = app
6
+ end
7
+
8
+ def call(env)
9
+ raise Errors::KeyNameNotSpecified if env["config"].aws.key_name.nil?
10
+
11
+ env.ui.info I18n.t("vagrant.plugins.aws.actions.create.creating")
12
+
13
+ server_def = server_definition(env["config"])
14
+
15
+ # Verify AMI is valid (and in the future enable options specific to EBS-based AMIs)
16
+ image = env["vm"].connection.images.get(server_def[:image_id])
17
+ image.wait_for { state == 'available' }
18
+
19
+ env["vm"].vm = env["vm"].connection.servers.create(server_def)
20
+ raise VagrantAWS::Errors::VMCreateFailure if env["vm"].vm.nil? || env["vm"].vm.id.nil?
21
+
22
+ env.ui.info I18n.t("vagrant.plugins.aws.actions.create.created", :id => env["vm"].vm.id)
23
+
24
+ env["vm"].vm.wait_for { ready? }
25
+ env["vm"].connection.create_tags(env["vm"].vm.id, { "name" => env["vm"].name })
26
+
27
+ env.ui.info I18n.t("vagrant.plugins.aws.actions.create.available", :dns => env["vm"].vm.dns_name)
28
+
29
+ @app.call(env)
30
+ end
31
+
32
+ def recover(env)
33
+ if env["vm"].created?
34
+ return if env["vagrant.error"].is_a?(Vagrant::Errors::VagrantError)
35
+
36
+ # Interrupted, destroy the VM
37
+ env["actions"].run(:aws_destroy)
38
+ end
39
+ end
40
+
41
+ def server_definition(config)
42
+ {
43
+ :image_id => config.aws.image,
44
+ :groups => config.aws.security_groups,
45
+ :flavor_id => config.aws.flavor,
46
+ :key_name => config.aws.key_name,
47
+ :username => config.aws.username,
48
+ :private_key_path => config.aws.private_key_path,
49
+ :availability_zone => config.aws.availability_zone
50
+ }
51
+ end
52
+
53
+ end
54
+ end
55
+ end
56
+
@@ -0,0 +1,106 @@
1
+ require 'fileutils'
2
+
3
+ # Path vagrant to not delete pre-existing package file (pull request submitted upstream)
4
+
5
+ module Vagrant
6
+ class Action
7
+ module General
8
+
9
+ class Package
10
+ def recover(env)
11
+ unless env["vagrant.error"].is_a?(Errors::PackageOutputExists)
12
+ # Cleanup any packaged files if the packaging failed at some point.
13
+ File.delete(tar_path) if File.exist?(tar_path)
14
+ end
15
+ end
16
+ end
17
+
18
+ end
19
+ end
20
+ end
21
+
22
+ module VagrantAWS
23
+ class Action
24
+ class CreateImage
25
+ include Vagrant::Util
26
+
27
+ PACKAGE_VAGRANTFILE = <<EOT
28
+ Vagrant::Config.run do |config|
29
+ # This Vagrantfile is auto-generated by `vagrant aws create_image` to contain
30
+ # the image id and region. Custom configuration should be placed in
31
+ # the actual `Vagrantfile` in this box.
32
+ config.vm.base_mac = "000000000000" # Unneeded
33
+ config.aws.image = "<%= image %>"
34
+ config.aws.region = "<%= region %>"
35
+ end
36
+ EOT
37
+
38
+ attr_reader :temp_dir, :image
39
+
40
+ def initialize(app, env)
41
+ @app = app
42
+ @env = env
43
+ end
44
+
45
+ def call(env)
46
+ @env = env
47
+
48
+ raise Vagrant::Errors::VMNotCreatedError if !@env["vm"].created?
49
+ raise Vagrant::Errors::VMNotRunningError if !@env["vm"].vm.running?
50
+ raise VagrantAWS::Errors::EBSDeviceRequired, :command => "box_create" if @env["image.register"] and @env["vm"].vm.root_device_type != "ebs"
51
+
52
+ if @env["image.register"]
53
+ @env.ui.info I18n.t("vagrant.plugins.aws.actions.create_image.creating")
54
+ @image = @env["vm"].connection.create_image(@env["vm"].vm.id, @env['image.name'], @env['image.desc'])
55
+ @image = @env["vm"].connection.images.new({ :id => @image.body['imageId'] })
56
+ @image.wait_for { state == "available" }
57
+ else
58
+ @image = @env["vm"].connection.images.get(@env["vm"].vm.image_id)
59
+ end
60
+
61
+ setup_temp_dir
62
+ export
63
+
64
+ @app.call(@env)
65
+
66
+ cleanup_temp_dir
67
+ end
68
+
69
+ def recover(env)
70
+ if env["image.register"]
71
+ env.ui.info I18n.t("vagrant.plugins.aws.actions.deregister_image.deregistering", :image => @image.id)
72
+ @image.deregister(!@image.root_device_name.nil?) # Don't try to delete backing snapshot if it was not created
73
+ end
74
+ cleanup_temp_dir
75
+ end
76
+
77
+ def cleanup_temp_dir
78
+ if temp_dir && File.exist?(temp_dir)
79
+ FileUtils.rm_rf(temp_dir)
80
+ end
81
+ end
82
+
83
+ def setup_temp_dir
84
+ @env.ui.info I18n.t("vagrant.actions.vm.export.create_dir")
85
+ @temp_dir = @env["export.temp_dir"] = @env.env.tmp_path.join(Time.now.to_i.to_s)
86
+ FileUtils.mkpath(@env["export.temp_dir"])
87
+ end
88
+
89
+ # Write a Vagrantfile with the relevant information
90
+ def export
91
+ File.open(File.join(@env["export.temp_dir"], 'Vagrantfile'), "w") do |f|
92
+ f.write(TemplateRenderer.render_string(PACKAGE_VAGRANTFILE, {
93
+ :image => @image.id,
94
+ :region => @env["config"].aws.region
95
+ }))
96
+ end
97
+ File.open(File.join(@env["export.temp_dir"], 'image.json'), "w") do |f|
98
+ f.write(@image.to_json)
99
+ end
100
+ end
101
+
102
+
103
+ end
104
+ end
105
+ end
106
+
@@ -0,0 +1,39 @@
1
+ require 'Macaddr'
2
+
3
+ module VagrantAWS
4
+ class Action
5
+ class CreateSSHKey
6
+ def initialize(app, env)
7
+ @app = app
8
+ end
9
+
10
+ def call(env)
11
+ @env = env
12
+
13
+ if @env["config"].aws.key_name.nil?
14
+ # Do we have a previously created key available on AWS?
15
+ key = @env["vm"].connection.key_pairs.all('key-name' => @env.env.ssh_keys).first
16
+ if key.nil?
17
+ # Create and save key
18
+ key = @env["vm"].connection.key_pairs.create(:name => "vagrantaws_#{Mac.addr.gsub(':','')}")
19
+ env.ui.info I18n.t("vagrant.plugins.aws.actions.create_ssh_key.created", :name => key.name)
20
+ File.open(local_key_path(key.name), File::WRONLY|File::TRUNC|File::CREAT, 0600) { |f| f.write(key.private_key) }
21
+ end
22
+
23
+ @env["config"].aws.key_name = key.name
24
+ @env["config"].aws.private_key_path = local_key_path(key.name)
25
+ end
26
+
27
+ @app.call(env)
28
+ end
29
+
30
+ def local_key_path(name)
31
+ @env.env.ssh_keys_path.join(name)
32
+ end
33
+
34
+ end
35
+ end
36
+ end
37
+
38
+
39
+
@@ -0,0 +1,27 @@
1
+ module VagrantAWS
2
+ class Action
3
+ class DeregisterImage
4
+ def initialize(app, env)
5
+ @app = app
6
+ end
7
+
8
+ def call(env)
9
+ if env['image.deregister']
10
+ image = Fog::Compute.new(:provider => 'AWS').images.new(load_image(env))
11
+ env.ui.info I18n.t("vagrant.plugins.aws.actions.deregister_image.deregistering", :image => image.id)
12
+ image.reload
13
+ image.deregister(true) # Delete snapshot when deregistering
14
+ end
15
+ @app.call(env)
16
+ end
17
+
18
+ def load_image(env)
19
+ File.open(File.join(env["box"].directory, "image.json"), "r") do |f|
20
+ JSON.parse(f.read)
21
+ end
22
+ end
23
+
24
+ end
25
+ end
26
+ end
27
+
@@ -0,0 +1,41 @@
1
+ module VagrantAWS
2
+ class Action
3
+ class PopulateSSH
4
+ def initialize(app, env)
5
+ @app = app
6
+ end
7
+
8
+ def call(env)
9
+ @env = env
10
+
11
+ if @env["config"].aws.private_key_path.nil?
12
+ # See if we are using a key vagrant aws generated
13
+ @env["config"].aws.private_key_path = local_key_path(env["vm"].vm.key_name) if env["vm"].vm.key_name =~ /^vagrantaws_[0-9a-f]{12}/
14
+ end
15
+
16
+ raise VagrantAWS::Errors::PrivateKeyFileNotSpecified if env["config"].aws.private_key_path.nil? || !File.exists?(env["config"].aws.private_key_path)
17
+
18
+ env["config"].ssh.host = env["vm"].vm.dns_name
19
+ env["config"].ssh.username = env["config"].aws.username
20
+ env["config"].ssh.private_key_path = env["config"].aws.private_key_path
21
+ env["config"].ssh.port = 22
22
+
23
+ # Make sure we can connect
24
+ begin
25
+ env["vm"].vm.wait_for { env["vm"].ssh.up? }
26
+ rescue Fog::Errors::Error
27
+ raise Vagrant::Errors::SSHConnectionRefused
28
+ end
29
+
30
+ @app.call(env)
31
+ end
32
+
33
+ def local_key_path(name)
34
+ @env.env.ssh_keys_path.join(name)
35
+ end
36
+
37
+ end
38
+ end
39
+ end
40
+
41
+
@@ -0,0 +1,127 @@
1
+ require 'archive/tar/minitar'
2
+
3
+ module VagrantAWS
4
+ class Action
5
+ class PrepareProvisioners
6
+ def initialize(app, env)
7
+ @app = app
8
+ @env = env
9
+ @env["provision.enabled"] = true if !@env.has_key?("provision.enabled")
10
+ @provisioner_configs = []
11
+
12
+ load_provisioner_configs if provisioning_enabled?
13
+ end
14
+
15
+ def call(env)
16
+ @provisioner_configs.each do |provisioner_config|
17
+ if provisioner_config.is_a?(Vagrant::Provisioners::ChefSolo::Config)
18
+ env.ui.info I18n.t("vagrant.plugins.aws.actions.prepare_provisioners.uploading_chef_resources")
19
+ ChefSolo.prepare(provisioner_config)
20
+ end
21
+ end
22
+ @app.call(env)
23
+ end
24
+
25
+ def provisioning_enabled?
26
+ !@env["config"].vm.provisioners.empty? && @env["provision.enabled"]
27
+ end
28
+
29
+ def load_provisioner_configs
30
+ @env["config"].vm.provisioners.each do |provisioner|
31
+ @provisioner_configs << provisioner.config
32
+ end
33
+ end
34
+
35
+ class ChefSolo
36
+
37
+ def self.prepare(config)
38
+ my_preparer = new(config)
39
+ my_preparer.bootstrap_if_needed
40
+ my_preparer.chown_provisioning_folder
41
+ my_preparer.copy_and_update_paths
42
+ end
43
+
44
+ def initialize(config)
45
+ @config = config
46
+ end
47
+
48
+ def bootstrap_if_needed
49
+ begin
50
+ @config.env.vm.ssh.execute do |ssh|
51
+ ssh.sudo!("which chef-solo")
52
+ end
53
+ rescue Vagrant::Errors::VagrantError
54
+ # Bootstrap chef-solo
55
+ @config.env.ui.info I18n.t("vagrant.plugins.aws.actions.prepare_provisioners.chef_not_detected", :binary => "chef-solo")
56
+ @config.env.vm.system.bootstrap_chef
57
+ end
58
+ end
59
+
60
+
61
+ def chown_provisioning_folder
62
+ @config.env.vm.ssh.execute do |ssh|
63
+ ssh.sudo!("mkdir -p #{@config.provisioning_path}")
64
+ ssh.sudo!("chown #{@config.env.config.ssh.username} #{@config.provisioning_path}")
65
+ end
66
+ end
67
+
68
+ def copy_and_update_paths
69
+ # Copy relevant host paths to remote instance and update provisioner config
70
+ # to point to new "vm" paths for cookbooks, etc.
71
+ %w{ cookbooks_path roles_path data_bags_path }.each do |path|
72
+ copy_host_paths(@config.send(path), path)
73
+ @config.send "#{path}=", strip_host_paths(@config.send(path)).push([:vm, path])
74
+ end
75
+ end
76
+
77
+ def copy_host_paths(paths, target_directory)
78
+ archive = tar_host_folder_paths(paths)
79
+
80
+ target_d = "#{@config.provisioning_path}/#{target_directory}"
81
+ target_f = target_d + '.tar'
82
+
83
+ @config.env.vm.ssh.upload!(archive.path, target_f)
84
+ @config.env.vm.ssh.execute do |ssh|
85
+ ssh.sudo!([
86
+ "mkdir -p #{target_d}",
87
+ "chown #{@config.env.config.ssh.username} #{target_d}",
88
+ "tar -C #{target_d} -xf #{target_f}"
89
+ ])
90
+ end
91
+
92
+ target_directory
93
+ end
94
+
95
+ def tar_host_folder_paths(paths)
96
+ tarf = Tempfile.new(['vagrant-chef-solo','.tar'])
97
+ Archive::Tar::Minitar::Output.open(tarf) do |outp|
98
+ host_folder_paths(paths).each do |full_path|
99
+ Dir.chdir(full_path) do |ignored|
100
+ Dir.glob("**#{File::SEPARATOR}**") { |entry| Archive::Tar::Minitar.pack_file(entry, outp) }
101
+ end
102
+ end
103
+ end
104
+ tarf
105
+ end
106
+
107
+
108
+ def host_folder_paths(paths)
109
+ paths = [paths] if paths.is_a?(String) || paths.first.is_a?(Symbol)
110
+ paths.inject([]) do |acc, path|
111
+ path = [:host, path] if !path.is_a?(Array)
112
+ type, path = path
113
+ acc << File.expand_path(path, @config.env.root_path) if type == :host
114
+ acc
115
+ end
116
+ end
117
+
118
+ def strip_host_paths(paths)
119
+ paths = [paths] if paths.is_a?(String) || paths.first.is_a?(Symbol)
120
+ paths.delete_if { |path| !path.is_a?(Array) || path[0] == :host }
121
+ end
122
+ end
123
+
124
+
125
+ end
126
+ end
127
+ end