[PUP-4178] defined() function returns true for undef and empty string, and false for "main" Created: 2015/03/10  Updated: 2015/05/20  Resolved: 2015/05/20

Status: Closed
Project: Puppet
Component/s: None
Affects Version/s: None
Fix Version/s: PUP 3.8.1, PUP 4.1.0

Type: Bug Priority: Minor
Reporter: Henrik Lindberg Assignee: Unassigned
Resolution: Done Votes: 0
Labels: None
Remaining Estimate: Not Specified
Time Spent: Not Specified
Original Estimate: Not Specified

Issue Links:
Blocks
is blocked by PUP-4438 Add required_repeated_param to 4.x fu... Closed
Duplicate
is duplicated by PUP-4331 should warn on checking an empty stri... Closed
Template:
Epic Link: 4.x Language
Story Points: 1
Sprint: Language 2015-04-29
Release Notes: Bug Fix

 Description   

The defined() function is written using the 3.x API and cannot differentiate between undefined and the empty string. Moving the function to the 4.x function API solves that issue.

The second issue is if the main class (known as '' internally) really should be available under that name. Everyone knows it as "main", and it is not possible to create your own "main" class without causing strange side effects.

puppet apply -e 'notice(defined(""))'
Notice: Scope(Class[main]): true
 
puppet apply -e 'notice(defined("main"))'
Notice: Scope(Class[main]): false
 
puppet apply -e 'notice(defined(undef))'
Notice: Scope(Class[main]): true

Further, it is not possible to ask specifically if a class foo is defined, or if a resource type foo is defined using the current version of the function since
a request for Class[foo] requires that the class is included in the catalog, and a request for 'foo' is satisfied by either a built in type, a define
or a class. For a resource type (built in or user defined) it is possible to ask for only the type e.g. File, or Resource[File]. To facilitate this, and for completeness, it should be possible to ask if Type[Class[foo]] is defined (as well as Type[Resource[T]] (even if the later is not absolutely required).

The solutions (in PR 3840) produces:

notice defined('')     # false
notice defined(undef)  # error
notice defined('main') # true
 
class not_included { }
notice defined('not_included') # true (but you don't know what it is)
notice defined(Class[not_included]) # false
notice defined(Type[Class[not_included]]) # true
notice defined(Type[Class[non_existing]]) # false

When defined is used with the future parser on 3.x, and always on 4.x

risk: medium
probability: medium
severity: medium (confusing, can cause code to be unpredictable, but work arounds exist)
test layer: unit



 Comments   
Comment by Henrik Lindberg [ 2015/04/13 ]

The ticket PUP-4331 is about warning/error when asking if an empty string is defined.

Comment by Henrik Lindberg [ 2015/04/14 ]

Added fix version puppet 3.8.1 as this should be made available to future parser on 3.x

Comment by Henrik Lindberg [ 2015/04/17 ]

The PR 3840 will also fix PUP-4338 (that will be closed as a duplicate).

It was easiest to deal with the error message by just typing the arguments to the defined function.

Comment by Henrik Lindberg [ 2015/04/17 ]

When working on this, it was clear that there were holes in both the implementation and spec test. The 4.x version of the function was updated along with the documentation (documentation is also reflected in the 3.x version). The description of this ticket is also updated to describe the full extent of the changes.

Comment by Thomas Hallgren [ 2015/04/21 ]

Merged to 3.x at 06aab91

Comment by Thomas Hallgren [ 2015/04/22 ]

Merged to master at 5124432 with subsequent maintenance fix at 4acc96d that addresses post-merge test failures.

Comment by Eric Thompson [ 2015/04/22 ]

validated on rhel7 at SHA: e5bce51

[root@lwfnn6bjeklzkfk puppet]# puppet apply -e "notice defined('')"
Notice: Scope(Class[main]): false
Notice: Compiled catalog for lwfnn6bjeklzkfk.delivery.puppetlabs.net in environment production in 0.39 seconds
Notice: Applied catalog in 0.01 seconds
[root@lwfnn6bjeklzkfk puppet]# puppet apply -e "notice defined(undef)"
Error: Evaluation Error: Error while evaluating a Function Call, function 'defined' called with mis-matched arguments
expected:
  defined(Variant[String, Type[CatalogEntry], Type[Type[CatalogEntry]]] vals{1,}) - arg count {1,}
actual:
  defined(Undef) - arg count {1}  at line 1:8 on node lwfnn6bjeklzkfk.delivery.puppetlabs.net
[root@lwfnn6bjeklzkfk puppet]# puppet apply -e "notice defined('main')"
Notice: Scope(Class[main]): true
Notice: Compiled catalog for lwfnn6bjeklzkfk.delivery.puppetlabs.net in environment production in 0.40 seconds
Notice: Applied catalog in 0.01 seconds
[root@lwfnn6bjeklzkfk puppet]# puppet apply -e 'class not_included{ } notice defined("not_included")'
Notice: Scope(Class[main]): true
Notice: Compiled catalog for lwfnn6bjeklzkfk.delivery.puppetlabs.net in environment production in 0.38 seconds
Notice: Applied catalog in 0.01 seconds
[root@lwfnn6bjeklzkfk puppet]# puppet apply -e 'class not_included{ } notice defined(Class[not_included])'
Notice: Scope(Class[main]): false
Notice: Compiled catalog for lwfnn6bjeklzkfk.delivery.puppetlabs.net in environment production in 0.39 seconds
Notice: Applied catalog in 0.01 seconds
[root@lwfnn6bjeklzkfk puppet]# puppet apply -e 'class not_included{ } notice defined(Type[Class[not_included]])'
Notice: Scope(Class[main]): true
Notice: Compiled catalog for lwfnn6bjeklzkfk.delivery.puppetlabs.net in environment production in 0.39 seconds
Notice: Applied catalog in 0.01 seconds
[root@lwfnn6bjeklzkfk puppet]# puppet apply -e 'class not_included{ } notice defined(Type[Class[not_existing]])'
Notice: Scope(Class[main]): false
Notice: Compiled catalog for lwfnn6bjeklzkfk.delivery.puppetlabs.net in environment production in 0.37 seconds
Notice: Applied catalog in 0.01 seconds
[root@lwfnn6bjeklzkfk puppet]# puppet apply -e 'class not_included{ } notice defined(Class[not_existing])'
Notice: Scope(Class[main]): false
Notice: Compiled catalog for lwfnn6bjeklzkfk.delivery.puppetlabs.net in environment production in 0.38 seconds
Notice: Applied catalog in 0.01 seconds
[root@lwfnn6bjeklzkfk puppet]# puppet apply -e 'class not_included{ } notice defined("not_existing")'
Notice: Scope(Class[main]): false
Notice: Compiled catalog for lwfnn6bjeklzkfk.delivery.puppetlabs.net in environment production in 0.37 seconds
Notice: Applied catalog in 0.01 seconds

Comment by Eric Thompson [ 2015/04/23 ]

validated on windows2012r2-rubyx64 at SHA: accdc21

Administrator@jeyap4urmyeyehz /opt/puppet-git-repos/puppet
$ cmd /c puppet apply -e "notice defined('')"
Notice: Scope(Class[main]): false
Notice: Compiled catalog for jeyap4urmyeyehz.delivery.puppetlabs.net in environment production in 0.45 seconds
Notice: Applied catalog in 0.02 seconds
c/
Administrator@jeyap4urmyeyehz /opt/puppet-git-repos/puppet
$ cmd /c puppet apply -e "notice defined(undef)"
Error: Evaluation Error: Error while evaluating a Function Call, function 'defined' called with mis-matched arguments
expected:
  defined(Variant[String, Type[CatalogEntry], Type[Type[CatalogEntry]]] vals{1,}) - arg count {1,}
actual:
  defined(Undef) - arg count {1}  at line 1:8 on node jeyap4urmyeyehz.delivery.puppetlabs.net
 
Administrator@jeyap4urmyeyehz /opt/puppet-git-repos/puppet
$ cmd /c puppet apply -e "notice defined('main')"
Notice: Scope(Class[main]): true
Notice: Compiled catalog for jeyap4urmyeyehz.delivery.puppetlabs.net in environment production in 0.45 seconds
cmd /c Notice: Applied catalog in 0.17 seconds
 
Administrator@jeyap4urmyeyehz /opt/puppet-git-repos/puppet
$ cmd /c puppet apply -e 'class not_included{ } notice defined("not_included")'
Notice: Scope(Class[main]): true
Notice: Compiled catalog for jeyap4urmyeyehz.delivery.puppetlabs.net in environment production in 0.47 seconds
Notice: Applied catalog in 0.06 seconds
 
Administrator@jeyap4urmyeyehz /opt/puppet-git-repos/puppet
$ cmd /c puppet apply -e 'class not_included{ } notice defined(Class[not_included])'
Notice: Scope(Class[main]): false
Notice: Compiled catalog for jeyap4urmyeyehz.delivery.puppetlabs.net in environment production in 0.47 seconds
cmd /c Notice: Applied catalog in 0.02 seconds
 
Administrator@jeyap4urmyeyehz /opt/puppet-git-repos/puppet
$ cmd /c puppet apply -e 'class not_included{ } notice defined(Type[Class[not_included]])'
 
Notice: Scope(Class[main]): true
Notice: Compiled catalog for jeyap4urmyeyehz.delivery.puppetlabs.net in environment production in 0.45 seconds
Notice: Applied catalog in 0.02 seconds
 
Administrator@jeyap4urmyeyehz /opt/puppet-git-repos/puppet
$ Notice: Scope(Class[main]): true
-bash: syntax error near unexpected token `('
 
Administrator@jeyap4urmyeyehz /opt/puppet-git-repos/puppet
$ cmd /c puppet apply -e 'class not_included{ } notice defined(Type[Class[not_existing]])'
Notice: Scope(Class[main]): false
Notice: Compiled catalog for jeyap4urmyeyehz.delivery.puppetlabs.net in environment production in 0.47 seconds
cmd /c Notice: Applied catalog in 0.03 seconds
 
Administrator@jeyap4urmyeyehz /opt/puppet-git-repos/puppet
$ cmd /c  puppet apply -e 'class not_included{ } notice defined(Class[not_existing])'
Notice: Scope(Class[main]): false
Notice: Compiled catalog for jeyap4urmyeyehz.delivery.puppetlabs.net in environment production in 0.45 seconds
cmd /c Notice: Applied catalog in 0.12 seconds
 
Administrator@jeyap4urmyeyehz /opt/puppet-git-repos/puppet
$ cmd /c   puppet apply -e 'class not_included{ } notice defined("not_existing")'
Notice: Scope(Class[main]): false
Notice: Compiled catalog for jeyap4urmyeyehz.delivery.puppetlabs.net in environment production in 0.47 seconds
Notice: Applied catalog in 0.19 seconds

Generated at Wed Oct 16 12:13:08 PDT 2019 using JIRA 7.7.1#77002-sha1:e75ca93d5574d9409c0630b81c894d9065296414.