blimpy 0.1.0 → 0.2.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.
@@ -17,4 +17,5 @@ Gem::Specification.new do |gem|
17
17
 
18
18
  gem.add_dependency 'fog'
19
19
  gem.add_dependency 'thor'
20
+ gem.add_dependency 'minitar'
20
21
  end
@@ -0,0 +1,37 @@
1
+ Feature: SCP a file into a named VM
2
+ In order to easily copy files from my local host to the named VM
3
+ As a Blimpy user
4
+ I should be able to run `blimpy scp <name> <file> [dest]`
5
+
6
+ Scenario: SCPing with an invalid blimp name
7
+ Given I have the Blimpfile:
8
+ """
9
+ Blimpy.fleet do |f|
10
+ f.add do |host|
11
+ host.name = 'Cucumber Host'
12
+ end
13
+ end
14
+ """
15
+ And I have a file named "hello.txt"
16
+ When I run `blimpy scp Gherkins hello.txt`
17
+ Then the exit status should be 1
18
+ And the output should contain:
19
+ """
20
+ Could not find a blimp named "Gherkins"
21
+ """
22
+
23
+ # This test is in the same boat that the complimentary test in ssh.feature is
24
+ # in.
25
+ @slow @destroy @wip
26
+ Scenario: SCPing a valid file
27
+ Given I have the Blimpfile:
28
+ """
29
+ Blimpy.fleet do |f|
30
+ f.add do |host|
31
+ host.name = 'Cucumber Host'
32
+ end
33
+ end
34
+ """
35
+ And I have a file named "hello.txt"
36
+ When I run `blimpy scp "Cucumber Host" hello.txt`
37
+ Then the exit status should be 0
@@ -16,7 +16,7 @@ Feature: SSH into a named VM
16
16
  Then the exit status should be 1
17
17
  And the output should contain:
18
18
  """
19
- Could not find blimp named "Gherkins"
19
+ Could not find a blimp named "Gherkins"
20
20
  """
21
21
 
22
22
 
@@ -26,6 +26,12 @@ Given /^I have a single VM running$/ do
26
26
  end
27
27
  end
28
28
 
29
+ Given /^I have a file named "([^"]*)"$/ do |filename|
30
+ File.open(filename, 'w') do |fd|
31
+ fd.write("I am #{filename}\n")
32
+ end
33
+ end
34
+
29
35
  When /^I ssh into the machine$/ do
30
36
  step %{I run `blimpy start`}
31
37
  step %{I run `blimpy ssh "Cucumber Host" -o StrictHostKeyChecking=no` interactively}
@@ -1,4 +1,5 @@
1
1
  require 'blimpy/helpers/state'
2
+ require 'blimpy/livery'
2
3
  require 'blimpy/keys'
3
4
 
4
5
  module Blimpy
@@ -37,6 +38,8 @@ module Blimpy
37
38
  @tags = {}
38
39
  @server = server
39
40
  @fleet_id = 0
41
+ @ssh_connected = false
42
+ @exec_commands = true
40
43
  end
41
44
 
42
45
  def region=(newRegion)
@@ -65,6 +68,14 @@ module Blimpy
65
68
  write_state_file
66
69
  end
67
70
 
71
+ def bootstrap
72
+ @exec_commands = false
73
+ unless livery.nil?
74
+ wait_for_sshd
75
+ bootstrap_livery
76
+ end
77
+ end
78
+
68
79
  def stop
69
80
  unless @server.nil?
70
81
  @server.stop
@@ -91,7 +102,6 @@ module Blimpy
91
102
  end
92
103
  end
93
104
 
94
-
95
105
  def state_file
96
106
  if @server.nil?
97
107
  raise Exception, "I can't make a state file without a @server!"
@@ -120,19 +130,62 @@ module Blimpy
120
130
  'no name'
121
131
  end
122
132
 
123
- def ssh_into
133
+ def run_command(*args)
134
+ if @exec_commands
135
+ ::Kernel.exec(*args)
136
+ else
137
+ ::Kernel.system(*args)
138
+ end
139
+ end
140
+
141
+ def ssh_into(*args)
142
+ # Support using #ssh_into within our own code as well to pass arguments
143
+ # to the ssh(1) binary
144
+ args = args || ARGV[2 .. -1]
145
+ run_command('ssh', '-o', 'StrictHostKeyChecking=no',
146
+ '-l', username, dns_name, *args)
147
+ end
148
+
149
+ def scp_file(filename)
150
+ filename = File.expand_path(filename)
151
+ run_command('scp', '-o', 'StrictHostKeyChecking=no',
152
+ filename, "#{username}@#{dns_name}:", *ARGV[3..-1])
153
+ end
154
+
155
+ def bootstrap_livery
156
+ tarball = nil
157
+ if livery == :cwd
158
+ tarball = Blimpy::Livery.tarball_directory(Dir.pwd)
159
+ scp_file(tarball)
160
+ # HAXX
161
+ basename = File.basename(tarball)
162
+ puts 'Bootstrapping the livery'
163
+ ssh_into("tar -zxf #{basename} && cd #{File.basename(tarball, '.tar.gz')} && sudo ./bootstrap.sh")
164
+ end
165
+ end
166
+
167
+ def wait_for_sshd
168
+ return if @ssh_connected
124
169
  start = Time.now.to_i
125
- print "..making sure #{@name} is online"
126
- begin
127
- TCPSocket.new(dns_name, 22)
128
- rescue Errno::ECONNREFUSED
129
- if (Time.now.to_i - start) < 30
130
- print '.'
131
- retry
170
+ use_exec = @exec_commands
171
+ # Even if we are supposed to use #exec here, we wait to disable it until
172
+ # after sshd(8) comes online
173
+ @exec_commands = false
174
+
175
+ print "..waiting for sshd on #{@name} to come online"
176
+ until @ssh_connected
177
+ # Run the `true` command and exit
178
+ @ssh_connected = ssh_into('-q', 'true')
179
+
180
+ unless @ssh_connected
181
+ if (Time.now.to_i - start) < 30
182
+ print '.'
183
+ sleep 1
184
+ end
132
185
  end
133
186
  end
134
187
  puts
135
- ::Kernel.exec('ssh', '-l', username, dns_name, *ARGV[2..-1])
188
+ @exec_commands = use_exec
136
189
  end
137
190
 
138
191
  private
@@ -15,6 +15,17 @@ module Blimpy
15
15
  exit 1
16
16
  end
17
17
  end
18
+
19
+ def box_by_name(name)
20
+ fleet = Blimpy::Fleet.new
21
+ box = nil
22
+ fleet.members.each do |instance_id, data|
23
+ box_name = data['name']
24
+ next unless box_name == name
25
+ box = Blimpy::Box.from_instance_id(instance_id, data)
26
+ end
27
+ box
28
+ end
18
29
  end
19
30
 
20
31
  desc 'start', 'Start up a fleet of blimps'
@@ -84,19 +95,25 @@ end
84
95
  desc 'ssh BLIMP_NAME', 'Log into a running blimp'
85
96
  def ssh(name, *args)
86
97
  ensure_blimpfile
87
- fleet = Blimpy::Fleet.new
88
- box = nil
89
- fleet.members.each do |instance_id, data|
90
- box_name = data['name']
91
- next unless box_name == name
92
- box = Blimpy::Box.from_instance_id(instance_id, data)
93
- end
98
+ box = box_by_name(name)
94
99
  if box.nil?
95
- puts "Could not find blimp named \"#{name}\""
100
+ puts "Could not find a blimp named \"#{name}\""
96
101
  exit 1
97
102
  end
98
-
103
+ box.wait_for_sshd
99
104
  box.ssh_into
100
105
  end
106
+
107
+ desc 'scp BLIMP_NAME FILE_NAME', 'Securely copy FILE_NAME into the blimp'
108
+ def scp(name, filename, *args)
109
+ ensure_blimpfile
110
+ box = box_by_name(name)
111
+ if box.nil?
112
+ puts "Could not find a blimp named \"#{name}\""
113
+ exit 1
114
+ end
115
+ box.wait_for_sshd
116
+ box.scp_file(filename)
117
+ end
101
118
  end
102
119
  end
@@ -69,6 +69,7 @@ module Blimpy
69
69
  host.wait_for_state('running') { print '.' }
70
70
  print ".. online at: #{host.dns_name}"
71
71
  host.online!
72
+ host.bootstrap
72
73
  puts
73
74
  end
74
75
 
@@ -0,0 +1,33 @@
1
+ require 'rubygems'
2
+ require 'zlib'
3
+ require 'archive/tar/minitar'
4
+
5
+ module Blimpy
6
+ class Livery
7
+ def self.tarball_directory(directory)
8
+ if directory.nil? || !(File.directory? directory)
9
+ raise ArgumentError, "The argument '#{directory}' doesn't appear to be a directory"
10
+ end
11
+
12
+ directory = File.expand_path(directory)
13
+ short_name = File.basename(directory)
14
+
15
+ tarball = nil
16
+ Dir.chdir(File.expand_path(directory + '/../')) do
17
+ tarball = self.gzip_for_directory(short_name, '/tmp') do |tgz|
18
+ Archive::Tar::Minitar.pack(short_name, tgz)
19
+ end
20
+ end
21
+ tarball
22
+ end
23
+
24
+ private
25
+
26
+ def self.gzip_for_directory(directory, root)
27
+ filename = File.join(root, "#{directory}.tar.gz")
28
+ yield Zlib::GzipWriter.new(File.open(filename, 'wb'))
29
+ filename
30
+ end
31
+
32
+ end
33
+ end
@@ -1,3 +1,3 @@
1
1
  module Blimpy
2
- VERSION = "0.1.0"
2
+ VERSION = "0.2.0"
3
3
  end
@@ -147,5 +147,21 @@ describe Blimpy::Box do
147
147
  end
148
148
 
149
149
  end
150
+
151
+ describe '#bootstrap_livery' do
152
+ context 'with a livery of :cwd' do
153
+ before :each do
154
+ subject.livery = :cwd
155
+ end
156
+
157
+ it 'should tarball up the current directory' do
158
+ Dir.should_receive(:pwd).and_return('mock-pwd')
159
+ Blimpy::Livery.should_receive(:tarball_directory).with('mock-pwd').and_return('mock-pwd.tar.gz')
160
+ subject.should_receive(:scp_file).with('mock-pwd.tar.gz')
161
+ subject.should_receive(:ssh_into)
162
+ subject.bootstrap_livery
163
+ end
164
+ end
165
+ end
150
166
  end
151
167
  end
@@ -0,0 +1,19 @@
1
+ require 'spec_helper'
2
+ require 'blimpy/livery'
3
+
4
+ describe Blimpy::Livery do
5
+ context 'class methods' do
6
+ describe '#tarball_directory' do
7
+ subject { Blimpy::Livery } # No instantiating!
8
+ it 'should raise an exception if the directory doesn\'t exist' do
9
+ expect {
10
+ subject.tarball_directory(nil)
11
+ }.to raise_error(ArgumentError)
12
+
13
+ expect {
14
+ subject.tarball_directory('/tmp/never-gonna-give-you-up.lolz')
15
+ }.to raise_error(ArgumentError)
16
+ end
17
+ end
18
+ end
19
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: blimpy
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-04-28 00:00:00.000000000Z
12
+ date: 2012-04-29 00:00:00.000000000Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: fog
16
- requirement: &6754760 !ruby/object:Gem::Requirement
16
+ requirement: &6248380 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ! '>='
@@ -21,10 +21,10 @@ dependencies:
21
21
  version: '0'
22
22
  type: :runtime
23
23
  prerelease: false
24
- version_requirements: *6754760
24
+ version_requirements: *6248380
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: thor
27
- requirement: &6754100 !ruby/object:Gem::Requirement
27
+ requirement: &6247840 !ruby/object:Gem::Requirement
28
28
  none: false
29
29
  requirements:
30
30
  - - ! '>='
@@ -32,7 +32,18 @@ dependencies:
32
32
  version: '0'
33
33
  type: :runtime
34
34
  prerelease: false
35
- version_requirements: *6754100
35
+ version_requirements: *6247840
36
+ - !ruby/object:Gem::Dependency
37
+ name: minitar
38
+ requirement: &6247360 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ! '>='
42
+ - !ruby/object:Gem::Version
43
+ version: '0'
44
+ type: :runtime
45
+ prerelease: false
46
+ version_requirements: *6247360
36
47
  description: Blimpy is a tool for managing a fleet of machines in the CLOUD!
37
48
  email:
38
49
  - tyler@monkeypox.org
@@ -50,6 +61,7 @@ files:
50
61
  - blimpy.gemspec
51
62
  - features/cli/init.feature
52
63
  - features/cli/list.feature
64
+ - features/cli/scp.feature
53
65
  - features/cli/ssh.feature
54
66
  - features/cli/start.feature
55
67
  - features/step_definitions/cli_steps.rb
@@ -62,12 +74,14 @@ files:
62
74
  - lib/blimpy/fleet.rb
63
75
  - lib/blimpy/helpers/state.rb
64
76
  - lib/blimpy/keys.rb
77
+ - lib/blimpy/livery.rb
65
78
  - lib/blimpy/version.rb
66
79
  - spec/blimpy/box_spec.rb
67
80
  - spec/blimpy/engine_spec.rb
68
81
  - spec/blimpy/fleet_spec.rb
69
82
  - spec/blimpy/helpers/state_spec.rb
70
83
  - spec/blimpy/keys_spec.rb
84
+ - spec/blimpy/livery_spec.rb
71
85
  - spec/blimpy_spec.rb
72
86
  - spec/spec_helper.rb
73
87
  homepage: https://github.com/rtyler/blimpy
@@ -97,6 +111,7 @@ summary: Ruby + CLOUD = Blimpy
97
111
  test_files:
98
112
  - features/cli/init.feature
99
113
  - features/cli/list.feature
114
+ - features/cli/scp.feature
100
115
  - features/cli/ssh.feature
101
116
  - features/cli/start.feature
102
117
  - features/step_definitions/cli_steps.rb
@@ -107,5 +122,6 @@ test_files:
107
122
  - spec/blimpy/fleet_spec.rb
108
123
  - spec/blimpy/helpers/state_spec.rb
109
124
  - spec/blimpy/keys_spec.rb
125
+ - spec/blimpy/livery_spec.rb
110
126
  - spec/blimpy_spec.rb
111
127
  - spec/spec_helper.rb