diff --git a/.noexec.yaml b/.noexec.yaml new file mode 100644 index 000000000..0e0b0206b --- /dev/null +++ b/.noexec.yaml @@ -0,0 +1,5 @@ +--- +exclude: + - gem + - rake + - rspec diff --git a/spec/spec.opts b/.rspec similarity index 93% rename from spec/spec.opts rename to .rspec index 425f0edd3..6fbdfc1c1 100644 --- a/spec/spec.opts +++ b/.rspec @@ -1,4 +1,4 @@ --format -s +p --colour --backtrace diff --git a/Gemfile b/Gemfile new file mode 100644 index 000000000..5145f12ca --- /dev/null +++ b/Gemfile @@ -0,0 +1,32 @@ +source :rubygems + +gemspec + +group(:development, :test) do + gem "facter", "~> 1.6.4", :require => false + gem "rack", "~> 1.4.1", :require => false + gem "rspec", "~> 2.10.0", :require => false + gem "mocha", "~> 0.10.5", :require => false +end + +platforms :mswin, :mingw do + # See http://jenkins.puppetlabs.com/ for current Gem listings for the Windows + # CI Jobs. + gem "sys-admin", "~> 1.5.6", :require => false + gem "win32-api", "~> 1.4.8", :require => false + gem "win32-dir", "~> 0.3.7", :require => false + gem "win32-eventlog", "~> 0.5.3", :require => false + gem "win32-process", "~> 0.6.5", :require => false + gem "win32-security", "~> 0.1.2", :require => false + gem "win32-service", "~> 0.7.2", :require => false + gem "win32-taskscheduler", "~> 0.2.2", :require => false + gem "win32console", "~> 1.3.2", :require => false + gem "windows-api", "~> 0.4.1", :require => false + gem "windows-pr", "~> 1.2.1", :require => false +end + +if File.exists? "#{__FILE__}.local" + eval(File.read("#{__FILE__}.local"), binding) +end + +# vim:filetype=ruby diff --git a/Gemfile.lock b/Gemfile.lock new file mode 100644 index 000000000..445d46707 --- /dev/null +++ b/Gemfile.lock @@ -0,0 +1,44 @@ +PATH + remote: . + specs: + puppet (2.7.19) + facter (~> 1.5) + +GEM + remote: http://rubygems.org/ + specs: + diff-lcs (1.1.3) + facter (1.6.11) + metaclass (0.0.1) + mocha (0.10.5) + metaclass (~> 0.0.1) + rack (1.4.1) + rspec (2.10.0) + rspec-core (~> 2.10.0) + rspec-expectations (~> 2.10.0) + rspec-mocks (~> 2.10.0) + rspec-core (2.10.1) + rspec-expectations (2.10.0) + diff-lcs (~> 1.1.3) + rspec-mocks (2.10.1) + +PLATFORMS + ruby + +DEPENDENCIES + facter (~> 1.6.4) + mocha (~> 0.10.5) + puppet! + rack (~> 1.4.1) + rspec (~> 2.10.0) + sys-admin (~> 1.5.6) + win32-api (~> 1.4.8) + win32-dir (~> 0.3.7) + win32-eventlog (~> 0.5.3) + win32-process (~> 0.6.5) + win32-security (~> 0.1.2) + win32-service (~> 0.7.2) + win32-taskscheduler (~> 0.2.2) + win32console (~> 1.3.2) + windows-api (~> 0.4.1) + windows-pr (~> 1.2.1) diff --git a/README_DEVELOPER.md b/README_DEVELOPER.md index 75e123509..796af989e 100644 --- a/README_DEVELOPER.md +++ b/README_DEVELOPER.md @@ -1,126 +1,213 @@ # Developer README # This file is intended to provide a place for developers and contributors to document what other developers need to know about changes made to Puppet. +# Use of RVM considered dangerous # + +Use of RVM in production situations, e.g. running CI tests against this +repository, is considered dangerous. The reason we consider RVM to be +dangerous is because the default behavior of RVM is to hijack the builtin +behavior of the shell, causing Gemfile files to be loaded and evaluated when +the shell changes directories into the project root. + +This behavior causes the CI Job execution environment that runs with `set -e` +to be incompatible with RVM. + +We work around this issue by disabling the per-project RC file parsing using + + if ! grep -qx rvm_project_rvmrc=0 ~/.rvmrc; then + echo rvm_project_rvmrc=0 >> ~/.rvmrc + fi + +When we setup CI nodes, but this is not standard or expected behavior. + +Please consider rbenv instead of rvm. The default behavior of rvm is difficult +to maintain with `set -e` shell environments. + +# Dependencies # + +Puppet is considered an Application as it relates to the recommendation of +adding a Gemfile.lock file to the repository and the information published at +[Clarifying the Roles of the .gemspec and +Gemfile](http://yehudakatz.com/2010/12/16/clarifying-the-roles-of-the-gemspec-and-gemfile/) + +To install the dependencies run: `bundle install` to install the dependencies. + +A checkout of the source repository should be used in a way that provides +puppet as a gem rather than a simple Ruby library. The parent directory should +be set along the `GEM_PATH`, preferably before other tools such as RVM that +manage gemsets using `GEM_PATH`. + +For example, Puppet checked out into `/workspace/src/puppet` using `git +checkout https://github.com/puppetlabs/puppet` in `/workspace/src` can be used +with the following actions. The trick is to symlink `gems` to `src`. + + $ cd /workspace + $ ln -s src gems + $ mkdir specifications + $ pushd specifications; ln -s ../gems/puppet/puppet.gemspec; popd + $ export GEM_PATH="/workspace:${GEM_PATH}" + $ gem list puppet + +This should list out + + puppet (2.7.19) + +## Bundler ## + +With a source checkout of Puppet properly setup as a gem, dependencies can be +installed using [Bundler](http://gembundler.com/) + + $ bundle install + Fetching gem metadata from http://rubygems.org/........ + Using diff-lcs (1.1.3) + Installing facter (1.6.11) + Using metaclass (0.0.1) + Using mocha (0.10.5) + Using puppet (2.7.19) from source at /workspace/puppet-2.7.x/src/puppet + Using rack (1.4.1) + Using rspec-core (2.10.1) + Using rspec-expectations (2.10.0) + Using rspec-mocks (2.10.1) + Using rspec (2.10.0) + Using bundler (1.1.5) + Your bundle is complete! Use `bundle show [gemname]` to see where a bundled gem is installed. + # UTF-8 Handling # As Ruby 1.9 becomes more commonly used with Puppet, developers should be aware of major changes to the way Strings and Regexp objects are handled. Specifically, every instance of these two classes will have an encoding attribute determined in a number of ways. * If the source file has an encoding specified in the magic comment at the top, the instance will take on that encoding. * Otherwise, the encoding will be determined by the LC\_LANG or LANG environment variables. * Otherwise, the encoding will default to ASCII-8BIT ## References ## Excellent information about the differences between encodings in Ruby 1.8 and Ruby 1.9 is published in this blog series: [Understanding M17n](http://links.puppetlabs.com/understanding_m17n) ## Encodings of Regexp and String instances ## In general, please be aware that Ruby 1.9 regular expressions need to be compatible with the encoding of a string being used to match them. If they are not compatible you can expect to receive and error such as: Encoding::CompatibilityError: incompatible encoding regexp match (ASCII-8BIT regexp with UTF-8 string) In addition, some escape sequences were valid in Ruby 1.8 are no longer valid in 1.9 if the regular expression is not marked as an ASCII-8BIT object. You may expect errors like this in this situation: SyntaxError: (irb):7: invalid multibyte escape: /\xFF/ This error is particularly common when serializing a string to other representations like JSON or YAML. To resolve the problem you can explicitly mark the regular expression as ASCII-8BIT using the /n flag: "a" =~ /\342\230\203/n Finally, any time you're thinking of a string as an array of bytes rather than an array of characters, common when escaping a string, you should work with everything in ASCII-8BIT. Changing the encoding will not change the data itself and allow the Regexp and the String to deal with bytes rather than characters. Puppet provides a monkey patch to String which returns an encoding suitable for byte manipulations: # Example of how to escape non ASCII printable characters for YAML. >> snowman = "☃" >> snowman.to_ascii8bit.gsub(/([\x80-\xFF])/n) { |x| "\\x#{x.unpack("C")[0].to_s(16)} } => "\\xe2\\x98\\x83" If the Regexp is not marked as ASCII-8BIT using /n, then you can expect the SyntaxError, invalid multibyte escape as mentioned above. # Windows # If you'd like to run Puppet from source on Windows platforms, the include `ext/envpuppet.bat` will help. To quickly run Puppet from source, assuming you already have Ruby installed from [rubyinstaller.org](http://rubyinstaller.org). gem install sys-admin win32-process win32-dir win32-taskscheduler --no-rdoc --no-ri gem install win32-service --platform=mswin32 --no-rdoc --no-ri --version 0.7.1 net use Z: "\\vmware-host\Shared Folders" /persistent:yes Z: cd set PATH=%PATH%;Z:\\ext envpuppet puppet --version 2.7.9 Some spec tests are known to fail on Windows, e.g. no mount provider on Windows, so use the following rspec exclude filter: cd envpuppet rspec --tag ~fails_on_windows spec This will give you a shared filesystem with your Mac and allow you to run Puppet directly from source without using install.rb or copying files around. ## Common Issues ## * Don't assume file paths start with '/', as that is not a valid path on - Windows. Use Puppet::Util.absolute_path? to validate that a path is fully + Windows. Use Puppet::Util.absolute\_path? to validate that a path is fully qualified. - * Use File.expand_path('/tmp') in tests to generate a fully qualified path + * Use File.expand\_path('/tmp') in tests to generate a fully qualified path that is valid on POSIX and Windows. In the latter case, the current working directory will be used to expand the path. * Always use binary mode when performing file I/O, unless you explicitly want Ruby to translate between unix and dos line endings. For example, opening an executable file in text mode will almost certainly corrupt the resulting stream, as will occur when using: IO.open(path, 'r') { |f| ... } IO.read(path) If in doubt, specify binary mode explicitly: IO.open(path, 'rb') - * Don't assume file paths are separated by ':'. Use File::PATH_SEPARATOR + * Don't assume file paths are separated by ':'. Use `File::PATH_SEPARATOR` instead, which is ':' on POSIX and ';' on Windows. - * On Windows, File::SEPARATOR is '/', and File::ALT_SEPARATOR is '\'. On - POSIX systems, File::ALT_SEPARATOR is nil. In general, use '/' as the + * On Windows, File::SEPARATOR is '/', and `File::ALT_SEPARATOR` is '\'. On + POSIX systems, `File::ALT_SEPARATOR` is nil. In general, use '/' as the separator as most Windows APIs, e.g. CreateFile, accept both types of separators. * Don't use waitpid/waitpid2 if you need the child process' exit code, as the child process may exit before it has a chance to open the child's HANDLE and retrieve its exit code. Use Puppet::Util.execute. * Don't assume 'C' drive. Use environment variables to look these up: "#{ENV['windir']}/system32/netsh.exe" + +# Determining the Puppet Version + +If you need to programmatically work with the Puppet version, please use the +following: + + require 'puppet/version' + # Get the version baked into the sourcecode: + version = Puppet.version + # Set the version (e.g. in a Rakefile based on `git describe`) + Puppet.version = '2.3.4' + +Please do not monkey patch the constant `Puppet::PUPPETVERSION` or obtain the +version using the constant. The only supported way to set and get the Puppet +version is through the accessor methods. + EOF diff --git a/Rakefile b/Rakefile index f63309150..3121d3bc4 100644 --- a/Rakefile +++ b/Rakefile @@ -1,69 +1,69 @@ # Rakefile for Puppet -*- ruby -*- +# We need access to the Puppet.version method +$LOAD_PATH.unshift(File.expand_path("lib")) +require 'puppet/version' + $LOAD_PATH << File.join(File.dirname(__FILE__), 'tasks') begin require 'rubygems' require 'rubygems/package_task' rescue LoadError # Users of older versions of Rake (0.8.7 for example) will not necessarily # have rubygems installed, or the newer rubygems package_task for that # matter. require 'rake/packagetask' require 'rake/gempackagetask' end require 'rake' require 'rspec' require "rspec/core/rake_task" -module Puppet - %x{which git &> /dev/null} - if $?.success? and File.exist?('.git') - # remove the git hash from git describe string - PUPPETVERSION=%x{git describe}.chomp.gsub('-','.').split('.')[0..3].join('.') - else - PUPPETVERSION=File.read('lib/puppet.rb')[/PUPPETVERSION *= *'(.*)'/,1] or fail "Couldn't find PUPPETVERSION" - end +%x{which git &> /dev/null} +if $?.success? and File.exist?('.git') + # remove the git hash from git describe string + Puppet.version = %x{git describe}.chomp.gsub('-','.').split('.')[0..3].join('.') end Dir['tasks/**/*.rake'].each { |t| load t } FILES = FileList[ '[A-Z]*', 'install.rb', 'bin/**/*', 'sbin/**/*', 'lib/**/*', 'conf/**/*', 'man/**/*', 'examples/**/*', 'ext/**/*', 'tasks/**/*', 'test/**/*', 'spec/**/*' ] -Rake::PackageTask.new("puppet", Puppet::PUPPETVERSION) do |pkg| +Rake::PackageTask.new("puppet", Puppet.version) do |pkg| pkg.package_dir = 'pkg' pkg.need_tar_gz = true pkg.package_files = FILES.to_a end task :default do sh %{rake -T} end desc "Create the tarball and the gem - use when releasing" task :puppetpackages => [:create_gem, :package] RSpec::Core::RakeTask.new do |t| t.pattern ='spec/{unit,integration}/**/*.rb' t.fail_on_error = true end desc "Run the unit tests" task :unit do Dir.chdir("test") { sh "rake" } end diff --git a/lib/puppet.rb b/lib/puppet.rb index f45f7c77e..1596c41c5 100644 --- a/lib/puppet.rb +++ b/lib/puppet.rb @@ -1,129 +1,125 @@ +require 'puppet/version' + # Try to load rubygems. Hey rubygems, I hate you. begin require 'rubygems' rescue LoadError end # see the bottom of the file for further inclusions require 'singleton' require 'facter' require 'puppet/error' require 'puppet/util' require 'puppet/util/autoload' require 'puppet/util/settings' require 'puppet/util/feature' require 'puppet/util/suidmanager' require 'puppet/util/run_mode' #------------------------------------------------------------ # the top-level module # # all this really does is dictate how the whole system behaves, through # preferences for things like debugging # # it's also a place to find top-level commands like 'debug' module Puppet - PUPPETVERSION = '2.7.19' - - def Puppet.version - PUPPETVERSION - end - class << self include Puppet::Util attr_reader :features attr_writer :name end # the hash that determines how our system behaves @@settings = Puppet::Util::Settings.new # The services running in this process. @services ||= [] require 'puppet/util/logging' extend Puppet::Util::Logging # The feature collection @features = Puppet::Util::Feature.new('puppet/feature') # Load the base features. require 'puppet/feature/base' # Store a new default value. def self.setdefaults(section, hash) @@settings.setdefaults(section, hash) end # configuration parameter access and stuff def self.[](param) if param == :debug return Puppet::Util::Log.level == :debug else return @@settings[param] end end # configuration parameter access and stuff def self.[]=(param,value) @@settings[param] = value end def self.clear @@settings.clear end def self.debug=(value) if value Puppet::Util::Log.level=(:debug) else Puppet::Util::Log.level=(:notice) end end def self.settings @@settings end def self.run_mode $puppet_application_mode || Puppet::Util::RunMode[:user] end def self.application_name $puppet_application_name ||= "apply" end # Load all of the configuration parameters. require 'puppet/defaults' def self.genmanifest if Puppet[:genmanifest] puts Puppet.settings.to_manifest exit(0) end end # Parse the config file for this process. def self.parse_config Puppet.settings.parse end # Create a new type. Just proxy to the Type class. The mirroring query # code was deprecated in 2008, but this is still in heavy use. I suppose # this can count as a soft deprecation for the next dev. --daniel 2011-04-12 def self.newtype(name, options = {}, &block) Puppet::Type.newtype(name, options, &block) end end require 'puppet/type' require 'puppet/parser' require 'puppet/resource' require 'puppet/network' require 'puppet/ssl' require 'puppet/module' require 'puppet/util/storage' require 'puppet/status' require 'puppet/file_bucket/file' diff --git a/lib/puppet/face/help/global.erb b/lib/puppet/face/help/global.erb index c5a9ec9e0..6d60782f5 100644 --- a/lib/puppet/face/help/global.erb +++ b/lib/puppet/face/help/global.erb @@ -1,19 +1,19 @@ Usage: puppet [options] [options] Available subcommands, from Puppet Faces: <% Puppet::Face.faces.sort.each do |name| face = Puppet::Face[name, :current] -%> <%= face.name.to_s.ljust(16) %> <%= face.summary %> <% end -%> <% unless legacy_applications.empty? then # great victory when this is true! -%> Available applications, soon to be ported to Faces: <% legacy_applications.each do |appname| summary = horribly_extract_summary_from appname -%> <%= appname.to_s.ljust(16) %> <%= summary %> <% end end -%> See 'puppet help ' for help on a specific subcommand action. See 'puppet help ' for help on a specific subcommand. -Puppet v<%= Puppet::PUPPETVERSION %> +Puppet v<%= Puppet.version %> diff --git a/lib/puppet/version.rb b/lib/puppet/version.rb new file mode 100644 index 000000000..03fe5a669 --- /dev/null +++ b/lib/puppet/version.rb @@ -0,0 +1,18 @@ +# The version method and constant are isolated in puppet/version.rb so that a +# simple `require 'puppet/version'` allows a rubygems gemspec or bundler +# Gemfile to get the Puppet version of the gem install. +# +# The version is programatically settable because we want to allow the +# Raketasks and such to set the version based on the output of `git describe` +# +module Puppet + PUPPETVERSION = '2.7.19' + + def self.version + @puppet_version || PUPPETVERSION + end + + def self.version=(version) + @puppet_version = version + end +end diff --git a/puppet.gemspec b/puppet.gemspec new file mode 100644 index 000000000..abc9e0c1c --- /dev/null +++ b/puppet.gemspec @@ -0,0 +1,32 @@ +# -*- encoding: utf-8 -*- + +Gem::Specification.new do |s| + s.name = "puppet" + s.version = "2.7.19" + + s.required_rubygems_version = Gem::Requirement.new("> 1.3.1") if s.respond_to? :required_rubygems_version= + s.authors = ["Puppet Labs"] + s.date = "2012-08-17" + s.description = "Puppet, an automated configuration management tool" + s.email = "puppet@puppetlabs.com" + s.executables = ["puppet"] + s.files = ["bin/puppet"] + s.homepage = "http://puppetlabs.com" + s.rdoc_options = ["--title", "Puppet - Configuration Management", "--main", "README", "--line-numbers"] + s.require_paths = ["lib"] + s.rubyforge_project = "puppet" + s.rubygems_version = "1.8.24" + s.summary = "Puppet, an automated configuration management tool" + + if s.respond_to? :specification_version then + s.specification_version = 3 + + if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then + s.add_runtime_dependency(%q, ["~> 1.5"]) + else + s.add_dependency(%q, ["~> 1.5"]) + end + else + s.add_dependency(%q, ["~> 1.5"]) + end +end diff --git a/tasks/rake/gem.rake b/tasks/rake/gem.rake index 0ec54079c..9d5f70940 100644 --- a/tasks/rake/gem.rake +++ b/tasks/rake/gem.rake @@ -1,60 +1,61 @@ require 'fileutils' +require 'puppet/version' GEM_FILES = FileList[ '[A-Z]*', 'install.rb', 'bin/**/*', 'lib/**/*', 'conf/**/*', 'man/**/*', 'examples/**/*', 'ext/**/*', 'tasks/**/*', 'test/**/*', 'spec/**/*' ] EXECUTABLES = FileList[ 'bin/**/*', 'sbin/**/*' ] SBIN = Dir.glob("sbin/*") spec = Gem::Specification.new do |spec| spec.platform = Gem::Platform::RUBY spec.name = 'puppet' spec.files = GEM_FILES.to_a spec.executables = EXECUTABLES.gsub(/sbin\/|bin\//, '').to_a - spec.version = Puppet::PUPPETVERSION + spec.version = Puppet.version spec.add_dependency('facter', '~> 1.5') spec.summary = 'Puppet, an automated configuration management tool' spec.description = 'Puppet, an automated configuration management tool' spec.author = 'Puppet Labs' spec.email = 'puppet@puppetlabs.com' spec.homepage = 'http://puppetlabs.com' spec.rubyforge_project = 'puppet' spec.has_rdoc = true spec.rdoc_options << '--title' << 'Puppet - Configuration Management' << '--main' << 'README' << '--line-numbers' end desc "Prepare binaries for gem creation" task :prepare_gem do SBIN.each do |f| FileUtils.copy(f,"bin") end end desc "Create the gem" task :create_gem => :prepare_gem do Dir.mkdir("pkg") rescue nil Gem::Builder.new(spec).build - FileUtils.move("puppet-#{Puppet::PUPPETVERSION}.gem", "pkg") + FileUtils.move("puppet-#{Puppet.version}.gem", "pkg") SBIN.each do |f| fn = f.gsub(/sbin\/(.*)/, '\1') FileUtils.rm_r "bin/" + fn end end diff --git a/tasks/rake/sign.rake b/tasks/rake/sign.rake index f96a4604b..6447be149 100644 --- a/tasks/rake/sign.rake +++ b/tasks/rake/sign.rake @@ -1,14 +1,14 @@ desc "Sign to the package with the Puppet Labs release key" task :sign_packages do -version = Puppet::PUPPETVERSION +version = Puppet.version # Sign package sh "gpg --homedir $HOME/pl_release_key --detach-sign --output pkg/puppet-#{version}.tar.gz.sign --armor pkg/puppet-#{version}.tar.gz" # Sign gem sh "gpg --homedir $HOME/pl_release_key --detach-sign --output pkg/puppet-#{version}.gem.sign --armor pkg/puppet-#{version}.gem" end