fabric 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.
- data/.document +5 -0
- data/.gitignore +26 -0
- data/Gemfile +18 -0
- data/LICENSE +20 -0
- data/README.rdoc +91 -0
- data/Rakefile +8 -0
- data/VERSION +1 -0
- data/bin/fab +7 -0
- data/fabric.gemspec +97 -0
- data/features/support/env.rb +6 -0
- data/lib/extensions/narrator.rb +19 -0
- data/lib/fabric-test.rb +10 -0
- data/lib/fabric.rb +57 -0
- data/lib/fabric/account.rb +36 -0
- data/lib/fabric/capture.rb +21 -0
- data/lib/fabric/grant.rb +8 -0
- data/lib/fabric/group.rb +8 -0
- data/lib/fabric/key.rb +8 -0
- data/lib/fabric/map.rb +102 -0
- data/lib/fabric/role.rb +24 -0
- data/lib/fabric/server.rb +148 -0
- data/lib/fabric/user.rb +33 -0
- data/lib/initialisers/data_mapper.rb +4 -0
- data/spec/classes/account_spec.rb +92 -0
- data/spec/classes/capture_spec.rb +28 -0
- data/spec/classes/map_spec.rb +212 -0
- data/spec/classes/role_spec.rb +4 -0
- data/spec/classes/server_spec.rb +104 -0
- data/spec/classes/user_spec.rb +34 -0
- data/spec/spec.opts +5 -0
- data/spec/spec_helper.rb +27 -0
- data/tasks/features.rake +15 -0
- data/tasks/jeweler.rake +14 -0
- data/tasks/rdoc.rake +9 -0
- data/tasks/spec.rake +15 -0
- data/tasks/test.rake +6 -0
- data/test.yml.example +5 -0
- metadata +116 -0
data/.document
ADDED
data/.gitignore
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
## MAC OS
|
2
|
+
.DS_Store
|
3
|
+
|
4
|
+
## TEXTMATE
|
5
|
+
*.tmproj
|
6
|
+
tmtags
|
7
|
+
|
8
|
+
## EMACS
|
9
|
+
*~
|
10
|
+
\#*
|
11
|
+
.\#*
|
12
|
+
|
13
|
+
## VIM
|
14
|
+
*.swp
|
15
|
+
|
16
|
+
## PROJECT::GENERAL
|
17
|
+
coverage
|
18
|
+
rdoc
|
19
|
+
pkg
|
20
|
+
|
21
|
+
## PROJECT::SPECIFIC
|
22
|
+
vendor/gems/ruby/
|
23
|
+
vendor/gems/bin
|
24
|
+
vendor/gems/environment.rb
|
25
|
+
!vendor/gems/ruby/1.8/cache
|
26
|
+
test.yml
|
data/Gemfile
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
bin_path "vendor/gems/bin"
|
2
|
+
|
3
|
+
gem 'net-ssh', '2.0.15'
|
4
|
+
gem 'dm-core', '0.10.2'
|
5
|
+
gem 'data_objects', '0.10.0'
|
6
|
+
gem 'do_sqlite3', '0.10.0'
|
7
|
+
|
8
|
+
# Currently forced to bundle this, otherwise internals don't load properly
|
9
|
+
# Alternative is to comment out 'disable_system_gems'
|
10
|
+
gem 'cucumber', '0.6.1'
|
11
|
+
gem 'rspec', '1.2.9'
|
12
|
+
|
13
|
+
gem 'jeweler', '1.4.0', :bundle => false
|
14
|
+
gem 'database_cleaner', '0.4.0', :bundle => false
|
15
|
+
|
16
|
+
gem 'ruby-debug', :bundle => false
|
17
|
+
|
18
|
+
# disable_system_gems
|
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2009 Sam Phillips
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
@@ -0,0 +1,91 @@
|
|
1
|
+
= fabric
|
2
|
+
|
3
|
+
Fabric is a small ruby app to perform tasks on servers via SSH. Built around net/ssh and taking heavy inspiration from Capistrano, it allows you to create policies for server management and perform sysadmin tasks without the need for a process/daemon/dependency or even ruby being installed on the remote server.
|
4
|
+
|
5
|
+
Its primary purpose at the moment is to manage user access and ssh keys across a large cluster.
|
6
|
+
|
7
|
+
== Installation
|
8
|
+
|
9
|
+
gem install fabric
|
10
|
+
|
11
|
+
== Usage
|
12
|
+
|
13
|
+
Fabric parses a 'map' of your servers. On the simplest level, this will look something like:
|
14
|
+
|
15
|
+
Map.draw do |map|
|
16
|
+
map.role 'web server', '192.168.1.2'
|
17
|
+
map.user 'sammy'
|
18
|
+
map.grant 'sammy', 'web server'
|
19
|
+
end
|
20
|
+
|
21
|
+
Let's take this apart:
|
22
|
+
|
23
|
+
Map.draw do |map|
|
24
|
+
# Define a role called 'web server', and pass the IP of a host that is within the role
|
25
|
+
map.role 'web server', '192.168.1.2'
|
26
|
+
|
27
|
+
# Define a user - we'll look for sammy.pub in the key_repository (by default, './keys')
|
28
|
+
map.user 'sammy'
|
29
|
+
|
30
|
+
# Give the 'sammy' user access to all servers that are within the 'web server' role
|
31
|
+
map.grant 'sammy', 'web server'
|
32
|
+
end
|
33
|
+
|
34
|
+
Save this file (let's say you called it map.rb) and run it as so:
|
35
|
+
|
36
|
+
fab map.rb
|
37
|
+
|
38
|
+
For a detailed output of the process and commands being run, enable narration with:
|
39
|
+
|
40
|
+
fab map.rb --narrate
|
41
|
+
|
42
|
+
Or
|
43
|
+
|
44
|
+
fab map.rb -n
|
45
|
+
|
46
|
+
This will then execute the map file as pure Ruby, and perform the necessary operations
|
47
|
+
|
48
|
+
== More complex mapping
|
49
|
+
|
50
|
+
Map.draw do |map|
|
51
|
+
|
52
|
+
# Specify the directory to look for users' ssh keys:
|
53
|
+
map.key_repository 'other_key_repository'
|
54
|
+
|
55
|
+
# Pass multiple users if you like
|
56
|
+
map.user 'sammy', 'joey'
|
57
|
+
|
58
|
+
map.role 'file server', '2.3.4.5'
|
59
|
+
map.role 'database', '1.2.3.4'
|
60
|
+
|
61
|
+
# Grant all users access to all servers
|
62
|
+
map.grant :all, :all
|
63
|
+
|
64
|
+
# Create a namespace - this will contain all the users defined in the parent,
|
65
|
+
# but any users and roles added will only exist within this block
|
66
|
+
map.namespace do |cool_website|
|
67
|
+
# pass multiple IPs to add multiple hosts to this role
|
68
|
+
cool_website.role 'memcached', '1.1.1.1', '2.2.2.2'
|
69
|
+
|
70
|
+
# This user will only exist within the current namespace
|
71
|
+
cool_website.user 'timmy'
|
72
|
+
|
73
|
+
# Add all our users, including from the parent, to the memcached server:
|
74
|
+
grant :all, 'memcached'
|
75
|
+
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
== Normal Requirements
|
80
|
+
|
81
|
+
None - this should run straight out of the box.
|
82
|
+
|
83
|
+
== Testing Requirements
|
84
|
+
|
85
|
+
Bundler (http://github.com/wycats/bundler) is used to manage dependencies, and has cached versions of those gems which are required for execution. For development, it expects those files listed in the Gemfile with :bundle => false to be installed into the version of gems that the gem will run under.
|
86
|
+
|
87
|
+
The tests for this gem rely on being able to ssh to a target machine. This can be your development machine, but it's easier if it's a Virtual Machine (try out something from http://virtualbox.org). To run tests, first ensure you have bundled the gems with 'gem bundle', and copy the test.yml.example file to point at a machine which can be safely used to test SSH commands.
|
88
|
+
|
89
|
+
Note: add the moment, the tests assume at various points a Linux, rather than an OS X, way of doing things.
|
90
|
+
|
91
|
+
Copyright (c) 2009 Sam Phillips. See LICENSE for details.
|
data/Rakefile
ADDED
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.2.0
|
data/bin/fab
ADDED
data/fabric.gemspec
ADDED
@@ -0,0 +1,97 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = %q{fabric}
|
8
|
+
s.version = "0.2.0"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["Sam Phillips"]
|
12
|
+
s.date = %q{2010-01-23}
|
13
|
+
s.default_executable = %q{fab}
|
14
|
+
s.description = %q{Fabric is a small ruby app to perform tasks on servers via SSH. Built around net/ssh and taking heavy inspiration from Capistrano, it allows you to create policies for server management and perform sysadmin tasks without the need for a process/daemon/dependency or even ruby being installed on the remote server.}
|
15
|
+
s.email = %q{sam.phillips@setfiremedia.com}
|
16
|
+
s.executables = ["fab"]
|
17
|
+
s.extra_rdoc_files = [
|
18
|
+
"LICENSE",
|
19
|
+
"README.rdoc"
|
20
|
+
]
|
21
|
+
s.files = [
|
22
|
+
".document",
|
23
|
+
".gitignore",
|
24
|
+
"Gemfile",
|
25
|
+
"LICENSE",
|
26
|
+
"README.rdoc",
|
27
|
+
"Rakefile",
|
28
|
+
"VERSION",
|
29
|
+
"bin/fab",
|
30
|
+
"fabric.gemspec",
|
31
|
+
"features/support/env.rb",
|
32
|
+
"lib/extensions/narrator.rb",
|
33
|
+
"lib/fabric-test.rb",
|
34
|
+
"lib/fabric.rb",
|
35
|
+
"lib/fabric/account.rb",
|
36
|
+
"lib/fabric/capture.rb",
|
37
|
+
"lib/fabric/grant.rb",
|
38
|
+
"lib/fabric/group.rb",
|
39
|
+
"lib/fabric/key.rb",
|
40
|
+
"lib/fabric/map.rb",
|
41
|
+
"lib/fabric/role.rb",
|
42
|
+
"lib/fabric/server.rb",
|
43
|
+
"lib/fabric/user.rb",
|
44
|
+
"lib/initialisers/data_mapper.rb",
|
45
|
+
"spec/classes/account_spec.rb",
|
46
|
+
"spec/classes/capture_spec.rb",
|
47
|
+
"spec/classes/map_spec.rb",
|
48
|
+
"spec/classes/role_spec.rb",
|
49
|
+
"spec/classes/server_spec.rb",
|
50
|
+
"spec/classes/user_spec.rb",
|
51
|
+
"spec/spec.opts",
|
52
|
+
"spec/spec_helper.rb",
|
53
|
+
"tasks/features.rake",
|
54
|
+
"tasks/jeweler.rake",
|
55
|
+
"tasks/rdoc.rake",
|
56
|
+
"tasks/spec.rake",
|
57
|
+
"tasks/test.rake",
|
58
|
+
"test.yml.example",
|
59
|
+
"vendor/gems/ruby/1.8/cache/addressable-2.1.1.gem",
|
60
|
+
"vendor/gems/ruby/1.8/cache/columnize-0.3.1.gem",
|
61
|
+
"vendor/gems/ruby/1.8/cache/data_objects-0.10.0.gem",
|
62
|
+
"vendor/gems/ruby/1.8/cache/dm-core-0.10.2.gem",
|
63
|
+
"vendor/gems/ruby/1.8/cache/do_sqlite3-0.10.0.gem",
|
64
|
+
"vendor/gems/ruby/1.8/cache/extlib-0.9.14.gem",
|
65
|
+
"vendor/gems/ruby/1.8/cache/linecache-0.43.gem",
|
66
|
+
"vendor/gems/ruby/1.8/cache/net-ssh-2.0.15.gem",
|
67
|
+
"vendor/gems/ruby/1.8/cache/ruby-debug-base-0.10.3.gem"
|
68
|
+
]
|
69
|
+
s.homepage = %q{http://github.com/setfire/fabric}
|
70
|
+
s.rdoc_options = ["--charset=UTF-8"]
|
71
|
+
s.require_paths = ["lib"]
|
72
|
+
s.rubygems_version = %q{1.3.5}
|
73
|
+
s.summary = %q{Fabric is a small ruby app to perform tasks on servers via SSH.}
|
74
|
+
s.test_files = [
|
75
|
+
"spec/classes/account_spec.rb",
|
76
|
+
"spec/classes/capture_spec.rb",
|
77
|
+
"spec/classes/map_spec.rb",
|
78
|
+
"spec/classes/role_spec.rb",
|
79
|
+
"spec/classes/server_spec.rb",
|
80
|
+
"spec/classes/user_spec.rb",
|
81
|
+
"spec/spec_helper.rb"
|
82
|
+
]
|
83
|
+
|
84
|
+
if s.respond_to? :specification_version then
|
85
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
86
|
+
s.specification_version = 3
|
87
|
+
|
88
|
+
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
89
|
+
s.add_development_dependency(%q<cucumber>, [">= 0.3.104"])
|
90
|
+
else
|
91
|
+
s.add_dependency(%q<cucumber>, [">= 0.3.104"])
|
92
|
+
end
|
93
|
+
else
|
94
|
+
s.add_dependency(%q<cucumber>, [">= 0.3.104"])
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Narrator
|
2
|
+
def narrate(text)
|
3
|
+
return unless Fabric.options[:narrate]
|
4
|
+
narration = ''
|
5
|
+
|
6
|
+
if self.respond_to?(:narrate_as)
|
7
|
+
narration << "[#{self.narrate_as}]"
|
8
|
+
else
|
9
|
+
narration << "[#{self.class.to_s.downcase}]"
|
10
|
+
end
|
11
|
+
narration << " #{text}"
|
12
|
+
|
13
|
+
puts narration
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
class Object
|
18
|
+
include Narrator
|
19
|
+
end
|
data/lib/fabric-test.rb
ADDED
data/lib/fabric.rb
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
require 'optparse'
|
2
|
+
require 'initialisers/data_mapper'
|
3
|
+
|
4
|
+
module Fabric
|
5
|
+
def self.narrate_as
|
6
|
+
'fabric'
|
7
|
+
end
|
8
|
+
|
9
|
+
@@options = {}
|
10
|
+
def self.options(option = nil)
|
11
|
+
return @@options[option] if option
|
12
|
+
@@options
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.run!(arguments)
|
16
|
+
OptionParser.new do |options|
|
17
|
+
options.banner = "Usage: fab mapfile [options]"
|
18
|
+
|
19
|
+
@@options[:narrate] = false
|
20
|
+
options.on('-n', '--narrate', 'Give full narrative output') do
|
21
|
+
@@options[:narrate] = true
|
22
|
+
end
|
23
|
+
|
24
|
+
options.on('-h', '--help', 'Show this help screen') do
|
25
|
+
puts options
|
26
|
+
exit
|
27
|
+
end
|
28
|
+
end.parse!
|
29
|
+
|
30
|
+
map_file = arguments.first
|
31
|
+
|
32
|
+
if map_file.nil? or not File.exists?(map_file)
|
33
|
+
no_map_error = <<NO_MAP_FILE
|
34
|
+
|
35
|
+
Usage: fab mapfile [options]
|
36
|
+
|
37
|
+
Please specify a valid map file.
|
38
|
+
|
39
|
+
NO_MAP_FILE
|
40
|
+
|
41
|
+
puts no_map_error
|
42
|
+
|
43
|
+
exit 1
|
44
|
+
end
|
45
|
+
|
46
|
+
@@options[:map_root] = File.dirname(File.expand_path(map_file))
|
47
|
+
narrate "Setting map_root to #{Fabric.options(:map_root)}"
|
48
|
+
|
49
|
+
require map_file
|
50
|
+
|
51
|
+
0
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
Dir[File.dirname(__FILE__) + "/**/*.rb"].each do |file|
|
56
|
+
require file
|
57
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
class Account
|
2
|
+
include DataMapper::Resource
|
3
|
+
|
4
|
+
property :id, Serial
|
5
|
+
|
6
|
+
belongs_to :user
|
7
|
+
belongs_to :server
|
8
|
+
|
9
|
+
def add_user
|
10
|
+
self.server.execute_command("sudo /usr/sbin/useradd -m #{self.user.name}")
|
11
|
+
end
|
12
|
+
|
13
|
+
def add_ssh_directory
|
14
|
+
self.server.execute_command("sudo mkdir #{self.user.ssh_config_directory_path}")
|
15
|
+
self.server.execute_command("sudo chown #{self.user.name} #{user.ssh_config_directory_path}")
|
16
|
+
self.server.execute_command("sudo chmod 700 #{self.user.ssh_config_directory_path}")
|
17
|
+
end
|
18
|
+
|
19
|
+
def write_ssh_key
|
20
|
+
self.server.execute_command("sudo touch #{self.user.authorized_keys_file}")
|
21
|
+
self.server.execute_command("echo '#{self.user.authorized_keys_file}' | sudo tee #{self.user.authorized_keys_file_path}")
|
22
|
+
self.set_ssh_key_permissions
|
23
|
+
end
|
24
|
+
|
25
|
+
def add_to_groups
|
26
|
+
self.user.groups.each do |group|
|
27
|
+
self.server.execute_command("sudo /usr/sbin/usermod -G#{group.name} #{self.user.name}")
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
protected
|
32
|
+
def set_ssh_key_permissions
|
33
|
+
self.server.execute_command("sudo chown #{self.user.name} #{self.user.authorized_keys_file_path}")
|
34
|
+
self.server.execute_command("sudo chmod 600 #{self.user.authorized_keys_file_path}")
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
class Capture
|
2
|
+
include DataMapper::Resource
|
3
|
+
|
4
|
+
property :id, Serial
|
5
|
+
property :name, String
|
6
|
+
property :contents, Text
|
7
|
+
|
8
|
+
belongs_to :server, :required => false
|
9
|
+
|
10
|
+
after :create do
|
11
|
+
self.clear!
|
12
|
+
end
|
13
|
+
|
14
|
+
def clear!
|
15
|
+
self.contents = ''
|
16
|
+
end
|
17
|
+
|
18
|
+
def append(data)
|
19
|
+
self.contents << data
|
20
|
+
end
|
21
|
+
end
|