finger-puppet 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (41) hide show
  1. data/.gitignore +1 -0
  2. data/Gemfile +4 -0
  3. data/Gemfile.lock +34 -0
  4. data/LICENSE +165 -0
  5. data/README.md +138 -0
  6. data/Rakefile +1 -0
  7. data/bin/finger-puppet +30 -0
  8. data/features/finger-puppet.feature +184 -0
  9. data/features/step_definitions/finger-puppet_steps.rb +149 -0
  10. data/features/support/env.rb +11 -0
  11. data/features/support/repos/simple/Gemfile +5 -0
  12. data/features/support/repos/simple/manifests/nodes/.stub +0 -0
  13. data/features/support/repos/simple/manifests/site.pp +1 -0
  14. data/features/support/repos/simple/modules/test/manifests/init.pp +5 -0
  15. data/features/support/repos/simple/var/.stub +0 -0
  16. data/features/support/repos/simple/var/reports/.stub +0 -0
  17. data/features/support/repos/simple_26/manifests/nodes/.stub +0 -0
  18. data/features/support/repos/simple_26/manifests/site.pp +1 -0
  19. data/features/support/repos/simple_26/modules/test/files/checkout +0 -0
  20. data/features/support/repos/simple_26/modules/test/manifests/init.pp +5 -0
  21. data/features/support/repos/simple_26/var/.stub +0 -0
  22. data/features/support/repos/simple_26/var/reports/.stub +0 -0
  23. data/finger-puppet.gemspec +25 -0
  24. data/lib/finger-puppet.rb +204 -0
  25. data/lib/generators/git/description +1 -0
  26. data/lib/generators/git/hooks/pre-commit +29 -0
  27. data/lib/generators/git/hooks/pre-receive +0 -0
  28. data/lib/generators/git/info/exclude +6 -0
  29. data/lib/generators/puppet/.gitignore +2 -0
  30. data/lib/generators/puppet/Gemfile +6 -0
  31. data/lib/generators/puppet/README.md +27 -0
  32. data/lib/generators/puppet/etc/puppet.conf +2 -0
  33. data/lib/generators/puppet/manifests/nodes.pp +1 -0
  34. data/lib/generators/puppet/manifests/site.pp +8 -0
  35. data/lib/generators/puppet/modules/.gitkeep +0 -0
  36. data/lib/generators/puppet/var/.gitkeep +0 -0
  37. data/lib/generators/puppet/vendor/.gitkeep +0 -0
  38. data/man/finger-puppet.1 +328 -0
  39. data/man/finger-puppet.1.html +308 -0
  40. data/man/finger-puppet.1.ronn +214 -0
  41. metadata +178 -0
@@ -0,0 +1,149 @@
1
+ Given /^I am working in "([^\"]*)"$/ do |directory|
2
+ @basedir = Pathname.new(directory)
3
+ end
4
+
5
+ Given /^I have an empty git repository named "([^\"]*)"$/ do |repo_name|
6
+ repo_path = @basedir.join(repo_name)
7
+ FileUtils.rm_rf(repo_path)
8
+ FileUtils.mkdir_p(repo_path)
9
+
10
+ Dir.chdir(repo_path) do
11
+ commands = [ "git init -q",
12
+ "touch foo",
13
+ "git add foo",
14
+ "git commit -qm 'init' ." ]
15
+ commands.each do |command|
16
+ system(command).should be_true
17
+ end
18
+ end
19
+
20
+ File.exists?(repo_path.join('.git')).should be_true
21
+ end
22
+
23
+ When /^I run "([^\"]*)"$/ do |cmd|
24
+ if cmd.split(' ').first == "finger-puppet"
25
+ command = %w(ruby -rubygems)
26
+ command << ROOT.join('bin', 'finger-puppet')
27
+ command << cmd.split[1..-1].join(' ')
28
+ command = command.join(' ')
29
+ else
30
+ command = cmd
31
+ end
32
+
33
+ Dir.chdir(@basedir) do
34
+ system(command).should be_true
35
+ end
36
+ end
37
+
38
+ Then /^running "([^\"]*)" should fail$/ do |command|
39
+ Dir.chdir(@basedir) do
40
+ system(command).should be_false
41
+ end
42
+ end
43
+
44
+ Then /^running "([^\"]*)" should succeed$/ do |command|
45
+ Dir.chdir(@basedir) do
46
+ system(command).should be_true
47
+ end
48
+ end
49
+
50
+ Then /^running "([^\"]*)" should output "([^\"]*)"$/ do |cmd, string|
51
+ if cmd.split(' ').first == "finger-puppet"
52
+ command = %w(ruby -rubygems)
53
+ command << ROOT.join('bin', 'finger-puppet')
54
+ command << cmd.split[1..-1].join(' ')
55
+ command = command.join(' ')
56
+ else
57
+ command = cmd
58
+ end
59
+
60
+ Dir.chdir(@basedir) do
61
+ (`#{command}` =~ /#{string}/).should be_true
62
+ end
63
+ end
64
+
65
+ Then /^I should have a git repository at "([^\"]*)"$/ do |repo_name|
66
+ repo_path = @basedir.join(repo_name)
67
+ File.exists?(repo_path.join('.git')).should be_true
68
+ end
69
+
70
+ Given /^there is no "([^\"]*)" repository$/ do |repo_name|
71
+ repo_path = @basedir.join(repo_name)
72
+ FileUtils.rm_rf(repo_path)
73
+ end
74
+
75
+ Given /^I have a simple Puppet repository named "([^\"]*)"$/ do |repo_name|
76
+ repo_path = @basedir.join(repo_name)
77
+ simple_path = ROOT.join('features', 'support', 'repos', 'simple')
78
+ hostname = Socket.gethostname.split('.').first
79
+
80
+ FileUtils.rm_rf(repo_path)
81
+ FileUtils.cp_r(simple_path, repo_path)
82
+
83
+ File.open(repo_path.join('manifests', 'nodes', "#{hostname}.pp"), 'w') do |f|
84
+ f << "node #{hostname} { include 'test' }"
85
+ end
86
+
87
+ Dir.chdir(repo_path) do
88
+ commands = ["git init -q", "git add .", "git commit -qm 'init' ."]
89
+ commands.each do |command|
90
+ system(command).should be_true
91
+ end
92
+ end
93
+
94
+ File.exists?(repo_path.join('.git')).should be_true
95
+ end
96
+
97
+ Given /^I have a simple Puppet 2.6 repository named "([^"]*)"$/ do |repo_name|
98
+ repo_path = @basedir.join(repo_name)
99
+ simple_path = ROOT.join('features', 'support', 'repos', 'simple_26')
100
+ hostname = Socket.gethostname.split('.').first
101
+
102
+ FileUtils.rm_rf(repo_path)
103
+ FileUtils.cp_r(simple_path, repo_path)
104
+
105
+ File.open(repo_path.join('manifests', 'nodes', "#{hostname}.pp"), 'w') do |f|
106
+ f << "node #{hostname} { include 'test' }"
107
+ end
108
+
109
+ Dir.chdir(repo_path) do
110
+ commands = ["git init -q", "git add .", "git commit -qm 'init' ."]
111
+ commands.each do |command|
112
+ system(command).should be_true
113
+ end
114
+ end
115
+
116
+ File.exists?(repo_path.join('.git')).should be_true
117
+ end
118
+
119
+ Then /^I should see a file at "([^\"]*)"$/ do |path|
120
+ File.exists?(path).should be_true
121
+ end
122
+
123
+ Then /^I should see a directory at "([^\"]*)"$/ do |path|
124
+ File.directory?(path).should be_true
125
+ end
126
+
127
+ Given /^there is no "([^\"]*)" file$/ do |file|
128
+ FileUtils.rm_rf(file).should be_true
129
+ end
130
+
131
+ Given /^"([^\"]*)" is on my path$/ do |command|
132
+ system("which #{command} > /dev/null").should be_true
133
+ end
134
+
135
+ Then /^I should see the following directories:$/ do |table|
136
+ table.hashes.each do |attrs|
137
+ File.directory?(attrs["directory"]).should be_true
138
+ end
139
+ end
140
+
141
+ When /^I touch "([^\"]*)"$/ do |filename|
142
+ system("touch #{filename} > /dev/null").should be_true
143
+ end
144
+
145
+ When /^the Gemfile looks like this:$/ do |string|
146
+ File.open(@basedir + 'Gemfile', 'w') do |f|
147
+ f << string + "\n"
148
+ end
149
+ end
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'socket'
4
+ require 'pathname'
5
+
6
+ ROOT = Pathname.new(File.expand_path(File.join(File.dirname(__FILE__), '..', '..')))
7
+
8
+ def silent_system(cmd)
9
+ command = [cmd, "2>&1 /dev/null"]
10
+ system(command)
11
+ end
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ source :rubygems
4
+
5
+ gem "puppet"
@@ -0,0 +1 @@
1
+ import "nodes/*"
@@ -0,0 +1,5 @@
1
+ class test {
2
+ file { "/tmp/checkout":
3
+ content => "checkout"
4
+ }
5
+ }
File without changes
File without changes
@@ -0,0 +1 @@
1
+ import "nodes/*"
@@ -0,0 +1,5 @@
1
+ class test($path="/tmp/checkout") {
2
+ file { "$path":
3
+ source => "puppet:///modules/test/checkout"
4
+ }
5
+ }
File without changes
@@ -0,0 +1,25 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+
4
+ Gem::Specification.new do |s|
5
+ s.name = "finger-puppet"
6
+ s.version = "0.3.0"
7
+ s.authors = ["Anthony Somerset"]
8
+ s.email = ["anthony@somersettechsolutions.co.uk"]
9
+ s.homepage = "http://github.com/anthonysomerset/finger-puppet"
10
+ s.summary = %q{finger-puppet helps you run Puppet locally against a Git checkout.}
11
+ s.description = %q{finger-puppet helps you run Puppet locally against a Git checkout. This is great for locally iterating your Puppet manifests very quickly, then pushng them up to a repository somewhere else to share the changes.}
12
+
13
+ s.rubyforge_project = "finger-puppet"
14
+
15
+ s.files = `git ls-files`.split("\n")
16
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
17
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
18
+ s.require_paths = ["lib"]
19
+
20
+ s.add_dependency 'thor', '0.13.4'
21
+ s.add_dependency 'bundler'
22
+
23
+ s.add_development_dependency 'puppet'
24
+ s.add_development_dependency 'cucumber'
25
+ end
@@ -0,0 +1,204 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'thor'
4
+ require 'pathname'
5
+
6
+ module Logging
7
+ ESCAPES = { :green => "\033[0;32m",
8
+ :yellow => "\033[0;33m",
9
+ :red => "\033[47;31m",
10
+ :reset => "\033[0m" }
11
+
12
+ def info(message)
13
+ emit(:message => message, :color => :green)
14
+ end
15
+
16
+ def warn(message)
17
+ emit(:message => message, :color => :yellow)
18
+ end
19
+
20
+ def error(message)
21
+ emit(:message => message, :color => :red)
22
+ end
23
+
24
+ def emit(opts={})
25
+ color = opts[:color]
26
+ message = opts[:message]
27
+ print ESCAPES[color]
28
+ print message
29
+ print ESCAPES[:reset]
30
+ print "\n"
31
+ end
32
+ end
33
+
34
+ # Thor's default stack trace on errors is ugly - make it pretty.
35
+ class Thor
36
+ class << self
37
+ def handle_argument_error(task, error)
38
+ puts "#{task.name.inspect} was called incorrectly. Call as #{task.formatted_usage(self, banner_base == "thor").inspect}."
39
+ exit 1
40
+ end
41
+ end
42
+ end
43
+
44
+
45
+ class finger-puppet < Thor
46
+ include Logging
47
+
48
+ def initialize
49
+ super
50
+ @root = Pathname.new(Dir.getwd)
51
+ @install_root = Pathname.new(File.expand_path(File.join(File.dirname(__FILE__))))
52
+ end
53
+
54
+ desc "clone <repository> [directory]", "clone a Git repository of Puppet manifests"
55
+ def clone(repository, directory=nil)
56
+ abort_unless_git_installed
57
+ directory ||= File.basename(repository.split('/').last, '.git')
58
+ command = "git clone -q #{repository} #{directory}"
59
+ system(command)
60
+
61
+ # A cloned repo may have submodules - automatically initialise them.
62
+ if File.exists?(File.join(directory, '.gitmodules'))
63
+ Dir.chdir(directory) do
64
+ system("git submodule init")
65
+ system("git submodule update")
66
+ end
67
+ end
68
+ end
69
+
70
+ desc "go [puppet arguments]", "do a local Puppet run"
71
+ def go(*puppet_args)
72
+ if ENV['USER'] != "root"
73
+ warn "You should probably be root when running this! Proceeding anyway..."
74
+ end
75
+
76
+ args = []
77
+ if has_frozen_components?
78
+ args << "bundle exec puppet"
79
+ info "Using frozen Puppet from #{@root.join('vendor', 'puppet')}."
80
+ else
81
+ abort_unless_puppet_installed(:message => "Please either install it on your system or freeze it with 'finger-puppet freeze'")
82
+ args << "puppet"
83
+ end
84
+
85
+ if puppet_version[0] > 0
86
+ args << 'apply'
87
+ end
88
+
89
+ args << "--modulepath #{@root.join('modules')}"
90
+ args << "--confdir #{@root.join('etc')}" unless puppet_args.include?("--confdir")
91
+ args << "--vardir #{@root.join('var')}" unless puppet_args.include?("--vardir")
92
+ args << "#{@root.join('manifests', 'site.pp')}"
93
+
94
+ args += puppet_args
95
+
96
+ command = args.join(' ')
97
+ puts command if args.include?("--debug")
98
+ system(command) ? exit(0) : exit(2)
99
+ end
100
+
101
+ desc "freeze [repository, project]", "freeze Puppet into your manifests repository"
102
+ def freeze(*args)
103
+ abort_unless_git_installed
104
+ abort_unless_bundle_installed
105
+
106
+ commands = []
107
+ commands << { :command => "bundle install --path=#{@root + 'vendor'} --binstubs" }
108
+ commands << { :command => "git add Gemfile.lock bin/ vendor/ .bundle/" }
109
+ commands << { :command => "git commit --quiet -m 'Bundled Puppet + Facter.' Gemfile* bin/ vendor/ .bundle/" }
110
+
111
+ commands.each do |attrs|
112
+ dir = attrs[:directory] || @root
113
+ Dir.chdir(dir) do
114
+ exit(2) unless system(attrs[:command])
115
+ end
116
+ end
117
+
118
+ info "Freezing complete."
119
+ end
120
+
121
+ desc "scaffold <project>", "generate scaffolding for a repository of Puppet manifests"
122
+ def scaffold(puppet_path)
123
+ template_path = @install_root.join('generators', 'puppet')
124
+ FileUtils.cp_r(template_path, puppet_path)
125
+ end
126
+
127
+ desc "init <project>", "initialise a repo of scaffolded Puppet manifests"
128
+ def init(project)
129
+ repo_path = @root.join(project)
130
+ template_path = @install_root.join('generators', 'git')
131
+
132
+ scaffold(repo_path)
133
+
134
+ Dir.chdir(repo_path) do
135
+ commands = [ "git init --quiet --template=#{template_path}",
136
+ "git add .",
137
+ "git commit --quiet -am 'Initial commit.'" ]
138
+ commands.each do |command|
139
+ system(command)
140
+ end
141
+ end
142
+
143
+ info "Your new finger-puppet project has been initialised in #{repo_path}"
144
+ end
145
+
146
+ desc "whoami [rfc2822-address]", "set the current commit author"
147
+ def whoami(address=nil)
148
+ # getter
149
+ if address
150
+ name = address[/^(.+)\s+</, 1]
151
+ email = address[/<(.+)>$/, 1]
152
+
153
+ unless name && email
154
+ abort("Supplied address isn't a valid rfc2822 email address")
155
+ end
156
+
157
+ system("git config user.name '#{name}'")
158
+ system("git config user.email '#{email}'")
159
+ # setter
160
+ else
161
+ name = `git config user.name`.strip
162
+ email = `git config user.email`.strip
163
+
164
+ if name.empty? || email.empty?
165
+ warn "You don't have a name or email set."
166
+ else
167
+ puts "#{name} <#{email}>"
168
+ end
169
+ end
170
+ end
171
+
172
+ private
173
+ def has_frozen_components?
174
+ puppet = system("bundle show puppet")
175
+ facter = system("bundle show facter")
176
+
177
+ puppet && facter
178
+ end
179
+
180
+ def puppet_version
181
+ @puppet_version ||= `puppet --version`.split('.').map(&:to_i)
182
+ end
183
+
184
+ # helper + abortive methods
185
+ %w(puppet git bundle).each do |bin|
186
+ class_eval <<-METHOD, __FILE__, __LINE__
187
+ no_tasks do
188
+ def #{bin}_installed?
189
+ `which #{bin}` =~ /#{bin}$/ ? true : false
190
+ end
191
+
192
+ def abort_unless_#{bin}_installed(opts={})
193
+ unless #{bin}_installed?
194
+ error "You don't have #{bin.capitalize} installed!"
195
+ error opts[:message] || "Please install it on your system."
196
+ exit 3
197
+ end
198
+ end
199
+ end
200
+ METHOD
201
+ end
202
+
203
+ end
204
+
@@ -0,0 +1 @@
1
+ a finger-puppet-ified repository of Puppet manifests
@@ -0,0 +1,29 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "socket"
4
+
5
+ name = ENV['GIT_AUTHOR_NAME'] || `git config user.name`
6
+ email = ENV['GIT_AUTHOR_EMAIL'] || `git config user.email`
7
+ hostname = Socket.gethostname
8
+
9
+ @errors = []
10
+ case
11
+ when !name, name.strip.empty?
12
+ @errors << "You don't have an author name set."
13
+ @errors << "Please set this to your real name before committing."
14
+ when !email, email.strip.empty?
15
+ @errors << "You don't have an author email address set."
16
+ @errors << "Please set this to your real email address before committing."
17
+ when name == "root", name =~ /^\s*$/
18
+ @errors << "Your author name is '#{name}'."
19
+ @errors << "Please set this to your real name before committing."
20
+ when email =~ /^\s*$/, email =~ /#{hostname}/
21
+ @errors << "You author email is '#{email}'."
22
+ @errors << "This matches part of the system hostname (#{hostname})."
23
+ @errors << "If this is correct, please run again with --no-verify."
24
+ end
25
+
26
+ if @errors.size > 0
27
+ puts @errors
28
+ exit 1
29
+ end
File without changes
@@ -0,0 +1,6 @@
1
+ # git-ls-files --others --exclude-from=.git/info/exclude
2
+ # Lines that start with '#' are comments.
3
+ # For a project mostly in C, the following would be a good set of
4
+ # exclude patterns (uncomment them if you want to use them):
5
+ # *.[oa]
6
+ # *~
@@ -0,0 +1,2 @@
1
+ var/state/
2
+ var/reports/
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ source :rubygems
4
+
5
+ gem 'puppet'
6
+ gem 'facter'
@@ -0,0 +1,27 @@
1
+ Manifests
2
+ =========
3
+
4
+ modules/ <= Puppet modules
5
+ manifests/ <= Puppet nodes
6
+ vendor/ <= frozen Puppet + Facter
7
+
8
+
9
+ Running Puppet with finger-puppet
10
+ ------------------------
11
+
12
+ From within this directory, run:
13
+
14
+ finger-puppet go
15
+
16
+ You can pass options to Puppet after the 'go':
17
+
18
+ finger-puppet go --debug --test
19
+
20
+ Freezing Puppet
21
+ ---------------
22
+
23
+ You can freeze Puppet:
24
+
25
+ finger-puppet freeze
26
+
27
+ Now finger-puppet will use the frozen Puppet when you run 'finger-puppet go'.
@@ -0,0 +1,2 @@
1
+ [main]
2
+ show_diff = true
@@ -0,0 +1 @@
1
+ # Put yur nodes here!
@@ -0,0 +1,8 @@
1
+ import "nodes"
2
+
3
+ filebucket { main: server => puppet }
4
+
5
+ File { backup => main }
6
+
7
+ Exec { path => "/usr/bin:/usr/sbin:/bin:/sbin" }
8
+
File without changes
File without changes
File without changes