1

Given a data structure like

$local_users => {
  "user" => {
    "ssh" => {
      "config_entries" => [
        { "host" => "dummyhost",
          "lines" => [
            "ProxyCommand /usr/bin/corkscrew proxy.example.net 8080 %h %p"
          ]
        }
      ]
    }
  }
}

I've put together a couple of reduce calls, but not really sure if there is a more effective way to determine if there is an element matching some condition. I think this will at least this will start skipping subsequent items if a match is found, but seems a little clunky doing 3 reduce calls to extract something nested this deeply and wondering if there are any better patterns in puppet for extracting data to determine if something is required or not.

$require_corkscrew = $local_users.reduce(false) |$memo, $user| {
  $memo or dig44($user[1], ['ssh', 'config_entries'], []).reduce |$memo, $entry| {
    $memo or $entry['lines'].reduce |$memo, $line| {
      $memo or $line.match(/ProxyCommand.*corkscrew/)
    }
  }
}

if $require_corkscrew {
  $corkscrew_ensure = 'present'
} else {
  $corkscrew_ensure = 'absent'
}

package {'corkscrew':
  ensure => $corkscrew_ensure,
}
dbailey
  • 1,417
  • 1
  • 10
  • 16
  • I posted an answer about solving this with `dig`, but I now realize that will not help since 1. there will be an `n` number of hashes in the `config_entries` array and 2. `match` can only be invoked on `String` types and not `undef`. – Matthew Schuchard Mar 07 '19 at 19:00

1 Answers1

1

wondering if there are any better patterns in puppet for extracting data to determine if something is required or not.

There are some possible improvements around how you structure your data:

  • consider avoiding such deep nesting
  • consider avoiding optional keys in your hashes, especially in middle layers
  • consider minimizing use of arrays of hashes, as there's generally no way to process these other than by iterating over them.
  • ditto for use of hashes with uncontrolled key spaces
  • do use Puppet data types to both document and enforce the data structure you choose

As for computational patterns,

  • consider using the any() function when analyzing a collection to compute a boolean property, as this gets you bona fide short circuiting.

  • don't overlook the keys() and values() functions for analyzing hashes, for they can at least reduce your code's cognitive load when processing complex data structures.

  • consider using functions and function variations that operate directly collections instead of iterating collections and using scalar functions on the elements. For example, match() works in a useful way on arrays.

Here's an approach that I like a little better than your original code. Instead of nested reductions, it uses nested any() computations and the array version of the match function. It relies on the fact that undef is falsey, and uses dig() and then() to deal with optional hash keys. Overall, I think that's clearer and a little lighter weight, but there's only so much you can do to achieve simple code for analyzing complex data.

$require_corkscrew = $local_users.values.any |$user| {
  $user.dig('ssh', 'config_entries').then |$entries| {
    $entries.any |$entry| {
      $entry.dig('lines').then |$lines| {
        ! empty($lines.match(/ProxyCommand.*corkscrew/))
      }
    }
  }
}

It would be possible to replace the array-wise match() with another any() wrapped around a scalar match(), but although it has the potential to short-circuit a little earlier in an element-wise sense, that must be weighed against the (likely) efficiency improvement from reducing the number of function calls, and of iterating inside the function instead of at the DSL level.

John Bollinger
  • 160,171
  • 8
  • 81
  • 157
  • That's very handy, wasn't aware of the `then` function, that makes a big difference, certainly should have been more aware of the `any` function but didn't quite click on how to apply it. – dbailey Mar 08 '19 at 10:50
  • The problem is that even builtin facts are nested so you can't really avoid nesting. `.dig` makes handling them reasonable but still, Puppet aint that great when dealing with complex structures – XANi Aug 13 '20 at 14:50