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 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
- I use Necktie to setup and upgrade multiple servers.
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
- == Dress to impress
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
- task "/vol" do
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
- task "/etc/mysql"=>"/vol" do
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 "mysql"=>"/etc/mysql" do
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
@@ -3,6 +3,7 @@ require "necktie/files"
3
3
  require "necktie/gems"
4
4
  require "necktie/services"
5
5
  require "necktie/rush"
6
+ require "necktie/erb"
6
7
 
7
8
 
8
9
  # Includes all methods from Necktie::Files and Necktie::Gems methods.
@@ -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 #{options.env}"
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
- @syslog.send level, "[#{@sha}] #{message.strip.gsub(/%/, '%%')}" # syslog(3) freaks on % (printf)
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: #{name}"
162
+ application.syslog :info, "execute: %s", name
162
163
  execute_without_syslog args
163
164
  rescue
164
- application.syslog :err, "#{$!.backtrace.first}: #{$!.class}: #{$!}"
165
+ application.syslog :err, "%s: %s: %s", $!.backtrace.first, $!.class.name, $!.to_s
165
166
  raise
166
167
  end
167
168
  end
@@ -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"
@@ -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
- module Necktie::Files
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
- # Writes contents to a new file, or overwrites existing file.
8
- # Takes string as second argument, or yields to block. For example:
9
- # write "/etc/mailname", "example.com"
10
- # write("/var/run/bowtie.pid") { Process.pid }
11
- def write(name, contents = nil)
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
- # Append contents to a file, creating it if necessary.
19
- # Takes string as second argument, or yields to block. For example:
20
- # append "/etc/fstab", "/dev/sdh /vol xfs\n" unless read("/etc/fstab")["/dev/sdh "]
21
- def append(name, contents = nil)
22
- contents ||= yield
23
- File.open name, "a" do |f|
24
- f.write contents
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
- # Updates a file: read contents, substitue and write it back.
29
- # Takes two arguments for substitution, or yields to block.
30
- # These two are equivalent:
31
- # update "/etc/memcached.conf", /^-l 127.0.0.1/, "-l 0.0.0.0"
32
- # update("/etc/memcached.conf") { |s| s.sub(/^-l 127.0.0.1/, "-l 0.0.0.0") }
33
- def update(name, from = nil, to = nil)
34
- contents = File.read(name)
35
- if from && to
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
- File.open name, "w" do |f|
41
- f.write contents
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::Gems
4
- # Installs the specified gem, if not already installed. First argument is the
5
- # name of the gem, or file containing the gem. Second argument is version requirement.
6
- # For example:
7
- # install_gem "unicorn", "~>0.93"
8
- #
9
- # Dir["gems/*.gem"].each do |gem|
10
- # install_gem gem
11
- # end
12
- def install_gem(name, version = nil)
13
- installer = Gem::DependencyInstaller.new
14
- spec = installer.find_spec_by_name_and_version(name, version).first.first
15
- if Gem::SourceIndex.from_installed_gems.find_name(spec.name, spec.version).empty?
16
- puts " ** Installing the gem #{spec.name} #{spec.version}"
17
- installer.install name, version
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
@@ -1,6 +1,6 @@
1
1
  Gem::Specification.new do |spec|
2
2
  spec.name = "necktie"
3
- spec.version = "1.0.4"
3
+ spec.version = "1.0.5"
4
4
  spec.author = "Assaf Arkin"
5
5
  spec.email = "assaf@labnotes.org"
6
6
  spec.homepage = "http://github.com/assaf/necktie"
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
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-04 00:00:00 -08:00
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.4
259
+ - Necktie 1.0.5
259
260
  - --main
260
261
  - README.rdoc
261
262
  - --webcvs