teleport 1.0.0 → 1.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/Gemfile +0 -1
- data/README.md +1 -1
- data/Rakefile +51 -3
- data/bin/teleport +0 -0
- data/lib/teleport.rb +1 -0
- data/lib/teleport/config.rb +9 -4
- data/lib/teleport/infer.rb +349 -0
- data/lib/teleport/install.rb +9 -3
- data/lib/teleport/main.rb +81 -38
- data/lib/teleport/mirror.rb +6 -2
- data/lib/teleport/run.sh +4 -4
- data/lib/teleport/util.rb +21 -2
- data/lib/teleport/version.rb +1 -1
- data/spec/end_to_end_spec.rb +39 -0
- data/spec/spec_helper.rb +25 -0
- data/spec/support/ec2.rb +115 -0
- data/spec/support/exit_code.rb +29 -0
- data/spec/support/telfile.rb +25 -0
- data/spec/unit/teleport/config_spec.rb +64 -0
- data/teleport.gemspec +10 -3
- metadata +84 -6
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -13,7 +13,7 @@ At the moment Teleport supports **Ubuntu 10.04/10.10/11.04 with Ruby 1.8.7, 1.9.
|
|
13
13
|
1. Install Teleport on your local machine.
|
14
14
|
|
15
15
|
```
|
16
|
-
$ sudo gem install teleport
|
16
|
+
$ sudo gem install teleport
|
17
17
|
```
|
18
18
|
|
19
19
|
1. Create a `Telfile` config file. Here's a simple example. Note that we actually define two machines, `server_app1` and `server_db1`:
|
data/Rakefile
CHANGED
@@ -1,10 +1,58 @@
|
|
1
1
|
require "bundler"
|
2
|
-
require "
|
2
|
+
require "bundler/setup"
|
3
3
|
|
4
|
-
|
4
|
+
require "rake"
|
5
|
+
require "rdoc/task"
|
6
|
+
require "rspec"
|
7
|
+
require "rspec/core/rake_task"
|
5
8
|
|
6
|
-
|
9
|
+
$LOAD_PATH << File.expand_path("../lib", __FILE__)
|
10
|
+
require "teleport/version"
|
11
|
+
|
12
|
+
#
|
13
|
+
# gem
|
14
|
+
#
|
15
|
+
|
16
|
+
task :gem => :build
|
17
|
+
task :build do
|
18
|
+
system "gem build --quiet teleport.gemspec"
|
19
|
+
end
|
20
|
+
|
21
|
+
task :install => :build do
|
22
|
+
system "sudo gem install --quiet teleport-#{Teleport::VERSION}.gem"
|
23
|
+
end
|
24
|
+
|
25
|
+
task :release => :build do
|
26
|
+
system "git tag -a #{Teleport::VERSION} -m 'Tagging #{Teleport::VERSION}'"
|
27
|
+
system "git push --tags"
|
28
|
+
system "gem push teleport-#{Teleport::VERSION}.gem"
|
29
|
+
end
|
30
|
+
|
31
|
+
#
|
32
|
+
# rspec
|
33
|
+
#
|
34
|
+
|
35
|
+
RSpec::Core::RakeTask.new(:spec) do |spec|
|
36
|
+
spec.rspec_opts = %w(--color --tty)
|
37
|
+
spec.pattern = "spec/**/*_spec.rb"
|
38
|
+
end
|
39
|
+
|
40
|
+
RSpec::Core::RakeTask.new("spec:unit") do |spec|
|
41
|
+
spec.pattern = "spec/unit/**/*_spec.rb"
|
42
|
+
end
|
43
|
+
|
44
|
+
RSpec::Core::RakeTask.new("spec:end") do |spec|
|
45
|
+
spec.pattern = "spec/end*_spec.rb"
|
46
|
+
end
|
47
|
+
|
48
|
+
#
|
49
|
+
# rdoc
|
50
|
+
#
|
51
|
+
|
52
|
+
RDoc::Task.new do |rdoc|
|
7
53
|
rdoc.rdoc_dir = "rdoc"
|
8
54
|
rdoc.title = "teleport #{Teleport::VERSION}"
|
9
55
|
rdoc.rdoc_files.include("lib/**/*.rb")
|
10
56
|
end
|
57
|
+
|
58
|
+
task :default => :spec
|
data/bin/teleport
CHANGED
File without changes
|
data/lib/teleport.rb
CHANGED
data/lib/teleport/config.rb
CHANGED
@@ -2,11 +2,10 @@ module Teleport
|
|
2
2
|
# This class parses Telfile, and includes DSL and the models.
|
3
3
|
class Config
|
4
4
|
RUBIES = ["1.9.2", "REE", "1.8.7"]
|
5
|
-
PATH = "Telfile"
|
6
5
|
|
7
|
-
attr_accessor :user, :ruby, :roles, :servers, :apt, :packages, :callbacks, :dsl
|
6
|
+
attr_accessor :user, :ruby, :ssh_options, :roles, :servers, :apt, :packages, :callbacks, :dsl
|
8
7
|
|
9
|
-
def initialize
|
8
|
+
def initialize(file = "Telfile")
|
10
9
|
@roles = []
|
11
10
|
@servers = []
|
12
11
|
@apt = []
|
@@ -14,7 +13,7 @@ module Teleport
|
|
14
13
|
@callbacks = { }
|
15
14
|
|
16
15
|
@dsl = DSL.new(self)
|
17
|
-
@dsl.instance_eval(File.read(
|
16
|
+
@dsl.instance_eval(File.read(file), file)
|
18
17
|
|
19
18
|
@user ||= Util.whoami
|
20
19
|
@ruby ||= RUBIES.first
|
@@ -95,6 +94,12 @@ module Teleport
|
|
95
94
|
@config.user = v
|
96
95
|
end
|
97
96
|
|
97
|
+
def ssh_options(v)
|
98
|
+
raise "ssh_options called twice" if @config.ssh_options
|
99
|
+
raise "ssh_options must be an Array" if !v.is_a?(Array)
|
100
|
+
@config.ssh_options = v
|
101
|
+
end
|
102
|
+
|
98
103
|
def role(name, options = {})
|
99
104
|
raise "role #{name.inspect} defined twice" if @config.roles.any? { |i| i.name == name }
|
100
105
|
@config.roles << Role.new(name, options)
|
@@ -0,0 +1,349 @@
|
|
1
|
+
require "set"
|
2
|
+
|
3
|
+
# Many, many thanks to Blueprint!
|
4
|
+
# https://github.com/devstructure/blueprint
|
5
|
+
|
6
|
+
module Teleport
|
7
|
+
class Infer
|
8
|
+
include Util
|
9
|
+
|
10
|
+
# Copyright 2011 DevStructure. All rights reserved.
|
11
|
+
#
|
12
|
+
# Redistribution and use in source and binary forms, with or without
|
13
|
+
# modification, are permitted provided that the following conditions are
|
14
|
+
# met:
|
15
|
+
#
|
16
|
+
# 1. Redistributions of source code must retain the above copyright
|
17
|
+
# notice, this list of conditions and the following disclaimer.
|
18
|
+
#
|
19
|
+
# 2. Redistributions in binary form must reproduce the above
|
20
|
+
# copyright notice, this list of conditions and the following
|
21
|
+
# disclaimer in the documentation and/or other materials provided
|
22
|
+
# with the distribution.
|
23
|
+
#
|
24
|
+
# THIS SOFTWARE IS PROVIDED BY DEVSTRUCTURE ``AS IS'' AND ANY EXPRESS
|
25
|
+
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
26
|
+
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
27
|
+
# DISCLAIMED. IN NO EVENT SHALL DEVSTRUCTURE OR CONTRIBUTORS BE LIABLE
|
28
|
+
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
29
|
+
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
30
|
+
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
31
|
+
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
32
|
+
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
33
|
+
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
34
|
+
# THE POSSIBILITY OF SUCH DAMAGE.
|
35
|
+
#
|
36
|
+
# The views and conclusions contained in the software and documentation
|
37
|
+
# are those of the authors and should not be interpreted as representing
|
38
|
+
# official policies, either expressed or implied, of DevStructure.
|
39
|
+
#
|
40
|
+
# (for MD5SUMS)
|
41
|
+
|
42
|
+
MD5SUMS = {
|
43
|
+
'/etc/adduser.conf' => ['/usr/share/adduser/adduser.conf'],
|
44
|
+
'/etc/apparmor.d/tunables/home.d/ubuntu' =>
|
45
|
+
['2a88811f7b763daa96c20b20269294a4'],
|
46
|
+
'/etc/apt/apt.conf.d/00CDMountPoint' =>
|
47
|
+
['cb46a4e03f8c592ee9f56c948c14ea4e'],
|
48
|
+
'/etc/apt/apt.conf.d/00trustcdrom' =>
|
49
|
+
['a8df82e6e6774f817b500ee10202a968'],
|
50
|
+
'/etc/chatscripts/provider' => ['/usr/share/ppp/provider.chatscript'],
|
51
|
+
'/etc/default/console-setup' =>
|
52
|
+
['0fb6cec686d0410993bdf17192bee7d6',
|
53
|
+
'b684fd43b74ac60c6bdafafda8236ed3',
|
54
|
+
'/usr/share/console-setup/console-setup'],
|
55
|
+
'/etc/default/grub' => ['ee9df6805efb2a7d1ba3f8016754a119',
|
56
|
+
'ad9283019e54cedfc1f58bcc5e615dce'],
|
57
|
+
'/etc/default/irqbalance' => ['7e10d364b9f72b11d7bf7bd1cfaeb0ff'],
|
58
|
+
'/etc/default/keyboard' => ['06d66484edaa2fbf89aa0c1ec4989857'],
|
59
|
+
'/etc/default/locale' => ['164aba1ef1298affaa58761647f2ceba',
|
60
|
+
'7c32189e775ac93487aa4a01dffbbf76'],
|
61
|
+
'/etc/default/rcS' => ['/usr/share/initscripts/default.rcS'],
|
62
|
+
'/etc/environment' => ['44ad415fac749e0c39d6302a751db3f2'],
|
63
|
+
'/etc/hosts.allow' => ['8c44735847c4f69fb9e1f0d7a32e94c1'],
|
64
|
+
'/etc/hosts.deny' => ['92a0a19db9dc99488f00ac9e7b28eb3d'],
|
65
|
+
'/etc/initramfs-tools/modules' =>
|
66
|
+
['/usr/share/initramfs-tools/modules'],
|
67
|
+
'/etc/inputrc' => ['/usr/share/readline/inputrc'],
|
68
|
+
'/etc/iscsi/iscsid.conf' => ['6c6fd718faae84a4ab1b276e78fea471'],
|
69
|
+
'/etc/kernel-img.conf' => ['f1ed9c3e91816337aa7351bdf558a442'],
|
70
|
+
'/etc/ld.so.conf' => ['4317c6de8564b68d628c21efa96b37e4'],
|
71
|
+
'/etc/networks' => ['/usr/share/base-files/networks'],
|
72
|
+
'/etc/nsswitch.conf' => ['/usr/share/base-files/nsswitch.conf'],
|
73
|
+
'/etc/pam.d/common-account' => ['9d50c7dda6ba8b6a8422fd4453722324'],
|
74
|
+
'/etc/pam.d/common-auth' => ['a326c972f4f3d20e5f9e1b06eef4d620'],
|
75
|
+
'/etc/pam.d/common-password' => ['9f2fbf01b1a36a017b16ea62c7ff4c22'],
|
76
|
+
'/etc/pam.d/common-session' => ['e2b72dd3efb2d6b29698f944d8723ab1'],
|
77
|
+
'/etc/pam.d/common-session-noninteractive' =>
|
78
|
+
['508d44b6daafbc3d6bd587e357a6ff5b'],
|
79
|
+
'/etc/ppp/chap-secrets' => ['faac59e116399eadbb37644de6494cc4'],
|
80
|
+
'/etc/ppp/pap-secrets' => ['698c4d412deedc43dde8641f84e8b2fd'],
|
81
|
+
'/etc/ppp/peers/provider' => ['/usr/share/ppp/provider.peer'],
|
82
|
+
'/etc/profile' => ['/usr/share/base-files/profile'],
|
83
|
+
'/etc/python/debian_config' => ['7f4739eb8858d231601a5ed144099ac8'],
|
84
|
+
'/etc/rc.local' => ['10fd9f051accb6fd1f753f2d48371890'],
|
85
|
+
'/etc/rsyslog.d/50-default.conf' =>
|
86
|
+
['/usr/share/rsyslog/50-default.conf'],
|
87
|
+
'/etc/security/opasswd' => ['d41d8cd98f00b204e9800998ecf8427e'],
|
88
|
+
'/etc/sgml/xml-core.cat' => ['bcd454c9bf55a3816a134f9766f5928f'],
|
89
|
+
'/etc/shells' => ['0e85c87e09d716ecb03624ccff511760'],
|
90
|
+
'/etc/ssh/sshd_config' => ['e24f749808133a27d94fda84a89bb27b',
|
91
|
+
'8caefdd9e251b7cc1baa37874149a870'],
|
92
|
+
'/etc/sudoers' => ['02f74ccbec48997f402a063a172abb48'],
|
93
|
+
'/etc/ufw/after.rules' => ['/usr/share/ufw/after.rules'],
|
94
|
+
'/etc/ufw/after6.rules' => ['/usr/share/ufw/after6.rules'],
|
95
|
+
'/etc/ufw/before.rules' => ['/usr/share/ufw/before.rules'],
|
96
|
+
'/etc/ufw/before6.rules' => ['/usr/share/ufw/before6.rules'],
|
97
|
+
'/etc/ufw/ufw.conf' => ['/usr/share/ufw/ufw.conf']
|
98
|
+
}
|
99
|
+
|
100
|
+
NEW_FILES_WITHIN = %w(cron.d logrotate.d rsyslog.d init)
|
101
|
+
CHECKSUM_FILES = %w(bash.bashrc environment inputrc rc.local ssh/ssh_config ssh/sshd_config)
|
102
|
+
|
103
|
+
def initialize
|
104
|
+
@telfile = []
|
105
|
+
|
106
|
+
if fails?("grep -q Ubuntu /etc/lsb-release")
|
107
|
+
fatal "Sorry, --infer can only run on an Ubuntu machine."
|
108
|
+
end
|
109
|
+
|
110
|
+
append "#" * 72
|
111
|
+
append "# Telfile inferred from #{`hostname`.strip} at #{Time.now}"
|
112
|
+
append "#" * 72
|
113
|
+
append
|
114
|
+
|
115
|
+
user
|
116
|
+
ruby
|
117
|
+
apt
|
118
|
+
packages
|
119
|
+
files
|
120
|
+
|
121
|
+
banner "Done!"
|
122
|
+
$stderr.puts
|
123
|
+
@telfile.each { |i| puts i }
|
124
|
+
end
|
125
|
+
|
126
|
+
def append(s = nil)
|
127
|
+
@telfile << (s || "")
|
128
|
+
end
|
129
|
+
|
130
|
+
def user
|
131
|
+
append "user #{`whoami`.strip.inspect}"
|
132
|
+
end
|
133
|
+
|
134
|
+
def ruby
|
135
|
+
version = `ruby --version`
|
136
|
+
ruby = nil
|
137
|
+
case version
|
138
|
+
when /Ruby Enterprise Edition/ then ruby = "REE"
|
139
|
+
when /1\.8\.7/ then ruby = "1.8.7"
|
140
|
+
when /1\.9\.2/ then ruby = "1.9.2"
|
141
|
+
end
|
142
|
+
append "ruby #{ruby.inspect}" if ruby
|
143
|
+
end
|
144
|
+
|
145
|
+
def apt
|
146
|
+
banner "Calculating apt sources and keys..."
|
147
|
+
list = run_capture_lines("cat /etc/apt/sources.list /etc/apt/sources.list.d/*.list")
|
148
|
+
list = list.grep(/^deb /).sort
|
149
|
+
list.each do |line|
|
150
|
+
if line =~ /^deb http:\/\/(\S+)\s+(\S+)/
|
151
|
+
source, dist = $1, $2
|
152
|
+
file = source.chomp("/").gsub(/[^a-z0-9.-]/, "_")
|
153
|
+
file = "/var/lib/apt/lists/#{file}_dists_#{dist}_Release"
|
154
|
+
next if !File.exists?(file)
|
155
|
+
|
156
|
+
verify = run_capture("gpgv --keyring /etc/apt/trusted.gpg #{file}.gpg #{file} 2>&1")
|
157
|
+
key = verify[/key ID ([A-Z0-9]{8})$/, 1]
|
158
|
+
next if key == "437D05B5" # canonical key
|
159
|
+
append "apt #{line.inspect}, :key => #{key.inspect}"
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
def packages
|
165
|
+
banner "Looking for interesting packages..."
|
166
|
+
@packages = Apt.new.added
|
167
|
+
if !@packages.empty?
|
168
|
+
append
|
169
|
+
append "# Note: You should read this package list very carefully and remove"
|
170
|
+
append "# packages that you don't want on your server."
|
171
|
+
append
|
172
|
+
append "packages %w(#{@packages.join(" ")})"
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
def files
|
177
|
+
banner "Looking for interesting files..."
|
178
|
+
files = []
|
179
|
+
|
180
|
+
# read checksums from dpkg status
|
181
|
+
conf = { }
|
182
|
+
File.readlines("/var/lib/dpkg/status").each do |line|
|
183
|
+
if line =~ /^ (\S+) ([0-9a-f]{32})/
|
184
|
+
conf[$1] = $2
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
# look for changed conf files
|
189
|
+
$stderr.puts " scanning conf files from interesting packages..."
|
190
|
+
@packages.each do |pkg|
|
191
|
+
list = run_capture_lines("dpkg -L #{pkg}")
|
192
|
+
list = list.select { |i| i =~ /^\/etc/ }.sort
|
193
|
+
list = list.select { |i| File.file?(i) }
|
194
|
+
list = list.select { |i| conf[i] && conf[i] != md5sum(i) }
|
195
|
+
files += list
|
196
|
+
end
|
197
|
+
|
198
|
+
# look for new files in NEW_FILES_WITHIN
|
199
|
+
dirs = NEW_FILES_WITHIN.map { |i| "/etc/#{i}" }
|
200
|
+
dirs.sort.each do |dir|
|
201
|
+
$stderr.puts " scanning #{dir} for new files..."
|
202
|
+
list = Dir["#{dir}/*"].sort
|
203
|
+
list = list.select { |i| !MD5SUMS[i] }
|
204
|
+
list = list.select { |i| fails?("dpkg -S #{i}") }
|
205
|
+
files += list
|
206
|
+
end
|
207
|
+
|
208
|
+
# now look for changed files from CHECKSUM_FILES
|
209
|
+
scan = CHECKSUM_FILES.map { |i| "/etc/#{i}" }
|
210
|
+
scan = scan.select { |i| File.file?(i) }
|
211
|
+
scan.each do |i|
|
212
|
+
new_sum = md5sum(i)
|
213
|
+
if old_sum = MD5SUMS[i]
|
214
|
+
match = old_sum.any? do |sum|
|
215
|
+
sum = md5sum(sum) if sum =~ /^\//
|
216
|
+
new_sum == sum
|
217
|
+
end
|
218
|
+
files << i if !match
|
219
|
+
elsif old_sum = conf[i]
|
220
|
+
files << i if new_sum != old_sum
|
221
|
+
end
|
222
|
+
end
|
223
|
+
files = files.sort
|
224
|
+
|
225
|
+
if !files.empty?
|
226
|
+
append
|
227
|
+
append "#" * 72
|
228
|
+
append "# Also, I think these should be included in files/"
|
229
|
+
append "#" * 72
|
230
|
+
append
|
231
|
+
files.each { |i| append "# #{i}" }
|
232
|
+
append
|
233
|
+
append "# You can do that with this magical command:"
|
234
|
+
append "#"
|
235
|
+
append "# mkdir files && cd files && tar cf - #{files.join(" ")} | tar xf -"
|
236
|
+
end
|
237
|
+
end
|
238
|
+
|
239
|
+
class Apt
|
240
|
+
include Util
|
241
|
+
|
242
|
+
BLACKLIST = /^(linux-|grub-|cloud-init)/
|
243
|
+
|
244
|
+
Package = Struct.new(:name, :status, :deps, :base, :parents)
|
245
|
+
|
246
|
+
def initialize
|
247
|
+
@packages = nil
|
248
|
+
@map = nil
|
249
|
+
end
|
250
|
+
|
251
|
+
def packages
|
252
|
+
if !@packages
|
253
|
+
# run dpkg
|
254
|
+
lines = run_capture_lines("dpkg-query '-f=${Package}\t${Status}\t${Pre-Depends},${Depends},${Recommends}\t${Essential}\t${Priority}\n' -W")
|
255
|
+
@packages = lines.map do |line|
|
256
|
+
name, status, deps, essential, priority = line.split("\t")
|
257
|
+
deps = deps.gsub(/\([^)]+\)/, "")
|
258
|
+
deps = deps.split(/[,|]/)
|
259
|
+
deps = deps.map(&:strip).select { |i| !i.empty? }.sort
|
260
|
+
base = false
|
261
|
+
base = true if essential == "yes"
|
262
|
+
base = true if priority =~ /^(important|required|standard)$/
|
263
|
+
Package.new(name, status, deps, base, [])
|
264
|
+
end
|
265
|
+
|
266
|
+
# calculate ancestors
|
267
|
+
@packages.each do |pkg|
|
268
|
+
pkg.deps.each do |i|
|
269
|
+
if d = self[i]
|
270
|
+
d.parents << pkg.name
|
271
|
+
end
|
272
|
+
end
|
273
|
+
end
|
274
|
+
@packages.each do |pkg|
|
275
|
+
pkg.parents = pkg.parents.sort.uniq
|
276
|
+
end
|
277
|
+
end
|
278
|
+
|
279
|
+
@packages
|
280
|
+
end
|
281
|
+
|
282
|
+
def [](name)
|
283
|
+
if !@map
|
284
|
+
@map = { }
|
285
|
+
packages.each { |i| @map[i.name] = i }
|
286
|
+
end
|
287
|
+
@map[name]
|
288
|
+
end
|
289
|
+
|
290
|
+
def base_packages
|
291
|
+
packages.select { |i| i.base }.map(&:name)
|
292
|
+
end
|
293
|
+
|
294
|
+
def ignored_packages
|
295
|
+
list = packages.select { |i| i.base }.map(&:name)
|
296
|
+
list += %w(grub-pc installation-report language-pack-en language-pack-gnome-en linux-generic-pae linux-server os-prober ubuntu-desktop ubuntu-minimal ubuntu-standard wireless-crda)
|
297
|
+
dependencies(list)
|
298
|
+
end
|
299
|
+
|
300
|
+
def dependencies(list)
|
301
|
+
check = list
|
302
|
+
while !check.empty?
|
303
|
+
check = check.map do |i|
|
304
|
+
if pkg = self[i]
|
305
|
+
pkg.deps
|
306
|
+
end
|
307
|
+
end
|
308
|
+
check = check.compact.flatten.uniq.sort
|
309
|
+
check -= list
|
310
|
+
list += check
|
311
|
+
end
|
312
|
+
list.sort
|
313
|
+
end
|
314
|
+
|
315
|
+
def added
|
316
|
+
# calculate raw list
|
317
|
+
ignored = Set.new(ignored_packages)
|
318
|
+
list = packages.select do |i|
|
319
|
+
i.status == "install ok installed" && !ignored.include?(i.name)
|
320
|
+
end
|
321
|
+
list = list.map(&:name)
|
322
|
+
|
323
|
+
# now calculate parents
|
324
|
+
roots = []
|
325
|
+
check = list
|
326
|
+
while !check.empty?
|
327
|
+
check = check.map do |i|
|
328
|
+
if pkg = self[i]
|
329
|
+
if !pkg.parents.empty?
|
330
|
+
pkg.parents
|
331
|
+
else
|
332
|
+
roots << pkg.name
|
333
|
+
nil
|
334
|
+
end
|
335
|
+
end
|
336
|
+
end
|
337
|
+
check = check.compact.flatten.uniq.sort
|
338
|
+
check -= list
|
339
|
+
list += check
|
340
|
+
end
|
341
|
+
|
342
|
+
# blacklist
|
343
|
+
roots = roots.reject { |i| i =~ BLACKLIST }
|
344
|
+
|
345
|
+
roots.sort
|
346
|
+
end
|
347
|
+
end
|
348
|
+
end
|
349
|
+
end
|
data/lib/teleport/install.rb
CHANGED
@@ -51,14 +51,14 @@ module Teleport
|
|
51
51
|
# do we have a server object?
|
52
52
|
@server = @config.server(@host)
|
53
53
|
if !@server && !@config.servers.empty?
|
54
|
-
fatal "Hm. I couldn't find server #{@host.inspect} in
|
54
|
+
fatal "Hm. I couldn't find server #{@host.inspect} in Telfile."
|
55
55
|
end
|
56
56
|
|
57
57
|
@role = nil
|
58
58
|
if @server && (role_name = @server.options[:role])
|
59
59
|
@role = @config.role(role_name)
|
60
60
|
if !@role
|
61
|
-
fatal "Hm. I couldn't find role #{role_name.inspect} in
|
61
|
+
fatal "Hm. I couldn't find role #{role_name.inspect} in Telfile."
|
62
62
|
end
|
63
63
|
end
|
64
64
|
end
|
@@ -88,7 +88,13 @@ module Teleport
|
|
88
88
|
end
|
89
89
|
|
90
90
|
def _hostname
|
91
|
-
banner "Hostname..."
|
91
|
+
banner "Hostname..."
|
92
|
+
|
93
|
+
# ipv4?
|
94
|
+
return if @host =~ /^\d+(\.\d+){3}$/
|
95
|
+
# ipv6?
|
96
|
+
return if @host =~ /:/
|
97
|
+
|
92
98
|
old_hostname = `hostname`.strip
|
93
99
|
return if old_hostname == @host
|
94
100
|
|
data/lib/teleport/main.rb
CHANGED
@@ -1,5 +1,4 @@
|
|
1
|
-
require "
|
2
|
-
require "getoptlong"
|
1
|
+
require "optparse"
|
3
2
|
|
4
3
|
module Teleport
|
5
4
|
# The main class for the teleport command line.
|
@@ -9,53 +8,83 @@ module Teleport
|
|
9
8
|
|
10
9
|
TAR = "#{DIR}.tgz"
|
11
10
|
|
12
|
-
attr_accessor :host, :options
|
13
|
-
|
14
11
|
def initialize(cmd = :teleport)
|
15
|
-
|
16
|
-
["--help", "-h", GetoptLong::NO_ARGUMENT]
|
17
|
-
)
|
18
|
-
opts.each do |opt, arg|
|
19
|
-
case opt
|
20
|
-
when "--help"
|
21
|
-
usage(0)
|
22
|
-
end
|
23
|
-
end
|
24
|
-
|
25
|
-
$stderr = $stdout
|
12
|
+
cli(cmd)
|
26
13
|
|
27
|
-
case cmd
|
14
|
+
case @options[:cmd]
|
28
15
|
when :teleport
|
29
|
-
|
16
|
+
$stderr = $stdout
|
17
|
+
teleport
|
30
18
|
when :install
|
19
|
+
$stderr = $stdout
|
31
20
|
install
|
21
|
+
when :infer
|
22
|
+
infer
|
32
23
|
end
|
33
24
|
end
|
25
|
+
|
26
|
+
# Parse ARGV.
|
27
|
+
def cli(cmd)
|
28
|
+
@options = { }
|
29
|
+
@options[:cmd] = cmd
|
30
|
+
@options[:file] = "Telfile"
|
31
|
+
|
32
|
+
opt = OptionParser.new do |o|
|
33
|
+
o.banner = "Usage: teleport <hostname>"
|
34
|
+
o.on("-f", "--file FILE", "use this file instead of Telfile") do |f|
|
35
|
+
@options[:file] = f
|
36
|
+
end
|
37
|
+
o.on("-i", "--infer", "infer a new Telfile from YOUR machine") do |f|
|
38
|
+
@options[:cmd] = :infer
|
39
|
+
end
|
40
|
+
o.on_tail("-h", "--help", "print this help text") do
|
41
|
+
puts opt
|
42
|
+
exit(0)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
begin
|
46
|
+
opt.parse!
|
47
|
+
rescue OptionParser::InvalidOption, OptionParser::MissingArgument
|
48
|
+
puts $!
|
49
|
+
puts opt
|
50
|
+
exit(1)
|
51
|
+
end
|
34
52
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
53
|
+
if @options[:cmd] == :teleport
|
54
|
+
# print this error message early, to give the user a hint
|
55
|
+
# instead of complaining about command line arguments
|
56
|
+
if ARGV.length != 1
|
57
|
+
puts opt
|
58
|
+
exit(1)
|
59
|
+
end
|
60
|
+
@options[:host] = ARGV.shift
|
61
|
+
end
|
39
62
|
end
|
40
63
|
|
64
|
+
# Read Telfile
|
41
65
|
def read_config
|
42
|
-
if !File.exists?(
|
43
|
-
fatal("Sadly, I can't find #{
|
66
|
+
if !File.exists?(@options[:file])
|
67
|
+
fatal("Sadly, I can't find #{@options[:file]} here. Please create one.")
|
44
68
|
end
|
45
|
-
@config = Config.new
|
69
|
+
@config = Config.new(@options[:file])
|
46
70
|
end
|
47
71
|
|
48
|
-
|
72
|
+
# Assemble the the tgz before we teleport to the host
|
73
|
+
def assemble_tgz
|
49
74
|
banner "Assembling #{TAR}..."
|
50
75
|
rm_and_mkdir(DIR)
|
51
76
|
|
52
77
|
# gem
|
53
78
|
run("cp", ["-r", "#{File.dirname(__FILE__)}/../../lib", GEM])
|
79
|
+
# Telfile, if necessary
|
80
|
+
if @options[:file] != "Telfile"
|
81
|
+
run("cp", [@options[:file], "Telfile"])
|
82
|
+
end
|
54
83
|
# data
|
55
84
|
run("cp", ["-r", ".", DATA])
|
56
85
|
# config.sh
|
57
86
|
File.open("#{DIR}/config", "w") do |f|
|
58
|
-
f.puts("CONFIG_HOST='#{host}'")
|
87
|
+
f.puts("CONFIG_HOST='#{@options[:host]}'")
|
59
88
|
f.puts("CONFIG_RUBY='#{@config.ruby}'")
|
60
89
|
f.puts("CONFIG_RUBYGEMS='#{RUBYGEMS}'")
|
61
90
|
end
|
@@ -63,8 +92,6 @@ module Teleport
|
|
63
92
|
ssh_key = "#{ENV["HOME"]}/.ssh/#{PUBKEY}"
|
64
93
|
if File.exists?(ssh_key)
|
65
94
|
run("cp", [ssh_key, DIR])
|
66
|
-
else
|
67
|
-
puts "Could not find #{ssh_key} - skipping."
|
68
95
|
end
|
69
96
|
|
70
97
|
Dir.chdir(File.dirname(DIR)) do
|
@@ -72,10 +99,16 @@ module Teleport
|
|
72
99
|
end
|
73
100
|
end
|
74
101
|
|
75
|
-
|
102
|
+
# Copy the tgz to the host, then run there.
|
103
|
+
def ssh_tgz
|
76
104
|
begin
|
77
|
-
banner "scp #{TAR} to #{host}:#{TAR}..."
|
78
|
-
|
105
|
+
banner "scp #{TAR} to #{@options[:host]}:#{TAR}..."
|
106
|
+
|
107
|
+
args = []
|
108
|
+
args += @config.ssh_options if @config.ssh_options
|
109
|
+
args << TAR
|
110
|
+
args << "#{@options[:host]}:#{TAR}"
|
111
|
+
run("scp", args)
|
79
112
|
|
80
113
|
cmd = [
|
81
114
|
"cd /tmp",
|
@@ -84,27 +117,37 @@ module Teleport
|
|
84
117
|
"sudo tar xfpz #{TAR}",
|
85
118
|
"sudo #{DIR}/gem/teleport/run.sh"
|
86
119
|
]
|
87
|
-
banner "ssh to #{host} and run..."
|
88
|
-
|
120
|
+
banner "ssh to #{@options[:host]} and run..."
|
121
|
+
|
122
|
+
args = []
|
123
|
+
args += @config.ssh_options if @config.ssh_options
|
124
|
+
args << @options[:host]
|
125
|
+
args << cmd.join(" && ")
|
126
|
+
run("ssh", args)
|
89
127
|
rescue RunError
|
90
128
|
fatal("Failed!")
|
91
129
|
end
|
92
130
|
banner "Success!"
|
93
131
|
end
|
94
132
|
|
95
|
-
|
133
|
+
# Teleport to the host.
|
134
|
+
def teleport
|
96
135
|
read_config
|
97
|
-
|
98
|
-
|
99
|
-
ssh_tgz(host)
|
136
|
+
assemble_tgz
|
137
|
+
ssh_tgz
|
100
138
|
end
|
101
139
|
|
140
|
+
# We're running on the host - install!
|
102
141
|
def install
|
103
142
|
Dir.chdir(DATA) do
|
104
143
|
read_config
|
105
144
|
end
|
106
145
|
Install.new(@config)
|
107
146
|
end
|
108
|
-
|
147
|
+
|
148
|
+
# try to infer a new Telfile based on the current machine
|
149
|
+
def infer
|
150
|
+
Infer.new
|
151
|
+
end
|
109
152
|
end
|
110
153
|
end
|
data/lib/teleport/mirror.rb
CHANGED
@@ -31,8 +31,12 @@ module Teleport
|
|
31
31
|
copy_metadata(path, tmp)
|
32
32
|
path = tmp
|
33
33
|
end
|
34
|
-
|
35
|
-
|
34
|
+
|
35
|
+
if !File.symlink?(path)
|
36
|
+
cp_if_necessary(path, dst, user_for_file(dst), mode_for_file(dst))
|
37
|
+
else
|
38
|
+
ln_if_necessary(File.readlink(path), dst)
|
39
|
+
end
|
36
40
|
end
|
37
41
|
|
38
42
|
# Install directory from the teleport data directory into the
|
data/lib/teleport/run.sh
CHANGED
@@ -90,15 +90,15 @@ function install_ruby_ree() {
|
|
90
90
|
#
|
91
91
|
|
92
92
|
# are we on Ubuntu?
|
93
|
-
if !
|
93
|
+
if ! grep -q Ubuntu /etc/lsb-release ; then
|
94
94
|
fatal "Teleport only works with Ubuntu"
|
95
95
|
fi
|
96
96
|
|
97
97
|
# which version?
|
98
98
|
. /etc/lsb-release
|
99
|
-
case $
|
100
|
-
10.*
|
101
|
-
11.04) ;; # nop
|
99
|
+
case $DISTRIB_RELEASE in
|
100
|
+
10.* ) ;; # nop
|
101
|
+
11.04 ) ;; # nop
|
102
102
|
*)
|
103
103
|
banner "warning - Ubuntu $DISTRIB_RELEASE hasn't been tested with Teleport yet"
|
104
104
|
esac
|
data/lib/teleport/util.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require "cgi"
|
2
|
+
require "digest/md5"
|
2
3
|
require "etc"
|
3
4
|
require "fileutils"
|
4
5
|
|
@@ -56,7 +57,7 @@ module Teleport
|
|
56
57
|
end
|
57
58
|
|
58
59
|
# Run a command, raise an error upon failure. The output is
|
59
|
-
#
|
60
|
+
# captured as a string and returned.
|
60
61
|
def run_capture(command, *args)
|
61
62
|
if !args.empty?
|
62
63
|
args = args.flatten.map { |i| shell_escape(i) }.join(" ")
|
@@ -72,6 +73,13 @@ module Teleport
|
|
72
73
|
result
|
73
74
|
end
|
74
75
|
|
76
|
+
# Run a command and split the result into lines, raise an error
|
77
|
+
# upon failure. The output is captured as an array of strings and
|
78
|
+
# returned.
|
79
|
+
def run_capture_lines(command, *args)
|
80
|
+
run_capture(command, args).split("\n")
|
81
|
+
end
|
82
|
+
|
75
83
|
# Run a command but don't send any output to $stdout/$stderr.
|
76
84
|
def run_quietly(command, *args)
|
77
85
|
if !args.empty?
|
@@ -304,7 +312,18 @@ module Teleport
|
|
304
312
|
end
|
305
313
|
false
|
306
314
|
end
|
307
|
-
|
315
|
+
|
316
|
+
# Calculate the md5 checksum for a file
|
317
|
+
def md5sum(path)
|
318
|
+
digest, buf = Digest::MD5.new, ""
|
319
|
+
File.open(path) do |f|
|
320
|
+
while f.read(4096, buf)
|
321
|
+
digest.update(buf)
|
322
|
+
end
|
323
|
+
end
|
324
|
+
digest.hexdigest
|
325
|
+
end
|
326
|
+
|
308
327
|
private
|
309
328
|
|
310
329
|
# Returns true if verbosity is turned on.
|
data/lib/teleport/version.rb
CHANGED
@@ -0,0 +1,39 @@
|
|
1
|
+
require "erb"
|
2
|
+
require "spec_helper"
|
3
|
+
|
4
|
+
describe "a new ec2 instance" do
|
5
|
+
ec2
|
6
|
+
|
7
|
+
telfile do
|
8
|
+
<<EOF
|
9
|
+
user "gub"
|
10
|
+
ruby "1.8.7"
|
11
|
+
ssh_options ["-o", "User=ubuntu", "-o", "StrictHostKeyChecking=no", "-o", "IdentityFile=#{ENV["TELEPORT_SSH_KEY"]}"]
|
12
|
+
|
13
|
+
role :master, :packages => %w(nginx)
|
14
|
+
server "#{$ec2_ip_address}", :role => :master, :packages => %w(strace)
|
15
|
+
packages %w(atop)
|
16
|
+
|
17
|
+
before_install do
|
18
|
+
puts "BEFORE_INSTALL"
|
19
|
+
end
|
20
|
+
|
21
|
+
after_install do
|
22
|
+
puts "AFTER_INSTALL"
|
23
|
+
run "touch /tmp/gub.txt"
|
24
|
+
end
|
25
|
+
EOF
|
26
|
+
end
|
27
|
+
|
28
|
+
it "installs properly" do
|
29
|
+
ARGV.clear
|
30
|
+
ARGV << $ec2_ip_address
|
31
|
+
Teleport::Main.new
|
32
|
+
end
|
33
|
+
|
34
|
+
it "installs again" do
|
35
|
+
ARGV.clear
|
36
|
+
ARGV << $ec2_ip_address
|
37
|
+
Teleport::Main.new
|
38
|
+
end
|
39
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
SUPPORT = "#{File.dirname(__FILE__)}/support"
|
2
|
+
|
3
|
+
$LOAD_PATH << "#{File.dirname(__FILE__)}/../lib"
|
4
|
+
$LOAD_PATH << File.dirname(__FILE__)
|
5
|
+
$LOAD_PATH << SUPPORT
|
6
|
+
|
7
|
+
require "awesome_print"
|
8
|
+
require "rspec"
|
9
|
+
require "teleport"
|
10
|
+
|
11
|
+
Dir["#{SUPPORT}/*.rb"].each { |i| require File.basename(i) }
|
12
|
+
|
13
|
+
TELDIRS = "#{File.dirname(__FILE__)}/teldirs"
|
14
|
+
|
15
|
+
RSpec.configure do |config|
|
16
|
+
config.extend Support::Telfile
|
17
|
+
config.extend Support::Ec2
|
18
|
+
|
19
|
+
ec2_configured = Support::Ec2.configured?
|
20
|
+
warn(Support::Ec2.message) if !ec2_configured
|
21
|
+
|
22
|
+
config.filter_run_excluding(:config => lambda { |value|
|
23
|
+
return true if value == :ec2 && !ec2_configured
|
24
|
+
})
|
25
|
+
end
|
data/spec/support/ec2.rb
ADDED
@@ -0,0 +1,115 @@
|
|
1
|
+
require "AWS"
|
2
|
+
|
3
|
+
# spin up a fresh ec2 instance
|
4
|
+
module Support
|
5
|
+
module Ec2
|
6
|
+
AMI_10_04 = "fbbf7892"
|
7
|
+
AMI_10_10 = "08f40561"
|
8
|
+
AMI_11_04 = "68ad5201"
|
9
|
+
KEYPAIR = "teleport"
|
10
|
+
GROUP = "teleport"
|
11
|
+
|
12
|
+
AMI = "ami-#{AMI_10_04}"
|
13
|
+
|
14
|
+
def self.configured?
|
15
|
+
ENV["TELEPORT_ACCESS_KEY_ID"] && ENV["TELEPORT_SECRET_ACCESS_KEY"] && ENV["TELEPORT_SSH_KEY"]
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.message
|
19
|
+
<<EOF
|
20
|
+
------------------------------------------------------------------------
|
21
|
+
If you want to test against EC2, do the following:
|
22
|
+
|
23
|
+
1. Create a "teleport" keypair on EC2.
|
24
|
+
2. Set the TELEPORT_ACCESS_KEY_ID, TELEPORT_SECRET_ACCESS_KEY and
|
25
|
+
TELEPORT_SSH_KEY environment variables.
|
26
|
+
|
27
|
+
End-to-end tests that rely on EC2 will be skipped in the meantime.
|
28
|
+
------------------------------------------------------------------------
|
29
|
+
EOF
|
30
|
+
end
|
31
|
+
|
32
|
+
#
|
33
|
+
# specs call this
|
34
|
+
#
|
35
|
+
|
36
|
+
def ec2
|
37
|
+
controller = nil
|
38
|
+
before(:all) do
|
39
|
+
if ENV["TELEPORT_IP"]
|
40
|
+
$ec2_ip_address = ENV["TELEPORT_IP"]
|
41
|
+
else
|
42
|
+
controller = Controller.new
|
43
|
+
controller.stop
|
44
|
+
$ec2_ip_address = controller.start
|
45
|
+
end
|
46
|
+
end
|
47
|
+
after(:all) do
|
48
|
+
controller.stop if controller
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
#
|
53
|
+
# this controller class does all the work
|
54
|
+
#
|
55
|
+
|
56
|
+
class Controller
|
57
|
+
def initialize
|
58
|
+
raise "not configured" if !Support::Ec2::configured?
|
59
|
+
@ec2 = AWS::EC2::Base.new(:access_key_id => ENV["TELEPORT_ACCESS_KEY_ID"], :secret_access_key => ENV["TELEPORT_SECRET_ACCESS_KEY"])
|
60
|
+
end
|
61
|
+
|
62
|
+
def start
|
63
|
+
puts "Running new ec2 instance..."
|
64
|
+
# setup security group and allow ssh
|
65
|
+
begin
|
66
|
+
@ec2.create_security_group(:group_name => GROUP, :group_description => GROUP)
|
67
|
+
rescue AWS::InvalidGroupDuplicate
|
68
|
+
# ignore
|
69
|
+
end
|
70
|
+
@ec2.authorize_security_group_ingress(:group_name => GROUP, :ip_protocol => "tcp", :from_port => 22, :to_port => 22)
|
71
|
+
|
72
|
+
# create the instance
|
73
|
+
@ec2.run_instances(:image_id => AMI, :instance_type => "m1.large", :key_name => KEYPAIR, :security_group => GROUP)
|
74
|
+
|
75
|
+
# wait for the new instance to start
|
76
|
+
puts "Waiting for ec2 instance to start..."
|
77
|
+
while true
|
78
|
+
sleep 3
|
79
|
+
instance = describe_instances.first
|
80
|
+
status = instance["instanceState"]["name"]
|
81
|
+
puts " #{instance["instanceId"]}: #{status}"
|
82
|
+
break if status == "running"
|
83
|
+
end
|
84
|
+
|
85
|
+
# return the ip address
|
86
|
+
ip = instance["ipAddress"]
|
87
|
+
puts " #{instance["instanceId"]}: #{ip}"
|
88
|
+
puts " sleeping to give ssh a chance to start..."
|
89
|
+
sleep 10
|
90
|
+
ip
|
91
|
+
end
|
92
|
+
|
93
|
+
def stop
|
94
|
+
puts "Terminating existing ec2 instances..."
|
95
|
+
ids = describe_instances.map { |i| i["instanceId"] }
|
96
|
+
if !ids.empty?
|
97
|
+
puts " terminate: #{ids.join(" ")}"
|
98
|
+
@ec2.terminate_instances(:instance_id => ids)
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
def describe_instances
|
103
|
+
list = []
|
104
|
+
hash = @ec2.describe_instances
|
105
|
+
if hash = hash["reservationSet"]
|
106
|
+
list = hash["item"].map { |i| i["instancesSet"]["item"] }.flatten
|
107
|
+
end
|
108
|
+
# cull stuff we don't care about
|
109
|
+
list = list.select { |i| i["keyName"] == KEYPAIR }
|
110
|
+
list = list.select { |i| i["instanceState"]["name"] !~ /terminated|shutting-down/ }
|
111
|
+
list
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# http://stackoverflow.com/questions/5118745/is-systemexit-a-special-kind-of-exception
|
2
|
+
|
3
|
+
module ExitCodeMatchers
|
4
|
+
RSpec::Matchers.define :exit_with_code do |code|
|
5
|
+
actual = nil
|
6
|
+
match do |block|
|
7
|
+
begin
|
8
|
+
block.call
|
9
|
+
rescue SystemExit => e
|
10
|
+
actual = e.status
|
11
|
+
end
|
12
|
+
actual && actual == code
|
13
|
+
end
|
14
|
+
failure_message_for_should do |block|
|
15
|
+
"expected block to call exit(#{code}) but exit" +
|
16
|
+
(actual.nil? ? " not called" : "(#{actual}) was called")
|
17
|
+
end
|
18
|
+
failure_message_for_should_not do |block|
|
19
|
+
"expected block not to call exit(#{code})"
|
20
|
+
end
|
21
|
+
description do
|
22
|
+
"expect block to call exit(#{code})"
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
RSpec.configure do |config|
|
28
|
+
config.include(ExitCodeMatchers)
|
29
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# run inside a specific dir
|
2
|
+
module Support
|
3
|
+
module Telfile
|
4
|
+
TMP = "/tmp/teleport_spec"
|
5
|
+
|
6
|
+
def telfile(contents = nil, &block)
|
7
|
+
pwd = nil
|
8
|
+
before(:all) do
|
9
|
+
pwd = Dir.pwd
|
10
|
+
`rm -rf #{TMP} && mkdir -p #{TMP}`
|
11
|
+
Dir.chdir(TMP)
|
12
|
+
File.open("Telfile", "w") do |f|
|
13
|
+
if block
|
14
|
+
contents = block.call
|
15
|
+
end
|
16
|
+
f.puts(contents)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
after(:all) do
|
21
|
+
Dir.chdir(pwd)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Teleport::Config do
|
4
|
+
context "with a blank Telfile" do
|
5
|
+
telfile("")
|
6
|
+
|
7
|
+
let(:config) do
|
8
|
+
Teleport::Config.new
|
9
|
+
end
|
10
|
+
it "defaults to the current username" do
|
11
|
+
config.user.should == `whoami`.strip
|
12
|
+
end
|
13
|
+
it "defaults to the first vm in RUBIES" do
|
14
|
+
config.ruby.should == Teleport::Config::RUBIES.first
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
context "with a simple Telfile" do
|
19
|
+
telfile do
|
20
|
+
<<EOF
|
21
|
+
user "somebody"
|
22
|
+
ruby "1.8.7"
|
23
|
+
|
24
|
+
role :master, :packages => %w(nginx)
|
25
|
+
role :slave, :packages => %w(memcached)
|
26
|
+
server "one", :role => :master, :packages => %w(strace)
|
27
|
+
server "two", :role => :slave, :packages => %w(telnet)
|
28
|
+
packages %w(atop)
|
29
|
+
apt "blah blah blah", :key => "123"
|
30
|
+
|
31
|
+
before_install do
|
32
|
+
puts "before_install running"
|
33
|
+
end
|
34
|
+
|
35
|
+
after_install do
|
36
|
+
puts "after_install running"
|
37
|
+
end
|
38
|
+
EOF
|
39
|
+
end
|
40
|
+
|
41
|
+
let(:config) do
|
42
|
+
Teleport::Config.new
|
43
|
+
end
|
44
|
+
it "has the master role" do
|
45
|
+
config.role(:master).name.should == :master
|
46
|
+
config.role(:master).packages.should == %w(nginx)
|
47
|
+
end
|
48
|
+
it "has server one" do
|
49
|
+
config.server("one").name.should == "one"
|
50
|
+
config.server("one").packages.should == %w(strace)
|
51
|
+
end
|
52
|
+
it "has default packages" do
|
53
|
+
config.packages.should == %w(atop)
|
54
|
+
end
|
55
|
+
it "has callbacks" do
|
56
|
+
config.callbacks[:before_install].should_not == nil
|
57
|
+
config.callbacks[:after_install].should_not == nil
|
58
|
+
end
|
59
|
+
it "has an apt line" do
|
60
|
+
config.apt.first.line.should == "blah blah blah"
|
61
|
+
config.apt.first.options[:key].should == "123"
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
data/teleport.gemspec
CHANGED
@@ -1,4 +1,5 @@
|
|
1
|
-
|
1
|
+
$LOAD_PATH << File.expand_path("../lib", __FILE__)
|
2
|
+
|
2
3
|
require "teleport/version"
|
3
4
|
|
4
5
|
Gem::Specification.new do |s|
|
@@ -8,11 +9,17 @@ Gem::Specification.new do |s|
|
|
8
9
|
s.authors = ["Adam Doppelt"]
|
9
10
|
s.email = ["amd@gurge.com"]
|
10
11
|
s.homepage = "http://github.com/rglabs/teleport"
|
11
|
-
s.summary =
|
12
|
-
s.description =
|
12
|
+
s.summary = "Teleport - opinionated Ubuntu server setup with Ruby."
|
13
|
+
s.description = "Easy Ubuntu server setup via teleportation."
|
13
14
|
|
14
15
|
s.rubyforge_project = "teleport"
|
15
16
|
|
17
|
+
s.add_development_dependency("amazon-ec2")
|
18
|
+
s.add_development_dependency("awesome_print")
|
19
|
+
s.add_development_dependency("rake")
|
20
|
+
s.add_development_dependency("rdoc", ["~> 3.9"])
|
21
|
+
s.add_development_dependency("rspec", ["~> 2.6"])
|
22
|
+
|
16
23
|
s.files = `git ls-files`.split("\n")
|
17
24
|
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
18
25
|
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: teleport
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 21
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 1
|
8
8
|
- 0
|
9
|
-
-
|
10
|
-
version: 1.0.
|
9
|
+
- 1
|
10
|
+
version: 1.0.1
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Adam Doppelt
|
@@ -15,10 +15,81 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2011-08-
|
18
|
+
date: 2011-08-10 00:00:00 -07:00
|
19
19
|
default_executable:
|
20
|
-
dependencies:
|
21
|
-
|
20
|
+
dependencies:
|
21
|
+
- !ruby/object:Gem::Dependency
|
22
|
+
name: amazon-ec2
|
23
|
+
type: :development
|
24
|
+
version_requirements: &id001 !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ">="
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
hash: 3
|
30
|
+
segments:
|
31
|
+
- 0
|
32
|
+
version: "0"
|
33
|
+
requirement: *id001
|
34
|
+
prerelease: false
|
35
|
+
- !ruby/object:Gem::Dependency
|
36
|
+
name: awesome_print
|
37
|
+
type: :development
|
38
|
+
version_requirements: &id002 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ">="
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
hash: 3
|
44
|
+
segments:
|
45
|
+
- 0
|
46
|
+
version: "0"
|
47
|
+
requirement: *id002
|
48
|
+
prerelease: false
|
49
|
+
- !ruby/object:Gem::Dependency
|
50
|
+
name: rake
|
51
|
+
type: :development
|
52
|
+
version_requirements: &id003 !ruby/object:Gem::Requirement
|
53
|
+
none: false
|
54
|
+
requirements:
|
55
|
+
- - ">="
|
56
|
+
- !ruby/object:Gem::Version
|
57
|
+
hash: 3
|
58
|
+
segments:
|
59
|
+
- 0
|
60
|
+
version: "0"
|
61
|
+
requirement: *id003
|
62
|
+
prerelease: false
|
63
|
+
- !ruby/object:Gem::Dependency
|
64
|
+
name: rdoc
|
65
|
+
type: :development
|
66
|
+
version_requirements: &id004 !ruby/object:Gem::Requirement
|
67
|
+
none: false
|
68
|
+
requirements:
|
69
|
+
- - ~>
|
70
|
+
- !ruby/object:Gem::Version
|
71
|
+
hash: 21
|
72
|
+
segments:
|
73
|
+
- 3
|
74
|
+
- 9
|
75
|
+
version: "3.9"
|
76
|
+
requirement: *id004
|
77
|
+
prerelease: false
|
78
|
+
- !ruby/object:Gem::Dependency
|
79
|
+
name: rspec
|
80
|
+
type: :development
|
81
|
+
version_requirements: &id005 !ruby/object:Gem::Requirement
|
82
|
+
none: false
|
83
|
+
requirements:
|
84
|
+
- - ~>
|
85
|
+
- !ruby/object:Gem::Version
|
86
|
+
hash: 15
|
87
|
+
segments:
|
88
|
+
- 2
|
89
|
+
- 6
|
90
|
+
version: "2.6"
|
91
|
+
requirement: *id005
|
92
|
+
prerelease: false
|
22
93
|
description: Easy Ubuntu server setup via teleportation.
|
23
94
|
email:
|
24
95
|
- amd@gurge.com
|
@@ -38,12 +109,19 @@ files:
|
|
38
109
|
- lib/teleport.rb
|
39
110
|
- lib/teleport/config.rb
|
40
111
|
- lib/teleport/constants.rb
|
112
|
+
- lib/teleport/infer.rb
|
41
113
|
- lib/teleport/install.rb
|
42
114
|
- lib/teleport/main.rb
|
43
115
|
- lib/teleport/mirror.rb
|
44
116
|
- lib/teleport/run.sh
|
45
117
|
- lib/teleport/util.rb
|
46
118
|
- lib/teleport/version.rb
|
119
|
+
- spec/end_to_end_spec.rb
|
120
|
+
- spec/spec_helper.rb
|
121
|
+
- spec/support/ec2.rb
|
122
|
+
- spec/support/exit_code.rb
|
123
|
+
- spec/support/telfile.rb
|
124
|
+
- spec/unit/teleport/config_spec.rb
|
47
125
|
- teleport.gemspec
|
48
126
|
has_rdoc: true
|
49
127
|
homepage: http://github.com/rglabs/teleport
|