diff --git a/CHANGELOG b/CHANGELOG new file mode 100644 index 00000000..a7c26c49 --- /dev/null +++ b/CHANGELOG @@ -0,0 +1,2 @@ +0.1.0 2012-04-30 Nan Liu +* Initial publish of module. diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000..8961ce8a --- /dev/null +++ b/LICENSE @@ -0,0 +1,15 @@ +Copyright (C) 2012 Puppet Labs Inc + +Puppet Labs can be contacted at: info@puppetlabs.com + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/Modulefile b/Modulefile new file mode 100644 index 00000000..cad6deeb --- /dev/null +++ b/Modulefile @@ -0,0 +1,12 @@ +name 'puppetlabs-nodejs' +version '0.1.0' +source 'git://github.com/puppetlabs/puppetlabs-nodejs' +author 'puppetlabs' +license 'Apache 2.0' +summary 'Nodejs & NPM Puppet Module' +description 'Nodejs & NPM Puppet Module.' +project_page 'https://github.com/puppetlabs/puppetlabs-nodejs' + +## Add dependencies, if any: +# dependency 'username/name', '>= 1.2.0' +'puppetlabs/stdlib', '>= 1.0.0' diff --git a/Rakefile b/Rakefile new file mode 100644 index 00000000..6f288367 --- /dev/null +++ b/Rakefile @@ -0,0 +1,35 @@ +require 'rubygems' +require 'rake' +require 'rspec/core/rake_task' + +task :default do + system("rake -T") +end + +task :specs => [:spec] + +desc "Run all rspec-puppet tests" +RSpec::Core::RakeTask.new(:spec) do |t| + t.rspec_opts = ['--color'] + # ignores fixtures directory. + t.pattern = 'spec/{classes,defines,unit}/**/*_spec.rb' +end + +desc "Build puppet module package" +task :build do + # This will be deprecated once puppet-module is a face. + begin + Gem::Specification.find_by_name('puppet-module') + rescue Gem::LoadError, NoMethodError + require 'puppet/face' + pmod = Puppet::Face['module', :current] + pmod.build('./') + end +end + +desc "Check puppet manifests with puppet-lint" +task :lint do + # This requires pull request: https://github.com/rodjek/puppet-lint/pull/81 + system("puppet-lint manifests") + system("puppet-lint tests") +end diff --git a/manifests/init.pp b/manifests/init.pp index cbf074ec..51b965b5 100644 --- a/manifests/init.pp +++ b/manifests/init.pp @@ -34,6 +34,10 @@ before => Package['nodejs'], } } + + default: { + fail("Class nodejs does not support $::operatingsystem") + } } package { 'nodejs': @@ -46,7 +50,7 @@ # npm installation is a hack since there's no packages: exec { 'install_npm': - command => 'curl http://npmjs.org/install.sh | sed "s/<\/dev\/tty//g" > /tmp/install_$$.sh; chmod 755 /tmp/install_$$.sh; /tmp/install_$$.sh', + command => 'curl http://npmjs.org/install.sh | sed "s/<\/dev\/tty//g" > /tmp/install_$$.sh; chmod 755 /tmp/install_$$.sh; /tmp/install_$$.sh', unless => 'which npm', path => $::path, logoutput => 'on_failure', diff --git a/spec/fixtures/unit/puppet/provider/pakages/npm/npm_global b/spec/fixtures/unit/puppet/provider/pakages/npm/npm_global new file mode 100644 index 00000000..f622291f --- /dev/null +++ b/spec/fixtures/unit/puppet/provider/pakages/npm/npm_global @@ -0,0 +1,117 @@ +{ + "dependencies": { + "express": { + "version": "2.5.9", + "dependencies": { + "connect": { + "version": "1.8.7", + "dependencies": { + "formidable": { + "version": "1.0.9" + } + } + }, + "mime": { + "version": "1.2.4" + }, + "qs": { + "version": "0.4.2" + }, + "mkdirp": { + "version": "0.3.0" + } + } + }, + "npm": { + "version": "1.1.15", + "dependencies": { + "semver": { + "version": "1.0.13" + }, + "ini": { + "version": "1.0.2" + }, + "slide": { + "version": "1.1.3" + }, + "abbrev": { + "version": "1.0.3" + }, + "graceful-fs": { + "version": "1.1.8" + }, + "minimatch": { + "version": "0.2.2" + }, + "nopt": { + "version": "1.0.10" + }, + "node-uuid": { + "version": "1.3.3" + }, + "proto-list": { + "version": "1.0.0" + }, + "rimraf": { + "version": "2.0.1" + }, + "request": { + "version": "2.9.153" + }, + "which": { + "version": "1.0.5" + }, + "tar": { + "version": "0.1.13" + }, + "fstream": { + "version": "0.1.18" + }, + "block-stream": { + "version": "0.0.5" + }, + "inherits": { + "version": "1.0.0", + "from": "git://github.com/isaacs/inherits" + }, + "mkdirp": { + "version": "0.3.0" + }, + "read": { + "version": "0.0.1" + }, + "lru-cache": { + "version": "1.0.5" + }, + "node-gyp": { + "version": "0.3.9", + "dependencies": { + "ansi": { + "version": "0.0.4" + }, + "glob": { + "version": "3.1.9" + } + } + }, + "fstream-npm": { + "version": "0.0.4", + "dependencies": { + "fstream-ignore": { + "version": "0.0.5" + } + } + }, + "uid-number": { + "version": "0.0.3" + }, + "archy": { + "version": "0.0.2" + }, + "chownr": { + "version": "0.0.1" + } + } + } + } +} diff --git a/spec/puppetlabs_spec/files.rb b/spec/puppetlabs_spec/files.rb new file mode 100644 index 00000000..d64cb3fb --- /dev/null +++ b/spec/puppetlabs_spec/files.rb @@ -0,0 +1,57 @@ +require 'fileutils' +require 'tempfile' +require 'pathname' + +# A support module for testing files. +module PuppetlabsSpec::Files + # This code exists only to support tests that run as root, pretty much. + # Once they have finally been eliminated this can all go... --daniel 2011-04-08 + def self.in_tmp(path) + tempdir = Dir.tmpdir + + Pathname.new(path).ascend do |dir| + return true if File.identical?(tempdir, dir) + end + + false + end + + def self.cleanup + $global_tempfiles ||= [] + while path = $global_tempfiles.pop do + fail "Not deleting tmpfile #{path} outside regular tmpdir" unless in_tmp(path) + + begin + FileUtils.rm_r path, :secure => true + rescue Errno::ENOENT + # nothing to do + end + end + end + + def make_absolute(path) + path = File.expand_path(path) + path[0] = 'c' if Puppet.features.microsoft_windows? + path + end + + def tmpfilename(name) + # Generate a temporary file, just for the name... + source = Tempfile.new(name) + path = source.path + source.close! + + # ...record it for cleanup, + $global_tempfiles ||= [] + $global_tempfiles << File.expand_path(path) + + # ...and bam. + path + end + + def tmpdir(name) + path = tmpfilename(name) + FileUtils.mkdir_p(path) + path + end +end diff --git a/spec/puppetlabs_spec/fixtures.rb b/spec/puppetlabs_spec/fixtures.rb new file mode 100644 index 00000000..9ce0a6b0 --- /dev/null +++ b/spec/puppetlabs_spec/fixtures.rb @@ -0,0 +1,49 @@ +# This module provides some helper methods to assist with fixtures. It's +# methods are designed to help when you have a conforming fixture layout so we +# get project consistency. +module PuppetlabsSpec::Fixtures + + # Returns the joined path of the global FIXTURE_DIR plus any path given to it + def fixtures(*rest) + File.join(PuppetlabsSpec::FIXTURE_DIR, *rest) + end + + # Returns the path to your relative fixture dir. So if your spec test is + # /spec/unit/facter/foo_spec.rb then your relative dir will be + # /spec/fixture/unit/facter/foo + def my_fixture_dir + callers = caller + while line = callers.shift do + next unless found = line.match(%r{/spec/(.*)_spec\.rb:}) + return fixtures(found[1]) + end + fail "sorry, I couldn't work out your path from the caller stack!" + end + + # Given a name, returns the full path of a file from your relative fixture + # dir as returned by my_fixture_dir. + def my_fixture(name) + file = File.join(my_fixture_dir, name) + unless File.readable? file then + fail "fixture '#{name}' for #{my_fixture_dir} is not readable" + end + return file + end + + # Return the contents of the file using read when given a name. Uses + # my_fixture to work out the relative path. + def my_fixture_read(name) + File.read(my_fixture(name)) + end + + # Provides a block mechanism for iterating across the files in your fixture + # area. + def my_fixtures(glob = '*', flags = 0) + files = Dir.glob(File.join(my_fixture_dir, glob), flags) + unless files.length > 0 then + fail "fixture '#{glob}' for #{my_fixture_dir} had no files!" + end + block_given? and files.each do |file| yield file end + files + end +end diff --git a/spec/puppetlabs_spec/matchers.rb b/spec/puppetlabs_spec/matchers.rb new file mode 100644 index 00000000..77f58033 --- /dev/null +++ b/spec/puppetlabs_spec/matchers.rb @@ -0,0 +1,87 @@ +require 'stringio' + +######################################################################## +# Backward compatibility for Jenkins outdated environment. +module RSpec + module Matchers + module BlockAliases + alias_method :to, :should unless method_defined? :to + alias_method :to_not, :should_not unless method_defined? :to_not + alias_method :not_to, :should_not unless method_defined? :not_to + end + end +end + + +######################################################################## +# Custom matchers... +RSpec::Matchers.define :have_matching_element do |expected| + match do |actual| + actual.any? { |item| item =~ expected } + end +end + + +RSpec::Matchers.define :exit_with do |expected| + actual = nil + match do |block| + begin + block.call + rescue SystemExit => e + actual = e.status + end + actual and actual == expected + end + failure_message_for_should do |block| + "expected exit with code #{expected} but " + + (actual.nil? ? " exit was not called" : "we exited with #{actual} instead") + end + failure_message_for_should_not do |block| + "expected that exit would not be called with #{expected}" + end + description do + "expect exit with #{expected}" + end +end + + +RSpec::Matchers.define :have_printed do |expected| + match do |block| + $stderr = $stdout = StringIO.new + + begin + block.call + ensure + $stdout.rewind + @actual = $stdout.read + + $stdout = STDOUT + $stderr = STDERR + end + + if @actual then + case expected + when String + @actual.include? expected + when Regexp + expected.match @actual + else + raise ArgumentError, "No idea how to match a #{@actual.class.name}" + end + end + end + + failure_message_for_should do |actual| + if actual.nil? then + "expected #{expected.inspect}, but nothing was printed" + else + "expected #{expected.inspect} to be printed; got:\n#{actual}" + end + end + + description do + "expect #{expected.inspect} to be printed" + end + + diffable +end diff --git a/spec/puppetlabs_spec_helper.rb b/spec/puppetlabs_spec_helper.rb new file mode 100644 index 00000000..ba2ae7ac --- /dev/null +++ b/spec/puppetlabs_spec_helper.rb @@ -0,0 +1,25 @@ +# Define the main module namespace for use by the helper modules +module PuppetlabsSpec + # FIXTURE_DIR represents the standard locations of all fixture data. Normally + # this represents /spec/fixtures. This will be used by the fixtures + # library to find relative fixture data. + FIXTURE_DIR = File.join(dir = File.expand_path(File.dirname(__FILE__)), \ + "fixtures") unless defined?(FIXTURE_DIR) +end + +# Require all necessary helper libraries so they can be used later +require 'puppetlabs_spec/files' +require 'puppetlabs_spec/fixtures' +require 'puppetlabs_spec/matchers' + +RSpec.configure do |config| + # Include PuppetlabsSpec helpers so they can be called at convenience + config.extend PuppetlabsSpec::Files + config.extend PuppetlabsSpec::Fixtures + config.include PuppetlabsSpec::Fixtures + + # This will cleanup any files that were created with tmpdir or tmpfile + config.after :each do + PuppetlabsSpec::Files.cleanup + end +end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb new file mode 100644 index 00000000..6b0d866d --- /dev/null +++ b/spec/spec_helper.rb @@ -0,0 +1,21 @@ +require 'puppet' +require 'mocha' +require 'rspec' +require 'rspec-puppet' +require 'rspec/expectations' +require 'puppetlabs_spec_helper' + +def param_value(subject, type, title, param) + subject.resource(type, title).send(:parameters)[param.to_sym] +end + +RSpec.configure do |c| + c.module_path = File.expand_path(File.join(File.dirname(__FILE__), 'fixtures/modules')) + # Using an empty site.pp file to avoid: https://github.com/rodjek/rspec-puppet/issues/15 + c.manifest_dir = File.expand_path(File.join(File.dirname(__FILE__), 'fixtures/manifests')) + # Use fixtures for config file mainly to support using our own hiera.yaml settings. + # Pending: https://github.com/rodjek/rspec-puppet/pull/21 + # c.config = File.expand_path(File.join(File.dirname(__FILE__), 'fixtures/puppet.conf')) + # + c.mock_with :mocha +end diff --git a/spec/unit/puppet/provider/pakages/npm_spec.rb b/spec/unit/puppet/provider/pakages/npm_spec.rb new file mode 100644 index 00000000..913cccd6 --- /dev/null +++ b/spec/unit/puppet/provider/pakages/npm_spec.rb @@ -0,0 +1,55 @@ +#!/usr/bin/env rspec +require 'spec_helper' + +describe Puppet::Type.type(:package).provider(:npm) do + before :each do + @provider.class.stubs(:optional_commands).with(:npm).returns "/usr/local/bin/npm" + @resource = Puppet::Type.type(:package).new( + :name => 'express', + :ensure => :present + ) + @provider = described_class.new(@resource) + end + + def self.it_should_respond_to(*actions) + actions.each do |action| + it "should respond to :#{action}" do + @provider.should respond_to(action) + end + end + end + + it_should_respond_to :install, :uninstall, :update, :query, :latest + + describe "when installing npm packages" do + it "should use package name by default" do + @provider.expects(:npm).with('install', '--global', 'express') + @provider.install + end + + describe "and a source is specified" do + it "should use the source instead of the gem name" do + @resource[:source] = "/tmp/express.tar.gz" + @provider.expects(:npm).with('install', '--global', '/tmp/express.tar.gz') + @provider.install + end + end + end + + describe "when npm packages are installed globally" do + it "should return a list of npm packages installed globally" do + @provider.class.stubs(:npm).with('list', '--json', '--global').returns(my_fixture_read('npm_global')) + @provider.class.instances.map {|p| p.properties}.should == [ + {:ensure => '1.1.15', :provider => 'npm', :name => 'npm' }, + {:ensure => '2.5.9' , :provider => 'npm', :name => 'express' }, + ] + end + end + +# describe "when no npm packages are installed globally" do +# it "should return nothing is installed" do +# @provider.class.stubs(:npm).with('list', '--json', '--global').returns("{}\n") +# @provider.class.instances.should == [] +# end +# end +end diff --git a/tests/init.pp b/tests/init.pp new file mode 100644 index 00000000..5fb5598d --- /dev/null +++ b/tests/init.pp @@ -0,0 +1 @@ +include nodejs diff --git a/tests/npm.pp b/tests/npm.pp index fbcd4f73..e38d28b4 100644 --- a/tests/npm.pp +++ b/tests/npm.pp @@ -1,6 +1,6 @@ include 'nodejs' nodejs::npm { '/tmp/npm:express': - ensure => present, + ensure => present, version => '2.5.9', }