Skip to content

Commit

Permalink
Modernise has_interface_with function
Browse files Browse the repository at this point in the history
Convert the function to the modern function API as a namespaced function
and use the `networking` fact instead of legacy facts.

A non-namespaced shim is also created (but marked deprecated), to
preserve compatibility.
  • Loading branch information
alexjfisher committed Apr 25, 2023
1 parent 39f1e80 commit 1dd922d
Show file tree
Hide file tree
Showing 4 changed files with 258 additions and 14 deletions.
78 changes: 78 additions & 0 deletions REFERENCE.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ environment.
* [`glob`](#glob): Uses same patterns as Dir#glob.
* [`grep`](#grep): This function searches through an array and returns any elements that match
the provided regular expression.
* [`has_interface_with`](#has_interface_with): DEPRECATED. Use the namespaced function [`stdlib::has_interface_with`](#stdlibhas_interface_with) instead.
* [`has_interface_with`](#has_interface_with): Returns boolean based on kind and value.
* [`has_ip_address`](#has_ip_address): Returns true if the client has the requested IP address on some interface.
* [`has_ip_network`](#has_ip_network): Returns true if the client has an IP address within the requested network.
Expand Down Expand Up @@ -179,6 +180,7 @@ the provided regular expression.
* [`stdlib::ensure`](#stdlib--ensure): function to cast ensure parameter to resource specific value
* [`stdlib::extname`](#stdlib--extname): Returns the Extension (the Portion of Filename in Path starting from the
last Period).
* [`stdlib::has_interface_with`](#stdlib--has_interface_with): Returns boolean based on network interfaces present and their attribute values.
* [`stdlib::ip_in_range`](#stdlib--ip_in_range): Returns true if the ipaddress is within the given CIDRs
* [`stdlib::sha256`](#stdlib--sha256): Run a SHA256 calculation against a given value.
* [`stdlib::start_with`](#stdlib--start_with): Returns true if str starts with one of the prefixes given. Each of the prefixes should be a String.
Expand Down Expand Up @@ -2584,6 +2586,24 @@ Returns: `Any` array of elements that match the provided regular expression.
grep(['aaa','bbb','ccc','aaaddd'], 'aaa') # Returns ['aaa','aaaddd']
```

### <a name="has_interface_with"></a>`has_interface_with`

Type: Ruby 4.x API

DEPRECATED. Use the namespaced function [`stdlib::has_interface_with`](#stdlibhas_interface_with) instead.

#### `has_interface_with(Any *$args)`

The has_interface_with function.

Returns: `Any`

##### `*args`

Data type: `Any`



### <a name="has_interface_with"></a>`has_interface_with`

Type: Ruby 3.x API
Expand Down Expand Up @@ -4881,6 +4901,64 @@ Data type: `String`

The Filename

### <a name="stdlib--has_interface_with"></a>`stdlib::has_interface_with`

Type: Ruby 4.x API

Can be called with one, or two arguments.

#### `stdlib::has_interface_with(String[1] $interface)`

The stdlib::has_interface_with function.

Returns: `Boolean` Returns `true` if `interface` exists and `false` otherwise

##### Examples

###### When called with a single argument, the presence of the interface is checked

```puppet
stdlib::has_interface_with('lo') # Returns `true`
```

##### `interface`

Data type: `String[1]`

The name of an interface

#### `stdlib::has_interface_with(Enum['macaddress','netmask','ipaddress','network','ip','mac'] $kind, String[1] $value)`

The stdlib::has_interface_with function.

Returns: `Boolean` Returns `true` if any of the interfaces in the `networking` fact has a `kind` attribute with the value `value`. Otherwise returns `false`

##### Examples

###### Checking if an interface exists with a given mac address

```puppet
stdlib::has_interface_with('macaddress', 'x:x:x:x:x:x') # Returns `false`
```

###### Checking if an interface exists with a given IP address

```puppet
stdlib::has_interface_with('ipaddress', '127.0.0.1') # Returns `true`
```

##### `kind`

Data type: `Enum['macaddress','netmask','ipaddress','network','ip','mac']`

A supported interface attribute

##### `value`

Data type: `String[1]`

The value of the attribute

### <a name="stdlib--ip_in_range"></a>`stdlib::ip_in_range`

Type: Ruby 4.x API
Expand Down
12 changes: 12 additions & 0 deletions lib/puppet/functions/has_interface_with.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# frozen_string_literal: true

# @summary DEPRECATED. Use the namespaced function [`stdlib::has_interface_with`](#stdlibhas_interface_with) instead.
Puppet::Functions.create_function(:has_interface_with) do
dispatch :deprecation_gen do
repeated_param 'Any', :args
end
def deprecation_gen(*args)
call_function('deprecation', 'has_interface_with', 'This method is deprecated, please use stdlib::has_interface_with instead.')
call_function('stdlib::has_interface_with', *args)
end
end
48 changes: 48 additions & 0 deletions lib/puppet/functions/stdlib/has_interface_with.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# frozen_string_literal: true

# @summary Returns boolean based on network interfaces present and their attribute values.
#
# Can be called with one, or two arguments.
Puppet::Functions.create_function(:'stdlib::has_interface_with') do
# @param interface
# The name of an interface
# @return [Boolean] Returns `true` if `interface` exists and `false` otherwise
# @example When called with a single argument, the presence of the interface is checked
# stdlib::has_interface_with('lo') # Returns `true`
dispatch :has_interface do
param 'String[1]', :interface
return_type 'Boolean'
end

# @param kind
# A supported interface attribute
# @param value
# The value of the attribute
# @return [Boolean] Returns `true` if any of the interfaces in the `networking` fact has a `kind` attribute with the value `value`. Otherwise returns `false`
# @example Checking if an interface exists with a given mac address
# stdlib::has_interface_with('macaddress', 'x:x:x:x:x:x') # Returns `false`
# @example Checking if an interface exists with a given IP address
# stdlib::has_interface_with('ipaddress', '127.0.0.1') # Returns `true`
dispatch :has_interface_with do
param "Enum['macaddress','netmask','ipaddress','network','ip','mac']", :kind
param 'String[1]', :value
return_type 'Boolean'
end

def has_interface(interface) # rubocop:disable Naming/PredicateName
interfaces.key? interface
end

def has_interface_with(kind, value) # rubocop:disable Naming/PredicateName
# For compatibility with older version of function that used the legacy facts, alias `ip` with `ipaddress` and `mac` with `macaddress`
kind = 'ip' if kind == 'ipaddress'
kind = 'mac' if kind == 'macaddress'

interface = interfaces.find { |_interface, params| params[kind] == value }
!interface.nil?
end

def interfaces
closure_scope['facts']['networking']['interfaces']
end
end
134 changes: 120 additions & 14 deletions spec/functions/has_interface_with_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,49 @@

describe 'has_interface_with' do
it { is_expected.not_to eq(nil) }
it { is_expected.to run.with_params.and_raise_error(Puppet::ParseError, %r{wrong number of arguments}i) }
it { is_expected.to run.with_params('one', 'two', 'three').and_raise_error(Puppet::ParseError, %r{wrong number of arguments}i) }
it { is_expected.to run.with_params.and_raise_error(ArgumentError, %r{expects between 1 and 2 arguments, got none}) }
it { is_expected.to run.with_params('one', 'two', 'three').and_raise_error(ArgumentError, %r{expects between 1 and 2 arguments, got 3}) }

# We need to mock out the Facts so we can specify how we expect this function
# to behave on different platforms.
context 'when on Mac OS X Systems' do
let(:facts) { { interfaces: 'lo0,gif0,stf0,en1,p2p0,fw0,en0,vmnet1,vmnet8,utun0' } }
let(:facts) do
{
'networking' => {
'interfaces' => {
'lo0' => {
'bindings' => [
{
'address' => '127.0.0.1',
'netmask' => '255.0.0.0',
'network' => '127.0.0.0'
},
],
"bindings6": [
{
'address' => '::1',
'netmask' => 'ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff',
'network' => '::1'
},
{
'address' => 'fe80::1',
'netmask' => 'ffff:ffff:ffff:ffff::',
'network' => 'fe80::'
},
],
'ip' => '127.0.0.1',
'ip6' => '::1',
'mtu' => 16_384,
'netmask' => '255.0.0.0',
'netmask6' => 'ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff',
'network' => '127.0.0.0',
'network6' => '::1',
'scope6' => 'host'
},
}
}
}
end

it { is_expected.to run.with_params('lo0').and_return(true) }
it { is_expected.to run.with_params('lo').and_return(false) }
Expand All @@ -19,23 +55,93 @@
context 'when on Linux Systems' do
let(:facts) do
{
interfaces: 'eth0,lo',
ipaddress: '10.0.0.1',
ipaddress_lo: '127.0.0.1',
ipaddress_eth0: '10.0.0.1',
muppet: 'kermit',
muppet_lo: 'mspiggy',
muppet_eth0: 'kermit',
'networking' => {
'interfaces' => {
'eth0' => {
'bindings' => [
{
'address' => '10.0.2.15',
'netmask' => '255.255.255.0',
'network' => '10.0.2.0'
},
],
'bindings6' => [
{
'address' => 'fe80::5054:ff:fe8a:fee6',
'netmask' => 'ffff:ffff:ffff:ffff::',
'network' => 'fe80::'
},
],
'dhcp' => '10.0.2.2',
'ip' => '10.0.2.15',
'ip6' => 'fe80::5054:ff:fe8a:fee6',
'mac' => '52:54:00:8a:fe:e6',
'mtu' => 1500,
'netmask' => '255.255.255.0',
'netmask6' => 'ffff:ffff:ffff:ffff::',
'network' => '10.0.2.0',
'network6' => 'fe80::'
},
'eth1' => {
'bindings' => [
{
'address' => '10.0.0.2',
'netmask' => '255.255.255.0',
'network' => '10.0.0.0'
},
],
'bindings6' => [
{
'address' => 'fe80::a00:27ff:fed1:d28c',
'netmask' => 'ffff:ffff:ffff:ffff::',
'network' => 'fe80::'
},
],
'ip' => '10.0.0.2',
'ip6' => 'fe80::a00:27ff:fed1:d28c',
'mac' => '08:00:27:d1:d2:8c',
'mtu' => 1500,
'netmask' => '255.255.255.0',
'netmask6' => 'ffff:ffff:ffff:ffff::',
'network' => '10.0.0.0',
'network6' => 'fe80::'
},
'lo' => {
'bindings' => [
{
'address' => '127.0.0.1',
'netmask' => '255.0.0.0',
'network' => '127.0.0.0'
},
],
'bindings6' => [
{
'address' => '::1',
'netmask' => 'ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff',
'network' => '::1'
},
],
'ip' => '127.0.0.1',
'ip6' => '::1',
'mtu' => 65_536,
'netmask' => '255.0.0.0',
'netmask6' => 'ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff',
'network' => '127.0.0.0',
'network6' => '::1'
}
},
},
}
end

it { is_expected.to run.with_params('lo').and_return(true) }
it { is_expected.to run.with_params('lo0').and_return(false) }
it { is_expected.to run.with_params('ipaddress', '127.0.0.1').and_return(true) }
it { is_expected.to run.with_params('ipaddress', '10.0.0.1').and_return(true) }
it { is_expected.to run.with_params('ipaddress', '10.0.0.2').and_return(true) }
it { is_expected.to run.with_params('ipaddress', '8.8.8.8').and_return(false) }
it { is_expected.to run.with_params('muppet', 'kermit').and_return(true) }
it { is_expected.to run.with_params('muppet', 'mspiggy').and_return(true) }
it { is_expected.to run.with_params('muppet', 'bigbird').and_return(false) }
it { is_expected.to run.with_params('netmask', '255.255.255.0').and_return(true) }
it { is_expected.to run.with_params('macaddress', '52:54:00:8a:fe:e6').and_return(true) }
it { is_expected.to run.with_params('network', '42.0.0.0').and_return(false) }
it { is_expected.to run.with_params('network', '10.0.0.0').and_return(true) }
end
end

0 comments on commit 1dd922d

Please sign in to comment.