pairhost 0.0.2 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -1,11 +1,10 @@
1
1
  # Pairhost
2
2
 
3
- Create an EC2 instance for Relevance, Inc. remote pairing! It creates the instance and reports the public DNS name right from the command line.
3
+ Manage EC2 instances for remote pairing the style that Relevance prefers.
4
4
 
5
- ## How To Use
5
+ ## How to Use
6
6
 
7
- # install bundler
8
- bundle install
9
- cp config.example.yml config.yml
10
- # edit the config to use real EC2 & AMI stuff (ask me if you need it)
11
- #TODO: ./pairhost up
7
+ gem install pairhost
8
+ pairhost init
9
+ # edit ~/.pairhost/config.yml to use your real EC2 & AMI settings
10
+ pairhost up
data/Rakefile CHANGED
@@ -1 +1,5 @@
1
- require "bundler/gem_tasks"
1
+ require 'bundler/gem_tasks'
2
+ require 'rspec/core/rake_task'
3
+
4
+ RSpec::Core::RakeTask.new
5
+ task :default => :spec
@@ -1,3 +1,3 @@
1
1
  module Pairhost
2
- VERSION = "0.0.2"
2
+ VERSION = "0.0.3"
3
3
  end
data/lib/pairhost.rb CHANGED
@@ -12,14 +12,18 @@ module Pairhost
12
12
  def self.config
13
13
  @config ||= begin
14
14
  unless File.exists?(config_file)
15
- abort "No pairhost config found. First run 'pairhost init'."
15
+ abort "pairhost: No config found. First run 'pairhost init'."
16
16
  end
17
17
  YAML.load_file(config_file)
18
18
  end
19
19
  end
20
20
 
21
21
  def self.instance_id
22
- @instance_id ||= File.read(File.expand_path('~/.pairhost/instance')).chomp
22
+ return @instance_id unless @instance_id.nil?
23
+
24
+ file = File.expand_path('~/.pairhost/instance')
25
+ @instance_id = File.read(file).chomp if File.exists?(file)
26
+ return @instance_id
23
27
  end
24
28
 
25
29
  def self.connection
@@ -38,7 +42,7 @@ module Pairhost
38
42
 
39
43
  def self.create(name)
40
44
  server_options = {
41
- "tags" => {"Name" => name,
45
+ "tags" => {"Name" => name,
42
46
  "Created-By-Pairhost-Gem" => VERSION},
43
47
  "image_id" => config['ami_id'],
44
48
  "flavor_id" => config['flavor_id'],
@@ -58,6 +62,7 @@ module Pairhost
58
62
  File.open(File.expand_path('~/.pairhost/instance'), "w") do |f|
59
63
  f.write(instance_id)
60
64
  end
65
+ @instance_id = nil
61
66
  end
62
67
 
63
68
  def self.start(server)
@@ -70,32 +75,56 @@ module Pairhost
70
75
  server.wait_for { state == "stopped" }
71
76
  end
72
77
 
78
+ def self.fetch!
79
+ server = fetch
80
+ abort "pairhost: No instance found. Please create or attach to one." if server.nil?
81
+ server
82
+ end
83
+
73
84
  def self.fetch
74
- connection.servers.get(instance_id) if instance_id
85
+ config
86
+ return instance_id.nil? ? nil : connection.servers.get(instance_id)
75
87
  end
76
88
 
77
89
  class CLI < Thor
78
90
  include Thor::Actions
79
91
 
80
- desc "ssh", "SSH to your pairhost"
81
- def ssh
82
- server = Pairhost.fetch
83
- exec "ssh -A -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o LogLevel=QUIET pair@#{server.dns_name}"
92
+ desc "verify", "Verify the config is in place"
93
+ def verify
94
+ Pairhost.config
84
95
  end
85
96
 
86
- desc "status", "Print the status of your pairhost"
87
- def status
88
- server = Pairhost.fetch
89
- puts "#{server.id}: #{server.tags['Name']}"
90
- puts "State: #{server.state}"
91
- puts server.dns_name if server.dns_name
97
+ desc "init", "Setup your ~/.pairhost directory with default config"
98
+ def init
99
+ if File.exists?(Pairhost.config_file)
100
+ STDERR.puts "pairhost: Already initialized."
101
+ else
102
+ FileUtils.mkdir_p File.dirname(Pairhost.config_file)
103
+ FileUtils.cp(File.dirname(__FILE__) + '/../config.example.yml', Pairhost.config_file)
104
+ end
105
+ end
106
+
107
+ desc "create [NAME]", "Provision a new pairhost; all future commands affect this pairhost"
108
+ def create(name=nil)
109
+ invoke :verify, []
110
+
111
+ if name == nil
112
+ initials = `git config user.initials`.chomp.split("/").map(&:upcase).join(" ")
113
+ name = "Pairhost (#{initials})"
114
+ end
115
+
116
+ puts "Provisioning \"#{name}\"..."
117
+ server = Pairhost.create(name)
118
+ puts "provisioned!"
119
+ invoke :status, []
92
120
  end
93
121
 
94
122
  map "start" => :resume
95
123
 
96
124
  desc "resume", "Start a stopped pairhost"
97
125
  def resume
98
- server = Pairhost.fetch
126
+ invoke :verify
127
+ server = Pairhost.fetch!
99
128
  puts "Starting..."
100
129
  Pairhost.start(server.reload)
101
130
  puts "started!"
@@ -103,20 +132,9 @@ module Pairhost
103
132
  invoke :status
104
133
  end
105
134
 
106
- desc "create", "Provision a new pairhost; all future commands affect this pairhost"
107
- def create
108
- initials = `git config user.initials`.chomp.split("/").map(&:upcase).join(" ")
109
- default_name = "something1 #{initials}"
110
- name = ask_with_default("What to name your pairhost? [#{default_name}]", default_name)
111
- puts "Name will be: #{name}"
112
- puts "Provisioning..."
113
- # server = Pairhost.create(name)
114
- # puts "provisioning!"
115
- # invoke :status
116
- end
117
-
118
135
  desc "up", "Create a new pairhost or start your stopped pairhost"
119
136
  def up
137
+ invoke :verify
120
138
  server = Pairhost.fetch
121
139
 
122
140
  if server
@@ -126,32 +144,42 @@ module Pairhost
126
144
  end
127
145
  end
128
146
 
147
+ desc "status", "Print the status of your pairhost"
148
+ def status
149
+ invoke :verify
150
+ server = Pairhost.fetch!
151
+ puts "#{server.id}: #{server.tags['Name']}"
152
+ puts "State: #{server.state}"
153
+ puts server.dns_name if server.dns_name
154
+ end
155
+
156
+ desc "ssh", "SSH to your pairhost"
157
+ def ssh
158
+ invoke :verify
159
+ server = Pairhost.fetch!
160
+ exec "ssh -A -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o LogLevel=QUIET pair@#{server.dns_name}"
161
+ end
162
+
129
163
  map "halt" => :stop
130
164
  map "shutdown" => :stop
131
165
  map "suspend" => :stop
132
166
 
133
167
  desc "stop", "Stop your pairhost"
134
168
  def stop
135
- server = Pairhost.fetch
169
+ invoke :verify
170
+ server = Pairhost.fetch!
136
171
  puts "Shutting down..."
137
172
  Pairhost.stop(server)
138
173
  puts "shutdown!"
139
174
  end
140
175
 
141
- desc "attach", "All future commands affect this pairhost"
142
- def attach
143
- instance_id = ask("EC2 Instance?")
144
- Pairhost.write_instance_id(instance_id)
145
- invoke :status
146
- end
147
-
148
176
  map "terminate" => :destroy
149
177
 
150
178
  desc "destroy", "Terminate your pairhost"
151
179
  def destroy
152
- server = Pairhost.fetch
180
+ invoke :verify
181
+ server = Pairhost.fetch!
153
182
  confirm = ask("Type 'yes' to confirm deleting '#{server.tags['Name']}'.\n>")
154
-
155
183
  return unless confirm == "yes"
156
184
 
157
185
  puts "Destroying..."
@@ -160,15 +188,25 @@ module Pairhost
160
188
  puts "destroyed!"
161
189
  end
162
190
 
163
- desc "init", "Setup your ~/.pairhost directory with default config"
164
- def init
165
- FileUtils.mkdir_p File.dirname(Pairhost.config_file)
166
- FileUtils.cp(File.dirname(__FILE__) + '/../config.example.yml', Pairhost.config_file)
167
- end
168
-
169
191
  desc "provision", "Freshen the Chef recipes"
170
192
  def provision
171
- puts "coming soon..."
193
+ invoke :verify
194
+ # TODO implement
195
+ puts "Coming soon..."
196
+ end
197
+
198
+ desc "attach INSTANCE", "All future commands affect the pairhost with the given EC2 instance ID"
199
+ def attach(instance_id)
200
+ invoke :verify, []
201
+ Pairhost.write_instance_id(instance_id)
202
+ invoke :status, []
203
+ end
204
+
205
+ desc "detach", "Forget the currently-attached pairhost"
206
+ def detach
207
+ invoke :verify
208
+ # TODO implement
209
+ puts "Coming soon..."
172
210
  end
173
211
 
174
212
  private
data/pairhost.gemspec CHANGED
@@ -8,13 +8,17 @@ Gem::Specification.new do |s|
8
8
  s.authors = ["Larry Karnowski"]
9
9
  s.email = ["larry@hickorywind.org"]
10
10
  s.homepage = "http://www.github.com/karnowski/pairhost"
11
- s.summary = %q{Automate creation of Relevance pairhost EC2 instances.}
12
- s.description = %q{A Vagrant-like command line interface for creating, managing, and using EC2 instances for remote pairing.}
11
+ s.summary = %q{Automate creation of Relevance-style pairhost EC2 instances.}
12
+ s.description = %q{A Vagrant-like command line interface for creating, managing, and using EC2 instances for remote pairing like we do at Relevance.}
13
13
 
14
14
  s.files = `git ls-files`.split("\n")
15
15
  s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
16
16
  s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
17
17
 
18
- s.add_runtime_dependency "fog", "1.1.2"
19
- s.add_runtime_dependency "thor", "0.14.6"
18
+ s.add_runtime_dependency "fog", "1.3.1"
19
+ s.add_runtime_dependency "thor", "~> 0.15.2"
20
+
21
+ s.add_development_dependency 'rspec', '~> 2.9.0'
22
+ s.add_development_dependency 'bahia', '~> 0.7.2'
23
+ s.add_development_dependency 'rake', '~> 0.9.2.2'
20
24
  end
@@ -0,0 +1,90 @@
1
+ require 'spec_helper'
2
+ require 'fileutils'
3
+
4
+ def ensure_config_file
5
+ config_file = File.expand_path('~/.pairhost/config.yml')
6
+ return if File.exist?(config_file)
7
+ abort "Pairhost config file was NOT found."
8
+ end
9
+
10
+ def safely_move(source, target)
11
+ if File.exist?(source)
12
+ FileUtils.rm_rf(target)
13
+ FileUtils.mv(source, target)
14
+ end
15
+ end
16
+
17
+ describe Pairhost do
18
+ context "when NO pairhost config is present" do
19
+ let(:config_dir) { File.expand_path('~/.pairhost') }
20
+ let(:backup_dir) { File.expand_path('~/.pairhost_test_backup') }
21
+ let(:config_file) { File.expand_path('~/.pairhost/config.yml') }
22
+
23
+ before(:all) { safely_move config_dir, backup_dir }
24
+ after(:all) { safely_move backup_dir, config_dir }
25
+
26
+ the "attach command returns an error message and failure exit code" do
27
+ pairhost "attach some-instance"
28
+ stderr.should == "pairhost: No config found. First run 'pairhost init'.\n"
29
+ process.should_not be_success
30
+ end
31
+
32
+ %w{create up provision detach status ssh resume stop destroy}.each do |method|
33
+ the "#{method} command returns an error message and failure exit code" do
34
+ pairhost method
35
+ stderr.should == "pairhost: No config found. First run 'pairhost init'.\n"
36
+ process.should_not be_success
37
+ end
38
+ end
39
+
40
+ it "init creates a config directory and file" do
41
+ config_dir.should_not exist_on_filesystem
42
+
43
+ pairhost "init"
44
+
45
+ config_dir.should exist_on_filesystem
46
+ config_file.should exist_on_filesystem
47
+ end
48
+ end
49
+
50
+ context "when a valid pairhost config is present" do
51
+ # NOTE: this assumes that as a developer you're also
52
+ # a user of pairhost and have already called "pairhost init"
53
+ # and configured real AWS credentials.
54
+ before(:all) { ensure_config_file }
55
+
56
+ the "init command complains that the config is already present" do
57
+ pairhost "init"
58
+ stderr.should == "pairhost: Already initialized.\n"
59
+ process.should be_success
60
+ end
61
+
62
+ context "when an instance has NOT been provisioned" do
63
+ let(:instance_file) { File.expand_path('~/.pairhost/instance') }
64
+ let(:backup_file) { File.expand_path('~/.pairhost/instance_test_backup') }
65
+
66
+ before(:all) { FileUtils.mv(instance_file, backup_file) if File.exist?(instance_file) }
67
+ after(:all) { FileUtils.mv(backup_file, instance_file) if File.exist?(backup_file) }
68
+
69
+ # create --> asks to confirm creation
70
+ # up --> asks to confirm creation
71
+ # attach --> multiple branches; with instance id specific and without
72
+ # provision --> not implemented yet
73
+ # detach --> not implemented yet
74
+
75
+ %w{status ssh resume stop destroy}.each do |method|
76
+ the "#{method} command returns an error message and failure exit code" do
77
+ pairhost method
78
+ stderr.should == "pairhost: No instance found. Please create or attach to one.\n"
79
+ process.should_not be_success
80
+ end
81
+ end
82
+ end
83
+
84
+ context "when an instance has been provisioned" do
85
+ # TODO: make sure the instance_id file exists;
86
+ # provision an EC2 instance?
87
+ end
88
+
89
+ end
90
+ end
@@ -0,0 +1,18 @@
1
+ require 'bahia'
2
+ require 'pairhost'
3
+
4
+ RSpec.configure do |config|
5
+ config.include Bahia
6
+ config.run_all_when_everything_filtered = true
7
+ config.filter_run :focused => true
8
+ config.filter_run_excluding :disabled => true
9
+ config.alias_example_to :fit, :focused => true
10
+ config.alias_example_to :the
11
+ config.color_enabled = true
12
+ end
13
+
14
+ RSpec::Matchers.define :exist_on_filesystem do |name|
15
+ match do |name|
16
+ File.exist?(name)
17
+ end
18
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pairhost
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 0.0.3
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-04-25 00:00:00.000000000 Z
12
+ date: 2012-05-17 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: fog
@@ -18,7 +18,7 @@ dependencies:
18
18
  requirements:
19
19
  - - '='
20
20
  - !ruby/object:Gem::Version
21
- version: 1.1.2
21
+ version: 1.3.1
22
22
  type: :runtime
23
23
  prerelease: false
24
24
  version_requirements: !ruby/object:Gem::Requirement
@@ -26,25 +26,73 @@ dependencies:
26
26
  requirements:
27
27
  - - '='
28
28
  - !ruby/object:Gem::Version
29
- version: 1.1.2
29
+ version: 1.3.1
30
30
  - !ruby/object:Gem::Dependency
31
31
  name: thor
32
32
  requirement: !ruby/object:Gem::Requirement
33
33
  none: false
34
34
  requirements:
35
- - - '='
35
+ - - ~>
36
36
  - !ruby/object:Gem::Version
37
- version: 0.14.6
37
+ version: 0.15.2
38
38
  type: :runtime
39
39
  prerelease: false
40
40
  version_requirements: !ruby/object:Gem::Requirement
41
41
  none: false
42
42
  requirements:
43
- - - '='
43
+ - - ~>
44
+ - !ruby/object:Gem::Version
45
+ version: 0.15.2
46
+ - !ruby/object:Gem::Dependency
47
+ name: rspec
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ~>
52
+ - !ruby/object:Gem::Version
53
+ version: 2.9.0
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ version: 2.9.0
62
+ - !ruby/object:Gem::Dependency
63
+ name: bahia
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ~>
68
+ - !ruby/object:Gem::Version
69
+ version: 0.7.2
70
+ type: :development
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ~>
76
+ - !ruby/object:Gem::Version
77
+ version: 0.7.2
78
+ - !ruby/object:Gem::Dependency
79
+ name: rake
80
+ requirement: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ~>
84
+ - !ruby/object:Gem::Version
85
+ version: 0.9.2.2
86
+ type: :development
87
+ prerelease: false
88
+ version_requirements: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ~>
44
92
  - !ruby/object:Gem::Version
45
- version: 0.14.6
93
+ version: 0.9.2.2
46
94
  description: A Vagrant-like command line interface for creating, managing, and using
47
- EC2 instances for remote pairing.
95
+ EC2 instances for remote pairing like we do at Relevance.
48
96
  email:
49
97
  - larry@hickorywind.org
50
98
  executables:
@@ -61,6 +109,8 @@ files:
61
109
  - lib/pairhost.rb
62
110
  - lib/pairhost/version.rb
63
111
  - pairhost.gemspec
112
+ - spec/pairhost_spec.rb
113
+ - spec/spec_helper.rb
64
114
  - update-nx.sh
65
115
  homepage: http://www.github.com/karnowski/pairhost
66
116
  licenses: []
@@ -85,5 +135,7 @@ rubyforge_project:
85
135
  rubygems_version: 1.8.21
86
136
  signing_key:
87
137
  specification_version: 3
88
- summary: Automate creation of Relevance pairhost EC2 instances.
89
- test_files: []
138
+ summary: Automate creation of Relevance-style pairhost EC2 instances.
139
+ test_files:
140
+ - spec/pairhost_spec.rb
141
+ - spec/spec_helper.rb