Uploaded image for project: 'Puppet'
  1. Puppet
  2. PUP-4669

Varargs support for "args by name" calls to user defined resource types and EPP templates

    XMLWordPrintable

    Details

    • Type: Improvement
    • Status: Closed
    • Priority: Normal
    • Resolution: Won't Do
    • Affects Version/s: None
    • Fix Version/s: None
    • Component/s: Compiler
    • Labels:
      None
    • Template:
    • Team:
      Coremunity
    • Story Points:
      3

      Description

      UPDATED


      For advanced use cases when defining resources e.g. a resource that delegates to one or more other resources it is of great value
      to be able to capture parameter key-values instead of having to wrap such in a Hash since that makes it impossible to use the new resource as a drop in replacement for what it delegates to without having to update all logic where the replacement should be used (see examples below in comments). (There are also other variations on this theme).

      Add support for varargs/captures in user defined resources by allowing the same syntax that is used for functions to specify that extra parameters are captured into a variable. For functions this is an Array, and for "args by name" calls, this is a Hash.

      define foo($a, $b, *$captured) {
      }
      

      In the body the variable $capture is always a Hash, it may be empty, or contain any keys the user has given. To restrict the keys, the user can type the parameter with a Hash[String, <wanted-data-type>, min, max], or a Struct. When using a Struct the keys can be pattern based (as defined in PUP-5942) and it is thus possible to type different pattern based parameters to different types.

      define foo($a, $b, Struct[{Patter[/prefix_[a-z]+/] => Integer}], *$captured) {
      }
      

      See PUP-5942 for how to constraint the number of keys (from min required to max allowed).

      The above definition of foo allows this:

      foo { 'example':
        prefix_abc => 1,
        prefix_bcd => 2,
      }
      

      • The same varargs support should also be added to EPP templates since they use "args by name".
      • This should not be added to classes (there is no way to do data binding, and parameters are part of the class' API and they are externally addressable).

      A concern has been raised that adding "varargs" support is less declarative, but that is only a matter of perception - something that is defined by a pattern is just as specified as a concrete specification when this is side effect free. What we cannot accept though is that the API of something changes depending on how it is used - this means that varargs support cannot be added to classes unless the expansion of the varargs hash into individual variables is made such that those variables are private to the class body thus making it impossible to refer to its individual matching parameters from outside of the class body. The captured hash itself could be made public. Support for varargs in classes is therefore not included in this ticket. If wanted a new ticket should be opened, and that ticket should depend on the ability to have private variables in class scopes.

      ORIGINAL


      Through roles and profiles, a pattern started to emerge where we write a wrapper define, for convenience, for better naming, both, or other reasons.

      Example:

      define http2https_vhost (
        $servername    = $name,
        $serveraliases = undef,
      ) {
        apache::vhost ( "http-${servername}":
          servername      => $servername,
          serveraliases   => $serveraliases,
          redirect_status => '301',
          redirect_dest   => "https://{servername}/",
        }
       
        apache::vhost ( "https-${servername}":
          servername => $servername,
          ssl_ca     => "/etc/ssl/certs/${servername}s.ca.pem",
          ssl_cert   => "/etc/ssl/certs/${servername}.crt.pem",
          ssl_key    => "/etc/ssl/private/${servername}.key.pem",
        }
      }
      

      This is nice and convenient for the most simple of cases. If we need more options from apache::vhost, we have to extend our wrapper profile.
      Similarly, if apache::vhost changes, and new admins want to use those new options, we (or they!) have to touch our convenience wrappers.

      We could do a gruesome hack, such as:

      define http2https_vhost (
        $servername    = $name,
        $serveraliases = undef,
        $options       = {},
      ) {
        apache::vhost ( "http-${servername}":
          servername      => $servername,
          serveraliases   => $serveraliases,
          redirect_status => '301',
          redirect_dest   => "https://{servername}/",
        }
       
        create_resources('apache::vhost', { "https-${servername}" => $options}, {
          servername => $servername,
          ssl_ca     => "/etc/ssl/certs/${servername}s.ca.pem",
          ssl_cert   => "/etc/ssl/certs/${servername}.crt.pem",
          ssl_key    => "/etc/ssl/private/${servername}.key.pem",
        })
      

      but the additional indirection through $options is hard to follow, and with more complex wrappers it's also hard to know where our options start, and where their options end… what is a default, what's being overwritten, etc…

      I propose we introduce a syntax which helps us create such, or even more powerful wrappers, analogous to clojure's &rest:

      define http2https_vhost (
        $servername    = $name,
        $serveraliases = undef,
        &rest,
      ) {
        apache::vhost ( "http-${servername}":
          servername      => $servername,
          serveraliases   => $serveraliases,
          redirect_status => '301',
          redirect_dest   => "https://{servername}/",
        }
       
        apache::vhost ( "https-${servername}":
          servername => $servername,
          docroot    => "/srv/web/${servername}/htdocs",
          ssl_ca     => "/etc/ssl/certs/${servername}s.ca.pem",
          ssl_cert   => "/etc/ssl/certs/${servername}.crt.pem",
          ssl_key    => "/etc/ssl/private/${servername}.key.pem",
          &rest,
        }
      }
      

      Example use:

      http2https_vhost { 'blag.esotericsystems.at':
        fallbackresource => '/index.php'
      }
      

      This passes fallbackresource down to the second apache::vhost.

      It might be useful to filter for specific prefixes,

      define tc_and_apache_vhost (
        $servername    = $name,
        $port          = 8080,
        &tomcat_,
        &apache_,
      ) {
       
        apache::vhost ( "https-${servername}":
          servername => $servername,
          ssl_ca     => "/etc/ssl/certs/${servername}s.ca.pem",
          ssl_cert   => "/etc/ssl/certs/${servername}.crt.pem",
          ssl_key    => "/etc/ssl/private/${servername}.key.pem",
          proxy_pass => '/',
          proxy_dest => "http://localhost:${port}/"
          &apache_,
        }
       
        tomcat::instance ( "tc-${servername}":
          application => $servername,
          proxy      => "https://servername:443/"
          &tomcat_,
        }
      }
      

      The idea here is that all parameters passed to a tc_and_apache_vhost that start with apache_ would be routed into the apache::vhost, and all parameters that start with tomcat_ would be routed to tomcat::instance.

      I'm not sure yet, if they should be stripped of their prefix before being routed, or not.

        Attachments

          Issue Links

            Activity

              People

              Assignee:
              Unassigned
              Reporter:
              igalic Igor Galić
              QA Contact:
              Kurt Wall
              Votes:
              2 Vote for this issue
              Watchers:
              9 Start watching this issue

                Dates

                Created:
                Updated:
                Resolved:

                  Zendesk Support