Skip to content

Commit

Permalink
INFRA-3218 Initial commit of ip_compare()
Browse files Browse the repository at this point in the history
Initial commit of implementation and tests for ip_compare(), designed
for use with Puppet's `sort()` to sort a series of IP addresses into
binary-numerical order, regardless of the human-readable representation
in which they were originally written.
  • Loading branch information
greatflyingsteve committed Apr 9, 2024
1 parent 56bd874 commit ed615a9
Show file tree
Hide file tree
Showing 3 changed files with 81 additions and 1 deletion.
2 changes: 1 addition & 1 deletion .fixtures.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@
---
fixtures:
forge_modules:
# stdlib: "puppetlabs/stdlib"
stdlib: "puppetlabs/stdlib"
67 changes: 67 additions & 0 deletions lib/puppet/functions/ipcalc/ip_compare.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# frozen_string_literal: true

require 'ipaddr'

Puppet::Functions.create_function(:"ipcalc::ip_compare") do
# Compare two IP addresses and return a `sort()`-compatible Integer comparison result. This will
# work with either plain addresses or CIDR-notation addresses; if a plain address is supplied, a
# full-width netmask is assumed. That is, '127.0.0.1' and '127.0.0.1/32' are equivalent, just as
# 'fe80::1' and 'fe80::1/128' are equivalent. The netmask IS assessed in the comparison, but is
# only relevant if all bits of the address are equivalent.
# @param left
# The left address for comparison. If no netmask is given, a full-width mask is assumed.
# @param right
# The right address for comparison. If no netmask is given, a full-width mask is assumed.
# @return [Integer[-1, 1]]
# Either 1, 0, or -1 if the left operand is larger than, equal to, or smaller than the right,
# respectively.
# @example Usage with `sort()`
# $my_addr_array.sort |$left, $right| {
# ipcalc::ip_compare($left, $right)
# }
dispatch :ip_compare do
param 'Stdlib::IP::Address::V4', :left
param 'Stdlib::IP::Address::V4', :right
return_type 'Integer[-1, 1]'
end

dispatch :ip_compare do
param 'Stdlib::IP::Address::V6', :left
param 'Stdlib::IP::Address::V6', :right
return_type 'Integer[-1, 1]'
end

# We refuse to handle addresses in different families. There isn't a clear precedence between
# them, and if needed, any relative ordering required can be handled in your Puppet code by
# assessing the types handed to `sort()`. There are examples of this in the documentation for the
# Puppet `sort()` function, but a specific example for sorting all IPv6 addresses before all IPv4
# address is included for reference.
# @example Usage with `sort()` where IPv6 sorts above IPv4
# $my_ip_array.sort |$left, $right| {
# case [$left, $right] {
# [Stdlib::IP::Address::V6, Stdlib::IP::Address::V4]: { 1 }
# [Stdlib::IP::Address::V4, Stdlib::IP::Address::V6]: { -1 }
# default: { ipcalc::ip_compare($left, $right) }
# }
# }
argument_mismatch :mixed_families do
param 'Stdlib::IP::Address::V4', :left
param 'Stdlib::IP::Address::V6', :right
end

argument_mismatch :mixed_families do
param 'Stdlib::IP::Address::V6', :left
param 'Stdlib::IP::Address::V4', :right
end

def ip_compare(left, right)
left_addr = IPAddr.new(left)
right_addr = IPAddr.new(right)

left_addr <=> right_addr
end

def mixed_families(*)
'both addresses must be in the same family'
end
end
13 changes: 13 additions & 0 deletions spec/functions/ip_compare_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# frozen_string_literal: true

require 'spec_helper'

describe 'ipcalc::ip_compare' do
it { is_expected.to run.with_params('127.0.0.1', '127.0.0.2').and_return(-1) }
it { is_expected.to run.with_params('127.0.0.2', '127.0.0.1').and_return(1) }
it { is_expected.to run.with_params('fe80::1', 'fe80::2').and_return(-1) }
it { is_expected.to run.with_params('fe80::2', 'fe80::1').and_return(1) }
it { is_expected.to run.with_params(nil).and_raise_error(StandardError) }
it { is_expected.to run.with_params('127.0.0.1', 'fe80::1').and_raise_error(StandardError) }
it { is_expected.to run.with_params('fe80::1', '127.0.0.1').and_raise_error(StandardError) }
end

0 comments on commit ed615a9

Please sign in to comment.