Skip to content
Snippets Groups Projects
validate.pp 7.34 KiB
Newer Older
Alex Tayts's avatar
Alex Tayts committed
# @summary
#   Validate the server state based on predefined checks.
#
# The class generates or removes the validation script for the
# state of the server. The resulting script can be automatically
# invoked by AWS SSM after unattended patching.
#
# A hiera example:
#
# ```yaml
# server_patching::validate::ensure: present
# server_patching::validate::services:
#   - name: open-vm-tools.service
#     active: true
#   - name: openipmi.service
#     active: false
# server_patching::validate::processes:
#   - name: falcond
#     running: true
# server_patching::validate::urls:
#   - url: https://netdb.stanford.edu/status-3654hkfjd7fhbd
#     status: 200
#   - url: https://netdb.stanford.edu
#     status: 302
# server_patching::validate::ports:
#   - port: 22
#     proto: tcp
#     ip_ver: ipv4
#     listening: true
#   - port: 23
#     proto: tcp
#     ip_ver: ipv4
#     listening: false
# server_patching::validate::mounts:
#   - /home
#   - /mnt/data
# server_patching::validate::exports:
#   - /share/raw_data
#   - /share/processed_data
# server_patching::validate::zfs_pools:
#   - pool1
#   - pool2
# ```
#
# @param ensure
#   Deploy or remove the validation script.
#
# @param validation_script
#   Location of the validation script on the system.
#
# @param use_remctl
#   Whether to create a remctl for validation script or not.
#
# @param services
#   Array of hashes, where each hash is describing a service and
#   its desired state.
#
# @option services [String] :name
#   The name of the service to check.
#
# @option services [Boolean] :active
#   Desired state of the service: `true` if the service should be running or
#   `false` if the service should be stopped, disabled, masked or really failed.
#   Defaults to `true`.
#
# @param processes
#   Array of hashes, where each hash describes the name of a process
#   and whether it is supposed to be running.
#
# @option processes [String] :name
#   The name of the process to check.
#
# @option processes [Boolean] :running
#   The desired state of the process, `true` if the process should be running
#   or `false` if it is not supposed to. Defaults to `true`.
#
# @option processes [Boolean] :command
#   Search string for a command line arguments of a process.
#
Alex Tayts's avatar
Alex Tayts committed
# @param urls
#   Array of hashes, where each hash describes the web URL
#   and the HTTP status code it is supposed to return
#
# @option urls [Stdlib::HTTPUrl] :url
#   The HTTP URL to check.
#
# @option urls [Integer] :status
#   The HTTP status code :url is expected to return. Defaults to `200`.
#
# @option urls [Integer] :resolve_to
#   Force the domain specified in a URL to resolve to this IP address.
#
Alex Tayts's avatar
Alex Tayts committed
# @param ports
#   Array of hashes, where each hash contains a port number, protocol,
#   IP protocol version (IPv4/IPv6) and whether anything is expected
#   to be listening on it.
#
# @option ports [Stdlib::Port] :port
#   Port number to check.
#
# @option ports [Enum['tcp','udp']] :proto
#   Protocol TCP or UDP which is to be used for the check. Defaults to `tcp`.
#
# @option ports [Enum['ipv4','ipv6']] :ip_ver
#   Version of IP protocol to use for the check, IPv4 or IPv6. Defaults to `ipv4`.
#
# @option ports [Boolean] :listenting
#   `true`, if something is expected to listed on this port or `false` if
#   the port is not supposed to respond. Defaults to `true`.
#
# @param mounts
#   Array of mounts to be checked for presence.
#
# @param exports
#   Array of NFS exports to be checked for presence. Only local exports are checked.
#
# @param zfs_pools
#   Array of ZFS pools to be checked for presence.
#
# @example
#   include server_patching::validate
#
class server_patching::validate (
  Enum['absent','present'] $ensure = 'present',
  Stdlib::Unixpath $validation_script = '/usr/local/bin/validate.sh',
  Array[Struct[{
    'name'       => String,
    'active'     => Optional[Boolean]}]] $services = [],
Alex Tayts's avatar
Alex Tayts committed
  Array[Struct[{
    'name'       => String,
    'command'    => Optional[String],
    'running'    => Optional[Boolean]}]] $processes = [],
Alex Tayts's avatar
Alex Tayts committed
  Array[Struct[{
    'url'        => Stdlib::HTTPUrl,
    'resolve_to' => Optional[Stdlib::IP::Address],
    'status'     => Optional[Integer[100,510]]}]] $urls = [],
Alex Tayts's avatar
Alex Tayts committed
  Array[Struct[{
    'port'       => Stdlib::Port,
    'proto'      => Optional[Enum['tcp','udp']],
    'ip_ver'     => Optional[Enum['ipv4','ipv6']],
    'listening'  => Optional[Boolean]}]] $ports = [],
Alex Tayts's avatar
Alex Tayts committed
  Array[Stdlib::Unixpath] $mounts = [],
  Array[Stdlib::Unixpath] $exports = [],
  Array[String] $zfs_pools = [],
) {

  if [$services, $processes, $urls, $ports, $mounts, $exports, $zfs_pools].any |$param| { !empty($param) } {
    concat { $validation_script:
      ensure => $ensure,
      mode   => '0700',
    }

    concat::fragment { 'bash-header':
      target  => $validation_script,
      order   => '01',
      content => epp('server_patching/validate/header.epp'),
    }

    concat::fragment { 'bash-footer':
      target  => $validation_script,
      order   => '50',
      content => epp('server_patching/validate/footer.epp'),
    }

    if !empty($services) {
      # By default we want services running
      $_services = $services.map |$service| {
        { 'active' => true } + $service
      }

      concat::fragment { 'validate-services':
        target  => $validation_script,
        order   => '01',
        content => epp('server_patching/validate/service.epp', { 'services' => $_services }),
      }
    }

    if !empty($processes) {
      # By default we want processes running
      $_processes = $processes.map |$process| {
          { 'running' => true } + $process
      }

      concat::fragment { 'validate-processes':
        target  => $validation_script,
        order   => '02',
        content => epp('server_patching/validate/process.epp', { 'processes' => $_processes }),
      }
    }

    if !empty($urls) {
      # By default we expect URLs to return HTTP status code 200
      $_urls = $urls.map |$url| {
          { 'status' => 200 } + $url
      }

      concat::fragment { 'validate-urls':
        target  => $validation_script,
        order   => '03',
        content => epp('server_patching/validate/urls.epp', { 'urls' => $_urls }),
      }
    }

    if !empty($ports) {
      # By default we want to check TCP ports over IPv4 and expect them to respond.
      $_ports = $ports.map |$port| {
        { 'proto' => 'tcp', 'ip_ver' => 'ipv4', 'listening' => true } + $port
      }

      concat::fragment { 'validate-ports':
        target  => $validation_script,
        order   => '04',
        content => epp('server_patching/validate/ports.epp', { 'ports' => $_ports }),
      }
    }

    if !empty($mounts) {
      concat::fragment { 'validate-mounts':
        target  => $validation_script,
        order   => '05',
        content => epp('server_patching/validate/mount.epp', { 'mounts' => $mounts }),
      }
    }

    if !empty($exports) {
      concat::fragment { 'validate-exports':
        target  => $validation_script,
        order   => '06',
        content => epp('server_patching/validate/export.epp', { 'exports' => $exports }),
      }
    }

    if !empty($zfs_pools) {
      concat::fragment { 'validate-zpools':
        target  => $validation_script,
        order   => '07',
        content => epp('server_patching/validate/zpool.epp', { 'zfs_pools' => $zfs_pools }),
      }
    }
  } else {
    # If no checks are defined, no reason to keep an empty
    # validation script around.
    file { $validation_script: ensure => absent }
  }
}