File: defined.rb

package info (click to toggle)
puppet-agent 8.10.0-6
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 27,404 kB
  • sloc: ruby: 286,820; sh: 492; xml: 116; makefile: 88; cs: 68
file content (163 lines) | stat: -rw-r--r-- 6,248 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
# frozen_string_literal: true

# Determines whether a given class or resource type is defined and returns a Boolean
# value. You can also use `defined` to determine whether a specific resource is defined,
# or whether a variable has a value (including `undef`, as opposed to the variable never
# being declared or assigned).
#
# This function takes at least one string argument, which can be a class name, type name,
# resource reference, or variable reference of the form `'$name'`. (Note that the `$` sign
# is included in the string which must be in single quotes to prevent the `$` character
# to be interpreted as interpolation.
#
# The `defined` function checks both native and defined types, including types
# provided by modules. Types and classes are matched by their names. The function matches
# resource declarations by using resource references.
#
# @example Different types of `defined` function matches
#
# ```puppet
# # Matching resource types
# defined("file")
# defined("customtype")
#
# # Matching defines and classes
# defined("foo")
# defined("foo::bar")
#
# # Matching variables (note the single quotes)
# defined('$name')
#
# # Matching declared resources
# defined(File['/tmp/file'])
# ```
#
# Puppet depends on the configuration's evaluation order when checking whether a resource
# is declared.
#
# @example Importance of evaluation order when using `defined`
#
# ```puppet
# # Assign values to $is_defined_before and $is_defined_after using identical `defined`
# # functions.
#
# $is_defined_before = defined(File['/tmp/file'])
#
# file { "/tmp/file":
#   ensure => present,
# }
#
# $is_defined_after = defined(File['/tmp/file'])
#
# # $is_defined_before returns false, but $is_defined_after returns true.
# ```
#
# This order requirement only refers to evaluation order. The order of resources in the
# configuration graph (e.g. with `before` or `require`) does not affect the `defined`
# function's behavior.
#
# > **Warning:** Avoid relying on the result of the `defined` function in modules, as you
# > might not be able to guarantee the evaluation order well enough to produce consistent
# > results. This can cause other code that relies on the function's result to behave
# > inconsistently or fail.
#
# If you pass more than one argument to `defined`, the function returns `true` if _any_
# of the arguments are defined. You can also match resources by type, allowing you to
# match conditions of different levels of specificity, such as whether a specific resource
# is of a specific data type.
#
# @example Matching multiple resources and resources by different types with `defined`
#
# ```puppet
# file { "/tmp/file1":
#   ensure => file,
# }
#
# $tmp_file = file { "/tmp/file2":
#   ensure => file,
# }
#
# # Each of these statements return `true` ...
# defined(File['/tmp/file1'])
# defined(File['/tmp/file1'],File['/tmp/file2'])
# defined(File['/tmp/file1'],File['/tmp/file2'],File['/tmp/file3'])
# # ... but this returns `false`.
# defined(File['/tmp/file3'])
#
# # Each of these statements returns `true` ...
# defined(Type[Resource['file','/tmp/file2']])
# defined(Resource['file','/tmp/file2'])
# defined(File['/tmp/file2'])
# defined('$tmp_file')
# # ... but each of these returns `false`.
# defined(Type[Resource['exec','/tmp/file2']])
# defined(Resource['exec','/tmp/file2'])
# defined(File['/tmp/file3'])
# defined('$tmp_file2')
# ```
#
# @since 2.7.0
# @since 3.6.0 variable reference and future parser types
# @since 3.8.1 type specific requests with future parser
# @since 4.0.0
#
Puppet::Functions.create_function(:defined, Puppet::Functions::InternalFunction) do
  dispatch :is_defined do
    scope_param
    required_repeated_param 'Variant[String, Type[CatalogEntry], Type[Type[CatalogEntry]]]', :vals
  end

  def is_defined(scope, *vals) # rubocop:disable Naming/PredicateName
    vals.any? do |val|
      case val
      when String
        if val =~ /^\$(.+)$/
          scope.exist?(Regexp.last_match(1))
        else
          case val
          when ''
            next nil
          when 'main'
            # Find the main class (known as ''), it does not have to be in the catalog
            Puppet::Pops::Evaluator::Runtime3ResourceSupport.find_main_class(scope)
          else
            # Find a resource type, definition or class definition
            Puppet::Pops::Evaluator::Runtime3ResourceSupport.find_resource_type_or_class(scope, val)
          end
        end
      when Puppet::Resource
        # Find instance of given resource type and title that is in the catalog
        scope.compiler.findresource(val.resource_type, val.title)

      when Puppet::Pops::Types::PResourceType
        raise ArgumentError, _('The given resource type is a reference to all kind of types') if val.type_name.nil?

        type = Puppet::Pops::Evaluator::Runtime3ResourceSupport.find_resource_type(scope, val.type_name)
        val.title.nil? ? type : scope.compiler.findresource(type, val.title)

      when Puppet::Pops::Types::PClassType
        raise ArgumentError, _('The given class type is a reference to all classes') if val.class_name.nil?

        scope.compiler.findresource(:class, val.class_name)

      when Puppet::Pops::Types::PTypeType
        case val.type
        when Puppet::Pops::Types::PResourceType
          # It is most reasonable to take Type[File] and Type[File[foo]] to mean the same as if not wrapped in a Type
          # Since the difference between File and File[foo] already captures the distinction of type vs instance.
          is_defined(scope, val.type)

        when Puppet::Pops::Types::PClassType
          # Interpreted as asking if a class (and nothing else) is defined without having to be included in the catalog
          # (this is the same as asking for just the class' name, but with the added certainty that it cannot be a defined type.
          #
          raise ArgumentError, _('The given class type is a reference to all classes') if val.type.class_name.nil?

          Puppet::Pops::Evaluator::Runtime3ResourceSupport.find_hostclass(scope, val.type.class_name)
        end
      else
        raise ArgumentError, _("Invalid argument of type '%{value_class}' to 'defined'") % { value_class: val.class }
      end
    end
  end
end