necktie 1.0.4 → 1.0.5
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/CHANGELOG +6 -0
- data/README.rdoc +16 -19
- data/example/tasks/db.rb +3 -3
- data/lib/necktie.rb +1 -0
- data/lib/necktie/application.rb +7 -6
- data/lib/necktie/capistrano.rb +1 -1
- data/lib/necktie/erb.rb +66 -0
- data/lib/necktie/files.rb +58 -35
- data/lib/necktie/gems.rb +17 -15
- data/necktie.gemspec +1 -1
- metadata +4 -3
data/CHANGELOG
CHANGED
@@ -1,3 +1,9 @@
|
|
1
|
+
1.0.5 (2009-11-13)
|
2
|
+
* Added erb: a file task that creates a file form an ERB template.
|
3
|
+
* Changed: The write and update file methods now perform atomic writes (create
|
4
|
+
temporary file, rename to desired path).
|
5
|
+
* Fix: MySQL examples used task instead of file task.
|
6
|
+
|
1
7
|
1.0.4 (2009-11-4)
|
2
8
|
* When used with -U option and no other argument, necktie command updates the
|
3
9
|
local repo but does not run any task.
|
data/README.rdoc
CHANGED
@@ -1,29 +1,22 @@
|
|
1
|
-
|
1
|
+
= Minimalistic server configuration tool
|
2
2
|
|
3
|
+
Necktie is tool for running configuration tasks: setting up new servers and
|
4
|
+
upgrading production servers. You can use it to manage services, mount
|
5
|
+
volumes, change configuration, manage cronjobs, etc.
|
3
6
|
|
4
|
-
|
5
|
-
|
6
|
-
Necktie runs a set of scripts (or configuration tasks) that can create and edit
|
7
|
-
files, install and setup services, mount volumes, and basically anything you
|
8
|
-
can script with Ruby and a shell.
|
9
|
-
|
10
|
-
To use Necktie:
|
11
|
-
- Write your Necktie tasks
|
12
|
-
- Include support files (config, binaries, etc)
|
13
|
-
- git push && cap necktie
|
14
|
-
|
15
|
-
I use it to:
|
16
|
-
|
17
|
-
- gem install, apt-get
|
18
|
-
- Configure Nginx, install and setup Unicorn
|
19
|
-
- Change memcached configuration from default
|
20
|
-
- Setup MySQL to use a mounted (EBS) volume
|
21
|
-
- Install and update cron scripts
|
7
|
+
http://19.media.tumblr.com/tumblr_kspcaa4Djr1qzo4cfo1_250.jpg
|
22
8
|
|
23
9
|
To install Necktie:
|
24
10
|
|
25
11
|
$ gem install necktie --source http://gemcutter.org
|
26
12
|
|
13
|
+
To use Necktie:
|
14
|
+
1. Write a Necktie file with configurtaion tasks
|
15
|
+
2. Include any support files: config, scripts, binaries, etc
|
16
|
+
3. git push && cap necktie
|
17
|
+
|
18
|
+
Introduction to Necktie: http://blog.labnotes.org/2009/11/04/necktie-dress-to-impress/
|
19
|
+
|
27
20
|
|
28
21
|
== Tasking
|
29
22
|
|
@@ -215,4 +208,8 @@ Includes Session, created by Ara T. Howard and released under the Ruby License
|
|
215
208
|
http://raa.ruby-lang.org/project/session
|
216
209
|
http://www.codeforpeople.com/lib/ruby/session
|
217
210
|
|
211
|
+
Atomic file write adapted from Rails.
|
212
|
+
|
213
|
+
ERB task adapted from Luke Bayes http://gist.github.com/215270.
|
214
|
+
|
218
215
|
Developed for managing EC2 instances for http://apartly.com
|
data/example/tasks/db.rb
CHANGED
@@ -3,13 +3,13 @@ file "/etc/cron.hourly/snapshot"=>"etc/cron/snapshot" do
|
|
3
3
|
chmod 0755, "/etc/cron.hourly/snapshot"
|
4
4
|
end
|
5
5
|
|
6
|
-
|
6
|
+
file "/vol" do
|
7
7
|
# Assumes we attach EBS volume to /dev/sdh, formatted it to XFS, mounted to /vol.
|
8
8
|
append "/etc/fstab", "/dev/sdh /vol xfs noatime,nobarrier 0 0\n" unless read("/etc/fstab")["/dev/sdh "]
|
9
9
|
sh "mount /vol"
|
10
10
|
end
|
11
11
|
|
12
|
-
|
12
|
+
file "/etc/mysql"=>"/vol" do
|
13
13
|
# Mount the respective MySQL directories. Make sure they exist on your EBS volume first, see:
|
14
14
|
# http://developer.amazonwebservices.com/connect/entry.jspa?externalID=1663
|
15
15
|
mounts = { "/vol/etc/mysql"=>"/etc/mysql",
|
@@ -23,7 +23,7 @@ task "/etc/mysql"=>"/vol" do
|
|
23
23
|
chmod 0755, "/etc/mysql/debian-start"
|
24
24
|
end
|
25
25
|
|
26
|
-
task
|
26
|
+
task :mysql=>"/etc/mysql" do
|
27
27
|
services.start "mysql" unless services.running?("mysql")
|
28
28
|
end
|
29
29
|
|
data/lib/necktie.rb
CHANGED
data/lib/necktie/application.rb
CHANGED
@@ -10,7 +10,6 @@ module Necktie
|
|
10
10
|
@rakefiles = ["Necktie", "necktie", "Necktie.rb", "necktie.rb"]
|
11
11
|
options.nosearch = true
|
12
12
|
options.env = "production"
|
13
|
-
@syslog = Syslog.open("necktie")
|
14
13
|
end
|
15
14
|
|
16
15
|
def run
|
@@ -31,7 +30,7 @@ module Necktie
|
|
31
30
|
sh "git checkout #{options.ref}" if options.ref
|
32
31
|
@sha = `git rev-parse --verify HEAD --short`.strip
|
33
32
|
puts "(in #{Dir.pwd}, head is #{@sha}, environment is #{options.env})"
|
34
|
-
syslog :info, "environment is
|
33
|
+
syslog :info, "environment is %s", options.env
|
35
34
|
|
36
35
|
load_rakefile
|
37
36
|
top_level unless options.pull && ARGV.empty?
|
@@ -141,8 +140,10 @@ module Necktie
|
|
141
140
|
load_imports
|
142
141
|
end
|
143
142
|
|
144
|
-
def syslog(level, message)
|
145
|
-
|
143
|
+
def syslog(level, message, *args)
|
144
|
+
Syslog.open("necktie") do |s|
|
145
|
+
s.send level, "[#{@sha}] #{message}", *args
|
146
|
+
end
|
146
147
|
end
|
147
148
|
|
148
149
|
end
|
@@ -158,10 +159,10 @@ end
|
|
158
159
|
class Rake::Task #:nodoc:
|
159
160
|
alias :execute_without_syslog :execute
|
160
161
|
def execute(args=nil)
|
161
|
-
application.syslog :info, "execute:
|
162
|
+
application.syslog :info, "execute: %s", name
|
162
163
|
execute_without_syslog args
|
163
164
|
rescue
|
164
|
-
application.syslog :err, "
|
165
|
+
application.syslog :err, "%s: %s: %s", $!.backtrace.first, $!.class.name, $!.to_s
|
165
166
|
raise
|
166
167
|
end
|
167
168
|
end
|
data/lib/necktie/capistrano.rb
CHANGED
@@ -13,7 +13,7 @@ Capistrano::Configuration.instance.load do
|
|
13
13
|
desc "[internal] Pull updates from Git"
|
14
14
|
task :pull do
|
15
15
|
fail "You need to set :necktie_url, <git_url>" unless necktie_url
|
16
|
-
sudo "necktie --source #{necktie_url} --update"
|
16
|
+
sudo "necktie --environment #{fetch(:rails_env, "production")} --source #{necktie_url} --update"
|
17
17
|
end
|
18
18
|
|
19
19
|
desc "[internal] Run necktie upgrade"
|
data/lib/necktie/erb.rb
ADDED
@@ -0,0 +1,66 @@
|
|
1
|
+
require "erb"
|
2
|
+
|
3
|
+
module Necktie
|
4
|
+
|
5
|
+
# Rake task that makes it stupid-easy to render ERB templates to disk. Just
|
6
|
+
# add parameters to the yielded object, and they will be available to your
|
7
|
+
# Template.
|
8
|
+
#
|
9
|
+
# Loosely based on: http://gist.github.com/215270
|
10
|
+
class ErbTask < Rake::FileTask
|
11
|
+
|
12
|
+
# Path to the input ERB template. This value will default to the value of
|
13
|
+
# "#{output}.erb"
|
14
|
+
attr_accessor :template
|
15
|
+
|
16
|
+
def initialize(name, app) # :nodoc:
|
17
|
+
super
|
18
|
+
@output = name
|
19
|
+
@template = "#{name}.erb"
|
20
|
+
@values = {}
|
21
|
+
end
|
22
|
+
|
23
|
+
def execute(*args)
|
24
|
+
super
|
25
|
+
content = File.read(template)
|
26
|
+
result = ERB.new(content, nil, '>').result(binding)
|
27
|
+
write name, result
|
28
|
+
puts " * Created ERB output at: #{name}" if application.options.trace
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.define_task(args)
|
32
|
+
task = super
|
33
|
+
if task.is_a?(Necktie::ErbTask)
|
34
|
+
yield task if block_given?
|
35
|
+
task.prerequisites << file(task.template)
|
36
|
+
end
|
37
|
+
return task
|
38
|
+
end
|
39
|
+
|
40
|
+
def method_missing(method, *params, &block)
|
41
|
+
if method.to_s[/^(.*)=$/, 1]
|
42
|
+
return @values[$1] = params.first
|
43
|
+
elsif @values.keys.include?(method.to_s)
|
44
|
+
return @values[method.to_s]
|
45
|
+
else
|
46
|
+
super
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
# Rake task that makes it stupid-easy to render ERB templates to disk. Just add
|
54
|
+
# parameters to the yielded object, and they will be available to your
|
55
|
+
# Template.
|
56
|
+
#
|
57
|
+
# For example:
|
58
|
+
# erb 'config/SomeFile.xml' do |t|
|
59
|
+
# t.param1 = 'value'
|
60
|
+
# t.other_param = 'other value'
|
61
|
+
# t.another_param = ['a', 'b', 'c']
|
62
|
+
# t.template = 'config/SomeFile.xml.erb' # Optional - will automatically look here...
|
63
|
+
# end
|
64
|
+
def erb(args, &block)
|
65
|
+
Necktie::ErbTask.define_task(args, &block)
|
66
|
+
end
|
data/lib/necktie/files.rb
CHANGED
@@ -1,44 +1,67 @@
|
|
1
|
-
|
2
|
-
# Return the contents of the file (same as File.read).
|
3
|
-
def read(name)
|
4
|
-
File.read(name)
|
5
|
-
end
|
1
|
+
require "tempfile"
|
6
2
|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
contents ||= yield
|
13
|
-
File.open name, "w" do |f|
|
14
|
-
f.write contents
|
3
|
+
module Necktie
|
4
|
+
module Files
|
5
|
+
# Return the contents of the file (same as File.read).
|
6
|
+
def read(name)
|
7
|
+
File.read(name)
|
15
8
|
end
|
16
|
-
end
|
17
9
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
10
|
+
# Writes contents to a new file, or overwrites existing file. Takes string
|
11
|
+
# as second argument, or yields to block. For example:
|
12
|
+
#
|
13
|
+
# write "/etc/mailname", "example.com"
|
14
|
+
# write("/var/run/bowtie.pid") { Process.pid }
|
15
|
+
#
|
16
|
+
# This method performs an atomic write using TMPDIR or (/tmp) as the
|
17
|
+
# temporary directory and renaming the file over to the new location.
|
18
|
+
# Ownership and premission are retained if replacing existing file.
|
19
|
+
def write(name, contents = nil)
|
20
|
+
temp = Tempfile.new(File.basename(name))
|
21
|
+
temp.write contents || yield
|
22
|
+
temp.close
|
23
|
+
|
24
|
+
begin
|
25
|
+
# Get original file permissions
|
26
|
+
stat = File.stat(name)
|
27
|
+
rescue Errno::ENOENT
|
28
|
+
# No old permissions, write a temp file to determine the defaults
|
29
|
+
stat_check = File.join(File.dirname(name), ".permissions_check.#{Thread.current.object_id}.#{Process.pid}.#{rand(1000000)}")
|
30
|
+
File.open(stat_check, "w") { }
|
31
|
+
stat = File.stat(stat_check)
|
32
|
+
File.unlink stat_check
|
33
|
+
end
|
34
|
+
|
35
|
+
# Overwrite original file with temp file (tries rename, if that fails, copies).
|
36
|
+
FileUtils.mv temp.path, name
|
37
|
+
|
38
|
+
# Set correct permissions on new file
|
39
|
+
File.chown stat.uid, stat.gid, name
|
40
|
+
File.chmod stat.mode, name
|
25
41
|
end
|
26
|
-
end
|
27
42
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
contents = contents.sub(from, to)
|
37
|
-
else
|
38
|
-
contents = yield(contents)
|
43
|
+
# Append contents to a file, creating it if necessary. Takes string as
|
44
|
+
# second argument, or yields to block. For example:
|
45
|
+
# append "/etc/fstab", "/dev/sdh /vol xfs\n" unless read("/etc/fstab")["/dev/sdh "]
|
46
|
+
def append(name, contents = nil)
|
47
|
+
contents ||= yield
|
48
|
+
File.open name, "a" do |f|
|
49
|
+
f.write contents
|
50
|
+
end
|
39
51
|
end
|
40
|
-
|
41
|
-
|
52
|
+
|
53
|
+
# Updates a file: read contents, substitue and write it back. Takes two
|
54
|
+
# arguments for substitution, or yields to block. These two are equivalent:
|
55
|
+
# update "/etc/memcached.conf", /^-l 127.0.0.1/, "-l 0.0.0.0"
|
56
|
+
# update("/etc/memcached.conf") { |s| s.sub(/^-l 127.0.0.1/, "-l 0.0.0.0") }
|
57
|
+
def update(name, from = nil, to = nil)
|
58
|
+
contents = File.read(name)
|
59
|
+
if from && to
|
60
|
+
contents = contents.sub(from, to)
|
61
|
+
else
|
62
|
+
contents = yield(contents)
|
63
|
+
end
|
64
|
+
write name, contents
|
42
65
|
end
|
43
66
|
end
|
44
67
|
end
|
data/lib/necktie/gems.rb
CHANGED
@@ -1,20 +1,22 @@
|
|
1
1
|
require "rubygems/dependency_installer"
|
2
2
|
|
3
|
-
module Necktie
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
3
|
+
module Necktie
|
4
|
+
module Gems
|
5
|
+
# Installs the specified gem, if not already installed. First argument is the
|
6
|
+
# name of the gem, or file containing the gem. Second argument is version requirement.
|
7
|
+
# For example:
|
8
|
+
# install_gem "unicorn", "~>0.93"
|
9
|
+
#
|
10
|
+
# Dir["gems/*.gem"].each do |gem|
|
11
|
+
# install_gem gem
|
12
|
+
# end
|
13
|
+
def install_gem(name, version = nil)
|
14
|
+
installer = Gem::DependencyInstaller.new
|
15
|
+
spec = installer.find_spec_by_name_and_version(name, version).first.first
|
16
|
+
if Gem::SourceIndex.from_installed_gems.find_name(spec.name, spec.version).empty?
|
17
|
+
puts " ** Installing the gem #{spec.name} #{spec.version}"
|
18
|
+
installer.install name, version
|
19
|
+
end
|
18
20
|
end
|
19
21
|
end
|
20
22
|
end
|
data/necktie.gemspec
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: necktie
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.
|
4
|
+
version: 1.0.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Assaf Arkin
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2009-11-
|
12
|
+
date: 2009-11-13 00:00:00 -08:00
|
13
13
|
default_executable:
|
14
14
|
dependencies: []
|
15
15
|
|
@@ -26,6 +26,7 @@ files:
|
|
26
26
|
- bin/necktie
|
27
27
|
- lib/necktie/application.rb
|
28
28
|
- lib/necktie/capistrano.rb
|
29
|
+
- lib/necktie/erb.rb
|
29
30
|
- lib/necktie/files.rb
|
30
31
|
- lib/necktie/gems.rb
|
31
32
|
- lib/necktie/rush.rb
|
@@ -255,7 +256,7 @@ licenses: []
|
|
255
256
|
post_install_message:
|
256
257
|
rdoc_options:
|
257
258
|
- --title
|
258
|
-
- Necktie 1.0.
|
259
|
+
- Necktie 1.0.5
|
259
260
|
- --main
|
260
261
|
- README.rdoc
|
261
262
|
- --webcvs
|