finger-puppet 0.3.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.
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