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 ADDED
@@ -0,0 +1,5 @@
1
+ README.rdoc
2
+ lib/**/*.rb
3
+ bin/*
4
+ features/**/*.feature
5
+ LICENSE
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
@@ -0,0 +1,8 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ Dir['tasks/**/*.rake'].each do |task|
5
+ load task
6
+ end
7
+
8
+ task :default => :test
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.2.0
data/bin/fab ADDED
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $:.unshift(File.dirname(__FILE__) + '/../lib/')
4
+ require File.join(File.dirname(__FILE__), '..', 'vendor','gems','environment')
5
+ require 'fabric'
6
+
7
+ exit Fabric.run!(ARGV)
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,6 @@
1
+ $LOAD_PATH.unshift(File.dirname(__FILE__) + '/../../lib')
2
+
3
+ require File.join(File.dirname(__FILE__), '..', '..', 'vendor','gems','environment')
4
+
5
+ require 'fabric'
6
+ require 'fabric-test'
@@ -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
@@ -0,0 +1,10 @@
1
+ module Fabric::Test
2
+ def self.settings
3
+ @@settings ||= self.load_settings
4
+ end
5
+
6
+ protected
7
+ def self.load_settings
8
+ YAML.load_file(File.join(File.dirname(__FILE__), '..', 'test.yml'))
9
+ end
10
+ end
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