Skip to content

Modernise has_interface_with function #1326

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Apr 27, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
47 changes: 47 additions & 0 deletions lib/puppet/functions/stdlib/has_interface_with.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# 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'

interfaces.any? { |_interface, params| params[kind] == value }
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