diff --git a/acceptance/tests/modules/backwards_compatibility/13682_do_not_monkey_patch_old_pmt.rb b/acceptance/tests/modules/backwards_compatibility/13682_do_not_monkey_patch_old_pmt.rb new file mode 100644 index 000000000..f763246a0 --- /dev/null +++ b/acceptance/tests/modules/backwards_compatibility/13682_do_not_monkey_patch_old_pmt.rb @@ -0,0 +1,23 @@ +begin + test_name "puppet module should not monkey patch puppet-module" + step "Simulate the behavior of puppet-module" + puppet_module = <<-'PUPPET_MODULE' +ruby -e ' +module Puppet + class Module + module Tool + REPOSITORY_URL=1 + end + end +end +require "puppet" +puts Puppet.version +' +PUPPET_MODULE + on(master, puppet_module) do + # If we monkey patch the existing puppet-module Gem then Ruby will issue a + # warning about redefined constants. This is not a comprehensive test but + # it should catch the majority of regressions. + assert_no_match(/warning/, stderr) + end +end diff --git a/acceptance/tests/modules/upgrade/in_a_secondary_directory.rb b/acceptance/tests/modules/upgrade/in_a_secondary_directory.rb index d7b9d4df8..08ecf9151 100644 --- a/acceptance/tests/modules/upgrade/in_a_secondary_directory.rb +++ b/acceptance/tests/modules/upgrade/in_a_secondary_directory.rb @@ -1,32 +1,42 @@ begin test_name "puppet module upgrade (in a secondary directory)" step 'Setup' require 'resolv'; ip = Resolv.getaddress('forge-dev.puppetlabs.com') apply_manifest_on master, "host { 'forge.puppetlabs.com': ip => '#{ip}' }" -apply_manifest_on master, "file { ['/etc/puppet/modules', '/usr/share/puppet/modules']: ensure => directory, recurse => true, purge => true, force => true }" +apply_manifest_on master, <<-'MANIFEST1' +file { '/usr/share/puppet': + ensure => directory, +} +file { ['/etc/puppet/modules', '/usr/share/puppet/modules']: + ensure => directory, + recurse => true, + purge => true, + force => true, +} +MANIFEST1 on master, puppet("module install pmtacceptance-java --version 1.6.0 --target-dir /usr/share/puppet/modules") on master, puppet("module list") do assert_output <<-OUTPUT /etc/puppet/modules (no modules installed) /usr/share/puppet/modules ├── pmtacceptance-java (\e[0;36mv1.6.0\e[0m) └── pmtacceptance-stdlib (\e[0;36mv1.0.0\e[0m) OUTPUT end step "Upgrade a module that has a more recent version published" on master, puppet("module upgrade pmtacceptance-java") do assert_output <<-OUTPUT Preparing to upgrade 'pmtacceptance-java' ... Found 'pmtacceptance-java' (\e[0;36mv1.6.0\e[0m) in /usr/share/puppet/modules ... Downloading from http://forge.puppetlabs.com ... Upgrading -- do not interrupt ... /usr/share/puppet/modules └── pmtacceptance-java (\e[0;36mv1.6.0 -> v1.7.1\e[0m) OUTPUT end ensure step "Teardown" apply_manifest_on master, "host { 'forge.puppetlabs.com': ensure => absent }" apply_manifest_on master, "file { ['/etc/puppet/modules', '/usr/share/puppet/modules']: ensure => directory, recurse => true, purge => true, force => true }" end diff --git a/acceptance/tests/modules/upgrade/introducing_new_dependencies.rb b/acceptance/tests/modules/upgrade/introducing_new_dependencies.rb index 14e924da3..fb4188a88 100644 --- a/acceptance/tests/modules/upgrade/introducing_new_dependencies.rb +++ b/acceptance/tests/modules/upgrade/introducing_new_dependencies.rb @@ -1,36 +1,46 @@ begin test_name "puppet module upgrade (introducing new dependencies)" step 'Setup' require 'resolv'; ip = Resolv.getaddress('forge-dev.puppetlabs.com') apply_manifest_on master, "host { 'forge.puppetlabs.com': ip => '#{ip}' }" -apply_manifest_on master, "file { ['/etc/puppet/modules', '/usr/share/puppet/modules']: ensure => directory, recurse => true, purge => true, force => true }" +apply_manifest_on master, <<-'MANIFEST1' +file { '/usr/share/puppet': + ensure => directory, +} +file { ['/etc/puppet/modules', '/usr/share/puppet/modules']: + ensure => directory, + recurse => true, + purge => true, + force => true, +} +MANIFEST1 on master, puppet("module install pmtacceptance-stdlib --version 1.0.0") on master, puppet("module install pmtacceptance-java --version 1.7.0") on master, puppet("module install pmtacceptance-postgresql --version 0.0.2") on master, puppet("module list") do assert_output <<-OUTPUT /etc/puppet/modules ├── pmtacceptance-java (\e[0;36mv1.7.0\e[0m) ├── pmtacceptance-postgresql (\e[0;36mv0.0.2\e[0m) └── pmtacceptance-stdlib (\e[0;36mv1.0.0\e[0m) /usr/share/puppet/modules (no modules installed) OUTPUT end step "Upgrade a module to a version that introduces new dependencies" on master, puppet("module upgrade pmtacceptance-postgresql") do assert_output <<-OUTPUT Preparing to upgrade 'pmtacceptance-postgresql' ... Found 'pmtacceptance-postgresql' (\e[0;36mv0.0.2\e[0m) in /etc/puppet/modules ... Downloading from http://forge.puppetlabs.com ... Upgrading -- do not interrupt ... /etc/puppet/modules └─┬ pmtacceptance-postgresql (\e[0;36mv0.0.2 -> v1.0.0\e[0m) └── pmtacceptance-geordi (\e[0;36mv0.0.1\e[0m) OUTPUT end ensure step "Teardown" apply_manifest_on master, "host { 'forge.puppetlabs.com': ensure => absent }" apply_manifest_on master, "file { ['/etc/puppet/modules', '/usr/share/puppet/modules']: ensure => directory, recurse => true, purge => true, force => true }" end diff --git a/acceptance/tests/modules/upgrade/not_upgradable.rb b/acceptance/tests/modules/upgrade/not_upgradable.rb index dfb8ed5ff..051d93a24 100644 --- a/acceptance/tests/modules/upgrade/not_upgradable.rb +++ b/acceptance/tests/modules/upgrade/not_upgradable.rb @@ -1,82 +1,92 @@ begin test_name "puppet module upgrade (not upgradable)" step 'Setup' require 'resolv'; ip = Resolv.getaddress('forge-dev.puppetlabs.com') apply_manifest_on master, "host { 'forge.puppetlabs.com': ip => '#{ip}' }" -apply_manifest_on master, "file { ['/etc/puppet/modules', '/usr/share/puppet/modules']: ensure => directory, recurse => true, purge => true, force => true }" +apply_manifest_on master, <<-'MANIFEST1' +file { '/usr/share/puppet': + ensure => directory, +} +file { ['/etc/puppet/modules', '/usr/share/puppet/modules']: + ensure => directory, + recurse => true, + purge => true, + force => true, +} +MANIFEST1 apply_manifest_on master, <<-PP file { [ '/etc/puppet/modules/nginx', '/etc/puppet/modules/unicorns', ]: ensure => directory; '/etc/puppet/modules/unicorns/metadata.json': content => '{ "name": "notpmtacceptance/unicorns", "version": "0.0.3", "source": "", "author": "notpmtacceptance", "license": "MIT", "dependencies": [] }'; } PP on master, puppet("module install pmtacceptance-java --version 1.6.0") on master, puppet("module list") do assert_output <<-OUTPUT /etc/puppet/modules ├── nginx (\e[0;36m???\e[0m) ├── notpmtacceptance-unicorns (\e[0;36mv0.0.3\e[0m) ├── pmtacceptance-java (\e[0;36mv1.6.0\e[0m) └── pmtacceptance-stdlib (\e[0;36mv1.0.0\e[0m) /usr/share/puppet/modules (no modules installed) OUTPUT end step "Try to upgrade a module that is not installed" on master, puppet("module upgrade pmtacceptance-nginx"), :acceptable_exit_codes => [1] do assert_output <<-OUTPUT STDOUT> Preparing to upgrade 'pmtacceptance-nginx' ... STDERR> \e[1;31mError: Could not upgrade module 'pmtacceptance-nginx' STDERR> Module 'pmtacceptance-nginx' is not installed STDERR> Use `puppet module install` to install this module\e[0m OUTPUT end step "Try to upgrade a local module" on master, puppet("module upgrade nginx"), :acceptable_exit_codes => [1] do assert_output <<-OUTPUT STDOUT> Preparing to upgrade 'nginx' ... STDOUT> Found 'nginx' (\e[0;36m???\e[0m) in /etc/puppet/modules ... STDOUT> Downloading from http://forge.puppetlabs.com ... STDERR> \e[1;31mError: Could not upgrade module 'nginx' (??? -> latest) STDERR> Module 'nginx' does not exist on http://forge.puppetlabs.com\e[0m OUTPUT end step "Try to upgrade a module that doesn't exist" on master, puppet("module upgrade notpmtacceptance-unicorns"), :acceptable_exit_codes => [1] do assert_output <<-OUTPUT STDOUT> Preparing to upgrade 'notpmtacceptance-unicorns' ... STDOUT> Found 'notpmtacceptance-unicorns' (\e[0;36mv0.0.3\e[0m) in /etc/puppet/modules ... STDOUT> Downloading from http://forge.puppetlabs.com ... STDERR> \e[1;31mError: Could not upgrade module 'notpmtacceptance-unicorns' (v0.0.3 -> latest) STDERR> Module 'notpmtacceptance-unicorns' does not exist on http://forge.puppetlabs.com\e[0m OUTPUT end step "Try to upgrade an installed module to a version that doesn't exist" on master, puppet("module upgrade pmtacceptance-java --version 2.0.0"), :acceptable_exit_codes => [1] do assert_output <<-OUTPUT STDOUT> Preparing to upgrade 'pmtacceptance-java' ... STDOUT> Found 'pmtacceptance-java' (\e[0;36mv1.6.0\e[0m) in /etc/puppet/modules ... STDOUT> Downloading from http://forge.puppetlabs.com ... STDERR> \e[1;31mError: Could not upgrade module 'pmtacceptance-java' (v1.6.0 -> v2.0.0) STDERR> No version matching '2.0.0' exists on http://forge.puppetlabs.com\e[0m OUTPUT end ensure step "Teardown" apply_manifest_on master, "host { 'forge.puppetlabs.com': ensure => absent }" apply_manifest_on master, "file { ['/etc/puppet/modules', '/usr/share/puppet/modules']: ensure => directory, recurse => true, purge => true, force => true }" end diff --git a/acceptance/tests/modules/upgrade/that_was_installed_twice.rb b/acceptance/tests/modules/upgrade/that_was_installed_twice.rb index c42952634..9ce8d3c99 100644 --- a/acceptance/tests/modules/upgrade/that_was_installed_twice.rb +++ b/acceptance/tests/modules/upgrade/that_was_installed_twice.rb @@ -1,47 +1,57 @@ begin test_name "puppet module upgrade (that was installed twice)" step 'Setup' require 'resolv'; ip = Resolv.getaddress('forge-dev.puppetlabs.com') apply_manifest_on master, "host { 'forge.puppetlabs.com': ip => '#{ip}' }" -apply_manifest_on master, "file { ['/etc/puppet/modules', '/usr/share/puppet/modules']: ensure => directory, recurse => true, purge => true, force => true }" +apply_manifest_on master, <<-'MANIFEST1' +file { '/usr/share/puppet': + ensure => directory, +} +file { ['/etc/puppet/modules', '/usr/share/puppet/modules']: + ensure => directory, + recurse => true, + purge => true, + force => true, +} +MANIFEST1 on master, puppet("module install pmtacceptance-java --version 1.6.0 --modulepath /etc/puppet/modules") on master, puppet("module install pmtacceptance-java --version 1.7.0 --modulepath /usr/share/puppet/modules") on master, puppet("module list") do assert_output <<-OUTPUT /etc/puppet/modules ├── pmtacceptance-java (\e[0;36mv1.6.0\e[0m) └── pmtacceptance-stdlib (\e[0;36mv1.0.0\e[0m) /usr/share/puppet/modules ├── pmtacceptance-java (\e[0;36mv1.7.0\e[0m) └── pmtacceptance-stdlib (\e[0;36mv1.0.0\e[0m) OUTPUT end step "Try to upgrade a module that exists multiple locations in the module path" on master, puppet("module upgrade pmtacceptance-java"), :acceptable_exit_codes => [1] do assert_output <<-OUTPUT STDOUT> Preparing to upgrade 'pmtacceptance-java' ... STDERR> \e[1;31mError: Could not upgrade module 'pmtacceptance-java' STDERR> Module 'pmtacceptance-java' appears multiple places in the module path STDERR> 'pmtacceptance-java' (v1.6.0) was found in /etc/puppet/modules STDERR> 'pmtacceptance-java' (v1.7.0) was found in /usr/share/puppet/modules STDERR> Use the `--modulepath` option to limit the search to specific directories\e[0m OUTPUT end step "Upgrade a module that exists multiple locations by restricting the --modulepath" on master, puppet("module upgrade pmtacceptance-java --modulepath /etc/puppet/modules") do assert_output <<-OUTPUT Preparing to upgrade 'pmtacceptance-java' ... Found 'pmtacceptance-java' (\e[0;36mv1.6.0\e[0m) in /etc/puppet/modules ... Downloading from http://forge.puppetlabs.com ... Upgrading -- do not interrupt ... /etc/puppet/modules └── pmtacceptance-java (\e[0;36mv1.6.0 -> v1.7.1\e[0m) OUTPUT end ensure step "Teardown" apply_manifest_on master, "host { 'forge.puppetlabs.com': ensure => absent }" apply_manifest_on master, "file { ['/etc/puppet/modules', '/usr/share/puppet/modules']: ensure => directory, recurse => true, purge => true, force => true }" end diff --git a/acceptance/tests/modules/upgrade/to_a_specific_version.rb b/acceptance/tests/modules/upgrade/to_a_specific_version.rb index 77889d7d3..6d34b0da1 100644 --- a/acceptance/tests/modules/upgrade/to_a_specific_version.rb +++ b/acceptance/tests/modules/upgrade/to_a_specific_version.rb @@ -1,44 +1,54 @@ begin test_name "puppet module upgrade (to a specific version)" step 'Setup' require 'resolv'; ip = Resolv.getaddress('forge-dev.puppetlabs.com') apply_manifest_on master, "host { 'forge.puppetlabs.com': ip => '#{ip}' }" -apply_manifest_on master, "file { ['/etc/puppet/modules', '/usr/share/puppet/modules']: ensure => directory, recurse => true, purge => true, force => true }" +apply_manifest_on master, <<-'MANIFEST1' +file { '/usr/share/puppet': + ensure => directory, +} +file { ['/etc/puppet/modules', '/usr/share/puppet/modules']: + ensure => directory, + recurse => true, + purge => true, + force => true, +} +MANIFEST1 on master, puppet("module install pmtacceptance-java --version 1.6.0") on master, puppet("module list") do assert_output <<-OUTPUT /etc/puppet/modules ├── pmtacceptance-java (\e[0;36mv1.6.0\e[0m) └── pmtacceptance-stdlib (\e[0;36mv1.0.0\e[0m) /usr/share/puppet/modules (no modules installed) OUTPUT end step "Upgrade a module to a specific (greater) version" on master, puppet("module upgrade pmtacceptance-java --version 1.7.0") do assert_output <<-OUTPUT Preparing to upgrade 'pmtacceptance-java' ... Found 'pmtacceptance-java' (\e[0;36mv1.6.0\e[0m) in /etc/puppet/modules ... Downloading from http://forge.puppetlabs.com ... Upgrading -- do not interrupt ... /etc/puppet/modules └── pmtacceptance-java (\e[0;36mv1.6.0 -> v1.7.0\e[0m) OUTPUT end step "Upgrade a module to a specific (lesser) version" on master, puppet("module upgrade pmtacceptance-java --version 1.6.0") do assert_output <<-OUTPUT Preparing to upgrade 'pmtacceptance-java' ... Found 'pmtacceptance-java' (\e[0;36mv1.7.0\e[0m) in /etc/puppet/modules ... Downloading from http://forge.puppetlabs.com ... Upgrading -- do not interrupt ... /etc/puppet/modules └── pmtacceptance-java (\e[0;36mv1.7.0 -> v1.6.0\e[0m) OUTPUT end ensure step "Teardown" apply_manifest_on master, "host { 'forge.puppetlabs.com': ensure => absent }" apply_manifest_on master, "file { ['/etc/puppet/modules', '/usr/share/puppet/modules']: ensure => directory, recurse => true, purge => true, force => true }" end diff --git a/acceptance/tests/modules/upgrade/to_installed_version.rb b/acceptance/tests/modules/upgrade/to_installed_version.rb index 496749ba1..39ad579c1 100644 --- a/acceptance/tests/modules/upgrade/to_installed_version.rb +++ b/acceptance/tests/modules/upgrade/to_installed_version.rb @@ -1,81 +1,91 @@ begin test_name "puppet module upgrade (to installed version)" step 'Setup' require 'resolv'; ip = Resolv.getaddress('forge-dev.puppetlabs.com') apply_manifest_on master, "host { 'forge.puppetlabs.com': ip => '#{ip}' }" -apply_manifest_on master, "file { ['/etc/puppet/modules', '/usr/share/puppet/modules']: ensure => directory, recurse => true, purge => true, force => true }" +apply_manifest_on master, <<-'MANIFEST1' +file { '/usr/share/puppet': + ensure => directory, +} +file { ['/etc/puppet/modules', '/usr/share/puppet/modules']: + ensure => directory, + recurse => true, + purge => true, + force => true, +} +MANIFEST1 on master, puppet("module install pmtacceptance-java --version 1.6.0") on master, puppet("module list") do assert_output <<-OUTPUT /etc/puppet/modules ├── pmtacceptance-java (\e[0;36mv1.6.0\e[0m) └── pmtacceptance-stdlib (\e[0;36mv1.0.0\e[0m) /usr/share/puppet/modules (no modules installed) OUTPUT end step "Try to upgrade a module to the current version" on master, puppet("module upgrade pmtacceptance-java --version 1.6.x"), :acceptable_exit_codes => [0] do assert_output <<-OUTPUT STDOUT> Preparing to upgrade 'pmtacceptance-java' ... STDOUT> Found 'pmtacceptance-java' (\e[0;36mv1.6.0\e[0m) in /etc/puppet/modules ... STDOUT> Downloading from http://forge.puppetlabs.com ... STDERR> \e[1;31mError: Could not upgrade module 'pmtacceptance-java' (v1.6.0 -> v1.6.x) STDERR> The installed version is already the best fit for the current dependencies STDERR> You specified 'pmtacceptance-java' (v1.6.x) STDERR> Use `puppet module install --force` to re-install this module\e[0m OUTPUT end step "Upgrade a module to the current version with --force" on master, puppet("module upgrade pmtacceptance-java --version 1.6.x --force") do assert_output <<-OUTPUT Preparing to upgrade 'pmtacceptance-java' ... Found 'pmtacceptance-java' (\e[0;36mv1.6.0\e[0m) in /etc/puppet/modules ... Downloading from http://forge.puppetlabs.com ... Upgrading -- do not interrupt ... /etc/puppet/modules └── pmtacceptance-java (\e[0;36mv1.6.0 -> v1.6.0\e[0m) OUTPUT end step "Upgrade to the latest version" on master, puppet("module upgrade pmtacceptance-java") do assert_output <<-OUTPUT Preparing to upgrade 'pmtacceptance-java' ... Found 'pmtacceptance-java' (\e[0;36mv1.6.0\e[0m) in /etc/puppet/modules ... Downloading from http://forge.puppetlabs.com ... Upgrading -- do not interrupt ... /etc/puppet/modules └── pmtacceptance-java (\e[0;36mv1.6.0 -> v1.7.1\e[0m) OUTPUT end step "Try to upgrade a module to the latest version with the latest version installed" on master, puppet("module upgrade pmtacceptance-java"), :acceptable_exit_codes => [0] do assert_output <<-OUTPUT STDOUT> Preparing to upgrade 'pmtacceptance-java' ... STDOUT> Found 'pmtacceptance-java' (\e[0;36mv1.7.1\e[0m) in /etc/puppet/modules ... STDOUT> Downloading from http://forge.puppetlabs.com ... STDERR> \e[1;31mError: Could not upgrade module 'pmtacceptance-java' (v1.7.1 -> latest: v1.7.1) STDERR> The installed version is already the latest version STDERR> Use `puppet module install --force` to re-install this module\e[0m OUTPUT end step "Upgrade a module to the latest version with --force" on master, puppet("module upgrade pmtacceptance-java --force") do assert_output <<-OUTPUT Preparing to upgrade 'pmtacceptance-java' ... Found 'pmtacceptance-java' (\e[0;36mv1.7.1\e[0m) in /etc/puppet/modules ... Downloading from http://forge.puppetlabs.com ... Upgrading -- do not interrupt ... /etc/puppet/modules └── pmtacceptance-java (\e[0;36mv1.7.1 -> v1.7.1\e[0m) OUTPUT end ensure step "Teardown" apply_manifest_on master, "host { 'forge.puppetlabs.com': ensure => absent }" apply_manifest_on master, "file { ['/etc/puppet/modules', '/usr/share/puppet/modules']: ensure => directory, recurse => true, purge => true, force => true }" end diff --git a/acceptance/tests/modules/upgrade/with_constraints_on_it.rb b/acceptance/tests/modules/upgrade/with_constraints_on_it.rb index 0a5238acc..bb88a0b2f 100644 --- a/acceptance/tests/modules/upgrade/with_constraints_on_it.rb +++ b/acceptance/tests/modules/upgrade/with_constraints_on_it.rb @@ -1,48 +1,58 @@ begin test_name "puppet module upgrade (with constraints on it)" step 'Setup' require 'resolv'; ip = Resolv.getaddress('forge-dev.puppetlabs.com') apply_manifest_on master, "host { 'forge.puppetlabs.com': ip => '#{ip}' }" -apply_manifest_on master, "file { ['/etc/puppet/modules', '/usr/share/puppet/modules']: ensure => directory, recurse => true, purge => true, force => true }" +apply_manifest_on master, <<-'MANIFEST1' +file { '/usr/share/puppet': + ensure => directory, +} +file { ['/etc/puppet/modules', '/usr/share/puppet/modules']: + ensure => directory, + recurse => true, + purge => true, + force => true, +} +MANIFEST1 on master, puppet("module install pmtacceptance-java --version 1.7.0") on master, puppet("module install pmtacceptance-apollo") on master, puppet("module list") do assert_output <<-OUTPUT /etc/puppet/modules ├── pmtacceptance-apollo (\e[0;36mv0.0.1\e[0m) ├── pmtacceptance-java (\e[0;36mv1.7.0\e[0m) └── pmtacceptance-stdlib (\e[0;36mv1.0.0\e[0m) /usr/share/puppet/modules (no modules installed) OUTPUT end step "Upgrade a version-constrained module that has an upgrade" on master, puppet("module upgrade pmtacceptance-java") do assert_output <<-OUTPUT Preparing to upgrade 'pmtacceptance-java' ... Found 'pmtacceptance-java' (\e[0;36mv1.7.0\e[0m) in /etc/puppet/modules ... Downloading from http://forge.puppetlabs.com ... Upgrading -- do not interrupt ... /etc/puppet/modules └── pmtacceptance-java (\e[0;36mv1.7.0 -> v1.7.1\e[0m) OUTPUT end step "Try to upgrade a version-constrained module that has no upgrade" on master, puppet("module upgrade pmtacceptance-stdlib"), :acceptable_exit_codes => [0] do assert_output <<-OUTPUT STDOUT> Preparing to upgrade 'pmtacceptance-stdlib' ... STDOUT> Found 'pmtacceptance-stdlib' (\e[0;36mv1.0.0\e[0m) in /etc/puppet/modules ... STDOUT> Downloading from http://forge.puppetlabs.com ... STDERR> \e[1;31mError: Could not upgrade module 'pmtacceptance-stdlib' (v1.0.0 -> best: v1.0.0) STDERR> The installed version is already the best fit for the current dependencies STDERR> 'pmtacceptance-apollo' (v0.0.1) requires 'pmtacceptance-stdlib' (>= 1.0.0) STDERR> 'pmtacceptance-java' (v1.7.1) requires 'pmtacceptance-stdlib' (v1.0.0) STDERR> Use `puppet module install --force` to re-install this module\e[0m OUTPUT end ensure step "Teardown" apply_manifest_on master, "host { 'forge.puppetlabs.com': ensure => absent }" apply_manifest_on master, "file { ['/etc/puppet/modules', '/usr/share/puppet/modules']: ensure => directory, recurse => true, purge => true, force => true }" end diff --git a/acceptance/tests/modules/upgrade/with_constraints_on_its_dependencies.rb b/acceptance/tests/modules/upgrade/with_constraints_on_its_dependencies.rb index 03f131d8c..07a9a8933 100644 --- a/acceptance/tests/modules/upgrade/with_constraints_on_its_dependencies.rb +++ b/acceptance/tests/modules/upgrade/with_constraints_on_its_dependencies.rb @@ -1,90 +1,100 @@ begin test_name "puppet module upgrade (with constraints on its dependencies)" step 'Setup' require 'resolv'; ip = Resolv.getaddress('forge-dev.puppetlabs.com') apply_manifest_on master, "host { 'forge.puppetlabs.com': ip => '#{ip}' }" -apply_manifest_on master, "file { ['/etc/puppet/modules', '/usr/share/puppet/modules']: ensure => directory, recurse => true, purge => true, force => true }" +apply_manifest_on master, <<-'MANIFEST1' +file { '/usr/share/puppet': + ensure => directory, +} +file { ['/etc/puppet/modules', '/usr/share/puppet/modules']: + ensure => directory, + recurse => true, + purge => true, + force => true, +} +MANIFEST1 apply_manifest_on master, <<-PP file { [ '/etc/puppet/modules/unicorns', ]: ensure => directory; '/etc/puppet/modules/unicorns/metadata.json': content => '{ "name": "notpmtacceptance/unicorns", "version": "0.0.3", "source": "", "author": "notpmtacceptance", "license": "MIT", "dependencies": [ { "name": "pmtacceptance/stdlib", "version_requirement": "0.0.2" } ] }'; } PP on master, puppet("module install pmtacceptance-stdlib --version 0.0.2") on master, puppet("module install pmtacceptance-java --version 1.6.0") on master, puppet("module list") do assert_output <<-OUTPUT /etc/puppet/modules ├── notpmtacceptance-unicorns (\e[0;36mv0.0.3\e[0m) ├── pmtacceptance-java (\e[0;36mv1.6.0\e[0m) └── pmtacceptance-stdlib (\e[0;36mv0.0.2\e[0m) /usr/share/puppet/modules (no modules installed) OUTPUT end step "Try to upgrade a module with constraints on its dependencies that cannot be met" on master, puppet("module upgrade pmtacceptance-java"), :acceptable_exit_codes => [1] do assert_output <<-OUTPUT STDOUT> Preparing to upgrade 'pmtacceptance-java' ... STDOUT> Found 'pmtacceptance-java' (\e[0;36mv1.6.0\e[0m) in /etc/puppet/modules ... STDOUT> Downloading from http://forge.puppetlabs.com ... STDERR> \e[1;31mError: Could not upgrade module 'pmtacceptance-java' (v1.6.0 -> latest: v1.7.1) STDERR> No version of 'pmtacceptance-stdlib' will satisfy dependencies STDERR> 'notpmtacceptance-unicorns' (v0.0.3) requires 'pmtacceptance-stdlib' (v0.0.2) STDERR> 'pmtacceptance-java' (v1.7.1) requires 'pmtacceptance-stdlib' (v1.0.0) STDERR> Use `puppet module upgrade --ignore-dependencies` to upgrade only this module\e[0m OUTPUT end step "Relax constraints" on master, puppet("module uninstall notpmtacceptance-unicorns") on master, puppet("module list") do assert_output <<-OUTPUT /etc/puppet/modules ├── pmtacceptance-java (\e[0;36mv1.6.0\e[0m) └── pmtacceptance-stdlib (\e[0;36mv0.0.2\e[0m) /usr/share/puppet/modules (no modules installed) OUTPUT end step "Upgrade a single module, ignoring its dependencies" on master, puppet("module upgrade pmtacceptance-java --version 1.7.0 --ignore-dependencies") do assert_output <<-OUTPUT Preparing to upgrade 'pmtacceptance-java' ... Found 'pmtacceptance-java' (\e[0;36mv1.6.0\e[0m) in /etc/puppet/modules ... Downloading from http://forge.puppetlabs.com ... Upgrading -- do not interrupt ... /etc/puppet/modules └── pmtacceptance-java (\e[0;36mv1.6.0 -> v1.7.0\e[0m) OUTPUT end step "Upgrade a module with constraints on its dependencies that can be met" on master, puppet("module upgrade pmtacceptance-java") do assert_output <<-OUTPUT Preparing to upgrade 'pmtacceptance-java' ... Found 'pmtacceptance-java' (\e[0;36mv1.7.0\e[0m) in /etc/puppet/modules ... Downloading from http://forge.puppetlabs.com ... Upgrading -- do not interrupt ... /etc/puppet/modules └─┬ pmtacceptance-java (\e[0;36mv1.7.0 -> v1.7.1\e[0m) └── pmtacceptance-stdlib (\e[0;36mv0.0.2 -> v1.0.0\e[0m) OUTPUT end ensure step "Teardown" apply_manifest_on master, "host { 'forge.puppetlabs.com': ensure => absent }" apply_manifest_on master, "file { ['/etc/puppet/modules', '/usr/share/puppet/modules']: ensure => directory, recurse => true, purge => true, force => true }" end diff --git a/acceptance/tests/modules/upgrade/with_local_changes.rb b/acceptance/tests/modules/upgrade/with_local_changes.rb index 3d75a317d..b782dbc9d 100644 --- a/acceptance/tests/modules/upgrade/with_local_changes.rb +++ b/acceptance/tests/modules/upgrade/with_local_changes.rb @@ -1,53 +1,63 @@ begin test_name "puppet module upgrade (with local changes)" step 'Setup' require 'resolv'; ip = Resolv.getaddress('forge-dev.puppetlabs.com') apply_manifest_on master, "host { 'forge.puppetlabs.com': ip => '#{ip}' }" -apply_manifest_on master, "file { ['/etc/puppet/modules', '/usr/share/puppet/modules']: ensure => directory, recurse => true, purge => true, force => true }" +apply_manifest_on master, <<-'MANIFEST1' +file { '/usr/share/puppet': + ensure => directory, +} +file { ['/etc/puppet/modules', '/usr/share/puppet/modules']: + ensure => directory, + recurse => true, + purge => true, + force => true, +} +MANIFEST1 on master, puppet("module install pmtacceptance-java --version 1.6.0") on master, puppet("module list") do assert_output <<-OUTPUT /etc/puppet/modules ├── pmtacceptance-java (\e[0;36mv1.6.0\e[0m) └── pmtacceptance-stdlib (\e[0;36mv1.0.0\e[0m) /usr/share/puppet/modules (no modules installed) OUTPUT end apply_manifest_on master, <<-PP file { '/etc/puppet/modules/java/README': content => "I CHANGE MY READMES"; '/etc/puppet/modules/java/NEWFILE': content => "I don't exist.'"; } PP step "Try to upgrade a module with local changes" on master, puppet("module upgrade pmtacceptance-java"), :acceptable_exit_codes => [1] do assert_output <<-OUTPUT STDOUT> Preparing to upgrade 'pmtacceptance-java' ... STDOUT> Found 'pmtacceptance-java' (\e[0;36mv1.6.0\e[0m) in /etc/puppet/modules ... STDERR> \e[1;31mError: Could not upgrade module 'pmtacceptance-java' (v1.6.0 -> latest) STDERR> Installed module has had changes made locally STDERR> Use `puppet module upgrade --force` to upgrade this module anyway\e[0m OUTPUT end on master, '[[ "$(cat /etc/puppet/modules/java/README)" == "I CHANGE MY READMES" ]]' on master, '[ -f /etc/puppet/modules/java/NEWFILE ]' step "Upgrade a module with local changes with --force" on master, puppet("module upgrade pmtacceptance-java --force") do assert_output <<-OUTPUT Preparing to upgrade 'pmtacceptance-java' ... Found 'pmtacceptance-java' (\e[0;36mv1.6.0\e[0m) in /etc/puppet/modules ... Downloading from http://forge.puppetlabs.com ... Upgrading -- do not interrupt ... /etc/puppet/modules └── pmtacceptance-java (\e[0;36mv1.6.0 -> v1.7.1\e[0m) OUTPUT end on master, '[[ "$(cat /etc/puppet/modules/java/README)" != "I CHANGE MY READMES" ]]' on master, '[ ! -f /etc/puppet/modules/java/NEWFILE ]' ensure step "Teardown" apply_manifest_on master, "host { 'forge.puppetlabs.com': ensure => absent }" apply_manifest_on master, "file { ['/etc/puppet/modules', '/usr/share/puppet/modules']: ensure => directory, recurse => true, purge => true, force => true }" end diff --git a/acceptance/tests/modules/upgrade/with_scattered_dependencies.rb b/acceptance/tests/modules/upgrade/with_scattered_dependencies.rb index 53612d217..c5e9f9a9e 100644 --- a/acceptance/tests/modules/upgrade/with_scattered_dependencies.rb +++ b/acceptance/tests/modules/upgrade/with_scattered_dependencies.rb @@ -1,38 +1,48 @@ begin test_name "puppet module upgrade (with scattered dependencies)" step 'Setup' require 'resolv'; ip = Resolv.getaddress('forge-dev.puppetlabs.com') apply_manifest_on master, "host { 'forge.puppetlabs.com': ip => '#{ip}' }" -apply_manifest_on master, "file { ['/etc/puppet/modules', '/usr/share/puppet/modules']: ensure => directory, recurse => true, purge => true, force => true }" +apply_manifest_on master, <<-'MANIFEST1' + file { '/usr/share/puppet': + ensure => directory, + } + file { ['/etc/puppet/modules', '/usr/share/puppet/modules']: + ensure => directory, + recurse => true, + purge => true, + force => true, + } +MANIFEST1 on master, puppet("module install pmtacceptance-stdlib --version 0.0.2 --target-dir /usr/share/puppet/modules") on master, puppet("module install pmtacceptance-java --version 1.6.0 --target-dir /etc/puppet/modules --ignore-dependencies") on master, puppet("module install pmtacceptance-postgresql --version 0.0.1 --target-dir /etc/puppet/modules --ignore-dependencies") on master, puppet("module list") do assert_output <<-OUTPUT /etc/puppet/modules ├── pmtacceptance-java (\e[0;36mv1.6.0\e[0m) └── pmtacceptance-postgresql (\e[0;36mv0.0.1\e[0m) /usr/share/puppet/modules └── pmtacceptance-stdlib (\e[0;36mv0.0.2\e[0m) OUTPUT end step "Upgrade a module that has a more recent version published" on master, puppet("module upgrade pmtacceptance-postgresql --version 0.0.2") do assert_output <<-OUTPUT Preparing to upgrade 'pmtacceptance-postgresql' ... Found 'pmtacceptance-postgresql' (\e[0;36mv0.0.1\e[0m) in /etc/puppet/modules ... Downloading from http://forge.puppetlabs.com ... Upgrading -- do not interrupt ... /etc/puppet/modules └─┬ pmtacceptance-postgresql (\e[0;36mv0.0.1 -> v0.0.2\e[0m) ├─┬ pmtacceptance-java (\e[0;36mv1.6.0 -> v1.7.0\e[0m) │ └── pmtacceptance-stdlib (\e[0;36mv0.0.2 -> v1.0.0\e[0m) [/usr/share/puppet/modules] └── pmtacceptance-stdlib (\e[0;36mv0.0.2 -> v1.0.0\e[0m) [/usr/share/puppet/modules] OUTPUT end ensure step "Teardown" apply_manifest_on master, "host { 'forge.puppetlabs.com': ensure => absent }" apply_manifest_on master, "file { ['/etc/puppet/modules', '/usr/share/puppet/modules']: ensure => directory, recurse => true, purge => true, force => true }" end diff --git a/acceptance/tests/modules/upgrade/with_update_available.rb b/acceptance/tests/modules/upgrade/with_update_available.rb index 0109f81e2..5e61c6ec9 100644 --- a/acceptance/tests/modules/upgrade/with_update_available.rb +++ b/acceptance/tests/modules/upgrade/with_update_available.rb @@ -1,32 +1,42 @@ begin test_name "puppet module upgrade (with update available)" step 'Setup' require 'resolv'; ip = Resolv.getaddress('forge-dev.puppetlabs.com') apply_manifest_on master, "host { 'forge.puppetlabs.com': ip => '#{ip}' }" -apply_manifest_on master, "file { ['/etc/puppet/modules', '/usr/share/puppet/modules']: ensure => directory, recurse => true, purge => true, force => true }" +apply_manifest_on master, <<-'MANIFEST1' + file { '/usr/share/puppet': + ensure => directory, + } + file { ['/etc/puppet/modules', '/usr/share/puppet/modules']: + ensure => directory, + recurse => true, + purge => true, + force => true, + } +MANIFEST1 on master, puppet("module install pmtacceptance-java --version 1.6.0") on master, puppet("module list") do assert_output <<-OUTPUT /etc/puppet/modules ├── pmtacceptance-java (\e[0;36mv1.6.0\e[0m) └── pmtacceptance-stdlib (\e[0;36mv1.0.0\e[0m) /usr/share/puppet/modules (no modules installed) OUTPUT end step "Upgrade a module that has a more recent version published" on master, puppet("module upgrade pmtacceptance-java") do assert_output <<-OUTPUT Preparing to upgrade 'pmtacceptance-java' ... Found 'pmtacceptance-java' (\e[0;36mv1.6.0\e[0m) in /etc/puppet/modules ... Downloading from http://forge.puppetlabs.com ... Upgrading -- do not interrupt ... /etc/puppet/modules └── pmtacceptance-java (\e[0;36mv1.6.0 -> v1.7.1\e[0m) OUTPUT end ensure step "Teardown" apply_manifest_on master, "host { 'forge.puppetlabs.com': ensure => absent }" apply_manifest_on master, "file { ['/etc/puppet/modules', '/usr/share/puppet/modules']: ensure => directory, recurse => true, purge => true, force => true }" end diff --git a/autotest/images/fail.png b/autotest/images/fail.png index 2d5539442..41f49055a 100644 Binary files a/autotest/images/fail.png and b/autotest/images/fail.png differ diff --git a/autotest/images/pass.png b/autotest/images/pass.png index cbe537186..7167a61d4 100644 Binary files a/autotest/images/pass.png and b/autotest/images/pass.png differ diff --git a/autotest/images/pending.png b/autotest/images/pending.png index b8cf9a391..7c412e6fb 100644 Binary files a/autotest/images/pending.png and b/autotest/images/pending.png differ diff --git a/conf/osx/preflight b/conf/osx/preflight index 8220d02a3..39dc3d3eb 100755 --- a/conf/osx/preflight +++ b/conf/osx/preflight @@ -1,24 +1,24 @@ #!/bin/bash # # Make sure that old puppet cruft is removed # This also allows us to downgrade puppet as # it's more likely that installing old versions # over new will cause issues. # # ${3} is the destination volume so that this works correctly # when being installed to volumes other than the current OS. /bin/rm -Rf "${3}{SITELIBDIR}/puppet" /bin/rm -Rf "${3}{SITELIBDIR}/puppet.rb" # remove old doc files /bin/rm -Rf "${3}/usr/share/doc/puppet" # In puppet 0.24.x these executables lived in bindir, but in 0.25.x they # have been moved to sbindir. This cleans out old ones before installing. /bin/rm -Rf "${3}{BINDIR}/puppetca" /bin/rm -Rf "${3}{BINDIR}/puppetd" /bin/rm -Rf "${3}{BINDIR}/puppetmasterd" /bin/rm -Rf "${3}{BINDIR}/puppetqd" -/bin/rm -Rf "${3}{BINDIR}/puppetrun" \ No newline at end of file +/bin/rm -Rf "${3}{BINDIR}/puppetrun" diff --git a/lib/puppet/face/module/build.rb b/lib/puppet/face/module/build.rb index eec482ed9..3fcbe275f 100644 --- a/lib/puppet/face/module/build.rb +++ b/lib/puppet/face/module/build.rb @@ -1,37 +1,37 @@ Puppet::Face.define(:module, '1.0.0') do action(:build) do summary "Build a module release package." description <<-EOT Prepares a local module for release on the Puppet Forge by building a ready-to-upload archive file. This action uses the Modulefile in the module directory to set metadata used by the Forge. See for more about writing modulefiles. After being built, the release archive file can be found in the module's `pkg` directory. EOT returns "Pathname object representing the path to the release archive." examples <<-EOT Build a module release: $ puppet module build puppetlabs-apache notice: Building /Users/kelseyhightower/puppetlabs-apache for release puppetlabs-apache/pkg/puppetlabs-apache-0.0.1.tar.gz EOT arguments "" when_invoked do |path, options| - Puppet::Module::Tool::Applications::Builder.run(path, options) + Puppet::ModuleTool::Applications::Builder.run(path, options) end when_rendering :console do |return_value| # Get the string representation of the Pathname object. return_value.to_s end end end diff --git a/lib/puppet/face/module/changes.rb b/lib/puppet/face/module/changes.rb index 602e423dc..9f7bcb41e 100644 --- a/lib/puppet/face/module/changes.rb +++ b/lib/puppet/face/module/changes.rb @@ -1,38 +1,38 @@ Puppet::Face.define(:module, '1.0.0') do action(:changes) do summary "Show modified files of an installed module." description <<-EOT Shows any files in a module that have been modified since it was installed. This action compares the files on disk to the md5 checksums included in the module's metadata. EOT returns "Array of strings representing paths of modified files." examples <<-EOT Show modified files of an installed module: $ puppet module changes /etc/puppet/modules/vcsrepo/ warning: 1 files modified lib/puppet/provider/vcsrepo.rb EOT arguments "" when_invoked do |path, options| - root_path = Puppet::Module::Tool.find_module_root(path) - Puppet::Module::Tool::Applications::Checksummer.run(root_path, options) + root_path = Puppet::ModuleTool.find_module_root(path) + Puppet::ModuleTool::Applications::Checksummer.run(root_path, options) end when_rendering :console do |return_value| if return_value.empty? Puppet.notice "No modified files" else Puppet.warning "#{return_value.size} files modified" end return_value.map do |changed_file| "#{changed_file}" end.join("\n") end end end diff --git a/lib/puppet/face/module/generate.rb b/lib/puppet/face/module/generate.rb index 8f1622cd6..fce1c6257 100644 --- a/lib/puppet/face/module/generate.rb +++ b/lib/puppet/face/module/generate.rb @@ -1,42 +1,42 @@ Puppet::Face.define(:module, '1.0.0') do action(:generate) do summary "Generate boilerplate for a new module." description <<-EOT Generates boilerplate for a new module by creating the directory structure and files recommended for the Puppet community's best practices. A module may need additional directories beyond this boilerplate if it provides plugins, files, or templates. EOT returns "Array of Pathname objects representing paths of generated files." examples <<-EOT Generate a new module in the current directory: $ puppet module generate puppetlabs-ssh notice: Generating module at /Users/kelseyhightower/puppetlabs-ssh puppetlabs-ssh puppetlabs-ssh/tests puppetlabs-ssh/tests/init.pp puppetlabs-ssh/spec puppetlabs-ssh/spec/spec_helper.rb puppetlabs-ssh/spec/spec.opts puppetlabs-ssh/README puppetlabs-ssh/Modulefile puppetlabs-ssh/metadata.json puppetlabs-ssh/manifests puppetlabs-ssh/manifests/init.pp EOT arguments "" when_invoked do |name, options| - Puppet::Module::Tool::Applications::Generator.run(name, options) + Puppet::ModuleTool::Applications::Generator.run(name, options) end when_rendering :console do |return_value| return_value.map {|f| f.to_s }.join("\n") end end end diff --git a/lib/puppet/face/module/install.rb b/lib/puppet/face/module/install.rb index c3e0754eb..db0a0eb91 100644 --- a/lib/puppet/face/module/install.rb +++ b/lib/puppet/face/module/install.rb @@ -1,173 +1,173 @@ # encoding: UTF-8 Puppet::Face.define(:module, '1.0.0') do action(:install) do summary "Install a module from a repository or release archive." description <<-EOT Installs a module from the Puppet Forge, from a release archive file on-disk, or from a private Forge-like repository. The specified module will be installed into the directory specified with the `--target-dir` option, which defaults to #{Puppet.settings[:modulepath].split(File::PATH_SEPARATOR).first}. EOT returns "Pathname object representing the path to the installed module." examples <<-EOT Install a module: $ puppet module install puppetlabs-vcsrepo Preparing to install into /etc/puppet/modules ... Downloading from http://forge.puppetlabs.com ... Installing -- do not interrupt ... /etc/puppet/modules └── puppetlabs-vcsrepo (v0.0.4) Install a module to a specific environment: $ puppet module install puppetlabs-vcsrepo --environment development Preparing to install into /etc/puppet/environments/development/modules ... Downloading from http://forge.puppetlabs.com ... Installing -- do not interrupt ... /etc/puppet/environments/development/modules └── puppetlabs-vcsrepo (v0.0.4) Install a specific module version: $ puppet module install puppetlabs-vcsrepo -v 0.0.4 Preparing to install into /etc/puppet/modules ... Downloading from http://forge.puppetlabs.com ... Installing -- do not interrupt ... /etc/puppet/modules └── puppetlabs-vcsrepo (v0.0.4) Install a module into a specific directory: $ puppet module install puppetlabs-vcsrepo --target-dir=/usr/share/puppet/modules Preparing to install into /usr/share/puppet/modules ... Downloading from http://forge.puppetlabs.com ... Installing -- do not interrupt ... /usr/share/puppet/modules └── puppetlabs-vcsrepo (v0.0.4) Install a module into a specific directory and check for dependencies in other directories: $ puppet module install puppetlabs-vcsrepo --target-dir=/usr/share/puppet/modules --modulepath /etc/puppet/modules Preparing to install into /usr/share/puppet/modules ... Downloading from http://forge.puppetlabs.com ... Installing -- do not interrupt ... /usr/share/puppet/modules └── puppetlabs-vcsrepo (v0.0.4) Install a module from a release archive: $ puppet module install puppetlabs-vcsrepo-0.0.4.tar.gz Preparing to install into /etc/puppet/modules ... Downloading from http://forge.puppetlabs.com ... Installing -- do not interrupt ... /etc/puppet/modules └── puppetlabs-vcsrepo (v0.0.4) Install a module from a release archive and ignore dependencies: $ puppet module install puppetlabs-vcsrepo-0.0.4.tar.gz --ignore-dependencies Preparing to install into /etc/puppet/modules ... Installing -- do not interrupt ... /etc/puppet/modules └── puppetlabs-vcsrepo (v0.0.4) EOT arguments "" option "--force", "-f" do summary "Force overwrite of existing module, if any." description <<-EOT Force overwrite of existing module, if any. EOT end option "--target-dir DIR", "-i DIR" do summary "The directory into which modules are installed." description <<-EOT The directory into which modules are installed; defaults to the first directory in the modulepath. Specifying this option will change the installation directory, and will use the existing modulepath when checking for dependencies. If you wish to check a different set of directories for dependencies, you must also use the `--environment` or `--modulepath` options. EOT end option "--ignore-dependencies" do summary "Do not attempt to install dependencies" description <<-EOT Do not attempt to install dependencies. EOT end option "--modulepath MODULEPATH" do default_to { Puppet.settings[:modulepath] } summary "Which directories to look for modules in" description <<-EOT The list of directories to check for modules. When installing a new module, this setting determines where the module tool will look for its dependencies. If the `--target dir` option is not specified, the first directory in the modulepath will also be used as the install directory. When installing a module into an environment whose modulepath is specified in puppet.conf, you can use the `--environment` option instead, and its modulepath will be used automatically. This setting should be a list of directories separated by the path separator character. (The path separator is `:` on Unix-like platforms and `;` on Windows.) EOT end option "--version VER", "-v VER" do summary "Module version to install." description <<-EOT Module version to install; can be an exact version or a requirement string, eg '>= 1.0.3'. Defaults to latest version. EOT end option "--environment NAME" do default_to { "production" } summary "The target environment to install modules into." description <<-EOT The target environment to install modules into. Only applicable if multiple environments (with different modulepaths) have been configured in puppet.conf. EOT end when_invoked do |name, options| sep = File::PATH_SEPARATOR if options[:target_dir] options[:modulepath] = "#{options[:target_dir]}#{sep}#{options[:modulepath]}" end Puppet.settings[:modulepath] = options[:modulepath] options[:target_dir] = Puppet.settings[:modulepath].split(sep).first Puppet.notice "Preparing to install into #{options[:target_dir]} ..." - Puppet::Module::Tool::Applications::Installer.run(name, options) + Puppet::ModuleTool::Applications::Installer.run(name, options) end when_rendering :console do |return_value, name, options| if return_value[:result] == :failure Puppet.err(return_value[:error][:multiline]) exit 1 else - tree = Puppet::Module::Tool.build_tree(return_value[:installed_modules], return_value[:install_dir]) + tree = Puppet::ModuleTool.build_tree(return_value[:installed_modules], return_value[:install_dir]) return_value[:install_dir] + "\n" + - Puppet::Module::Tool.format_tree(tree) + Puppet::ModuleTool.format_tree(tree) end end end end diff --git a/lib/puppet/face/module/list.rb b/lib/puppet/face/module/list.rb index e3acbd9ae..ebd0fc6cd 100644 --- a/lib/puppet/face/module/list.rb +++ b/lib/puppet/face/module/list.rb @@ -1,285 +1,285 @@ # encoding: UTF-8 Puppet::Face.define(:module, '1.0.0') do action(:list) do summary "List installed modules" description <<-HEREDOC Lists the installed puppet modules. By default, this action scans the modulepath from puppet.conf's `[main]` block; use the --modulepath option to change which directories are scanned. The output of this action includes information from the module's metadata, including version numbers and unmet module dependencies. HEREDOC returns "hash of paths to module objects" option "--environment NAME" do default_to { "production" } summary "Which environments' modules to list" description <<-EOT Which environments' modules to list. EOT end option "--modulepath MODULEPATH" do summary "Which directories to look for modules in" description <<-EOT Which directories to look for modules in; use the system path separator character (`:` on Unix-like systems and `;` on Windows) to specify multiple directories. EOT end option "--tree" do summary "Whether to show dependencies as a tree view" end examples <<-EOT List installed modules: $ puppet module list /etc/puppet/modules ├── bodepd-create_resources (v0.0.1) ├── puppetlabs-bacula (v0.0.2) ├── puppetlabs-mysql (v0.0.1) ├── puppetlabs-sqlite (v0.0.1) └── puppetlabs-stdlib (v2.2.1) /usr/share/puppet/modules (no modules installed) List installed modules in a tree view: $ puppet module list --tree /etc/puppet/modules └─┬ puppetlabs-bacula (v0.0.2) ├── puppetlabs-stdlib (v2.2.1) ├─┬ puppetlabs-mysql (v0.0.1) │ └── bodepd-create_resources (v0.0.1) └── puppetlabs-sqlite (v0.0.1) /usr/share/puppet/modules (no modules installed) List installed modules from a specified environment: $ puppet module list --environment production /etc/puppet/modules ├── bodepd-create_resources (v0.0.1) ├── puppetlabs-bacula (v0.0.2) ├── puppetlabs-mysql (v0.0.1) ├── puppetlabs-sqlite (v0.0.1) └── puppetlabs-stdlib (v2.2.1) /usr/share/puppet/modules (no modules installed) List installed modules from a specified modulepath: $ puppet module list --modulepath /usr/share/puppet/modules /usr/share/puppet/modules (no modules installed) EOT when_invoked do |options| Puppet[:modulepath] = options[:modulepath] if options[:modulepath] environment = Puppet::Node::Environment.new(options[:environment]) environment.modules_by_path end when_rendering :console do |modules_by_path, options| output = '' Puppet[:modulepath] = options[:modulepath] if options[:modulepath] environment = Puppet::Node::Environment.new(options[:production]) error_types = { :non_semantic_version => { :title => "Non semantic version dependency" }, :missing => { :title => "Missing dependency" }, :version_mismatch => { :title => "Module '%s' (v%s) fails to meet some dependencies:" } } @unmet_deps = {} error_types.each_key do |type| @unmet_deps[type] = Hash.new do |hash, key| hash[key] = { :errors => [], :parent => nil } end end # Prepare the unmet dependencies for display on the console. environment.modules.sort_by {|mod| mod.name}.each do |mod| unmet_grouped = Hash.new { |h,k| h[k] = [] } unmet_grouped = mod.unmet_dependencies.inject(unmet_grouped) do |acc, dep| acc[dep[:reason]] << dep acc end unmet_grouped.each do |type, deps| unless deps.empty? unmet_grouped[type].sort_by { |dep| dep[:name] }.each do |dep| dep_name = dep[:name].gsub('/', '-') installed_version = dep[:mod_details][:installed_version] version_constraint = dep[:version_constraint] parent_name = dep[:parent][:name].gsub('/', '-') parent_version = dep[:parent][:version] msg = "'#{parent_name}' (#{parent_version})" msg << " requires '#{dep_name}' (#{version_constraint})" @unmet_deps[type][dep[:name]][:errors] << msg @unmet_deps[type][dep[:name]][:parent] = { :name => dep[:parent][:name], :version => parent_version } @unmet_deps[type][dep[:name]][:version] = installed_version end end end end # Display unmet dependencies by category. error_display_order = [:non_semantic_version, :version_mismatch, :missing] error_display_order.each do |type| unless @unmet_deps[type].empty? @unmet_deps[type].keys.sort_by {|dep| dep }.each do |dep| name = dep.gsub('/', '-') title = error_types[type][:title] errors = @unmet_deps[type][dep][:errors] version = @unmet_deps[type][dep][:version] msg = case type when :version_mismatch title % [name, version] + "\n" when :non_semantic_version title + " '#{name}' (v#{version}):\n" else title + " '#{name}':\n" end errors.each { |error_string| msg << " #{error_string}\n" } Puppet.warning msg.chomp end end end environment.modulepath.each do |path| modules = modules_by_path[path] no_mods = modules.empty? ? ' (no modules installed)' : '' output << "#{path}#{no_mods}\n" if options[:tree] # The modules with fewest things depending on them will be the # parent of the tree. Can't assume to start with 0 dependencies # since dependencies may be cyclical. modules_by_num_requires = modules.sort_by {|m| m.required_by.size} @seen = {} tree = list_build_tree(modules_by_num_requires, [], nil, :label_unmet => true, :path => path, :label_invalid => false) else tree = [] modules.sort_by { |mod| mod.forge_name or mod.name }.each do |mod| tree << list_build_node(mod, path, :label_unmet => false, :path => path, :label_invalid => true) end end - output << Puppet::Module::Tool.format_tree(tree) + output << Puppet::ModuleTool.format_tree(tree) end output end end # Prepare a list of module objects and their dependencies for print in a # tree view. # # Returns an Array of Hashes # # Example: # # [ # { # :text => "puppetlabs-bacula (v0.0.2)", # :dependencies=> [ # { :text => "puppetlabs-stdlib (v2.2.1)", :dependencies => [] }, # { # :text => "puppetlabs-mysql (v1.0.0)" # :dependencies => [ # { # :text => "bodepd-create_resources (v0.0.1)", # :dependencies => [] # } # ] # }, # { :text => "puppetlabs-sqlite (v0.0.1)", :dependencies => [] }, # ] # } # ] # - # When the above data structure is passed to Puppet::Module::Tool.build_tree + # When the above data structure is passed to Puppet::ModuleTool.build_tree # you end up with something like this: # # /etc/puppet/modules # └─┬ puppetlabs-bacula (v0.0.2) # ├── puppetlabs-stdlib (v2.2.1) # ├─┬ puppetlabs-mysql (v1.0.0) # │ └── bodepd-create_resources (v0.0.1) # └── puppetlabs-sqlite (v0.0.1) # def list_build_tree(list, ancestors=[], parent=nil, params={}) list.map do |mod| next if @seen[(mod.forge_name or mod.name)] node = list_build_node(mod, parent, params) @seen[(mod.forge_name or mod.name)] = true unless ancestors.include?(mod) node[:dependencies] ||= [] missing_deps = mod.unmet_dependencies.select do |dep| dep[:reason] == :missing end missing_deps.map do |mis_mod| str = "#{colorize(:bg_red, 'UNMET DEPENDENCY')} #{mis_mod[:name].gsub('/', '-')} " str << "(#{colorize(:cyan, mis_mod[:version_constraint])})" node[:dependencies] << { :text => str } end node[:dependencies] += list_build_tree(mod.dependencies_as_modules, ancestors + [mod], mod, params) end node end.compact end # Prepare a module object for print in a tree view. Each node in the tree # must be a Hash in the following format: # # { :text => "puppetlabs-mysql (v1.0.0)" } # # The value of a module's :text is affected by three (3) factors: the format # of the tree, it's dependency status, and the location in the modulepath # relative to it's parent. # # Returns a Hash # def list_build_node(mod, parent, params) str = '' str << (mod.forge_name ? mod.forge_name.gsub('/', '-') : mod.name) str << ' (' + colorize(:cyan, mod.version ? "v#{mod.version}" : '???') + ')' unless File.dirname(mod.path) == params[:path] str << " [#{File.dirname(mod.path)}]" end if @unmet_deps[:version_mismatch].include?(mod.forge_name) if params[:label_invalid] str << ' ' + colorize(:red, 'invalid') elsif parent.respond_to?(:forge_name) unmet_parent = @unmet_deps[:version_mismatch][mod.forge_name][:parent] if (unmet_parent[:name] == parent.forge_name && unmet_parent[:version] == "v#{parent.version}") str << ' ' + colorize(:red, 'invalid') end end end { :text => str } end end diff --git a/lib/puppet/face/module/search.rb b/lib/puppet/face/module/search.rb index 169f64090..7632f56de 100644 --- a/lib/puppet/face/module/search.rb +++ b/lib/puppet/face/module/search.rb @@ -1,88 +1,88 @@ require 'puppet/util/terminal' Puppet::Face.define(:module, '1.0.0') do action(:search) do summary "Search a repository for a module." description <<-EOT Searches a repository for modules whose names, descriptions, or keywords match the provided search term. EOT returns "Array of module metadata hashes" examples <<-EOT Search the default repository for a module: $ puppet module search puppetlabs NAME DESCRIPTION AUTHOR KEYWORDS bacula This is a generic Apache module @puppetlabs backups EOT arguments "" when_invoked do |term, options| - Puppet::Module::Tool::Applications::Searcher.run(term, options) + Puppet::ModuleTool::Applications::Searcher.run(term, options) end when_rendering :console do |results, term, options| return "No results found for '#{term}'." if results.empty? padding = ' ' headers = { 'full_name' => 'NAME', 'desc' => 'DESCRIPTION', 'author' => 'AUTHOR', 'tag_list' => 'KEYWORDS', } min_widths = Hash[ *headers.map { |k,v| [k, v.length] }.flatten ] min_widths['full_name'] = min_widths['author'] = 12 min_width = min_widths.inject(0) { |sum,pair| sum += pair.last } + (padding.length * (headers.length - 1)) terminal_width = [Puppet::Util::Terminal.width, min_width].max columns = results.inject(min_widths) do |hash, result| { 'full_name' => [ hash['full_name'], result['full_name'].length ].max, 'desc' => [ hash['desc'], result['desc'].length ].max, 'author' => [ hash['author'], "@#{result['author']}".length ].max, 'tag_list' => [ hash['tag_list'], result['tag_list'].join(' ').length ].max, } end flex_width = terminal_width - columns['full_name'] - columns['author'] - (padding.length * (headers.length - 1)) tag_lists = results.map { |r| r['tag_list'] } while (columns['tag_list'] > flex_width / 3) longest_tag_list = tag_lists.sort_by { |tl| tl.join(' ').length }.last break if [ [], [term] ].include? longest_tag_list longest_tag_list.delete(longest_tag_list.sort_by { |t| t == term ? -1 : t.length }.last) columns['tag_list'] = tag_lists.map { |tl| tl.join(' ').length }.max end columns['tag_list'] = [ flex_width / 3, tag_lists.map { |tl| tl.join(' ').length }.max, ].max columns['desc'] = flex_width - columns['tag_list'] format = %w{full_name desc author tag_list}.map do |k| "%-#{ [ columns[k], min_widths[k] ].max }s" end.join(padding) + "\n" highlight = proc do |s| s = s.gsub(term, colorize(:green, term)) s = s.gsub(term.gsub('/', '-'), colorize(:green, term.gsub('/', '-'))) if term =~ /\// s end format % [ headers['full_name'], headers['desc'], headers['author'], headers['tag_list'] ] + results.map do |match| name, desc, author, keywords = %w{full_name desc author tag_list}.map { |k| match[k] } desc = desc[0...(columns['desc'] - 3)] + '...' if desc.length > columns['desc'] highlight[format % [ name.sub('/', '-'), desc, "@#{author}", [keywords].flatten.join(' ') ]] end.join end end end diff --git a/lib/puppet/face/module/uninstall.rb b/lib/puppet/face/module/uninstall.rb index 1effa6c38..81fc4347c 100644 --- a/lib/puppet/face/module/uninstall.rb +++ b/lib/puppet/face/module/uninstall.rb @@ -1,86 +1,86 @@ Puppet::Face.define(:module, '1.0.0') do action(:uninstall) do summary "Uninstall a puppet module." description <<-EOT Uninstalls a puppet module from the modulepath (or a specific target directory). EOT returns "Hash of module objects representing uninstalled modules and related errors." examples <<-EOT Uninstall a module: $ puppet module uninstall puppetlabs-ssh Removed /etc/puppet/modules/ssh (v1.0.0) Uninstall a module from a specific directory: $ puppet module uninstall puppetlabs-ssh --modulepath /usr/share/puppet/modules Removed /usr/share/puppet/modules/ssh (v1.0.0) Uninstall a module from a specific environment: $ puppet module uninstall puppetlabs-ssh --environment development Removed /etc/puppet/environments/development/modules/ssh (v1.0.0) Uninstall a specific version of a module: $ puppet module uninstall puppetlabs-ssh --version 2.0.0 Removed /etc/puppet/modules/ssh (v2.0.0) EOT arguments "" option "--force", "-f" do summary "Force uninstall of an installed module." description <<-EOT Force the uninstall of an installed module even if there are local changes or the possibility of causing broken dependencies. EOT end option "--environment NAME" do default_to { "production" } summary "The target environment to uninstall modules from." description <<-EOT The target environment to uninstall modules from. EOT end option "--version=" do summary "The version of the module to uninstall" description <<-EOT The version of the module to uninstall. When using this option, a module matching the specified version must be installed or else an error is raised. EOT end option "--modulepath=" do summary "The target directory to search for modules." description <<-EOT The target directory to search for modules. EOT end when_invoked do |name, options| Puppet[:modulepath] = options[:modulepath] if options[:modulepath] name = name.gsub('/', '-') Puppet.notice "Preparing to uninstall '#{name}'" << (options[:version] ? " (#{colorize(:cyan, options[:version].sub(/^(?=\d)/, 'v'))})" : '') << " ..." - Puppet::Module::Tool::Applications::Uninstaller.run(name, options) + Puppet::ModuleTool::Applications::Uninstaller.run(name, options) end when_rendering :console do |return_value| if return_value[:result] == :failure Puppet.err(return_value[:error][:multiline]) exit 1 else mod = return_value[:affected_modules].first "Removed '#{return_value[:module_name]}'" << (mod.version ? " (#{colorize(:cyan, mod.version.to_s.sub(/^(?=\d)/, 'v'))})" : '') << " from #{mod.modulepath}" end end end end diff --git a/lib/puppet/face/module/upgrade.rb b/lib/puppet/face/module/upgrade.rb index e62a7c535..9ad4fcc89 100644 --- a/lib/puppet/face/module/upgrade.rb +++ b/lib/puppet/face/module/upgrade.rb @@ -1,84 +1,84 @@ # encoding: UTF-8 Puppet::Face.define(:module, '1.0.0') do action(:upgrade) do summary "Upgrade a puppet module." description <<-EOT Upgrades a puppet module. EOT returns "Hash" examples <<-EOT upgrade an installed module to the latest version $ puppet module upgrade puppetlabs-apache /etc/puppet/modules └── puppetlabs-apache (v1.0.0 -> v2.4.0) upgrade an installed module to a specific version $ puppet module upgrade puppetlabs-apache --version 2.1.0 /etc/puppet/modules └── puppetlabs-apache (v1.0.0 -> v2.1.0) upgrade an installed module for a specific environment $ puppet module upgrade puppetlabs-apache --environment test /usr/share/puppet/environments/test/modules └── puppetlabs-apache (v1.0.0 -> v2.4.0) EOT arguments "" option "--force", "-f" do summary "Force upgrade of an installed module." description <<-EOT Force the upgrade of an installed module even if there are local changes or the possibility of causing broken dependencies. EOT end option "--ignore-dependencies" do summary "Do not attempt to install dependencies." description <<-EOT Do not attempt to install dependencies EOT end option "--environment NAME" do default_to { "production" } summary "The target environment to search for modules." description <<-EOT The target environment to search for modules. EOT end option "--version=" do summary "The version of the module to upgrade to." description <<-EOT The version of the module to upgrade to. EOT end when_invoked do |name, options| name = name.gsub('/', '-') Puppet.notice "Preparing to upgrade '#{name}' ..." - Puppet::Module::Tool::Applications::Upgrader.new(name, options).run + Puppet::ModuleTool::Applications::Upgrader.new(name, options).run end when_rendering :console do |return_value| if return_value[:result] == :failure Puppet.err(return_value[:error][:multiline]) exit 1 elsif return_value[:result] == :noop Puppet.err(return_value[:error][:multiline]) exit 0 else - tree = Puppet::Module::Tool.build_tree(return_value[:affected_modules], return_value[:base_dir]) + tree = Puppet::ModuleTool.build_tree(return_value[:affected_modules], return_value[:base_dir]) return_value[:base_dir] + "\n" + - Puppet::Module::Tool.format_tree(tree) + Puppet::ModuleTool.format_tree(tree) end end end end diff --git a/lib/puppet/indirector/facts/network_device.rb b/lib/puppet/indirector/facts/network_device.rb index c9bac803e..a84b46157 100644 --- a/lib/puppet/indirector/facts/network_device.rb +++ b/lib/puppet/indirector/facts/network_device.rb @@ -1,25 +1,25 @@ require 'puppet/node/facts' require 'puppet/indirector/code' class Puppet::Node::Facts::NetworkDevice < Puppet::Indirector::Code desc "Retrieve facts from a network device." # Look a device's facts up through the current device. def find(request) result = Puppet::Node::Facts.new(request.key, Puppet::Util::NetworkDevice.current.facts) result.add_local_facts result.stringify result.downcase_if_necessary result end def destroy(facts) raise Puppet::DevError, "You cannot destroy facts in the code store; it is only used for getting facts from a remote device" end def save(facts) raise Puppet::DevError, "You cannot save facts to the code store; it is only used for getting facts from a remote device" end -end \ No newline at end of file +end diff --git a/lib/puppet/module.rb b/lib/puppet/module.rb index db27abbc4..04319cc18 100644 --- a/lib/puppet/module.rb +++ b/lib/puppet/module.rb @@ -1,314 +1,314 @@ require 'puppet/util/logging' require 'semver' require 'puppet/module_tool/applications' # Support for modules class Puppet::Module class Error < Puppet::Error; end class MissingModule < Error; end class IncompatibleModule < Error; end class UnsupportedPlatform < Error; end class IncompatiblePlatform < Error; end class MissingMetadata < Error; end class InvalidName < Error; end include Puppet::Util::Logging TEMPLATES = "templates" FILES = "files" MANIFESTS = "manifests" PLUGINS = "plugins" FILETYPES = [MANIFESTS, FILES, TEMPLATES, PLUGINS] # Find and return the +module+ that +path+ belongs to. If +path+ is # absolute, or if there is no module whose name is the first component # of +path+, return +nil+ def self.find(modname, environment = nil) return nil unless modname Puppet::Node::Environment.new(environment).module(modname) end attr_reader :name, :environment attr_writer :environment attr_accessor :dependencies, :forge_name attr_accessor :source, :author, :version, :license, :puppetversion, :summary, :description, :project_page def has_metadata? return false unless metadata_file return false unless FileTest.exist?(metadata_file) metadata = PSON.parse File.read(metadata_file) return metadata.is_a?(Hash) && !metadata.keys.empty? end def initialize(name, options = {}) @name = name @path = options[:path] assert_validity if options[:environment].is_a?(Puppet::Node::Environment) @environment = options[:environment] else @environment = Puppet::Node::Environment.new(options[:environment]) end load_metadata if has_metadata? validate_puppet_version end FILETYPES.each do |type| # A boolean method to let external callers determine if # we have files of a given type. define_method(type +'?') do return false unless path return false unless FileTest.exist?(subpath(type)) return true end # A method for returning a given file of a given type. # e.g., file = mod.manifest("my/manifest.pp") # # If the file name is nil, then the base directory for the # file type is passed; this is used for fileserving. define_method(type.to_s.sub(/s$/, '')) do |file| return nil unless path # If 'file' is nil then they're asking for the base path. # This is used for things like fileserving. if file full_path = File.join(subpath(type), file) else full_path = subpath(type) end return nil unless FileTest.exist?(full_path) return full_path end end def exist? ! path.nil? end # Find the first 'files' directory. This is used by the XMLRPC fileserver. def file_directory subpath("files") end def license_file return @license_file if defined?(@license_file) return @license_file = nil unless path @license_file = File.join(path, "License") end def load_metadata data = PSON.parse File.read(metadata_file) @forge_name = data['name'].gsub('-', '/') if data['name'] [:source, :author, :version, :license, :puppetversion, :dependencies].each do |attr| unless value = data[attr.to_s] unless attr == :puppetversion raise MissingMetadata, "No #{attr} module metadata provided for #{self.name}" end end # NOTICE: The fallback to `versionRequirement` is something we'd like to # not have to support, but we have a reasonable number of releases that # don't use `version_requirement`. When we can deprecate this, we should. if attr == :dependencies value.tap do |dependencies| dependencies.each do |dep| dep['version_requirement'] ||= dep['versionRequirement'] || '>= 0.0.0' end end end send(attr.to_s + "=", value) end end # Return the list of manifests matching the given glob pattern, # defaulting to 'init.{pp,rb}' for empty modules. def match_manifests(rest) pat = File.join(path, MANIFESTS, rest || 'init') [manifest("init.pp"),manifest("init.rb")].compact + Dir. glob(pat + (File.extname(pat).empty? ? '.{pp,rb}' : '')). reject { |f| FileTest.directory?(f) } end def metadata_file return @metadata_file if defined?(@metadata_file) return @metadata_file = nil unless path @metadata_file = File.join(path, "metadata.json") end # Find this module in the modulepath. def path @path ||= environment.modulepath.collect { |path| File.join(path, name) }.find { |d| FileTest.directory?(d) } end def modulepath File.dirname(path) if path end # Find all plugin directories. This is used by the Plugins fileserving mount. def plugin_directory subpath("plugins") end def supports(name, version = nil) @supports ||= [] @supports << [name, version] end def to_s result = "Module #{name}" result += "(#{path})" if path result end def dependencies_as_modules dependent_modules = [] dependencies and dependencies.each do |dep| author, dep_name = dep["name"].split('/') found_module = environment.module(dep_name) dependent_modules << found_module if found_module end dependent_modules end def required_by environment.module_requirements[self.forge_name] || {} end def has_local_changes? - changes = Puppet::Module::Tool::Applications::Checksummer.run(path) + changes = Puppet::ModuleTool::Applications::Checksummer.run(path) !changes.empty? end def local_changes - Puppet::Module::Tool::Applications::Checksummer.run(path) + Puppet::ModuleTool::Applications::Checksummer.run(path) end # Identify and mark unmet dependencies. A dependency will be marked unmet # for the following reasons: # # * not installed and is thus considered missing # * installed and does not meet the version requirements for this module # * installed and doesn't use semantic versioning # # Returns a list of hashes representing the details of an unmet dependency. # # Example: # # [ # { # :reason => :missing, # :name => 'puppetlabs-mysql', # :version_constraint => 'v0.0.1', # :mod_details => { # :installed_version => '0.0.1' # } # :parent => { # :name => 'puppetlabs-bacula', # :version => 'v1.0.0' # } # } # ] # def unmet_dependencies unmet_dependencies = [] return unmet_dependencies unless dependencies dependencies.each do |dependency| forge_name = dependency['name'] version_string = dependency['version_requirement'] || '>= 0.0.0' dep_mod = begin environment.module_by_forge_name(forge_name) rescue => e nil end error_details = { :name => forge_name, :version_constraint => version_string.gsub(/^(?=\d)/, "v"), :parent => { :name => self.forge_name, :version => self.version.gsub(/^(?=\d)/, "v") }, :mod_details => { :installed_version => dep_mod.nil? ? nil : dep_mod.version } } unless dep_mod error_details[:reason] = :missing unmet_dependencies << error_details next end if version_string begin required_version_semver_range = SemVer[version_string] actual_version_semver = SemVer.new(dep_mod.version) rescue ArgumentError error_details[:reason] = :non_semantic_version unmet_dependencies << error_details next end unless required_version_semver_range.include? actual_version_semver error_details[:reason] = :version_mismatch unmet_dependencies << error_details next end end end unmet_dependencies end def validate_puppet_version return unless puppetversion and puppetversion != Puppet.version raise IncompatibleModule, "Module #{self.name} is only compatible with Puppet version #{puppetversion}, not #{Puppet.version}" end private def subpath(type) return File.join(path, type) unless type.to_s == "plugins" backward_compatible_plugins_dir end def backward_compatible_plugins_dir if dir = File.join(path, "plugins") and FileTest.exist?(dir) Puppet.warning "using the deprecated 'plugins' directory for ruby extensions; please move to 'lib'" return dir else return File.join(path, "lib") end end def assert_validity raise InvalidName, "Invalid module name #{name}; module names must be alphanumeric (plus '-'), not '#{name}'" unless name =~ /^[-\w]+$/ end def ==(other) self.name == other.name && self.version == other.version && self.path == other.path && self.environment == other.environment end end diff --git a/lib/puppet/module_tool.rb b/lib/puppet/module_tool.rb index a05ac8e3a..3516ba713 100644 --- a/lib/puppet/module_tool.rb +++ b/lib/puppet/module_tool.rb @@ -1,101 +1,99 @@ # encoding: UTF-8 # Load standard libraries require 'pathname' require 'fileutils' require 'puppet/util/colors' # Define tool module Puppet - class Module - module Tool - extend Puppet::Util::Colors + module ModuleTool + extend Puppet::Util::Colors - # Directory and names that should not be checksummed. - ARTIFACTS = ['pkg', /^\./, /^~/, /^#/, 'coverage', 'metadata.json', 'REVISION'] - FULL_MODULE_NAME_PATTERN = /\A([^-\/|.]+)[-|\/](.+)\z/ - REPOSITORY_URL = Puppet.settings[:module_repository] + # Directory and names that should not be checksummed. + ARTIFACTS = ['pkg', /^\./, /^~/, /^#/, 'coverage', 'metadata.json', 'REVISION'] + FULL_MODULE_NAME_PATTERN = /\A([^-\/|.]+)[-|\/](.+)\z/ + REPOSITORY_URL = Puppet.settings[:module_repository] - # Is this a directory that shouldn't be checksummed? - # - # TODO: Should this be part of Checksums? - # TODO: Rename this method to reflect it's purpose? - # TODO: Shouldn't this be used when building packages too? - def self.artifact?(path) - case File.basename(path) - when *ARTIFACTS - true - else - false - end + # Is this a directory that shouldn't be checksummed? + # + # TODO: Should this be part of Checksums? + # TODO: Rename this method to reflect it's purpose? + # TODO: Shouldn't this be used when building packages too? + def self.artifact?(path) + case File.basename(path) + when *ARTIFACTS + true + else + false end + end - # Return the +username+ and +modname+ for a given +full_module_name+, or raise an - # ArgumentError if the argument isn't parseable. - def self.username_and_modname_from(full_module_name) - if matcher = full_module_name.match(FULL_MODULE_NAME_PATTERN) - return matcher.captures - else - raise ArgumentError, "Not a valid full name: #{full_module_name}" - end + # Return the +username+ and +modname+ for a given +full_module_name+, or raise an + # ArgumentError if the argument isn't parseable. + def self.username_and_modname_from(full_module_name) + if matcher = full_module_name.match(FULL_MODULE_NAME_PATTERN) + return matcher.captures + else + raise ArgumentError, "Not a valid full name: #{full_module_name}" end + end - def self.find_module_root(path) - for dir in [path, Dir.pwd].compact - if File.exist?(File.join(dir, 'Modulefile')) - return dir - end + def self.find_module_root(path) + for dir in [path, Dir.pwd].compact + if File.exist?(File.join(dir, 'Modulefile')) + return dir end - raise ArgumentError, "Could not find a valid module at #{path ? path.inspect : 'current directory'}" end + raise ArgumentError, "Could not find a valid module at #{path ? path.inspect : 'current directory'}" + end - # Builds a formatted tree from a list of node hashes containing +:text+ - # and +:dependencies+ keys. - def self.format_tree(nodes, level = 0) - str = '' - nodes.each_with_index do |node, i| - last_node = nodes.length - 1 == i - deps = node[:dependencies] || [] - - str << (indent = " " * level) - str << (last_node ? "└" : "├") - str << "─" - str << (deps.empty? ? "─" : "┬") - str << " #{node[:text]}\n" + # Builds a formatted tree from a list of node hashes containing +:text+ + # and +:dependencies+ keys. + def self.format_tree(nodes, level = 0) + str = '' + nodes.each_with_index do |node, i| + last_node = nodes.length - 1 == i + deps = node[:dependencies] || [] - branch = format_tree(deps, level + 1) - branch.gsub!(/^#{indent} /, indent + '│') unless last_node - str << branch - end + str << (indent = " " * level) + str << (last_node ? "└" : "├") + str << "─" + str << (deps.empty? ? "─" : "┬") + str << " #{node[:text]}\n" - return str + branch = format_tree(deps, level + 1) + branch.gsub!(/^#{indent} /, indent + '│') unless last_node + str << branch end - def self.build_tree(mods, dir) - mods.each do |mod| - version_string = mod[:version][:vstring].sub(/^(?!v)/, 'v') + return str + end - if mod[:action] == :upgrade - previous_version = mod[:previous_version].sub(/^(?!v)/, 'v') - version_string = "#{previous_version} -> #{version_string}" - end + def self.build_tree(mods, dir) + mods.each do |mod| + version_string = mod[:version][:vstring].sub(/^(?!v)/, 'v') - mod[:text] = "#{mod[:module]} (#{colorize(:cyan, version_string)})" - mod[:text] += " [#{mod[:path]}]" unless mod[:path] == dir - build_tree(mod[:dependencies], dir) + if mod[:action] == :upgrade + previous_version = mod[:previous_version].sub(/^(?!v)/, 'v') + version_string = "#{previous_version} -> #{version_string}" end + + mod[:text] = "#{mod[:module]} (#{colorize(:cyan, version_string)})" + mod[:text] += " [#{mod[:path]}]" unless mod[:path] == dir + build_tree(mod[:dependencies], dir) end end end end # Load remaining libraries require 'puppet/module_tool/errors' require 'puppet/module_tool/applications' require 'puppet/module_tool/checksums' require 'puppet/module_tool/contents_description' require 'puppet/module_tool/dependency' require 'puppet/module_tool/metadata' require 'puppet/module_tool/modulefile' require 'puppet/module_tool/skeleton' require 'puppet/forge/cache' require 'puppet/forge' diff --git a/lib/puppet/module_tool/applications.rb b/lib/puppet/module_tool/applications.rb index c3fb85731..c377bc97c 100644 --- a/lib/puppet/module_tool/applications.rb +++ b/lib/puppet/module_tool/applications.rb @@ -1,17 +1,15 @@ -require 'puppet/module' +require 'puppet/module_tool' -class Puppet::Module - module Tool - module Applications - require 'puppet/module_tool/applications/application' - require 'puppet/module_tool/applications/builder' - require 'puppet/module_tool/applications/checksummer' - require 'puppet/module_tool/applications/generator' - require 'puppet/module_tool/applications/installer' - require 'puppet/module_tool/applications/searcher' - require 'puppet/module_tool/applications/unpacker' - require 'puppet/module_tool/applications/uninstaller' - require 'puppet/module_tool/applications/upgrader' - end +module Puppet::ModuleTool + module Applications + require 'puppet/module_tool/applications/application' + require 'puppet/module_tool/applications/builder' + require 'puppet/module_tool/applications/checksummer' + require 'puppet/module_tool/applications/generator' + require 'puppet/module_tool/applications/installer' + require 'puppet/module_tool/applications/searcher' + require 'puppet/module_tool/applications/unpacker' + require 'puppet/module_tool/applications/uninstaller' + require 'puppet/module_tool/applications/upgrader' end end diff --git a/lib/puppet/module_tool/applications/application.rb b/lib/puppet/module_tool/applications/application.rb index bce0e84b1..feb0777fd 100644 --- a/lib/puppet/module_tool/applications/application.rb +++ b/lib/puppet/module_tool/applications/application.rb @@ -1,82 +1,82 @@ require 'net/http' require 'semver' require 'puppet/util/colors' -module Puppet::Module::Tool +module Puppet::ModuleTool module Applications class Application include Puppet::Util::Colors def self.run(*args) new(*args).run end attr_accessor :options def initialize(options = {}) if Puppet.features.microsoft_windows? raise Puppet::Error, "`puppet module` actions are currently not supported on Microsoft Windows" end @options = options end def run raise NotImplementedError, "Should be implemented in child classes." end def discuss(response, success, failure) case response when Net::HTTPOK, Net::HTTPCreated Puppet.notice success else errors = PSON.parse(response.body)['error'] rescue "HTTP #{response.code}, #{response.body}" Puppet.warning "#{failure} (#{errors})" end end def metadata(require_modulefile = false) unless @metadata unless @path raise ArgumentError, "Could not determine module path" end - @metadata = Puppet::Module::Tool::Metadata.new + @metadata = Puppet::ModuleTool::Metadata.new contents = ContentsDescription.new(@path) contents.annotate(@metadata) checksums = Checksums.new(@path) checksums.annotate(@metadata) modulefile_path = File.join(@path, 'Modulefile') if File.file?(modulefile_path) - Puppet::Module::Tool::ModulefileReader.evaluate(@metadata, modulefile_path) + Puppet::ModuleTool::ModulefileReader.evaluate(@metadata, modulefile_path) elsif require_modulefile raise ArgumentError, "No Modulefile found." end end @metadata end def load_modulefile! @metadata = nil metadata(true) end def parse_filename(filename) if match = /^((.*?)-(.*?))-(\d+\.\d+\.\d+.*?)$/.match(File.basename(filename,'.tar.gz')) module_name, author, shortname, version = match.captures else raise ArgumentError, "Could not parse filename to obtain the username, module name and version. (#{@release_name})" end unless SemVer.valid?(version) raise ArgumentError, "Invalid version format: #{version} (Semantic Versions are acceptable: http://semver.org)" end return { :module_name => module_name, :author => author, :dir_name => shortname, :version => version } end end end end diff --git a/lib/puppet/module_tool/applications/builder.rb b/lib/puppet/module_tool/applications/builder.rb index 4fb9f26dc..9b9a8d31c 100644 --- a/lib/puppet/module_tool/applications/builder.rb +++ b/lib/puppet/module_tool/applications/builder.rb @@ -1,91 +1,91 @@ require 'fileutils' -module Puppet::Module::Tool +module Puppet::ModuleTool module Applications class Builder < Application def initialize(path, options = {}) - @path = File.expand_path(Puppet::Module::Tool.find_module_root(path)) + @path = File.expand_path(Puppet::ModuleTool.find_module_root(path)) @pkg_path = File.join(@path, 'pkg') super(options) end def run load_modulefile! create_directory copy_contents add_metadata Puppet.notice "Building #{@path} for release" tar gzip relative = Pathname.new(File.join(@pkg_path, filename('tar.gz'))).relative_path_from(Pathname.new(Dir.pwd)) # Return the Pathname object representing the path to the release # archive just created. This return value is used by the module_tool # face build action, and displayed to on the console using the to_s # method. # # Example return value: # # # relative end private def filename(ext) ext.sub!(/^\./, '') "#{metadata.release_name}.#{ext}" end def tar tar_name = filename('tar') Dir.chdir(@pkg_path) do FileUtils.rm tar_name rescue nil unless system "tar -cf #{tar_name} #{metadata.release_name}" raise RuntimeError, "Could not create #{tar_name}" end end end def gzip Dir.chdir(@pkg_path) do FileUtils.rm filename('tar.gz') rescue nil unless system "gzip #{filename('tar')}" raise RuntimeError, "Could not compress #{filename('tar')}" end end end def create_directory FileUtils.mkdir(@pkg_path) rescue nil if File.directory?(build_path) FileUtils.rm_rf(build_path, :secure => true) end FileUtils.mkdir(build_path) end def copy_contents Dir[File.join(@path, '*')].each do |path| case File.basename(path) - when *Puppet::Module::Tool::ARTIFACTS + when *Puppet::ModuleTool::ARTIFACTS next else FileUtils.cp_r path, build_path end end end def add_metadata File.open(File.join(build_path, 'metadata.json'), 'w') do |f| f.write(PSON.pretty_generate(metadata)) end end def build_path @build_path ||= File.join(@pkg_path, metadata.release_name) end end end end diff --git a/lib/puppet/module_tool/applications/checksummer.rb b/lib/puppet/module_tool/applications/checksummer.rb index f0c3a7130..9f6814731 100644 --- a/lib/puppet/module_tool/applications/checksummer.rb +++ b/lib/puppet/module_tool/applications/checksummer.rb @@ -1,56 +1,56 @@ require 'puppet/module_tool/checksums' -module Puppet::Module::Tool +module Puppet::ModuleTool module Applications class Checksummer < Application def initialize(path, options = {}) @path = Pathname.new(path) super(options) end def run changes = [] if metadata_file.exist? - sums = Puppet::Module::Tool::Checksums.new(@path) + sums = Puppet::ModuleTool::Checksums.new(@path) (metadata['checksums'] || {}).each do |child_path, canonical_checksum| # Work around an issue where modules built with an older version # of PMT would include the metadata.json file in the list of files # checksummed. This causes metadata.json to always report local # changes. next if File.basename(child_path) == "metadata.json" path = @path + child_path if canonical_checksum != sums.checksum(path) changes << child_path end end else raise ArgumentError, "No metadata.json found." end # Return an Array of strings representing file paths of files that have # been modified since this module was installed. All paths are relative # to the installed module directory. This return value is used by the # module_tool face changes action, and displayed on the console. # # Example return value: # # [ "REVISION", "manifests/init.pp"] # changes end private def metadata PSON.parse(metadata_file.read) end def metadata_file (@path + 'metadata.json') end end end end diff --git a/lib/puppet/module_tool/applications/generator.rb b/lib/puppet/module_tool/applications/generator.rb index 699fe246d..6b5bda98f 100644 --- a/lib/puppet/module_tool/applications/generator.rb +++ b/lib/puppet/module_tool/applications/generator.rb @@ -1,141 +1,141 @@ require 'pathname' require 'fileutils' require 'erb' -module Puppet::Module::Tool +module Puppet::ModuleTool module Applications class Generator < Application def initialize(full_module_name, options = {}) begin @metadata = Metadata.new(:full_module_name => full_module_name) rescue ArgumentError raise "Could not generate directory #{full_module_name.inspect}, you must specify a dash-separated username and module name." end super(options) end def skeleton @skeleton ||= Skeleton.new end def get_binding binding end def run if destination.directory? raise ArgumentError, "#{destination} already exists." end Puppet.notice "Generating module at #{Dir.pwd}/#{@metadata.dashed_name}" files_created = [] skeleton.path.find do |path| if path == skeleton destination.mkpath else node = Node.on(path, self) if node node.install! files_created << node.target else Puppet.notice "Could not generate from #{path}" end end end # Return an array of Pathname objects representing file paths of files # and directories just generated. This return value is used by the # module_tool face generate action, and displayed on the console. # # Example return value: # # [ # #, # #, # #, # #, # #, # #, # #, # #, # #, # #, # # @filename unless File.exist?(@filename) parsed = parse_filename(@filename) @module_name = parsed[:module_name] @version = parsed[:version] else @source = :repository @module_name = @name.gsub('/', '-') @version = options[:version] end results = { :module_name => @module_name, :module_version => @version, :install_dir => options[:target_dir], } unless File.directory? options[:target_dir] raise MissingInstallDirectoryError, :requested_module => @module_name, :requested_version => @version || 'latest', :directory => options[:target_dir] end cached_paths = get_release_packages unless @graph.empty? Puppet.notice 'Installing -- do not interrupt ...' cached_paths.each do |hash| hash.each do |dir, path| Unpacker.new(path, @options.merge(:target_dir => dir)).run end end end rescue ModuleToolError => err results[:error] = { :oneline => err.message, :multiline => err.multiline, } else results[:result] = :success results[:installed_modules] = @graph ensure results[:result] ||= :failure end results end private - include Puppet::Module::Tool::Shared + include Puppet::ModuleTool::Shared # Return a Pathname object representing the path to the module # release package in the `Puppet.settings[:module_working_dir]`. def get_release_packages get_local_constraints if !@force && @installed.include?(@module_name) raise AlreadyInstalledError, :module_name => @module_name, :installed_version => @installed[@module_name].first.version, :requested_version => @version || (@conditions[@module_name].empty? ? :latest : :best), :local_changes => @installed[@module_name].first.local_changes end if @ignore_dependencies && @source == :filesystem @urls = {} @remote = { "#{@module_name}@#{@version}" => { } } @versions = { @module_name => [ { :vstring => @version, :semver => SemVer.new(@version) } ] } else get_remote_constraints end @graph = resolve_constraints({ @module_name => @version }) @graph.first[:tarball] = @filename if @source == :filesystem resolve_install_conflicts(@graph) unless @force # This clean call means we never "cache" the module we're installing, but this # is desired since module authors can easily rerelease modules different content but the same # version number, meaning someone with the old content cached will be very confused as to why # they can't get new content. # Long term we should just get rid of this caching behavior and cleanup downloaded modules after they install # but for now this is a quick fix to disable caching Puppet::Forge::Cache.clean download_tarballs(@graph, @graph.last[:path]) end # # Resolve installation conflicts by checking if the requested module # or one of it's dependencies conflicts with an installed module. # # Conflicts occur under the following conditions: # # When installing 'puppetlabs-foo' and an existing directory in the # target install path contains a 'foo' directory and we cannot determine # the "full name" of the installed module. # # When installing 'puppetlabs-foo' and 'pete-foo' is already installed. # This is considered a conflict because 'puppetlabs-foo' and 'pete-foo' # install into the same directory 'foo'. # def resolve_install_conflicts(graph, is_dependency = false) graph.each do |release| @environment.modules_by_path[options[:target_dir]].each do |mod| if mod.has_metadata? metadata = { :name => mod.forge_name.gsub('/', '-'), :version => mod.version } next if release[:module] == metadata[:name] else metadata = nil end if release[:module] =~ /-#{mod.name}$/ dependency_info = { :name => release[:module], :version => release[:version][:vstring] } dependency = is_dependency ? dependency_info : nil latest_version = @versions["#{@module_name}"].sort_by { |h| h[:semver] }.last[:vstring] raise InstallConflictError, :requested_module => @module_name, :requested_version => @version || "latest: v#{latest_version}", :dependency => dependency, :directory => mod.path, :metadata => metadata end resolve_install_conflicts(release[:dependencies], true) end end end # # Check if a file is a vaild module package. # --- # FIXME: Checking for a valid module package should be more robust and # use the acutal metadata contained in the package. 03132012 - Hightower # +++ # def is_module_package?(name) filename = File.expand_path(name) filename =~ /.tar.gz$/ end end end end diff --git a/lib/puppet/module_tool/applications/searcher.rb b/lib/puppet/module_tool/applications/searcher.rb index 923aaf92d..28c78375f 100644 --- a/lib/puppet/module_tool/applications/searcher.rb +++ b/lib/puppet/module_tool/applications/searcher.rb @@ -1,15 +1,15 @@ -module Puppet::Module::Tool +module Puppet::ModuleTool module Applications class Searcher < Application def initialize(term, options = {}) @term = term super(options) end def run Puppet::Forge.search(@term) end end end end diff --git a/lib/puppet/module_tool/applications/uninstaller.rb b/lib/puppet/module_tool/applications/uninstaller.rb index 5ffaecd65..006a88b8e 100644 --- a/lib/puppet/module_tool/applications/uninstaller.rb +++ b/lib/puppet/module_tool/applications/uninstaller.rb @@ -1,107 +1,107 @@ -module Puppet::Module::Tool +module Puppet::ModuleTool module Applications class Uninstaller < Application - include Puppet::Module::Tool::Errors + include Puppet::ModuleTool::Errors def initialize(name, options) @name = name @options = options @errors = Hash.new {|h, k| h[k] = {}} @unfiltered = [] @installed = [] @suggestions = [] @environment = Puppet::Node::Environment.new(options[:environment]) end def run results = { :module_name => @name, :requested_version => @version, } begin find_installed_module validate_module FileUtils.rm_rf(@installed.first.path, :secure => true) results[:affected_modules] = @installed results[:result] = :success rescue ModuleToolError => err results[:error] = { :oneline => err.message, :multiline => err.multiline, } rescue => e results[:error] = { :oneline => e.message, :multiline => e.respond_to?(:multiline) ? e.multiline : [e.to_s, e.backtrace].join("\n") } ensure results[:result] ||= :failure end results end private def find_installed_module @environment.modules_by_path.values.flatten.each do |mod| mod_name = (mod.forge_name || mod.name).gsub('/', '-') if mod_name == @name @unfiltered << { :name => mod_name, :version => mod.version, :path => mod.modulepath, } if @options[:version] && mod.version next unless SemVer[@options[:version]].include?(SemVer.new(mod.version)) end @installed << mod elsif mod_name =~ /#{@name}/ @suggestions << mod_name end end if @installed.length > 1 raise MultipleInstalledError, :action => :uninstall, :module_name => @name, :installed_modules => @installed.sort_by { |mod| @environment.modulepath.index(mod.modulepath) } elsif @installed.empty? if @unfiltered.empty? raise NotInstalledError, :action => :uninstall, :suggestions => @suggestions, :module_name => @name else raise NoVersionMatchesError, :installed_modules => @unfiltered.sort_by { |mod| @environment.modulepath.index(mod[:path]) }, :version_range => @options[:version], :module_name => @name end end end def validate_module mod = @installed.first if !@options[:force] && mod.has_metadata? && mod.has_local_changes? raise LocalChangesError, :action => :uninstall, :module_name => (mod.forge_name || mod.name).gsub('/', '-'), :requested_version => @options[:version], :installed_version => mod.version end if !@options[:force] && !mod.required_by.empty? raise ModuleIsRequiredError, :module_name => (mod.forge_name || mod.name).gsub('/', '-'), :required_by => mod.required_by, :requested_version => @options[:version], :installed_version => mod.version end end end end end diff --git a/lib/puppet/module_tool/applications/unpacker.rb b/lib/puppet/module_tool/applications/unpacker.rb index 67a76fbcc..e9b0b50d1 100644 --- a/lib/puppet/module_tool/applications/unpacker.rb +++ b/lib/puppet/module_tool/applications/unpacker.rb @@ -1,48 +1,48 @@ require 'pathname' require 'tmpdir' -module Puppet::Module::Tool +module Puppet::ModuleTool module Applications class Unpacker < Application def initialize(filename, options = {}) @filename = Pathname.new(filename) parsed = parse_filename(filename) super(options) @module_dir = Pathname.new(options[:target_dir]) + parsed[:dir_name] end def run extract_module_to_install_dir # Return the Pathname object representing the directory where the # module release archive was unpacked the to, and the module release # name. @module_dir end private def extract_module_to_install_dir delete_existing_installation_or_abort! build_dir = Puppet::Forge::Cache.base_path + "tmp-unpacker-#{Digest::SHA1.hexdigest(@filename.basename.to_s)}" build_dir.mkpath begin unless system "tar xzf #{@filename} -C #{build_dir}" raise RuntimeError, "Could not extract contents of module archive." end # grab the first directory extracted = build_dir.children.detect { |c| c.directory? } FileUtils.mv extracted, @module_dir ensure build_dir.rmtree end end def delete_existing_installation_or_abort! return unless @module_dir.exist? FileUtils.rm_rf(@module_dir, :secure => true) end end end end diff --git a/lib/puppet/module_tool/applications/upgrader.rb b/lib/puppet/module_tool/applications/upgrader.rb index 1a56a6deb..94c1ddc6a 100644 --- a/lib/puppet/module_tool/applications/upgrader.rb +++ b/lib/puppet/module_tool/applications/upgrader.rb @@ -1,109 +1,109 @@ -module Puppet::Module::Tool +module Puppet::ModuleTool module Applications class Upgrader < Application - include Puppet::Module::Tool::Errors + include Puppet::ModuleTool::Errors def initialize(name, options) @action = :upgrade @environment = Puppet::Node::Environment.new(Puppet.settings[:environment]) @module_name = name @options = options @force = options[:force] @ignore_dependencies = options[:force] || options[:ignore_dependencies] @version = options[:version] end def run begin results = { :module_name => @module_name } get_local_constraints if @installed[@module_name].length > 1 raise MultipleInstalledError, :action => :upgrade, :module_name => @module_name, :installed_modules => @installed[@module_name].sort_by { |mod| @environment.modulepath.index(mod.modulepath) } elsif @installed[@module_name].empty? raise NotInstalledError, :action => :upgrade, :module_name => @module_name end @module = @installed[@module_name].last results[:installed_version] = @module.version ? @module.version.sub(/^(?=\d)/, 'v') : nil results[:requested_version] = @version || (@conditions[@module_name].empty? ? :latest : :best) dir = @module.modulepath Puppet.notice "Found '#{@module_name}' (#{colorize(:cyan, results[:installed_version] || '???')}) in #{dir} ..." if !@options[:force] && @module.has_metadata? && @module.has_local_changes? raise LocalChangesError, :action => :upgrade, :module_name => @module_name, :requested_version => @version || (@conditions[@module_name].empty? ? :latest : :best), :installed_version => @module.version end begin get_remote_constraints rescue => e raise UnknownModuleError, results.merge(:repository => Puppet::Forge.repository.uri) else raise UnknownVersionError, results.merge(:repository => Puppet::Forge.repository.uri) if @remote.empty? end if !@options[:force] && @versions["#{@module_name}"].last[:vstring].sub(/^(?=\d)/, 'v') == (@module.version || '0.0.0').sub(/^(?=\d)/, 'v') raise VersionAlreadyInstalledError, :module_name => @module_name, :requested_version => @version || ((@conditions[@module_name].empty? ? 'latest' : 'best') + ": #{@versions["#{@module_name}"].last[:vstring].sub(/^(?=\d)/, 'v')}"), :installed_version => @installed[@module_name].last.version, :conditions => @conditions[@module_name] + [{ :module => :you, :version => @version }] end @graph = resolve_constraints({ @module_name => @version }) # This clean call means we never "cache" the module we're installing, but this # is desired since module authors can easily rerelease modules different content but the same # version number, meaning someone with the old content cached will be very confused as to why # they can't get new content. # Long term we should just get rid of this caching behavior and cleanup downloaded modules after they install # but for now this is a quick fix to disable caching Puppet::Forge::Cache.clean tarballs = download_tarballs(@graph, @graph.last[:path]) unless @graph.empty? Puppet.notice 'Upgrading -- do not interrupt ...' tarballs.each do |hash| hash.each do |dir, path| Unpacker.new(path, @options.merge(:target_dir => dir)).run end end end results[:result] = :success results[:base_dir] = @graph.first[:path] results[:affected_modules] = @graph rescue VersionAlreadyInstalledError => e results[:result] = :noop results[:error] = { :oneline => e.message, :multiline => e.multiline } rescue => e results[:error] = { :oneline => e.message, :multiline => e.respond_to?(:multiline) ? e.multiline : [e.to_s, e.backtrace].join("\n") } ensure results[:result] ||= :failure end return results end private - include Puppet::Module::Tool::Shared + include Puppet::ModuleTool::Shared end end end diff --git a/lib/puppet/module_tool/checksums.rb b/lib/puppet/module_tool/checksums.rb index a566090d6..f2c6971b2 100644 --- a/lib/puppet/module_tool/checksums.rb +++ b/lib/puppet/module_tool/checksums.rb @@ -1,52 +1,52 @@ require 'digest/md5' -module Puppet::Module::Tool +module Puppet::ModuleTool # = Checksums # # This class proides methods for generating checksums for data and adding # them to +Metadata+. class Checksums include Enumerable # Instantiate object with string +path+ to create checksums from. def initialize(path) @path = Pathname.new(path) end # Return checksum for the +Pathname+. def checksum(pathname) return Digest::MD5.hexdigest(pathname.read) end # Return checksums for object's +Pathname+, generate if it's needed. # Result is a hash of path strings to checksum strings. def data unless @data @data = {} @path.find do |descendant| - if Puppet::Module::Tool.artifact?(descendant) + if Puppet::ModuleTool.artifact?(descendant) Find.prune elsif descendant.file? path = descendant.relative_path_from(@path) @data[path.to_s] = checksum(descendant) end end end return @data end # TODO: Why? def each(&block) data.each(&block) end # Update +Metadata+'s checksums with this object's. def annotate(metadata) metadata.checksums.replace(data) end # TODO: Move the Checksummer#run checksum checking to here? end end diff --git a/lib/puppet/module_tool/contents_description.rb b/lib/puppet/module_tool/contents_description.rb index 6a590741d..0d19cd3ef 100644 --- a/lib/puppet/module_tool/contents_description.rb +++ b/lib/puppet/module_tool/contents_description.rb @@ -1,82 +1,82 @@ -module Puppet::Module::Tool +module Puppet::ModuleTool # = ContentsDescription # # This class populates +Metadata+'s Puppet type information. class ContentsDescription # Instantiate object for string +module_path+. def initialize(module_path) @module_path = module_path end # Update +Metadata+'s Puppet type information. def annotate(metadata) metadata.types.replace data.clone end # Return types for this module. Result is an array of hashes, each of which # describes a Puppet type. The type description hash structure is: # * :name => Name of this Puppet type. # * :doc => Documentation for this type. # * :properties => Array of hashes representing the type's properties, each # containing :name and :doc. # * :parameters => Array of hashes representing the type's parameters, each # containing :name and :doc. # * :providers => Array of hashes representing the types providers, each # containing :name and :doc. # TODO Write a TypeDescription to encapsulate these structures and logic? def data unless @data @data = [] type_names = [] for module_filename in Dir[File.join(@module_path, "lib/puppet/type/*.rb")] require module_filename type_name = File.basename(module_filename, ".rb") type_names << type_name for provider_filename in Dir[File.join(@module_path, "lib/puppet/provider/#{type_name}/*.rb")] require provider_filename end end type_names.each do |type_name| if type = Puppet::Type.type(type_name.to_sym) type_hash = {:name => type_name, :doc => type.doc} type_hash[:properties] = attr_doc(type, :property) type_hash[:parameters] = attr_doc(type, :param) if type.providers.size > 0 type_hash[:providers] = provider_doc(type) end @data << type_hash else Puppet.warning "Could not find/load type: #{type_name}" end end end @data end # Return an array of hashes representing this +type+'s attrs of +kind+ # (e.g. :param or :property), each containing :name and :doc. def attr_doc(type, kind) [].tap do |attrs| type.allattrs.each do |name| if type.attrtype(name) == kind && name != :provider attrs.push(:name => name, :doc => type.attrclass(name).doc) end end end end # Return an array of hashes representing this +type+'s providers, each # containing :name and :doc. def provider_doc(type) [].tap do |providers| type.providers.sort.each do |prov| providers.push(:name => prov, :doc => type.provider(prov).doc) end end end end end diff --git a/lib/puppet/module_tool/dependency.rb b/lib/puppet/module_tool/dependency.rb index d0eead196..9ebffab2b 100644 --- a/lib/puppet/module_tool/dependency.rb +++ b/lib/puppet/module_tool/dependency.rb @@ -1,24 +1,24 @@ -module Puppet::Module::Tool +module Puppet::ModuleTool class Dependency # Instantiates a new module dependency with a +full_module_name+ (e.g. # "myuser-mymodule"), and optional +version_requirement+ (e.g. "0.0.1") and # optional repository (a URL string). def initialize(full_module_name, version_requirement = nil, repository = nil) @full_module_name = full_module_name # TODO: add error checking, the next line raises ArgumentError when +full_module_name+ is invalid - @username, @name = Puppet::Module::Tool.username_and_modname_from(full_module_name) + @username, @name = Puppet::ModuleTool.username_and_modname_from(full_module_name) @version_requirement = version_requirement @repository = repository ? Puppet::Forge::Repository.new(repository) : nil end # Return PSON representation of this data. def to_pson(*args) result = { :name => @full_module_name } result[:version_requirement] = @version_requirement if @version_requirement && ! @version_requirement.nil? result[:repository] = @repository.to_s if @repository && ! @repository.nil? result.to_pson(*args) end end end diff --git a/lib/puppet/module_tool/errors.rb b/lib/puppet/module_tool/errors.rb index b90e5ba97..b2b36ada6 100644 --- a/lib/puppet/module_tool/errors.rb +++ b/lib/puppet/module_tool/errors.rb @@ -1,9 +1,9 @@ -module Puppet::Module::Tool +module Puppet::ModuleTool module Errors require 'puppet/module_tool/errors/base' require 'puppet/module_tool/errors/installer' require 'puppet/module_tool/errors/uninstaller' require 'puppet/module_tool/errors/upgrader' require 'puppet/module_tool/errors/shared' end end diff --git a/lib/puppet/module_tool/errors/base.rb b/lib/puppet/module_tool/errors/base.rb index 8ec3e7599..14f97fe9e 100644 --- a/lib/puppet/module_tool/errors/base.rb +++ b/lib/puppet/module_tool/errors/base.rb @@ -1,15 +1,15 @@ -module Puppet::Module::Tool::Errors +module Puppet::ModuleTool::Errors class ModuleToolError < StandardError def v(version) (version || '???').to_s.sub(/^(?=\d)/, 'v') end def vstring if @action == :upgrade "#{v(@installed_version)} -> #{v(@requested_version)}" else "#{v(@installed_version || @requested_version)}" end end end end diff --git a/lib/puppet/module_tool/errors/installer.rb b/lib/puppet/module_tool/errors/installer.rb index 48b3b77bf..3ee2f3f82 100644 --- a/lib/puppet/module_tool/errors/installer.rb +++ b/lib/puppet/module_tool/errors/installer.rb @@ -1,90 +1,90 @@ -module Puppet::Module::Tool::Errors +module Puppet::ModuleTool::Errors class InstallError < ModuleToolError; end class AlreadyInstalledError < InstallError def initialize(options) @module_name = options[:module_name] @installed_version = v(options[:installed_version]) @requested_version = v(options[:requested_version]) @local_changes = options[:local_changes] super "'#{@module_name}' (#{@requested_version}) requested; '#{@module_name}' (#{@installed_version}) already installed" end def multiline message = [] message << "Could not install module '#{@module_name}' (#{@requested_version})" message << " Module '#{@module_name}' (#{@installed_version}) is already installed" message << " Installed module has had changes made locally" unless @local_changes.empty? message << " Use `puppet module upgrade` to install a different version" message << " Use `puppet module install --force` to re-install only this module" message.join("\n") end end class InstallConflictError < InstallError def initialize(options) @requested_module = options[:requested_module] @requested_version = v(options[:requested_version]) @dependency = options[:dependency] @directory = options[:directory] @metadata = options[:metadata] super "'#{@requested_module}' (#{@requested_version}) requested; Installation conflict" end def multiline message = [] message << "Could not install module '#{@requested_module}' (#{@requested_version})" if @dependency message << " Dependency '#{@dependency[:name]}' (#{v(@dependency[:version])}) would overwrite #{@directory}" else message << " Installation would overwrite #{@directory}" end if @metadata message << " Currently, '#{@metadata[:name]}' (#{v(@metadata[:version])}) is installed to that directory" end message << " Use `puppet module install --dir ` to install modules elsewhere" if @dependency message << " Use `puppet module install --ignore-dependencies` to install only this module" else message << " Use `puppet module install --force` to install this module anyway" end message.join("\n") end end class MissingPackageError < InstallError def initialize(options) @requested_package = options[:requested_package] super "#{@requested_package} requested; Package #{@requested_package} does not exist" end def multiline <<-MSG.strip Could not install package #{@requested_package} Package #{@requested_package} does not exist MSG end end class MissingInstallDirectoryError < InstallError def initialize(options) @requested_module = options[:requested_module] @requested_version = options[:requested_version] @directory = options[:directory] super "'#{@requested_module}' (#{@requested_version}) requested; Directory #{@directory} does not exist" end def multiline <<-MSG.strip Could not install module '#{@requested_module}' (#{@requested_version}) Directory #{@directory} does not exist MSG end end end diff --git a/lib/puppet/module_tool/errors/shared.rb b/lib/puppet/module_tool/errors/shared.rb index f24d16bfe..fb8aa8e46 100644 --- a/lib/puppet/module_tool/errors/shared.rb +++ b/lib/puppet/module_tool/errors/shared.rb @@ -1,115 +1,115 @@ -module Puppet::Module::Tool::Errors +module Puppet::ModuleTool::Errors class NoVersionsSatisfyError < ModuleToolError def initialize(options) @requested_name = options[:requested_name] @requested_version = options[:requested_version] @installed_version = options[:installed_version] @dependency_name = options[:dependency_name] @conditions = options[:conditions] @action = options[:action] super "Could not #{@action} '#{@requested_name}' (#{vstring}); module '#{@dependency_name}' cannot satisfy dependencies" end def multiline same_mod = @requested_name == @dependency_name message = [] message << "Could not #{@action} module '#{@requested_name}' (#{vstring})" message << " No version of '#{@dependency_name}' will satisfy dependencies" message << " You specified '#{@requested_name}' (#{v(@requested_version)})" if same_mod message += @conditions.select { |c| c[:module] != :you }.sort_by { |c| c[:module] }.map do |c| " '#{c[:module]}' (#{v(c[:version])}) requires '#{@dependency_name}' (#{v(c[:dependency])})" end message << " Use `puppet module #{@action} --force` to #{@action} this module anyway" if same_mod message << " Use `puppet module #{@action} --ignore-dependencies` to #{@action} only this module" unless same_mod message.join("\n") end end class InvalidDependencyCycleError < ModuleToolError def initialize(options) @module_name = options[:module_name] @requested_module = options[:requested_module] @requested_version = options[:requested_version] @conditions = options[:conditions] @source = options[:source][1..-1] super "'#{@requested_module}' (#{v(@requested_version)}) requested; Invalid dependency cycle" end def multiline trace = [] trace << "You specified '#{@source.first[:name]}' (#{v(@requested_version)})" trace += @source[1..-1].map { |m| "which depends on '#{m[:name]}' (#{v(m[:version])})" } message = [] message << "Could not install module '#{@requested_module}' (#{v(@requested_version)})" message << " No version of '#{@module_name}' will satisfy dependencies" message << trace.map { |s| " #{s}" }.join(",\n") message << " Use `puppet module install --force` to install this module anyway" message.join("\n") end end class NotInstalledError < ModuleToolError def initialize(options) @module_name = options[:module_name] @suggestions = options[:suggestions] || [] @action = options[:action] super "Could not #{@action} '#{@module_name}'; module is not installed" end def multiline message = [] message << "Could not #{@action} module '#{@module_name}'" message << " Module '#{@module_name}' is not installed" message += @suggestions.map do |suggestion| " You may have meant `puppet module #{@action} #{suggestion}`" end message << " Use `puppet module install` to install this module" if @action == :upgrade message.join("\n") end end class MultipleInstalledError < ModuleToolError def initialize(options) @module_name = options[:module_name] @modules = options[:installed_modules] @action = options[:action] super "Could not #{@action} '#{@module_name}'; module appears in multiple places in the module path" end def multiline message = [] message << "Could not #{@action} module '#{@module_name}'" message << " Module '#{@module_name}' appears multiple places in the module path" message += @modules.map do |mod| " '#{@module_name}' (#{v(mod.version)}) was found in #{mod.modulepath}" end message << " Use the `--modulepath` option to limit the search to specific directories" message.join("\n") end end class LocalChangesError < ModuleToolError def initialize(options) @module_name = options[:module_name] @requested_version = options[:requested_version] @installed_version = options[:installed_version] @action = options[:action] super "Could not #{@action} '#{@module_name}'; module is not installed" end def multiline message = [] message << "Could not #{@action} module '#{@module_name}' (#{vstring})" message << " Installed module has had changes made locally" message << " Use `puppet module #{@action} --force` to #{@action} this module anyway" message.join("\n") end end end diff --git a/lib/puppet/module_tool/errors/uninstaller.rb b/lib/puppet/module_tool/errors/uninstaller.rb index 3beee7872..18809cb6c 100644 --- a/lib/puppet/module_tool/errors/uninstaller.rb +++ b/lib/puppet/module_tool/errors/uninstaller.rb @@ -1,45 +1,45 @@ -module Puppet::Module::Tool::Errors +module Puppet::ModuleTool::Errors class UninstallError < ModuleToolError; end class NoVersionMatchesError < UninstallError def initialize(options) @module_name = options[:module_name] @modules = options[:installed_modules] @version = options[:version_range] super "Could not uninstall '#{@module_name}'; no installed version matches" end def multiline message = [] message << "Could not uninstall module '#{@module_name}' (#{v(@version)})" message << " No installed version of '#{@module_name}' matches (#{v(@version)})" message += @modules.map do |mod| " '#{mod[:name]}' (#{v(mod[:version])}) is installed in #{mod[:path]}" end message.join("\n") end end class ModuleIsRequiredError < UninstallError def initialize(options) @module_name = options[:module_name] @required_by = options[:required_by] @requested_version = options[:requested_version] @installed_version = options[:installed_version] super "Could not uninstall '#{@module_name}'; installed modules still depend upon it" end def multiline message = [] message << ("Could not uninstall module '#{@module_name}'" << (@requested_version ? " (#{v(@requested_version)})" : '')) message << " Other installed modules have dependencies on '#{@module_name}' (#{v(@installed_version)})" message += @required_by.map do |mod| " '#{mod['name']}' (#{v(mod['version'])}) requires '#{@module_name}' (#{v(mod['version_requirement'])})" end message << " Use `puppet module uninstall --force` to uninstall this module anyway" message.join("\n") end end end diff --git a/lib/puppet/module_tool/errors/upgrader.rb b/lib/puppet/module_tool/errors/upgrader.rb index abc8375b8..43893856a 100644 --- a/lib/puppet/module_tool/errors/upgrader.rb +++ b/lib/puppet/module_tool/errors/upgrader.rb @@ -1,72 +1,72 @@ -module Puppet::Module::Tool::Errors +module Puppet::ModuleTool::Errors class UpgradeError < ModuleToolError def initialize(msg) @action = :upgrade super end end class VersionAlreadyInstalledError < UpgradeError def initialize(options) @module_name = options[:module_name] @requested_version = options[:requested_version] @installed_version = options[:installed_version] @dependency_name = options[:dependency_name] @conditions = options[:conditions] super "Could not upgrade '#{@module_name}'; module is not installed" end def multiline message = [] message << "Could not upgrade module '#{@module_name}' (#{vstring})" if @conditions.length == 1 && @conditions.last[:version].nil? message << " The installed version is already the latest version" else message << " The installed version is already the best fit for the current dependencies" message += @conditions.select { |c| c[:module] == :you && c[:version] }.map do |c| " You specified '#{@module_name}' (#{v(c[:version])})" end message += @conditions.select { |c| c[:module] != :you }.sort_by { |c| c[:module] }.map do |c| " '#{c[:module]}' (#{v(c[:version])}) requires '#{@module_name}' (#{v(c[:dependency])})" end end message << " Use `puppet module install --force` to re-install this module" message.join("\n") end end class UnknownModuleError < UpgradeError def initialize(options) @module_name = options[:module_name] @installed_version = options[:installed_version] @requested_version = options[:requested_version] @repository = options[:repository] super "Could not upgrade '#{@module_name}'; module is unknown to #{@repository}" end def multiline message = [] message << "Could not upgrade module '#{@module_name}' (#{vstring})" message << " Module '#{@module_name}' does not exist on #{@repository}" message.join("\n") end end class UnknownVersionError < UpgradeError def initialize(options) @module_name = options[:module_name] @installed_version = options[:installed_version] @requested_version = options[:requested_version] @repository = options[:repository] super "Could not upgrade '#{@module_name}' (#{vstring}); module has no versions #{ @requested_version && "matching #{v(@requested_version)} "}published on #{@repository}" end def multiline message = [] message << "Could not upgrade module '#{@module_name}' (#{vstring})" message << " No version matching '#{@requested_version || ">= 0.0.0"}' exists on #{@repository}" message.join("\n") end end end diff --git a/lib/puppet/module_tool/metadata.rb b/lib/puppet/module_tool/metadata.rb index a84100a10..73868a2ad 100644 --- a/lib/puppet/module_tool/metadata.rb +++ b/lib/puppet/module_tool/metadata.rb @@ -1,141 +1,141 @@ -module Puppet::Module::Tool +module Puppet::ModuleTool # = Metadata # # This class provides a data structure representing a module's metadata. # It provides some basic parsing, but other data is injected into it using # +annotate+ methods in other classes. class Metadata # The full name of the module, which is a dash-separated combination of the # +username+ and module +name+. attr_reader :full_module_name # The name of the user that owns this module. attr_reader :username # The name of this module. See also +full_module_name+. attr_reader :name # The version of this module. attr_reader :version # Instantiate from a hash, whose keys are setters in this class. def initialize(settings={}) settings.each do |key, value| send("#{key}=", value) end end # Set the full name of this module, and from it, the +username+ and # module +name+. def full_module_name=(full_module_name) @full_module_name = full_module_name - @username, @name = Puppet::Module::Tool::username_and_modname_from(full_module_name) + @username, @name = Puppet::ModuleTool::username_and_modname_from(full_module_name) end # Return an array of the module's Dependency objects. def dependencies return @dependencies ||= [] end def author @author || @username end def author=(author) @author = author end def source @source || 'UNKNOWN' end def source=(source) @source = source end def license @license || 'Apache License, Version 2.0' end def license=(license) @license = license end def summary @summary || 'UNKNOWN' end def summary=(summary) @summary = summary end def description @description || 'UNKNOWN' end def description=(description) @description = description end def project_page @project_page || 'UNKNOWN' end def project_page=(project_page) @project_page = project_page end # Return an array of the module's Puppet types, each one is a hash # containing :name and :doc. def types return @types ||= [] end # Return module's file checksums. def checksums return @checksums ||= {} end # Return the dashed name of the module, which may either be the # dash-separated combination of the +username+ and module +name+, or just # the module +name+. def dashed_name return [@username, @name].compact.join('-') end # Return the release name, which is the combination of the +dashed_name+ # of the module and its +version+ number. def release_name return [dashed_name, @version].join('-') end # Set the version of this module, ensure a string like '0.1.0' see the # Semantic Versions here: http://semver.org def version=(version) if SemVer.valid?(version) @version = version else raise ArgumentError, "Invalid version format: #{@version} (Semantic Versions are acceptable: http://semver.org)" end end # Return the PSON record representing this instance. def to_pson(*args) return { :name => @full_module_name, :version => @version, :source => source, :author => author, :license => license, :summary => summary, :description => description, :project_page => project_page, :dependencies => dependencies, :types => types, :checksums => checksums }.to_pson(*args) end end end diff --git a/lib/puppet/module_tool/modulefile.rb b/lib/puppet/module_tool/modulefile.rb index 4e91b2ab9..4b50f1160 100644 --- a/lib/puppet/module_tool/modulefile.rb +++ b/lib/puppet/module_tool/modulefile.rb @@ -1,75 +1,75 @@ -module Puppet::Module::Tool +module Puppet::ModuleTool # = Modulefile # # This class provides the DSL used for evaluating the module's 'Modulefile'. # These methods are used to concisely define this module's attributes, which # are later rendered as PSON into a 'metadata.json' file. class ModulefileReader # Read the +filename+ and eval its Ruby code to set values in the Metadata # +metadata+ instance. def self.evaluate(metadata, filename) builder = new(metadata) if File.file?(filename) builder.instance_eval(File.read(filename.to_s), filename.to_s, 1) else Puppet.warning "No Modulefile: #{filename}" end return builder end # Instantiate with the Metadata +metadata+ instance. def initialize(metadata) @metadata = metadata end # Set the +full_module_name+ (e.g. "myuser-mymodule"), which will also set the # +username+ and module +name+. Required. def name(name) @metadata.full_module_name = name end # Set the module +version+ (e.g., "0.0.1"). Required. def version(version) @metadata.version = version end # Add a dependency with the full_module_name +name+ (e.g. "myuser-mymodule"), an # optional +version_requirement+ (e.g. "0.0.1") and +repository+ (a URL # string). Optional. Can be called multiple times to add many dependencies. def dependency(name, version_requirement = nil, repository = nil) @metadata.dependencies << Dependency.new(name, version_requirement, repository) end # Set the source def source(source) @metadata.source = source end # Set the author or default to +username+ def author(author) @metadata.author = author end # Set the license def license(license) @metadata.license = license end # Set the summary def summary(summary) @metadata.summary = summary end # Set the description def description(description) @metadata.description = description end # Set the project page def project_page(project_page) @metadata.project_page = project_page end end end diff --git a/lib/puppet/module_tool/shared_behaviors.rb b/lib/puppet/module_tool/shared_behaviors.rb index dae588e34..b7821fc91 100644 --- a/lib/puppet/module_tool/shared_behaviors.rb +++ b/lib/puppet/module_tool/shared_behaviors.rb @@ -1,161 +1,161 @@ -module Puppet::Module::Tool::Shared +module Puppet::ModuleTool::Shared - include Puppet::Module::Tool::Errors + include Puppet::ModuleTool::Errors def get_local_constraints @local = Hash.new { |h,k| h[k] = { } } @conditions = Hash.new { |h,k| h[k] = [] } @installed = Hash.new { |h,k| h[k] = [] } @environment.modules_by_path.values.flatten.each do |mod| mod_name = (mod.forge_name || mod.name).gsub('/', '-') @installed[mod_name] << mod d = @local["#{mod_name}@#{mod.version}"] (mod.dependencies || []).each do |hash| name, conditions = hash['name'], hash['version_requirement'] name = name.gsub('/', '-') d[name] = conditions @conditions[name] << { :module => mod_name, :version => mod.version, :dependency => conditions } end end end def get_remote_constraints @remote = Hash.new { |h,k| h[k] = { } } @urls = {} @versions = Hash.new { |h,k| h[k] = [] } Puppet.notice "Downloading from #{Puppet::Forge.repository.uri} ..." - author, modname = Puppet::Module::Tool.username_and_modname_from(@module_name) + author, modname = Puppet::ModuleTool.username_and_modname_from(@module_name) info = Puppet::Forge.remote_dependency_info(author, modname, @options[:version]) info.each do |pair| mod_name, releases = pair mod_name = mod_name.gsub('/', '-') releases.each do |rel| semver = SemVer.new(rel['version'] || '0.0.0') rescue SemVer.MIN @versions[mod_name] << { :vstring => rel['version'], :semver => semver } @versions[mod_name].sort! { |a, b| a[:semver] <=> b[:semver] } @urls["#{mod_name}@#{rel['version']}"] = rel['file'] d = @remote["#{mod_name}@#{rel['version']}"] (rel['dependencies'] || []).each do |name, conditions| d[name.gsub('/', '-')] = conditions end end end end def implicit_version(mod) return :latest if @conditions[mod].empty? if @conditions[mod].all? { |c| c[:queued] || c[:module] == :you } return :latest end return :best end def annotated_version(mod, versions) if versions.empty? return implicit_version(mod) else return "#{implicit_version(mod)}: #{versions.last}" end end def resolve_constraints(dependencies, source = [{:name => :you}], seen = {}, action = @action) dependencies = dependencies.map do |mod, range| source.last[:dependency] = range @conditions[mod] << { :module => source.last[:name], :version => source.last[:version], :dependency => range, :queued => true } if @force range = SemVer[@version] rescue SemVer['>= 0.0.0'] else range = (@conditions[mod]).map do |r| SemVer[r[:dependency]] rescue SemVer['>= 0.0.0'] end.inject(&:&) end if @action == :install && seen.include?(mod) next if range === seen[mod][:semver] req_module = @module_name req_versions = @versions["#{@module_name}"].map { |v| v[:semver] } raise InvalidDependencyCycleError, :module_name => mod, :source => (source + [{ :name => mod, :version => source.last[:dependency] }]), :requested_module => req_module, :requested_version => @version || annotated_version(req_module, req_versions), :conditions => @conditions end if !(@force || @installed[mod].empty? || source.last[:name] == :you) next if range === SemVer.new(@installed[mod].first.version) action = :upgrade elsif @installed[mod].empty? action = :install end if action == :upgrade @conditions.each { |_, conds| conds.delete_if { |c| c[:module] == mod } } end valid_versions = @versions["#{mod}"].select { |h| range === h[:semver] } unless version = valid_versions.last req_module = @module_name req_versions = @versions["#{@module_name}"].map { |v| v[:semver] } raise NoVersionsSatisfyError, :requested_name => req_module, :requested_version => @version || annotated_version(req_module, req_versions), :installed_version => @installed[@module_name].empty? ? nil : @installed[@module_name].first.version, :dependency_name => mod, :conditions => @conditions[mod], :action => @action end seen[mod] = version { :module => mod, :version => version, :action => action, :previous_version => @installed[mod].empty? ? nil : @installed[mod].first.version, :file => @urls["#{mod}@#{version[:vstring]}"], :path => action == :install ? @options[:target_dir] : (@installed[mod].empty? ? @options[:target_dir] : @installed[mod].first.modulepath), :dependencies => [] } end.compact dependencies.each do |mod| deps = @remote["#{mod[:module]}@#{mod[:version][:vstring]}"].sort_by(&:first) mod[:dependencies] = resolve_constraints(deps, source + [{ :name => mod[:module], :version => mod[:version][:vstring] }], seen, :install) end unless @ignore_dependencies return dependencies end def download_tarballs(graph, default_path) graph.map do |release| begin if release[:tarball] cache_path = Pathname(release[:tarball]) else cache_path = Puppet::Forge.repository.retrieve(release[:file]) end rescue OpenURI::HTTPError => e raise RuntimeError, "Could not download module: #{e.message}" end [ { (release[:path] ||= default_path) => cache_path}, *download_tarballs(release[:dependencies], default_path) ] end.flatten end end diff --git a/lib/puppet/module_tool/skeleton.rb b/lib/puppet/module_tool/skeleton.rb index 4c41c7611..33f92636e 100644 --- a/lib/puppet/module_tool/skeleton.rb +++ b/lib/puppet/module_tool/skeleton.rb @@ -1,34 +1,34 @@ -module Puppet::Module::Tool +module Puppet::ModuleTool # = Skeleton # # This class provides methods for finding templates for the 'generate' action. class Skeleton # TODO Review whether the 'freeze' feature should be fixed or deleted. # def freeze! # FileUtils.rm_fr custom_path rescue nil # FileUtils.cp_r default_path, custom_path # end # Return Pathname with 'generate' templates. def path paths.detect { |path| path.directory? } end # Return Pathnames to look for 'generate' templates. def paths @paths ||= [ custom_path, default_path ] end # Return Pathname of custom templates directory. def custom_path Pathname(Puppet.settings[:module_working_dir]) + 'skeleton' end # Return Pathname of default template directory. def default_path Pathname(__FILE__).dirname + 'skeleton/templates/generator' end end end diff --git a/lib/puppet/type/vlan.rb b/lib/puppet/type/vlan.rb index 01fe6efe8..dc33ea2e0 100644 --- a/lib/puppet/type/vlan.rb +++ b/lib/puppet/type/vlan.rb @@ -1,26 +1,26 @@ # # Manages a Vlan on a given router or switch # Puppet::Type.newtype(:vlan) do @doc = "Manages a VLAN on a router or switch." apply_to_device ensurable newparam(:name) do desc "The numeric VLAN ID." isnamevar newvalues(/^\d+/) end newproperty(:description) do desc "The VLAN's name." end newparam(:device_url) do desc "The URL of the router or switch maintaining this VLAN." end -end \ No newline at end of file +end diff --git a/lib/puppet/util/instrumentation/indirection_probe.rb b/lib/puppet/util/instrumentation/indirection_probe.rb index 66e5f92ab..ad9323a38 100644 --- a/lib/puppet/util/instrumentation/indirection_probe.rb +++ b/lib/puppet/util/instrumentation/indirection_probe.rb @@ -1,29 +1,29 @@ require 'puppet/indirector' require 'puppet/util/instrumentation' # We need to use a class other than Probe for the indirector because # the Indirection class might declare some probes, and this would be a huge unbreakable # dependency cycle. class Puppet::Util::Instrumentation::IndirectionProbe extend Puppet::Indirector indirects :instrumentation_probe, :terminus_class => :local attr_reader :probe_name def initialize(probe_name) @probe_name = probe_name end def to_pson(*args) result = { :document_type => "Puppet::Util::Instrumentation::IndirectionProbe", :data => { :name => probe_name } } result.to_pson(*args) end def self.from_pson(data) self.new(data["name"]) end -end \ No newline at end of file +end diff --git a/lib/puppet/util/instrumentation/instrumentable.rb b/lib/puppet/util/instrumentation/instrumentable.rb index 5789dcbe2..1ce18a777 100644 --- a/lib/puppet/util/instrumentation/instrumentable.rb +++ b/lib/puppet/util/instrumentation/instrumentable.rb @@ -1,143 +1,143 @@ require 'monitor' require 'puppet/util/instrumentation' # This is the central point of all declared probes. # Every class needed to declare probes should include this module # and declare the methods that are subject to instrumentation: # # class MyClass # extend Puppet::Util::Instrumentation::Instrumentable # # probe :mymethod # # def mymethod # ... this is code to be instrumented ... # end # end module Puppet::Util::Instrumentation::Instrumentable INSTRUMENTED_CLASSES = {}.extend(MonitorMixin) attr_reader :probes class Probe attr_reader :klass, :method, :label, :data def initialize(method, klass, options = {}) @method = method @klass = klass @label = options[:label] || method @data = options[:data] || {} end def enable raise "Probe already enabled" if enabled? # We're forced to perform this copy because in the class_eval'uated # block below @method would be evaluated in the class context. It's better # to close on locally-scoped variables than to resort to complex namespacing # to get access to the probe instance variables. method = @method; label = @label; data = @data klass.class_eval { alias_method("instrumented_#{method}", method) define_method(method) do |*args| id = nil instrumentation_data = nil begin instrumentation_label = label.respond_to?(:call) ? label.call(self, args) : label instrumentation_data = data.respond_to?(:call) ? data.call(self, args) : data id = Puppet::Util::Instrumentation.start(instrumentation_label, instrumentation_data) send("instrumented_#{method}".to_sym, *args) ensure Puppet::Util::Instrumentation.stop(instrumentation_label, id, instrumentation_data || {}) end end } @enabled = true end def disable raise "Probe is not enabled" unless enabled? # For the same reason as in #enable, we're forced to do a local # copy method = @method klass.class_eval do alias_method(method, "instrumented_#{method}") remove_method("instrumented_#{method}".to_sym) end @enabled = false end def enabled? !!@enabled end end # Declares a new probe # # It is possible to pass several options that will be later on evaluated # and sent to the instrumentation layer. # # label:: # this can either be a static symbol/string or a block. If it's a block # this one will be evaluated on every call of the instrumented method and # should return a string or a symbol # # data:: # this can be a hash or a block. If it's a block this one will be evaluated # on every call of the instrumented method and should return a hash. # #Example: # # class MyClass # extend Instrumentable # # probe :mymethod, :data => Proc.new { |args| { :data => args[1] } }, :label => Proc.new { |args| args[0] } # # def mymethod(name, options) # end # # end # def probe(method, options = {}) INSTRUMENTED_CLASSES.synchronize { (@probes ||= []) << Probe.new(method, self, options) INSTRUMENTED_CLASSES[self] = @probes } end def self.probes @probes end def self.probe_names probe_names = [] each_probe { |probe| probe_names << "#{probe.klass}.#{probe.method}" } probe_names end def self.enable_probes each_probe { |probe| probe.enable } end def self.disable_probes each_probe { |probe| probe.disable } end def self.clear_probes INSTRUMENTED_CLASSES.synchronize { INSTRUMENTED_CLASSES.clear } nil # do not leak our probes to the exterior world end def self.each_probe INSTRUMENTED_CLASSES.synchronize { INSTRUMENTED_CLASSES.each_key do |klass| klass.probes.each { |probe| yield probe } end } nil # do not leak our probes to the exterior world end -end \ No newline at end of file +end diff --git a/lib/puppet/util/instrumentation/listeners/log.rb b/lib/puppet/util/instrumentation/listeners/log.rb index 59e9daff9..fcff14621 100644 --- a/lib/puppet/util/instrumentation/listeners/log.rb +++ b/lib/puppet/util/instrumentation/listeners/log.rb @@ -1,29 +1,29 @@ require 'monitor' # This is an example instrumentation listener that stores the last # 20 instrumented probe run time. Puppet::Util::Instrumentation.new_listener(:log) do SIZE = 20 attr_accessor :last_logs def initialize @last_logs = {}.extend(MonitorMixin) end def notify(label, event, data) return if event == :start log_line = "#{label} took #{data[:finished] - data[:started]}" @last_logs.synchronize { (@last_logs[label] ||= []) << log_line @last_logs[label].shift if @last_logs[label].length > SIZE } end def data @last_logs.synchronize { @last_logs.dup } end -end \ No newline at end of file +end diff --git a/lib/puppet/util/instrumentation/listeners/performance.rb b/lib/puppet/util/instrumentation/listeners/performance.rb index b3175a0a2..3ad51b7de 100644 --- a/lib/puppet/util/instrumentation/listeners/performance.rb +++ b/lib/puppet/util/instrumentation/listeners/performance.rb @@ -1,30 +1,30 @@ require 'monitor' Puppet::Util::Instrumentation.new_listener(:performance) do attr_reader :samples def initialize @samples = {}.extend(MonitorMixin) end def notify(label, event, data) return if event == :start duration = data[:finished] - data[:started] samples.synchronize do @samples[label] ||= { :count => 0, :max => 0, :min => nil, :sum => 0, :average => 0 } @samples[label][:count] += 1 @samples[label][:sum] += duration @samples[label][:max] = [ @samples[label][:max], duration ].max @samples[label][:min] = [ @samples[label][:min], duration ].reject { |val| val.nil? }.min @samples[label][:average] = @samples[label][:sum] / @samples[label][:count] end end def data samples.synchronize do @samples.dup end end -end \ No newline at end of file +end diff --git a/lib/puppet/util/network_device/base.rb b/lib/puppet/util/network_device/base.rb index 7d6c3fc44..d2fbfccb2 100644 --- a/lib/puppet/util/network_device/base.rb +++ b/lib/puppet/util/network_device/base.rb @@ -1,27 +1,27 @@ require 'puppet/util/autoload' require 'uri' require 'puppet/util/network_device/transport' require 'puppet/util/network_device/transport/base' class Puppet::Util::NetworkDevice::Base attr_accessor :url, :transport def initialize(url) @url = URI.parse(url) @autoloader = Puppet::Util::Autoload.new( self, "puppet/util/network_device/transport", :wrap => false ) if @autoloader.load(@url.scheme) @transport = Puppet::Util::NetworkDevice::Transport.const_get(@url.scheme.capitalize).new @transport.host = @url.host @transport.port = @url.port || case @url.scheme ; when "ssh" ; 22 ; when "telnet" ; 23 ; end @transport.user = @url.user @transport.password = @url.password end end -end \ No newline at end of file +end diff --git a/lib/puppet/util/network_device/cisco.rb b/lib/puppet/util/network_device/cisco.rb index c03a00104..8d1802082 100644 --- a/lib/puppet/util/network_device/cisco.rb +++ b/lib/puppet/util/network_device/cisco.rb @@ -1,4 +1,4 @@ module Puppet::Util::NetworkDevice::Cisco -end \ No newline at end of file +end diff --git a/lib/puppet/util/network_device/cisco/facts.rb b/lib/puppet/util/network_device/cisco/facts.rb index 8bba02ba0..658c0abd0 100644 --- a/lib/puppet/util/network_device/cisco/facts.rb +++ b/lib/puppet/util/network_device/cisco/facts.rb @@ -1,72 +1,72 @@ require 'puppet/util/network_device/cisco' require 'puppet/util/network_device/ipcalc' # this retrieves facts from a cisco device class Puppet::Util::NetworkDevice::Cisco::Facts attr_reader :transport def initialize(transport) @transport = transport end def retrieve facts = {} facts.merge(parse_show_ver) end def parse_show_ver facts = {} out = @transport.command("sh ver") lines = out.split("\n") lines.shift; lines.pop lines.each do |l| case l # cisco WS-C2924C-XL (PowerPC403GA) processor (revision 0x11) with 8192K/1024K bytes of memory. # Cisco 1841 (revision 5.0) with 355328K/37888K bytes of memory. # Cisco 877 (MPC8272) processor (revision 0x200) with 118784K/12288K bytes of memory. # cisco WS-C2960G-48TC-L (PowerPC405) processor (revision C0) with 61440K/4088K bytes of memory. # cisco WS-C2950T-24 (RC32300) processor (revision R0) with 19959K bytes of memory. when /[cC]isco ([\w-]+) (?:\(([\w-]+)\) processor )?\(revision (.+)\) with (\d+[KMG])(?:\/(\d+[KMG]))? bytes of memory\./ facts[:hardwaremodel] = $1 facts[:processor] = $2 if $2 facts[:hardwarerevision] = $3 facts[:memorysize] = $4 # uptime # Switch uptime is 1 year, 12 weeks, 6 days, 22 hours, 32 minutes # c2950 uptime is 3 weeks, 1 day, 23 hours, 36 minutes # c2960 uptime is 2 years, 27 weeks, 5 days, 21 hours, 30 minutes # router uptime is 5 weeks, 1 day, 3 hours, 30 minutes when /^\s*([\w-]+)\s+uptime is (.*?)$/ facts[:hostname] = $1 facts[:uptime] = $2 facts[:uptime_seconds] = uptime_to_seconds($2) facts[:uptime_days] = facts[:uptime_seconds] / 86400 # "IOS (tm) C2900XL Software (C2900XL-C3H2S-M), Version 12.0(5)WC10, RELEASE SOFTWARE (fc1)"=> { :operatingsystem => "IOS", :operatingsystemrelease => "12.0(5)WC10", :operatingsystemmajrelease => "12.0", :operatingsystemfeature => "C3H2S"}, # "IOS (tm) C2950 Software (C2950-I6K2L2Q4-M), Version 12.1(22)EA8a, RELEASE SOFTWARE (fc1)"=> { :operatingsystem => "IOS", :operatingsystemrelease => "12.1(22)EA8a", :operatingsystemmajrelease => "12.1", :operatingsystemfeature => "I6K2L2Q4"}, # "Cisco IOS Software, C2960 Software (C2960-LANBASEK9-M), Version 12.2(44)SE, RELEASE SOFTWARE (fc1)"=>{ :operatingsystem => "IOS", :operatingsystemrelease => "12.2(44)SE", :operatingsystemmajrelease => "12.2", :operatingsystemfeature => "LANBASEK9"}, # "Cisco IOS Software, C870 Software (C870-ADVIPSERVICESK9-M), Version 12.4(11)XJ4, RELEASE SOFTWARE (fc2)"=>{ :operatingsystem => "IOS", :operatingsystemrelease => "12.4(11)XJ40", :operatingsystemmajrelease => "12.4XJ", :operatingsystemfeature => "ADVIPSERVICESK9"}, # "Cisco IOS Software, 1841 Software (C1841-ADVSECURITYK9-M), Version 12.4(24)T4, RELEASE SOFTWARE (fc2)" =>{ :operatingsystem => "IOS", :operatingsystemrelease => "12.4(24)T4", :operatingsystemmajrelease => "12.4T", :operatingsystemfeature => "ADVSECURITYK9"}, when /(?:Cisco )?(IOS)\s*(?:\(tm\) |Software, )?(?:\w+)\s+Software\s+\(\w+-(\w+)-\w+\), Version ([0-9.()A-Za-z]+),/ facts[:operatingsystem] = $1 facts[:operatingsystemrelease] = $3 facts[:operatingsystemmajrelease] = ios_major_version(facts[:operatingsystemrelease]) facts[:operatingsystemfeature] = $2 end end facts end def ios_major_version(version) version.gsub(/^(\d+)\.(\d+)\(.+\)([A-Z]+)([\da-z]+)?/, '\1.\2\3') end def uptime_to_seconds(uptime) captures = (uptime.match /^(?:(\d+) years?,)?\s*(?:(\d+) weeks?,)?\s*(?:(\d+) days?,)?\s*(?:(\d+) hours?,)?\s*(\d+) minutes?$/).captures seconds = captures.zip([31536000, 604800, 86400, 3600, 60]).inject(0) do |total, (x,y)| total + (x.nil? ? 0 : x.to_i * y) end end -end \ No newline at end of file +end diff --git a/lib/puppet/util/network_device/cisco/interface.rb b/lib/puppet/util/network_device/cisco/interface.rb index 63d5492a7..d184b9c3c 100644 --- a/lib/puppet/util/network_device/cisco/interface.rb +++ b/lib/puppet/util/network_device/cisco/interface.rb @@ -1,82 +1,82 @@ require 'puppet/util/network_device/cisco' require 'puppet/util/network_device/ipcalc' # this manages setting properties to an interface in a cisco switch or router class Puppet::Util::NetworkDevice::Cisco::Interface include Puppet::Util::NetworkDevice::IPCalc extend Puppet::Util::NetworkDevice::IPCalc attr_reader :transport, :name def initialize(name, transport) @name = name @transport = transport end COMMANDS = { # property => order, ios command/block/array :description => [1, "description %s"], :speed => [2, "speed %s"], :duplex => [3, "duplex %s"], :native_vlan => [4, "switchport access vlan %s"], :encapsulation => [5, "switchport trunk encapsulation %s"], :mode => [6, "switchport mode %s"], :allowed_trunk_vlans => [7, "switchport trunk allowed vlan %s"], :etherchannel => [8, ["channel-group %s", "port group %s"]], :ipaddress => [9, lambda do |prefix,ip,option| ip.ipv6? ? "ipv6 address #{ip.to_s}/#{prefix} #{option}" : "ip address #{ip.to_s} #{netmask(Socket::AF_INET,prefix)}" end], :ensure => [10, lambda { |value| value == :present ? "no shutdown" : "shutdown" } ] } def update(is={}, should={}) Puppet.debug("Updating interface #{name}") command("conf t") command("interface #{name}") # apply changes in a defined orders for cisco IOS devices [is.keys, should.keys].flatten.uniq.sort {|a,b| COMMANDS[a][0] <=> COMMANDS[b][0] }.each do |property| # They're equal, so do nothing. next if is[property] == should[property] # We're deleting it if should[property] == :absent or should[property].nil? execute(property, is[property], "no ") next end # We're replacing an existing value or creating a new one execute(property, should[property]) end command("exit") command("exit") end def execute(property, value, prefix='') case COMMANDS[property][1] when Array COMMANDS[property][1].each do |command| transport.command(prefix + command % value) do |out| break unless out =~ /^%/ end end when String command(prefix + COMMANDS[property][1] % value) when Proc value = [value] unless value.is_a?(Array) value.each do |value| command(prefix + COMMANDS[property][1].call(*value)) end end end def command(command) transport.command(command) do |out| Puppet.err "Error while executing #{command}, device returned #{out}" if out =~ /^%/mo end end -end \ No newline at end of file +end diff --git a/lib/puppet/util/network_device/config.rb b/lib/puppet/util/network_device/config.rb index afb9a150b..029b0d137 100644 --- a/lib/puppet/util/network_device/config.rb +++ b/lib/puppet/util/network_device/config.rb @@ -1,92 +1,92 @@ require 'ostruct' require 'puppet/util/loadedfile' class Puppet::Util::NetworkDevice::Config < Puppet::Util::LoadedFile def self.main @main ||= self.new end def self.devices main.devices || [] end attr_reader :devices def exists? FileTest.exists?(@file) end def initialize() @file = Puppet[:deviceconfig] raise Puppet::DevError, "No device config file defined" unless @file return unless self.exists? super(@file) @devices = {} read(true) # force reading at start end # Read the configuration file. def read(force = false) return unless FileTest.exists?(@file) parse if force or changed? end private def parse begin devices = {} device = nil File.open(@file) { |f| count = 1 f.each { |line| case line when /^\s*#/ # skip comments count += 1 next when /^\s*$/ # skip blank lines count += 1 next when /^\[([\w.-]+)\]\s*$/ # [device.fqdn] name = $1 name.chomp! raise Puppet::Error, "Duplicate device found at line #{count}, already found at #{device.line}" if devices.include?(name) device = OpenStruct.new device.name = name device.line = count Puppet.debug "found device: #{device.name} at #{device.line}" devices[name] = device when /^\s*(type|url)\s+(.+)$/ parse_directive(device, $1, $2, count) else raise Puppet::Error, "Invalid line #{count}: #{line}" end count += 1 } } rescue Errno::EACCES => detail Puppet.err "Configuration error: Cannot read #{@file}; cannot serve" #raise Puppet::Error, "Cannot read #{@config}" rescue Errno::ENOENT => detail Puppet.err "Configuration error: '#{@file}' does not exit; cannot serve" end @devices = devices end def parse_directive(device, var, value, count) case var when "type" device.provider = value when "url" device.url = value else raise Puppet::Error, "Invalid argument '#{var}' at line #{count}" end end -end \ No newline at end of file +end diff --git a/lib/puppet/util/network_device/ipcalc.rb b/lib/puppet/util/network_device/ipcalc.rb index 2b4f360b7..b2e3aa673 100644 --- a/lib/puppet/util/network_device/ipcalc.rb +++ b/lib/puppet/util/network_device/ipcalc.rb @@ -1,68 +1,68 @@ require 'puppet/util/network_device' module Puppet::Util::NetworkDevice::IPCalc # This is a rip-off of authstore Octet = '(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])' IPv4 = "#{Octet}\.#{Octet}\.#{Octet}\.#{Octet}" IPv6_full = "_:_:_:_:_:_:_:_|_:_:_:_:_:_::_?|_:_:_:_:_::((_:)?_)?|_:_:_:_::((_:){0,2}_)?|_:_:_::((_:){0,3}_)?|_:_::((_:){0,4}_)?|_::((_:){0,5}_)?|::((_:){0,6}_)?" IPv6_partial = "_:_:_:_:_:_:|_:_:_:_::(_:)?|_:_::(_:){0,2}|_::(_:){0,3}" IP = "#{IPv4}|#{IPv6_full}".gsub(/_/,'([0-9a-fA-F]{1,4})').gsub(/\(/,'(?:') def parse(value) case value when /^(#{IP})\/(\d+)$/ # 12.34.56.78/24, a001:b002::efff/120, c444:1000:2000::9:192.168.0.1/112 [$2.to_i,IPAddr.new($1)] when /^(#{IP})$/ # 10.20.30.40, value = IPAddr.new(value) [bits(value.family),value] end end def bits(family) family == Socket::AF_INET6 ? 128 : 32 end def fullmask(family) (1 << bits(family)) - 1 end def mask(family, length) (1 << (bits(family) - length)) - 1 end # returns ip address netmask from prefix length def netmask(family, length) IPAddr.new(fullmask(family) & ~mask(family, length) , family) end # returns an IOS wildmask def wildmask(family, length) IPAddr.new(mask(family, length) , family) end # returns ip address prefix length from netmask def prefix_length(netmask) mask_addr = netmask.to_i return 0 if mask_addr == 0 length=32 if (netmask.ipv6?) length=128 end mask = mask_addr < 2**length ? length : 128 mask.times do if ((mask_addr & 1) == 1) break end mask_addr = mask_addr >> 1 mask = mask - 1 end mask end def linklocal?(ip) end -end \ No newline at end of file +end diff --git a/lib/puppet/util/network_device/transport.rb b/lib/puppet/util/network_device/transport.rb index cef8f3859..59762b19b 100644 --- a/lib/puppet/util/network_device/transport.rb +++ b/lib/puppet/util/network_device/transport.rb @@ -1,3 +1,3 @@ # stub module Puppet::Util::NetworkDevice::Transport -end \ No newline at end of file +end diff --git a/lib/puppet/util/network_device/transport/base.rb b/lib/puppet/util/network_device/transport/base.rb index 1d62209cb..4cee0dd3a 100644 --- a/lib/puppet/util/network_device/transport/base.rb +++ b/lib/puppet/util/network_device/transport/base.rb @@ -1,26 +1,26 @@ require 'puppet/util/network_device' require 'puppet/util/network_device/transport' class Puppet::Util::NetworkDevice::Transport::Base attr_accessor :user, :password, :host, :port attr_accessor :default_prompt, :timeout def initialize @timeout = 10 end def send(cmd) end def expect(prompt) end def command(cmd, options = {}) send(cmd) expect(options[:prompt] || default_prompt) do |output| yield output if block_given? end end -end \ No newline at end of file +end diff --git a/lib/puppet/util/network_device/transport/ssh.rb b/lib/puppet/util/network_device/transport/ssh.rb index 3d7976543..dca5600bd 100644 --- a/lib/puppet/util/network_device/transport/ssh.rb +++ b/lib/puppet/util/network_device/transport/ssh.rb @@ -1,121 +1,121 @@ require 'puppet/util/network_device' require 'puppet/util/network_device/transport' require 'puppet/util/network_device/transport/base' # This is an adaptation/simplification of gem net-ssh-telnet, which aims to have # a sane interface to Net::SSH. Credits goes to net-ssh-telnet authors class Puppet::Util::NetworkDevice::Transport::Ssh < Puppet::Util::NetworkDevice::Transport::Base attr_accessor :buf, :ssh, :channel, :verbose def initialize super unless Puppet.features.ssh? raise 'Connecting with ssh to a network device requires the \'net/ssh\' ruby library' end end def handles_login? true end def eof? !! @eof end def connect(&block) @output = [] @channel_data = "" begin Puppet.debug("connecting to #{host} as #{user}") @ssh = Net::SSH.start(host, user, :port => port, :password => password, :timeout => timeout) rescue TimeoutError raise TimeoutError, "timed out while opening an ssh connection to the host" rescue Net::SSH::AuthenticationFailed raise Puppet::Error, "SSH authentication failure connecting to #{host} as #{user}" rescue Net::SSH::Exception => detail raise Puppet::Error, "SSH connection failure to #{host}" end @buf = "" @eof = false @channel = nil @ssh.open_channel do |channel| channel.request_pty { |ch,success| raise "failed to open pty" unless success } channel.send_channel_request("shell") do |ch, success| raise "failed to open ssh shell channel" unless success ch.on_data { |ch,data| @buf << data } ch.on_extended_data { |ch,type,data| @buf << data if type == 1 } ch.on_close { @eof = true } @channel = ch expect(default_prompt, &block) # this is a little bit unorthodox, we're trying to escape # the ssh loop there while still having the ssh connection up # otherwise we wouldn't be able to return ssh stdout/stderr # for a given call of command. return end end @ssh.loop end def close @channel.close if @channel @channel = nil @ssh.close if @ssh end def expect(prompt) line = '' sock = @ssh.transport.socket while not @eof break if line =~ prompt and @buf == '' break if sock.closed? IO::select([sock], [sock], nil, nil) process_ssh # at this point we have accumulated some data in @buf # or the channel has been closed if @buf != "" line += @buf.gsub(/\r\n/no, "\n") @buf = '' yield line if block_given? elsif @eof # channel has been closed break if line =~ prompt if line == '' line = nil yield nil if block_given? end break end end Puppet.debug("ssh: expected #{line}") if @verbose line end def send(line) Puppet.debug("ssh: send #{line}") if @verbose @channel.send_data(line + "\n") end def process_ssh while @buf == "" and not eof? begin @channel.connection.process(0.1) rescue IOError @eof = true end end end -end \ No newline at end of file +end diff --git a/spec/fixtures/releases/jamtur01-apache/metadata.json b/spec/fixtures/releases/jamtur01-apache/metadata.json index 7fb6c0868..346da9b25 100644 --- a/spec/fixtures/releases/jamtur01-apache/metadata.json +++ b/spec/fixtures/releases/jamtur01-apache/metadata.json @@ -1 +1 @@ -{"dependencies":[],"types":[{"providers":[{"name":"a2mod","doc":"Manage Apache 2 modules on Debian and Ubuntu Required binaries: ``a2enmod``, ``a2dismod``. Default for ``operatingsystem`` == ``debianubuntu``. "}],"parameters":[{"name":"name","doc":"The name of the module to be managed"}],"properties":[{"name":"ensure","doc":"The basic property that the resource should be in. Valid values are ``present``, ``absent``."}],"name":"a2mod","doc":"Manage Apache 2 modules on Debian and Ubuntu"}],"checksums":{"manifests/params.pp":"71734796921dbdbfd58f503622527616","tests/ssl.pp":"191912535199531fd631f911c6329e56","tests/vhost.pp":"1b91e03c8ef89a7ecb6793831ac18399","manifests/php.pp":"b78cc593f1c4cd800c906e0891c9b11f","files/httpd":"295f5e924afe6f752d29327e73fe6d0a","tests/php.pp":"ce7bb9eef69d32b42a32ce32d9653625","lib/puppet/provider/a2mod/a2mod.rb":"18c5bb180b75a2375e95e07f88a94257","files/test.vhost":"0602022c19a7b6b289f218c7b93c1aea","manifests/ssl.pp":"b4334a161a2ba5fa8a62cf7b38f352c8","manifests/dev.pp":"510813942246cc9a7786d8f2d8874a35","manifests/vhost.pp":"cbc4657b0cce5cd432057393d5f6b0c2","tests/init.pp":"4eac4a7ef68499854c54a78879e25535","lib/puppet/type/a2mod.rb":"0e1b4843431413a10320ac1f6a055d15","tests/apache.pp":"4eac4a7ef68499854c54a78879e25535","tests/dev.pp":"4cf15c1fecea3ca86009f182b402c7ab","templates/vhost-default.conf.erb":"9055aed946e1111c30ab81fedac2c8b0","manifests/init.pp":"dc503e26e8021351078813b541c4bd3d","Modulefile":"d43334b4072cd1744121b3b25cd9ed15"},"version":"0.0.1","name":"jamtur01-apache"} \ No newline at end of file +{"dependencies":[],"types":[{"providers":[{"name":"a2mod","doc":"Manage Apache 2 modules on Debian and Ubuntu Required binaries: ``a2enmod``, ``a2dismod``. Default for ``operatingsystem`` == ``debianubuntu``. "}],"parameters":[{"name":"name","doc":"The name of the module to be managed"}],"properties":[{"name":"ensure","doc":"The basic property that the resource should be in. Valid values are ``present``, ``absent``."}],"name":"a2mod","doc":"Manage Apache 2 modules on Debian and Ubuntu"}],"checksums":{"manifests/params.pp":"71734796921dbdbfd58f503622527616","tests/ssl.pp":"191912535199531fd631f911c6329e56","tests/vhost.pp":"1b91e03c8ef89a7ecb6793831ac18399","manifests/php.pp":"b78cc593f1c4cd800c906e0891c9b11f","files/httpd":"295f5e924afe6f752d29327e73fe6d0a","tests/php.pp":"ce7bb9eef69d32b42a32ce32d9653625","lib/puppet/provider/a2mod/a2mod.rb":"18c5bb180b75a2375e95e07f88a94257","files/test.vhost":"0602022c19a7b6b289f218c7b93c1aea","manifests/ssl.pp":"b4334a161a2ba5fa8a62cf7b38f352c8","manifests/dev.pp":"510813942246cc9a7786d8f2d8874a35","manifests/vhost.pp":"cbc4657b0cce5cd432057393d5f6b0c2","tests/init.pp":"4eac4a7ef68499854c54a78879e25535","lib/puppet/type/a2mod.rb":"0e1b4843431413a10320ac1f6a055d15","tests/apache.pp":"4eac4a7ef68499854c54a78879e25535","tests/dev.pp":"4cf15c1fecea3ca86009f182b402c7ab","templates/vhost-default.conf.erb":"9055aed946e1111c30ab81fedac2c8b0","manifests/init.pp":"dc503e26e8021351078813b541c4bd3d","Modulefile":"d43334b4072cd1744121b3b25cd9ed15"},"version":"0.0.1","name":"jamtur01-apache"} diff --git a/spec/fixtures/unit/parser/lexer/arithmetic_expression.pp b/spec/fixtures/unit/parser/lexer/arithmetic_expression.pp index aae98a4db..2d27d7d5f 100644 --- a/spec/fixtures/unit/parser/lexer/arithmetic_expression.pp +++ b/spec/fixtures/unit/parser/lexer/arithmetic_expression.pp @@ -1,8 +1,8 @@ $one = 1.30 $two = 2.034e-2 $result = ((( $two + 2) / $one) + 4 * 5.45) - (6 << 7) + (0x800 + -9) -notice("result is $result == 1295.87692307692") \ No newline at end of file +notice("result is $result == 1295.87692307692") diff --git a/spec/fixtures/yaml/report0.25.x.yaml b/spec/fixtures/yaml/report0.25.x.yaml index ce6a64286..b6e4a73d6 100644 --- a/spec/fixtures/yaml/report0.25.x.yaml +++ b/spec/fixtures/yaml/report0.25.x.yaml @@ -1,64 +1,64 @@ --- !ruby/object:Puppet::Transaction::Report host: mattmac.local logs: - !ruby/object:Puppet::Util::Log level: !ruby/sym info message: Applying configuration version '1285283846' source: Puppet tags: - info time: 2010-09-23 16:17:26.977750 -07:00 metrics: time: !ruby/object:Puppet::Util::Metric label: Time name: time values: - - !ruby/sym config_retrieval - Config retrieval - 0.955046892166138 - - !ruby/sym schedule - Schedule - 0.00123691558837891 - - !ruby/sym total - Total - 0.956486701965332 - - !ruby/sym filebucket - Filebucket - 0.00020289421081543 resources: !ruby/object:Puppet::Util::Metric label: Resources name: resources values: - - !ruby/sym skipped - Skipped - 0 - - !ruby/sym scheduled - Scheduled - 7 - - !ruby/sym applied - Applied - 0 - - !ruby/sym restarted - Restarted - 0 - - !ruby/sym total - Total - 10 - - !ruby/sym failed_restarts - Failed restarts - 0 - - !ruby/sym out_of_sync - Out of sync - 0 - - !ruby/sym failed - Failed - 0 changes: !ruby/object:Puppet::Util::Metric label: Changes name: changes values: - - !ruby/sym total - Total - 0 records: {} - time: 2010-09-23 16:17:26.987789 -07:00 \ No newline at end of file + time: 2010-09-23 16:17:26.987789 -07:00 diff --git a/spec/fixtures/yaml/report2.6.x.yaml b/spec/fixtures/yaml/report2.6.x.yaml index dd4c3814e..0eee633b2 100644 --- a/spec/fixtures/yaml/report2.6.x.yaml +++ b/spec/fixtures/yaml/report2.6.x.yaml @@ -1,190 +1,190 @@ --- !ruby/object:Puppet::Transaction::Report external_times: !ruby/sym config_retrieval: 0.170313835144043 host: ubuntu1004desktop.localdomain logs: - !ruby/object:Puppet::Util::Log level: !ruby/sym debug message: Using cached certificate for ca source: Puppet tags: - debug time: 2010-09-23 15:44:06.244173 -07:00 version: &id001 2.6.1 - !ruby/object:Puppet::Util::Log level: !ruby/sym debug message: Using cached certificate for ubuntu1004desktop.localdomain source: Puppet tags: - debug time: 2010-09-23 15:44:06.244764 -07:00 version: *id001 - !ruby/object:Puppet::Util::Log level: !ruby/sym debug message: Using cached certificate_revocation_list for ca source: Puppet tags: - debug time: 2010-09-23 15:44:06.245677 -07:00 version: *id001 - !ruby/object:Puppet::Util::Log level: !ruby/sym debug message: "catalog supports formats: b64_zlib_yaml dot marshal pson raw yaml; using pson" source: Puppet tags: - debug time: 2010-09-23 15:44:06.247069 -07:00 version: *id001 - !ruby/object:Puppet::Util::Log level: !ruby/sym info message: Caching catalog for ubuntu1004desktop.localdomain source: Puppet tags: - info time: 2010-09-23 15:44:06.409109 -07:00 version: *id001 - !ruby/object:Puppet::Util::Log level: !ruby/sym debug message: Creating default schedules source: Puppet tags: - debug time: 2010-09-23 15:44:06.418755 -07:00 version: *id001 - !ruby/object:Puppet::Util::Log level: !ruby/sym debug message: Loaded state in 0.00 seconds source: Puppet tags: - debug time: 2010-09-23 15:44:06.427441 -07:00 version: *id001 - !ruby/object:Puppet::Util::Log level: !ruby/sym info message: Applying configuration version '1285281846' source: Puppet tags: - info time: 2010-09-23 15:44:06.429532 -07:00 version: *id001 metrics: time: !ruby/object:Puppet::Util::Metric label: Time name: time values: - - config_retrieval - Config retrieval - 0.170313835144043 - - schedule - Schedule - 0.00077 - - filebucket - Filebucket - 0.000166 resources: !ruby/object:Puppet::Util::Metric label: Resources name: resources values: - - !ruby/sym total - Total - 7 events: !ruby/object:Puppet::Util::Metric label: Events name: events values: - - !ruby/sym total - Total - 0 changes: !ruby/object:Puppet::Util::Metric label: Changes name: changes values: - - !ruby/sym total - Total - 0 resource_statuses: "Schedule[monthly]": !ruby/object:Puppet::Resource::Status evaluation_time: 0.000121 events: [] file: line: resource: "Schedule[monthly]" source_description: "/Schedule[monthly]" tags: - schedule - monthly time: 2010-09-23 15:44:06.430577 -07:00 version: 1285281846 "Filebucket[puppet]": !ruby/object:Puppet::Resource::Status evaluation_time: 0.000166 events: [] file: line: resource: "Filebucket[puppet]" source_description: "/Filebucket[puppet]" tags: - filebucket - puppet time: 2010-09-23 15:44:06.430998 -07:00 version: 1285281846 "Schedule[never]": !ruby/object:Puppet::Resource::Status evaluation_time: 0.000119 events: [] file: line: resource: "Schedule[never]" source_description: "/Schedule[never]" tags: - schedule - never time: 2010-09-23 15:44:06.433034 -07:00 version: 1285281846 "Schedule[weekly]": !ruby/object:Puppet::Resource::Status evaluation_time: 0.000118 events: [] file: line: resource: "Schedule[weekly]" source_description: "/Schedule[weekly]" tags: - schedule - weekly time: 2010-09-23 15:44:06.431443 -07:00 version: 1285281846 "Schedule[puppet]": !ruby/object:Puppet::Resource::Status evaluation_time: 0.000129 events: [] file: line: resource: "Schedule[puppet]" source_description: "/Schedule[puppet]" tags: - schedule - puppet time: 2010-09-23 15:44:06.432626 -07:00 version: 1285281846 "Schedule[daily]": !ruby/object:Puppet::Resource::Status evaluation_time: 0.000154 events: [] file: line: resource: "Schedule[daily]" source_description: "/Schedule[daily]" tags: - schedule - daily time: 2010-09-23 15:44:06.430130 -07:00 version: 1285281846 "Schedule[hourly]": !ruby/object:Puppet::Resource::Status evaluation_time: 0.000129 events: [] file: line: resource: "Schedule[hourly]" source_description: "/Schedule[hourly]" tags: - schedule - hourly time: 2010-09-23 15:44:06.432185 -07:00 version: 1285281846 - time: 2010-09-23 15:44:05.894401 -07:00 \ No newline at end of file + time: 2010-09-23 15:44:05.894401 -07:00 diff --git a/spec/integration/network/rest_authconfig_spec.rb b/spec/integration/network/rest_authconfig_spec.rb index d2f539cd4..fb21abddd 100644 --- a/spec/integration/network/rest_authconfig_spec.rb +++ b/spec/integration/network/rest_authconfig_spec.rb @@ -1,145 +1,145 @@ require 'spec_helper' require 'puppet/network/rest_authconfig' RSpec::Matchers.define :allow do |params| match do |auth| begin auth.check_authorization(params[0], params[1], params[2], params[3]) true rescue Puppet::Network::AuthorizationError false end end failure_message_for_should do |instance| "expected #{params[3][:node]}/#{params[3][:ip]} to be allowed" end failure_message_for_should_not do |instance| "expected #{params[3][:node]}/#{params[3][:ip]} to be forbidden" end end describe Puppet::Network::RestAuthConfig do include PuppetSpec::Files before(:each) do Puppet[:rest_authconfig] = tmpfile('auth.conf') end def add_rule(rule) File.open(Puppet[:rest_authconfig],"w+") do |f| f.print "path /test\n#{rule}\n" end @auth = Puppet::Network::RestAuthConfig.new(Puppet[:rest_authconfig], true) end def add_regex_rule(regex, rule) File.open(Puppet[:rest_authconfig],"w+") do |f| f.print "path ~ #{regex}\n#{rule}\n" end @auth = Puppet::Network::RestAuthConfig.new(Puppet[:rest_authconfig], true) end def request(args = {}) { :ip => '10.1.1.1', :node => 'host.domain.com', :key => 'key', :authenticated => true }.each do |k,v| args[k] ||= v end ['test', :find, args[:key], args] end it "should support IPv4 address" do add_rule("allow 10.1.1.1") @auth.should allow(request) end it "should support CIDR IPv4 address" do add_rule("allow 10.0.0.0/8") @auth.should allow(request) end it "should support wildcard IPv4 address" do add_rule("allow 10.1.1.*") @auth.should allow(request) end it "should support IPv6 address" do add_rule("allow 2001:DB8::8:800:200C:417A") @auth.should allow(request(:ip => '2001:DB8::8:800:200C:417A')) end it "should support hostname" do add_rule("allow host.domain.com") @auth.should allow(request) end it "should support wildcard host" do add_rule("allow *.domain.com") @auth.should allow(request) end it "should support hostname backreferences" do add_regex_rule('^/test/([^/]+)$', "allow $1.domain.com") @auth.should allow(request(:key => 'host')) end it "should support opaque strings" do add_rule("allow this-is-opaque@or-not") @auth.should allow(request(:node => 'this-is-opaque@or-not')) end it "should support opaque strings and backreferences" do add_regex_rule('^/test/([^/]+)$', "allow $1") @auth.should allow(request(:key => 'this-is-opaque@or-not', :node => 'this-is-opaque@or-not')) end it "should support hostname ending with '.'" do pending('bug #7589') add_rule("allow host.domain.com.") @auth.should allow(request(:node => 'host.domain.com.')) end it "should support hostname ending with '.' and backreferences" do pending('bug #7589') add_regex_rule('^/test/([^/]+)$',"allow $1") @auth.should allow(request(:node => 'host.domain.com.')) end it "should support trailing whitespace" do add_rule('allow host.domain.com ') @auth.should allow(request) end it "should support inlined comments" do add_rule('allow host.domain.com # will it work?') @auth.should allow(request) end it "should deny non-matching host" do add_rule("allow inexistant") @auth.should_not allow(request) end it "should deny denied hosts" do add_rule("deny host.domain.com") @auth.should_not allow(request) end -end \ No newline at end of file +end diff --git a/spec/unit/face/module/install_spec.rb b/spec/unit/face/module/install_spec.rb index 9f67800a4..33101d338 100644 --- a/spec/unit/face/module/install_spec.rb +++ b/spec/unit/face/module/install_spec.rb @@ -1,158 +1,158 @@ require 'spec_helper' require 'puppet/face' require 'puppet/module_tool' describe "puppet module install" do subject { Puppet::Face[:module, :current] } let(:options) do {} end describe "option validation" do before do Puppet.settings[:modulepath] = fakemodpath end let(:expected_options) do { :target_dir => fakefirstpath, :modulepath => fakemodpath, :environment => 'production' } end let(:sep) { File::PATH_SEPARATOR } let(:fakefirstpath) { "/my/fake/modpath" } let(:fakesecondpath) { "/other/fake/path" } let(:fakemodpath) { "#{fakefirstpath}#{sep}#{fakesecondpath}" } let(:fakedirpath) { "/my/fake/path" } context "without any options" do it "should require a name" do pattern = /wrong number of arguments/ expect { subject.install }.to raise_error ArgumentError, pattern end it "should not require any options" do - Puppet::Module::Tool::Applications::Installer.expects(:run).with("puppetlabs-apache", expected_options).once + Puppet::ModuleTool::Applications::Installer.expects(:run).with("puppetlabs-apache", expected_options).once subject.install("puppetlabs-apache") end end it "should accept the --force option" do options[:force] = true expected_options.merge!(options) - Puppet::Module::Tool::Applications::Installer.expects(:run).with("puppetlabs-apache", expected_options).once + Puppet::ModuleTool::Applications::Installer.expects(:run).with("puppetlabs-apache", expected_options).once subject.install("puppetlabs-apache", options) end it "should accept the --target-dir option" do options[:target_dir] = "/foo/puppet/modules" expected_options.merge!(options) expected_options[:modulepath] = "#{options[:target_dir]}#{sep}#{fakemodpath}" - Puppet::Module::Tool::Applications::Installer.expects(:run).with("puppetlabs-apache", expected_options).once + Puppet::ModuleTool::Applications::Installer.expects(:run).with("puppetlabs-apache", expected_options).once subject.install("puppetlabs-apache", options) end it "should accept the --version option" do options[:version] = "0.0.1" expected_options.merge!(options) - Puppet::Module::Tool::Applications::Installer.expects(:run).with("puppetlabs-apache", expected_options).once + Puppet::ModuleTool::Applications::Installer.expects(:run).with("puppetlabs-apache", expected_options).once subject.install("puppetlabs-apache", options) end it "should accept the --ignore-dependencies option" do options[:ignore_dependencies] = true expected_options.merge!(options) - Puppet::Module::Tool::Applications::Installer.expects(:run).with("puppetlabs-apache", expected_options).once + Puppet::ModuleTool::Applications::Installer.expects(:run).with("puppetlabs-apache", expected_options).once subject.install("puppetlabs-apache", options) end describe "when modulepath option is passed" do let(:expected_options) { { :modulepath => fakemodpath, :environment => 'production' } } let(:options) { { :modulepath => fakemodpath } } describe "when target-dir option is not passed" do it "should set target-dir to be first path from modulepath" do expected_options[:target_dir] = fakefirstpath - Puppet::Module::Tool::Applications::Installer. + Puppet::ModuleTool::Applications::Installer. expects(:run). with("puppetlabs-apache", expected_options) Puppet::Face[:module, :current].install("puppetlabs-apache", options) Puppet.settings[:modulepath].should == fakemodpath end end describe "when target-dir option is passed" do it "should set target-dir to be first path of modulepath" do options[:target_dir] = fakedirpath expected_options[:target_dir] = fakedirpath expected_options[:modulepath] = "#{fakedirpath}#{sep}#{fakemodpath}" - Puppet::Module::Tool::Applications::Installer. + Puppet::ModuleTool::Applications::Installer. expects(:run). with("puppetlabs-apache", expected_options) Puppet::Face[:module, :current].install("puppetlabs-apache", options) Puppet.settings[:modulepath].should == "#{fakedirpath}#{sep}#{fakemodpath}" end end end describe "when modulepath option is not passed" do before do Puppet.settings[:modulepath] = fakemodpath end describe "when target-dir option is not passed" do it "should set target-dir to be first path of default mod path" do expected_options[:target_dir] = fakefirstpath expected_options[:modulepath] = fakemodpath - Puppet::Module::Tool::Applications::Installer. + Puppet::ModuleTool::Applications::Installer. expects(:run). with("puppetlabs-apache", expected_options) Puppet::Face[:module, :current].install("puppetlabs-apache", options) end end describe "when target-dir option is passed" do it "should prepend target-dir to modulepath" do options[:target_dir] = fakedirpath expected_options[:target_dir] = fakedirpath expected_options[:modulepath] = "#{options[:target_dir]}#{sep}#{fakemodpath}" - Puppet::Module::Tool::Applications::Installer. + Puppet::ModuleTool::Applications::Installer. expects(:run). with("puppetlabs-apache", expected_options) Puppet::Face[:module, :current].install("puppetlabs-apache", options) Puppet.settings[:modulepath].should == expected_options[:modulepath] end end end end describe "inline documentation" do subject { Puppet::Face[:module, :current].get_action :install } its(:summary) { should =~ /install.*module/im } its(:description) { should =~ /install.*module/im } its(:returns) { should =~ /pathname/i } its(:examples) { should_not be_empty } %w{ license copyright summary description returns examples }.each do |doc| context "of the" do its(doc.to_sym) { should_not =~ /(FIXME|REVISIT|TODO)/ } end end end end diff --git a/spec/unit/face/module/search_spec.rb b/spec/unit/face/module/search_spec.rb index 51f62bd1f..0603becff 100644 --- a/spec/unit/face/module/search_spec.rb +++ b/spec/unit/face/module/search_spec.rb @@ -1,163 +1,163 @@ require 'spec_helper' require 'puppet/face' require 'puppet/application/module' require 'puppet/module_tool' describe "puppet module search", :fails_on_windows => true do subject { Puppet::Face[:module, :current] } let(:options) do {} end describe Puppet::Application::Module do subject do app = Puppet::Application::Module.new app.stubs(:action).returns(Puppet::Face.find_action(:module, :search)) app end before { subject.render_as = :console } before { Puppet::Util::Terminal.stubs(:width).returns(100) } it 'should output nothing when receiving an empty dataset' do subject.render([], ['apache', {}]).should == "No results found for 'apache'." end it 'should output a header when receiving a non-empty dataset' do results = [ {'full_name' => '', 'author' => '', 'desc' => '', 'tag_list' => [] }, ] subject.render(results, ['apache', {}]).should =~ /NAME/ subject.render(results, ['apache', {}]).should =~ /DESCRIPTION/ subject.render(results, ['apache', {}]).should =~ /AUTHOR/ subject.render(results, ['apache', {}]).should =~ /KEYWORDS/ end it 'should output the relevant fields when receiving a non-empty dataset' do results = [ {'full_name' => 'Name', 'author' => 'Author', 'desc' => 'Summary', 'tag_list' => ['tag1', 'tag2'] }, ] subject.render(results, ['apache', {}]).should =~ /Name/ subject.render(results, ['apache', {}]).should =~ /Author/ subject.render(results, ['apache', {}]).should =~ /Summary/ subject.render(results, ['apache', {}]).should =~ /tag1/ subject.render(results, ['apache', {}]).should =~ /tag2/ end it 'should elide really long descriptions' do results = [ { 'full_name' => 'Name', 'author' => 'Author', 'desc' => 'This description is really too long to fit in a single data table, guys -- we should probably set about truncating it', 'tag_list' => ['tag1', 'tag2'], }, ] subject.render(results, ['apache', {}]).should =~ /\.{3} @Author/ end it 'should never truncate the module name' do results = [ { 'full_name' => 'This-module-has-a-really-really-long-name', 'author' => 'Author', 'desc' => 'Description', 'tag_list' => ['tag1', 'tag2'], }, ] subject.render(results, ['apache', {}]).should =~ /This-module-has-a-really-really-long-name/ end it 'should never truncate the author name' do results = [ { 'full_name' => 'Name', 'author' => 'This-author-has-a-really-really-long-name', 'desc' => 'Description', 'tag_list' => ['tag1', 'tag2'], }, ] subject.render(results, ['apache', {}]).should =~ /@This-author-has-a-really-really-long-name/ end it 'should never remove tags that match the search term' do results = [ { 'full_name' => 'Name', 'author' => 'Author', 'desc' => 'Description', 'tag_list' => ['Supercalifragilisticexpialidocious'] + (1..100).map { |i| "tag#{i}" }, }, ] subject.render(results, ['Supercalifragilisticexpialidocious', {}]).should =~ /Supercalifragilisticexpialidocious/ subject.render(results, ['Supercalifragilisticexpialidocious', {}]).should_not =~ /tag/ end { 100 => "NAME DESCRIPTION AUTHOR KEYWORDS#{' '*15}\n"\ "Name This description is really too long to fit ... @JohnnyApples tag1 tag2 taggitty3#{' '*4}\n", 70 => "NAME DESCRIPTION AUTHOR KEYWORDS#{' '*5}\n"\ "Name This description is rea... @JohnnyApples tag1 tag2#{' '*4}\n", 80 => "NAME DESCRIPTION AUTHOR KEYWORDS#{' '*8}\n"\ "Name This description is really too... @JohnnyApples tag1 tag2#{' '*7}\n", 200 => "NAME DESCRIPTION AUTHOR KEYWORDS#{' '*48}\n"\ "Name This description is really too long to fit in a single data table, guys -- we should probably set about trunca... @JohnnyApples tag1 tag2 taggitty3#{' '*37}\n" }.each do |width, expectation| it "should resize the table to fit the screen, when #{width} columns" do results = [ { 'full_name' => 'Name', 'author' => 'JohnnyApples', 'desc' => 'This description is really too long to fit in a single data table, guys -- we should probably set about truncating it', 'tag_list' => ['tag1', 'tag2', 'taggitty3'], }, ] Puppet::Util::Terminal.expects(:width).returns(width) result = subject.render(results, ['apache', {}]) result.lines.sort_by(&:length).last.chomp.length.should <= width result.should == expectation end end end describe "option validation" do context "without any options" do it "should require a search term" do pattern = /wrong number of arguments/ expect { subject.search }.to raise_error ArgumentError, pattern end end it "should accept the --module-repository option" do options[:module_repository] = "http://forge.example.com" - Puppet::Module::Tool::Applications::Searcher.expects(:run).with("puppetlabs-apache", options).once + Puppet::ModuleTool::Applications::Searcher.expects(:run).with("puppetlabs-apache", options).once subject.search("puppetlabs-apache", options) end end describe "inline documentation" do subject { Puppet::Face[:module, :current].get_action :search } its(:summary) { should =~ /search.*module/im } its(:description) { should =~ /search.*module/im } its(:returns) { should =~ /array/i } its(:examples) { should_not be_empty } %w{ license copyright summary description returns examples }.each do |doc| context "of the" do its(doc.to_sym) { should_not =~ /(FIXME|REVISIT|TODO)/ } end end end end diff --git a/spec/unit/face/module/uninstall_spec.rb b/spec/unit/face/module/uninstall_spec.rb index a157df509..74d82672f 100644 --- a/spec/unit/face/module/uninstall_spec.rb +++ b/spec/unit/face/module/uninstall_spec.rb @@ -1,77 +1,77 @@ require 'spec_helper' require 'puppet/face' require 'puppet/module_tool' describe "puppet module uninstall" do subject { Puppet::Face[:module, :current] } let(:options) do {} end describe "option validation" do context "without any options" do it "should require a name" do pattern = /wrong number of arguments/ expect { subject.uninstall }.to raise_error ArgumentError, pattern end it "should not require any options" do - Puppet::Module::Tool::Applications::Uninstaller.expects(:run).once + Puppet::ModuleTool::Applications::Uninstaller.expects(:run).once subject.uninstall("puppetlabs-apache") end end it "should accept the --environment option" do options[:environment] = "development" expected_options = { :environment => 'development' } - Puppet::Module::Tool::Applications::Uninstaller.expects(:run).with("puppetlabs-apache", expected_options).once + Puppet::ModuleTool::Applications::Uninstaller.expects(:run).with("puppetlabs-apache", expected_options).once subject.uninstall("puppetlabs-apache", options) end it "should accept the --modulepath option" do options[:modulepath] = "/foo/puppet/modules" expected_options = { :modulepath => '/foo/puppet/modules', :environment => 'production', } - Puppet::Module::Tool::Applications::Uninstaller.expects(:run).with("puppetlabs-apache", expected_options).once + Puppet::ModuleTool::Applications::Uninstaller.expects(:run).with("puppetlabs-apache", expected_options).once subject.uninstall("puppetlabs-apache", options) end it "should accept the --version option" do options[:version] = "1.0.0" expected_options = { :version => '1.0.0', :environment => 'production', } - Puppet::Module::Tool::Applications::Uninstaller.expects(:run).with("puppetlabs-apache", expected_options).once + Puppet::ModuleTool::Applications::Uninstaller.expects(:run).with("puppetlabs-apache", expected_options).once subject.uninstall("puppetlabs-apache", options) end it "should accept the --force flag" do options[:force] = true expected_options = { :environment => 'production', :force => true, } - Puppet::Module::Tool::Applications::Uninstaller.expects(:run).with("puppetlabs-apache", expected_options).once + Puppet::ModuleTool::Applications::Uninstaller.expects(:run).with("puppetlabs-apache", expected_options).once subject.uninstall("puppetlabs-apache", options) end end describe "inline documentation" do subject { Puppet::Face[:module, :current].get_action :uninstall } its(:summary) { should =~ /uninstall.*module/im } its(:description) { should =~ /uninstall.*module/im } its(:returns) { should =~ /hash of module objects.*/im } its(:examples) { should_not be_empty } %w{ license copyright summary description returns examples }.each do |doc| context "of the" do its(doc.to_sym) { should_not =~ /(FIXME|REVISIT|TODO)/ } end end end end diff --git a/spec/unit/module_spec.rb b/spec/unit/module_spec.rb index adabf55b3..c6b575e15 100755 --- a/spec/unit/module_spec.rb +++ b/spec/unit/module_spec.rb @@ -1,864 +1,864 @@ #!/usr/bin/env rspec require 'spec_helper' require 'puppet_spec/files' require 'puppet_spec/modules' require 'puppet/module_tool/checksums' describe Puppet::Module do include PuppetSpec::Files before do # This is necessary because of the extra checks we have for the deprecated # 'plugins' directory FileTest.stubs(:exist?).returns false end it "should have a class method that returns a named module from a given environment" do env = mock 'module' env.expects(:module).with("mymod").returns "yep" Puppet::Node::Environment.expects(:new).with("myenv").returns env Puppet::Module.find("mymod", "myenv").should == "yep" end it "should return nil if asked for a named module that doesn't exist" do env = mock 'module' env.expects(:module).with("mymod").returns nil Puppet::Node::Environment.expects(:new).with("myenv").returns env Puppet::Module.find("mymod", "myenv").should be_nil end it "should support a 'version' attribute" do mod = Puppet::Module.new("mymod") mod.version = 1.09 mod.version.should == 1.09 end it "should support a 'source' attribute" do mod = Puppet::Module.new("mymod") mod.source = "http://foo/bar" mod.source.should == "http://foo/bar" end it "should support a 'project_page' attribute" do mod = Puppet::Module.new("mymod") mod.project_page = "http://foo/bar" mod.project_page.should == "http://foo/bar" end it "should support an 'author' attribute" do mod = Puppet::Module.new("mymod") mod.author = "Luke Kanies " mod.author.should == "Luke Kanies " end it "should support a 'license' attribute" do mod = Puppet::Module.new("mymod") mod.license = "GPL2" mod.license.should == "GPL2" end it "should support a 'summary' attribute" do mod = Puppet::Module.new("mymod") mod.summary = "GPL2" mod.summary.should == "GPL2" end it "should support a 'description' attribute" do mod = Puppet::Module.new("mymod") mod.description = "GPL2" mod.description.should == "GPL2" end it "should support specifying a compatible puppet version" do mod = Puppet::Module.new("mymod") mod.puppetversion = "0.25" mod.puppetversion.should == "0.25" end it "should validate that the puppet version is compatible" do mod = Puppet::Module.new("mymod") mod.puppetversion = "0.25" Puppet.expects(:version).returns "0.25" mod.validate_puppet_version end it "should fail if the specified puppet version is not compatible" do mod = Puppet::Module.new("mymod") mod.puppetversion = "0.25" Puppet.stubs(:version).returns "0.24" lambda { mod.validate_puppet_version }.should raise_error(Puppet::Module::IncompatibleModule) end describe "when finding unmet dependencies" do before do FileTest.unstub(:exist?) @modpath = tmpdir('modpath') Puppet.settings[:modulepath] = @modpath end it "should list modules that are missing" do mod = PuppetSpec::Modules.create( 'needy', @modpath, :metadata => { :dependencies => [{ "version_requirement" => ">= 2.2.0", "name" => "baz/foobar" }] } ) mod.unmet_dependencies.should == [{ :reason => :missing, :name => "baz/foobar", :version_constraint => ">= 2.2.0", :parent => { :name => 'puppetlabs/needy', :version => 'v9.9.9' }, :mod_details => { :installed_version => nil } }] end it "should list modules that are missing and have invalid names" do mod = PuppetSpec::Modules.create( 'needy', @modpath, :metadata => { :dependencies => [{ "version_requirement" => ">= 2.2.0", "name" => "baz/foobar=bar" }] } ) mod.unmet_dependencies.should == [{ :reason => :missing, :name => "baz/foobar=bar", :version_constraint => ">= 2.2.0", :parent => { :name => 'puppetlabs/needy', :version => 'v9.9.9' }, :mod_details => { :installed_version => nil } }] end it "should list modules with unmet version requirement" do mod = PuppetSpec::Modules.create( 'foobar', @modpath, :metadata => { :dependencies => [{ "version_requirement" => ">= 2.2.0", "name" => "baz/foobar" }] } ) mod2 = PuppetSpec::Modules.create( 'foobaz', @modpath, :metadata => { :dependencies => [{ "version_requirement" => "1.0.0", "name" => "baz/foobar" }] } ) PuppetSpec::Modules.create( 'foobar', @modpath, :metadata => { :version => '2.0.0', :author => 'baz' } ) mod.unmet_dependencies.should == [{ :reason => :version_mismatch, :name => "baz/foobar", :version_constraint => ">= 2.2.0", :parent => { :version => "v9.9.9", :name => "puppetlabs/foobar" }, :mod_details => { :installed_version => "2.0.0" } }] mod2.unmet_dependencies.should == [{ :reason => :version_mismatch, :name => "baz/foobar", :version_constraint => "v1.0.0", :parent => { :version => "v9.9.9", :name => "puppetlabs/foobaz" }, :mod_details => { :installed_version => "2.0.0" } }] end it "should consider a dependency without a version requirement to be satisfied" do mod = PuppetSpec::Modules.create( 'foobar', @modpath, :metadata => { :dependencies => [{ "name" => "baz/foobar" }] } ) PuppetSpec::Modules.create( 'foobar', @modpath, :metadata => { :version => '2.0.0', :author => 'baz' } ) mod.unmet_dependencies.should be_empty end it "should consider a dependency without a semantic version to be unmet" do mod = PuppetSpec::Modules.create( 'foobar', @modpath, :metadata => { :dependencies => [{ "name" => "baz/foobar" }] } ) PuppetSpec::Modules.create( 'foobar', @modpath, :metadata => { :version => '5.1', :author => 'baz' } ) mod.unmet_dependencies.should == [{ :reason => :non_semantic_version, :parent => { :version => "v9.9.9", :name => "puppetlabs/foobar" }, :mod_details => { :installed_version => "5.1" }, :name => "baz/foobar", :version_constraint => ">= 0.0.0" }] end it "should have valid dependencies when no dependencies have been specified" do mod = PuppetSpec::Modules.create( 'foobar', @modpath, :metadata => { :dependencies => [] } ) mod.unmet_dependencies.should == [] end it "should only list unmet dependencies" do mod = PuppetSpec::Modules.create( 'mymod', @modpath, :metadata => { :dependencies => [ { "version_requirement" => ">= 2.2.0", "name" => "baz/satisfied" }, { "version_requirement" => ">= 2.2.0", "name" => "baz/notsatisfied" } ] } ) PuppetSpec::Modules.create( 'satisfied', @modpath, :metadata => { :version => '3.3.0', :author => 'baz' } ) mod.unmet_dependencies.should == [{ :reason => :missing, :mod_details => { :installed_version => nil }, :parent => { :version => "v9.9.9", :name => "puppetlabs/mymod" }, :name => "baz/notsatisfied", :version_constraint => ">= 2.2.0" }] end it "should be empty when all dependencies are met" do mod = PuppetSpec::Modules.create( 'mymod2', @modpath, :metadata => { :dependencies => [ { "version_requirement" => ">= 2.2.0", "name" => "baz/satisfied" }, { "version_requirement" => "< 2.2.0", "name" => "baz/alsosatisfied" } ] } ) PuppetSpec::Modules.create( 'satisfied', @modpath, :metadata => { :version => '3.3.0', :author => 'baz' } ) PuppetSpec::Modules.create( 'alsosatisfied', @modpath, :metadata => { :version => '2.1.0', :author => 'baz' } ) mod.unmet_dependencies.should be_empty end end describe "when managing supported platforms" do it "should support specifying a supported platform" do mod = Puppet::Module.new("mymod") mod.supports "solaris" end it "should support specifying a supported platform and version" do mod = Puppet::Module.new("mymod") mod.supports "solaris", 1.0 end it "should fail when not running on a supported platform" do pending "Not sure how to send client platform to the module" mod = Puppet::Module.new("mymod") Facter.expects(:value).with("operatingsystem").returns "Solaris" mod.supports "hpux" lambda { mod.validate_supported_platform }.should raise_error(Puppet::Module::UnsupportedPlatform) end it "should fail when supported platforms are present but of the wrong version" do pending "Not sure how to send client platform to the module" mod = Puppet::Module.new("mymod") Facter.expects(:value).with("operatingsystem").returns "Solaris" Facter.expects(:value).with("operatingsystemrelease").returns 2.0 mod.supports "Solaris", 1.0 lambda { mod.validate_supported_platform }.should raise_error(Puppet::Module::IncompatiblePlatform) end it "should be considered supported when no supported platforms have been specified" do pending "Not sure how to send client platform to the module" mod = Puppet::Module.new("mymod") lambda { mod.validate_supported_platform }.should_not raise_error end it "should be considered supported when running on a supported platform" do pending "Not sure how to send client platform to the module" mod = Puppet::Module.new("mymod") Facter.expects(:value).with("operatingsystem").returns "Solaris" Facter.expects(:value).with("operatingsystemrelease").returns 2.0 mod.supports "Solaris", 1.0 lambda { mod.validate_supported_platform }.should raise_error(Puppet::Module::IncompatiblePlatform) end it "should be considered supported when running on any of multiple supported platforms" do pending "Not sure how to send client platform to the module" end it "should validate its platform support on initialization" do pending "Not sure how to send client platform to the module" end end it "should return nil if asked for a module whose name is 'nil'" do Puppet::Module.find(nil, "myenv").should be_nil end it "should provide support for logging" do Puppet::Module.ancestors.should be_include(Puppet::Util::Logging) end it "should be able to be converted to a string" do Puppet::Module.new("foo").to_s.should == "Module foo" end it "should add the path to its string form if the module is found" do mod = Puppet::Module.new("foo") mod.stubs(:path).returns "/a" mod.to_s.should == "Module foo(/a)" end it "should fail if its name is not alphanumeric" do lambda { Puppet::Module.new(".something") }.should raise_error(Puppet::Module::InvalidName) end it "should require a name at initialization" do lambda { Puppet::Module.new }.should raise_error(ArgumentError) end it "should convert an environment name into an Environment instance" do Puppet::Module.new("foo", :environment => "prod").environment.should be_instance_of(Puppet::Node::Environment) end it "should accept an environment at initialization" do Puppet::Module.new("foo", :environment => :prod).environment.name.should == :prod end it "should use the default environment if none is provided" do env = Puppet::Node::Environment.new Puppet::Module.new("foo").environment.should equal(env) end it "should use any provided Environment instance" do env = Puppet::Node::Environment.new Puppet::Module.new("foo", :environment => env).environment.should equal(env) end describe ".path" do before do dir = tmpdir("deep_path") @first = File.join(dir, "first") @second = File.join(dir, "second") Puppet[:modulepath] = "#{@first}#{File::PATH_SEPARATOR}#{@second}" FileUtils.mkdir_p(@first) FileUtils.mkdir_p(@second) end it "should return the path to the first found instance in its environment's module paths as its path" do modpath = File.join(@first, "foo") FileUtils.mkdir_p(modpath) # Make a second one, which we shouldn't find FileUtils.mkdir_p(File.join(@second, "foo")) mod = Puppet::Module.new("foo") mod.path.should == modpath end it "should be able to find itself in a directory other than the first directory in the module path" do modpath = File.join(@second, "foo") FileUtils.mkdir_p(modpath) mod = Puppet::Module.new("foo") mod.should be_exist mod.path.should == modpath end it "should be able to find itself in a directory other than the first directory in the module path even when it exists in the first" do environment = Puppet::Node::Environment.new first_modpath = File.join(@first, "foo") FileUtils.mkdir_p(first_modpath) second_modpath = File.join(@second, "foo") FileUtils.mkdir_p(second_modpath) mod = Puppet::Module.new("foo", :environment => environment, :path => second_modpath) mod.path.should == File.join(@second, "foo") mod.environment.should == environment end end describe '#modulepath' do it "should return the directory the module is installed in, if a path exists" do mod = Puppet::Module.new("foo") mod.stubs(:path).returns "/a/foo" mod.modulepath.should == '/a' end it "should return nil if no path exists" do mod = Puppet::Module.new("foo") mod.stubs(:path).returns nil mod.modulepath.should be_nil end end it "should be considered existent if it exists in at least one module path" do mod = Puppet::Module.new("foo") mod.expects(:path).returns "/a/foo" mod.should be_exist end it "should be considered nonexistent if it does not exist in any of the module paths" do mod = Puppet::Module.new("foo") mod.expects(:path).returns nil mod.should_not be_exist end [:plugins, :templates, :files, :manifests].each do |filetype| dirname = filetype == :plugins ? "lib" : filetype.to_s it "should be able to return individual #{filetype}" do mod = Puppet::Module.new("foo") mod.stubs(:path).returns "/a/foo" path = File.join("/a/foo", dirname, "my/file") FileTest.expects(:exist?).with(path).returns true mod.send(filetype.to_s.sub(/s$/, ''), "my/file").should == path end it "should consider #{filetype} to be present if their base directory exists" do mod = Puppet::Module.new("foo") mod.stubs(:path).returns "/a/foo" path = File.join("/a/foo", dirname) FileTest.expects(:exist?).with(path).returns true mod.send(filetype.to_s + "?").should be_true end it "should consider #{filetype} to be absent if their base directory does not exist" do mod = Puppet::Module.new("foo") mod.stubs(:path).returns "/a/foo" path = File.join("/a/foo", dirname) FileTest.expects(:exist?).with(path).returns false mod.send(filetype.to_s + "?").should be_false end it "should consider #{filetype} to be absent if the module base directory does not exist" do mod = Puppet::Module.new("foo") mod.stubs(:path).returns nil mod.send(filetype.to_s + "?").should be_false end it "should return nil if asked to return individual #{filetype} that don't exist" do mod = Puppet::Module.new("foo") mod.stubs(:path).returns "/a/foo" path = File.join("/a/foo", dirname, "my/file") FileTest.expects(:exist?).with(path).returns false mod.send(filetype.to_s.sub(/s$/, ''), "my/file").should be_nil end it "should return nil when asked for individual #{filetype} if the module does not exist" do mod = Puppet::Module.new("foo") mod.stubs(:path).returns nil mod.send(filetype.to_s.sub(/s$/, ''), "my/file").should be_nil end it "should return the base directory if asked for a nil path" do mod = Puppet::Module.new("foo") mod.stubs(:path).returns "/a/foo" base = File.join("/a/foo", dirname) FileTest.expects(:exist?).with(base).returns true mod.send(filetype.to_s.sub(/s$/, ''), nil).should == base end end %w{plugins files}.each do |filetype| short = filetype.sub(/s$/, '') dirname = filetype == "plugins" ? "lib" : filetype.to_s it "should be able to return the #{short} directory" do Puppet::Module.new("foo").should respond_to(short + "_directory") end it "should return the path to the #{short} directory" do mod = Puppet::Module.new("foo") mod.stubs(:path).returns "/a/foo" mod.send(short + "_directory").should == "/a/foo/#{dirname}" end end it "should throw a warning if plugins are in a 'plugins' directory rather than a 'lib' directory" do mod = Puppet::Module.new("foo") mod.stubs(:path).returns "/a/foo" FileTest.expects(:exist?).with("/a/foo/plugins").returns true mod.plugin_directory.should == "/a/foo/plugins" @logs.first.message.should == "using the deprecated 'plugins' directory for ruby extensions; please move to 'lib'" @logs.first.level.should == :warning end it "should default to 'lib' for the plugins directory" do mod = Puppet::Module.new("foo") mod.stubs(:path).returns "/a/foo" mod.plugin_directory.should == "/a/foo/lib" end end describe Puppet::Module, "when finding matching manifests" do before do @mod = Puppet::Module.new("mymod") @mod.stubs(:path).returns "/a" @pq_glob_with_extension = "yay/*.xx" @fq_glob_with_extension = "/a/manifests/#{@pq_glob_with_extension}" end it "should return all manifests matching the glob pattern" do Dir.expects(:glob).with(@fq_glob_with_extension).returns(%w{foo bar}) FileTest.stubs(:directory?).returns false @mod.match_manifests(@pq_glob_with_extension).should == %w{foo bar} end it "should not return directories" do Dir.expects(:glob).with(@fq_glob_with_extension).returns(%w{foo bar}) FileTest.expects(:directory?).with("foo").returns false FileTest.expects(:directory?).with("bar").returns true @mod.match_manifests(@pq_glob_with_extension).should == %w{foo} end it "should default to the 'init' file if no glob pattern is specified" do Dir.expects(:glob).with("/a/manifests/init.{pp,rb}").returns(%w{/a/manifests/init.pp}) @mod.match_manifests(nil).should == %w{/a/manifests/init.pp} end it "should return all manifests matching the glob pattern in all existing paths" do Dir.expects(:glob).with(@fq_glob_with_extension).returns(%w{a b}) @mod.match_manifests(@pq_glob_with_extension).should == %w{a b} end it "should match the glob pattern plus '.{pp,rb}' if no extention is specified" do Dir.expects(:glob).with("/a/manifests/yay/foo.{pp,rb}").returns(%w{yay}) @mod.match_manifests("yay/foo").should == %w{yay} end it "should return an empty array if no manifests matched" do Dir.expects(:glob).with(@fq_glob_with_extension).returns([]) @mod.match_manifests(@pq_glob_with_extension).should == [] end end describe Puppet::Module do include PuppetSpec::Files before do @modpath = tmpdir('modpath') @module = PuppetSpec::Modules.create('mymod', @modpath) end it "should use 'License' in its current path as its metadata file" do @module.license_file.should == "#{@modpath}/mymod/License" end it "should return nil as its license file when the module has no path" do Puppet::Module.any_instance.stubs(:path).returns nil Puppet::Module.new("foo").license_file.should be_nil end it "should cache the license file" do @module.expects(:path).once.returns nil @module.license_file @module.license_file end it "should use 'metadata.json' in its current path as its metadata file" do @module.metadata_file.should == "#{@modpath}/mymod/metadata.json" end it "should return nil as its metadata file when the module has no path" do Puppet::Module.any_instance.stubs(:path).returns nil Puppet::Module.new("foo").metadata_file.should be_nil end it "should cache the metadata file" do Puppet::Module.any_instance.expects(:path).once.returns nil mod = Puppet::Module.new("foo") mod.metadata_file.should == mod.metadata_file end it "should have metadata if it has a metadata file and its data is not empty" do FileTest.expects(:exist?).with(@module.metadata_file).returns true File.stubs(:read).with(@module.metadata_file).returns "{\"foo\" : \"bar\"}" @module.should be_has_metadata end it "should have metadata if it has a metadata file and its data is not empty" do FileTest.expects(:exist?).with(@module.metadata_file).returns true File.stubs(:read).with(@module.metadata_file).returns "{\"foo\" : \"bar\"}" @module.should be_has_metadata end it "should not have metadata if has a metadata file and its data is empty" do FileTest.expects(:exist?).with(@module.metadata_file).returns true File.stubs(:read).with(@module.metadata_file).returns "/* +-----------------------------------------------------------------------+ | | | ==> DO NOT EDIT THIS FILE! <== | | | | You should edit the `Modulefile` and run `puppet-module build` | | to generate the `metadata.json` file for your releases. | | | +-----------------------------------------------------------------------+ */ {}" @module.should_not be_has_metadata end it "should know if it is missing a metadata file" do FileTest.expects(:exist?).with(@module.metadata_file).returns false @module.should_not be_has_metadata end it "should be able to parse its metadata file" do @module.should respond_to(:load_metadata) end it "should parse its metadata file on initialization if it is present" do Puppet::Module.any_instance.expects(:has_metadata?).returns true Puppet::Module.any_instance.expects(:load_metadata) Puppet::Module.new("yay") end describe "when loading the metadata file", :if => Puppet.features.pson? do before do @data = { :license => "GPL2", :author => "luke", :version => "1.0", :source => "http://foo/", :puppetversion => "0.25", :dependencies => [] } @text = @data.to_pson @module = Puppet::Module.new("foo") @module.stubs(:metadata_file).returns "/my/file" File.stubs(:read).with("/my/file").returns @text end %w{source author version license}.each do |attr| it "should set #{attr} if present in the metadata file" do @module.load_metadata @module.send(attr).should == @data[attr.to_sym] end it "should fail if #{attr} is not present in the metadata file" do @data.delete(attr.to_sym) @text = @data.to_pson File.stubs(:read).with("/my/file").returns @text lambda { @module.load_metadata }.should raise_error( Puppet::Module::MissingMetadata, "No #{attr} module metadata provided for foo" ) end end it "should set puppetversion if present in the metadata file" do @module.load_metadata @module.puppetversion.should == @data[:puppetversion] end context "when versionRequirement is used for dependency version info" do before do @data = { :license => "GPL2", :author => "luke", :version => "1.0", :source => "http://foo/", :puppetversion => "0.25", :dependencies => [ { "versionRequirement" => "0.0.1", "name" => "pmtacceptance/stdlib" }, { "versionRequirement" => "0.1.0", "name" => "pmtacceptance/apache" } ] } @text = @data.to_pson @module = Puppet::Module.new("foo") @module.stubs(:metadata_file).returns "/my/file" File.stubs(:read).with("/my/file").returns @text end it "should set the dependency version_requirement key" do @module.load_metadata @module.dependencies[0]['version_requirement'].should == "0.0.1" end it "should set the version_requirement key for all dependencies" do @module.load_metadata @module.dependencies[0]['version_requirement'].should == "0.0.1" @module.dependencies[1]['version_requirement'].should == "0.1.0" end end it "should fail if the discovered name is different than the metadata name" end it "should be able to tell if there are local changes", :fails_on_windows => true do modpath = tmpdir('modpath') foo_checksum = 'acbd18db4cc2f85cedef654fccc4a4d8' checksummed_module = PuppetSpec::Modules.create( 'changed', modpath, :metadata => { :checksums => { "foo" => foo_checksum, } } ) foo_path = Pathname.new(File.join(checksummed_module.path, 'foo')) IO.binwrite(foo_path, 'notfoo') - Puppet::Module::Tool::Checksums.new(foo_path).checksum(foo_path).should_not == foo_checksum + Puppet::ModuleTool::Checksums.new(foo_path).checksum(foo_path).should_not == foo_checksum checksummed_module.has_local_changes?.should be_true IO.binwrite(foo_path, 'foo') - Puppet::Module::Tool::Checksums.new(foo_path).checksum(foo_path).should == foo_checksum + Puppet::ModuleTool::Checksums.new(foo_path).checksum(foo_path).should == foo_checksum checksummed_module.has_local_changes?.should be_false end it "should know what other modules require it" do Puppet.settings[:modulepath] = @modpath dependable = PuppetSpec::Modules.create( 'dependable', @modpath, :metadata => {:author => 'puppetlabs'} ) PuppetSpec::Modules.create( 'needy', @modpath, :metadata => { :author => 'beggar', :dependencies => [{ "version_requirement" => ">= 2.2.0", "name" => "puppetlabs/dependable" }] } ) PuppetSpec::Modules.create( 'wantit', @modpath, :metadata => { :author => 'spoiled', :dependencies => [{ "version_requirement" => "< 5.0.0", "name" => "puppetlabs/dependable" }] } ) dependable.required_by.should =~ [ { "name" => "beggar/needy", "version" => "9.9.9", "version_requirement" => ">= 2.2.0" }, { "name" => "spoiled/wantit", "version" => "9.9.9", "version_requirement" => "< 5.0.0" } ] end end diff --git a/spec/unit/module_tool/application_spec.rb b/spec/unit/module_tool/application_spec.rb index a2ef184f7..f6b039230 100644 --- a/spec/unit/module_tool/application_spec.rb +++ b/spec/unit/module_tool/application_spec.rb @@ -1,27 +1,27 @@ require 'spec_helper' require 'puppet/module_tool' -describe Puppet::Module::Tool::Applications::Application, :fails_on_windows => true do +describe Puppet::ModuleTool::Applications::Application, :fails_on_windows => true do describe 'app' do good_versions = %w{ 1.2.4 0.0.1 0.0.0 0.0.2-git-8-g3d316d1 0.0.3-b1 10.100.10000 0.1.2-rc1 0.1.2-dev-1 0.1.2-svn12345 0.1.2-3 } bad_versions = %w{ 0.1 0 0.1.2.3 dev 0.1.2beta } before do @app = Class.new(described_class).new end good_versions.each do |ver| it "should accept version string #{ver}" do @app.parse_filename("puppetlabs-ntp-#{ver}") end end bad_versions.each do |ver| it "should not accept version string #{ver}" do lambda { @app.parse_filename("puppetlabs-ntp-#{ver}") }.should raise_error end end end end diff --git a/spec/unit/module_tool/applications/application_spec.rb b/spec/unit/module_tool/applications/application_spec.rb index 101079f61..c559e4dd9 100644 --- a/spec/unit/module_tool/applications/application_spec.rb +++ b/spec/unit/module_tool/applications/application_spec.rb @@ -1,19 +1,19 @@ require 'spec_helper' require 'puppet/module_tool/applications' -describe Puppet::Module::Tool::Applications do - module Puppet::Module::Tool +describe Puppet::ModuleTool::Applications do + module Puppet::ModuleTool module Applications class Fake < Application end end end it "should raise an error on microsoft windows" do Puppet.features.stubs(:microsoft_windows?).returns true - expect { Puppet::Module::Tool::Applications::Fake.new }.to raise_error( + expect { Puppet::ModuleTool::Applications::Fake.new }.to raise_error( Puppet::Error, "`puppet module` actions are currently not supported on Microsoft Windows" ) end end diff --git a/spec/unit/module_tool/applications/installer_spec.rb b/spec/unit/module_tool/applications/installer_spec.rb index 1ad80ee60..09e3f9a20 100644 --- a/spec/unit/module_tool/applications/installer_spec.rb +++ b/spec/unit/module_tool/applications/installer_spec.rb @@ -1,205 +1,205 @@ require 'spec_helper' require 'puppet/module_tool/applications' require 'puppet_spec/modules' require 'semver' -describe Puppet::Module::Tool::Applications::Installer, :fails_on_windows => true do +describe Puppet::ModuleTool::Applications::Installer, :fails_on_windows => true do include PuppetSpec::Files before do FileUtils.mkdir_p(modpath1) fake_env.modulepath = [modpath1] FileUtils.touch(stdlib_pkg) Puppet.settings[:modulepath] = modpath1 Puppet::Forge.stubs(:remote_dependency_info).returns(remote_dependency_info) Puppet::Forge.stubs(:repository).returns(repository) end let(:unpacker) { stub(:run) } - let(:installer_class) { Puppet::Module::Tool::Applications::Installer } + let(:installer_class) { Puppet::ModuleTool::Applications::Installer } let(:modpath1) { File.join(tmpdir("installer"), "modpath1") } let(:stdlib_pkg) { File.join(modpath1, "pmtacceptance-stdlib-0.0.1.tar.gz") } let(:fake_env) { Puppet::Node::Environment.new('fake_env') } let(:options) { Hash[:target_dir => modpath1] } let(:repository) do repository = mock() repository.stubs(:uri => 'forge-dev.puppetlabs.com') releases = remote_dependency_info.each_key do |mod| remote_dependency_info[mod].each do |release| repository.stubs(:retrieve).with(release['file'])\ .returns("/fake_cache#{release['file']}") end end repository end let(:remote_dependency_info) do { "pmtacceptance/stdlib" => [ { "dependencies" => [], "version" => "0.0.1", "file" => "/pmtacceptance-stdlib-0.0.1.tar.gz" }, { "dependencies" => [], "version" => "0.0.2", "file" => "/pmtacceptance-stdlib-0.0.2.tar.gz" }, { "dependencies" => [], "version" => "1.0.0", "file" => "/pmtacceptance-stdlib-1.0.0.tar.gz" } ], "pmtacceptance/java" => [ { "dependencies" => [["pmtacceptance/stdlib", ">= 0.0.1"]], "version" => "1.7.0", "file" => "/pmtacceptance-java-1.7.0.tar.gz" }, { "dependencies" => [["pmtacceptance/stdlib", "1.0.0"]], "version" => "1.7.1", "file" => "/pmtacceptance-java-1.7.1.tar.gz" } ], "pmtacceptance/apollo" => [ { "dependencies" => [ ["pmtacceptance/java", "1.7.1"], ["pmtacceptance/stdlib", "0.0.1"] ], "version" => "0.0.1", "file" => "/pmtacceptance-apollo-0.0.1.tar.gz" }, { "dependencies" => [ ["pmtacceptance/java", ">= 1.7.0"], ["pmtacceptance/stdlib", ">= 1.0.0"] ], "version" => "0.0.2", "file" => "/pmtacceptance-apollo-0.0.2.tar.gz" } ] } end describe "the behavior of .is_module_package?" do it "should return true when file is a module package" do installer = installer_class.new("foo", options) installer.send(:is_module_package?, stdlib_pkg).should be_true end it "should return false when file is not a module package" do installer = installer_class.new("foo", options) installer.send(:is_module_package?, "pmtacceptance-apollo-0.0.2.tar").should be_false end end context "when the source is a repository" do it "should require a valid name" do lambda { installer_class.run('puppet', params) }.should raise_error(ArgumentError, "Could not install module with invalid name: puppet") end it "should install the requested module" do - Puppet::Module::Tool::Applications::Unpacker.expects(:new)\ + Puppet::ModuleTool::Applications::Unpacker.expects(:new)\ .with('/fake_cache/pmtacceptance-stdlib-1.0.0.tar.gz', options)\ .returns(unpacker) results = installer_class.run('pmtacceptance-stdlib', options) results[:installed_modules].length == 1 results[:installed_modules][0][:module].should == "pmtacceptance-stdlib" results[:installed_modules][0][:version][:vstring].should == "1.0.0" end context "when the requested module has dependencies" do it "should install dependencies" do - Puppet::Module::Tool::Applications::Unpacker.expects(:new)\ + Puppet::ModuleTool::Applications::Unpacker.expects(:new)\ .with('/fake_cache/pmtacceptance-stdlib-1.0.0.tar.gz', options)\ .returns(unpacker) - Puppet::Module::Tool::Applications::Unpacker.expects(:new)\ + Puppet::ModuleTool::Applications::Unpacker.expects(:new)\ .with('/fake_cache/pmtacceptance-apollo-0.0.2.tar.gz', options)\ .returns(unpacker) - Puppet::Module::Tool::Applications::Unpacker.expects(:new)\ + Puppet::ModuleTool::Applications::Unpacker.expects(:new)\ .with('/fake_cache/pmtacceptance-java-1.7.1.tar.gz', options)\ .returns(unpacker) results = installer_class.run('pmtacceptance-apollo', options) installed_dependencies = results[:installed_modules][0][:dependencies] dependencies = installed_dependencies.inject({}) do |result, dep| result[dep[:module]] = dep[:version][:vstring] result end dependencies.length.should == 2 dependencies['pmtacceptance-java'].should == '1.7.1' dependencies['pmtacceptance-stdlib'].should == '1.0.0' end it "should install requested module if the '--force' flag is used" do options = { :force => true, :target_dir => modpath1 } - Puppet::Module::Tool::Applications::Unpacker.expects(:new)\ + Puppet::ModuleTool::Applications::Unpacker.expects(:new)\ .with('/fake_cache/pmtacceptance-apollo-0.0.2.tar.gz', options)\ .returns(unpacker) results = installer_class.run('pmtacceptance-apollo', options) results[:installed_modules][0][:module].should == "pmtacceptance-apollo" end it "should not install dependencies if the '--force' flag is used" do options = { :force => true, :target_dir => modpath1 } - Puppet::Module::Tool::Applications::Unpacker.expects(:new)\ + Puppet::ModuleTool::Applications::Unpacker.expects(:new)\ .with('/fake_cache/pmtacceptance-apollo-0.0.2.tar.gz', options)\ .returns(unpacker) results = installer_class.run('pmtacceptance-apollo', options) dependencies = results[:installed_modules][0][:dependencies] dependencies.should == [] end it "should not install dependencies if the '--ignore-dependencies' flag is used" do options = { :ignore_dependencies => true, :target_dir => modpath1 } - Puppet::Module::Tool::Applications::Unpacker.expects(:new)\ + Puppet::ModuleTool::Applications::Unpacker.expects(:new)\ .with('/fake_cache/pmtacceptance-apollo-0.0.2.tar.gz', options)\ .returns(unpacker) results = installer_class.run('pmtacceptance-apollo', options) dependencies = results[:installed_modules][0][:dependencies] dependencies.should == [] end it "should set an error if dependencies can't be resolved" do options = { :version => '0.0.1', :target_dir => modpath1 } oneline = "'pmtacceptance-apollo' (v0.0.1) requested; Invalid dependency cycle" multiline = <<-MSG.strip Could not install module 'pmtacceptance-apollo' (v0.0.1) No version of 'pmtacceptance-stdlib' will satisfy dependencies You specified 'pmtacceptance-apollo' (v0.0.1), which depends on 'pmtacceptance-java' (v1.7.1), which depends on 'pmtacceptance-stdlib' (v1.0.0) Use `puppet module install --force` to install this module anyway MSG results = installer_class.run('pmtacceptance-apollo', options) results[:result].should == :failure results[:error][:oneline].should == oneline results[:error][:multiline].should == multiline end end context "when there are modules installed" do it "should use local version when already exists and satisfies constraints" it "should reinstall the local version if force is used" it "should upgrade local version when necessary to satisfy constraints" it "should error when a local version can't be upgraded to satisfy constraints" end context "when a local module needs upgrading to satisfy constraints but has changes" do it "should error" it "should warn and continue if force is used" end it "should error when a local version of a dependency has no version metadata" it "should error when a local version of a dependency has a non-semver version" it "should error when a local version of a dependency has a different forge name" it "should error when a local version of a dependency has no metadata" end context "when the source is a filesystem" do before do @sourcedir = tmpdir('sourcedir') end it "should error if it can't parse the name" it "should try to get_release_package_from_filesystem if it has a valid name" end end diff --git a/spec/unit/module_tool/applications/uninstaller_spec.rb b/spec/unit/module_tool/applications/uninstaller_spec.rb index e8a4b3f46..67b39fbd9 100644 --- a/spec/unit/module_tool/applications/uninstaller_spec.rb +++ b/spec/unit/module_tool/applications/uninstaller_spec.rb @@ -1,206 +1,206 @@ require 'spec_helper' require 'puppet/module_tool' require 'tmpdir' require 'puppet_spec/modules' -describe Puppet::Module::Tool::Applications::Uninstaller, :fails_on_windows => true do +describe Puppet::ModuleTool::Applications::Uninstaller, :fails_on_windows => true do include PuppetSpec::Files def mkmod(name, path, metadata=nil) modpath = File.join(path, name) FileUtils.mkdir_p(modpath) if metadata File.open(File.join(modpath, 'metadata.json'), 'w') do |f| f.write(metadata.to_pson) end end modpath end describe "the behavior of the instances" do before do - @uninstaller = Puppet::Module::Tool::Applications::Uninstaller + @uninstaller = Puppet::ModuleTool::Applications::Uninstaller FileUtils.mkdir_p(modpath1) FileUtils.mkdir_p(modpath2) fake_env.modulepath = [modpath1, modpath2] end let(:modpath1) { File.join(tmpdir("uninstaller"), "modpath1") } let(:modpath2) { File.join(tmpdir("uninstaller"), "modpath2") } let(:fake_env) { Puppet::Node::Environment.new('fake_env') } let(:options) { {:environment => "fake_env"} } let(:foo_metadata) do { :author => "puppetlabs", :name => "puppetlabs/foo", :version => "1.0.0", :source => "http://dummyurl/foo", :license => "Apache2", :dependencies => [], } end let(:bar_metadata) do { :author => "puppetlabs", :name => "puppetlabs/bar", :version => "1.0.0", :source => "http://dummyurl/bar", :license => "Apache2", :dependencies => [], } end context "when the module is not installed" do it "should fail" do @uninstaller.new('fakemod_not_installed', options).run[:result].should == :failure end end context "when the module is installed" do it "should uninstall the module" do PuppetSpec::Modules.create('foo', modpath1, :metadata => foo_metadata) results = @uninstaller.new("puppetlabs-foo", options).run results[:affected_modules].first.forge_name.should == "puppetlabs/foo" end it "should only uninstall the requested module" do PuppetSpec::Modules.create('foo', modpath1, :metadata => foo_metadata) PuppetSpec::Modules.create('bar', modpath1, :metadata => bar_metadata) results = @uninstaller.new("puppetlabs-foo", options).run results[:affected_modules].length == 1 results[:affected_modules].first.forge_name.should == "puppetlabs/foo" end it "should uninstall fail if a module exists twice in the modpath" do PuppetSpec::Modules.create('foo', modpath1, :metadata => foo_metadata) PuppetSpec::Modules.create('foo', modpath2, :metadata => foo_metadata) @uninstaller.new('puppetlabs-foo', options).run[:result].should == :failure end context "when options[:version] is specified" do it "should uninstall the module if the version matches" do PuppetSpec::Modules.create('foo', modpath1, :metadata => foo_metadata) options[:version] = "1.0.0" results = @uninstaller.new("puppetlabs-foo", options).run results[:affected_modules].length.should == 1 results[:affected_modules].first.forge_name.should == "puppetlabs/foo" results[:affected_modules].first.version.should == "1.0.0" end it "should not uninstall the module if the version does not match" do PuppetSpec::Modules.create('foo', modpath1, :metadata => foo_metadata) options[:version] = "2.0.0" @uninstaller.new("puppetlabs-foo", options).run[:result].should == :failure end end context "when the module metadata is missing" do it "should not uninstall the module" do PuppetSpec::Modules.create('foo', modpath1) @uninstaller.new("puppetlabs-foo", options).run[:result].should == :failure end end context "when the module has local changes" do it "should not uninstall the module" do PuppetSpec::Modules.create('foo', modpath1, :metadata => foo_metadata) Puppet::Module.any_instance.stubs(:has_local_changes?).returns(true) @uninstaller.new("puppetlabs-foo", options).run[:result].should == :failure end end context "when the module does not have local changes" do it "should uninstall the module" do PuppetSpec::Modules.create('foo', modpath1, :metadata => foo_metadata) results = @uninstaller.new("puppetlabs-foo", options).run results[:affected_modules].length.should == 1 results[:affected_modules].first.forge_name.should == "puppetlabs/foo" end end context "when uninstalling the module will cause broken dependencies" do it "should not uninstall the module" do Puppet.settings[:modulepath] = modpath1 PuppetSpec::Modules.create('foo', modpath1, :metadata => foo_metadata) PuppetSpec::Modules.create( 'needy', modpath1, :metadata => { :author => 'beggar', :dependencies => [{ "version_requirement" => ">= 1.0.0", "name" => "puppetlabs/foo" }] } ) @uninstaller.new("puppetlabs-foo", options).run[:result].should == :failure end end context "when using the --force flag" do let(:fakemod) do stub( :forge_name => 'puppetlabs/fakemod', :version => '0.0.1', :has_local_changes? => true ) end it "should ignore local changes" do foo = mkmod("foo", modpath1, foo_metadata) options[:force] = true results = @uninstaller.new("puppetlabs-foo", options).run results[:affected_modules].length.should == 1 results[:affected_modules].first.forge_name.should == "puppetlabs/foo" end it "should ignore broken dependencies" do Puppet.settings[:modulepath] = modpath1 PuppetSpec::Modules.create('foo', modpath1, :metadata => foo_metadata) PuppetSpec::Modules.create( 'needy', modpath1, :metadata => { :author => 'beggar', :dependencies => [{ "version_requirement" => ">= 1.0.0", "name" => "puppetlabs/foo" }] } ) options[:force] = true results = @uninstaller.new("puppetlabs-foo", options).run results[:affected_modules].length.should == 1 results[:affected_modules].first.forge_name.should == "puppetlabs/foo" end end end end end diff --git a/spec/unit/module_tool/applications/upgrader_spec.rb b/spec/unit/module_tool/applications/upgrader_spec.rb index 542e00b7a..1f5d2c502 100644 --- a/spec/unit/module_tool/applications/upgrader_spec.rb +++ b/spec/unit/module_tool/applications/upgrader_spec.rb @@ -1,37 +1,37 @@ require 'spec_helper' require 'puppet/module_tool/applications' require 'puppet_spec/modules' require 'semver' -describe Puppet::Module::Tool::Applications::Upgrader, :fails_on_windows => true do +describe Puppet::ModuleTool::Applications::Upgrader, :fails_on_windows => true do include PuppetSpec::Files before do end it "should update the requested module" it "should not update dependencies" it "should fail when updating a dependency to an unsupported version" it "should fail when updating a module that is not installed" it "should warn when the latest version is already installed" it "should warn when the best version is already installed" context "when using the '--version' option" do it "should update an installed module to the requested version" end context "when using the '--force' flag" do it "should ignore missing dependencies" it "should ignore version constraints" it "should not update a module that is not installed" end context "when using the '--env' option" do it "should use the correct environment" end context "when there are missing dependencies" do it "should fail to upgrade the original module" it "should raise an error" end end diff --git a/spec/unit/module_tool/metadata_spec.rb b/spec/unit/module_tool/metadata_spec.rb index 85d743fbc..af86e4d68 100644 --- a/spec/unit/module_tool/metadata_spec.rb +++ b/spec/unit/module_tool/metadata_spec.rb @@ -1,11 +1,11 @@ require 'spec_helper' require 'puppet/module_tool' -describe Puppet::Module::Tool::Metadata do +describe Puppet::ModuleTool::Metadata do context "when using default values" do it "should set license to 'Apache License, Version 2.0'" do - metadata = Puppet::Module::Tool::Metadata.new + metadata = Puppet::ModuleTool::Metadata.new metadata.license.should == "Apache License, Version 2.0" end end end diff --git a/spec/unit/module_tool_spec.rb b/spec/unit/module_tool_spec.rb index 1517f30f5..914b22e82 100644 --- a/spec/unit/module_tool_spec.rb +++ b/spec/unit/module_tool_spec.rb @@ -1,113 +1,113 @@ # encoding: UTF-8 require 'spec_helper' require 'puppet/module_tool' -describe Puppet::Module::Tool, :fails_on_windows => true do +describe Puppet::ModuleTool, :fails_on_windows => true do describe '.format_tree' do it 'should return an empty tree when given an empty list' do subject.format_tree([]).should == '' end it 'should return a shallow when given a list without dependencies' do list = [ { :text => 'first' }, { :text => 'second' }, { :text => 'third' } ] subject.format_tree(list).should == <<-TREE ├── first ├── second └── third TREE end it 'should return a deeply nested tree when given a list with deep dependencies' do list = [ { :text => 'first', :dependencies => [ { :text => 'second', :dependencies => [ { :text => 'third' } ] } ] }, ] subject.format_tree(list).should == <<-TREE └─┬ first └─┬ second └── third TREE end it 'should show connectors when deep dependencies are not on the last node of the top level' do list = [ { :text => 'first', :dependencies => [ { :text => 'second', :dependencies => [ { :text => 'third' } ] } ] }, { :text => 'fourth' } ] subject.format_tree(list).should == <<-TREE ├─┬ first │ └─┬ second │ └── third └── fourth TREE end it 'should show connectors when deep dependencies are not on the last node of any level' do list = [ { :text => 'first', :dependencies => [ { :text => 'second', :dependencies => [ { :text => 'third' } ] }, { :text => 'fourth' } ] } ] subject.format_tree(list).should == <<-TREE └─┬ first ├─┬ second │ └── third └── fourth TREE end it 'should show connectors in every case when deep dependencies are not on the last node' do list = [ { :text => 'first', :dependencies => [ { :text => 'second', :dependencies => [ { :text => 'third' } ] }, { :text => 'fourth' } ] }, { :text => 'fifth' } ] subject.format_tree(list).should == <<-TREE ├─┬ first │ ├─┬ second │ │ └── third │ └── fourth └── fifth TREE end end end diff --git a/spec/unit/util/instrumentation/data_spec.rb b/spec/unit/util/instrumentation/data_spec.rb index c2465f622..4a5fd9508 100755 --- a/spec/unit/util/instrumentation/data_spec.rb +++ b/spec/unit/util/instrumentation/data_spec.rb @@ -1,44 +1,44 @@ #!/usr/bin/env rspec require 'spec_helper' require 'matchers/json' require 'puppet/util/instrumentation' require 'puppet/util/instrumentation/data' describe Puppet::Util::Instrumentation::Data do Puppet::Util::Instrumentation::Data before(:each) do @listener = stub 'listener', :name => "name" Puppet::Util::Instrumentation.stubs(:[]).with("name").returns(@listener) end it "should indirect instrumentation_data" do Puppet::Util::Instrumentation::Data.indirection.name.should == :instrumentation_data end it "should lookup the corresponding listener" do Puppet::Util::Instrumentation.expects(:[]).with("name").returns(@listener) Puppet::Util::Instrumentation::Data.new("name") end it "should error if the listener can not be found" do Puppet::Util::Instrumentation.expects(:[]).with("name").returns(nil) expect { Puppet::Util::Instrumentation::Data.new("name") }.to raise_error end it "should return pson data" do data = Puppet::Util::Instrumentation::Data.new("name") @listener.stubs(:data).returns({ :this_is_data => "here also" }) data.should set_json_attribute('name').to("name") data.should set_json_attribute('this_is_data').to("here also") end it "should not error if the underlying listener doesn't have data" do lambda { Puppet::Util::Instrumentation::Data.new("name").to_pson }.should_not raise_error end it "should return a hash containing data when unserializing from pson" do Puppet::Util::Instrumentation::Data.from_pson({:name => "name"}).should == {:name => "name"} end -end \ No newline at end of file +end diff --git a/spec/unit/util/instrumentation/indirection_probe_spec.rb b/spec/unit/util/instrumentation/indirection_probe_spec.rb index 654825c9a..5f4e82866 100644 --- a/spec/unit/util/instrumentation/indirection_probe_spec.rb +++ b/spec/unit/util/instrumentation/indirection_probe_spec.rb @@ -1,19 +1,19 @@ #!/usr/bin/env rspec require 'spec_helper' require 'matchers/json' require 'puppet/util/instrumentation' require 'puppet/util/instrumentation/indirection_probe' describe Puppet::Util::Instrumentation::IndirectionProbe do Puppet::Util::Instrumentation::IndirectionProbe it "should indirect instrumentation_probe" do Puppet::Util::Instrumentation::IndirectionProbe.indirection.name.should == :instrumentation_probe end it "should return pson data" do probe = Puppet::Util::Instrumentation::IndirectionProbe.new("probe") probe.should set_json_attribute('name').to("probe") end -end \ No newline at end of file +end diff --git a/spec/unit/util/instrumentation/instrumentable_spec.rb b/spec/unit/util/instrumentation/instrumentable_spec.rb index dd2ad3084..82f481dfd 100755 --- a/spec/unit/util/instrumentation/instrumentable_spec.rb +++ b/spec/unit/util/instrumentation/instrumentable_spec.rb @@ -1,186 +1,186 @@ #!/usr/bin/env rspec require 'spec_helper' require 'puppet/util/instrumentation' require 'puppet/util/instrumentation/instrumentable' describe Puppet::Util::Instrumentation::Instrumentable::Probe do before(:each) do Puppet::Util::Instrumentation.stubs(:start) Puppet::Util::Instrumentation.stubs(:stop) class ProbeTest def mymethod(arg1, arg2, arg3) :it_worked end end end after(:each) do if ProbeTest.method_defined?(:instrumented_mymethod) ProbeTest.class_eval { remove_method(:mymethod) alias_method(:mymethod, :instrumented_mymethod) } end Puppet::Util::Instrumentation::Instrumentable.clear_probes end describe "when enabling a probe" do it "should raise an error if the probe is already enabled" do probe = Puppet::Util::Instrumentation::Instrumentable::Probe.new(:mymethod, ProbeTest) probe.enable lambda { probe.enable }.should raise_error end it "should rename the original method name" do probe = Puppet::Util::Instrumentation::Instrumentable::Probe.new(:mymethod, ProbeTest) probe.enable ProbeTest.new.should respond_to(:instrumented_mymethod) end it "should create a new method of the original name" do probe = Puppet::Util::Instrumentation::Instrumentable::Probe.new(:mymethod, ProbeTest) probe.enable ProbeTest.new.should respond_to(:mymethod) end end describe "when disabling a probe" do it "should raise an error if the probe is already enabled" do probe = Puppet::Util::Instrumentation::Instrumentable::Probe.new(:mymethod, ProbeTest) lambda { probe.disable }.should raise_error end it "should rename the original method name" do probe = Puppet::Util::Instrumentation::Instrumentable::Probe.new(:mymethod, ProbeTest) probe.enable probe.disable Puppet::Util::Instrumentation.expects(:start).never Puppet::Util::Instrumentation.expects(:stop).never ProbeTest.new.mymethod(1,2,3).should == :it_worked end it "should remove the created method" do probe = Puppet::Util::Instrumentation::Instrumentable::Probe.new(:mymethod, ProbeTest) probe.enable probe.disable ProbeTest.new.should_not respond_to(:instrumented_mymethod) end end describe "when a probe is called" do it "should call the original method" do probe = Puppet::Util::Instrumentation::Instrumentable::Probe.new(:mymethod, ProbeTest) probe.enable test = ProbeTest.new test.expects(:instrumented_mymethod).with(1,2,3) test.mymethod(1,2,3) end it "should start the instrumentation" do Puppet::Util::Instrumentation.expects(:start) probe = Puppet::Util::Instrumentation::Instrumentable::Probe.new(:mymethod, ProbeTest) probe.enable test = ProbeTest.new test.mymethod(1,2,3) end it "should stop the instrumentation" do Puppet::Util::Instrumentation.expects(:stop) probe = Puppet::Util::Instrumentation::Instrumentable::Probe.new(:mymethod, ProbeTest) probe.enable test = ProbeTest.new test.mymethod(1,2,3) end describe "and the original method raises an exception" do it "should propagate the exception" do probe = Puppet::Util::Instrumentation::Instrumentable::Probe.new(:mymethod, ProbeTest) probe.enable test = ProbeTest.new test.expects(:instrumented_mymethod).with(1,2,3).raises lambda { test.mymethod(1,2,3) }.should raise_error end it "should stop the instrumentation" do Puppet::Util::Instrumentation.expects(:stop) probe = Puppet::Util::Instrumentation::Instrumentable::Probe.new(:mymethod, ProbeTest) probe.enable test = ProbeTest.new test.expects(:instrumented_mymethod).with(1,2,3).raises lambda { test.mymethod(1,2,3) }.should raise_error end end describe "with a static label" do it "should send the label to the instrumentation layer" do probe = Puppet::Util::Instrumentation::Instrumentable::Probe.new(:mymethod, ProbeTest, :label => :mylabel) probe.enable test = ProbeTest.new Puppet::Util::Instrumentation.expects(:start).with { |label,data| label == :mylabel }.returns(42) Puppet::Util::Instrumentation.expects(:stop).with(:mylabel, 42, {}) test.mymethod(1,2,3) end end describe "with a dynamic label" do it "should send the evaluated label to the instrumentation layer" do probe = Puppet::Util::Instrumentation::Instrumentable::Probe.new(:mymethod, ProbeTest, :label => Proc.new { |parent,args| "dynamic#{args[0]}" } ) probe.enable test = ProbeTest.new Puppet::Util::Instrumentation.expects(:start).with { |label,data| label == "dynamic1" }.returns(42) Puppet::Util::Instrumentation.expects(:stop).with("dynamic1",42,{}) test.mymethod(1,2,3) end end describe "with static data" do it "should send the data to the instrumentation layer" do probe = Puppet::Util::Instrumentation::Instrumentable::Probe.new(:mymethod, ProbeTest, :data => { :static_data => "nothing" }) probe.enable test = ProbeTest.new Puppet::Util::Instrumentation.expects(:start).with { |label,data| data == { :static_data => "nothing" }} test.mymethod(1,2,3) end end describe "with dynamic data" do it "should send the evaluated label to the instrumentation layer" do probe = Puppet::Util::Instrumentation::Instrumentable::Probe.new(:mymethod, ProbeTest, :data => Proc.new { |parent, args| { :key => args[0] } } ) probe.enable test = ProbeTest.new Puppet::Util::Instrumentation.expects(:start).with { |label,data| data == { :key => 1 } } Puppet::Util::Instrumentation.expects(:stop) test.mymethod(1,2,3) end end end end describe Puppet::Util::Instrumentation::Instrumentable do before(:each) do class ProbeTest2 extend Puppet::Util::Instrumentation::Instrumentable probe :mymethod def mymethod(arg1,arg2,arg3) end end end after do Puppet::Util::Instrumentation::Instrumentable.clear_probes end it "should allow probe definition" do Puppet::Util::Instrumentation::Instrumentable.probe_names.should be_include("ProbeTest2.mymethod") end it "should be able to enable all probes" do Puppet::Util::Instrumentation::Instrumentable.enable_probes ProbeTest2.new.should respond_to(:instrumented_mymethod) end -end \ No newline at end of file +end diff --git a/spec/unit/util/instrumentation/listener_spec.rb b/spec/unit/util/instrumentation/listener_spec.rb index bc49d265c..bc7ffcf68 100755 --- a/spec/unit/util/instrumentation/listener_spec.rb +++ b/spec/unit/util/instrumentation/listener_spec.rb @@ -1,100 +1,100 @@ #!/usr/bin/env rspec require 'spec_helper' require 'matchers/json' require 'puppet/util/instrumentation' require 'puppet/util/instrumentation/listener' describe Puppet::Util::Instrumentation::Listener do Listener = Puppet::Util::Instrumentation::Listener before(:each) do @delegate = stub 'listener', :notify => nil, :name => 'listener' @listener = Listener.new(@delegate) @listener.enabled = true end it "should indirect instrumentation_listener" do Listener.indirection.name.should == :instrumentation_listener end it "should raise an error if delegate doesn't support notify" do lambda { Listener.new(Object.new) }.should raise_error end it "should not be enabled by default" do Listener.new(@delegate).should_not be_enabled end it "should delegate notification" do @delegate.expects(:notify).with(:event, :start, {}) listener = Listener.new(@delegate) listener.notify(:event, :start, {}) end it "should not listen is not enabled" do @listener.enabled = false @listener.should_not be_listen_to(:label) end it "should listen to all label if created without pattern" do @listener.should be_listen_to(:improbable_label) end it "should listen to specific string pattern" do listener = Listener.new(@delegate, "specific") listener.enabled = true listener.should be_listen_to(:specific) end it "should not listen to non-matching string pattern" do listener = Listener.new(@delegate, "specific") listener.enabled = true listener.should_not be_listen_to(:unspecific) end it "should listen to specific regex pattern" do listener = Listener.new(@delegate, /spe.*/) listener.enabled = true listener.should be_listen_to(:specific_pattern) end it "should not listen to non matching regex pattern" do listener = Listener.new(@delegate, /^match.*/) listener.enabled = true listener.should_not be_listen_to(:not_matching) end it "should delegate its name to the underlying listener" do @delegate.expects(:name).returns("myname") @listener.name.should == "myname" end it "should delegate data fetching to the underlying listener" do @delegate.expects(:data).returns(:data) @listener.data.should == {:data => :data } end describe "when serializing to pson" do it "should return a pson object containing pattern, name and status" do @listener.should set_json_attribute('enabled').to(true) @listener.should set_json_attribute('name').to("listener") end end describe "when deserializing from pson" do it "should lookup the archetype listener from the instrumentation layer" do Puppet::Util::Instrumentation.expects(:[]).with("listener").returns(@listener) Puppet::Util::Instrumentation::Listener.from_pson({"name" => "listener"}) end it "should create a new listener shell instance delegating to the archetypal listener" do Puppet::Util::Instrumentation.expects(:[]).with("listener").returns(@listener) @listener.stubs(:listener).returns(@delegate) Puppet::Util::Instrumentation::Listener.expects(:new).with(@delegate, nil, true) Puppet::Util::Instrumentation::Listener.from_pson({"name" => "listener", "enabled" => true}) end end -end \ No newline at end of file +end diff --git a/spec/unit/util/instrumentation/listeners/log_spec.rb b/spec/unit/util/instrumentation/listeners/log_spec.rb index 8359625a1..1274acd1d 100755 --- a/spec/unit/util/instrumentation/listeners/log_spec.rb +++ b/spec/unit/util/instrumentation/listeners/log_spec.rb @@ -1,34 +1,34 @@ require 'spec_helper' require 'puppet/util/instrumentation' Puppet::Util::Instrumentation.init log = Puppet::Util::Instrumentation.listener(:log) describe log do before(:each) do @log = log.new end it "should have a notify method" do @log.should respond_to(:notify) end it "should have a data method" do @log.should respond_to(:data) end it "should keep data for stop event" do @log.notify(:test, :stop, { :started => Time.at(123456789), :finished => Time.at(123456790)}) @log.data.should == {:test=>["test took 1.0"]} end it "should not keep data for start event" do @log.notify(:test, :start, { :started => Time.at(123456789)}) @log.data.should be_empty end it "should not keep more than 20 events per label" do 25.times { @log.notify(:test, :stop, { :started => Time.at(123456789), :finished => Time.at(123456790)}) } @log.data[:test].size.should == 20 end -end \ No newline at end of file +end diff --git a/spec/unit/util/instrumentation/listeners/performance_spec.rb b/spec/unit/util/instrumentation/listeners/performance_spec.rb index 4cecbe3f6..519de5e90 100755 --- a/spec/unit/util/instrumentation/listeners/performance_spec.rb +++ b/spec/unit/util/instrumentation/listeners/performance_spec.rb @@ -1,36 +1,36 @@ require 'spec_helper' require 'puppet/util/instrumentation' Puppet::Util::Instrumentation.init performance = Puppet::Util::Instrumentation.listener(:performance) describe performance do before(:each) do @performance = performance.new end it "should have a notify method" do @performance.should respond_to(:notify) end it "should have a data method" do @performance.should respond_to(:data) end it "should keep data for stop event" do @performance.notify(:test, :stop, { :started => Time.at(123456789), :finished => Time.at(123456790)}) @performance.data.should == {:test=>{:average=>1.0, :count=>1, :min=>1.0, :max=>1.0, :sum=>1.0}} end it "should accumulate performance statistics" do @performance.notify(:test, :stop, { :started => Time.at(123456789), :finished => Time.at(123456790)}) @performance.notify(:test, :stop, { :started => Time.at(123456789), :finished => Time.at(123456791)}) @performance.data.should == {:test=>{:average=>1.5, :count=>2, :min=>1.0, :max=>2.0, :sum=>3.0}} end it "should not keep data for start event" do @performance.notify(:test, :start, { :started => Time.at(123456789)}) @performance.data.should be_empty end -end \ No newline at end of file +end diff --git a/spec/unit/util/instrumentation_spec.rb b/spec/unit/util/instrumentation_spec.rb index 0d19ee03c..ce6c32407 100755 --- a/spec/unit/util/instrumentation_spec.rb +++ b/spec/unit/util/instrumentation_spec.rb @@ -1,181 +1,181 @@ #!/usr/bin/env rspec require 'spec_helper' require 'puppet/util/instrumentation' describe Puppet::Util::Instrumentation do Instrumentation = Puppet::Util::Instrumentation after(:each) do Instrumentation.clear end it "should instance-load instrumentation listeners" do Instrumentation.instance_loader(:listener).should be_instance_of(Puppet::Util::Autoload) end it "should have a method for registering instrumentation listeners" do Instrumentation.should respond_to(:new_listener) end it "should have a method for retrieving instrumentation listener by name" do Instrumentation.should respond_to(:listener) end describe "when registering listeners" do it "should evaluate the supplied block as code for a class" do Instrumentation.expects(:genclass).returns(Class.new { def notify(label, event, data) ; end }) Instrumentation.new_listener(:testing, :label_pattern => :for_this_label, :event => :all) { } end it "should subscribe a new listener instance" do Instrumentation.expects(:genclass).returns(Class.new { def notify(label, event, data) ; end }) Instrumentation.new_listener(:testing, :label_pattern => :for_this_label, :event => :all) { } Instrumentation.listeners.size.should == 1 Instrumentation.listeners[0].pattern.should == "for_this_label" end it "should be possible to access listeners by name" do Instrumentation.expects(:genclass).returns(Class.new { def notify(label, event, data) ; end }) Instrumentation.new_listener(:testing, :label_pattern => :for_this_label, :event => :all) { } Instrumentation["testing"].should_not be_nil end it "should be possible to store a new listener by name" do listener = stub 'listener' Instrumentation["testing"] = listener Instrumentation["testing"].should == listener end it "should fail if listener is already subscribed" do listener = stub 'listener', :notify => nil, :name => "mylistener" Instrumentation.subscribe(listener, :for_this_label, :all) expect { Instrumentation.subscribe(listener, :for_this_label, :all) }.to raise_error end it 'should call #unsubscribed' do listener = stub 'listener', :notify => nil, :name => "mylistener" listener.expects(:subscribed) Instrumentation.subscribe(listener, :for_this_label, :all) end end describe "when unsubscribing listener" do it "should remove it from the listeners" do listener = stub 'listener', :notify => nil, :name => "mylistener" Instrumentation.subscribe(listener, :for_this_label, :all) Instrumentation.unsubscribe(listener) Instrumentation.listeners.size.should == 0 end it "should warn if the listener wasn't subscribed" do listener = stub 'listener', :notify => nil, :name => "mylistener" Puppet.expects(:warning) Instrumentation.unsubscribe(listener) end it 'should call #unsubscribed' do listener = stub 'listener', :notify => nil, :name => "mylistener" Instrumentation.subscribe(listener, :for_this_label, :all) listener.expects(:unsubscribed) Instrumentation.unsubscribe(listener) end end describe "when firing events" do it "should be able to find all listeners matching a label" do listener = stub 'listener', :notify => nil, :name => "mylistener" Instrumentation.subscribe(listener, :for_this_label, :all) Instrumentation.listeners[0].enabled = true count = 0 Instrumentation.each_listener(:for_this_label) { |l| count += 1 } count.should == 1 end it "should fire events to matching listeners" do listener = stub 'listener', :notify => nil, :name => "mylistener" Instrumentation.subscribe(listener, :for_this_label, :all) Instrumentation.listeners[0].enabled = true listener.expects(:notify).with(:for_this_label, :start, {}) Instrumentation.publish(:for_this_label, :start, {}) end it "should not fire events to non-matching listeners" do listener1 = stub 'listener1', :notify => nil, :name => "mylistener1" listener2 = stub 'listener2', :notify => nil, :name => "mylistener2" Instrumentation.subscribe(listener1, :for_this_label, :all) Instrumentation.listeners[0].enabled = true Instrumentation.subscribe(listener2, :for_this_other_label, :all) Instrumentation.listeners[1].enabled = true listener1.expects(:notify).never listener2.expects(:notify).with(:for_this_other_label, :start, {}) Instrumentation.publish(:for_this_other_label, :start, {}) end end describe "when instrumenting code" do before(:each) do Instrumentation.stubs(:publish) end describe "with a block" do it "should execute it" do executed = false Instrumentation.instrument(:event) do executed = true end executed.should be_true end it "should publish an event before execution" do Instrumentation.expects(:publish).with { |label,event,data| label == :event && event == :start } Instrumentation.instrument(:event) {} end it "should publish an event after execution" do Instrumentation.expects(:publish).with { |label,event,data| label == :event && event == :stop } Instrumentation.instrument(:event) {} end it "should publish the event even when block raised an exception" do Instrumentation.expects(:publish).with { |label,event,data| label == :event } lambda { Instrumentation.instrument(:event) { raise "not working" } }.should raise_error end it "should retain start end finish time of the event" do Instrumentation.expects(:publish).with { |label,event,data| data.include?(:started) and data.include?(:finished) } Instrumentation.instrument(:event) {} end end describe "without a block" do it "should raise an error if stop is called with no matching start" do lambda{ Instrumentation.stop(:event) }.should raise_error end it "should publish an event on stop" do Instrumentation.expects(:publish).with { |label,event,data| event == :start } Instrumentation.expects(:publish).with { |label,event,data| event == :stop and data.include?(:started) and data.include?(:finished) } data = {} Instrumentation.start(:event, data) Instrumentation.stop(:event, 1, data) end it "should return a different id per event" do data = {} Instrumentation.start(:event, data).should == 1 Instrumentation.start(:event, data).should == 2 end end end -end \ No newline at end of file +end diff --git a/spec/unit/util/reference_spec.rb b/spec/unit/util/reference_spec.rb index aa16299c7..60aa4eee1 100644 --- a/spec/unit/util/reference_spec.rb +++ b/spec/unit/util/reference_spec.rb @@ -1,39 +1,39 @@ #!/usr/bin/env rspec require 'spec_helper' require 'puppet/util/reference' describe Puppet::Util::Reference do it "should create valid Markdown extension definition lists" do my_fragment = nil Puppet::Util::Reference.newreference :testreference, :doc => "A peer of the type and configuration references, but with no useful information" do my_term = "A term" my_definition = <<-EOT The definition of this term, marked by a colon and a space. We should be able to handle multi-line definitions. Each subsequent line should left-align with the first word character after the colon used as the definition marker. We should be able to handle multi-paragraph definitions. Leading indentation should be stripped from the definition, which allows us to indent the source string for cosmetic purposes. EOT my_fragment = markdown_definitionlist(my_term, my_definition) end Puppet::Util::Reference.reference(:testreference).send(:to_markdown, true) my_fragment.should == <<-EOT A term : The definition of this term, marked by a colon and a space. We should be able to handle multi-line definitions. Each subsequent line should left-align with the first word character after the colon used as the definition marker. We should be able to handle multi-paragraph definitions. Leading indentation should be stripped from the definition, which allows us to indent the source string for cosmetic purposes. EOT end -end \ No newline at end of file +end diff --git a/tasks/rake/manpages.rake b/tasks/rake/manpages.rake index a2dc850c8..3fb48da73 100644 --- a/tasks/rake/manpages.rake +++ b/tasks/rake/manpages.rake @@ -1,69 +1,69 @@ # require 'fileutils' desc "Build Puppet manpages" task :gen_manpages do require 'puppet/face' require 'fileutils' helpface = Puppet::Face[:help, '0.0.1'] manface = Puppet::Face[:man, '0.0.1'] sbins = Dir.glob(%w{sbin/*}) bins = Dir.glob(%w{bin/*}) non_face_applications = helpface.legacy_applications faces = Puppet::Face.faces ronn_args = '--manual="Puppet manual" --organization="Puppet Labs, LLC" -r' # Locate ronn ronn = %x{which ronn}.chomp unless File.executable?(ronn) then fail("Ronn does not appear to be installed.") end # def write_manpage(text, filename) # IO.popen("#{ronn} #{ronn_args} -r > #{filename}") do |fh| fh.write text end # end # Create puppet.conf.5 man page # IO.popen("#{ronn} #{ronn_args} > ./man/man5/puppet.conf.5", 'w') do |fh| # fh.write %x{RUBYLIB=./lib:$RUBYLIB bin/puppetdoc --reference configuration} # end %x{RUBYLIB=./lib:$RUBYLIB bin/puppetdoc --reference configuration > ./man/man5/puppetconf.5.ronn} %x{#{ronn} #{ronn_args} ./man/man5/puppetconf.5.ronn} FileUtils.mv("./man/man5/puppetconf.5", "./man/man5/puppet.conf.5") FileUtils.rm("./man/man5/puppetconf.5.ronn") # Create LEGACY binary man pages (i.e. delete me for 2.8.0) binary = bins + sbins binary.each do |bin| b = bin.gsub( /^s?bin\//, "") %x{RUBYLIB=./lib:$RUBYLIB #{bin} --help > ./man/man8/#{b}.8.ronn} %x{#{ronn} #{ronn_args} ./man/man8/#{b}.8.ronn} FileUtils.rm("./man/man8/#{b}.8.ronn") end # Create regular non-face man pages non_face_applications.each do |app| %x{RUBYLIB=./lib:$RUBYLIB bin/puppet #{app} --help > ./man/man8/puppet-#{app}.8.ronn} %x{#{ronn} #{ronn_args} ./man/man8/puppet-#{app}.8.ronn} FileUtils.rm("./man/man8/puppet-#{app}.8.ronn") end # Create face man pages faces.each do |face| File.open("./man/man8/puppet-#{face}.8.ronn", 'w') do |fh| # For some reason no one understands at the moment, it duplicates termini, # so we have to remove the dupes with a gsub. fh.write manface.man("#{face}", {:render_as => :s}).gsub(/^(\* `[^`]+`)\n\1/, '\1') end %x{#{ronn} #{ronn_args} ./man/man8/puppet-#{face}.8.ronn} FileUtils.rm("./man/man8/puppet-#{face}.8.ronn") end # Vile hack: create puppet resource man page # Currently, the useless resource face wins against puppet resource in puppet # man. (And actually, it even gets removed from the list of legacy # applications.) So we overwrite it with the correct man page at the end. %x{RUBYLIB=./lib:$RUBYLIB bin/puppet resource --help > ./man/man8/puppet-resource.8.ronn} %x{#{ronn} #{ronn_args} ./man/man8/puppet-resource.8.ronn} FileUtils.rm("./man/man8/puppet-resource.8.ronn") -end \ No newline at end of file +end diff --git a/test/data/snippets/arithmetic_expression.pp b/test/data/snippets/arithmetic_expression.pp index aae98a4db..2d27d7d5f 100644 --- a/test/data/snippets/arithmetic_expression.pp +++ b/test/data/snippets/arithmetic_expression.pp @@ -1,8 +1,8 @@ $one = 1.30 $two = 2.034e-2 $result = ((( $two + 2) / $one) + 4 * 5.45) - (6 << 7) + (0x800 + -9) -notice("result is $result == 1295.87692307692") \ No newline at end of file +notice("result is $result == 1295.87692307692")