diff --git a/spec/fixtures/releases/jamtur01-apache/manifests/vhost.pp b/spec/fixtures/releases/jamtur01-apache/manifests/vhost.pp index 2fe6ed204..a210f3557 100644 --- a/spec/fixtures/releases/jamtur01-apache/manifests/vhost.pp +++ b/spec/fixtures/releases/jamtur01-apache/manifests/vhost.pp @@ -1,15 +1,15 @@ define apache::vhost( $port, $docroot, $ssl=true, $template='apache/vhost-default.conf.erb', $priority, $serveraliases = '' ) { include apache $vdir = $operatingsystem? { 'ubuntu' => '/etc/apache2/sites-enabled/', default => '/etc/httpd/conf.d', } file{"${vdir}/${priority}-${name}": content => template($template), owner => 'root', group => 'root', - mode => '777', + mode => '0777', require => Package['httpd'], notify => Service['httpd'], } } diff --git a/spec/fixtures/unit/parser/lexer/argumentdefaults.pp b/spec/fixtures/unit/parser/lexer/argumentdefaults.pp index eac9dd757..a296c2fb0 100644 --- a/spec/fixtures/unit/parser/lexer/argumentdefaults.pp +++ b/spec/fixtures/unit/parser/lexer/argumentdefaults.pp @@ -1,14 +1,14 @@ # $Id$ -define testargs($file, $mode = 755) { +define testargs($file, $mode = '0755') { file { $file: ensure => file, mode => $mode } } testargs { "testingname": file => "/tmp/argumenttest1" } testargs { "testingother": file => "/tmp/argumenttest2", - mode => 644 + mode => '0644' } diff --git a/spec/fixtures/unit/parser/lexer/casestatement.pp b/spec/fixtures/unit/parser/lexer/casestatement.pp index 66ecd72b9..c17242791 100644 --- a/spec/fixtures/unit/parser/lexer/casestatement.pp +++ b/spec/fixtures/unit/parser/lexer/casestatement.pp @@ -1,65 +1,65 @@ # $Id$ $var = "value" case $var { "nope": { - file { "/tmp/fakefile": mode => 644, ensure => file } + file { "/tmp/fakefile": mode => '0644', ensure => file } } "value": { - file { "/tmp/existsfile": mode => 755, ensure => file } + file { "/tmp/existsfile": mode => '0755', ensure => file } } } $ovar = "yayness" case $ovar { "fooness": { - file { "/tmp/nostillexistsfile": mode => 644, ensure => file } + file { "/tmp/nostillexistsfile": mode => '0644', ensure => file } } "booness", "yayness": { case $var { "nep": { - file { "/tmp/noexistsfile": mode => 644, ensure => file } + file { "/tmp/noexistsfile": mode => '0644', ensure => file } } "value": { - file { "/tmp/existsfile2": mode => 755, ensure => file } + file { "/tmp/existsfile2": mode => '0755', ensure => file } } } } } case $ovar { "fooness": { - file { "/tmp/nostillexistsfile": mode => 644, ensure => file } + file { "/tmp/nostillexistsfile": mode => '0644', ensure => file } } default: { - file { "/tmp/existsfile3": mode => 755, ensure => file } + file { "/tmp/existsfile3": mode => '0755', ensure => file } } } $bool = true case $bool { true: { - file { "/tmp/existsfile4": mode => 755, ensure => file } + file { "/tmp/existsfile4": mode => '0755', ensure => file } } } $yay = yay $a = yay $b = boo case $yay { - $a: { file { "/tmp/existsfile5": mode => 755, ensure => file } } - $b: { file { "/tmp/existsfile5": mode => 644, ensure => file } } - default: { file { "/tmp/existsfile5": mode => 711, ensure => file } } + $a: { file { "/tmp/existsfile5": mode => '0755', ensure => file } } + $b: { file { "/tmp/existsfile5": mode => '0644', ensure => file } } + default: { file { "/tmp/existsfile5": mode => '0711', ensure => file } } } $regexvar = "exists regex" case $regexvar { - "no match": { file { "/tmp/existsfile6": mode => 644, ensure => file } } - /(.*) regex$/: { file { "/tmp/${1}file6": mode => 755, ensure => file } } - default: { file { "/tmp/existsfile6": mode => 711, ensure => file } } + "no match": { file { "/tmp/existsfile6": mode => '0644', ensure => file } } + /(.*) regex$/: { file { "/tmp/${1}file6": mode => '0755', ensure => file } } + default: { file { "/tmp/existsfile6": mode => '0711', ensure => file } } } diff --git a/spec/fixtures/unit/parser/lexer/classheirarchy.pp b/spec/fixtures/unit/parser/lexer/classheirarchy.pp index 36619d8b9..5a51a8229 100644 --- a/spec/fixtures/unit/parser/lexer/classheirarchy.pp +++ b/spec/fixtures/unit/parser/lexer/classheirarchy.pp @@ -1,15 +1,15 @@ # $Id$ class base { - file { "/tmp/classheir1": ensure => file, mode => 755 } + file { "/tmp/classheir1": ensure => file, mode => '0755' } } class sub1 inherits base { - file { "/tmp/classheir2": ensure => file, mode => 755 } + file { "/tmp/classheir2": ensure => file, mode => '0755' } } class sub2 inherits base { - file { "/tmp/classheir3": ensure => file, mode => 755 } + file { "/tmp/classheir3": ensure => file, mode => '0755' } } include sub1, sub2 diff --git a/spec/fixtures/unit/parser/lexer/classincludes.pp b/spec/fixtures/unit/parser/lexer/classincludes.pp index bd5b44ed7..0b1bb07a8 100644 --- a/spec/fixtures/unit/parser/lexer/classincludes.pp +++ b/spec/fixtures/unit/parser/lexer/classincludes.pp @@ -1,17 +1,17 @@ # $Id$ class base { - file { "/tmp/classincludes1": ensure => file, mode => 755 } + file { "/tmp/classincludes1": ensure => file, mode => '0755' } } class sub1 inherits base { - file { "/tmp/classincludes2": ensure => file, mode => 755 } + file { "/tmp/classincludes2": ensure => file, mode => '0755' } } class sub2 inherits base { - file { "/tmp/classincludes3": ensure => file, mode => 755 } + file { "/tmp/classincludes3": ensure => file, mode => '0755' } } $sub = "sub2" include sub1, $sub diff --git a/spec/fixtures/unit/parser/lexer/classpathtest.pp b/spec/fixtures/unit/parser/lexer/classpathtest.pp index 580333369..fae7cadbf 100644 --- a/spec/fixtures/unit/parser/lexer/classpathtest.pp +++ b/spec/fixtures/unit/parser/lexer/classpathtest.pp @@ -1,11 +1,11 @@ # $Id$ define mytype { - file { "/tmp/classtest": ensure => file, mode => 755 } + file { "/tmp/classtest": ensure => file, mode => '0755' } } class testing { mytype { "componentname": } } include testing diff --git a/spec/fixtures/unit/parser/lexer/collection_override.pp b/spec/fixtures/unit/parser/lexer/collection_override.pp index b1b39ab16..f1063bf97 100644 --- a/spec/fixtures/unit/parser/lexer/collection_override.pp +++ b/spec/fixtures/unit/parser/lexer/collection_override.pp @@ -1,8 +1,8 @@ @file { "/tmp/collection": content => "whatever" } File<| |> { - mode => 0600 + mode => '0600' } diff --git a/spec/fixtures/unit/parser/lexer/componentrequire.pp b/spec/fixtures/unit/parser/lexer/componentrequire.pp index a61d2050c..8d67ab161 100644 --- a/spec/fixtures/unit/parser/lexer/componentrequire.pp +++ b/spec/fixtures/unit/parser/lexer/componentrequire.pp @@ -1,8 +1,8 @@ define testfile($mode) { file { $name: mode => $mode, ensure => present } } -testfile { "/tmp/testing_component_requires2": mode => 755 } +testfile { "/tmp/testing_component_requires2": mode => '0755' } -file { "/tmp/testing_component_requires1": mode => 755, ensure => present, +file { "/tmp/testing_component_requires1": mode => '0755', ensure => present, require => Testfile["/tmp/testing_component_requires2"] } diff --git a/spec/fixtures/unit/parser/lexer/deepclassheirarchy.pp b/spec/fixtures/unit/parser/lexer/deepclassheirarchy.pp index 249e6334d..8e477f7b7 100644 --- a/spec/fixtures/unit/parser/lexer/deepclassheirarchy.pp +++ b/spec/fixtures/unit/parser/lexer/deepclassheirarchy.pp @@ -1,23 +1,23 @@ # $Id$ class base { - file { "/tmp/deepclassheir1": ensure => file, mode => 755 } + file { "/tmp/deepclassheir1": ensure => file, mode => '0755' } } class sub1 inherits base { - file { "/tmp/deepclassheir2": ensure => file, mode => 755 } + file { "/tmp/deepclassheir2": ensure => file, mode => '0755' } } class sub2 inherits sub1 { - file { "/tmp/deepclassheir3": ensure => file, mode => 755 } + file { "/tmp/deepclassheir3": ensure => file, mode => '0755' } } class sub3 inherits sub2 { - file { "/tmp/deepclassheir4": ensure => file, mode => 755 } + file { "/tmp/deepclassheir4": ensure => file, mode => '0755' } } class sub4 inherits sub3 { - file { "/tmp/deepclassheir5": ensure => file, mode => 755 } + file { "/tmp/deepclassheir5": ensure => file, mode => '0755' } } include sub4 diff --git a/spec/fixtures/unit/parser/lexer/defineoverrides.pp b/spec/fixtures/unit/parser/lexer/defineoverrides.pp index c68b139e3..bc2b5647e 100644 --- a/spec/fixtures/unit/parser/lexer/defineoverrides.pp +++ b/spec/fixtures/unit/parser/lexer/defineoverrides.pp @@ -1,17 +1,17 @@ # $Id$ $file = "/tmp/defineoverrides1" define myfile($mode) { file { $name: ensure => file, mode => $mode } } class base { - myfile { $file: mode => 644 } + myfile { $file: mode => '0644' } } class sub inherits base { - Myfile[$file] { mode => 755, } # test the end-comma + Myfile[$file] { mode => '0755', } # test the end-comma } include sub diff --git a/spec/fixtures/unit/parser/lexer/filecreate.pp b/spec/fixtures/unit/parser/lexer/filecreate.pp index d7972c234..b8edcd540 100644 --- a/spec/fixtures/unit/parser/lexer/filecreate.pp +++ b/spec/fixtures/unit/parser/lexer/filecreate.pp @@ -1,11 +1,11 @@ # $Id$ file { - "/tmp/createatest": ensure => file, mode => 755; - "/tmp/createbtest": ensure => file, mode => 755 + "/tmp/createatest": ensure => file, mode => '0755'; + "/tmp/createbtest": ensure => file, mode => '0755' } file { "/tmp/createctest": ensure => file; "/tmp/createdtest": ensure => file; } diff --git a/spec/fixtures/unit/parser/lexer/ifexpression.pp b/spec/fixtures/unit/parser/lexer/ifexpression.pp index 29a637291..131530dcb 100644 --- a/spec/fixtures/unit/parser/lexer/ifexpression.pp +++ b/spec/fixtures/unit/parser/lexer/ifexpression.pp @@ -1,12 +1,12 @@ $one = 1 $two = 2 if ($one < $two) and (($two < 3) or ($two == 2)) { notice("True!") } if "test regex" =~ /(.*) regex/ { file { - "/tmp/${1}iftest": ensure => file, mode => 0755 + "/tmp/${1}iftest": ensure => file, mode => '0755' } } diff --git a/spec/fixtures/unit/parser/lexer/implicititeration.pp b/spec/fixtures/unit/parser/lexer/implicititeration.pp index 6f34cb29c..75719e985 100644 --- a/spec/fixtures/unit/parser/lexer/implicititeration.pp +++ b/spec/fixtures/unit/parser/lexer/implicititeration.pp @@ -1,15 +1,15 @@ # $Id$ $files = ["/tmp/iterationatest", "/tmp/iterationbtest"] -file { $files: ensure => file, mode => 755 } +file { $files: ensure => file, mode => '0755' } file { ["/tmp/iterationctest", "/tmp/iterationdtest"]: ensure => file, - mode => 755 + mode => '0755' } file { - ["/tmp/iterationetest", "/tmp/iterationftest"]: ensure => file, mode => 755; - ["/tmp/iterationgtest", "/tmp/iterationhtest"]: ensure => file, mode => 755; + ["/tmp/iterationetest", "/tmp/iterationftest"]: ensure => file, mode => '0755'; + ["/tmp/iterationgtest", "/tmp/iterationhtest"]: ensure => file, mode => '0755'; } diff --git a/spec/fixtures/unit/parser/lexer/multipleinstances.pp b/spec/fixtures/unit/parser/lexer/multipleinstances.pp index 2f9b3c2e8..bc0cdee24 100644 --- a/spec/fixtures/unit/parser/lexer/multipleinstances.pp +++ b/spec/fixtures/unit/parser/lexer/multipleinstances.pp @@ -1,7 +1,7 @@ # $Id$ file { - "/tmp/multipleinstancesa": ensure => file, mode => 755; - "/tmp/multipleinstancesb": ensure => file, mode => 755; - "/tmp/multipleinstancesc": ensure => file, mode => 755; + "/tmp/multipleinstancesa": ensure => file, mode => '0755'; + "/tmp/multipleinstancesb": ensure => file, mode => '0755'; + "/tmp/multipleinstancesc": ensure => file, mode => '0755'; } diff --git a/spec/fixtures/unit/parser/lexer/multisubs.pp b/spec/fixtures/unit/parser/lexer/multisubs.pp index bcec69e2a..079edf336 100644 --- a/spec/fixtures/unit/parser/lexer/multisubs.pp +++ b/spec/fixtures/unit/parser/lexer/multisubs.pp @@ -1,13 +1,13 @@ class base { - file { "/tmp/multisubtest": content => "base", mode => 644 } + file { "/tmp/multisubtest": content => "base", mode => '0644' } } class sub1 inherits base { - File["/tmp/multisubtest"] { mode => 755 } + File["/tmp/multisubtest"] { mode => '0755' } } class sub2 inherits base { File["/tmp/multisubtest"] { content => sub2 } } include sub1, sub2 diff --git a/spec/fixtures/unit/parser/lexer/namevartest.pp b/spec/fixtures/unit/parser/lexer/namevartest.pp index dbee1c356..d080cb467 100644 --- a/spec/fixtures/unit/parser/lexer/namevartest.pp +++ b/spec/fixtures/unit/parser/lexer/namevartest.pp @@ -1,9 +1,9 @@ define filetest($mode, $ensure = file) { file { $name: mode => $mode, ensure => $ensure } } -filetest { "/tmp/testfiletest": mode => 644} -filetest { "/tmp/testdirtest": mode => 755, ensure => directory} +filetest { "/tmp/testfiletest": mode => '0644'} +filetest { "/tmp/testdirtest": mode => '0755', ensure => directory} diff --git a/spec/fixtures/unit/parser/lexer/simpledefaults.pp b/spec/fixtures/unit/parser/lexer/simpledefaults.pp index 63d199a68..bbe61e25c 100644 --- a/spec/fixtures/unit/parser/lexer/simpledefaults.pp +++ b/spec/fixtures/unit/parser/lexer/simpledefaults.pp @@ -1,5 +1,5 @@ # $Id$ -File { mode => 755 } +File { mode => '0755' } file { "/tmp/defaulttest": ensure => file } diff --git a/spec/fixtures/unit/pops/parser/lexer/argumentdefaults.pp b/spec/fixtures/unit/pops/parser/lexer/argumentdefaults.pp index eac9dd757..29c7227ea 100644 --- a/spec/fixtures/unit/pops/parser/lexer/argumentdefaults.pp +++ b/spec/fixtures/unit/pops/parser/lexer/argumentdefaults.pp @@ -1,14 +1,14 @@ # $Id$ define testargs($file, $mode = 755) { file { $file: ensure => file, mode => $mode } } testargs { "testingname": file => "/tmp/argumenttest1" } testargs { "testingother": file => "/tmp/argumenttest2", - mode => 644 + mode => '0644' } diff --git a/spec/fixtures/unit/pops/parser/lexer/casestatement.pp b/spec/fixtures/unit/pops/parser/lexer/casestatement.pp index 66ecd72b9..c17242791 100644 --- a/spec/fixtures/unit/pops/parser/lexer/casestatement.pp +++ b/spec/fixtures/unit/pops/parser/lexer/casestatement.pp @@ -1,65 +1,65 @@ # $Id$ $var = "value" case $var { "nope": { - file { "/tmp/fakefile": mode => 644, ensure => file } + file { "/tmp/fakefile": mode => '0644', ensure => file } } "value": { - file { "/tmp/existsfile": mode => 755, ensure => file } + file { "/tmp/existsfile": mode => '0755', ensure => file } } } $ovar = "yayness" case $ovar { "fooness": { - file { "/tmp/nostillexistsfile": mode => 644, ensure => file } + file { "/tmp/nostillexistsfile": mode => '0644', ensure => file } } "booness", "yayness": { case $var { "nep": { - file { "/tmp/noexistsfile": mode => 644, ensure => file } + file { "/tmp/noexistsfile": mode => '0644', ensure => file } } "value": { - file { "/tmp/existsfile2": mode => 755, ensure => file } + file { "/tmp/existsfile2": mode => '0755', ensure => file } } } } } case $ovar { "fooness": { - file { "/tmp/nostillexistsfile": mode => 644, ensure => file } + file { "/tmp/nostillexistsfile": mode => '0644', ensure => file } } default: { - file { "/tmp/existsfile3": mode => 755, ensure => file } + file { "/tmp/existsfile3": mode => '0755', ensure => file } } } $bool = true case $bool { true: { - file { "/tmp/existsfile4": mode => 755, ensure => file } + file { "/tmp/existsfile4": mode => '0755', ensure => file } } } $yay = yay $a = yay $b = boo case $yay { - $a: { file { "/tmp/existsfile5": mode => 755, ensure => file } } - $b: { file { "/tmp/existsfile5": mode => 644, ensure => file } } - default: { file { "/tmp/existsfile5": mode => 711, ensure => file } } + $a: { file { "/tmp/existsfile5": mode => '0755', ensure => file } } + $b: { file { "/tmp/existsfile5": mode => '0644', ensure => file } } + default: { file { "/tmp/existsfile5": mode => '0711', ensure => file } } } $regexvar = "exists regex" case $regexvar { - "no match": { file { "/tmp/existsfile6": mode => 644, ensure => file } } - /(.*) regex$/: { file { "/tmp/${1}file6": mode => 755, ensure => file } } - default: { file { "/tmp/existsfile6": mode => 711, ensure => file } } + "no match": { file { "/tmp/existsfile6": mode => '0644', ensure => file } } + /(.*) regex$/: { file { "/tmp/${1}file6": mode => '0755', ensure => file } } + default: { file { "/tmp/existsfile6": mode => '0711', ensure => file } } } diff --git a/spec/fixtures/unit/pops/parser/lexer/classheirarchy.pp b/spec/fixtures/unit/pops/parser/lexer/classheirarchy.pp index 36619d8b9..5a51a8229 100644 --- a/spec/fixtures/unit/pops/parser/lexer/classheirarchy.pp +++ b/spec/fixtures/unit/pops/parser/lexer/classheirarchy.pp @@ -1,15 +1,15 @@ # $Id$ class base { - file { "/tmp/classheir1": ensure => file, mode => 755 } + file { "/tmp/classheir1": ensure => file, mode => '0755' } } class sub1 inherits base { - file { "/tmp/classheir2": ensure => file, mode => 755 } + file { "/tmp/classheir2": ensure => file, mode => '0755' } } class sub2 inherits base { - file { "/tmp/classheir3": ensure => file, mode => 755 } + file { "/tmp/classheir3": ensure => file, mode => '0755' } } include sub1, sub2 diff --git a/spec/fixtures/unit/pops/parser/lexer/classincludes.pp b/spec/fixtures/unit/pops/parser/lexer/classincludes.pp index bd5b44ed7..0b1bb07a8 100644 --- a/spec/fixtures/unit/pops/parser/lexer/classincludes.pp +++ b/spec/fixtures/unit/pops/parser/lexer/classincludes.pp @@ -1,17 +1,17 @@ # $Id$ class base { - file { "/tmp/classincludes1": ensure => file, mode => 755 } + file { "/tmp/classincludes1": ensure => file, mode => '0755' } } class sub1 inherits base { - file { "/tmp/classincludes2": ensure => file, mode => 755 } + file { "/tmp/classincludes2": ensure => file, mode => '0755' } } class sub2 inherits base { - file { "/tmp/classincludes3": ensure => file, mode => 755 } + file { "/tmp/classincludes3": ensure => file, mode => '0755' } } $sub = "sub2" include sub1, $sub diff --git a/spec/fixtures/unit/pops/parser/lexer/classpathtest.pp b/spec/fixtures/unit/pops/parser/lexer/classpathtest.pp index 580333369..fae7cadbf 100644 --- a/spec/fixtures/unit/pops/parser/lexer/classpathtest.pp +++ b/spec/fixtures/unit/pops/parser/lexer/classpathtest.pp @@ -1,11 +1,11 @@ # $Id$ define mytype { - file { "/tmp/classtest": ensure => file, mode => 755 } + file { "/tmp/classtest": ensure => file, mode => '0755' } } class testing { mytype { "componentname": } } include testing diff --git a/spec/fixtures/unit/pops/parser/lexer/collection_override.pp b/spec/fixtures/unit/pops/parser/lexer/collection_override.pp index b1b39ab16..f1063bf97 100644 --- a/spec/fixtures/unit/pops/parser/lexer/collection_override.pp +++ b/spec/fixtures/unit/pops/parser/lexer/collection_override.pp @@ -1,8 +1,8 @@ @file { "/tmp/collection": content => "whatever" } File<| |> { - mode => 0600 + mode => '0600' } diff --git a/spec/fixtures/unit/pops/parser/lexer/componentrequire.pp b/spec/fixtures/unit/pops/parser/lexer/componentrequire.pp index a61d2050c..8d67ab161 100644 --- a/spec/fixtures/unit/pops/parser/lexer/componentrequire.pp +++ b/spec/fixtures/unit/pops/parser/lexer/componentrequire.pp @@ -1,8 +1,8 @@ define testfile($mode) { file { $name: mode => $mode, ensure => present } } -testfile { "/tmp/testing_component_requires2": mode => 755 } +testfile { "/tmp/testing_component_requires2": mode => '0755' } -file { "/tmp/testing_component_requires1": mode => 755, ensure => present, +file { "/tmp/testing_component_requires1": mode => '0755', ensure => present, require => Testfile["/tmp/testing_component_requires2"] } diff --git a/spec/fixtures/unit/pops/parser/lexer/deepclassheirarchy.pp b/spec/fixtures/unit/pops/parser/lexer/deepclassheirarchy.pp index 249e6334d..8e477f7b7 100644 --- a/spec/fixtures/unit/pops/parser/lexer/deepclassheirarchy.pp +++ b/spec/fixtures/unit/pops/parser/lexer/deepclassheirarchy.pp @@ -1,23 +1,23 @@ # $Id$ class base { - file { "/tmp/deepclassheir1": ensure => file, mode => 755 } + file { "/tmp/deepclassheir1": ensure => file, mode => '0755' } } class sub1 inherits base { - file { "/tmp/deepclassheir2": ensure => file, mode => 755 } + file { "/tmp/deepclassheir2": ensure => file, mode => '0755' } } class sub2 inherits sub1 { - file { "/tmp/deepclassheir3": ensure => file, mode => 755 } + file { "/tmp/deepclassheir3": ensure => file, mode => '0755' } } class sub3 inherits sub2 { - file { "/tmp/deepclassheir4": ensure => file, mode => 755 } + file { "/tmp/deepclassheir4": ensure => file, mode => '0755' } } class sub4 inherits sub3 { - file { "/tmp/deepclassheir5": ensure => file, mode => 755 } + file { "/tmp/deepclassheir5": ensure => file, mode => '0755' } } include sub4 diff --git a/spec/fixtures/unit/pops/parser/lexer/defineoverrides.pp b/spec/fixtures/unit/pops/parser/lexer/defineoverrides.pp index c68b139e3..bc2b5647e 100644 --- a/spec/fixtures/unit/pops/parser/lexer/defineoverrides.pp +++ b/spec/fixtures/unit/pops/parser/lexer/defineoverrides.pp @@ -1,17 +1,17 @@ # $Id$ $file = "/tmp/defineoverrides1" define myfile($mode) { file { $name: ensure => file, mode => $mode } } class base { - myfile { $file: mode => 644 } + myfile { $file: mode => '0644' } } class sub inherits base { - Myfile[$file] { mode => 755, } # test the end-comma + Myfile[$file] { mode => '0755', } # test the end-comma } include sub diff --git a/spec/fixtures/unit/pops/parser/lexer/filecreate.pp b/spec/fixtures/unit/pops/parser/lexer/filecreate.pp index d7972c234..b8edcd540 100644 --- a/spec/fixtures/unit/pops/parser/lexer/filecreate.pp +++ b/spec/fixtures/unit/pops/parser/lexer/filecreate.pp @@ -1,11 +1,11 @@ # $Id$ file { - "/tmp/createatest": ensure => file, mode => 755; - "/tmp/createbtest": ensure => file, mode => 755 + "/tmp/createatest": ensure => file, mode => '0755'; + "/tmp/createbtest": ensure => file, mode => '0755' } file { "/tmp/createctest": ensure => file; "/tmp/createdtest": ensure => file; } diff --git a/spec/fixtures/unit/pops/parser/lexer/ifexpression.pp b/spec/fixtures/unit/pops/parser/lexer/ifexpression.pp index 29a637291..131530dcb 100644 --- a/spec/fixtures/unit/pops/parser/lexer/ifexpression.pp +++ b/spec/fixtures/unit/pops/parser/lexer/ifexpression.pp @@ -1,12 +1,12 @@ $one = 1 $two = 2 if ($one < $two) and (($two < 3) or ($two == 2)) { notice("True!") } if "test regex" =~ /(.*) regex/ { file { - "/tmp/${1}iftest": ensure => file, mode => 0755 + "/tmp/${1}iftest": ensure => file, mode => '0755' } } diff --git a/spec/fixtures/unit/pops/parser/lexer/implicititeration.pp b/spec/fixtures/unit/pops/parser/lexer/implicititeration.pp index 6f34cb29c..75719e985 100644 --- a/spec/fixtures/unit/pops/parser/lexer/implicititeration.pp +++ b/spec/fixtures/unit/pops/parser/lexer/implicititeration.pp @@ -1,15 +1,15 @@ # $Id$ $files = ["/tmp/iterationatest", "/tmp/iterationbtest"] -file { $files: ensure => file, mode => 755 } +file { $files: ensure => file, mode => '0755' } file { ["/tmp/iterationctest", "/tmp/iterationdtest"]: ensure => file, - mode => 755 + mode => '0755' } file { - ["/tmp/iterationetest", "/tmp/iterationftest"]: ensure => file, mode => 755; - ["/tmp/iterationgtest", "/tmp/iterationhtest"]: ensure => file, mode => 755; + ["/tmp/iterationetest", "/tmp/iterationftest"]: ensure => file, mode => '0755'; + ["/tmp/iterationgtest", "/tmp/iterationhtest"]: ensure => file, mode => '0755'; } diff --git a/spec/fixtures/unit/pops/parser/lexer/multipleinstances.pp b/spec/fixtures/unit/pops/parser/lexer/multipleinstances.pp index 2f9b3c2e8..bc0cdee24 100644 --- a/spec/fixtures/unit/pops/parser/lexer/multipleinstances.pp +++ b/spec/fixtures/unit/pops/parser/lexer/multipleinstances.pp @@ -1,7 +1,7 @@ # $Id$ file { - "/tmp/multipleinstancesa": ensure => file, mode => 755; - "/tmp/multipleinstancesb": ensure => file, mode => 755; - "/tmp/multipleinstancesc": ensure => file, mode => 755; + "/tmp/multipleinstancesa": ensure => file, mode => '0755'; + "/tmp/multipleinstancesb": ensure => file, mode => '0755'; + "/tmp/multipleinstancesc": ensure => file, mode => '0755'; } diff --git a/spec/fixtures/unit/pops/parser/lexer/multisubs.pp b/spec/fixtures/unit/pops/parser/lexer/multisubs.pp index bcec69e2a..079edf336 100644 --- a/spec/fixtures/unit/pops/parser/lexer/multisubs.pp +++ b/spec/fixtures/unit/pops/parser/lexer/multisubs.pp @@ -1,13 +1,13 @@ class base { - file { "/tmp/multisubtest": content => "base", mode => 644 } + file { "/tmp/multisubtest": content => "base", mode => '0644' } } class sub1 inherits base { - File["/tmp/multisubtest"] { mode => 755 } + File["/tmp/multisubtest"] { mode => '0755' } } class sub2 inherits base { File["/tmp/multisubtest"] { content => sub2 } } include sub1, sub2 diff --git a/spec/fixtures/unit/pops/parser/lexer/namevartest.pp b/spec/fixtures/unit/pops/parser/lexer/namevartest.pp index dbee1c356..d080cb467 100644 --- a/spec/fixtures/unit/pops/parser/lexer/namevartest.pp +++ b/spec/fixtures/unit/pops/parser/lexer/namevartest.pp @@ -1,9 +1,9 @@ define filetest($mode, $ensure = file) { file { $name: mode => $mode, ensure => $ensure } } -filetest { "/tmp/testfiletest": mode => 644} -filetest { "/tmp/testdirtest": mode => 755, ensure => directory} +filetest { "/tmp/testfiletest": mode => '0644'} +filetest { "/tmp/testdirtest": mode => '0755', ensure => directory} diff --git a/spec/fixtures/unit/pops/parser/lexer/simpledefaults.pp b/spec/fixtures/unit/pops/parser/lexer/simpledefaults.pp index 63d199a68..bbe61e25c 100644 --- a/spec/fixtures/unit/pops/parser/lexer/simpledefaults.pp +++ b/spec/fixtures/unit/pops/parser/lexer/simpledefaults.pp @@ -1,5 +1,5 @@ # $Id$ -File { mode => 755 } +File { mode => '0755' } file { "/tmp/defaulttest": ensure => file } diff --git a/spec/integration/parser/compiler_spec.rb b/spec/integration/parser/compiler_spec.rb index b2ec814ae..e6069d834 100755 --- a/spec/integration/parser/compiler_spec.rb +++ b/spec/integration/parser/compiler_spec.rb @@ -1,525 +1,525 @@ #! /usr/bin/env ruby require 'spec_helper' require 'puppet/parser/parser_factory' require 'puppet_spec/compiler' require 'matchers/resource' describe "Puppet::Parser::Compiler" do include PuppetSpec::Compiler include Matchers::Resource before :each do @node = Puppet::Node.new "testnode" @scope_resource = stub 'scope_resource', :builtin? => true, :finish => nil, :ref => 'Class[main]' @scope = stub 'scope', :resource => @scope_resource, :source => mock("source") end it "should be able to determine the configuration version from a local version control repository" do pending("Bug #14071 about semantics of Puppet::Util::Execute on Windows", :if => Puppet.features.microsoft_windows?) do # This should always work, because we should always be # in the puppet repo when we run this. version = %x{git rev-parse HEAD}.chomp Puppet.settings[:config_version] = 'git rev-parse HEAD' @parser = Puppet::Parser::ParserFactory.parser "development" @compiler = Puppet::Parser::Compiler.new(@node) @compiler.catalog.version.should == version end end it "should not create duplicate resources when a class is referenced both directly and indirectly by the node classifier (4792)" do Puppet[:code] = <<-PP class foo { notify { foo_notify: } include bar } class bar { notify { bar_notify: } } PP @node.stubs(:classes).returns(['foo', 'bar']) catalog = Puppet::Parser::Compiler.compile(@node) catalog.resource("Notify[foo_notify]").should_not be_nil catalog.resource("Notify[bar_notify]").should_not be_nil end describe "when resolving class references" do it "should favor local scope, even if there's an included class in topscope" do Puppet[:code] = <<-PP class experiment { class baz { } notify {"x" : require => Class[Baz] } } class baz { } include baz include experiment include experiment::baz PP catalog = Puppet::Parser::Compiler.compile(Puppet::Node.new("mynode")) notify_resource = catalog.resource( "Notify[x]" ) notify_resource[:require].title.should == "Experiment::Baz" end it "should favor local scope, even if there's an unincluded class in topscope" do Puppet[:code] = <<-PP class experiment { class baz { } notify {"x" : require => Class[Baz] } } class baz { } include experiment include experiment::baz PP catalog = Puppet::Parser::Compiler.compile(Puppet::Node.new("mynode")) notify_resource = catalog.resource( "Notify[x]" ) notify_resource[:require].title.should == "Experiment::Baz" end end describe "(ticket #13349) when explicitly specifying top scope" do ["class {'::bar::baz':}", "include ::bar::baz"].each do |include| describe "with #{include}" do it "should find the top level class" do Puppet[:code] = <<-MANIFEST class { 'foo::test': } class foo::test { #{include} } class bar::baz { notify { 'good!': } } class foo::bar::baz { notify { 'bad!': } } MANIFEST catalog = Puppet::Parser::Compiler.compile(Puppet::Node.new("mynode")) catalog.resource("Class[Bar::Baz]").should_not be_nil catalog.resource("Notify[good!]").should_not be_nil catalog.resource("Class[Foo::Bar::Baz]").should be_nil catalog.resource("Notify[bad!]").should be_nil end end end end it "should recompute the version after input files are re-parsed" do Puppet[:code] = 'class foo { }' Time.stubs(:now).returns(1) node = Puppet::Node.new('mynode') Puppet::Parser::Compiler.compile(node).version.should == 1 Time.stubs(:now).returns(2) Puppet::Parser::Compiler.compile(node).version.should == 1 # no change because files didn't change Puppet::Resource::TypeCollection.any_instance.stubs(:stale?).returns(true).then.returns(false) # pretend change Puppet::Parser::Compiler.compile(node).version.should == 2 end ['class', 'define', 'node'].each do |thing| it "should not allow '#{thing}' inside evaluated conditional constructs" do Puppet[:code] = <<-PP if true { #{thing} foo { } notify { decoy: } } PP begin Puppet::Parser::Compiler.compile(Puppet::Node.new("mynode")) raise "compilation should have raised Puppet::Error" rescue Puppet::Error => e e.message.should =~ /at line 2/ end end end it "should not allow classes inside unevaluated conditional constructs" do Puppet[:code] = <<-PP if false { class foo { } } PP lambda { Puppet::Parser::Compiler.compile(Puppet::Node.new("mynode")) }.should raise_error(Puppet::Error) end describe "when defining relationships" do def extract_name(ref) ref.sub(/File\[(\w+)\]/, '\1') end let(:node) { Puppet::Node.new('mynode') } let(:code) do <<-MANIFEST file { [a,b,c]: - mode => 0644, + mode => '0644', } file { [d,e]: - mode => 0755, + mode => '0755', } MANIFEST end let(:expected_relationships) { [] } let(:expected_subscriptions) { [] } before :each do Puppet[:code] = code end after :each do catalog = Puppet::Parser::Compiler.compile(node) resources = catalog.resources.select { |res| res.type == 'File' } actual_relationships, actual_subscriptions = [:before, :notify].map do |relation| resources.map do |res| dependents = Array(res[relation]) dependents.map { |ref| [res.title, extract_name(ref)] } end.inject(&:concat) end actual_relationships.should =~ expected_relationships actual_subscriptions.should =~ expected_subscriptions end it "should create a relationship" do code << "File[a] -> File[b]" expected_relationships << ['a','b'] end it "should create a subscription" do code << "File[a] ~> File[b]" expected_subscriptions << ['a', 'b'] end it "should create relationships using title arrays" do code << "File[a,b] -> File[c,d]" expected_relationships.concat [ ['a', 'c'], ['b', 'c'], ['a', 'd'], ['b', 'd'], ] end it "should create relationships using collection expressions" do code << "File <| mode == 0644 |> -> File <| mode == 0755 |>" expected_relationships.concat [ ['a', 'd'], ['b', 'd'], ['c', 'd'], ['a', 'e'], ['b', 'e'], ['c', 'e'], ] end it "should create relationships using resource names" do code << "'File[a]' -> 'File[b]'" expected_relationships << ['a', 'b'] end it "should create relationships using variables" do code << <<-MANIFEST $var = File[a] $var -> File[b] MANIFEST expected_relationships << ['a', 'b'] end it "should create relationships using case statements" do code << <<-MANIFEST $var = 10 case $var { 10: { file { s1: } } 12: { file { s2: } } } -> case $var + 2 { 10: { file { t1: } } 12: { file { t2: } } } MANIFEST expected_relationships << ['s1', 't2'] end it "should create relationships using array members" do code << <<-MANIFEST $var = [ [ [ File[a], File[b] ] ] ] $var[0][0][0] -> $var[0][0][1] MANIFEST expected_relationships << ['a', 'b'] end it "should create relationships using hash members" do code << <<-MANIFEST $var = {'foo' => {'bar' => {'source' => File[a], 'target' => File[b]}}} $var[foo][bar][source] -> $var[foo][bar][target] MANIFEST expected_relationships << ['a', 'b'] end it "should create relationships using resource declarations" do code << "file { l: } -> file { r: }" expected_relationships << ['l', 'r'] end it "should chain relationships" do code << "File[a] -> File[b] ~> File[c] <- File[d] <~ File[e]" expected_relationships << ['a', 'b'] << ['d', 'c'] expected_subscriptions << ['b', 'c'] << ['e', 'd'] end end context 'when working with immutable node data' do context 'and have opted in to immutable_node_data' do before :each do Puppet[:immutable_node_data] = true end def node_with_facts(facts) Puppet[:facts_terminus] = :memory Puppet::Node::Facts.indirection.save(Puppet::Node::Facts.new("testing", facts)) node = Puppet::Node.new("testing") node.fact_merge node end matcher :fail_compile_with do |node, message_regex| match do |manifest| @error = nil begin PuppetSpec::Compiler.compile_to_catalog(manifest, node) false rescue Puppet::Error => e @error = e message_regex.match(e.message) end end failure_message_for_should do if @error "failed with #{@error}\n#{@error.backtrace}" else "did not fail" end end end it 'should make $facts available' do node = node_with_facts('the_facts' => 'straight') catalog = compile_to_catalog(<<-MANIFEST, node) notify { 'test': message => $facts[the_facts] } MANIFEST catalog.resource("Notify[test]")[:message].should == "straight" end it 'should make $facts reserved' do node = node_with_facts('the_facts' => 'straight') expect('$facts = {}').to fail_compile_with(node, /assign to a reserved variable name: 'facts'/) expect('class a { $facts = {} } include a').to fail_compile_with(node, /assign to a reserved variable name: 'facts'/) end it 'should make $facts immutable' do node = node_with_facts('string' => 'value', 'array' => ['string'], 'hash' => { 'a' => 'string' }, 'number' => 1, 'boolean' => true) expect('$i=inline_template("<% @facts[%q{new}] = 2 %>")').to fail_compile_with(node, /frozen Hash/i) expect('$i=inline_template("<% @facts[%q{string}].chop! %>")').to fail_compile_with(node, /frozen String/i) expect('$i=inline_template("<% @facts[%q{array}][0].chop! %>")').to fail_compile_with(node, /frozen String/i) expect('$i=inline_template("<% @facts[%q{array}][1] = 2 %>")').to fail_compile_with(node, /frozen Array/i) expect('$i=inline_template("<% @facts[%q{hash}][%q{a}].chop! %>")').to fail_compile_with(node, /frozen String/i) expect('$i=inline_template("<% @facts[%q{hash}][%q{b}] = 2 %>")').to fail_compile_with(node, /frozen Hash/i) end it 'should make $facts available even if there are no facts' do Puppet[:facts_terminus] = :memory node = Puppet::Node.new("testing2") node.fact_merge catalog = compile_to_catalog(<<-MANIFEST, node) notify { 'test': message => $facts } MANIFEST expect(catalog).to have_resource("Notify[test]").with_parameter(:message, {}) end end context 'and have not opted in to immutable_node_data' do before :each do Puppet[:immutable_node_data] = false end it 'should not make $facts available' do Puppet[:facts_terminus] = :memory facts = Puppet::Node::Facts.new("testing", 'the_facts' => 'straight') Puppet::Node::Facts.indirection.save(facts) node = Puppet::Node.new("testing") node.fact_merge catalog = compile_to_catalog(<<-MANIFEST, node) notify { 'test': message => "An $facts space" } MANIFEST catalog.resource("Notify[test]")[:message].should == "An space" end end end context 'when working with the trusted data hash' do context 'and have opted in to trusted_node_data' do before :each do Puppet[:trusted_node_data] = true end it 'should make $trusted available' do node = Puppet::Node.new("testing") node.trusted_data = { "data" => "value" } catalog = compile_to_catalog(<<-MANIFEST, node) notify { 'test': message => $trusted[data] } MANIFEST catalog.resource("Notify[test]")[:message].should == "value" end it 'should not allow assignment to $trusted' do node = Puppet::Node.new("testing") node.trusted_data = { "data" => "value" } expect do catalog = compile_to_catalog(<<-MANIFEST, node) $trusted = 'changed' notify { 'test': message => $trusted == 'changed' } MANIFEST catalog.resource("Notify[test]")[:message].should == true end.to raise_error(Puppet::Error, /Attempt to assign to a reserved variable name: 'trusted'/) end it 'should not allow addition to $trusted hash' do node = Puppet::Node.new("testing") node.trusted_data = { "data" => "value" } expect do catalog = compile_to_catalog(<<-MANIFEST, node) $trusted['extra'] = 'added' notify { 'test': message => $trusted['extra'] == 'added' } MANIFEST catalog.resource("Notify[test]")[:message].should == true # different errors depending on regular or future parser end.to raise_error(Puppet::Error, /(can't modify frozen [hH]ash)|(Illegal attempt to assign)/) end it 'should not allow addition to $trusted hash via Ruby inline template' do node = Puppet::Node.new("testing") node.trusted_data = { "data" => "value" } expect do catalog = compile_to_catalog(<<-MANIFEST, node) $dummy = inline_template("<% @trusted['extra'] = 'added' %> lol") notify { 'test': message => $trusted['extra'] == 'added' } MANIFEST catalog.resource("Notify[test]")[:message].should == true end.to raise_error(Puppet::Error, /can't modify frozen [hH]ash/) end end context 'and have not opted in to trusted_node_data' do before :each do Puppet[:trusted_node_data] = false end it 'should not make $trusted available' do node = Puppet::Node.new("testing") node.trusted_data = { "data" => "value" } catalog = compile_to_catalog(<<-MANIFEST, node) notify { 'test': message => $trusted == undef } MANIFEST catalog.resource("Notify[test]")[:message].should == true end it 'should allow assignment to $trusted' do node = Puppet::Node.new("testing") catalog = compile_to_catalog(<<-MANIFEST, node) $trusted = 'changed' notify { 'test': message => $trusted == 'changed' } MANIFEST catalog.resource("Notify[test]")[:message].should == true end end end context 'when evaluating collection' do it 'matches on container inherited tags' do Puppet[:code] = <<-MANIFEST class xport_test { tag 'foo_bar' @notify { 'nbr1': message => 'explicitly tagged', tag => 'foo_bar' } @notify { 'nbr2': message => 'implicitly tagged' } Notify <| tag == 'foo_bar' |> { message => 'overridden' } } include xport_test MANIFEST catalog = Puppet::Parser::Compiler.compile(Puppet::Node.new("mynode")) expect(catalog).to have_resource("Notify[nbr1]").with_parameter(:message, 'overridden') expect(catalog).to have_resource("Notify[nbr2]").with_parameter(:message, 'overridden') end end end diff --git a/spec/integration/parser/future_compiler_spec.rb b/spec/integration/parser/future_compiler_spec.rb index ea0a87e36..d0fcfcdec 100644 --- a/spec/integration/parser/future_compiler_spec.rb +++ b/spec/integration/parser/future_compiler_spec.rb @@ -1,750 +1,750 @@ require 'spec_helper' require 'puppet/pops' require 'puppet/parser/parser_factory' require 'puppet_spec/compiler' require 'puppet_spec/pops' require 'puppet_spec/scope' require 'matchers/resource' require 'rgen/metamodel_builder' # Test compilation using the future evaluator describe "Puppet::Parser::Compiler" do include PuppetSpec::Compiler include Matchers::Resource before :each do Puppet[:parser] = 'future' end describe "the compiler when using future parser and evaluator" do it "should be able to determine the configuration version from a local version control repository" do pending("Bug #14071 about semantics of Puppet::Util::Execute on Windows", :if => Puppet.features.microsoft_windows?) do # This should always work, because we should always be # in the puppet repo when we run this. version = %x{git rev-parse HEAD}.chomp Puppet.settings[:config_version] = 'git rev-parse HEAD' compiler = Puppet::Parser::Compiler.new(Puppet::Node.new("testnode")) compiler.catalog.version.should == version end end it "should not create duplicate resources when a class is referenced both directly and indirectly by the node classifier (4792)" do node = Puppet::Node.new("testnodex") node.classes = ['foo', 'bar'] catalog = compile_to_catalog(<<-PP, node) class foo { notify { foo_notify: } include bar } class bar { notify { bar_notify: } } PP catalog = Puppet::Parser::Compiler.compile(node) expect(catalog).to have_resource("Notify[foo_notify]") expect(catalog).to have_resource("Notify[bar_notify]") end it 'applies defaults for defines with qualified names (PUP-2302)' do catalog = compile_to_catalog(<<-CODE) define my::thing($msg = 'foo') { notify {'check_me': message => $msg } } My::Thing { msg => 'evoe' } my::thing { 'name': } CODE expect(catalog).to have_resource("Notify[check_me]").with_parameter(:message, "evoe") end it 'Applies defaults from dynamic scopes (3x and future with reverted PUP-867)' do catalog = compile_to_catalog(<<-CODE) class a { Notify { message => "defaulted" } include b notify { bye: } } class b { notify { hi: } } include a CODE expect(catalog).to have_resource("Notify[hi]").with_parameter(:message, "defaulted") expect(catalog).to have_resource("Notify[bye]").with_parameter(:message, "defaulted") end it 'gets default from inherited class (PUP-867)' do catalog = compile_to_catalog(<<-CODE) class a { Notify { message => "defaulted" } include c notify { bye: } } class b { Notify { message => "inherited" } } class c inherits b { notify { hi: } } include a CODE expect(catalog).to have_resource("Notify[hi]").with_parameter(:message, "inherited") expect(catalog).to have_resource("Notify[bye]").with_parameter(:message, "defaulted") end it 'looks up default parameter values from inherited class (PUP-2532)' do catalog = compile_to_catalog(<<-CODE) class a { Notify { message => "defaulted" } include c notify { bye: } } class b { Notify { message => "inherited" } } class c inherits b { notify { hi: } } include a notify {hi_test: message => Notify[hi][message] } notify {bye_test: message => Notify[bye][message] } CODE expect(catalog).to have_resource("Notify[hi_test]").with_parameter(:message, "inherited") expect(catalog).to have_resource("Notify[bye_test]").with_parameter(:message, "defaulted") end it 'does not allow override of class parameters using a resource override expression' do expect do compile_to_catalog(<<-CODE) Class[a] { x => 2} CODE end.to raise_error(/Resource Override can only.*got: Class\[a\].*/) end describe "when resolving class references" do it "should not favor local scope (with class included in topscope)" do catalog = compile_to_catalog(<<-PP) class experiment { class baz { } notify {"x" : require => Class[Baz] } notify {"y" : require => Class[Experiment::Baz] } } class baz { } include baz include experiment include experiment::baz PP expect(catalog).to have_resource("Notify[x]").with_parameter(:require, be_resource("Class[Baz]")) expect(catalog).to have_resource("Notify[y]").with_parameter(:require, be_resource("Class[Experiment::Baz]")) end it "should not favor local scope, (with class not included in topscope)" do catalog = compile_to_catalog(<<-PP) class experiment { class baz { } notify {"x" : require => Class[Baz] } notify {"y" : require => Class[Experiment::Baz] } } class baz { } include experiment include experiment::baz PP expect(catalog).to have_resource("Notify[x]").with_parameter(:require, be_resource("Class[Baz]")) expect(catalog).to have_resource("Notify[y]").with_parameter(:require, be_resource("Class[Experiment::Baz]")) end end describe "(ticket #13349) when explicitly specifying top scope" do ["class {'::bar::baz':}", "include ::bar::baz"].each do |include| describe "with #{include}" do it "should find the top level class" do catalog = compile_to_catalog(<<-MANIFEST) class { 'foo::test': } class foo::test { #{include} } class bar::baz { notify { 'good!': } } class foo::bar::baz { notify { 'bad!': } } MANIFEST expect(catalog).to have_resource("Class[Bar::Baz]") expect(catalog).to have_resource("Notify[good!]") expect(catalog).to_not have_resource("Class[Foo::Bar::Baz]") expect(catalog).to_not have_resource("Notify[bad!]") end end end end it "should recompute the version after input files are re-parsed" do Puppet[:code] = 'class foo { }' Time.stubs(:now).returns(1) node = Puppet::Node.new('mynode') Puppet::Parser::Compiler.compile(node).version.should == 1 Time.stubs(:now).returns(2) Puppet::Parser::Compiler.compile(node).version.should == 1 # no change because files didn't change Puppet::Resource::TypeCollection.any_instance.stubs(:stale?).returns(true).then.returns(false) # pretend change Puppet::Parser::Compiler.compile(node).version.should == 2 end ['define', 'class', 'node'].each do |thing| it "'#{thing}' is not allowed inside evaluated conditional constructs" do expect do compile_to_catalog(<<-PP) if true { #{thing} foo { } notify { decoy: } } PP end.to raise_error(Puppet::Error, /Classes, definitions, and nodes may only appear at toplevel/) end it "'#{thing}' is not allowed inside un-evaluated conditional constructs" do expect do compile_to_catalog(<<-PP) if false { #{thing} foo { } notify { decoy: } } PP end.to raise_error(Puppet::Error, /Classes, definitions, and nodes may only appear at toplevel/) end end describe "relationships can be formed" do def extract_name(ref) ref.sub(/File\[(\w+)\]/, '\1') end def assert_creates_relationships(relationship_code, expectations) base_manifest = <<-MANIFEST file { [a,b,c]: - mode => 0644, + mode => '0644', } file { [d,e]: - mode => 0755, + mode => '0755', } MANIFEST catalog = compile_to_catalog(base_manifest + relationship_code) resources = catalog.resources.select { |res| res.type == 'File' } actual_relationships, actual_subscriptions = [:before, :notify].map do |relation| resources.map do |res| dependents = Array(res[relation]) dependents.map { |ref| [res.title, extract_name(ref)] } end.inject(&:concat) end actual_relationships.should =~ (expectations[:relationships] || []) actual_subscriptions.should =~ (expectations[:subscriptions] || []) end it "of regular type" do assert_creates_relationships("File[a] -> File[b]", :relationships => [['a','b']]) end it "of subscription type" do assert_creates_relationships("File[a] ~> File[b]", :subscriptions => [['a', 'b']]) end it "between multiple resources expressed as resource with multiple titles" do assert_creates_relationships("File[a,b] -> File[c,d]", :relationships => [['a', 'c'], ['b', 'c'], ['a', 'd'], ['b', 'd']]) end it "between collection expressions" do assert_creates_relationships("File <| mode == 0644 |> -> File <| mode == 0755 |>", :relationships => [['a', 'd'], ['b', 'd'], ['c', 'd'], ['a', 'e'], ['b', 'e'], ['c', 'e']]) end it "between resources expressed as Strings" do assert_creates_relationships("'File[a]' -> 'File[b]'", :relationships => [['a', 'b']]) end it "between resources expressed as variables" do assert_creates_relationships(<<-MANIFEST, :relationships => [['a', 'b']]) $var = File[a] $var -> File[b] MANIFEST end it "between resources expressed as case statements" do assert_creates_relationships(<<-MANIFEST, :relationships => [['s1', 't2']]) $var = 10 case $var { 10: { file { s1: } } 12: { file { s2: } } } -> case $var + 2 { 10: { file { t1: } } 12: { file { t2: } } } MANIFEST end it "using deep access in array" do assert_creates_relationships(<<-MANIFEST, :relationships => [['a', 'b']]) $var = [ [ [ File[a], File[b] ] ] ] $var[0][0][0] -> $var[0][0][1] MANIFEST end it "using deep access in hash" do assert_creates_relationships(<<-MANIFEST, :relationships => [['a', 'b']]) $var = {'foo' => {'bar' => {'source' => File[a], 'target' => File[b]}}} $var[foo][bar][source] -> $var[foo][bar][target] MANIFEST end it "using resource declarations" do assert_creates_relationships("file { l: } -> file { r: }", :relationships => [['l', 'r']]) end it "between entries in a chain of relationships" do assert_creates_relationships("File[a] -> File[b] ~> File[c] <- File[d] <~ File[e]", :relationships => [['a', 'b'], ['d', 'c']], :subscriptions => [['b', 'c'], ['e', 'd']]) end end context "when dealing with variable references" do it 'an initial underscore in a variable name is ok' do catalog = compile_to_catalog(<<-MANIFEST) class a { $_a = 10} include a notify { 'test': message => $a::_a } MANIFEST expect(catalog).to have_resource("Notify[test]").with_parameter(:message, 10) end it 'an initial underscore in not ok if elsewhere than last segment' do expect do catalog = compile_to_catalog(<<-MANIFEST) class a { $_a = 10} include a notify { 'test': message => $_a::_a } MANIFEST end.to raise_error(/Illegal variable name/) end it 'a missing variable as default value becomes undef' do # strict variables not on catalog = compile_to_catalog(<<-MANIFEST) class a ($b=$x) { notify {test: message=>"yes ${undef == $b}" } } include a MANIFEST expect(catalog).to have_resource("Notify[test]").with_parameter(:message, "yes true") end end context 'when working with the trusted data hash' do context 'and have opted in to hashed_node_data' do before :each do Puppet[:trusted_node_data] = true end it 'should make $trusted available' do node = Puppet::Node.new("testing") node.trusted_data = { "data" => "value" } catalog = compile_to_catalog(<<-MANIFEST, node) notify { 'test': message => $trusted[data] } MANIFEST expect(catalog).to have_resource("Notify[test]").with_parameter(:message, "value") end it 'should not allow assignment to $trusted' do node = Puppet::Node.new("testing") node.trusted_data = { "data" => "value" } expect do compile_to_catalog(<<-MANIFEST, node) $trusted = 'changed' notify { 'test': message => $trusted == 'changed' } MANIFEST end.to raise_error(Puppet::Error, /Attempt to assign to a reserved variable name: 'trusted'/) end end context 'and have not opted in to hashed_node_data' do before :each do Puppet[:trusted_node_data] = false end it 'should not make $trusted available' do node = Puppet::Node.new("testing") node.trusted_data = { "data" => "value" } catalog = compile_to_catalog(<<-MANIFEST, node) notify { 'test': message => ($trusted == undef) } MANIFEST expect(catalog).to have_resource("Notify[test]").with_parameter(:message, true) end it 'should allow assignment to $trusted' do catalog = compile_to_catalog(<<-MANIFEST) $trusted = 'changed' notify { 'test': message => $trusted == 'changed' } MANIFEST expect(catalog).to have_resource("Notify[test]").with_parameter(:message, true) end end end context 'when using typed parameters in definition' do it 'accepts type compliant arguments' do catalog = compile_to_catalog(<<-MANIFEST) define foo(String $x) { } foo { 'test': x =>'say friend' } MANIFEST expect(catalog).to have_resource("Foo[test]").with_parameter(:x, 'say friend') end it 'accepts anything when parameters are untyped' do expect do catalog = compile_to_catalog(<<-MANIFEST) define foo($a, $b, $c) { } foo { 'test': a => String, b=>10, c=>undef } MANIFEST end.to_not raise_error() end it 'denies non type compliant arguments' do expect do catalog = compile_to_catalog(<<-MANIFEST) define foo(Integer $x) { } foo { 'test': x =>'say friend' } MANIFEST end.to raise_error(/type Integer, got String/) end it 'denies non type compliant default argument' do expect do catalog = compile_to_catalog(<<-MANIFEST) define foo(Integer $x = 'pow') { } foo { 'test': } MANIFEST end.to raise_error(/type Integer, got String/) end it 'accepts a Resource as a Type' do catalog = compile_to_catalog(<<-MANIFEST) define foo(Type[Bar] $x) { notify { 'test': message => $x[text] } } define bar($text) { } bar { 'joke': text => 'knock knock' } foo { 'test': x => Bar[joke] } MANIFEST expect(catalog).to have_resource("Notify[test]").with_parameter(:message, 'knock knock') end end context 'when using typed parameters in class' do it 'accepts type compliant arguments' do catalog = compile_to_catalog(<<-MANIFEST) class foo(String $x) { } class { 'foo': x =>'say friend' } MANIFEST expect(catalog).to have_resource("Class[Foo]").with_parameter(:x, 'say friend') end it 'accepts anything when parameters are untyped' do expect do catalog = compile_to_catalog(<<-MANIFEST) class foo($a, $b, $c) { } class { 'foo': a => String, b=>10, c=>undef } MANIFEST end.to_not raise_error() end it 'denies non type compliant arguments' do expect do catalog = compile_to_catalog(<<-MANIFEST) class foo(Integer $x) { } class { 'foo': x =>'say friend' } MANIFEST end.to raise_error(/type Integer, got String/) end it 'denies non type compliant default argument' do expect do catalog = compile_to_catalog(<<-MANIFEST) class foo(Integer $x = 'pow') { } class { 'foo': } MANIFEST end.to raise_error(/type Integer, got String/) end it 'accepts a Resource as a Type' do catalog = compile_to_catalog(<<-MANIFEST) class foo(Type[Bar] $x) { notify { 'test': message => $x[text] } } define bar($text) { } bar { 'joke': text => 'knock knock' } class { 'foo': x => Bar[joke] } MANIFEST expect(catalog).to have_resource("Notify[test]").with_parameter(:message, 'knock knock') end end context 'when using typed parameters in lambdas' do it 'accepts type compliant arguments' do catalog = compile_to_catalog(<<-MANIFEST) with('value') |String $x| { notify { "$x": } } MANIFEST expect(catalog).to have_resource("Notify[value]") end it 'handles an array as a single argument' do catalog = compile_to_catalog(<<-MANIFEST) with(['value', 'second']) |$x| { notify { "${x[0]} ${x[1]}": } } MANIFEST expect(catalog).to have_resource("Notify[value second]") end it 'denies when missing required arguments' do expect do compile_to_catalog(<<-MANIFEST) with(1) |$x, $y| { } MANIFEST end.to raise_error(/Parameter \$y is required but no value was given/m) end it 'accepts anything when parameters are untyped' do catalog = compile_to_catalog(<<-MANIFEST) ['value', 1, true, undef].each |$x| { notify { "value: $x": } } MANIFEST expect(catalog).to have_resource("Notify[value: value]") expect(catalog).to have_resource("Notify[value: 1]") expect(catalog).to have_resource("Notify[value: true]") expect(catalog).to have_resource("Notify[value: ]") end it 'accepts type-compliant, slurped arguments' do catalog = compile_to_catalog(<<-MANIFEST) with(1, 2) |Integer *$x| { notify { "${$x[0] + $x[1]}": } } MANIFEST expect(catalog).to have_resource("Notify[3]") end it 'denies non-type-compliant arguments' do expect do compile_to_catalog(<<-MANIFEST) with(1) |String $x| { } MANIFEST end.to raise_error(/expected.*String.*actual.*Integer/m) end it 'denies non-type-compliant, slurped arguments' do expect do compile_to_catalog(<<-MANIFEST) with(1, "hello") |Integer *$x| { } MANIFEST end.to raise_error(/called with mis-matched arguments.*expected.*Integer.*actual.*Integer, String/m) end it 'denies non-type-compliant default argument' do expect do compile_to_catalog(<<-MANIFEST) with(1) |$x, String $defaulted = 1| { notify { "${$x + $defaulted}": }} MANIFEST end.to raise_error(/expected.*Any.*String.*actual.*Integer.*Integer/m) end it 'raises an error when a default argument value is an incorrect type and there are no arguments passed' do expect do compile_to_catalog(<<-MANIFEST) with() |String $defaulted = 1| {} MANIFEST end.to raise_error(/expected.*String.*actual.*Integer/m) end it 'raises an error when the default argument for a slurped parameter is an incorrect type' do expect do compile_to_catalog(<<-MANIFEST) with() |String *$defaulted = 1| {} MANIFEST end.to raise_error(/expected.*String.*actual.*Integer/m) end it 'allows using an array as the default slurped value' do catalog = compile_to_catalog(<<-MANIFEST) with() |String *$defaulted = [hi]| { notify { $defaulted[0]: } } MANIFEST expect(catalog).to have_resource('Notify[hi]') end it 'allows using a value of the type as the default slurped value' do catalog = compile_to_catalog(<<-MANIFEST) with() |String *$defaulted = hi| { notify { $defaulted[0]: } } MANIFEST expect(catalog).to have_resource('Notify[hi]') end it 'allows specifying the type of a slurped parameter as an array' do catalog = compile_to_catalog(<<-MANIFEST) with() |Array[String] *$defaulted = hi| { notify { $defaulted[0]: } } MANIFEST expect(catalog).to have_resource('Notify[hi]') end it 'raises an error when the number of default values does not match the parameter\'s size specification' do expect do compile_to_catalog(<<-MANIFEST) with() |Array[String, 2] *$defaulted = hi| { } MANIFEST end.to raise_error(/expected.*arg count \{2,\}.*actual.*arg count \{1\}/m) end it 'raises an error when the number of passed values does not match the parameter\'s size specification' do expect do compile_to_catalog(<<-MANIFEST) with(hi) |Array[String, 2] *$passed| { } MANIFEST end.to raise_error(/expected.*arg count \{2,\}.*actual.*arg count \{1\}/m) end it 'matches when the number of arguments passed for a slurp parameter match the size specification' do catalog = compile_to_catalog(<<-MANIFEST) with(hi, bye) |Array[String, 2] *$passed| { $passed.each |$n| { notify { $n: } } } MANIFEST expect(catalog).to have_resource('Notify[hi]') expect(catalog).to have_resource('Notify[bye]') end it 'raises an error when the number of allowed slurp parameters exceeds the size constraint' do expect do compile_to_catalog(<<-MANIFEST) with(hi, bye) |Array[String, 1, 1] *$passed| { } MANIFEST end.to raise_error(/expected.*arg count \{1\}.*actual.*arg count \{2\}/m) end it 'allows passing slurped arrays by specifying an array of arrays' do catalog = compile_to_catalog(<<-MANIFEST) with([hi], [bye]) |Array[Array[String, 1, 1]] *$passed| { notify { $passed[0][0]: } notify { $passed[1][0]: } } MANIFEST expect(catalog).to have_resource('Notify[hi]') expect(catalog).to have_resource('Notify[bye]') end it 'raises an error when a required argument follows an optional one' do expect do compile_to_catalog(<<-MANIFEST) with() |$y = first, $x, Array[String, 1] *$passed = bye| {} MANIFEST end.to raise_error(/Parameter \$x is required/) end it 'raises an error when the minimum size of a slurped argument makes it required and it follows an optional argument' do expect do compile_to_catalog(<<-MANIFEST) with() |$x = first, Array[String, 1] *$passed| {} MANIFEST end.to raise_error(/Parameter \$passed is required/) end it 'allows slurped arguments with a minimum size of 0 after an optional argument' do catalog = compile_to_catalog(<<-MANIFEST) with() |$x = first, Array[String, 0] *$passed| { notify { $x: } } MANIFEST expect(catalog).to have_resource('Notify[first]') end it 'accepts a Resource as a Type' do catalog = compile_to_catalog(<<-MANIFEST) define bar($text) { } bar { 'joke': text => 'knock knock' } with(Bar[joke]) |Type[Bar] $joke| { notify { "${joke[text]}": } } MANIFEST expect(catalog).to have_resource("Notify[knock knock]") end end end context 'when evaluating collection' do it 'matches on container inherited tags' do Puppet[:code] = <<-MANIFEST class xport_test { tag('foo_bar') @notify { 'nbr1': message => 'explicitly tagged', tag => 'foo_bar' } @notify { 'nbr2': message => 'implicitly tagged' } Notify <| tag == 'foo_bar' |> { message => 'overridden' } } include xport_test MANIFEST catalog = Puppet::Parser::Compiler.compile(Puppet::Node.new("mynode")) expect(catalog).to have_resource("Notify[nbr1]").with_parameter(:message, 'overridden') expect(catalog).to have_resource("Notify[nbr2]").with_parameter(:message, 'overridden') end end end diff --git a/spec/integration/type/file_spec.rb b/spec/integration/type/file_spec.rb index ba009c1d1..d1862e241 100755 --- a/spec/integration/type/file_spec.rb +++ b/spec/integration/type/file_spec.rb @@ -1,1378 +1,1378 @@ #! /usr/bin/env ruby require 'spec_helper' require 'puppet_spec/files' if Puppet.features.microsoft_windows? require 'puppet/util/windows' class WindowsSecurity extend Puppet::Util::Windows::Security end end describe Puppet::Type.type(:file), :uses_checksums => true do include PuppetSpec::Files let(:catalog) { Puppet::Resource::Catalog.new } let(:path) do # we create a directory first so backups of :path that are stored in # the same directory will also be removed after the tests parent = tmpdir('file_spec') File.join(parent, 'file_testing') end let(:dir) do # we create a directory first so backups of :path that are stored in # the same directory will also be removed after the tests parent = tmpdir('file_spec') File.join(parent, 'dir_testing') end if Puppet.features.posix? def set_mode(mode, file) File.chmod(mode, file) end def get_mode(file) Puppet::FileSystem.lstat(file).mode end def get_owner(file) Puppet::FileSystem.lstat(file).uid end def get_group(file) Puppet::FileSystem.lstat(file).gid end else class SecurityHelper extend Puppet::Util::Windows::Security end def set_mode(mode, file) SecurityHelper.set_mode(mode, file) end def get_mode(file) SecurityHelper.get_mode(file) end def get_owner(file) SecurityHelper.get_owner(file) end def get_group(file) SecurityHelper.get_group(file) end def get_aces_for_path_by_sid(path, sid) SecurityHelper.get_aces_for_path_by_sid(path, sid) end end before do # stub this to not try to create state.yaml Puppet::Util::Storage.stubs(:store) end it "should not attempt to manage files that do not exist if no means of creating the file is specified" do source = tmpfile('source') - catalog.add_resource described_class.new :path => source, :mode => 0755 + catalog.add_resource described_class.new :path => source, :mode => '0755' status = catalog.apply.report.resource_statuses["File[#{source}]"] status.should_not be_failed status.should_not be_changed Puppet::FileSystem.exist?(source).should be_false end describe "when ensure is absent" do it "should remove the file if present" do FileUtils.touch(path) catalog.add_resource(described_class.new(:path => path, :ensure => :absent, :backup => :false)) report = catalog.apply.report report.resource_statuses["File[#{path}]"].should_not be_failed Puppet::FileSystem.exist?(path).should be_false end it "should do nothing if file is not present" do catalog.add_resource(described_class.new(:path => path, :ensure => :absent, :backup => :false)) report = catalog.apply.report report.resource_statuses["File[#{path}]"].should_not be_failed Puppet::FileSystem.exist?(path).should be_false end # issue #14599 it "should not fail if parts of path aren't directories" do FileUtils.touch(path) catalog.add_resource(described_class.new(:path => File.join(path,'no_such_file'), :ensure => :absent, :backup => :false)) report = catalog.apply.report report.resource_statuses["File[#{File.join(path,'no_such_file')}]"].should_not be_failed end end describe "when setting permissions" do it "should set the owner" do target = tmpfile_with_contents('target', '') owner = get_owner(target) catalog.add_resource described_class.new( :name => target, :owner => owner ) catalog.apply get_owner(target).should == owner end it "should set the group" do target = tmpfile_with_contents('target', '') group = get_group(target) catalog.add_resource described_class.new( :name => target, :group => group ) catalog.apply get_group(target).should == group end describe "when setting mode" do describe "for directories" do let(:target) { tmpdir('dir_mode') } it "should set executable bits for newly created directories" do catalog.add_resource described_class.new(:path => target, :ensure => :directory, :mode => 0600) catalog.apply (get_mode(target) & 07777).should == 0700 end it "should set executable bits for existing readable directories" do set_mode(0600, target) - catalog.add_resource described_class.new(:path => target, :ensure => :directory, :mode => 0644) + catalog.add_resource described_class.new(:path => target, :ensure => :directory, :mode => '0644') catalog.apply (get_mode(target) & 07777).should == 0755 end it "should not set executable bits for unreadable directories" do begin catalog.add_resource described_class.new(:path => target, :ensure => :directory, :mode => 0300) catalog.apply (get_mode(target) & 07777).should == 0300 ensure # so we can cleanup set_mode(0700, target) end end it "should set user, group, and other executable bits" do catalog.add_resource described_class.new(:path => target, :ensure => :directory, :mode => 0664) catalog.apply (get_mode(target) & 07777).should == 0775 end it "should set executable bits when overwriting a non-executable file" do target_path = tmpfile_with_contents('executable', '') set_mode(0444, target_path) catalog.add_resource described_class.new(:path => target_path, :ensure => :directory, :mode => 0666, :backup => false) catalog.apply (get_mode(target_path) & 07777).should == 0777 File.should be_directory(target_path) end end describe "for files" do it "should not set executable bits" do catalog.add_resource described_class.new(:path => path, :ensure => :file, :mode => 0666) catalog.apply (get_mode(path) & 07777).should == 0666 end it "should not set executable bits when replacing an executable directory (#10365)" do pending("bug #10365") FileUtils.mkdir(path) set_mode(0777, path) catalog.add_resource described_class.new(:path => path, :ensure => :file, :mode => 0666, :backup => false, :force => true) catalog.apply (get_mode(path) & 07777).should == 0666 end end describe "for links", :if => described_class.defaultprovider.feature?(:manages_symlinks) do let(:link) { tmpfile('link_mode') } describe "when managing links" do let(:link_target) { tmpfile('target') } before :each do FileUtils.touch(link_target) File.chmod(0444, link_target) Puppet::FileSystem.symlink(link_target, link) end it "should not set the executable bit on the link nor the target" do catalog.add_resource described_class.new(:path => link, :ensure => :link, :mode => 0666, :target => link_target, :links => :manage) catalog.apply (Puppet::FileSystem.stat(link).mode & 07777) == 0666 (Puppet::FileSystem.lstat(link_target).mode & 07777) == 0444 end it "should ignore dangling symlinks (#6856)" do File.delete(link_target) catalog.add_resource described_class.new(:path => link, :ensure => :link, :mode => 0666, :target => link_target, :links => :manage) catalog.apply Puppet::FileSystem.exist?(link).should be_false end it "should create a link to the target if ensure is omitted" do FileUtils.touch(link_target) catalog.add_resource described_class.new(:path => link, :target => link_target) catalog.apply Puppet::FileSystem.exist?(link).should be_true Puppet::FileSystem.lstat(link).ftype.should == 'link' Puppet::FileSystem.readlink(link).should == link_target end end describe "when following links" do it "should ignore dangling symlinks (#6856)" do target = tmpfile('dangling') FileUtils.touch(target) Puppet::FileSystem.symlink(target, link) File.delete(target) catalog.add_resource described_class.new(:path => path, :source => link, :mode => 0600, :links => :follow) catalog.apply end describe "to a directory" do let(:link_target) { tmpdir('dir_target') } before :each do File.chmod(0600, link_target) Puppet::FileSystem.symlink(link_target, link) end after :each do File.chmod(0750, link_target) end describe "that is readable" do it "should set the executable bits when creating the destination (#10315)" do catalog.add_resource described_class.new(:path => path, :source => link, :mode => 0666, :links => :follow) catalog.apply File.should be_directory(path) (get_mode(path) & 07777).should == 0777 end it "should set the executable bits when overwriting the destination (#10315)" do FileUtils.touch(path) catalog.add_resource described_class.new(:path => path, :source => link, :mode => 0666, :links => :follow, :backup => false) catalog.apply File.should be_directory(path) (get_mode(path) & 07777).should == 0777 end end describe "that is not readable" do before :each do set_mode(0300, link_target) end # so we can cleanup after :each do set_mode(0700, link_target) end it "should set executable bits when creating the destination (#10315)" do catalog.add_resource described_class.new(:path => path, :source => link, :mode => 0666, :links => :follow) catalog.apply File.should be_directory(path) (get_mode(path) & 07777).should == 0777 end it "should set executable bits when overwriting the destination" do FileUtils.touch(path) catalog.add_resource described_class.new(:path => path, :source => link, :mode => 0666, :links => :follow, :backup => false) catalog.apply File.should be_directory(path) (get_mode(path) & 07777).should == 0777 end end end describe "to a file" do let(:link_target) { tmpfile('file_target') } before :each do FileUtils.touch(link_target) Puppet::FileSystem.symlink(link_target, link) end it "should create the file, not a symlink (#2817, #10315)" do catalog.add_resource described_class.new(:path => path, :source => link, :mode => 0600, :links => :follow) catalog.apply File.should be_file(path) (get_mode(path) & 07777).should == 0600 end it "should overwrite the file" do FileUtils.touch(path) catalog.add_resource described_class.new(:path => path, :source => link, :mode => 0600, :links => :follow) catalog.apply File.should be_file(path) (get_mode(path) & 07777).should == 0600 end end describe "to a link to a directory" do let(:real_target) { tmpdir('real_target') } let(:target) { tmpfile('target') } before :each do File.chmod(0666, real_target) # link -> target -> real_target Puppet::FileSystem.symlink(real_target, target) Puppet::FileSystem.symlink(target, link) end after :each do File.chmod(0750, real_target) end describe "when following all links" do it "should create the destination and apply executable bits (#10315)" do catalog.add_resource described_class.new(:path => path, :source => link, :mode => 0600, :links => :follow) catalog.apply File.should be_directory(path) (get_mode(path) & 07777).should == 0700 end it "should overwrite the destination and apply executable bits" do FileUtils.mkdir(path) catalog.add_resource described_class.new(:path => path, :source => link, :mode => 0600, :links => :follow) catalog.apply File.should be_directory(path) (get_mode(path) & 0111).should == 0100 end end end end end end end describe "when writing files" do with_digest_algorithms do it "should backup files to a filebucket when one is configured" do filebucket = Puppet::Type.type(:filebucket).new :path => tmpfile("filebucket"), :name => "mybucket" file = described_class.new :path => path, :backup => "mybucket", :content => "foo" catalog.add_resource file catalog.add_resource filebucket File.open(file[:path], "w") { |f| f.write("bar") } d = digest(IO.binread(file[:path])) catalog.apply filebucket.bucket.getfile(d).should == "bar" end it "should backup files in the local directory when a backup string is provided" do file = described_class.new :path => path, :backup => ".bak", :content => "foo" catalog.add_resource file File.open(file[:path], "w") { |f| f.puts "bar" } catalog.apply backup = file[:path] + ".bak" Puppet::FileSystem.exist?(backup).should be_true File.read(backup).should == "bar\n" end it "should fail if no backup can be performed" do dir = tmpdir("backups") file = described_class.new :path => File.join(dir, "testfile"), :backup => ".bak", :content => "foo" catalog.add_resource file File.open(file[:path], 'w') { |f| f.puts "bar" } # Create a directory where the backup should be so that writing to it fails Dir.mkdir(File.join(dir, "testfile.bak")) Puppet::Util::Log.stubs(:newmessage) catalog.apply File.read(file[:path]).should == "bar\n" end it "should not backup symlinks", :if => described_class.defaultprovider.feature?(:manages_symlinks) do link = tmpfile("link") dest1 = tmpfile("dest1") dest2 = tmpfile("dest2") bucket = Puppet::Type.type(:filebucket).new :path => tmpfile("filebucket"), :name => "mybucket" file = described_class.new :path => link, :target => dest2, :ensure => :link, :backup => "mybucket" catalog.add_resource file catalog.add_resource bucket File.open(dest1, "w") { |f| f.puts "whatever" } Puppet::FileSystem.symlink(dest1, link) d = digest(File.read(file[:path])) catalog.apply Puppet::FileSystem.readlink(link).should == dest2 Puppet::FileSystem.exist?(bucket[:path]).should be_false end it "should backup directories to the local filesystem by copying the whole directory" do file = described_class.new :path => path, :backup => ".bak", :content => "foo", :force => true catalog.add_resource file Dir.mkdir(path) otherfile = File.join(path, "foo") File.open(otherfile, "w") { |f| f.print "yay" } catalog.apply backup = "#{path}.bak" FileTest.should be_directory(backup) File.read(File.join(backup, "foo")).should == "yay" end it "should backup directories to filebuckets by backing up each file separately" do bucket = Puppet::Type.type(:filebucket).new :path => tmpfile("filebucket"), :name => "mybucket" file = described_class.new :path => tmpfile("bucket_backs"), :backup => "mybucket", :content => "foo", :force => true catalog.add_resource file catalog.add_resource bucket Dir.mkdir(file[:path]) foofile = File.join(file[:path], "foo") barfile = File.join(file[:path], "bar") File.open(foofile, "w") { |f| f.print "fooyay" } File.open(barfile, "w") { |f| f.print "baryay" } food = digest(File.read(foofile)) bard = digest(File.read(barfile)) catalog.apply bucket.bucket.getfile(food).should == "fooyay" bucket.bucket.getfile(bard).should == "baryay" end end end describe "when recursing" do def build_path(dir) Dir.mkdir(dir) File.chmod(0750, dir) @dirs = [dir] @files = [] %w{one two}.each do |subdir| fdir = File.join(dir, subdir) Dir.mkdir(fdir) File.chmod(0750, fdir) @dirs << fdir %w{three}.each do |file| ffile = File.join(fdir, file) @files << ffile File.open(ffile, "w") { |f| f.puts "test #{file}" } File.chmod(0640, ffile) end end end it "should be able to recurse over a nonexistent file" do @file = described_class.new( :name => path, :mode => 0644, :recurse => true, :backup => false ) catalog.add_resource @file lambda { @file.eval_generate }.should_not raise_error end it "should be able to recursively set properties on existing files" do path = tmpfile("file_integration_tests") build_path(path) file = described_class.new( :name => path, :mode => 0644, :recurse => true, :backup => false ) catalog.add_resource file catalog.apply @dirs.should_not be_empty @dirs.each do |path| (get_mode(path) & 007777).should == 0755 end @files.should_not be_empty @files.each do |path| (get_mode(path) & 007777).should == 0644 end end it "should be able to recursively make links to other files", :if => described_class.defaultprovider.feature?(:manages_symlinks) do source = tmpfile("file_link_integration_source") build_path(source) dest = tmpfile("file_link_integration_dest") @file = described_class.new(:name => dest, :target => source, :recurse => true, :ensure => :link, :backup => false) catalog.add_resource @file catalog.apply @dirs.each do |path| link_path = path.sub(source, dest) Puppet::FileSystem.lstat(link_path).should be_directory end @files.each do |path| link_path = path.sub(source, dest) Puppet::FileSystem.lstat(link_path).ftype.should == "link" end end it "should be able to recursively copy files" do source = tmpfile("file_source_integration_source") build_path(source) dest = tmpfile("file_source_integration_dest") @file = described_class.new(:name => dest, :source => source, :recurse => true, :backup => false) catalog.add_resource @file catalog.apply @dirs.each do |path| newpath = path.sub(source, dest) Puppet::FileSystem.lstat(newpath).should be_directory end @files.each do |path| newpath = path.sub(source, dest) Puppet::FileSystem.lstat(newpath).ftype.should == "file" end end it "should not recursively manage files managed by a more specific explicit file" do dir = tmpfile("recursion_vs_explicit_1") subdir = File.join(dir, "subdir") file = File.join(subdir, "file") FileUtils.mkdir_p(subdir) File.open(file, "w") { |f| f.puts "" } base = described_class.new(:name => dir, :recurse => true, :backup => false, :mode => "755") sub = described_class.new(:name => subdir, :recurse => true, :backup => false, :mode => "644") catalog.add_resource base catalog.add_resource sub catalog.apply (get_mode(file) & 007777).should == 0644 end it "should recursively manage files even if there is an explicit file whose name is a prefix of the managed file" do managed = File.join(path, "file") generated = File.join(path, "file_with_a_name_starting_with_the_word_file") managed_mode = 0700 FileUtils.mkdir_p(path) FileUtils.touch(managed) FileUtils.touch(generated) catalog.add_resource described_class.new(:name => path, :recurse => true, :backup => false, :mode => managed_mode) catalog.add_resource described_class.new(:name => managed, :recurse => true, :backup => false, :mode => "644") catalog.apply (get_mode(generated) & 007777).should == managed_mode end describe "when recursing remote directories" do describe "when sourceselect first" do describe "for a directory" do it "should recursively copy the first directory that exists" do one = File.expand_path('thisdoesnotexist') two = tmpdir('two') FileUtils.mkdir_p(File.join(two, 'three')) FileUtils.touch(File.join(two, 'three', 'four')) catalog.add_resource Puppet::Type.newfile( :path => path, :ensure => :directory, :backup => false, :recurse => true, :sourceselect => :first, :source => [one, two] ) catalog.apply File.should be_directory(path) Puppet::FileSystem.exist?(File.join(path, 'one')).should be_false Puppet::FileSystem.exist?(File.join(path, 'three', 'four')).should be_true end it "should recursively copy an empty directory" do one = File.expand_path('thisdoesnotexist') two = tmpdir('two') three = tmpdir('three') file_in_dir_with_contents(three, 'a', '') catalog.add_resource Puppet::Type.newfile( :path => path, :ensure => :directory, :backup => false, :recurse => true, :sourceselect => :first, :source => [one, two, three] ) catalog.apply File.should be_directory(path) Puppet::FileSystem.exist?(File.join(path, 'a')).should be_false end it "should only recurse one level" do one = tmpdir('one') FileUtils.mkdir_p(File.join(one, 'a', 'b')) FileUtils.touch(File.join(one, 'a', 'b', 'c')) two = tmpdir('two') FileUtils.mkdir_p(File.join(two, 'z')) FileUtils.touch(File.join(two, 'z', 'y')) catalog.add_resource Puppet::Type.newfile( :path => path, :ensure => :directory, :backup => false, :recurse => true, :recurselimit => 1, :sourceselect => :first, :source => [one, two] ) catalog.apply Puppet::FileSystem.exist?(File.join(path, 'a')).should be_true Puppet::FileSystem.exist?(File.join(path, 'a', 'b')).should be_false Puppet::FileSystem.exist?(File.join(path, 'z')).should be_false end end describe "for a file" do it "should copy the first file that exists" do one = File.expand_path('thisdoesnotexist') two = tmpfile_with_contents('two', 'yay') three = tmpfile_with_contents('three', 'no') catalog.add_resource Puppet::Type.newfile( :path => path, :ensure => :file, :backup => false, :sourceselect => :first, :source => [one, two, three] ) catalog.apply File.read(path).should == 'yay' end it "should copy an empty file" do one = File.expand_path('thisdoesnotexist') two = tmpfile_with_contents('two', '') three = tmpfile_with_contents('three', 'no') catalog.add_resource Puppet::Type.newfile( :path => path, :ensure => :file, :backup => false, :sourceselect => :first, :source => [one, two, three] ) catalog.apply File.read(path).should == '' end end end describe "when sourceselect all" do describe "for a directory" do it "should recursively copy all sources from the first valid source" do dest = tmpdir('dest') one = tmpdir('one') two = tmpdir('two') three = tmpdir('three') four = tmpdir('four') file_in_dir_with_contents(one, 'a', one) file_in_dir_with_contents(two, 'a', two) file_in_dir_with_contents(two, 'b', two) file_in_dir_with_contents(three, 'a', three) file_in_dir_with_contents(three, 'c', three) obj = Puppet::Type.newfile( :path => dest, :ensure => :directory, :backup => false, :recurse => true, :sourceselect => :all, :source => [one, two, three, four] ) catalog.add_resource obj catalog.apply File.read(File.join(dest, 'a')).should == one File.read(File.join(dest, 'b')).should == two File.read(File.join(dest, 'c')).should == three end it "should only recurse one level from each valid source" do one = tmpdir('one') FileUtils.mkdir_p(File.join(one, 'a', 'b')) FileUtils.touch(File.join(one, 'a', 'b', 'c')) two = tmpdir('two') FileUtils.mkdir_p(File.join(two, 'z')) FileUtils.touch(File.join(two, 'z', 'y')) obj = Puppet::Type.newfile( :path => path, :ensure => :directory, :backup => false, :recurse => true, :recurselimit => 1, :sourceselect => :all, :source => [one, two] ) catalog.add_resource obj catalog.apply Puppet::FileSystem.exist?(File.join(path, 'a')).should be_true Puppet::FileSystem.exist?(File.join(path, 'a', 'b')).should be_false Puppet::FileSystem.exist?(File.join(path, 'z')).should be_true Puppet::FileSystem.exist?(File.join(path, 'z', 'y')).should be_false end end end end end describe "when generating resources" do before do source = tmpdir("generating_in_catalog_source") s1 = file_in_dir_with_contents(source, "one", "uno") s2 = file_in_dir_with_contents(source, "two", "dos") @file = described_class.new( :name => path, :source => source, :recurse => true, :backup => false ) catalog.add_resource @file end it "should add each generated resource to the catalog" do catalog.apply do |trans| catalog.resource(:file, File.join(path, "one")).must be_a(described_class) catalog.resource(:file, File.join(path, "two")).must be_a(described_class) end end it "should have an edge to each resource in the relationship graph" do catalog.apply do |trans| one = catalog.resource(:file, File.join(path, "one")) catalog.relationship_graph.should be_edge(@file, one) two = catalog.resource(:file, File.join(path, "two")) catalog.relationship_graph.should be_edge(@file, two) end end end describe "when copying files" do it "should be able to copy files with pound signs in their names (#285)" do source = tmpfile_with_contents("filewith#signs", "foo") dest = tmpfile("destwith#signs") catalog.add_resource described_class.new(:name => dest, :source => source) catalog.apply File.read(dest).should == "foo" end it "should be able to copy files with spaces in their names" do dest = tmpfile("destwith spaces") source = tmpfile_with_contents("filewith spaces", "foo") expected_mode = 0755 Puppet::FileSystem.chmod(expected_mode, source) catalog.add_resource described_class.new(:path => dest, :source => source) catalog.apply File.read(dest).should == "foo" (Puppet::FileSystem.stat(dest).mode & 007777).should == expected_mode end it "should be able to copy individual files even if recurse has been specified" do source = tmpfile_with_contents("source", "foo") dest = tmpfile("dest") catalog.add_resource described_class.new(:name => dest, :source => source, :recurse => true) catalog.apply File.read(dest).should == "foo" end end it "should create a file with content if ensure is omitted" do catalog.add_resource described_class.new( :path => path, :content => "this is some content, yo" ) catalog.apply File.read(path).should == "this is some content, yo" end it "should create files with content if both content and ensure are set" do file = described_class.new( :path => path, :ensure => "file", :content => "this is some content, yo" ) catalog.add_resource file catalog.apply File.read(path).should == "this is some content, yo" end it "should delete files with sources but that are set for deletion" do source = tmpfile_with_contents("source_source_with_ensure", "yay") dest = tmpfile_with_contents("source_source_with_ensure", "boo") file = described_class.new( :path => dest, :ensure => :absent, :source => source, :backup => false ) catalog.add_resource file catalog.apply Puppet::FileSystem.exist?(dest).should be_false end describe "when sourcing" do let(:source) { tmpfile_with_contents("source_default_values", "yay") } it "should apply the source metadata values" do set_mode(0770, source) file = described_class.new( :path => path, :ensure => :file, :source => source, :backup => false ) catalog.add_resource file catalog.apply get_owner(path).should == get_owner(source) get_group(path).should == get_group(source) (get_mode(path) & 07777).should == 0770 end it "should override the default metadata values" do set_mode(0770, source) file = described_class.new( :path => path, :ensure => :file, :source => source, :backup => false, :mode => 0440 ) catalog.add_resource file catalog.apply (get_mode(path) & 07777).should == 0440 end describe "on Windows systems", :if => Puppet.features.microsoft_windows? do def expects_sid_granted_full_access_explicitly(path, sid) inherited_ace = Puppet::Util::Windows::AccessControlEntry::INHERITED_ACE aces = get_aces_for_path_by_sid(path, sid) aces.should_not be_empty aces.each do |ace| ace.mask.should == Puppet::Util::Windows::File::FILE_ALL_ACCESS (ace.flags & inherited_ace).should_not == inherited_ace end end def expects_system_granted_full_access_explicitly(path) expects_sid_granted_full_access_explicitly(path, @sids[:system]) end def expects_at_least_one_inherited_ace_grants_full_access(path, sid) inherited_ace = Puppet::Util::Windows::AccessControlEntry::INHERITED_ACE aces = get_aces_for_path_by_sid(path, sid) aces.should_not be_empty aces.any? do |ace| ace.mask == Puppet::Util::Windows::File::FILE_ALL_ACCESS && (ace.flags & inherited_ace) == inherited_ace end.should be_true end def expects_at_least_one_inherited_system_ace_grants_full_access(path) expects_at_least_one_inherited_ace_grants_full_access(path, @sids[:system]) end it "should provide valid default values when ACLs are not supported" do Puppet::Util::Windows::Security.stubs(:supports_acl?).returns(false) Puppet::Util::Windows::Security.stubs(:supports_acl?).with(source).returns false file = described_class.new( :path => path, :ensure => :file, :source => source, :backup => false ) catalog.add_resource file catalog.apply get_owner(path).should =~ /^S\-1\-5\-.*$/ get_group(path).should =~ /^S\-1\-0\-0.*$/ get_mode(path).should == 0644 end describe "when processing SYSTEM ACEs" do before do @sids = { :current_user => Puppet::Util::Windows::SID.name_to_sid(Puppet::Util::Windows::ADSI::User.current_user_name), :system => Win32::Security::SID::LocalSystem, :admin => Puppet::Util::Windows::SID.name_to_sid("Administrator"), :guest => Puppet::Util::Windows::SID.name_to_sid("Guest"), :users => Win32::Security::SID::BuiltinUsers, :power_users => Win32::Security::SID::PowerUsers, :none => Win32::Security::SID::Nobody } end describe "on files" do before :each do @file = described_class.new( :path => path, :ensure => :file, :source => source, :backup => false ) catalog.add_resource @file end describe "when source permissions are ignored" do before :each do @file[:source_permissions] = :ignore end it "preserves the inherited SYSTEM ACE" do catalog.apply expects_at_least_one_inherited_system_ace_grants_full_access(path) end end describe "when permissions are insync?" do it "preserves the explicit SYSTEM ACE" do FileUtils.touch(path) sd = Puppet::Util::Windows::Security.get_security_descriptor(path) sd.protect = true sd.owner = @sids[:none] sd.group = @sids[:none] Puppet::Util::Windows::Security.set_security_descriptor(source, sd) Puppet::Util::Windows::Security.set_security_descriptor(path, sd) catalog.apply expects_system_granted_full_access_explicitly(path) end end describe "when permissions are not insync?" do before :each do @file[:owner] = 'None' @file[:group] = 'None' end it "replaces inherited SYSTEM ACEs with an uninherited one for an existing file" do FileUtils.touch(path) expects_at_least_one_inherited_system_ace_grants_full_access(path) catalog.apply expects_system_granted_full_access_explicitly(path) end it "replaces inherited SYSTEM ACEs for a new file with an uninherited one" do catalog.apply expects_system_granted_full_access_explicitly(path) end end describe "created with SYSTEM as the group" do before :each do @file[:owner] = @sids[:users] @file[:group] = @sids[:system] @file[:mode] = 0644 catalog.apply end it "should allow the user to explicitly set the mode to 4" do system_aces = get_aces_for_path_by_sid(path, @sids[:system]) system_aces.should_not be_empty system_aces.each do |ace| ace.mask.should == Puppet::Util::Windows::File::FILE_GENERIC_READ end end it "prepends SYSTEM ace when changing group from system to power users" do @file[:group] = @sids[:power_users] catalog.apply system_aces = get_aces_for_path_by_sid(path, @sids[:system]) system_aces.size.should == 1 end end describe "with :links set to :follow" do it "should not fail to apply" do # at minimal, we need an owner and/or group @file[:owner] = @sids[:users] @file[:links] = :follow catalog.apply do |transaction| if transaction.any_failed? pretty_transaction_error(transaction) end end end end end describe "on directories" do before :each do @directory = described_class.new( :path => dir, :ensure => :directory ) catalog.add_resource @directory end def grant_everyone_full_access(path) sd = Puppet::Util::Windows::Security.get_security_descriptor(path) sd.dacl.allow( 'S-1-1-0', #everyone Puppet::Util::Windows::File::FILE_ALL_ACCESS, Puppet::Util::Windows::AccessControlEntry::OBJECT_INHERIT_ACE | Puppet::Util::Windows::AccessControlEntry::CONTAINER_INHERIT_ACE) Puppet::Util::Windows::Security.set_security_descriptor(path, sd) end after :each do grant_everyone_full_access(dir) end describe "when source permissions are ignored" do before :each do @directory[:source_permissions] = :ignore end it "preserves the inherited SYSTEM ACE" do catalog.apply expects_at_least_one_inherited_system_ace_grants_full_access(dir) end end describe "when permissions are insync?" do it "preserves the explicit SYSTEM ACE" do Dir.mkdir(dir) source_dir = tmpdir('source_dir') @directory[:source] = source_dir sd = Puppet::Util::Windows::Security.get_security_descriptor(source_dir) sd.protect = true sd.owner = @sids[:none] sd.group = @sids[:none] Puppet::Util::Windows::Security.set_security_descriptor(source_dir, sd) Puppet::Util::Windows::Security.set_security_descriptor(dir, sd) catalog.apply expects_system_granted_full_access_explicitly(dir) end end describe "when permissions are not insync?" do before :each do @directory[:owner] = 'None' @directory[:group] = 'None' @directory[:mode] = 0444 end it "replaces inherited SYSTEM ACEs with an uninherited one for an existing directory" do FileUtils.mkdir(dir) expects_at_least_one_inherited_system_ace_grants_full_access(dir) catalog.apply expects_system_granted_full_access_explicitly(dir) end it "replaces inherited SYSTEM ACEs with an uninherited one for an existing directory" do catalog.apply expects_system_granted_full_access_explicitly(dir) end describe "created with SYSTEM as the group" do before :each do @directory[:owner] = @sids[:users] @directory[:group] = @sids[:system] @directory[:mode] = 0644 catalog.apply end it "should allow the user to explicitly set the mode to 4" do system_aces = get_aces_for_path_by_sid(dir, @sids[:system]) system_aces.should_not be_empty system_aces.each do |ace| # unlike files, Puppet sets execute bit on directories that are readable ace.mask.should == Puppet::Util::Windows::File::FILE_GENERIC_READ | Puppet::Util::Windows::File::FILE_GENERIC_EXECUTE end end it "prepends SYSTEM ace when changing group from system to power users" do @directory[:group] = @sids[:power_users] catalog.apply system_aces = get_aces_for_path_by_sid(dir, @sids[:system]) system_aces.size.should == 1 end end describe "with :links set to :follow" do it "should not fail to apply" do # at minimal, we need an owner and/or group @directory[:owner] = @sids[:users] @directory[:links] = :follow catalog.apply do |transaction| if transaction.any_failed? pretty_transaction_error(transaction) end end end end end end end end end describe "when purging files" do before do sourcedir = tmpdir("purge_source") destdir = tmpdir("purge_dest") sourcefile = File.join(sourcedir, "sourcefile") @copiedfile = File.join(destdir, "sourcefile") @localfile = File.join(destdir, "localfile") @purgee = File.join(destdir, "to_be_purged") File.open(@localfile, "w") { |f| f.print "oldtest" } File.open(sourcefile, "w") { |f| f.print "funtest" } # this file should get removed File.open(@purgee, "w") { |f| f.print "footest" } lfobj = Puppet::Type.newfile( :title => "localfile", :path => @localfile, :content => "rahtest", :ensure => :file, :backup => false ) destobj = Puppet::Type.newfile( :title => "destdir", :path => destdir, :source => sourcedir, :backup => false, :purge => true, :recurse => true ) catalog.add_resource lfobj, destobj catalog.apply end it "should still copy remote files" do File.read(@copiedfile).should == 'funtest' end it "should not purge managed, local files" do File.read(@localfile).should == 'rahtest' end it "should purge files that are neither remote nor otherwise managed" do Puppet::FileSystem.exist?(@purgee).should be_false end end describe "when using validate_cmd" do it "should fail the file resource if command fails" do catalog.add_resource(described_class.new(:path => path, :content => "foo", :validate_cmd => "/usr/bin/env false")) Puppet::Util::Execution.expects(:execute).with("/usr/bin/env false", {:combine => true, :failonfail => true}).raises(Puppet::ExecutionFailure, "Failed") report = catalog.apply.report report.resource_statuses["File[#{path}]"].should be_failed Puppet::FileSystem.exist?(path).should be_false end it "should succeed the file resource if command succeeds" do catalog.add_resource(described_class.new(:path => path, :content => "foo", :validate_cmd => "/usr/bin/env true")) Puppet::Util::Execution.expects(:execute).with("/usr/bin/env true", {:combine => true, :failonfail => true}).returns '' report = catalog.apply.report report.resource_statuses["File[#{path}]"].should_not be_failed Puppet::FileSystem.exist?(path).should be_true end end def tmpfile_with_contents(name, contents) file = tmpfile(name) File.open(file, "w") { |f| f.write contents } file end def file_in_dir_with_contents(dir, name, contents) full_name = File.join(dir, name) File.open(full_name, "w") { |f| f.write contents } full_name end def pretty_transaction_error(transaction) report = transaction.report status_failures = report.resource_statuses.values.select { |r| r.failed? } status_fail_msg = status_failures. collect(&:events). flatten. select { |event| event.status == 'failure' }. collect { |event| "#{event.resource}: #{event.message}" }.join("; ") raise "Got #{status_failures.length} failure(s) while applying: #{status_fail_msg}" end end diff --git a/spec/unit/pops/parser/parse_resource_spec.rb b/spec/unit/pops/parser/parse_resource_spec.rb index 8e805b27a..cf7e7cb01 100644 --- a/spec/unit/pops/parser/parse_resource_spec.rb +++ b/spec/unit/pops/parser/parse_resource_spec.rb @@ -1,324 +1,324 @@ #! /usr/bin/env ruby require 'spec_helper' require 'puppet/pops' # relative to this spec file (./) does not work as this file is loaded by rspec require File.join(File.dirname(__FILE__), '/parser_rspec_helper') describe "egrammar parsing resource declarations" do include ParserRspecHelper context "When parsing regular resource" do ["File", "file"].each do |word| it "#{word} { 'title': }" do dump(parse("#{word} { 'title': }")).should == [ "(resource file", " ('title'))" ].join("\n") end - it "#{word} { 'title': path => '/somewhere', mode => 0777}" do - dump(parse("#{word} { 'title': path => '/somewhere', mode => 0777}")).should == [ + it "#{word} { 'title': path => '/somewhere', mode => '0777'}" do + dump(parse("#{word} { 'title': path => '/somewhere', mode => '0777'}")).should == [ "(resource file", " ('title'", " (path => '/somewhere')", - " (mode => 0777)))" + " (mode => '0777')))" ].join("\n") end it "#{word} { 'title': path => '/somewhere', }" do dump(parse("#{word} { 'title': path => '/somewhere', }")).should == [ "(resource file", " ('title'", " (path => '/somewhere')))" ].join("\n") end it "#{word} { 'title': , }" do dump(parse("#{word} { 'title': , }")).should == [ "(resource file", " ('title'))" ].join("\n") end it "#{word} { 'title': ; }" do dump(parse("#{word} { 'title': ; }")).should == [ "(resource file", " ('title'))" ].join("\n") end it "#{word} { 'title': ; 'other_title': }" do dump(parse("#{word} { 'title': ; 'other_title': }")).should == [ "(resource file", " ('title')", " ('other_title'))" ].join("\n") end # PUP-2898, trailing ';' it "#{word} { 'title': ; 'other_title': ; }" do dump(parse("#{word} { 'title': ; 'other_title': ; }")).should == [ "(resource file", " ('title')", " ('other_title'))" ].join("\n") end it "#{word} { 'title1': path => 'x'; 'title2': path => 'y'}" do dump(parse("#{word} { 'title1': path => 'x'; 'title2': path => 'y'}")).should == [ "(resource file", " ('title1'", " (path => 'x'))", " ('title2'", " (path => 'y')))", ].join("\n") end - it "#{word} { title: * => {mode => 0777} }" do - dump(parse("#{word} { title: * => {mode => 0777}}")).should == [ + it "#{word} { title: * => {mode => '0777'} }" do + dump(parse("#{word} { title: * => {mode => '0777'}}")).should == [ "(resource file", " (title", - " (* => ({} (mode 0777)))))" + " (* => ({} (mode '0777')))))" ].join("\n") end end end context "When parsing (type based) resource defaults" do it "File { }" do dump(parse("File { }")).should == "(resource-defaults file)" end - it "File { mode => 0777 }" do - dump(parse("File { mode => 0777}")).should == [ + it "File { mode => '0777' }" do + dump(parse("File { mode => '0777'}")).should == [ "(resource-defaults file", - " (mode => 0777))" + " (mode => '0777'))" ].join("\n") end - it "File { * => {mode => 0777} } (even if validated to be illegal)" do - dump(parse("File { * => {mode => 0777}}")).should == [ + it "File { * => {mode => '0777'} } (even if validated to be illegal)" do + dump(parse("File { * => {mode => '0777'}}")).should == [ "(resource-defaults file", - " (* => ({} (mode 0777))))" + " (* => ({} (mode '0777'))))" ].join("\n") end end context "When parsing resource override" do it "File['x'] { }" do dump(parse("File['x'] { }")).should == "(override (slice file 'x'))" end it "File['x'] { x => 1 }" do dump(parse("File['x'] { x => 1}")).should == [ "(override (slice file 'x')", " (x => 1))" ].join("\n") end it "File['x', 'y'] { x => 1 }" do dump(parse("File['x', 'y'] { x => 1}")).should == [ "(override (slice file ('x' 'y'))", " (x => 1))" ].join("\n") end it "File['x'] { x => 1, y => 2 }" do dump(parse("File['x'] { x => 1, y=> 2}")).should == [ "(override (slice file 'x')", " (x => 1)", " (y => 2))" ].join("\n") end it "File['x'] { x +> 1 }" do dump(parse("File['x'] { x +> 1}")).should == [ "(override (slice file 'x')", " (x +> 1))" ].join("\n") end - it "File['x'] { * => {mode => 0777} } (even if validated to be illegal)" do - dump(parse("File['x'] { * => {mode => 0777}}")).should == [ + it "File['x'] { * => {mode => '0777'} } (even if validated to be illegal)" do + dump(parse("File['x'] { * => {mode => '0777'}}")).should == [ "(override (slice file 'x')", - " (* => ({} (mode 0777))))" + " (* => ({} (mode '0777'))))" ].join("\n") end end context "When parsing virtual and exported resources" do it "parses exported @@file { 'title': }" do dump(parse("@@file { 'title': }")).should == "(exported-resource file\n ('title'))" end it "parses virtual @file { 'title': }" do dump(parse("@file { 'title': }")).should == "(virtual-resource file\n ('title'))" end it "nothing before the title colon is a syntax error" do expect do - parse("@file {: mode => 0777 }") + parse("@file {: mode => '0777' }") end.to raise_error(/Syntax error/) end it "raises error for user error; not a resource" do # The expression results in VIRTUAL, CALL FUNCTION('file', HASH) since the resource body has # no title. expect do - parse("@file { mode => 0777 }") + parse("@file { mode => '0777' }") end.to raise_error(/Virtual \(@\) can only be applied to a Resource Expression/) end it "parses global defaults with @ (even if validated to be illegal)" do - dump(parse("@File { mode => 0777 }")).should == [ + dump(parse("@File { mode => '0777' }")).should == [ "(virtual-resource-defaults file", - " (mode => 0777))" + " (mode => '0777'))" ].join("\n") end it "parses global defaults with @@ (even if validated to be illegal)" do - dump(parse("@@File { mode => 0777 }")).should == [ + dump(parse("@@File { mode => '0777' }")).should == [ "(exported-resource-defaults file", - " (mode => 0777))" + " (mode => '0777'))" ].join("\n") end it "parses override with @ (even if validated to be illegal)" do - dump(parse("@File[foo] { mode => 0777 }")).should == [ + dump(parse("@File[foo] { mode => '0777' }")).should == [ "(virtual-override (slice file foo)", - " (mode => 0777))" + " (mode => '0777'))" ].join("\n") end it "parses override combined with @@ (even if validated to be illegal)" do - dump(parse("@@File[foo] { mode => 0777 }")).should == [ + dump(parse("@@File[foo] { mode => '0777' }")).should == [ "(exported-override (slice file foo)", - " (mode => 0777))" + " (mode => '0777'))" ].join("\n") end end context "When parsing class resource" do it "class { 'cname': }" do dump(parse("class { 'cname': }")).should == [ "(resource class", " ('cname'))" ].join("\n") end it "@class { 'cname': }" do dump(parse("@class { 'cname': }")).should == [ "(virtual-resource class", " ('cname'))" ].join("\n") end it "@@class { 'cname': }" do dump(parse("@@class { 'cname': }")).should == [ "(exported-resource class", " ('cname'))" ].join("\n") end it "class { 'cname': x => 1, y => 2}" do dump(parse("class { 'cname': x => 1, y => 2}")).should == [ "(resource class", " ('cname'", " (x => 1)", " (y => 2)))" ].join("\n") end it "class { 'cname1': x => 1; 'cname2': y => 2}" do dump(parse("class { 'cname1': x => 1; 'cname2': y => 2}")).should == [ "(resource class", " ('cname1'", " (x => 1))", " ('cname2'", " (y => 2)))", ].join("\n") end end context "reported issues in 3.x" do it "should not screw up on brackets in title of resource #19632" do dump(parse('notify { "thisisa[bug]": }')).should == [ "(resource notify", " ('thisisa[bug]'))", ].join("\n") end end context "When parsing Relationships" do it "File[a] -> File[b]" do dump(parse("File[a] -> File[b]")).should == "(-> (slice file a) (slice file b))" end it "File[a] <- File[b]" do dump(parse("File[a] <- File[b]")).should == "(<- (slice file a) (slice file b))" end it "File[a] ~> File[b]" do dump(parse("File[a] ~> File[b]")).should == "(~> (slice file a) (slice file b))" end it "File[a] <~ File[b]" do dump(parse("File[a] <~ File[b]")).should == "(<~ (slice file a) (slice file b))" end it "Should chain relationships" do dump(parse("a -> b -> c")).should == "(-> (-> a b) c)" end it "Should chain relationships" do dump(parse("File[a] -> File[b] ~> File[c] <- File[d] <~ File[e]")).should == "(<~ (<- (~> (-> (slice file a) (slice file b)) (slice file c)) (slice file d)) (slice file e))" end it "should create relationships between collects" do dump(parse("File <| mode == 0644 |> -> File <| mode == 0755 |>")).should == "(-> (collect file\n (<| |> (== mode 0644))) (collect file\n (<| |> (== mode 0755))))" end end context "When parsing collection" do context "of virtual resources" do it "File <| |>" do dump(parse("File <| |>")).should == "(collect file\n (<| |>))" end end context "of exported resources" do it "File <<| |>>" do dump(parse("File <<| |>>")).should == "(collect file\n (<<| |>>))" end end context "queries are parsed with correct precedence" do it "File <| tag == 'foo' |>" do dump(parse("File <| tag == 'foo' |>")).should == "(collect file\n (<| |> (== tag 'foo')))" end - it "File <| tag == 'foo' and mode != 0777 |>" do - dump(parse("File <| tag == 'foo' and mode != 0777 |>")).should == "(collect file\n (<| |> (&& (== tag 'foo') (!= mode 0777))))" + it "File <| tag == 'foo' and mode != '0777' |>" do + dump(parse("File <| tag == 'foo' and mode != '0777' |>")).should == "(collect file\n (<| |> (&& (== tag 'foo') (!= mode '0777'))))" end - it "File <| tag == 'foo' or mode != 0777 |>" do - dump(parse("File <| tag == 'foo' or mode != 0777 |>")).should == "(collect file\n (<| |> (|| (== tag 'foo') (!= mode 0777))))" + it "File <| tag == 'foo' or mode != '0777' |>" do + dump(parse("File <| tag == 'foo' or mode != '0777' |>")).should == "(collect file\n (<| |> (|| (== tag 'foo') (!= mode '0777'))))" end - it "File <| tag == 'foo' or tag == 'bar' and mode != 0777 |>" do - dump(parse("File <| tag == 'foo' or tag == 'bar' and mode != 0777 |>")).should == - "(collect file\n (<| |> (|| (== tag 'foo') (&& (== tag 'bar') (!= mode 0777)))))" + it "File <| tag == 'foo' or tag == 'bar' and mode != '0777' |>" do + dump(parse("File <| tag == 'foo' or tag == 'bar' and mode != '0777' |>")).should == + "(collect file\n (<| |> (|| (== tag 'foo') (&& (== tag 'bar') (!= mode '0777')))))" end - it "File <| (tag == 'foo' or tag == 'bar') and mode != 0777 |>" do - dump(parse("File <| (tag == 'foo' or tag == 'bar') and mode != 0777 |>")).should == - "(collect file\n (<| |> (&& (|| (== tag 'foo') (== tag 'bar')) (!= mode 0777))))" + it "File <| (tag == 'foo' or tag == 'bar') and mode != '0777' |>" do + dump(parse("File <| (tag == 'foo' or tag == 'bar') and mode != '0777' |>")).should == + "(collect file\n (<| |> (&& (|| (== tag 'foo') (== tag 'bar')) (!= mode '0777'))))" end end end end diff --git a/spec/unit/settings_spec.rb b/spec/unit/settings_spec.rb index ad0ffae44..0a1cee2bd 100755 --- a/spec/unit/settings_spec.rb +++ b/spec/unit/settings_spec.rb @@ -1,1883 +1,1883 @@ #! /usr/bin/env ruby require 'spec_helper' require 'ostruct' require 'puppet/settings/errors' describe Puppet::Settings do include PuppetSpec::Files let(:main_config_file_default_location) do File.join(Puppet::Util::RunMode[:master].conf_dir, "puppet.conf") end let(:user_config_file_default_location) do File.join(Puppet::Util::RunMode[:user].conf_dir, "puppet.conf") end describe "when specifying defaults" do before do @settings = Puppet::Settings.new end it "should start with no defined parameters" do @settings.params.length.should == 0 end it "should not allow specification of default values associated with a section as an array" do expect { @settings.define_settings(:section, :myvalue => ["defaultval", "my description"]) }.to raise_error end it "should not allow duplicate parameter specifications" do @settings.define_settings(:section, :myvalue => { :default => "a", :desc => "b" }) lambda { @settings.define_settings(:section, :myvalue => { :default => "c", :desc => "d" }) }.should raise_error(ArgumentError) end it "should allow specification of default values associated with a section as a hash" do @settings.define_settings(:section, :myvalue => {:default => "defaultval", :desc => "my description"}) end it "should consider defined parameters to be valid" do @settings.define_settings(:section, :myvalue => { :default => "defaultval", :desc => "my description" }) @settings.valid?(:myvalue).should be_true end it "should require a description when defaults are specified with a hash" do lambda { @settings.define_settings(:section, :myvalue => {:default => "a value"}) }.should raise_error(ArgumentError) end it "should support specifying owner, group, and mode when specifying files" do @settings.define_settings(:section, :myvalue => {:type => :file, :default => "/some/file", :owner => "service", :mode => "boo", :group => "service", :desc => "whatever"}) end it "should support specifying a short name" do @settings.define_settings(:section, :myvalue => {:default => "w", :desc => "b", :short => "m"}) end it "should support specifying the setting type" do @settings.define_settings(:section, :myvalue => {:default => "/w", :desc => "b", :type => :string}) @settings.setting(:myvalue).should be_instance_of(Puppet::Settings::StringSetting) end it "should fail if an invalid setting type is specified" do lambda { @settings.define_settings(:section, :myvalue => {:default => "w", :desc => "b", :type => :foo}) }.should raise_error(ArgumentError) end it "should fail when short names conflict" do @settings.define_settings(:section, :myvalue => {:default => "w", :desc => "b", :short => "m"}) lambda { @settings.define_settings(:section, :myvalue => {:default => "w", :desc => "b", :short => "m"}) }.should raise_error(ArgumentError) end end describe "when initializing application defaults do" do let(:default_values) do values = {} PuppetSpec::Settings::TEST_APP_DEFAULT_DEFINITIONS.keys.each do |key| values[key] = 'default value' end values end before do @settings = Puppet::Settings.new @settings.define_settings(:main, PuppetSpec::Settings::TEST_APP_DEFAULT_DEFINITIONS) end it "should fail if the app defaults hash is missing any required values" do incomplete_default_values = default_values.reject { |key, _| key == :confdir } expect { @settings.initialize_app_defaults(default_values.reject { |key, _| key == :confdir }) }.to raise_error(Puppet::Settings::SettingsError) end # ultimately I'd like to stop treating "run_mode" as a normal setting, because it has so many special # case behaviors / uses. However, until that time... we need to make sure that our private run_mode= # setter method gets properly called during app initialization. it "sets the preferred run mode when initializing the app defaults" do @settings.initialize_app_defaults(default_values.merge(:run_mode => :master)) @settings.preferred_run_mode.should == :master end end describe "#call_hooks_deferred_to_application_initialization" do let(:good_default) { "yay" } let(:bad_default) { "$doesntexist" } before(:each) do @settings = Puppet::Settings.new end describe "when ignoring dependency interpolation errors" do let(:options) { {:ignore_interpolation_dependency_errors => true} } describe "if interpolation error" do it "should not raise an error" do hook_values = [] @settings.define_settings(:section, :badhook => {:default => bad_default, :desc => "boo", :call_hook => :on_initialize_and_write, :hook => lambda { |v| hook_values << v }}) expect do @settings.send(:call_hooks_deferred_to_application_initialization, options) end.to_not raise_error end end describe "if no interpolation error" do it "should not raise an error" do hook_values = [] @settings.define_settings(:section, :goodhook => {:default => good_default, :desc => "boo", :call_hook => :on_initialize_and_write, :hook => lambda { |v| hook_values << v }}) expect do @settings.send(:call_hooks_deferred_to_application_initialization, options) end.to_not raise_error end end end describe "when not ignoring dependency interpolation errors" do [ {}, {:ignore_interpolation_dependency_errors => false}].each do |options| describe "if interpolation error" do it "should raise an error" do hook_values = [] @settings.define_settings( :section, :badhook => { :default => bad_default, :desc => "boo", :call_hook => :on_initialize_and_write, :hook => lambda { |v| hook_values << v } } ) expect do @settings.send(:call_hooks_deferred_to_application_initialization, options) end.to raise_error(Puppet::Settings::InterpolationError) end it "should contain the setting name in error message" do hook_values = [] @settings.define_settings( :section, :badhook => { :default => bad_default, :desc => "boo", :call_hook => :on_initialize_and_write, :hook => lambda { |v| hook_values << v } } ) expect do @settings.send(:call_hooks_deferred_to_application_initialization, options) end.to raise_error(Puppet::Settings::InterpolationError, /badhook/) end end describe "if no interpolation error" do it "should not raise an error" do hook_values = [] @settings.define_settings( :section, :goodhook => { :default => good_default, :desc => "boo", :call_hook => :on_initialize_and_write, :hook => lambda { |v| hook_values << v } } ) expect do @settings.send(:call_hooks_deferred_to_application_initialization, options) end.to_not raise_error end end end end end describe "when setting values" do before do @settings = Puppet::Settings.new @settings.define_settings :main, :myval => { :default => "val", :desc => "desc" } @settings.define_settings :main, :bool => { :type => :boolean, :default => true, :desc => "desc" } end it "should provide a method for setting values from other objects" do @settings[:myval] = "something else" @settings[:myval].should == "something else" end it "should support a getopt-specific mechanism for setting values" do @settings.handlearg("--myval", "newval") @settings[:myval].should == "newval" end it "should support a getopt-specific mechanism for turning booleans off" do @settings.override_default(:bool, true) @settings.handlearg("--no-bool", "") @settings[:bool].should == false end it "should support a getopt-specific mechanism for turning booleans on" do # Turn it off first @settings.override_default(:bool, false) @settings.handlearg("--bool", "") @settings[:bool].should == true end it "should consider a cli setting with no argument to be a boolean" do # Turn it off first @settings.override_default(:bool, false) @settings.handlearg("--bool") @settings[:bool].should == true end it "should consider a cli setting with an empty string as an argument to be an empty argument, if the setting itself is not a boolean" do @settings.override_default(:myval, "bob") @settings.handlearg("--myval", "") @settings[:myval].should == "" end it "should consider a cli setting with a boolean as an argument to be a boolean" do # Turn it off first @settings.override_default(:bool, false) @settings.handlearg("--bool", "true") @settings[:bool].should == true end it "should not consider a cli setting of a non boolean with a boolean as an argument to be a boolean" do @settings.override_default(:myval, "bob") @settings.handlearg("--no-myval", "") @settings[:myval].should == "" end it "should flag string settings from the CLI" do @settings.handlearg("--myval", "12") @settings.set_by_cli?(:myval).should be_true end it "should flag bool settings from the CLI" do @settings.handlearg("--bool") @settings.set_by_cli?(:bool).should be_true end it "should not flag settings memory as from CLI" do @settings[:myval] = "12" @settings.set_by_cli?(:myval).should be_false end describe "setbycli" do it "should generate a deprecation warning" do Puppet.expects(:deprecation_warning).at_least(1) @settings.setting(:myval).setbycli = true end it "should set the value" do @settings[:myval] = "blah" @settings.setting(:myval).setbycli = true @settings.set_by_cli?(:myval).should be_true end it "should raise error if trying to unset value" do @settings.handlearg("--myval", "blah") expect do @settings.setting(:myval).setbycli = nil end.to raise_error(ArgumentError, /unset/) end end it "should clear the cache when setting getopt-specific values" do @settings.define_settings :mysection, :one => { :default => "whah", :desc => "yay" }, :two => { :default => "$one yay", :desc => "bah" } @settings.expects(:unsafe_flush_cache) @settings[:two].should == "whah yay" @settings.handlearg("--one", "else") @settings[:two].should == "else yay" end it "should clear the cache when the preferred_run_mode is changed" do @settings.expects(:flush_cache) @settings.preferred_run_mode = :master end it "should not clear other values when setting getopt-specific values" do @settings[:myval] = "yay" @settings.handlearg("--no-bool", "") @settings[:myval].should == "yay" end it "should clear the list of used sections" do @settings.expects(:clearused) @settings[:myval] = "yay" end describe "call_hook" do Puppet::Settings::StringSetting.available_call_hook_values.each do |val| describe "when :#{val}" do describe "and definition invalid" do it "should raise error if no hook defined" do expect do @settings.define_settings(:section, :hooker => {:default => "yay", :desc => "boo", :call_hook => val}) end.to raise_error(ArgumentError, /no :hook/) end it "should include the setting name in the error message" do expect do @settings.define_settings(:section, :hooker => {:default => "yay", :desc => "boo", :call_hook => val}) end.to raise_error(ArgumentError, /for :hooker/) end end describe "and definition valid" do before(:each) do hook_values = [] @settings.define_settings(:section, :hooker => {:default => "yay", :desc => "boo", :call_hook => val, :hook => lambda { |v| hook_values << v }}) end it "should call the hook when value written" do @settings.setting(:hooker).expects(:handle).with("something").once @settings[:hooker] = "something" end end end end it "should have a default value of :on_write_only" do @settings.define_settings(:section, :hooker => {:default => "yay", :desc => "boo", :hook => lambda { |v| hook_values << v }}) @settings.setting(:hooker).call_hook.should == :on_write_only end describe "when nil" do it "should generate a warning" do Puppet.expects(:warning) @settings.define_settings(:section, :hooker => {:default => "yay", :desc => "boo", :call_hook => nil, :hook => lambda { |v| hook_values << v }}) end it "should use default" do @settings.define_settings(:section, :hooker => {:default => "yay", :desc => "boo", :call_hook => nil, :hook => lambda { |v| hook_values << v }}) @settings.setting(:hooker).call_hook.should == :on_write_only end end describe "when invalid" do it "should raise an error" do expect do @settings.define_settings(:section, :hooker => {:default => "yay", :desc => "boo", :call_hook => :foo, :hook => lambda { |v| hook_values << v }}) end.to raise_error(ArgumentError, /invalid.*call_hook/i) end end describe "when :on_define_and_write" do it "should call the hook at definition" do hook_values = [] @settings.define_settings(:section, :hooker => {:default => "yay", :desc => "boo", :call_hook => :on_define_and_write, :hook => lambda { |v| hook_values << v }}) @settings.setting(:hooker).call_hook.should == :on_define_and_write hook_values.should == %w{yay} end end describe "when :on_initialize_and_write" do before(:each) do @hook_values = [] @settings.define_settings(:section, :hooker => {:default => "yay", :desc => "boo", :call_hook => :on_initialize_and_write, :hook => lambda { |v| @hook_values << v }}) end it "should not call the hook at definition" do @hook_values.should == [] @hook_values.should_not == %w{yay} end it "should call the hook at initialization" do app_defaults = {} Puppet::Settings::REQUIRED_APP_SETTINGS.each do |key| app_defaults[key] = "foo" end app_defaults[:run_mode] = :user @settings.define_settings(:main, PuppetSpec::Settings::TEST_APP_DEFAULT_DEFINITIONS) @settings.setting(:hooker).expects(:handle).with("yay").once @settings.initialize_app_defaults app_defaults end end end describe "call_on_define" do [true, false].each do |val| describe "to #{val}" do it "should generate a deprecation warning" do Puppet.expects(:deprecation_warning) values = [] @settings.define_settings(:section, :hooker => {:default => "yay", :desc => "boo", :call_on_define => val, :hook => lambda { |v| values << v }}) end it "should should set call_hook" do values = [] name = "hooker_#{val}".to_sym @settings.define_settings(:section, name => {:default => "yay", :desc => "boo", :call_on_define => val, :hook => lambda { |v| values << v }}) @settings.setting(name).call_hook.should == :on_define_and_write if val @settings.setting(name).call_hook.should == :on_write_only unless val end end end end it "should call passed blocks when values are set" do values = [] @settings.define_settings(:section, :hooker => {:default => "yay", :desc => "boo", :hook => lambda { |v| values << v }}) values.should == [] @settings[:hooker] = "something" values.should == %w{something} end it "should call passed blocks when values are set via the command line" do values = [] @settings.define_settings(:section, :hooker => {:default => "yay", :desc => "boo", :hook => lambda { |v| values << v }}) values.should == [] @settings.handlearg("--hooker", "yay") values.should == %w{yay} end it "should provide an option to call passed blocks during definition" do values = [] @settings.define_settings(:section, :hooker => {:default => "yay", :desc => "boo", :call_hook => :on_define_and_write, :hook => lambda { |v| values << v }}) values.should == %w{yay} end it "should pass the fully interpolated value to the hook when called on definition" do values = [] @settings.define_settings(:section, :one => { :default => "test", :desc => "a" }) @settings.define_settings(:section, :hooker => {:default => "$one/yay", :desc => "boo", :call_hook => :on_define_and_write, :hook => lambda { |v| values << v }}) values.should == %w{test/yay} end it "should munge values using the setting-specific methods" do @settings[:bool] = "false" @settings[:bool].should == false end it "should prefer values set in ruby to values set on the cli" do @settings[:myval] = "memarg" @settings.handlearg("--myval", "cliarg") @settings[:myval].should == "memarg" end it "should clear the list of environments" do Puppet::Node::Environment.expects(:clear).at_least(1) @settings[:myval] = "memarg" end it "should raise an error if we try to set a setting that hasn't been defined'" do lambda{ @settings[:why_so_serious] = "foo" }.should raise_error(ArgumentError, /unknown setting/) end it "allows overriding cli args based on the cli-set value" do @settings.handlearg("--myval", "cliarg") @settings.set_value(:myval, "modified #{@settings[:myval]}", :cli) expect(@settings[:myval]).to eq("modified cliarg") end end describe "when returning values" do before do @settings = Puppet::Settings.new @settings.define_settings :section, :config => { :type => :file, :default => "/my/file", :desc => "eh" }, :one => { :default => "ONE", :desc => "a" }, :two => { :default => "$one TWO", :desc => "b"}, :three => { :default => "$one $two THREE", :desc => "c"}, :four => { :default => "$two $three FOUR", :desc => "d"}, :five => { :default => nil, :desc => "e" } Puppet::FileSystem.stubs(:exist?).returns true end describe "call_on_define" do it "should generate a deprecation warning" do Puppet.expects(:deprecation_warning) @settings.define_settings(:section, :hooker => {:default => "yay", :desc => "boo", :hook => lambda { |v| hook_values << v }}) @settings.setting(:hooker).call_on_define end Puppet::Settings::StringSetting.available_call_hook_values.each do |val| it "should match value for call_hook => :#{val}" do hook_values = [] @settings.define_settings(:section, :hooker => {:default => "yay", :desc => "boo", :call_hook => val, :hook => lambda { |v| hook_values << v }}) @settings.setting(:hooker).call_on_define.should == @settings.setting(:hooker).call_hook_on_define? end end end it "should provide a mechanism for returning set values" do @settings[:one] = "other" @settings[:one].should == "other" end it "setting a value to nil causes it to return to its default" do default_values = { :one => "skipped value" } [:logdir, :confdir, :vardir].each do |key| default_values[key] = 'default value' end @settings.define_settings :main, PuppetSpec::Settings::TEST_APP_DEFAULT_DEFINITIONS @settings.initialize_app_defaults(default_values) @settings[:one] = "value will disappear" @settings[:one] = nil @settings[:one].should == "ONE" end it "should interpolate default values for other parameters into returned parameter values" do @settings[:one].should == "ONE" @settings[:two].should == "ONE TWO" @settings[:three].should == "ONE ONE TWO THREE" end it "should interpolate default values that themselves need to be interpolated" do @settings[:four].should == "ONE TWO ONE ONE TWO THREE FOUR" end it "should provide a method for returning uninterpolated values" do @settings[:two] = "$one tw0" @settings.uninterpolated_value(:two).should == "$one tw0" @settings.uninterpolated_value(:four).should == "$two $three FOUR" end it "should interpolate set values for other parameters into returned parameter values" do @settings[:one] = "on3" @settings[:two] = "$one tw0" @settings[:three] = "$one $two thr33" @settings[:four] = "$one $two $three f0ur" @settings[:one].should == "on3" @settings[:two].should == "on3 tw0" @settings[:three].should == "on3 on3 tw0 thr33" @settings[:four].should == "on3 on3 tw0 on3 on3 tw0 thr33 f0ur" end it "should not cache interpolated values such that stale information is returned" do @settings[:two].should == "ONE TWO" @settings[:one] = "one" @settings[:two].should == "one TWO" end it "should not cache values such that information from one environment is returned for another environment" do text = "[env1]\none = oneval\n[env2]\none = twoval\n" @settings.stubs(:read_file).returns(text) @settings.send(:parse_config_files) @settings.value(:one, "env1").should == "oneval" @settings.value(:one, "env2").should == "twoval" end it "should have a run_mode that defaults to user" do @settings.preferred_run_mode.should == :user end it "interpolates a boolean false without raising an error" do @settings.define_settings(:section, :trip_wire => { :type => :boolean, :default => false, :desc => "a trip wire" }, :tripping => { :default => '$trip_wire', :desc => "once tripped if interpolated was false" }) @settings[:tripping].should == "false" end describe "setbycli" do it "should generate a deprecation warning" do @settings.handlearg("--one", "blah") Puppet.expects(:deprecation_warning) @settings.setting(:one).setbycli end it "should be true" do @settings.handlearg("--one", "blah") @settings.setting(:one).setbycli.should be_true end end end describe "when choosing which value to return" do before do @settings = Puppet::Settings.new @settings.define_settings :section, :config => { :type => :file, :default => "/my/file", :desc => "a" }, :one => { :default => "ONE", :desc => "a" }, :two => { :default => "TWO", :desc => "b" } Puppet::FileSystem.stubs(:exist?).returns true @settings.preferred_run_mode = :agent end it "should return default values if no values have been set" do @settings[:one].should == "ONE" end it "should return values set on the cli before values set in the configuration file" do text = "[main]\none = fileval\n" @settings.stubs(:read_file).returns(text) @settings.handlearg("--one", "clival") @settings.send(:parse_config_files) @settings[:one].should == "clival" end it "should return values set in the mode-specific section before values set in the main section" do text = "[main]\none = mainval\n[agent]\none = modeval\n" @settings.stubs(:read_file).returns(text) @settings.send(:parse_config_files) @settings[:one].should == "modeval" end it "should not return values outside of its search path" do text = "[other]\none = oval\n" file = "/some/file" @settings.stubs(:read_file).returns(text) @settings.send(:parse_config_files) @settings[:one].should == "ONE" end it "should return values in a specified environment" do text = "[env]\none = envval\n" @settings.stubs(:read_file).returns(text) @settings.send(:parse_config_files) @settings.value(:one, "env").should == "envval" end it 'should use the current environment for $environment' do @settings.define_settings :main, :myval => { :default => "$environment/foo", :desc => "mydocs" } @settings.value(:myval, "myenv").should == "myenv/foo" end it "should interpolate found values using the current environment" do text = "[main]\none = mainval\n[myname]\none = nameval\ntwo = $one/two\n" @settings.stubs(:read_file).returns(text) @settings.send(:parse_config_files) @settings.value(:two, "myname").should == "nameval/two" end it "should return values in a specified environment before values in the main or name sections" do text = "[env]\none = envval\n[main]\none = mainval\n[myname]\none = nameval\n" @settings.stubs(:read_file).returns(text) @settings.send(:parse_config_files) @settings.value(:one, "env").should == "envval" end context "when interpolating a dynamic environments setting" do let(:dynamic_manifestdir) { "manifestdir=/somewhere/$environment/manifests" } let(:environment) { "environment=anenv" } before(:each) do @settings.define_settings :main, :manifestdir => { :default => "/manifests", :desc => "manifestdir setting" }, :environment => { :default => "production", :desc => "environment setting" } end it "interpolates default environment when no environment specified" do text = <<-EOF [main] #{dynamic_manifestdir} EOF @settings.stubs(:read_file).returns(text) @settings.send(:parse_config_files) expect(@settings.value(:manifestdir)).to eq("/somewhere/production/manifests") end it "interpolates the set environment when no environment specified" do text = <<-EOF [main] #{dynamic_manifestdir} #{environment} EOF @settings.stubs(:read_file).returns(text) @settings.send(:parse_config_files) expect(@settings.value(:manifestdir)).to eq("/somewhere/anenv/manifests") end end end describe "when locating config files" do before do @settings = Puppet::Settings.new end describe "when root" do it "should look for the main config file default location config settings haven't been overridden'" do Puppet.features.stubs(:root?).returns(true) Puppet::FileSystem.expects(:exist?).with(main_config_file_default_location).returns(false) Puppet::FileSystem.expects(:exist?).with(user_config_file_default_location).never @settings.send(:parse_config_files) end end describe "when not root" do it "should look for user config file default location if config settings haven't been overridden'" do Puppet.features.stubs(:root?).returns(false) seq = sequence "load config files" Puppet::FileSystem.expects(:exist?).with(user_config_file_default_location).returns(false).in_sequence(seq) @settings.send(:parse_config_files) end end end describe "when parsing its configuration" do before do @settings = Puppet::Settings.new @settings.stubs(:service_user_available?).returns true @settings.stubs(:service_group_available?).returns true @file = make_absolute("/some/file") @userconfig = make_absolute("/test/userconfigfile") @settings.define_settings :section, :user => { :default => "suser", :desc => "doc" }, :group => { :default => "sgroup", :desc => "doc" } @settings.define_settings :section, :config => { :type => :file, :default => @file, :desc => "eh" }, :one => { :default => "ONE", :desc => "a" }, :two => { :default => "$one TWO", :desc => "b" }, :three => { :default => "$one $two THREE", :desc => "c" } @settings.stubs(:user_config_file).returns(@userconfig) Puppet::FileSystem.stubs(:exist?).with(@file).returns true Puppet::FileSystem.stubs(:exist?).with(@userconfig).returns false end it "should not ignore the report setting" do @settings.define_settings :section, :report => { :default => "false", :desc => "a" } # This is needed in order to make sure we pass on windows myfile = File.expand_path(@file) @settings[:config] = myfile text = <<-CONF [puppetd] report=true CONF Puppet::FileSystem.expects(:exist?).with(myfile).returns(true) @settings.expects(:read_file).returns(text) @settings.send(:parse_config_files) @settings[:report].should be_true end it "should use its current ':config' value for the file to parse" do myfile = make_absolute("/my/file") # do not stub expand_path here, as this leads to a stack overflow, when mocha tries to use it @settings[:config] = myfile Puppet::FileSystem.expects(:exist?).with(myfile).returns(true) Puppet::FileSystem.expects(:read).with(myfile).returns "[main]" @settings.send(:parse_config_files) end it "should not try to parse non-existent files" do Puppet::FileSystem.expects(:exist?).with(@file).returns false File.expects(:read).with(@file).never @settings.send(:parse_config_files) end it "should return values set in the configuration file" do text = "[main] one = fileval " @settings.expects(:read_file).returns(text) @settings.send(:parse_config_files) @settings[:one].should == "fileval" end #484 - this should probably be in the regression area it "should not throw an exception on unknown parameters" do text = "[main]\nnosuchparam = mval\n" @settings.expects(:read_file).returns(text) lambda { @settings.send(:parse_config_files) }.should_not raise_error end it "should convert booleans in the configuration file into Ruby booleans" do text = "[main] one = true two = false " @settings.expects(:read_file).returns(text) @settings.send(:parse_config_files) @settings[:one].should == true @settings[:two].should == false end it "should convert integers in the configuration file into Ruby Integers" do text = "[main] one = 65 " @settings.expects(:read_file).returns(text) @settings.send(:parse_config_files) @settings[:one].should == 65 end it "should support specifying all metadata (owner, group, mode) in the configuration file" do @settings.define_settings :section, :myfile => { :type => :file, :default => make_absolute("/myfile"), :desc => "a" } otherfile = make_absolute("/other/file") @settings.parse_config(<<-CONF) [main] myfile = #{otherfile} {owner = service, group = service, mode = 644} CONF @settings[:myfile].should == otherfile @settings.metadata(:myfile).should == {:owner => "suser", :group => "sgroup", :mode => "644"} end it "should support specifying a single piece of metadata (owner, group, or mode) in the configuration file" do @settings.define_settings :section, :myfile => { :type => :file, :default => make_absolute("/myfile"), :desc => "a" } otherfile = make_absolute("/other/file") @settings.parse_config(<<-CONF) [main] myfile = #{otherfile} {owner = service} CONF @settings[:myfile].should == otherfile @settings.metadata(:myfile).should == {:owner => "suser"} end it "should support loading metadata (owner, group, or mode) from a run_mode section in the configuration file" do default_values = {} PuppetSpec::Settings::TEST_APP_DEFAULT_DEFINITIONS.keys.each do |key| default_values[key] = 'default value' end @settings.define_settings :main, PuppetSpec::Settings::TEST_APP_DEFAULT_DEFINITIONS @settings.define_settings :master, :myfile => { :type => :file, :default => make_absolute("/myfile"), :desc => "a" } otherfile = make_absolute("/other/file") text = "[master] myfile = #{otherfile} {mode = 664} " @settings.expects(:read_file).returns(text) # will start initialization as user @settings.preferred_run_mode.should == :user @settings.send(:parse_config_files) # change app run_mode to master @settings.initialize_app_defaults(default_values.merge(:run_mode => :master)) @settings.preferred_run_mode.should == :master # initializing the app should have reloaded the metadata based on run_mode @settings[:myfile].should == otherfile @settings.metadata(:myfile).should == {:mode => "664"} end it "does not use the metadata from the same setting in a different section" do default_values = {} PuppetSpec::Settings::TEST_APP_DEFAULT_DEFINITIONS.keys.each do |key| default_values[key] = 'default value' end file = make_absolute("/file") default_mode = "0600" @settings.define_settings :main, PuppetSpec::Settings::TEST_APP_DEFAULT_DEFINITIONS @settings.define_settings :master, :myfile => { :type => :file, :default => file, :desc => "a", :mode => default_mode } text = "[master] myfile = #{file}/foo [agent] myfile = #{file} {mode = 664} " @settings.expects(:read_file).returns(text) # will start initialization as user @settings.preferred_run_mode.should == :user @settings.send(:parse_config_files) # change app run_mode to master @settings.initialize_app_defaults(default_values.merge(:run_mode => :master)) @settings.preferred_run_mode.should == :master # initializing the app should have reloaded the metadata based on run_mode @settings[:myfile].should == "#{file}/foo" @settings.metadata(:myfile).should == { :mode => default_mode } end it "should call hooks associated with values set in the configuration file" do values = [] @settings.define_settings :section, :mysetting => {:default => "defval", :desc => "a", :hook => proc { |v| values << v }} text = "[main] mysetting = setval " @settings.expects(:read_file).returns(text) @settings.send(:parse_config_files) values.should == ["setval"] end it "should not call the same hook for values set multiple times in the configuration file" do values = [] @settings.define_settings :section, :mysetting => {:default => "defval", :desc => "a", :hook => proc { |v| values << v }} text = "[user] mysetting = setval [main] mysetting = other " @settings.expects(:read_file).returns(text) @settings.send(:parse_config_files) values.should == ["setval"] end it "should pass the environment-specific value to the hook when one is available" do values = [] @settings.define_settings :section, :mysetting => {:default => "defval", :desc => "a", :hook => proc { |v| values << v }} @settings.define_settings :section, :environment => { :default => "yay", :desc => "a" } @settings.define_settings :section, :environments => { :default => "yay,foo", :desc => "a" } text = "[main] mysetting = setval [yay] mysetting = other " @settings.expects(:read_file).returns(text) @settings.send(:parse_config_files) values.should == ["other"] end it "should pass the interpolated value to the hook when one is available" do values = [] @settings.define_settings :section, :base => {:default => "yay", :desc => "a", :hook => proc { |v| values << v }} @settings.define_settings :section, :mysetting => {:default => "defval", :desc => "a", :hook => proc { |v| values << v }} text = "[main] mysetting = $base/setval " @settings.expects(:read_file).returns(text) @settings.send(:parse_config_files) values.should == ["yay/setval"] end it "should allow hooks invoked at parse time to be deferred" do hook_invoked = false @settings.define_settings :section, :deferred => {:desc => '', :hook => proc { |v| hook_invoked = true }, :call_hook => :on_initialize_and_write, } @settings.define_settings(:main, :logdir => { :type => :directory, :default => nil, :desc => "logdir" }, :confdir => { :type => :directory, :default => nil, :desc => "confdir" }, :vardir => { :type => :directory, :default => nil, :desc => "vardir" }) text = <<-EOD [main] deferred=$confdir/goose EOD @settings.stubs(:read_file).returns(text) @settings.initialize_global_settings hook_invoked.should be_false @settings.initialize_app_defaults(:logdir => '/path/to/logdir', :confdir => '/path/to/confdir', :vardir => '/path/to/vardir') hook_invoked.should be_true @settings[:deferred].should eq(File.expand_path('/path/to/confdir/goose')) end it "does not require the value for a setting without a hook to resolve during global setup" do hook_invoked = false @settings.define_settings :section, :can_cause_problems => {:desc => '' } @settings.define_settings(:main, :logdir => { :type => :directory, :default => nil, :desc => "logdir" }, :confdir => { :type => :directory, :default => nil, :desc => "confdir" }, :vardir => { :type => :directory, :default => nil, :desc => "vardir" }) text = <<-EOD [main] can_cause_problems=$confdir/goose EOD @settings.stubs(:read_file).returns(text) @settings.initialize_global_settings @settings.initialize_app_defaults(:logdir => '/path/to/logdir', :confdir => '/path/to/confdir', :vardir => '/path/to/vardir') @settings[:can_cause_problems].should eq(File.expand_path('/path/to/confdir/goose')) end it "should allow empty values" do @settings.define_settings :section, :myarg => { :default => "myfile", :desc => "a" } text = "[main] myarg = " @settings.stubs(:read_file).returns(text) @settings.send(:parse_config_files) @settings[:myarg].should == "" end describe "deprecations" do let(:settings) { Puppet::Settings.new } let(:app_defaults) { { :logdir => "/dev/null", :confdir => "/dev/null", :vardir => "/dev/null", } } def assert_accessing_setting_is_deprecated(settings, setting) Puppet.expects(:deprecation_warning).with("Accessing '#{setting}' as a setting is deprecated. See http://links.puppetlabs.com/env-settings-deprecations") Puppet.expects(:deprecation_warning).with("Modifying '#{setting}' as a setting is deprecated. See http://links.puppetlabs.com/env-settings-deprecations") settings[setting.intern] = apath = File.expand_path('foo') expect(settings[setting.intern]).to eq(apath) end before(:each) do settings.define_settings(:main, { :logdir => { :default => 'a', :desc => 'a' }, :confdir => { :default => 'b', :desc => 'b' }, :vardir => { :default => 'c', :desc => 'c' }, }) end context "complete" do let(:completely_deprecated_settings) do settings.define_settings(:main, { :manifestdir => { :default => 'foo', :desc => 'a deprecated setting', :deprecated => :completely, } }) settings end it "warns when set in puppet.conf" do Puppet.expects(:deprecation_warning).with(regexp_matches(/manifestdir is deprecated\./), 'setting-manifestdir') completely_deprecated_settings.parse_config(<<-CONF) manifestdir='should warn' CONF completely_deprecated_settings.initialize_app_defaults(app_defaults) end it "warns when set on the commandline" do Puppet.expects(:deprecation_warning).with(regexp_matches(/manifestdir is deprecated\./), 'setting-manifestdir') args = ["--manifestdir", "/some/value"] completely_deprecated_settings.send(:parse_global_options, args) completely_deprecated_settings.initialize_app_defaults(app_defaults) end it "warns when set in code" do assert_accessing_setting_is_deprecated(completely_deprecated_settings, 'manifestdir') end end context "partial" do let(:partially_deprecated_settings) do settings.define_settings(:main, { :modulepath => { :default => 'foo', :desc => 'a partially deprecated setting', :deprecated => :allowed_on_commandline, } }) settings end it "warns for a deprecated setting allowed on the command line set in puppet.conf" do Puppet.expects(:deprecation_warning).with(regexp_matches(/modulepath is deprecated in puppet\.conf/), 'puppet-conf-setting-modulepath') partially_deprecated_settings.parse_config(<<-CONF) modulepath='should warn' CONF partially_deprecated_settings.initialize_app_defaults(app_defaults) end it "does not warn when manifest is set on command line" do Puppet.expects(:deprecation_warning).never args = ["--modulepath", "/some/value"] partially_deprecated_settings.send(:parse_global_options, args) partially_deprecated_settings.initialize_app_defaults(app_defaults) end it "warns when set in code" do assert_accessing_setting_is_deprecated(partially_deprecated_settings, 'modulepath') end end end end describe "when there are multiple config files" do let(:main_config_text) { "[main]\none = main\ntwo = main2" } let(:user_config_text) { "[main]\none = user\n" } let(:seq) { sequence "config_file_sequence" } before :each do @settings = Puppet::Settings.new @settings.define_settings(:section, { :confdir => { :default => nil, :desc => "Conf dir" }, :config => { :default => "$confdir/puppet.conf", :desc => "Config" }, :one => { :default => "ONE", :desc => "a" }, :two => { :default => "TWO", :desc => "b" }, }) end context "running non-root without explicit config file" do before :each do Puppet.features.stubs(:root?).returns(false) Puppet::FileSystem.expects(:exist?). with(user_config_file_default_location). returns(true).in_sequence(seq) @settings.expects(:read_file). with(user_config_file_default_location). returns(user_config_text).in_sequence(seq) end it "should return values from the user config file" do @settings.send(:parse_config_files) @settings[:one].should == "user" end it "should not return values from the main config file" do @settings.send(:parse_config_files) @settings[:two].should == "TWO" end end context "running as root without explicit config file" do before :each do Puppet.features.stubs(:root?).returns(true) Puppet::FileSystem.expects(:exist?). with(main_config_file_default_location). returns(true).in_sequence(seq) @settings.expects(:read_file). with(main_config_file_default_location). returns(main_config_text).in_sequence(seq) end it "should return values from the main config file" do @settings.send(:parse_config_files) @settings[:one].should == "main" end it "should not return values from the user config file" do @settings.send(:parse_config_files) @settings[:two].should == "main2" end end context "running with an explicit config file as a user (e.g. Apache + Passenger)" do before :each do Puppet.features.stubs(:root?).returns(false) @settings[:confdir] = File.dirname(main_config_file_default_location) Puppet::FileSystem.expects(:exist?). with(main_config_file_default_location). returns(true).in_sequence(seq) @settings.expects(:read_file). with(main_config_file_default_location). returns(main_config_text).in_sequence(seq) end it "should return values from the main config file" do @settings.send(:parse_config_files) @settings[:one].should == "main" end it "should not return values from the user config file" do @settings.send(:parse_config_files) @settings[:two].should == "main2" end end end describe "when reparsing its configuration" do before do @file = make_absolute("/test/file") @userconfig = make_absolute("/test/userconfigfile") @settings = Puppet::Settings.new @settings.define_settings :section, :config => { :type => :file, :default => @file, :desc => "a" }, :one => { :default => "ONE", :desc => "a" }, :two => { :default => "$one TWO", :desc => "b" }, :three => { :default => "$one $two THREE", :desc => "c" } Puppet::FileSystem.stubs(:exist?).with(@file).returns true Puppet::FileSystem.stubs(:exist?).with(@userconfig).returns false @settings.stubs(:user_config_file).returns(@userconfig) end it "does not create the WatchedFile instance and should not parse if the file does not exist" do Puppet::FileSystem.expects(:exist?).with(@file).returns false Puppet::Util::WatchedFile.expects(:new).never @settings.expects(:parse_config_files).never @settings.reparse_config_files end context "and watched file exists" do before do @watched_file = Puppet::Util::WatchedFile.new(@file) Puppet::Util::WatchedFile.expects(:new).with(@file).returns @watched_file end it "uses a WatchedFile instance to determine if the file has changed" do @watched_file.expects(:changed?) @settings.reparse_config_files end it "does not reparse if the file has not changed" do @watched_file.expects(:changed?).returns false @settings.expects(:parse_config_files).never @settings.reparse_config_files end it "reparses if the file has changed" do @watched_file.expects(:changed?).returns true @settings.expects(:parse_config_files) @settings.reparse_config_files end it "replaces in-memory values with on-file values" do @watched_file.stubs(:changed?).returns(true) @settings[:one] = "init" # Now replace the value text = "[main]\none = disk-replace\n" @settings.stubs(:read_file).returns(text) @settings.reparse_config_files @settings[:one].should == "disk-replace" end end it "should retain parameters set by cli when configuration files are reparsed" do @settings.handlearg("--one", "clival") text = "[main]\none = on-disk\n" @settings.stubs(:read_file).returns(text) @settings.send(:parse_config_files) @settings[:one].should == "clival" end it "should remove in-memory values that are no longer set in the file" do # Init the value text = "[main]\none = disk-init\n" @settings.expects(:read_file).returns(text) @settings.send(:parse_config_files) @settings[:one].should == "disk-init" # Now replace the value text = "[main]\ntwo = disk-replace\n" @settings.expects(:read_file).returns(text) @settings.send(:parse_config_files) # The originally-overridden value should be replaced with the default @settings[:one].should == "ONE" # and we should now have the new value in memory @settings[:two].should == "disk-replace" end it "should retain in-memory values if the file has a syntax error" do # Init the value text = "[main]\none = initial-value\n" @settings.expects(:read_file).with(@file).returns(text) @settings.send(:parse_config_files) @settings[:one].should == "initial-value" # Now replace the value with something bogus text = "[main]\nkenny = killed-by-what-follows\n1 is 2, blah blah florp\n" @settings.expects(:read_file).with(@file).returns(text) @settings.send(:parse_config_files) # The originally-overridden value should not be replaced with the default @settings[:one].should == "initial-value" # and we should not have the new value in memory @settings[:kenny].should be_nil end end it "should provide a method for creating a catalog of resources from its configuration" do Puppet::Settings.new.should respond_to(:to_catalog) end describe "when creating a catalog" do before do @settings = Puppet::Settings.new @settings.stubs(:service_user_available?).returns true @prefix = Puppet.features.posix? ? "" : "C:" end it "should add all file resources to the catalog if no sections have been specified" do @settings.define_settings :main, :maindir => { :type => :directory, :default => @prefix+"/maindir", :desc => "a"}, :seconddir => { :type => :directory, :default => @prefix+"/seconddir", :desc => "a"} @settings.define_settings :other, :otherdir => { :type => :directory, :default => @prefix+"/otherdir", :desc => "a" } catalog = @settings.to_catalog [@prefix+"/maindir", @prefix+"/seconddir", @prefix+"/otherdir"].each do |path| catalog.resource(:file, path).should be_instance_of(Puppet::Resource) end end it "should add only files in the specified sections if section names are provided" do @settings.define_settings :main, :maindir => { :type => :directory, :default => @prefix+"/maindir", :desc => "a" } @settings.define_settings :other, :otherdir => { :type => :directory, :default => @prefix+"/otherdir", :desc => "a" } catalog = @settings.to_catalog(:main) catalog.resource(:file, @prefix+"/otherdir").should be_nil catalog.resource(:file, @prefix+"/maindir").should be_instance_of(Puppet::Resource) end it "should not try to add the same file twice" do @settings.define_settings :main, :maindir => { :type => :directory, :default => @prefix+"/maindir", :desc => "a" } @settings.define_settings :other, :otherdir => { :type => :directory, :default => @prefix+"/maindir", :desc => "a" } lambda { @settings.to_catalog }.should_not raise_error end it "should ignore files whose :to_resource method returns nil" do @settings.define_settings :main, :maindir => { :type => :directory, :default => @prefix+"/maindir", :desc => "a" } @settings.setting(:maindir).expects(:to_resource).returns nil Puppet::Resource::Catalog.any_instance.expects(:add_resource).never @settings.to_catalog end describe "on Microsoft Windows" do before :each do Puppet.features.stubs(:root?).returns true Puppet.features.stubs(:microsoft_windows?).returns true @settings.define_settings :foo, :mkusers => { :type => :boolean, :default => true, :desc => "e" }, :user => { :default => "suser", :desc => "doc" }, :group => { :default => "sgroup", :desc => "doc" } @settings.define_settings :other, :otherdir => { :type => :directory, :default => "/otherdir", :desc => "a", :owner => "service", :group => "service"} @catalog = @settings.to_catalog end it "it should not add users and groups to the catalog" do @catalog.resource(:user, "suser").should be_nil @catalog.resource(:group, "sgroup").should be_nil end end describe "adding default directory environment to the catalog" do let(:tmpenv) { tmpdir("envs") } let(:default_path) { "#{tmpenv}/environments" } before(:each) do @settings.define_settings :main, :environment => { :default => "production", :desc => "env"}, :environmentpath => { :type => :path, :default => default_path, :desc => "envpath"} end it "adds if environmentpath exists" do envpath = "#{tmpenv}/custom_envpath" @settings[:environmentpath] = envpath Dir.mkdir(envpath) catalog = @settings.to_catalog expect(catalog.resource_keys).to include(["File", "#{envpath}/production"]) end it "adds the first directory of environmentpath" do envdir = "#{tmpenv}/custom_envpath" envpath = "#{envdir}#{File::PATH_SEPARATOR}/some/other/envdir" @settings[:environmentpath] = envpath Dir.mkdir(envdir) catalog = @settings.to_catalog expect(catalog.resource_keys).to include(["File", "#{envdir}/production"]) end it "handles a non-existent environmentpath" do catalog = @settings.to_catalog expect(catalog.resource_keys).to be_empty end it "handles a default environmentpath" do Dir.mkdir(default_path) catalog = @settings.to_catalog expect(catalog.resource_keys).to include(["File", "#{default_path}/production"]) end end describe "when adding users and groups to the catalog" do before do Puppet.features.stubs(:root?).returns true Puppet.features.stubs(:microsoft_windows?).returns false @settings.define_settings :foo, :mkusers => { :type => :boolean, :default => true, :desc => "e" }, :user => { :default => "suser", :desc => "doc" }, :group => { :default => "sgroup", :desc => "doc" } @settings.define_settings :other, :otherdir => {:type => :directory, :default => "/otherdir", :desc => "a", :owner => "service", :group => "service"} @catalog = @settings.to_catalog end it "should add each specified user and group to the catalog if :mkusers is a valid setting, is enabled, and we're running as root" do @catalog.resource(:user, "suser").should be_instance_of(Puppet::Resource) @catalog.resource(:group, "sgroup").should be_instance_of(Puppet::Resource) end it "should only add users and groups to the catalog from specified sections" do @settings.define_settings :yay, :yaydir => { :type => :directory, :default => "/yaydir", :desc => "a", :owner => "service", :group => "service"} catalog = @settings.to_catalog(:other) catalog.resource(:user, "jane").should be_nil catalog.resource(:group, "billy").should be_nil end it "should not add users or groups to the catalog if :mkusers not running as root" do Puppet.features.stubs(:root?).returns false catalog = @settings.to_catalog catalog.resource(:user, "suser").should be_nil catalog.resource(:group, "sgroup").should be_nil end it "should not add users or groups to the catalog if :mkusers is not a valid setting" do Puppet.features.stubs(:root?).returns true settings = Puppet::Settings.new settings.define_settings :other, :otherdir => {:type => :directory, :default => "/otherdir", :desc => "a", :owner => "service", :group => "service"} catalog = settings.to_catalog catalog.resource(:user, "suser").should be_nil catalog.resource(:group, "sgroup").should be_nil end it "should not add users or groups to the catalog if :mkusers is a valid setting but is disabled" do @settings[:mkusers] = false catalog = @settings.to_catalog catalog.resource(:user, "suser").should be_nil catalog.resource(:group, "sgroup").should be_nil end it "should not try to add users or groups to the catalog twice" do @settings.define_settings :yay, :yaydir => {:type => :directory, :default => "/yaydir", :desc => "a", :owner => "service", :group => "service"} # This would fail if users/groups were added twice lambda { @settings.to_catalog }.should_not raise_error end it "should set :ensure to :present on each created user and group" do @catalog.resource(:user, "suser")[:ensure].should == :present @catalog.resource(:group, "sgroup")[:ensure].should == :present end it "should set each created user's :gid to the service group" do @settings.to_catalog.resource(:user, "suser")[:gid].should == "sgroup" end it "should not attempt to manage the root user" do Puppet.features.stubs(:root?).returns true @settings.define_settings :foo, :foodir => {:type => :directory, :default => "/foodir", :desc => "a", :owner => "root", :group => "service"} @settings.to_catalog.resource(:user, "root").should be_nil end end end it "should be able to be converted to a manifest" do Puppet::Settings.new.should respond_to(:to_manifest) end describe "when being converted to a manifest" do it "should produce a string with the code for each resource joined by two carriage returns" do @settings = Puppet::Settings.new @settings.define_settings :main, :maindir => { :type => :directory, :default => "/maindir", :desc => "a"}, :seconddir => { :type => :directory, :default => "/seconddir", :desc => "a"} main = stub 'main_resource', :ref => "File[/maindir]" main.expects(:to_manifest).returns "maindir" second = stub 'second_resource', :ref => "File[/seconddir]" second.expects(:to_manifest).returns "seconddir" @settings.setting(:maindir).expects(:to_resource).returns main @settings.setting(:seconddir).expects(:to_resource).returns second @settings.to_manifest.split("\n\n").sort.should == %w{maindir seconddir} end end describe "when using sections of the configuration to manage the local host" do before do @settings = Puppet::Settings.new @settings.stubs(:service_user_available?).returns true @settings.stubs(:service_group_available?).returns true @settings.define_settings :main, :noop => { :default => false, :desc => "", :type => :boolean } @settings.define_settings :main, :maindir => { :type => :directory, :default => make_absolute("/maindir"), :desc => "a" }, :seconddir => { :type => :directory, :default => make_absolute("/seconddir"), :desc => "a"} @settings.define_settings :main, :user => { :default => "suser", :desc => "doc" }, :group => { :default => "sgroup", :desc => "doc" } - @settings.define_settings :other, :otherdir => {:type => :directory, :default => make_absolute("/otherdir"), :desc => "a", :owner => "service", :group => "service", :mode => 0755} + @settings.define_settings :other, :otherdir => {:type => :directory, :default => make_absolute("/otherdir"), :desc => "a", :owner => "service", :group => "service", :mode => '0755'} @settings.define_settings :third, :thirddir => { :type => :directory, :default => make_absolute("/thirddir"), :desc => "b"} - @settings.define_settings :files, :myfile => {:type => :file, :default => make_absolute("/myfile"), :desc => "a", :mode => 0755} + @settings.define_settings :files, :myfile => {:type => :file, :default => make_absolute("/myfile"), :desc => "a", :mode => '0755'} end it "should provide a method that creates directories with the correct modes" do Puppet::Util::SUIDManager.expects(:asuser).with("suser", "sgroup").yields - Dir.expects(:mkdir).with(make_absolute("/otherdir"), 0755) + Dir.expects(:mkdir).with(make_absolute("/otherdir"), '0755') @settings.mkdir(:otherdir) end it "should create a catalog with the specified sections" do @settings.expects(:to_catalog).with(:main, :other).returns Puppet::Resource::Catalog.new("foo") @settings.use(:main, :other) end it "should canonicalize the sections" do @settings.expects(:to_catalog).with(:main, :other).returns Puppet::Resource::Catalog.new("foo") @settings.use("main", "other") end it "should ignore sections that have already been used" do @settings.expects(:to_catalog).with(:main).returns Puppet::Resource::Catalog.new("foo") @settings.use(:main) @settings.expects(:to_catalog).with(:other).returns Puppet::Resource::Catalog.new("foo") @settings.use(:main, :other) end it "should convert the created catalog to a RAL catalog" do @catalog = Puppet::Resource::Catalog.new("foo") @settings.expects(:to_catalog).with(:main).returns @catalog @catalog.expects(:to_ral).returns @catalog @settings.use(:main) end it "should specify that it is not managing a host catalog" do catalog = Puppet::Resource::Catalog.new("foo") catalog.expects(:apply) @settings.expects(:to_catalog).returns catalog catalog.stubs(:to_ral).returns catalog catalog.expects(:host_config=).with false @settings.use(:main) end it "should support a method for re-using all currently used sections" do @settings.expects(:to_catalog).with(:main, :third).times(2).returns Puppet::Resource::Catalog.new("foo") @settings.use(:main, :third) @settings.reuse end it "should fail with an appropriate message if any resources fail" do @catalog = Puppet::Resource::Catalog.new("foo") @catalog.stubs(:to_ral).returns @catalog @settings.expects(:to_catalog).returns @catalog @trans = mock("transaction") @catalog.expects(:apply).yields(@trans) @trans.expects(:any_failed?).returns(true) resource = Puppet::Type.type(:notify).new(:title => 'failed') status = Puppet::Resource::Status.new(resource) event = Puppet::Transaction::Event.new( :name => 'failure', :status => 'failure', :message => 'My failure') status.add_event(event) report = Puppet::Transaction::Report.new('apply') report.add_resource_status(status) @trans.expects(:report).returns report @settings.expects(:raise).with(includes("My failure")) @settings.use(:whatever) end end describe "when dealing with printing configs" do before do @settings = Puppet::Settings.new #these are the magic default values @settings.stubs(:value).with(:configprint).returns("") @settings.stubs(:value).with(:genconfig).returns(false) @settings.stubs(:value).with(:genmanifest).returns(false) @settings.stubs(:value).with(:environment).returns(nil) end describe "when checking print_config?" do it "should return false when the :configprint, :genconfig and :genmanifest are not set" do @settings.print_configs?.should be_false end it "should return true when :configprint has a value" do @settings.stubs(:value).with(:configprint).returns("something") @settings.print_configs?.should be_true end it "should return true when :genconfig has a value" do @settings.stubs(:value).with(:genconfig).returns(true) @settings.print_configs?.should be_true end it "should return true when :genmanifest has a value" do @settings.stubs(:value).with(:genmanifest).returns(true) @settings.print_configs?.should be_true end end describe "when printing configs" do describe "when :configprint has a value" do it "should call print_config_options" do @settings.stubs(:value).with(:configprint).returns("something") @settings.expects(:print_config_options) @settings.print_configs end it "should get the value of the option using the environment" do @settings.stubs(:value).with(:configprint).returns("something") @settings.stubs(:include?).with("something").returns(true) @settings.expects(:value).with(:environment).returns("env") @settings.expects(:value).with("something", "env").returns("foo") @settings.stubs(:puts).with("foo") @settings.print_configs end it "should print the value of the option" do @settings.stubs(:value).with(:configprint).returns("something") @settings.stubs(:include?).with("something").returns(true) @settings.stubs(:value).with("something", nil).returns("foo") @settings.expects(:puts).with("foo") @settings.print_configs end it "should print the value pairs if there are multiple options" do @settings.stubs(:value).with(:configprint).returns("bar,baz") @settings.stubs(:include?).with("bar").returns(true) @settings.stubs(:include?).with("baz").returns(true) @settings.stubs(:value).with("bar", nil).returns("foo") @settings.stubs(:value).with("baz", nil).returns("fud") @settings.expects(:puts).with("bar = foo") @settings.expects(:puts).with("baz = fud") @settings.print_configs end it "should return true after printing" do @settings.stubs(:value).with(:configprint).returns("something") @settings.stubs(:include?).with("something").returns(true) @settings.stubs(:value).with("something", nil).returns("foo") @settings.stubs(:puts).with("foo") @settings.print_configs.should be_true end it "should return false if a config param is not found" do @settings.stubs :puts @settings.stubs(:value).with(:configprint).returns("something") @settings.stubs(:include?).with("something").returns(false) @settings.print_configs.should be_false end end describe "when genconfig is true" do before do @settings.stubs :puts end it "should call to_config" do @settings.stubs(:value).with(:genconfig).returns(true) @settings.expects(:to_config) @settings.print_configs end it "should return true from print_configs" do @settings.stubs(:value).with(:genconfig).returns(true) @settings.stubs(:to_config) @settings.print_configs.should be_true end end describe "when genmanifest is true" do before do @settings.stubs :puts end it "should call to_config" do @settings.stubs(:value).with(:genmanifest).returns(true) @settings.expects(:to_manifest) @settings.print_configs end it "should return true from print_configs" do @settings.stubs(:value).with(:genmanifest).returns(true) @settings.stubs(:to_manifest) @settings.print_configs.should be_true end end end end describe "when determining if the service user is available" do let(:settings) do settings = Puppet::Settings.new settings.define_settings :main, :user => { :default => nil, :desc => "doc" } settings end def a_user_type_for(username) user = mock 'user' Puppet::Type.type(:user).expects(:new).with { |args| args[:name] == username }.returns user user end it "should return false if there is no user setting" do settings.should_not be_service_user_available end it "should return false if the user provider says the user is missing" do settings[:user] = "foo" a_user_type_for("foo").expects(:exists?).returns false settings.should_not be_service_user_available end it "should return true if the user provider says the user is present" do settings[:user] = "foo" a_user_type_for("foo").expects(:exists?).returns true settings.should be_service_user_available end it "caches the result of determining if the user is present" do settings[:user] = "foo" a_user_type_for("foo").expects(:exists?).returns true settings.should be_service_user_available settings.should be_service_user_available end end describe "when determining if the service group is available" do let(:settings) do settings = Puppet::Settings.new settings.define_settings :main, :group => { :default => nil, :desc => "doc" } settings end def a_group_type_for(groupname) group = mock 'group' Puppet::Type.type(:group).expects(:new).with { |args| args[:name] == groupname }.returns group group end it "should return false if there is no group setting" do settings.should_not be_service_group_available end it "should return false if the group provider says the group is missing" do settings[:group] = "foo" a_group_type_for("foo").expects(:exists?).returns false settings.should_not be_service_group_available end it "should return true if the group provider says the group is present" do settings[:group] = "foo" a_group_type_for("foo").expects(:exists?).returns true settings.should be_service_group_available end it "caches the result of determining if the group is present" do settings[:group] = "foo" a_group_type_for("foo").expects(:exists?).returns true settings.should be_service_group_available settings.should be_service_group_available end end describe "when dealing with command-line options" do let(:settings) { Puppet::Settings.new } it "should get options from Puppet.settings.optparse_addargs" do settings.expects(:optparse_addargs).returns([]) settings.send(:parse_global_options, []) end it "should add options to OptionParser" do settings.stubs(:optparse_addargs).returns( [["--option","-o", "Funny Option", :NONE]]) settings.expects(:handlearg).with("--option", true) settings.send(:parse_global_options, ["--option"]) end it "should not die if it sees an unrecognized option, because the app/face may handle it later" do expect { settings.send(:parse_global_options, ["--topuppet", "value"]) } .to_not raise_error end it "should not pass an unrecognized option to handleargs" do settings.expects(:handlearg).with("--topuppet", "value").never expect { settings.send(:parse_global_options, ["--topuppet", "value"]) } .to_not raise_error end it "should pass valid puppet settings options to handlearg even if they appear after an unrecognized option" do settings.stubs(:optparse_addargs).returns( [["--option","-o", "Funny Option", :NONE]]) settings.expects(:handlearg).with("--option", true) settings.send(:parse_global_options, ["--invalidoption", "--option"]) end it "should transform boolean option to normal form" do Puppet::Settings.clean_opt("--[no-]option", true).should == ["--option", true] end it "should transform boolean option to no- form" do Puppet::Settings.clean_opt("--[no-]option", false).should == ["--no-option", false] end it "should set preferred run mode from --run_mode string without error" do args = ["--run_mode", "master"] settings.expects(:handlearg).with("--run_mode", "master").never expect { settings.send(:parse_global_options, args) } .to_not raise_error Puppet.settings.preferred_run_mode.should == :master args.empty?.should == true end it "should set preferred run mode from --run_mode= string without error" do args = ["--run_mode=master"] settings.expects(:handlearg).with("--run_mode", "master").never expect { settings.send(:parse_global_options, args) } .to_not raise_error Puppet.settings.preferred_run_mode.should == :master args.empty?.should == true end end describe "default_certname" do describe "using hostname and domainname" do before :each do Puppet::Settings.stubs(:hostname_fact).returns("testhostname") Puppet::Settings.stubs(:domain_fact).returns("domain.test.") end it "should use both to generate fqdn" do Puppet::Settings.default_certname.should =~ /testhostname\.domain\.test/ end it "should remove trailing dots from fqdn" do Puppet::Settings.default_certname.should == 'testhostname.domain.test' end end describe "using just hostname" do before :each do Puppet::Settings.stubs(:hostname_fact).returns("testhostname") Puppet::Settings.stubs(:domain_fact).returns("") end it "should use only hostname to generate fqdn" do Puppet::Settings.default_certname.should == "testhostname" end it "should removing trailing dots from fqdn" do Puppet::Settings.default_certname.should == "testhostname" end end end end diff --git a/spec/unit/type/file/mode_spec.rb b/spec/unit/type/file/mode_spec.rb index cb971b3a9..82bd5a09f 100755 --- a/spec/unit/type/file/mode_spec.rb +++ b/spec/unit/type/file/mode_spec.rb @@ -1,220 +1,220 @@ #! /usr/bin/env ruby require 'spec_helper' describe Puppet::Type.type(:file).attrclass(:mode) do include PuppetSpec::Files let(:path) { tmpfile('mode_spec') } - let(:resource) { Puppet::Type.type(:file).new :path => path, :mode => 0644 } + let(:resource) { Puppet::Type.type(:file).new :path => path, :mode => '0644' } let(:mode) { resource.property(:mode) } describe "#validate" do it "should accept values specified as integers" do expect { mode.value = 0755 }.not_to raise_error end it "should accept values specified as octal numbers in strings" do expect { mode.value = '0755' }.not_to raise_error end it "should accept valid symbolic strings" do expect { mode.value = 'g+w,u-x' }.not_to raise_error end it "should not accept strings other than octal numbers" do expect do mode.value = 'readable please!' end.to raise_error(Puppet::Error, /The file mode specification is invalid/) end end describe "#munge" do # This is sort of a redundant test, but its spec is important. it "should return the value as a string" do mode.munge('0644').should be_a(String) end it "should accept strings as arguments" do mode.munge('0644').should == '644' end it "should accept symbolic strings as arguments and return them intact" do mode.munge('u=rw,go=r').should == 'u=rw,go=r' end it "should accept integers are arguments" do mode.munge(0644).should == '644' end end describe "#dirmask" do before :each do Dir.mkdir(path) end it "should add execute bits corresponding to read bits for directories" do mode.dirmask('0644').should == '755' end it "should not add an execute bit when there is no read bit" do mode.dirmask('0600').should == '700' end it "should not add execute bits for files that aren't directories" do resource[:path] = tmpfile('other_file') mode.dirmask('0644').should == '0644' end end describe "#insync?" do it "should return true if the mode is correct" do FileUtils.touch(path) mode.must be_insync('644') end it "should return false if the mode is incorrect" do FileUtils.touch(path) mode.must_not be_insync('755') end it "should return true if the file is a link and we are managing links", :if => Puppet.features.manages_symlinks? do Puppet::FileSystem.symlink('anything', path) mode.must be_insync('644') end describe "with a symbolic mode" do let(:resource_sym) { Puppet::Type.type(:file).new :path => path, :mode => 'u+w,g-w' } let(:mode_sym) { resource_sym.property(:mode) } it "should return true if the mode matches, regardless of other bits" do FileUtils.touch(path) mode_sym.must be_insync('644') end it "should return false if the mode requires 0's where there are 1's" do FileUtils.touch(path) mode_sym.must_not be_insync('624') end it "should return false if the mode requires 1's where there are 0's" do FileUtils.touch(path) mode_sym.must_not be_insync('044') end end end describe "#retrieve" do it "should return absent if the resource doesn't exist" do resource[:path] = File.expand_path("/does/not/exist") mode.retrieve.should == :absent end it "should retrieve the directory mode from the provider" do Dir.mkdir(path) mode.expects(:dirmask).with('644').returns '755' resource.provider.expects(:mode).returns '755' mode.retrieve.should == '755' end it "should retrieve the file mode from the provider" do FileUtils.touch(path) mode.expects(:dirmask).with('644').returns '644' resource.provider.expects(:mode).returns '644' mode.retrieve.should == '644' end end describe '#should_to_s' do describe 'with a 3-digit mode' do it 'returns a 4-digit mode with a leading zero' do mode.should_to_s('755').should == '0755' end end describe 'with a 4-digit mode' do it 'returns the 4-digit mode when the first digit is a zero' do mode.should_to_s('0755').should == '0755' end it 'returns the 4-digit mode when the first digit is not a zero' do mode.should_to_s('1755').should == '1755' end end end describe '#is_to_s' do describe 'with a 3-digit mode' do it 'returns a 4-digit mode with a leading zero' do mode.is_to_s('755').should == '0755' end end describe 'with a 4-digit mode' do it 'returns the 4-digit mode when the first digit is a zero' do mode.is_to_s('0755').should == '0755' end it 'returns the 4-digit mode when the first digit is not a zero' do mode.is_to_s('1755').should == '1755' end end describe 'when passed :absent' do it 'returns :absent' do mode.is_to_s(:absent).should == :absent end end end describe "#sync with a symbolic mode" do let(:resource_sym) { Puppet::Type.type(:file).new :path => path, :mode => 'u+w,g-w' } let(:mode_sym) { resource_sym.property(:mode) } before { FileUtils.touch(path) } it "changes only the requested bits" do # lower nibble must be set to 4 for the sake of passing on Windows Puppet::FileSystem.chmod(0464, path) mode_sym.sync stat = Puppet::FileSystem.stat(path) (stat.mode & 0777).to_s(8).should == "644" end end describe '#sync with a symbolic mode of +X for a file' do let(:resource_sym) { Puppet::Type.type(:file).new :path => path, :mode => 'g+wX' } let(:mode_sym) { resource_sym.property(:mode) } before { FileUtils.touch(path) } it 'does not change executable bit if no executable bit is set' do Puppet::FileSystem.chmod(0644, path) mode_sym.sync stat = Puppet::FileSystem.stat(path) (stat.mode & 0777).to_s(8).should == '664' end it 'does change executable bit if an executable bit is set' do Puppet::FileSystem.chmod(0744, path) mode_sym.sync stat = Puppet::FileSystem.stat(path) (stat.mode & 0777).to_s(8).should == '774' end end end diff --git a/spec/unit/type/file_spec.rb b/spec/unit/type/file_spec.rb index 00b027ed7..7a4d02553 100755 --- a/spec/unit/type/file_spec.rb +++ b/spec/unit/type/file_spec.rb @@ -1,1504 +1,1504 @@ #! /usr/bin/env ruby require 'spec_helper' describe Puppet::Type.type(:file) do include PuppetSpec::Files let(:path) { tmpfile('file_testing') } let(:file) { described_class.new(:path => path, :catalog => catalog) } let(:provider) { file.provider } let(:catalog) { Puppet::Resource::Catalog.new } before do Puppet.features.stubs("posix?").returns(true) end describe "the path parameter" do describe "on POSIX systems", :if => Puppet.features.posix? do it "should remove trailing slashes" do file[:path] = "/foo/bar/baz/" file[:path].should == "/foo/bar/baz" end it "should remove double slashes" do file[:path] = "/foo/bar//baz" file[:path].should == "/foo/bar/baz" end it "should remove triple slashes" do file[:path] = "/foo/bar///baz" file[:path].should == "/foo/bar/baz" end it "should remove trailing double slashes" do file[:path] = "/foo/bar/baz//" file[:path].should == "/foo/bar/baz" end it "should leave a single slash alone" do file[:path] = "/" file[:path].should == "/" end it "should accept and collapse a double-slash at the start of the path" do file[:path] = "//tmp/xxx" file[:path].should == '/tmp/xxx' end it "should accept and collapse a triple-slash at the start of the path" do file[:path] = "///tmp/xxx" file[:path].should == '/tmp/xxx' end end describe "on Windows systems", :if => Puppet.features.microsoft_windows? do it "should remove trailing slashes" do file[:path] = "X:/foo/bar/baz/" file[:path].should == "X:/foo/bar/baz" end it "should remove double slashes" do file[:path] = "X:/foo/bar//baz" file[:path].should == "X:/foo/bar/baz" end it "should remove trailing double slashes" do file[:path] = "X:/foo/bar/baz//" file[:path].should == "X:/foo/bar/baz" end it "should leave a drive letter with a slash alone" do file[:path] = "X:/" file[:path].should == "X:/" end it "should not accept a drive letter without a slash" do expect { file[:path] = "X:" }.to raise_error(/File paths must be fully qualified/) end describe "when using UNC filenames", :if => Puppet.features.microsoft_windows? do it "should remove trailing slashes" do file[:path] = "//localhost/foo/bar/baz/" file[:path].should == "//localhost/foo/bar/baz" end it "should remove double slashes" do file[:path] = "//localhost/foo/bar//baz" file[:path].should == "//localhost/foo/bar/baz" end it "should remove trailing double slashes" do file[:path] = "//localhost/foo/bar/baz//" file[:path].should == "//localhost/foo/bar/baz" end it "should remove a trailing slash from a sharename" do file[:path] = "//localhost/foo/" file[:path].should == "//localhost/foo" end it "should not modify a sharename" do file[:path] = "//localhost/foo" file[:path].should == "//localhost/foo" end end end end describe "the backup parameter" do [false, 'false', :false].each do |value| it "should disable backup if the value is #{value.inspect}" do file[:backup] = value file[:backup].should == false end end [true, 'true', '.puppet-bak'].each do |value| it "should use .puppet-bak if the value is #{value.inspect}" do file[:backup] = value file[:backup].should == '.puppet-bak' end end it "should use the provided value if it's any other string" do file[:backup] = "over there" file[:backup].should == "over there" end it "should fail if backup is set to anything else" do expect do file[:backup] = 97 end.to raise_error(Puppet::Error, /Invalid backup type 97/) end end describe "the recurse parameter" do it "should default to recursion being disabled" do file[:recurse].should be_false end [true, "true", "inf", "remote"].each do |value| it "should consider #{value} to enable recursion" do file[:recurse] = value file[:recurse].should be_true end end it "should not allow numbers" do expect { file[:recurse] = 10 }.to raise_error( Puppet::Error, /Parameter recurse failed on File\[[^\]]+\]: Invalid recurse value 10/) end [false, "false"].each do |value| it "should consider #{value} to disable recursion" do file[:recurse] = value file[:recurse].should be_false end end end describe "the recurselimit parameter" do it "should accept integers" do file[:recurselimit] = 12 file[:recurselimit].should == 12 end it "should munge string numbers to number numbers" do file[:recurselimit] = '12' file[:recurselimit].should == 12 end it "should fail if given a non-number" do expect do file[:recurselimit] = 'twelve' end.to raise_error(Puppet::Error, /Invalid value "twelve"/) end end describe "the replace parameter" do [true, :true, :yes].each do |value| it "should consider #{value} to be true" do file[:replace] = value file[:replace].should be_true end end [false, :false, :no].each do |value| it "should consider #{value} to be false" do file[:replace] = value file[:replace].should be_false end end end describe ".instances" do it "should return an empty array" do described_class.instances.should == [] end end describe "#bucket" do it "should return nil if backup is off" do file[:backup] = false file.bucket.should == nil end it "should not return a bucket if using a file extension for backup" do file[:backup] = '.backup' file.bucket.should == nil end it "should return the default filebucket if using the 'puppet' filebucket" do file[:backup] = 'puppet' bucket = stub('bucket') file.stubs(:default_bucket).returns bucket file.bucket.should == bucket end it "should fail if using a remote filebucket and no catalog exists" do file.catalog = nil file[:backup] = 'my_bucket' expect { file.bucket }.to raise_error(Puppet::Error, "Can not find filebucket for backups without a catalog") end it "should fail if the specified filebucket isn't in the catalog" do file[:backup] = 'my_bucket' expect { file.bucket }.to raise_error(Puppet::Error, "Could not find filebucket my_bucket specified in backup") end it "should use the specified filebucket if it is in the catalog" do file[:backup] = 'my_bucket' filebucket = Puppet::Type.type(:filebucket).new(:name => 'my_bucket') catalog.add_resource(filebucket) file.bucket.should == filebucket.bucket end end describe "#asuser" do before :each do # Mocha won't let me just stub SUIDManager.asuser to yield and return, # but it will do exactly that if we're not root. Puppet.features.stubs(:root?).returns false end it "should return the desired owner if they can write to the parent directory" do file[:owner] = 1001 FileTest.stubs(:writable?).with(File.dirname file[:path]).returns true file.asuser.should == 1001 end it "should return nil if the desired owner can't write to the parent directory" do file[:owner] = 1001 FileTest.stubs(:writable?).with(File.dirname file[:path]).returns false file.asuser.should == nil end it "should return nil if not managing owner" do file.asuser.should == nil end end describe "#exist?" do it "should be considered existent if it can be stat'ed" do file.expects(:stat).returns mock('stat') file.must be_exist end it "should be considered nonexistent if it can not be stat'ed" do file.expects(:stat).returns nil file.must_not be_exist end end describe "#eval_generate" do before do @graph = stub 'graph', :add_edge => nil catalog.stubs(:relationship_graph).returns @graph end it "should recurse if recursion is enabled" do resource = stub('resource', :[] => 'resource') file.expects(:recurse).returns [resource] file[:recurse] = true file.eval_generate.should == [resource] end it "should not recurse if recursion is disabled" do file.expects(:recurse).never file[:recurse] = false file.eval_generate.should == [] end end describe "#ancestors" do it "should return the ancestors of the file, in ascending order" do file = described_class.new(:path => make_absolute("/tmp/foo/bar/baz/qux")) pieces = %W[#{make_absolute('/')} tmp foo bar baz] ancestors = file.ancestors ancestors.should_not be_empty ancestors.reverse.each_with_index do |path,i| path.should == File.join(*pieces[0..i]) end end end describe "#flush" do it "should flush all properties that respond to :flush" do file[:source] = File.expand_path(__FILE__) file.parameter(:source).expects(:flush) file.flush end it "should reset its stat reference" do FileUtils.touch(path) stat1 = file.stat file.stat.should equal(stat1) file.flush file.stat.should_not equal(stat1) end end describe "#initialize" do it "should remove a trailing slash from the title to create the path" do title = File.expand_path("/abc/\n\tdef/") file = described_class.new(:title => title) file[:path].should == title end it "should set a desired 'ensure' value if none is set and 'content' is set" do file = described_class.new(:path => path, :content => "/foo/bar") file[:ensure].should == :file end it "should set a desired 'ensure' value if none is set and 'target' is set", :if => described_class.defaultprovider.feature?(:manages_symlinks) do file = described_class.new(:path => path, :target => File.expand_path(__FILE__)) file[:ensure].should == :link end end describe "#mark_children_for_purging" do it "should set each child's ensure to absent" do paths = %w[foo bar baz] children = paths.inject({}) do |children,child| children.merge child => described_class.new(:path => File.join(path, child), :ensure => :present) end file.mark_children_for_purging(children) children.length.should == 3 children.values.each do |child| child[:ensure].should == :absent end end it "should skip children which have a source" do child = described_class.new(:path => path, :ensure => :present, :source => File.expand_path(__FILE__)) file.mark_children_for_purging('foo' => child) child[:ensure].should == :present end end describe "#newchild" do it "should create a new resource relative to the parent" do child = file.newchild('bar') child.must be_a(described_class) child[:path].should == File.join(file[:path], 'bar') end { :ensure => :present, :recurse => true, :recurselimit => 5, :target => "some_target", :source => File.expand_path("some_source"), }.each do |param, value| it "should omit the #{param} parameter", :if => described_class.defaultprovider.feature?(:manages_symlinks) do # Make a new file, because we have to set the param at initialization # or it wouldn't be copied regardless. file = described_class.new(:path => path, param => value) child = file.newchild('bar') child[param].should_not == value end end it "should copy all of the parent resource's 'should' values that were set at initialization" do parent = described_class.new(:path => path, :owner => 'root', :group => 'wheel') child = parent.newchild("my/path") child[:owner].should == 'root' child[:group].should == 'wheel' end it "should not copy default values to the new child" do child = file.newchild("my/path") child.original_parameters.should_not include(:backup) end it "should not copy values to the child which were set by the source" do source = File.expand_path(__FILE__) file[:source] = source - metadata = stub 'metadata', :owner => "root", :group => "root", :mode => 0755, :ftype => "file", :checksum => "{md5}whatever", :source => source + metadata = stub 'metadata', :owner => "root", :group => "root", :mode => '0755', :ftype => "file", :checksum => "{md5}whatever", :source => source file.parameter(:source).stubs(:metadata).returns metadata file.parameter(:source).copy_source_values file.class.expects(:new).with { |params| params[:group].nil? } file.newchild("my/path") end end describe "#purge?" do it "should return false if purge is not set" do file.must_not be_purge end it "should return true if purge is set to true" do file[:purge] = true file.must be_purge end it "should return false if purge is set to false" do file[:purge] = false file.must_not be_purge end end describe "#recurse" do before do file[:recurse] = true @metadata = Puppet::FileServing::Metadata end describe "and a source is set" do it "should pass the already-discovered resources to recurse_remote" do file[:source] = File.expand_path(__FILE__) file.stubs(:recurse_local).returns(:foo => "bar") file.expects(:recurse_remote).with(:foo => "bar").returns [] file.recurse end end describe "and a target is set" do it "should use recurse_link" do file[:target] = File.expand_path(__FILE__) file.stubs(:recurse_local).returns(:foo => "bar") file.expects(:recurse_link).with(:foo => "bar").returns [] file.recurse end end it "should use recurse_local if recurse is not remote" do file.expects(:recurse_local).returns({}) file.recurse end it "should not use recurse_local if recurse is remote" do file[:recurse] = :remote file.expects(:recurse_local).never file.recurse end it "should return the generated resources as an array sorted by file path" do one = stub 'one', :[] => "/one" two = stub 'two', :[] => "/one/two" three = stub 'three', :[] => "/three" file.expects(:recurse_local).returns(:one => one, :two => two, :three => three) file.recurse.should == [one, two, three] end describe "and purging is enabled" do before do file[:purge] = true end it "should mark each file for removal" do local = described_class.new(:path => path, :ensure => :present) file.expects(:recurse_local).returns("local" => local) file.recurse local[:ensure].should == :absent end it "should not remove files that exist in the remote repository" do file[:source] = File.expand_path(__FILE__) file.expects(:recurse_local).returns({}) remote = described_class.new(:path => path, :source => File.expand_path(__FILE__), :ensure => :present) file.expects(:recurse_remote).with { |hash| hash["remote"] = remote } file.recurse remote[:ensure].should_not == :absent end end end describe "#remove_less_specific_files" do it "should remove any nested files that are already in the catalog" do foo = described_class.new :path => File.join(file[:path], 'foo') bar = described_class.new :path => File.join(file[:path], 'bar') baz = described_class.new :path => File.join(file[:path], 'baz') catalog.add_resource(foo) catalog.add_resource(bar) file.remove_less_specific_files([foo, bar, baz]).should == [baz] end end describe "#remove_less_specific_files" do it "should remove any nested files that are already in the catalog" do foo = described_class.new :path => File.join(file[:path], 'foo') bar = described_class.new :path => File.join(file[:path], 'bar') baz = described_class.new :path => File.join(file[:path], 'baz') catalog.add_resource(foo) catalog.add_resource(bar) file.remove_less_specific_files([foo, bar, baz]).should == [baz] end end describe "#recurse?" do it "should be true if recurse is true" do file[:recurse] = true file.must be_recurse end it "should be true if recurse is remote" do file[:recurse] = :remote file.must be_recurse end it "should be false if recurse is false" do file[:recurse] = false file.must_not be_recurse end end describe "#recurse_link" do before do @first = stub 'first', :relative_path => "first", :full_path => "/my/first", :ftype => "directory" @second = stub 'second', :relative_path => "second", :full_path => "/my/second", :ftype => "file" @resource = stub 'file', :[]= => nil end it "should pass its target to the :perform_recursion method" do file[:target] = "mylinks" file.expects(:perform_recursion).with("mylinks").returns [@first] file.stubs(:newchild).returns @resource file.recurse_link({}) end it "should ignore the recursively-found '.' file and configure the top-level file to create a directory" do @first.stubs(:relative_path).returns "." file[:target] = "mylinks" file.expects(:perform_recursion).with("mylinks").returns [@first] file.stubs(:newchild).never file.expects(:[]=).with(:ensure, :directory) file.recurse_link({}) end it "should create a new child resource for each generated metadata instance's relative path that doesn't already exist in the children hash" do file.expects(:perform_recursion).returns [@first, @second] file.expects(:newchild).with(@first.relative_path).returns @resource file.recurse_link("second" => @resource) end it "should not create a new child resource for paths that already exist in the children hash" do file.expects(:perform_recursion).returns [@first] file.expects(:newchild).never file.recurse_link("first" => @resource) end it "should set the target to the full path of discovered file and set :ensure to :link if the file is not a directory", :if => described_class.defaultprovider.feature?(:manages_symlinks) do file.stubs(:perform_recursion).returns [@first, @second] file.recurse_link("first" => @resource, "second" => file) file[:ensure].should == :link file[:target].should == "/my/second" end it "should :ensure to :directory if the file is a directory" do file.stubs(:perform_recursion).returns [@first, @second] file.recurse_link("first" => file, "second" => @resource) file[:ensure].should == :directory end it "should return a hash with both created and existing resources with the relative paths as the hash keys" do file.expects(:perform_recursion).returns [@first, @second] file.stubs(:newchild).returns file file.recurse_link("second" => @resource).should == {"second" => @resource, "first" => file} end end describe "#recurse_local" do before do @metadata = stub 'metadata', :relative_path => "my/file" end it "should pass its path to the :perform_recursion method" do file.expects(:perform_recursion).with(file[:path]).returns [@metadata] file.stubs(:newchild) file.recurse_local end it "should return an empty hash if the recursion returns nothing" do file.expects(:perform_recursion).returns nil file.recurse_local.should == {} end it "should create a new child resource with each generated metadata instance's relative path" do file.expects(:perform_recursion).returns [@metadata] file.expects(:newchild).with(@metadata.relative_path).returns "fiebar" file.recurse_local end it "should not create a new child resource for the '.' directory" do @metadata.stubs(:relative_path).returns "." file.expects(:perform_recursion).returns [@metadata] file.expects(:newchild).never file.recurse_local end it "should return a hash of the created resources with the relative paths as the hash keys" do file.expects(:perform_recursion).returns [@metadata] file.expects(:newchild).with("my/file").returns "fiebar" file.recurse_local.should == {"my/file" => "fiebar"} end it "should set checksum_type to none if this file checksum is none" do file[:checksum] = :none Puppet::FileServing::Metadata.indirection.expects(:search).with { |path,params| params[:checksum_type] == :none }.returns [@metadata] file.expects(:newchild).with("my/file").returns "fiebar" file.recurse_local end end describe "#recurse_remote", :uses_checksums => true do let(:my) { File.expand_path('/my') } before do file[:source] = "puppet://foo/bar" @first = Puppet::FileServing::Metadata.new(my, :relative_path => "first") @second = Puppet::FileServing::Metadata.new(my, :relative_path => "second") @first.stubs(:ftype).returns "directory" @second.stubs(:ftype).returns "directory" @parameter = stub 'property', :metadata= => nil @resource = stub 'file', :[]= => nil, :parameter => @parameter end it "should pass its source to the :perform_recursion method" do data = Puppet::FileServing::Metadata.new(File.expand_path("/whatever"), :relative_path => "foobar") file.expects(:perform_recursion).with("puppet://foo/bar").returns [data] file.stubs(:newchild).returns @resource file.recurse_remote({}) end it "should not recurse when the remote file is not a directory" do data = Puppet::FileServing::Metadata.new(File.expand_path("/whatever"), :relative_path => ".") data.stubs(:ftype).returns "file" file.expects(:perform_recursion).with("puppet://foo/bar").returns [data] file.expects(:newchild).never file.recurse_remote({}) end it "should set the source of each returned file to the searched-for URI plus the found relative path" do @first.expects(:source=).with File.join("puppet://foo/bar", @first.relative_path) file.expects(:perform_recursion).returns [@first] file.stubs(:newchild).returns @resource file.recurse_remote({}) end it "should create a new resource for any relative file paths that do not already have a resource" do file.stubs(:perform_recursion).returns [@first] file.expects(:newchild).with("first").returns @resource file.recurse_remote({}).should == {"first" => @resource} end it "should not create a new resource for any relative file paths that do already have a resource" do file.stubs(:perform_recursion).returns [@first] file.expects(:newchild).never file.recurse_remote("first" => @resource) end it "should set the source of each resource to the source of the metadata" do file.stubs(:perform_recursion).returns [@first] @resource.stubs(:[]=) @resource.expects(:[]=).with(:source, File.join("puppet://foo/bar", @first.relative_path)) file.recurse_remote("first" => @resource) end # LAK:FIXME This is a bug, but I can't think of a fix for it. Fortunately it's already # filed, and when it's fixed, we'll just fix the whole flow. with_digest_algorithms do it "it should set the checksum type to #{metadata[:digest_algorithm]} if the remote file is a file" do @first.stubs(:ftype).returns "file" file.stubs(:perform_recursion).returns [@first] @resource.stubs(:[]=) @resource.expects(:[]=).with(:checksum, digest_algorithm.intern) file.recurse_remote("first" => @resource) end end it "should store the metadata in the source property for each resource so the source does not have to requery the metadata" do file.stubs(:perform_recursion).returns [@first] @resource.expects(:parameter).with(:source).returns @parameter @parameter.expects(:metadata=).with(@first) file.recurse_remote("first" => @resource) end it "should not create a new resource for the '.' file" do @first.stubs(:relative_path).returns "." file.stubs(:perform_recursion).returns [@first] file.expects(:newchild).never file.recurse_remote({}) end it "should store the metadata in the main file's source property if the relative path is '.'" do @first.stubs(:relative_path).returns "." file.stubs(:perform_recursion).returns [@first] file.parameter(:source).expects(:metadata=).with @first file.recurse_remote("first" => @resource) end describe "and multiple sources are provided" do let(:sources) do h = {} %w{/a /b /c /d}.each do |key| h[key] = URI.unescape(Puppet::Util.path_to_uri(File.expand_path(key)).to_s) end h end describe "and :sourceselect is set to :first" do it "should create file instances for the results for the first source to return any values" do data = Puppet::FileServing::Metadata.new(File.expand_path("/whatever"), :relative_path => "foobar") file[:source] = sources.keys.sort.map { |key| File.expand_path(key) } file.expects(:perform_recursion).with(sources['/a']).returns nil file.expects(:perform_recursion).with(sources['/b']).returns [] file.expects(:perform_recursion).with(sources['/c']).returns [data] file.expects(:perform_recursion).with(sources['/d']).never file.expects(:newchild).with("foobar").returns @resource file.recurse_remote({}) end end describe "and :sourceselect is set to :all" do before do file[:sourceselect] = :all end it "should return every found file that is not in a previous source" do klass = Puppet::FileServing::Metadata file[:source] = abs_path = %w{/a /b /c /d}.map {|f| File.expand_path(f) } file.stubs(:newchild).returns @resource one = [klass.new(abs_path[0], :relative_path => "a")] file.expects(:perform_recursion).with(sources['/a']).returns one file.expects(:newchild).with("a").returns @resource two = [klass.new(abs_path[1], :relative_path => "a"), klass.new(abs_path[1], :relative_path => "b")] file.expects(:perform_recursion).with(sources['/b']).returns two file.expects(:newchild).with("b").returns @resource three = [klass.new(abs_path[2], :relative_path => "a"), klass.new(abs_path[2], :relative_path => "c")] file.expects(:perform_recursion).with(sources['/c']).returns three file.expects(:newchild).with("c").returns @resource file.expects(:perform_recursion).with(sources['/d']).returns [] file.recurse_remote({}) end end end end describe "#perform_recursion" do it "should use Metadata to do its recursion" do Puppet::FileServing::Metadata.indirection.expects(:search) file.perform_recursion(file[:path]) end it "should use the provided path as the key to the search" do Puppet::FileServing::Metadata.indirection.expects(:search).with { |key, options| key == "/foo" } file.perform_recursion("/foo") end it "should return the results of the metadata search" do Puppet::FileServing::Metadata.indirection.expects(:search).returns "foobar" file.perform_recursion(file[:path]).should == "foobar" end it "should pass its recursion value to the search" do file[:recurse] = true Puppet::FileServing::Metadata.indirection.expects(:search).with { |key, options| options[:recurse] == true } file.perform_recursion(file[:path]) end it "should pass true if recursion is remote" do file[:recurse] = :remote Puppet::FileServing::Metadata.indirection.expects(:search).with { |key, options| options[:recurse] == true } file.perform_recursion(file[:path]) end it "should pass its recursion limit value to the search" do file[:recurselimit] = 10 Puppet::FileServing::Metadata.indirection.expects(:search).with { |key, options| options[:recurselimit] == 10 } file.perform_recursion(file[:path]) end it "should configure the search to ignore or manage links" do file[:links] = :manage Puppet::FileServing::Metadata.indirection.expects(:search).with { |key, options| options[:links] == :manage } file.perform_recursion(file[:path]) end it "should pass its 'ignore' setting to the search if it has one" do file[:ignore] = %w{.svn CVS} Puppet::FileServing::Metadata.indirection.expects(:search).with { |key, options| options[:ignore] == %w{.svn CVS} } file.perform_recursion(file[:path]) end end describe "#remove_existing" do it "should do nothing if the file doesn't exist" do file.remove_existing(:file).should == false end it "should fail if it can't backup the file" do file.stubs(:stat).returns stub('stat', :ftype => 'file') file.stubs(:perform_backup).returns false expect { file.remove_existing(:file) }.to raise_error(Puppet::Error, /Could not back up; will not replace/) end describe "backing up directories" do it "should not backup directories if force is false" do file[:force] = false file.stubs(:stat).returns stub('stat', :ftype => 'directory') file.expects(:perform_backup).never file.remove_existing(:file).should == false end it "should backup directories if force is true" do file[:force] = true FileUtils.expects(:rmtree).with(file[:path]) file.stubs(:stat).returns stub('stat', :ftype => 'directory') file.expects(:perform_backup).once.returns(true) file.remove_existing(:file).should == true end end it "should not do anything if the file is already the right type and not a link" do file.stubs(:stat).returns stub('stat', :ftype => 'file') file.remove_existing(:file).should == false end it "should not remove directories and should not invalidate the stat unless force is set" do # Actually call stat to set @needs_stat to nil file.stat file.stubs(:stat).returns stub('stat', :ftype => 'directory') file.remove_existing(:file) file.instance_variable_get(:@stat).should == nil @logs.should be_any {|log| log.level == :notice and log.message =~ /Not removing directory; use 'force' to override/} end it "should remove a directory if force is set" do file[:force] = true file.stubs(:stat).returns stub('stat', :ftype => 'directory') FileUtils.expects(:rmtree).with(file[:path]) file.remove_existing(:file).should == true end it "should remove an existing file" do file.stubs(:perform_backup).returns true FileUtils.touch(path) file.remove_existing(:directory).should == true Puppet::FileSystem.exist?(file[:path]).should == false end it "should remove an existing link", :if => described_class.defaultprovider.feature?(:manages_symlinks) do file.stubs(:perform_backup).returns true target = tmpfile('link_target') FileUtils.touch(target) Puppet::FileSystem.symlink(target, path) file[:target] = target file.remove_existing(:directory).should == true Puppet::FileSystem.exist?(file[:path]).should == false end it "should fail if the file is not a file, link, or directory" do file.stubs(:stat).returns stub('stat', :ftype => 'socket') expect { file.remove_existing(:file) }.to raise_error(Puppet::Error, /Could not back up files of type socket/) end it "should invalidate the existing stat of the file" do # Actually call stat to set @needs_stat to nil file.stat file.stubs(:stat).returns stub('stat', :ftype => 'file') Puppet::FileSystem.stubs(:unlink) file.remove_existing(:directory).should == true file.instance_variable_get(:@stat).should == :needs_stat end end describe "#retrieve" do it "should copy the source values if the 'source' parameter is set" do file[:source] = File.expand_path('/foo/bar') file.parameter(:source).expects(:copy_source_values) file.retrieve end end describe "#should_be_file?" do it "should have a method for determining if the file should be a normal file" do file.must respond_to(:should_be_file?) end it "should be a file if :ensure is set to :file" do file[:ensure] = :file file.must be_should_be_file end it "should be a file if :ensure is set to :present and the file exists as a normal file" do file.stubs(:stat).returns(mock('stat', :ftype => "file")) file[:ensure] = :present file.must be_should_be_file end it "should not be a file if :ensure is set to something other than :file" do file[:ensure] = :directory file.must_not be_should_be_file end it "should not be a file if :ensure is set to :present and the file exists but is not a normal file" do file.stubs(:stat).returns(mock('stat', :ftype => "directory")) file[:ensure] = :present file.must_not be_should_be_file end it "should be a file if :ensure is not set and :content is" do file[:content] = "foo" file.must be_should_be_file end it "should be a file if neither :ensure nor :content is set but the file exists as a normal file" do file.stubs(:stat).returns(mock("stat", :ftype => "file")) file.must be_should_be_file end it "should not be a file if neither :ensure nor :content is set but the file exists but not as a normal file" do file.stubs(:stat).returns(mock("stat", :ftype => "directory")) file.must_not be_should_be_file end end describe "#stat", :if => described_class.defaultprovider.feature?(:manages_symlinks) do before do target = tmpfile('link_target') FileUtils.touch(target) Puppet::FileSystem.symlink(target, path) file[:target] = target file[:links] = :manage # so we always use :lstat end it "should stat the target if it is following links" do file[:links] = :follow file.stat.ftype.should == 'file' end it "should stat the link if is it not following links" do file[:links] = :manage file.stat.ftype.should == 'link' end it "should return nil if the file does not exist" do file[:path] = make_absolute('/foo/bar/baz/non-existent') file.stat.should be_nil end it "should return nil if the file cannot be stat'ed" do dir = tmpfile('link_test_dir') child = File.join(dir, 'some_file') Dir.mkdir(dir) File.chmod(0, dir) file[:path] = child file.stat.should be_nil # chmod it back so we can clean it up File.chmod(0777, dir) end it "should return nil if parts of path are no directories" do regular_file = tmpfile('ENOTDIR_test') FileUtils.touch(regular_file) impossible_child = File.join(regular_file, 'some_file') file[:path] = impossible_child file.stat.should be_nil end it "should return the stat instance" do file.stat.should be_a(File::Stat) end it "should cache the stat instance" do file.stat.should equal(file.stat) end end describe "#write" do describe "when validating the checksum" do before { file.stubs(:validate_checksum?).returns(true) } it "should fail if the checksum parameter and content checksums do not match" do checksum = stub('checksum_parameter', :sum => 'checksum_b', :sum_file => 'checksum_b') file.stubs(:parameter).with(:checksum).returns(checksum) property = stub('content_property', :actual_content => "something", :length => "something".length, :write => 'checksum_a') file.stubs(:property).with(:content).returns(property) expect { file.write :NOTUSED }.to raise_error(Puppet::Error) end end describe "when not validating the checksum" do before { file.stubs(:validate_checksum?).returns(false) } it "should not fail if the checksum property and content checksums do not match" do checksum = stub('checksum_parameter', :sum => 'checksum_b') file.stubs(:parameter).with(:checksum).returns(checksum) property = stub('content_property', :actual_content => "something", :length => "something".length, :write => 'checksum_a') file.stubs(:property).with(:content).returns(property) expect { file.write :NOTUSED }.to_not raise_error end end describe "when resource mode is supplied" do before { file.stubs(:property_fix) } context "and writing temporary files" do before { file.stubs(:write_temporary_file?).returns(true) } it "should convert symbolic mode to int" do file[:mode] = 'oga=r' Puppet::Util.expects(:replace_file).with(file[:path], 0444) file.write :NOTUSED end it "should support int modes" do file[:mode] = '0444' Puppet::Util.expects(:replace_file).with(file[:path], 0444) file.write :NOTUSED end end context "and not writing temporary files" do before { file.stubs(:write_temporary_file?).returns(false) } it "should set a umask of 0" do file[:mode] = 'oga=r' Puppet::Util.expects(:withumask).with(0) file.write :NOTUSED end it "should convert symbolic mode to int" do file[:mode] = 'oga=r' File.expects(:open).with(file[:path], anything, 0444) file.write :NOTUSED end it "should support int modes" do file[:mode] = '0444' File.expects(:open).with(file[:path], anything, 0444) file.write :NOTUSED end end end describe "when resource mode is not supplied" do context "and content is supplied" do it "should default to 0644 mode" do file = described_class.new(:path => path, :content => "file content") file.write :NOTUSED expect(File.stat(file[:path]).mode & 0777).to eq(0644) end end context "and no content is supplied" do it "should use puppet's default umask of 022" do file = described_class.new(:path => path) umask_from_the_user = 0777 Puppet::Util.withumask(umask_from_the_user) do file.write :NOTUSED end expect(File.stat(file[:path]).mode & 0777).to eq(0644) end end end end describe "#fail_if_checksum_is_wrong" do it "should fail if the checksum of the file doesn't match the expected one" do expect do file.instance_eval do parameter(:checksum).stubs(:sum_file).returns('wrong!!') fail_if_checksum_is_wrong(self[:path], 'anything!') end end.to raise_error(Puppet::Error, /File written to disk did not match checksum/) end it "should not fail if the checksum is correct" do file.instance_eval do parameter(:checksum).stubs(:sum_file).returns('anything!') fail_if_checksum_is_wrong(self[:path], 'anything!').should == nil end end it "should not fail if the checksum is absent" do file.instance_eval do parameter(:checksum).stubs(:sum_file).returns(nil) fail_if_checksum_is_wrong(self[:path], 'anything!').should == nil end end end describe "#write_content" do it "should delegate writing the file to the content property" do io = stub('io') file[:content] = "some content here" file.property(:content).expects(:write).with(io) file.send(:write_content, io) end end describe "#write_temporary_file?" do it "should be true if the file has specified content" do file[:content] = 'some content' file.send(:write_temporary_file?).should be_true end it "should be true if the file has specified source" do file[:source] = File.expand_path('/tmp/foo') file.send(:write_temporary_file?).should be_true end it "should be false if the file has neither content nor source" do file.send(:write_temporary_file?).should be_false end end describe "#property_fix" do { :mode => 0777, :owner => 'joeuser', :group => 'joeusers', :seluser => 'seluser', :selrole => 'selrole', :seltype => 'seltype', :selrange => 'selrange' }.each do |name,value| it "should sync the #{name} property if it's not in sync" do file[name] = value prop = file.property(name) prop.expects(:retrieve) prop.expects(:safe_insync?).returns false prop.expects(:sync) file.send(:property_fix) end end end describe "when autorequiring" do describe "target" do it "should require file resource when specified with the target property", :if => described_class.defaultprovider.feature?(:manages_symlinks) do file = described_class.new(:path => File.expand_path("/foo"), :ensure => :directory) link = described_class.new(:path => File.expand_path("/bar"), :ensure => :link, :target => File.expand_path("/foo")) catalog.add_resource file catalog.add_resource link reqs = link.autorequire reqs.size.must == 1 reqs[0].source.must == file reqs[0].target.must == link end it "should require file resource when specified with the ensure property" do file = described_class.new(:path => File.expand_path("/foo"), :ensure => :directory) link = described_class.new(:path => File.expand_path("/bar"), :ensure => File.expand_path("/foo")) catalog.add_resource file catalog.add_resource link reqs = link.autorequire reqs.size.must == 1 reqs[0].source.must == file reqs[0].target.must == link end it "should not require target if target is not managed", :if => described_class.defaultprovider.feature?(:manages_symlinks) do link = described_class.new(:path => File.expand_path('/foo'), :ensure => :link, :target => '/bar') catalog.add_resource link link.autorequire.size.should == 0 end end describe "directories" do it "should autorequire its parent directory" do dir = described_class.new(:path => File.dirname(path)) catalog.add_resource file catalog.add_resource dir reqs = file.autorequire reqs[0].source.must == dir reqs[0].target.must == file end it "should autorequire its nearest ancestor directory" do dir = described_class.new(:path => File.dirname(path)) grandparent = described_class.new(:path => File.dirname(File.dirname(path))) catalog.add_resource file catalog.add_resource dir catalog.add_resource grandparent reqs = file.autorequire reqs.length.must == 1 reqs[0].source.must == dir reqs[0].target.must == file end it "should not autorequire anything when there is no nearest ancestor directory" do catalog.add_resource file file.autorequire.should be_empty end it "should not autorequire its parent dir if its parent dir is itself" do file[:path] = File.expand_path('/') catalog.add_resource file file.autorequire.should be_empty end describe "on Windows systems", :if => Puppet.features.microsoft_windows? do describe "when using UNC filenames" do it "should autorequire its parent directory" do file[:path] = '//localhost/foo/bar/baz' dir = described_class.new(:path => "//localhost/foo/bar") catalog.add_resource file catalog.add_resource dir reqs = file.autorequire reqs[0].source.must == dir reqs[0].target.must == file end it "should autorequire its nearest ancestor directory" do file = described_class.new(:path => "//localhost/foo/bar/baz/qux") dir = described_class.new(:path => "//localhost/foo/bar/baz") grandparent = described_class.new(:path => "//localhost/foo/bar") catalog.add_resource file catalog.add_resource dir catalog.add_resource grandparent reqs = file.autorequire reqs.length.must == 1 reqs[0].source.must == dir reqs[0].target.must == file end it "should not autorequire anything when there is no nearest ancestor directory" do file = described_class.new(:path => "//localhost/foo/bar/baz/qux") catalog.add_resource file file.autorequire.should be_empty end it "should not autorequire its parent dir if its parent dir is itself" do file = described_class.new(:path => "//localhost/foo") catalog.add_resource file puts file.autorequire file.autorequire.should be_empty end end end end end describe "when managing links", :if => Puppet.features.manages_symlinks? do require 'tempfile' before :each do Dir.mkdir(path) @target = File.join(path, "target") @link = File.join(path, "link") target = described_class.new( :ensure => :file, :path => @target, :catalog => catalog, :content => 'yayness', - :mode => 0644) + :mode => '0644') catalog.add_resource target @link_resource = described_class.new( :ensure => :link, :path => @link, :target => @target, :catalog => catalog, - :mode => 0755) + :mode => '0755') catalog.add_resource @link_resource # to prevent the catalog from trying to write state.yaml Puppet::Util::Storage.stubs(:store) end it "should preserve the original file mode and ignore the one set by the link" do @link_resource[:links] = :manage # default catalog.apply # I convert them to strings so they display correctly if there's an error. (Puppet::FileSystem.stat(@target).mode & 007777).to_s(8).should == '644' end it "should manage the mode of the followed link" do pending("Windows cannot presently manage the mode when following symlinks", :if => Puppet.features.microsoft_windows?) do @link_resource[:links] = :follow catalog.apply (Puppet::FileSystem.stat(@target).mode & 007777).to_s(8).should == '755' end end end describe "when using source" do before do file[:source] = File.expand_path('/one') end Puppet::Type::File::ParameterChecksum.value_collection.values.reject {|v| v == :none}.each do |checksum_type| describe "with checksum '#{checksum_type}'" do before do file[:checksum] = checksum_type end it 'should validate' do expect { file.validate }.to_not raise_error end end end describe "with checksum 'none'" do before do file[:checksum] = :none end it 'should raise an exception when validating' do expect { file.validate }.to raise_error(/You cannot specify source when using checksum 'none'/) end end end describe "when using content" do before do file[:content] = 'file contents' end (Puppet::Type::File::ParameterChecksum.value_collection.values - SOURCE_ONLY_CHECKSUMS).each do |checksum_type| describe "with checksum '#{checksum_type}'" do before do file[:checksum] = checksum_type end it 'should validate' do expect { file.validate }.to_not raise_error end end end SOURCE_ONLY_CHECKSUMS.each do |checksum_type| describe "with checksum '#{checksum_type}'" do it 'should raise an exception when validating' do file[:checksum] = checksum_type expect { file.validate }.to raise_error(/You cannot specify content when using checksum '#{checksum_type}'/) end end end end describe "when auditing" do before :each do # to prevent the catalog from trying to write state.yaml Puppet::Util::Storage.stubs(:store) end it "should not fail if creating a new file if group is not set" do file = described_class.new(:path => path, :audit => 'all', :content => 'content') catalog.add_resource(file) report = catalog.apply.report report.resource_statuses["File[#{path}]"].should_not be_failed File.read(path).should == 'content' end it "should not log errors if creating a new file with ensure present and no content" do file[:audit] = 'content' file[:ensure] = 'present' catalog.add_resource(file) catalog.apply Puppet::FileSystem.exist?(path).should be_true @logs.should_not be_any {|l| l.level != :notice } end end describe "when specifying both source and checksum" do it 'should use the specified checksum when source is first' do file[:source] = File.expand_path('/foo') file[:checksum] = :md5lite file[:checksum].should == :md5lite end it 'should use the specified checksum when source is last' do file[:checksum] = :md5lite file[:source] = File.expand_path('/foo') file[:checksum].should == :md5lite end end describe "when validating" do [[:source, :target], [:source, :content], [:target, :content]].each do |prop1,prop2| it "should fail if both #{prop1} and #{prop2} are specified" do file[prop1] = prop1 == :source ? File.expand_path("prop1 value") : "prop1 value" file[prop2] = "prop2 value" expect do file.validate end.to raise_error(Puppet::Error, /You cannot specify more than one of/) end end end end