cirrus-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.
- checksums.yaml +7 -0
- data/.gitignore +70 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +49 -0
- data/bin/cirrus +47 -0
- data/cirrus-aws.gemspec +22 -0
- data/lib/cirrus/instance.rb +75 -0
- data/lib/cirrus/key_pair.rb +46 -0
- data/lib/cirrus/scp.rb +26 -0
- data/lib/cirrus/security_group.rb +31 -0
- data/lib/cirrus/ssh.rb +110 -0
- data/lib/cirrus/version.rb +3 -0
- data/lib/cirrus.rb +75 -0
- metadata +72 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: cfe296c12d483dc3cfe49b9e7d1c6d12b03d9f6a
|
4
|
+
data.tar.gz: 67e5d6ef681de3f98ee9730fafde36698c069d38
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 540e9f5eff1d4f146bbf4e3ff9498cbaba05e433d62fa8eb22dfa5ce9c4a487e0ba594ea71d53b53c36f21f0f600e8292df30ddc17cbd0d80fa23bb6e7a67462
|
7
|
+
data.tar.gz: 74c83ec54402ab291fe231c5280349a898281053e974b071aa442a3f405300664b8a88cc5a216549af7be717525550d48ae7462986a56d3611846d086d0ce877
|
data/.gitignore
ADDED
@@ -0,0 +1,70 @@
|
|
1
|
+
# bundler state
|
2
|
+
/.bundle
|
3
|
+
/vendor/bundle/
|
4
|
+
/vendor/ruby/
|
5
|
+
|
6
|
+
# minimal Rails specific artifacts
|
7
|
+
db/*.sqlite3
|
8
|
+
/db/*.sqlite3-journal
|
9
|
+
/log/*
|
10
|
+
/tmp/*
|
11
|
+
|
12
|
+
# various artifacts
|
13
|
+
**.war
|
14
|
+
*.rbc
|
15
|
+
*.sassc
|
16
|
+
.rspec
|
17
|
+
.redcar/
|
18
|
+
.sass-cache
|
19
|
+
/config/config.yml
|
20
|
+
/config/database.yml
|
21
|
+
/config/application.yml
|
22
|
+
/config/secrets.yml
|
23
|
+
/coverage.data
|
24
|
+
/coverage/
|
25
|
+
/db/*.javadb/
|
26
|
+
/db/*.sqlite3
|
27
|
+
/doc/api/
|
28
|
+
/doc/app/
|
29
|
+
/doc/features.html
|
30
|
+
/doc/specs.html
|
31
|
+
/public/cache
|
32
|
+
/public/stylesheets/compiled
|
33
|
+
/public/system/*
|
34
|
+
/spec/tmp/*
|
35
|
+
/cache
|
36
|
+
/capybara*
|
37
|
+
/capybara-*.html
|
38
|
+
/gems
|
39
|
+
/specifications
|
40
|
+
rerun.txt
|
41
|
+
pickle-email-*.html
|
42
|
+
.zeus.sock
|
43
|
+
tags
|
44
|
+
gems.tags
|
45
|
+
|
46
|
+
# If you find yourself ignoring temporary files generated by your text editor
|
47
|
+
# or operating system, you probably want to add a global ignore instead:
|
48
|
+
# git config --global core.excludesfile ~/.gitignore_global
|
49
|
+
#
|
50
|
+
# Here are some files you may want to ignore globally:
|
51
|
+
|
52
|
+
# scm revert files
|
53
|
+
**.orig
|
54
|
+
|
55
|
+
# Mac finder artifacts
|
56
|
+
.DS_Store
|
57
|
+
|
58
|
+
# Netbeans project directory
|
59
|
+
/nbproject/
|
60
|
+
|
61
|
+
# RubyMine project files
|
62
|
+
.idea
|
63
|
+
|
64
|
+
# Textmate project files
|
65
|
+
/*.tmproj
|
66
|
+
|
67
|
+
# vim artifacts
|
68
|
+
**.swp
|
69
|
+
|
70
|
+
dump.rdb
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 Andrew Berls
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
# Cirrus
|
2
|
+
|
3
|
+
A command-line tool to launch and provision EC2 instances. Cirrus is an experimental work-in-progress and should not be considered production-ready.
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
```ruby
|
10
|
+
gem 'cirrus'
|
11
|
+
```
|
12
|
+
|
13
|
+
And then execute:
|
14
|
+
|
15
|
+
$ bundle
|
16
|
+
|
17
|
+
Or install it yourself as:
|
18
|
+
|
19
|
+
$ gem install cirrus
|
20
|
+
|
21
|
+
## Usage
|
22
|
+
|
23
|
+
The CLI ships with two commands: `launch` and `teardown`.
|
24
|
+
|
25
|
+
### Launching an instance
|
26
|
+
Usage: `cirrus launch IMAGE_ID AWS_ACCESS_KEY AWS_SECRET_KEY REGION`
|
27
|
+
|
28
|
+
This will automatically create a default Cirrus key pair and security group
|
29
|
+
(authorizing inbound SSH and HTTP traffic), and launch an instance
|
30
|
+
|
31
|
+
For example, `cirrus launch ami-ee4f77ab <access_key> <secret_key> us-west-1` will launch an instance running Ubuntu 14.04 in us-west-1 (N. California).
|
32
|
+
|
33
|
+
|
34
|
+
### Tearing down an instance
|
35
|
+
Usage: `cirrus teardown INSTANCE_ID AWS_ACCESS_KEY AWS_SECRET_KEY REGION`
|
36
|
+
|
37
|
+
Example: `cirrus teardown i-bdc5b377 <access_key> <secret_key> us-west-1`
|
38
|
+
|
39
|
+
This will terminate the specified instance and destroy its associated security groups and keypairs.
|
40
|
+
|
41
|
+
That's it!
|
42
|
+
|
43
|
+
## Contributing
|
44
|
+
|
45
|
+
1. Fork it ( https://github.com/andrewberls/cirrus/fork )
|
46
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
47
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
48
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
49
|
+
5. Create a new Pull Request
|
data/bin/cirrus
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'cirrus'
|
4
|
+
|
5
|
+
USAGE =
|
6
|
+
%Q{\
|
7
|
+
Usage: cirrus <command> <args>
|
8
|
+
|
9
|
+
Commands:
|
10
|
+
launch IMAGE_ID AWS_ACCESS_KEY AWS_SECRET_KEY REGION
|
11
|
+
teardown INSTANCE_ID AWS_ACCESS_KEY AWS_SECRET_KEY REGION}
|
12
|
+
|
13
|
+
command = ARGV[0]
|
14
|
+
|
15
|
+
case command
|
16
|
+
when 'launch'
|
17
|
+
ARGV.length == 5 or raise "Usage: cirrus launch IMAGE_ID AWS_ACCESS_KEY AWS_SECRET_KEY REGION"
|
18
|
+
|
19
|
+
image_id = ARGV[1]
|
20
|
+
aws_access_key = ARGV[2]
|
21
|
+
aws_secret_key = ARGV[3]
|
22
|
+
region = ARGV[4]
|
23
|
+
|
24
|
+
Cirrus.setup(
|
25
|
+
image_id,
|
26
|
+
access_key_id: aws_access_key,
|
27
|
+
secret_access_key: aws_secret_key,
|
28
|
+
region: region
|
29
|
+
)
|
30
|
+
when 'teardown'
|
31
|
+
ARGV.length == 5 or raise "Usage: cirrus teardown INSTANCE_ID AWS_ACCESS_KEY AWS_SECRET_KEY REGION"
|
32
|
+
|
33
|
+
instance_id = ARGV[1]
|
34
|
+
aws_access_key = ARGV[2]
|
35
|
+
aws_secret_key = ARGV[3]
|
36
|
+
region = ARGV[4]
|
37
|
+
|
38
|
+
Cirrus.teardown(instance_id, {
|
39
|
+
access_key_id: aws_access_key,
|
40
|
+
secret_access_key: aws_secret_key,
|
41
|
+
region: region
|
42
|
+
})
|
43
|
+
when '-h', '--help'
|
44
|
+
puts USAGE
|
45
|
+
else
|
46
|
+
raise USAGE
|
47
|
+
end
|
data/cirrus-aws.gemspec
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'cirrus/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "cirrus-aws"
|
8
|
+
spec.version = Cirrus::VERSION
|
9
|
+
spec.authors = ["Andrew Berls"]
|
10
|
+
spec.email = ["andrew.berls@gmail.com"]
|
11
|
+
spec.summary = %q{A command-line tool to provision and launch AWS EC2 instances}
|
12
|
+
spec.description = %q{}
|
13
|
+
spec.homepage = ""
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0")
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_development_dependency "bundler", "~> 1.7"
|
22
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
module Cirrus::Instance
|
2
|
+
extend self
|
3
|
+
|
4
|
+
STATUS_PENDING = :pending
|
5
|
+
STATUS_RUNNING = :running
|
6
|
+
STATUS_SHUTTING_DOWN = :shutting_down
|
7
|
+
STATUS_TERMINATED = :terminated
|
8
|
+
|
9
|
+
class UnexpectedStatusError < StandardError; end
|
10
|
+
|
11
|
+
SSH_WAIT_TIME = 45 # seconds
|
12
|
+
|
13
|
+
# Launch an instance, optionally adding tags, and wait for it to
|
14
|
+
# run
|
15
|
+
#
|
16
|
+
# Returns AWS::EC2::Instance
|
17
|
+
def launch(ec2, launch_opts, tags={})
|
18
|
+
puts "Launching instance (#{launch_opts[:image_id]}) ..."
|
19
|
+
instance = ec2.instances.create(launch_opts)
|
20
|
+
sleep 5 while instance.status == STATUS_PENDING
|
21
|
+
puts "Launched instance #{instance.id}, status: #{instance.status}"
|
22
|
+
|
23
|
+
assert_instance_status!(instance, STATUS_RUNNING)
|
24
|
+
|
25
|
+
puts "Tagging instance ..."
|
26
|
+
tags.each { |k, v| instance.add_tag(k, value: v) }
|
27
|
+
|
28
|
+
puts "Waiting for SSH to become available ..."
|
29
|
+
sleep SSH_WAIT_TIME
|
30
|
+
|
31
|
+
instance
|
32
|
+
end
|
33
|
+
|
34
|
+
|
35
|
+
# Terminate an instance and destroy its associated security groups / key pair
|
36
|
+
# TODO: if we have a standard Cirrus sec group / keypair, don't want to destroy them
|
37
|
+
def teardown(ec2, instance_id)
|
38
|
+
instance = ec2.instances[instance_id]
|
39
|
+
terminate(instance)
|
40
|
+
|
41
|
+
puts "Destroying security groups ..."
|
42
|
+
|
43
|
+
instance.security_groups.each do |group|
|
44
|
+
next if group.name == 'default'
|
45
|
+
puts "\tDestroying group #{group.id} ..."
|
46
|
+
group.delete
|
47
|
+
end
|
48
|
+
|
49
|
+
key_pair = ec2.key_pairs[instance.key_name]
|
50
|
+
puts "Destroying keypair #{key_pair.name} ..."
|
51
|
+
key_pair.delete
|
52
|
+
end
|
53
|
+
|
54
|
+
|
55
|
+
private
|
56
|
+
|
57
|
+
# Terminate an instance and wait for completion
|
58
|
+
def terminate(instance)
|
59
|
+
instance.terminate
|
60
|
+
puts "Instance terminated, waiting for shutdown completion ..."
|
61
|
+
sleep 10 while instance.status == STATUS_SHUTTING_DOWN
|
62
|
+
|
63
|
+
assert_instance_status!(instance, STATUS_TERMINATED)
|
64
|
+
end
|
65
|
+
|
66
|
+
|
67
|
+
def assert_instance_status!(instance, expected_status)
|
68
|
+
actual_status = instance.status
|
69
|
+
unless actual_status == expected_status
|
70
|
+
raise UnexpectedStatusError, "Expected #{expected_status}, was #{actual_status} for instance #{instance.id}"
|
71
|
+
end
|
72
|
+
nil
|
73
|
+
end
|
74
|
+
|
75
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
|
3
|
+
module Cirrus::KeyPair
|
4
|
+
extend self
|
5
|
+
|
6
|
+
DEFAULT_NAME = 'cirrus'
|
7
|
+
|
8
|
+
PRIVATE_KEY_PERMISSIONS = 0600
|
9
|
+
|
10
|
+
def find_or_create_default(ec2)
|
11
|
+
default_key_pair = ec2.key_pairs.filter("key-name", DEFAULT_NAME).first
|
12
|
+
|
13
|
+
if !default_key_pair.nil?
|
14
|
+
puts "Using default Cirrus key pair"
|
15
|
+
[default_key_pair, private_key_filename]
|
16
|
+
else
|
17
|
+
puts "Creating default Cirrus key pair ..."
|
18
|
+
create_default_key_pair!(ec2)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
# Create the default key pair and write its private key to local disk
|
25
|
+
#
|
26
|
+
# Returns tuple of [AWS::EC2::KeyPair, string_private_key_filename]
|
27
|
+
def create_default_key_pair!(ec2)
|
28
|
+
key_pair_name = DEFAULT_NAME
|
29
|
+
key_pair = ec2.key_pairs.create(key_pair_name)
|
30
|
+
filename = private_key_filename
|
31
|
+
|
32
|
+
File.open(filename, 'w') do |file|
|
33
|
+
file.write(key_pair.private_key)
|
34
|
+
end
|
35
|
+
FileUtils.chmod(PRIVATE_KEY_PERMISSIONS, filename)
|
36
|
+
puts "Saved new keypair to #{filename}"
|
37
|
+
|
38
|
+
[key_pair, filename]
|
39
|
+
end
|
40
|
+
|
41
|
+
|
42
|
+
def private_key_filename
|
43
|
+
"#{ENV['HOME']}/.ssh/#{DEFAULT_NAME}"
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
data/lib/cirrus/scp.rb
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'net/scp'
|
2
|
+
require 'cirrus/ssh'
|
3
|
+
|
4
|
+
module Cirrus
|
5
|
+
module SCP
|
6
|
+
extend self
|
7
|
+
|
8
|
+
class SCPUnavailable < StandardError; end
|
9
|
+
|
10
|
+
# Opens an SCP connection and yields it so that you can download
|
11
|
+
# and upload files.
|
12
|
+
def connect(host, username)
|
13
|
+
Cirrus::SSH.connect(host, username) do |connection|
|
14
|
+
scp = Net::SCP.new(connection)
|
15
|
+
return yield scp
|
16
|
+
end
|
17
|
+
rescue Net::SCP::Error => e
|
18
|
+
# If we get the exit code of 127, then this means SCP is unavailable.
|
19
|
+
raise SCPUnavailable if e.message =~ /\(127\)/
|
20
|
+
|
21
|
+
# Otherwise, just raise the error up
|
22
|
+
raise e
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Cirrus
|
2
|
+
module SecurityGroup
|
3
|
+
extend self
|
4
|
+
|
5
|
+
DEFAULT_NAME = 'cirrus'
|
6
|
+
|
7
|
+
def find_or_create_default(ec2)
|
8
|
+
default_group = ec2.security_groups.filter('group-name', DEFAULT_NAME).first
|
9
|
+
|
10
|
+
if !default_group.nil?
|
11
|
+
puts "Using default Cirrus security group"
|
12
|
+
default_group
|
13
|
+
else
|
14
|
+
puts "Creating default Cirrus security group ..."
|
15
|
+
create_default_group!(ec2)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
# Create default security group + authorize SSH / HTTP
|
22
|
+
# TODO: configurable allowed IP range ?
|
23
|
+
def create_default_group!(ec2)
|
24
|
+
ec2.security_groups.create(DEFAULT_NAME).tap do |g|
|
25
|
+
g.authorize_ingress(:tcp, 22, "0.0.0.0/0")
|
26
|
+
g.authorize_ingress(:tcp, 80, "0.0.0.0/0")
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
end
|
data/lib/cirrus/ssh.rb
ADDED
@@ -0,0 +1,110 @@
|
|
1
|
+
require 'tempfile'
|
2
|
+
require 'net/ssh'
|
3
|
+
require 'cirrus/scp'
|
4
|
+
|
5
|
+
$retry_count = 0
|
6
|
+
|
7
|
+
module Cirrus
|
8
|
+
module SSH
|
9
|
+
extend self
|
10
|
+
|
11
|
+
# Open an SSH connection and yield it
|
12
|
+
def connect(host, username)
|
13
|
+
timeout = 15
|
14
|
+
|
15
|
+
common_connect_opts = {
|
16
|
+
auth_methods: ["publickey"],
|
17
|
+
forward_agent: false,
|
18
|
+
port: 22,
|
19
|
+
timeout: 15,
|
20
|
+
user_known_hosts_file: [],
|
21
|
+
verbose: :debug
|
22
|
+
}
|
23
|
+
|
24
|
+
begin
|
25
|
+
connection =
|
26
|
+
Timeout.timeout(timeout) do
|
27
|
+
begin
|
28
|
+
connect_opts = common_connect_opts.dup
|
29
|
+
connect_opts[:logger] = Logger.new(StringIO.new)
|
30
|
+
Net::SSH.start(host, username, connect_opts)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
rescue Errno::EACCES
|
34
|
+
# This happens on connect() for unknown reasons yet...
|
35
|
+
raise "eacces"
|
36
|
+
rescue Errno::ETIMEDOUT, Timeout::Error
|
37
|
+
# This happens if we continued to timeout when attempting to connect.
|
38
|
+
if $retry_count < 3
|
39
|
+
$retry_count += 1
|
40
|
+
retry
|
41
|
+
else
|
42
|
+
raise "timeout"
|
43
|
+
end
|
44
|
+
rescue Net::SSH::AuthenticationFailed
|
45
|
+
# This happens if authentication failed. We wrap the error in our
|
46
|
+
# own exception.
|
47
|
+
raise "authentication"
|
48
|
+
rescue Net::SSH::Disconnect
|
49
|
+
# This happens if the remote server unexpectedly closes the
|
50
|
+
# connection. This is usually raised when SSH is running on the
|
51
|
+
# other side but can't properly setup a connection. This is
|
52
|
+
# usually a server-side issue.
|
53
|
+
raise "disconnect"
|
54
|
+
rescue Errno::ECONNREFUSED
|
55
|
+
# This is raised if we failed to connect the max amount of times
|
56
|
+
raise "econnrefused"
|
57
|
+
rescue Errno::ECONNRESET
|
58
|
+
# This is raised if we failed to connect the max number of times
|
59
|
+
# due to an ECONNRESET.
|
60
|
+
raise "econnreset"
|
61
|
+
rescue Errno::EHOSTDOWN
|
62
|
+
# This is raised if we get an ICMP DestinationUnknown error.
|
63
|
+
raise "ehostdown"
|
64
|
+
rescue Errno::EHOSTUNREACH
|
65
|
+
# This is raised if we can't work out how to route traffic.
|
66
|
+
raise "ehostunreach"
|
67
|
+
rescue Net::SSH::Exception => e
|
68
|
+
# This is an internal error in Net::SSH
|
69
|
+
raise "internal net ssh error"
|
70
|
+
rescue NotImplementedError
|
71
|
+
# This is raised if a private key type that Net-SSH doesn't support
|
72
|
+
# is used. Show a nicer error.
|
73
|
+
raise "unsupported private key type"
|
74
|
+
end
|
75
|
+
|
76
|
+
yield connection
|
77
|
+
end
|
78
|
+
|
79
|
+
|
80
|
+
# Start ssh-agent and add a key to the keychain
|
81
|
+
# TODO: avoid starting if already running
|
82
|
+
def add_to_keychain(private_key_path)
|
83
|
+
puts "Starting ssh-agent, adding keypair private key ..."
|
84
|
+
puts `eval $(ssh-agent)`
|
85
|
+
puts `ssh-add #{private_key_path}`
|
86
|
+
end
|
87
|
+
|
88
|
+
|
89
|
+
# TODO: better key handling (bootstrap with public key)
|
90
|
+
def bootstrap(host:, username:, private_key_path:, source_path:)
|
91
|
+
return unless source_path
|
92
|
+
|
93
|
+
puts "Bootstrapping instance (#{username}@#{host}) ..."
|
94
|
+
|
95
|
+
add_to_keychain(private_key_path)
|
96
|
+
|
97
|
+
tf = Tempfile.new('boxer-shell')
|
98
|
+
tf.write(File.read(source_path))
|
99
|
+
tf.rewind
|
100
|
+
|
101
|
+
source_path = tf.path
|
102
|
+
dest_path = "/tmp/boxer-shell"
|
103
|
+
|
104
|
+
Cirrus::SCP.connect(host, username) do |scp|
|
105
|
+
scp.upload!(source_path, dest_path)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
end
|
110
|
+
end
|
data/lib/cirrus.rb
ADDED
@@ -0,0 +1,75 @@
|
|
1
|
+
require 'aws-sdk'
|
2
|
+
|
3
|
+
module Cirrus
|
4
|
+
extend self
|
5
|
+
|
6
|
+
# Launch an instance with the default Cirrus key pair and security group,
|
7
|
+
# provision the instance, and wait for it to launch
|
8
|
+
#
|
9
|
+
# image_id - String identifier, ex: "ami-ee4f77ab"
|
10
|
+
# ec2_opts - Hash of
|
11
|
+
# access_key_id - String
|
12
|
+
# secret_access_key - String
|
13
|
+
# region - String, ex: "us-east-1"
|
14
|
+
#
|
15
|
+
#
|
16
|
+
# Returns nothing
|
17
|
+
def setup(image_id, ec2_opts)
|
18
|
+
puts "Starting setup for #{ec2_opts[:region]} ..."
|
19
|
+
|
20
|
+
ec2 = AWS::EC2.new(ec2_opts)
|
21
|
+
|
22
|
+
key_pair, private_key_filename = Cirrus::KeyPair.find_or_create_default(ec2)
|
23
|
+
|
24
|
+
security_group = Cirrus::SecurityGroup.find_or_create_default(ec2)
|
25
|
+
|
26
|
+
instance_type = "m1.small"
|
27
|
+
launch_opts = {
|
28
|
+
:image_id => image_id,
|
29
|
+
instance_type: instance_type,
|
30
|
+
:key_pair => key_pair,
|
31
|
+
:security_groups => security_group
|
32
|
+
# :associate_public_ip_address => true # Only for VPC launch
|
33
|
+
}
|
34
|
+
tags = { 'Name' => "cirrus-sample-#{Time.now.to_i}" }
|
35
|
+
instance = Cirrus::Instance.launch(ec2, launch_opts, tags)
|
36
|
+
|
37
|
+
host = instance.public_ip_address
|
38
|
+
|
39
|
+
Cirrus::SSH.bootstrap(
|
40
|
+
host: host,
|
41
|
+
username: 'ubuntu',
|
42
|
+
private_key_path: private_key_filename,
|
43
|
+
source_path: nil
|
44
|
+
)
|
45
|
+
|
46
|
+
puts "Done! Provisioned instance #{instance.id} host #{host}, key #{private_key_filename}"
|
47
|
+
end
|
48
|
+
|
49
|
+
|
50
|
+
# Terminate an instance and its associated security groups / key pairs
|
51
|
+
#
|
52
|
+
# instance_id - String id, ex: "i-74f86cbe"
|
53
|
+
# ec2_opts - Hash of
|
54
|
+
# access_key_id - String
|
55
|
+
# secret_access_key - String
|
56
|
+
# region - String, ex: "us-east-1"
|
57
|
+
#
|
58
|
+
# Returns nothing
|
59
|
+
def teardown(instance_id, ec2_opts)
|
60
|
+
puts "Tearing down #{instance_id} ..."
|
61
|
+
|
62
|
+
ec2 = AWS::EC2.new(ec2_opts)
|
63
|
+
Cirrus::Instance.teardown(ec2, instance_id)
|
64
|
+
|
65
|
+
puts "Teardown successful for #{instance_id}!"
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
69
|
+
|
70
|
+
require "cirrus/version"
|
71
|
+
require 'cirrus/instance'
|
72
|
+
require 'cirrus/key_pair'
|
73
|
+
require 'cirrus/scp'
|
74
|
+
require 'cirrus/security_group'
|
75
|
+
require 'cirrus/ssh'
|
metadata
ADDED
@@ -0,0 +1,72 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: cirrus-aws
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Andrew Berls
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-11-10 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.7'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.7'
|
27
|
+
description: ''
|
28
|
+
email:
|
29
|
+
- andrew.berls@gmail.com
|
30
|
+
executables:
|
31
|
+
- cirrus
|
32
|
+
extensions: []
|
33
|
+
extra_rdoc_files: []
|
34
|
+
files:
|
35
|
+
- ".gitignore"
|
36
|
+
- Gemfile
|
37
|
+
- LICENSE.txt
|
38
|
+
- README.md
|
39
|
+
- bin/cirrus
|
40
|
+
- cirrus-aws.gemspec
|
41
|
+
- lib/cirrus.rb
|
42
|
+
- lib/cirrus/instance.rb
|
43
|
+
- lib/cirrus/key_pair.rb
|
44
|
+
- lib/cirrus/scp.rb
|
45
|
+
- lib/cirrus/security_group.rb
|
46
|
+
- lib/cirrus/ssh.rb
|
47
|
+
- lib/cirrus/version.rb
|
48
|
+
homepage: ''
|
49
|
+
licenses:
|
50
|
+
- MIT
|
51
|
+
metadata: {}
|
52
|
+
post_install_message:
|
53
|
+
rdoc_options: []
|
54
|
+
require_paths:
|
55
|
+
- lib
|
56
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
57
|
+
requirements:
|
58
|
+
- - ">="
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: '0'
|
61
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
62
|
+
requirements:
|
63
|
+
- - ">="
|
64
|
+
- !ruby/object:Gem::Version
|
65
|
+
version: '0'
|
66
|
+
requirements: []
|
67
|
+
rubyforge_project:
|
68
|
+
rubygems_version: 2.4.1
|
69
|
+
signing_key:
|
70
|
+
specification_version: 4
|
71
|
+
summary: A command-line tool to provision and launch AWS EC2 instances
|
72
|
+
test_files: []
|