diff --git a/manifests/config.pp b/manifests/config.pp index 7dcac89..ee03017 100644 --- a/manifests/config.pp +++ b/manifests/config.pp @@ -1,256 +1,252 @@ # This class exists to coordinate all configuration related actions, # functionality and logical units in a central place. # # It is not intended to be used directly by external resources like node # definitions or other modules. # # @example importing this class into other classes to use its functionality: # class { 'elasticsearch::config': } # # @author Richard Pijnenburg # @author Tyler Langlois # @author Gavin Williams # class elasticsearch::config { #### Configuration Exec { path => [ '/bin', '/usr/bin', '/usr/local/bin' ], cwd => '/', } $init_defaults = merge( { 'MAX_OPEN_FILES' => '65535', }, $elasticsearch::init_defaults ) if ( $elasticsearch::ensure == 'present' ) { file { $elasticsearch::homedir: ensure => 'directory', group => $elasticsearch::elasticsearch_group, owner => $elasticsearch::elasticsearch_user; $elasticsearch::configdir: ensure => 'directory', group => $elasticsearch::elasticsearch_group, owner => 'root', mode => '2750'; $elasticsearch::datadir: ensure => 'directory', group => $elasticsearch::elasticsearch_group, owner => $elasticsearch::elasticsearch_user; $elasticsearch::logdir: ensure => 'directory', group => $elasticsearch::elasticsearch_group, owner => $elasticsearch::elasticsearch_user, mode => '0750'; $elasticsearch::_plugindir: ensure => 'directory', group => $elasticsearch::elasticsearch_group, owner => $elasticsearch::elasticsearch_user, mode => 'o+Xr'; "${elasticsearch::homedir}/lib": ensure => 'directory', group => '0', owner => 'root', recurse => true; } if $elasticsearch::pid_dir { file { $elasticsearch::pid_dir: ensure => 'directory', group => undef, owner => $elasticsearch::elasticsearch_user, recurse => true, } if ($elasticsearch::service_provider == 'systemd') { $group = $elasticsearch::elasticsearch_group $user = $elasticsearch::elasticsearch_user $pid_dir = $elasticsearch::pid_dir file { '/usr/lib/tmpfiles.d/elasticsearch.conf': ensure => 'file', content => template("${module_name}/usr/lib/tmpfiles.d/elasticsearch.conf.erb"), group => '0', owner => 'root', } } } if $elasticsearch::defaults_location { augeas { "${elasticsearch::defaults_location}/elasticsearch": incl => "${elasticsearch::defaults_location}/elasticsearch", lens => 'Shellvars.lns', changes => [ 'rm CONF_FILE', 'rm CONF_DIR', 'rm ES_PATH_CONF', ], } file { "${elasticsearch::defaults_location}/elasticsearch": ensure => 'file', group => $elasticsearch::elasticsearch_group, owner => $elasticsearch::elasticsearch_user, mode => '0640'; } } # Defaults file, either from file source or from hash to augeas commands if ($elasticsearch::init_defaults_file != undef) { file { "${elasticsearch::defaults_location}/elasticsearch": ensure => $elasticsearch::ensure, source => $elasticsearch::init_defaults_file, owner => 'root', group => '0', mode => '0644', before => Service['elasticsearch'], notify => $elasticsearch::_notify_service, } } else { augeas { 'init_defaults': incl => "${elasticsearch::defaults_location}/elasticsearch", lens => 'Shellvars.lns', changes => template("${module_name}/etc/sysconfig/defaults.erb"), before => Service['elasticsearch'], notify => $elasticsearch::_notify_service, } } # Generate config file $_config = deep_implode($elasticsearch::config) # Generate SSL config if $elasticsearch::ssl { if ($elasticsearch::keystore_password == undef) { fail('keystore_password required') } if ($elasticsearch::keystore_path == undef) { $_keystore_path = "${elasticsearch::configdir}/elasticsearch.ks" } else { $_keystore_path = $elasticsearch::keystore_path } $_tls_config = { 'xpack.security.transport.ssl.enabled' => true, 'xpack.security.http.ssl.enabled' => true, 'xpack.ssl.keystore.path' => $_keystore_path, 'xpack.ssl.keystore.password' => $elasticsearch::keystore_password, } # Trust CA Certificate java_ks { 'elasticsearch_ca': ensure => 'latest', certificate => $elasticsearch::ca_certificate, target => $_keystore_path, password => $elasticsearch::keystore_password, trustcacerts => true, } # Load node certificate and private key java_ks { 'elasticsearch_node': ensure => 'latest', certificate => $elasticsearch::certificate, private_key => $elasticsearch::private_key, target => $_keystore_path, password => $elasticsearch::keystore_password, } } else { $_tls_config = {} } - # Logging file or hash - if ($elasticsearch::logging_file != undef) { - $_log4j_content = undef - } else { - if ($elasticsearch::logging_template != undef ) { - $_log4j_content = template($elasticsearch::logging_template) - } else { - $_log4j_content = template("${module_name}/etc/elasticsearch/log4j2.properties.erb") - } - $_logging_source = undef - } - file { - "${elasticsearch::configdir}/log4j2.properties": - ensure => file, - content => $_log4j_content, - source => $_logging_source, - mode => '0644', - notify => $elasticsearch::_notify_service, - require => Class['elasticsearch::package'], - before => Class['elasticsearch::service'], - } + # # Logging file or hash + # if ($elasticsearch::logging_file != undef) { + # $_log4j_content = undef + # } else { + # if ($elasticsearch::logging_template != undef ) { + # $_log4j_content = template($elasticsearch::logging_template) + # } else { + # $_log4j_content = template("${module_name}/etc/elasticsearch/log4j2.properties.erb") + # } + # $_logging_source = undef + # } + # file { + # "${elasticsearch::configdir}/log4j2.properties": + # ensure => file, + # content => $_log4j_content, + # source => $_logging_source, + # mode => '0644', + # notify => $elasticsearch::_notify_service, + # require => Class['elasticsearch::package'], + # before => Class['elasticsearch::service'], + # } # Generate Elasticsearch config $_es_config = merge( $elasticsearch::config, { 'path.data' => $elasticsearch::datadir }, { 'path.logs' => $elasticsearch::logdir }, $_tls_config ) datacat_fragment { 'main_config': target => "${elasticsearch::configdir}/elasticsearch.yml", data => $_es_config, } datacat { "${elasticsearch::configdir}/elasticsearch.yml": template => "${module_name}/etc/elasticsearch/elasticsearch.yml.erb", notify => $elasticsearch::_notify_service, require => Class['elasticsearch::package'], owner => $elasticsearch::elasticsearch_user, group => $elasticsearch::elasticsearch_group, mode => '0440', } # Add any additional JVM options $elasticsearch::jvm_options.each |String $jvm_option| { file_line { "jvm_option_${jvm_option}": ensure => present, path => "${elasticsearch::configdir}/jvm.options", line => $jvm_option, notify => $elasticsearch::_notify_service, } } if $elasticsearch::system_key != undef { file { "${elasticsearch::configdir}/system_key": ensure => 'file', source => $elasticsearch::system_key, mode => '0400', } } # Add secrets to keystore if $elasticsearch::secrets != undef { elasticsearch_keystore { 'elasticsearch_secrets': configdir => $elasticsearch::configdir, purge => $elasticsearch::purge_secrets, settings => $elasticsearch::secrets, notify => $::elaticsearch::_notify_service, } } } elsif ( $elasticsearch::ensure == 'absent' ) { file { $elasticsearch::_plugindir: ensure => 'absent', force => true, backup => false, } - file { "${elasticsearch::configdir}/jvm.options": - ensure => 'absent', - } - file { "${elasticsearch::defaults_location}/elasticsearch": ensure => 'absent', subscribe => Service['elasticsearch'], } } } diff --git a/manifests/init.pp b/manifests/init.pp index 6d991f9..6c88a23 100644 --- a/manifests/init.pp +++ b/manifests/init.pp @@ -1,606 +1,606 @@ # Top-level Elasticsearch class which may manage installation of the # Elasticsearch package, package repository, and other # global options and parameters. # # @summary Manages the installation of Elasticsearch and related options. # # @example install Elasticsearch # class { 'elasticsearch': } # # @example removal and decommissioning # class { 'elasticsearch': # ensure => 'absent', # } # # @example install everything but disable service(s) afterwards # class { 'elasticsearch': # status => 'disabled', # } # # @param ensure # Controls if the managed resources shall be `present` or `absent`. # If set to `absent`, the managed software packages will be uninstalled, and # any traces of the packages will be purged as well as possible, possibly # including existing configuration files. # System modifications (if any) will be reverted as well as possible (e.g. # removal of created users, services, changed log settings, and so on). # This is a destructive parameter and should be used with care. # # @param api_basic_auth_password # Defines the default REST basic auth password for API authentication. # # @param api_basic_auth_username # Defines the default REST basic auth username for API authentication. # # @param api_ca_file # Path to a CA file which will be used to validate server certs when # communicating with the Elasticsearch API over HTTPS. # # @param api_ca_path # Path to a directory with CA files which will be used to validate server # certs when communicating with the Elasticsearch API over HTTPS. # # @param api_host # Default host to use when accessing Elasticsearch APIs. # # @param api_port # Default port to use when accessing Elasticsearch APIs. # # @param api_protocol # Default protocol to use when accessing Elasticsearch APIs. # # @param api_timeout # Default timeout (in seconds) to use when accessing Elasticsearch APIs. # # @param autoupgrade # If set to `true`, any managed package will be upgraded on each Puppet run # when the package provider is able to find a newer version than the present # one. The exact behavior is provider dependent (see # {package, "upgradeable"}[http://j.mp/xbxmNP] in the Puppet documentation). # # @param ca_certificate -# Path to the trusted CA certificate to add to this node's java keystore. +# Path to the trusted CA certificate to add to this node's Java keystore. # # @param certificate # Path to the certificate for this node signed by the CA listed in # ca_certificate. # # @param config # Elasticsearch configuration hash. # # @param configdir # Directory containing the elasticsearch configuration. # Use this setting if your packages deviate from the norm (`/etc/elasticsearch`) # # @param configdir_recurselimit # Dictates how deeply the file copy recursion logic should descend when # copying files from the `configdir` to instance `configdir`s. # # @param daily_rolling_date_pattern # File pattern for the file appender log when file_rolling_type is 'dailyRollingFile'. # # @param datadir # Allows you to set the data directory of Elasticsearch. # # @param datadir_instance_directories # Control whether individual directories for instances will be created within # each instance's data directory. # # @param default_logging_level # Default logging level for Elasticsearch. # # @param defaults_location # Absolute path to directory containing init defaults file. # # @param deprecation_logging # Wheter to enable deprecation logging. If enabled, deprecation logs will be -# saved to ${cluster.name}_deprecation.log in the elastic search log folder. +# saved to ${cluster.name}_deprecation.log in the Elasticsearch log folder. # # @param deprecation_logging_level # Default deprecation logging level for Elasticsearch. # # @param download_tool # Command-line invocation with which to retrieve an optional package_url. # # @param download_tool_insecure # Command-line invocation with which to retrieve an optional package_url when # certificate verification should be ignored. # # @param download_tool_verify_certificates # Whether or not to verify SSL/TLS certificates when retrieving package files # using a download tool instead of a package management provider. # # @param elasticsearch_group # The group Elasticsearch should run as. This also sets file group # permissions. # # @param elasticsearch_user # The user Elasticsearch should run as. This also sets file ownership. # # @param file_rolling_type # Configuration for the file appender rotation. It can be 'dailyRollingFile', # 'rollingFile' or 'file'. The first rotates by name, the second one by size # or third don't rotate automatically. # # @param homedir # Directory where the elasticsearch installation's files are kept (plugins, etc.) # # @param indices # Define indices via a hash. This is mainly used with Hiera's auto binding. # # @param init_defaults # Defaults file content in hash representation. # # @param init_defaults_file # Defaults file as puppet resource. # # @param init_template # Service file as a template. # # @param jvm_options # Array of options to set in jvm_options. # # @param keystore_password # Password to encrypt this node's Java keystore. # # @param keystore_path -# Custom path to the java keystore file. This parameter is optional. +# Custom path to the Java keystore file. This parameter is optional. # # @param license # Optional Elasticsearch license in hash or string form. # # @param logdir # Directory that will be used for Elasticsearch logging. # # @param logging_config -# Representation of information to be included in the logging.yml file. +# Representation of information to be included in the log4j.properties file. # # @param logging_file # Instead of a hash, you may supply a `puppet://` file source for the -# logging.yml file. +# log4j.properties file. # # @param logging_level # Default logging level for Elasticsearch. # # @param logging_template # Use a custom logging template - just supply the relative path, i.e. # `$module/elasticsearch/logging.yml.erb` # # @param manage_repo # Enable repo management by enabling official Elastic repositories. # # @param oss # Whether to use the purely open source Elasticsearch package distribution. # # @param package_dir # Directory where packages are downloaded to. # # @param package_dl_timeout # For http, https, and ftp downloads, you may set how long the exec resource # may take. # # @param package_name # Name Of the package to install. # # @param package_provider # Method to install the packages, currently only `package` is supported. # # @param package_url # URL of the package to download. # This can be an http, https, or ftp resource for remote packages, or a # `puppet://` resource or `file:/` for local packages # # @param pid_dir # Directory where the elasticsearch process should write out its PID. # # @param pipelines # Define pipelines via a hash. This is mainly used with Hiera's auto binding. # # @param plugindir # Directory containing elasticsearch plugins. # Use this setting if your packages deviate from the norm (/usr/share/elasticsearch/plugins) # # @param plugins # Define plugins via a hash. This is mainly used with Hiera's auto binding. # # @param private_key # Path to the key associated with this node's certificate. # # @param proxy_url # For http and https downloads, you may set a proxy server to use. By default, # no proxy is used. # Format: `proto://[user:pass@]server[:port]/` # # @param purge_configdir # Purge the config directory of any unmanaged files. # # @param purge_package_dir # Purge package directory on removal # # @param purge_secrets # Whether or not keys present in the keystore will be removed if they are not # present in the specified secrets hash. # # @param repo_stage # Use stdlib stage setup for managing the repo instead of relationship # ordering. # # @param restart_on_change # Determines if the application should be automatically restarted # whenever the configuration, package, or plugins change. Enabling this # setting will cause Elasticsearch to restart whenever there is cause to # re-read configuration files, load new plugins, or start the service using an # updated/changed executable. This may be undesireable in highly available # environments. If all other restart_* parameters are left unset, the value of # `restart_on_change` is used for all other restart_*_change defaults. # # @param restart_config_change # Determines if the application should be automatically restarted # whenever the configuration changes. This includes the Elasticsearch # configuration file, any service files, and defaults files. # Disabling automatic restarts on config changes may be desired in an # environment where you need to ensure restarts occur in a controlled/rolling # manner rather than during a Puppet run. # # @param restart_package_change # Determines if the application should be automatically restarted # whenever the package (or package version) for Elasticsearch changes. # Disabling automatic restarts on package changes may be desired in an # environment where you need to ensure restarts occur in a controlled/rolling # manner rather than during a Puppet run. # # @param restart_plugin_change # Determines if the application should be automatically restarted whenever # plugins are installed or removed. # Disabling automatic restarts on plugin changes may be desired in an # environment where you need to ensure restarts occur in a controlled/rolling # manner rather than during a Puppet run. # # @param roles # Define roles via a hash. This is mainly used with Hiera's auto binding. # # @param rolling_file_max_backup_index # Max number of logs to store whern file_rolling_type is 'rollingFile' # # @param rolling_file_max_file_size # Max log file size when file_rolling_type is 'rollingFile' # # @param scripts # Define scripts via a hash. This is mainly used with Hiera's auto binding. # # @param secrets # Optional default configuration hash of key/value pairs to store in the # Elasticsearch keystore file. If unset, the keystore is left unmanaged. # # @param security_logging_content -# File content for shield/x-pack logging configuration file (will be placed -# into logging.yml or log4j2.properties file as appropriate). +# File content for x-pack logging configuration file (will be placed +# into log4j2.properties file). # # @param security_logging_source -# File source for shield/x-pack logging configuration file (will be placed -# into logging.yml or log4j2.properties file as appropriate). +# File source for x-pack logging configuration file (will be placed +# into log4j2.properties). # # @param service_name # Elasticsearch service name # # @param service_provider # The service resource type provider to use when managing elasticsearch instances. # # @param snapshot_repositories # Define snapshot repositories via a hash. This is mainly used with Hiera's auto binding. # # @param ssl # Whether to manage TLS certificates. Requires the ca_certificate, # certificate, private_key and keystore_password parameters to be set. # # @param status # To define the status of the service. If set to `enabled`, the service will # be run and will be started at boot time. If set to `disabled`, the service # is stopped and will not be started at boot time. If set to `running`, the # service will be run but will not be started at boot time. You may use this # to start a service on the first Puppet run instead of the system startup. # If set to `unmanaged`, the service will not be started at boot time and Puppet # does not care whether the service is running or not. For example, this may # be useful if a cluster management software is used to decide when to start # the service plus assuring it is running on the desired node. # # @param system_key # Source for the x-pack system key. Valid values are any that are # supported for the file resource `source` parameter. # # @param systemd_service_path # Path to the directory in which to install systemd service units. # # @param templates # Define templates via a hash. This is mainly used with Hiera's auto binding. # # @param users # Define templates via a hash. This is mainly used with Hiera's auto binding. # # @param validate_tls # Enable TLS/SSL validation on API calls. # # @param version # To set the specific version you want to install. # # @author Richard Pijnenburg # @author Tyler Langlois # @author Gavin Williams # class elasticsearch ( Enum['absent', 'present'] $ensure, Optional[String] $api_basic_auth_password, Optional[String] $api_basic_auth_username, Optional[String] $api_ca_file, Optional[String] $api_ca_path, String $api_host, Integer[0, 65535] $api_port, Enum['http', 'https'] $api_protocol, Integer $api_timeout, Boolean $autoupgrade, Hash $config, Stdlib::Absolutepath $configdir, Integer $configdir_recurselimit, String $daily_rolling_date_pattern, Elasticsearch::Multipath $datadir, Boolean $datadir_instance_directories, Optional[Stdlib::Absolutepath] $defaults_location, Boolean $deprecation_logging, String $deprecation_logging_level, Optional[String] $download_tool, Optional[String] $download_tool_insecure, Boolean $download_tool_verify_certificates, String $elasticsearch_group, String $elasticsearch_user, Enum['dailyRollingFile', 'rollingFile', 'file'] $file_rolling_type, Stdlib::Absolutepath $homedir, Hash $indices, Hash $init_defaults, Optional[String] $init_defaults_file, String $init_template, Array[String] $jvm_options, Optional[Variant[String, Hash]] $license, Stdlib::Absolutepath $logdir, Hash $logging_config, Optional[String] $logging_file, String $logging_level, Optional[String] $logging_template, Boolean $manage_repo, Boolean $oss, Stdlib::Absolutepath $package_dir, Integer $package_dl_timeout, String $package_name, Enum['package'] $package_provider, Optional[String] $package_url, Optional[Stdlib::Absolutepath] $pid_dir, Hash $pipelines, Optional[Stdlib::Absolutepath] $plugindir, Hash $plugins, Optional[Stdlib::HTTPUrl] $proxy_url, Boolean $purge_configdir, Boolean $purge_package_dir, Boolean $purge_secrets, Variant[Boolean, String] $repo_stage, Boolean $restart_on_change, Hash $roles, Integer $rolling_file_max_backup_index, String $rolling_file_max_file_size, Hash $scripts, Optional[Hash] $secrets, Optional[String] $security_logging_content, Optional[String] $security_logging_source, String $service_name, Enum['init', 'openbsd', 'openrc', 'systemd'] $service_provider, Hash $snapshot_repositories, Boolean $ssl, Elasticsearch::Status $status, Optional[String] $system_key, Stdlib::Absolutepath $systemd_service_path, Hash $templates, Hash $users, Boolean $validate_tls, Variant[String, Boolean] $version, Optional[Stdlib::Absolutepath] $ca_certificate = undef, Optional[Stdlib::Absolutepath] $certificate = undef, String $default_logging_level = $logging_level, Optional[String] $keystore_password = undef, Optional[Stdlib::Absolutepath] $keystore_path = undef, Optional[Stdlib::Absolutepath] $private_key = undef, Boolean $restart_config_change = $restart_on_change, Boolean $restart_package_change = $restart_on_change, Boolean $restart_plugin_change = $restart_on_change, ) { #### Validate parameters if ($package_url != undef and $version != false) { fail('Unable to set the version number when using package_url option.') } if ($version != false) { case $facts['os']['family'] { 'RedHat', 'Linux', 'Suse': { if ($version =~ /.+-\d/) { $pkg_version = $version } else { $pkg_version = "${version}-1" } } default: { $pkg_version = $version } } } # This value serves as an unchanging default for platforms as a default for # init scripts to fallback on. $_datadir_default = $facts['kernel'] ? { 'Linux' => '/var/lib/elasticsearch', 'OpenBSD' => '/var/elasticsearch/data', default => undef, } # The OSS package distribution's package appends `-oss` to the end of the # canonical package name. $_package_name = $oss ? { true => "${package_name}-oss", default => $package_name, } # Set the plugin path variable for use later in the module. if $plugindir == undef { $_plugindir = "${homedir}/plugins" } else { $_plugindir = $plugindir } # Should we restart Elasticsearch on config change? $_notify_service = $elasticsearch::restart_config_change ? { true => Service[$elasticsearch::service_name], false => undef, } #### Manage actions contain elasticsearch::package contain elasticsearch::config contain elasticsearch::service - create_resources('elasticsearch::index', $::elasticsearch::indices) - create_resources('elasticsearch::pipeline', $::elasticsearch::pipelines) - create_resources('elasticsearch::plugin', $::elasticsearch::plugins) - create_resources('elasticsearch::role', $::elasticsearch::roles) - create_resources('elasticsearch::script', $::elasticsearch::scripts) - create_resources('elasticsearch::snapshot_repository', $::elasticsearch::snapshot_repositories) - create_resources('elasticsearch::template', $::elasticsearch::templates) - create_resources('elasticsearch::user', $::elasticsearch::users) + create_resources('elasticsearch::index', $elasticsearch::indices) + create_resources('elasticsearch::pipeline', $elasticsearch::pipelines) + create_resources('elasticsearch::plugin', $elasticsearch::plugins) + create_resources('elasticsearch::role', $elasticsearch::roles) + create_resources('elasticsearch::script', $elasticsearch::scripts) + create_resources('elasticsearch::snapshot_repository', $elasticsearch::snapshot_repositories) + create_resources('elasticsearch::template', $elasticsearch::templates) + create_resources('elasticsearch::user', $elasticsearch::users) if ($manage_repo == true) { if ($repo_stage == false) { # Use normal relationship ordering contain elastic_stack::repo Class['elastic_stack::repo'] -> Class['elasticsearch::package'] } else { # Use staging for ordering if !(defined(Stage[$repo_stage])) { stage { $repo_stage: before => Stage['main'] } } include elastic_stack::repo Class<|title == 'elastic_stack::repo'|>{ stage => $repo_stage, } } } if ($license != undef) { contain elasticsearch::license } #### Manage relationships # # Note that many of these overly verbose declarations work around # https://tickets.puppetlabs.com/browse/PUP-1410 # which means clean arrow order chaining won't work if someone, say, # doesn't declare any plugins. # # forgive me for what you're about to see if defined(Class['java']) { Class['java'] -> Class['elasticsearch::config'] } if $ensure == 'present' { # Installation, configuration and service Class['elasticsearch::package'] -> Class['elasticsearch::config'] ~> Class['elasticsearch::service'] # Top-level ordering bindings for resources. Class['elasticsearch::config'] -> Elasticsearch::Plugin <| ensure == 'present' or ensure == 'installed' |> Elasticsearch::Plugin <| ensure == 'absent' |> -> Class['elasticsearch::config'] # Class['elasticsearch::config'] # -> Elasticsearch::User <| ensure == 'present' |> # Elasticsearch::User <| ensure == 'absent' |> # -> Class['elasticsearch::config'] # Class['elasticsearch::config'] # -> Elasticsearch::Role <| |> Class['elasticsearch::config'] -> Elasticsearch::Template <| |> Class['elasticsearch::config'] -> Elasticsearch::Pipeline <| |> Class['elasticsearch::config'] -> Elasticsearch::Index <| |> Class['elasticsearch::config'] -> Elasticsearch::Snapshot_repository <| |> } else { # Absent; remove configuration before the package. Class['elasticsearch::config'] -> Class['elasticsearch::package'] # Top-level ordering bindings for resources. Elasticsearch::Plugin <| |> -> Class['elasticsearch::config'] Elasticsearch::User <| |> -> Class['elasticsearch::config'] Elasticsearch::Role <| |> -> Class['elasticsearch::config'] Elasticsearch::Template <| |> -> Class['elasticsearch::config'] Elasticsearch::Pipeline <| |> -> Class['elasticsearch::config'] Elasticsearch::Index <| |> -> Class['elasticsearch::config'] Elasticsearch::Snapshot_repository <| |> -> Class['elasticsearch::config'] } # Install plugins before managing users/roles Elasticsearch::Plugin <| ensure == 'present' or ensure == 'installed' |> -> Elasticsearch::User <| |> Elasticsearch::Plugin <| ensure == 'present' or ensure == 'installed' |> -> Elasticsearch::Role <| |> # Remove plugins after managing users/roles Elasticsearch::User <| |> -> Elasticsearch::Plugin <| ensure == 'absent' |> Elasticsearch::Role <| |> -> Elasticsearch::Plugin <| ensure == 'absent' |> # Ensure roles are defined before managing users that reference roles Elasticsearch::Role <| |> -> Elasticsearch::User <| ensure == 'present' |> # Ensure users are removed before referenced roles are managed Elasticsearch::User <| ensure == 'absent' |> -> Elasticsearch::Role <| |> # Ensure users and roles are managed before calling out to REST resources Elasticsearch::Role <| |> -> Elasticsearch::Template <| |> Elasticsearch::User <| |> -> Elasticsearch::Template <| |> Elasticsearch::Role <| |> -> Elasticsearch::Pipeline <| |> Elasticsearch::User <| |> -> Elasticsearch::Pipeline <| |> Elasticsearch::Role <| |> -> Elasticsearch::Index <| |> Elasticsearch::User <| |> -> Elasticsearch::Index <| |> Elasticsearch::Role <| |> -> Elasticsearch::Snapshot_repository <| |> Elasticsearch::User <| |> -> Elasticsearch::Snapshot_repository <| |> # Ensure that any command-line based user changes are performed before the # file is modified Elasticsearch_user <| |> -> Elasticsearch_user_file <| |> } diff --git a/manifests/package.pp b/manifests/package.pp index 20bcc50..e41aad8 100644 --- a/manifests/package.pp +++ b/manifests/package.pp @@ -1,192 +1,192 @@ # This class exists to coordinate all software package management related # actions, functionality and logical units in a central place. # # It is not intended to be used directly by external resources like node # definitions or other modules. # # @example importing this class by other classes to use its functionality: # class { 'elasticsearch::package': } # # @author Richard Pijnenburg # @author Tyler Langlois # class elasticsearch::package { Exec { path => [ '/bin', '/usr/bin', '/usr/local/bin' ], cwd => '/', tries => 3, try_sleep => 10, } if $elasticsearch::ensure == 'present' { if $elasticsearch::restart_package_change { Package['elasticsearch'] ~> Class['elasticsearch::service'] } Package['elasticsearch'] ~> Exec['remove_plugin_dir'] # Create directory to place the package file $package_dir = $elasticsearch::package_dir exec { 'create_package_dir_elasticsearch': cwd => '/', path => ['/usr/bin', '/bin'], command => "mkdir -p ${package_dir}", creates => $package_dir, } file { $package_dir: ensure => 'directory', purge => $elasticsearch::purge_package_dir, force => $elasticsearch::purge_package_dir, backup => false, require => Exec['create_package_dir_elasticsearch'], } # Check if we want to install a specific version or not if $elasticsearch::version == false { $package_ensure = $elasticsearch::autoupgrade ? { true => 'latest', false => 'present', } } else { # install specific version $package_ensure = $elasticsearch::pkg_version } # action if ($elasticsearch::package_url != undef) { case $elasticsearch::package_provider { 'package': { $before = Package['elasticsearch'] } default: { fail("software provider \"${elasticsearch::package_provider}\".") } } $filename_array = split($elasticsearch::package_url, '/') $basefilename = $filename_array[-1] $source_array = split($elasticsearch::package_url, ':') $protocol_type = $source_array[0] $ext_array = split($basefilename, '\.') $ext = $ext_array[-1] $pkg_source = "${package_dir}/${basefilename}" case $protocol_type { 'puppet': { file { $pkg_source: ensure => file, source => $elasticsearch::package_url, require => File[$package_dir], backup => false, before => $before, } } 'ftp', 'https', 'http': { if $elasticsearch::proxy_url != undef { $exec_environment = [ 'use_proxy=yes', "http_proxy=${elasticsearch::proxy_url}", "https_proxy=${elasticsearch::proxy_url}", ] } else { $exec_environment = [] } case $elasticsearch::download_tool { String: { $_download_command = if $elasticsearch::download_tool_verify_certificates { $elasticsearch::download_tool } else { $elasticsearch::download_tool_insecure } exec { 'download_package_elasticsearch': command => "${_download_command} ${pkg_source} ${elasticsearch::package_url} 2> /dev/null", creates => $pkg_source, environment => $exec_environment, timeout => $elasticsearch::package_dl_timeout, require => File[$package_dir], before => $before, } } default: { fail("no \$elasticsearch::download_tool defined for ${facts['os']['family']}") } } } 'file': { $source_path = $source_array[1] file { $pkg_source: ensure => file, source => $source_path, require => File[$package_dir], backup => false, before => $before, } } default: { fail("Protocol must be puppet, file, http, https, or ftp. You have given \"${protocol_type}\"") } } if ($elasticsearch::package_provider == 'package') { case $ext { 'deb': { Package { provider => 'dpkg', source => $pkg_source } } 'rpm': { Package { provider => 'rpm', source => $pkg_source } } default: { fail("Unknown file extention \"${ext}\".") } } } } else { if ($elasticsearch::manage_repo and $facts['os']['family'] == 'Debian') { Class['apt::update'] -> Package['elasticsearch'] } } # Package removal } else { if ($facts['os']['family'] == 'Suse') { Package { provider => 'rpm', } $package_ensure = 'absent' } else { $package_ensure = 'purged' } } if ($elasticsearch::package_provider == 'package') { package { 'elasticsearch': ensure => $package_ensure, name => $elasticsearch::_package_name, } exec { 'remove_plugin_dir': refreshonly => true, - command => "rm -rf ${::elasticsearch::_plugindir}", + command => "rm -rf ${elasticsearch::_plugindir}", } } else { fail("\"${elasticsearch::package_provider}\" is not supported") } } diff --git a/manifests/plugin.pp b/manifests/plugin.pp index e6453b6..ef96fbb 100644 --- a/manifests/plugin.pp +++ b/manifests/plugin.pp @@ -1,145 +1,145 @@ # This define allows you to install arbitrary Elasticsearch plugins # either by using the default repositories or by specifying an URL # # @example install from official repository # elasticsearch::plugin {'mobz/elasticsearch-head': module_dir => 'head'} # # @example installation using a custom URL # elasticsearch::plugin { 'elasticsearch-jetty': # module_dir => 'elasticsearch-jetty', # url => 'https://oss-es-plugins.s3.amazonaws.com/elasticsearch-jetty/elasticsearch-jetty-0.90.0.zip', # } # # @param ensure # Whether the plugin will be installed or removed. # Set to 'absent' to ensure a plugin is not installed # # @param configdir # Path to the elasticsearch configuration directory (ES_PATH_CONF) # to which the plugin should be installed. # # @param java_opts # Array of Java options to be passed to `ES_JAVA_OPTS` # # @param java_home # Path to JAVA_HOME, if Java is installed in a non-standard location. # # @param module_dir # Directory name where the module has been installed # This is automatically generated based on the module name # Specify a value here to override the auto generated value # # @param proxy_host # Proxy host to use when installing the plugin # # @param proxy_password # Proxy auth password to use when installing the plugin # # @param proxy_port # Proxy port to use when installing the plugin # # @param proxy_username # Proxy auth username to use when installing the plugin # # @param source # Specify the source of the plugin. # This will copy over the plugin to the node and use it for installation. # Useful for offline installation # # @param url # Specify an URL where to download the plugin from. # # @author Richard Pijnenburg # @author Matteo Sessa # @author Dennis Konert # @author Tyler Langlois # @author Gavin Williams # define elasticsearch::plugin ( Enum['absent', 'present'] $ensure = 'present', - Stdlib::Absolutepath $configdir = $::elasticsearch::configdir, + Stdlib::Absolutepath $configdir = $elasticsearch::configdir, Array[String] $java_opts = [], Optional[Stdlib::Absolutepath] $java_home = undef, Optional[String] $module_dir = undef, Optional[String] $proxy_host = undef, Optional[String] $proxy_password = undef, Optional[Integer[0, 65535]] $proxy_port = undef, Optional[String] $proxy_username = undef, Optional[String] $source = undef, Optional[Stdlib::HTTPUrl] $url = undef, ) { include elasticsearch case $ensure { 'present': { $_file_ensure = 'directory' $_file_before = [] } 'absent': { $_file_ensure = $ensure $_file_before = File[$elasticsearch::_plugindir] } default: { } } # set proxy by override or parse and use proxy_url from # elasticsearch::proxy_url or use no proxy at all if ($proxy_host != undef and $proxy_port != undef) { if ($proxy_username != undef and $proxy_password != undef) { $_proxy_auth = "${proxy_username}:${proxy_password}@" } else { $_proxy_auth = undef } $_proxy = "http://${_proxy_auth}${proxy_host}:${proxy_port}" } elsif ($elasticsearch::proxy_url != undef) { $_proxy = $elasticsearch::proxy_url } else { $_proxy = undef } if ($source != undef) { $filename_array = split($source, '/') $basefilename = $filename_array[-1] $file_source = "${elasticsearch::package_dir}/${basefilename}" file { $file_source: ensure => 'file', source => $source, before => Elasticsearch_plugin[$name], } } else { $file_source = undef } $_module_dir = es_plugin_name($module_dir, $name) elasticsearch_plugin { $name: ensure => $ensure, configdir => $configdir, elasticsearch_package_name => 'elasticsearch', java_opts => $java_opts, java_home => $java_home, source => $file_source, url => $url, proxy => $_proxy, - plugin_dir => $::elasticsearch::_plugindir, + plugin_dir => $elasticsearch::_plugindir, plugin_path => $module_dir, } - -> file { "${::elasticsearch::_plugindir}/${_module_dir}": + -> file { "${elasticsearch::_plugindir}/${_module_dir}": ensure => $_file_ensure, mode => 'o+Xr', recurse => true, before => $_file_before, } - if $::elasticsearch::restart_plugin_change { + if $elasticsearch::restart_plugin_change { Elasticsearch_plugin[$name] { notify +> Service['elasticsearch'], } } } diff --git a/manifests/service.pp b/manifests/service.pp index acf82a5..45a7465 100644 --- a/manifests/service.pp +++ b/manifests/service.pp @@ -1,85 +1,85 @@ # This class exists to coordinate all service management related actions, # functionality and logical units in a central place. # # *Note*: "service" is the Puppet term and type for background processes # in general and is used in a platform-independent way. E.g. "service" means # "daemon" in relation to Unix-like systems. # # @param ensure # Controls if the managed resources shall be `present` or `absent`. # If set to `absent`, the managed software packages will be uninstalled, and # any traces of the packages will be purged as well as possible, possibly # including existing configuration files. # System modifications (if any) will be reverted as well as possible (e.g. # removal of created users, services, changed log settings, and so on). # This is a destructive parameter and should be used with care. # # @param init_defaults # Defaults file content in hash representation # # @param init_defaults_file # Defaults file as puppet resource # # @param init_template # Service file as a template # # @param service_flags # Flags to pass to the service. # # @param status # Defines the status of the service. If set to `enabled`, the service is # started and will be enabled at boot time. If set to `disabled`, the # service is stopped and will not be started at boot time. If set to `running`, # the service is started but will not be enabled at boot time. You may use # this to start a service on the first Puppet run instead of the system startup. # If set to `unmanaged`, the service will not be started at boot time and Puppet # does not care whether the service is running or not. For example, this may # be useful if a cluster management software is used to decide when to start # the service plus assuring it is running on the desired node. # # @author Richard Pijnenburg # @author Tyler Langlois # @author Gavin Williams # class elasticsearch::service { #### Service management - if $::elasticsearch::ensure == 'present' { + if $elasticsearch::ensure == 'present' { - case $::elasticsearch::status { + case $elasticsearch::status { # make sure service is currently running, start it on boot 'enabled': { $_service_ensure = 'running' $_service_enable = true } # make sure service is currently stopped, do not start it on boot 'disabled': { $_service_ensure = 'stopped' $_service_enable = false } # make sure service is currently running, do not start it on boot 'running': { $_service_ensure = 'running' $_service_enable = false } # do not start service on boot, do not care whether currently running # or not 'unmanaged': { $_service_ensure = undef $_service_enable = false } default: { } } } else { # make sure the service is stopped and disabled (the removal itself will be # done by package.pp) $_service_ensure = 'stopped' $_service_enable = false } - service { $::elasticsearch::service_name: + service { $elasticsearch::service_name: ensure => $_service_ensure, enable => $_service_enable, } } diff --git a/manifests/service/systemd.pp b/manifests/service/systemd.pp index 61af150..d7fdfb5 100644 --- a/manifests/service/systemd.pp +++ b/manifests/service/systemd.pp @@ -1,195 +1,195 @@ # This class exists to coordinate all service management related actions, # functionality and logical units in a central place. # # *Note*: "service" is the Puppet term and type for background processes # in general and is used in a platform-independent way. E.g. "service" means # "daemon" in relation to Unix-like systems. # # @param ensure # Controls if the managed resources shall be `present` or # `absent`. If set to `absent`, the managed software packages will being # uninstalled and any traces of the packages will be purged as well as # possible. This may include existing configuration files (the exact # behavior is provider). This is thus destructive and should be used with # care. # # @param init_defaults # Defaults file content in hash representation # # @param init_defaults_file # Defaults file as puppet resource # # @param init_template # Service file as a template # # @param status # Defines the status of the service. If set to `enabled`, the service is # started and will be enabled at boot time. If set to `disabled`, the # service is stopped and will not be started at boot time. If set to `running`, # the service is started but will not be enabled at boot time. You may use # this to start a service on the first Puppet run instead of the system startup. # If set to `unmanaged`, the service will not be started at boot time and Puppet # does not care whether the service is running or not. For example, this may # be useful if a cluster management software is used to decide when to start # the service plus assuring it is running on the desired node. # # @author Richard Pijnenburg # @author Tyler Langlois # @author Gavin Williams # class elasticsearch::service::systemd ( Enum['absent', 'present'] $ensure = $elasticsearch::ensure, Hash $init_defaults = {}, Optional[String] $init_defaults_file = undef, Optional[String] $init_template = undef, Elasticsearch::Status $status = $elasticsearch::status, ) { #### Service management if $ensure == 'present' { case $status { # make sure service is currently running, start it on boot 'enabled': { $_service_ensure = 'running' $_service_enable = true } # make sure service is currently stopped, do not start it on boot 'disabled': { $_service_ensure = 'stopped' $_service_enable = false } # make sure service is currently running, do not start it on boot 'running': { $_service_ensure = 'running' $_service_enable = false } # do not start service on boot, do not care whether currently running # or not 'unmanaged': { $_service_ensure = undef $_service_enable = false } default: { } } } else { # make sure the service is stopped and disabled (the removal itself will be # done by package.pp) $_service_ensure = 'stopped' $_service_enable = false } if(has_key($init_defaults, 'ES_USER') and $init_defaults['ES_USER'] != $elasticsearch::elasticsearch_user) { fail('Found ES_USER setting for init_defaults but is not same as elasticsearch_user setting. Please use elasticsearch_user setting.') } $new_init_defaults = merge( { 'ES_USER' => $elasticsearch::elasticsearch_user, 'ES_GROUP' => $elasticsearch::elasticsearch_group, 'MAX_OPEN_FILES' => '65536', 'MAX_THREADS' => '4096', }, $init_defaults ) $_notify_service = $elasticsearch::restart_config_change ? { true => [ Exec["systemd_reload_${name}"], Service["elasticsearch-instance-${name}"] ], false => Exec["systemd_reload_${name}"] } if ($ensure == 'present') { # Defaults file, either from file source or from hash to augeas commands if ($init_defaults_file != undef) { file { "${elasticsearch::defaults_location}/elasticsearch-${name}": ensure => $ensure, source => $init_defaults_file, owner => 'root', group => '0', mode => '0644', before => Service["elasticsearch-instance-${name}"], notify => $_notify_service, } } else { augeas { "defaults_${name}": incl => "${elasticsearch::defaults_location}/elasticsearch-${name}", lens => 'Shellvars.lns', changes => template("${module_name}/etc/sysconfig/defaults.erb"), before => Service["elasticsearch-instance-${name}"], notify => $_notify_service, } } $_service_require = Exec["systemd_reload_${name}"] } else { # absent file { "${elasticsearch::defaults_location}/elasticsearch-${name}": ensure => 'absent', subscribe => Service["elasticsearch-instance-${name}"], notify => Exec["systemd_reload_${name}"], } $_service_require = undef } exec { "systemd_reload_${name}": command => '/bin/systemctl daemon-reload', refreshonly => true, } # init file from template if ($init_template != undef) { # Check for values in init defaults we may want to set in the init template if (has_key($new_init_defaults, 'MAX_OPEN_FILES')) { $nofile = $new_init_defaults['MAX_OPEN_FILES'] } else { $nofile = '65536' } if (has_key($new_init_defaults, 'MAX_LOCKED_MEMORY')) { $memlock = $new_init_defaults['MAX_LOCKED_MEMORY'] } else { $memlock = undef } if (has_key($new_init_defaults, 'MAX_THREADS')) { $nproc = $new_init_defaults['MAX_THREADS'] } else { $nproc = '4096' } # elasticsearch_service_file { "${elasticsearch::systemd_service_path}/elasticsearch-${name}.service": # ensure => 'present', # content => file($init_template), # defaults_location => $elasticsearch::defaults_location, # group => $elasticsearch::elasticsearch_group, # homedir => $elasticsearch::homedir, # instance => $name, # memlock => $memlock, # nofile => $nofile, # nproc => $nproc, # package_name => 'elasticsearch', # pid_dir => $elasticsearch::pid_dir, # user => $elasticsearch::elasticsearch_user, # notify => $_notify_service, # } # -> file { "${elasticsearch::systemd_service_path}/elasticsearch-${name}.service": # ensure => 'file', # owner => 'root', # group => 'root', # mode => '0644', # before => Service["elasticsearch-instance-${name}"], # notify => $_notify_service, # } } - service { $::elasticsearch::service_name: + service { $elasticsearch::service_name: ensure => $_service_ensure, enable => $_service_enable, provider => 'systemd', require => $_service_require, } } diff --git a/spec/acceptance/tests/acceptance_spec.rb b/spec/acceptance/tests/acceptance_spec.rb index cb6dc38..8cafa20 100644 --- a/spec/acceptance/tests/acceptance_spec.rb +++ b/spec/acceptance/tests/acceptance_spec.rb @@ -1,79 +1,83 @@ require 'spec_helper_acceptance' require 'helpers/acceptance/tests/basic_shared_examples.rb' require 'helpers/acceptance/tests/template_shared_examples.rb' require 'helpers/acceptance/tests/removal_shared_examples.rb' require 'helpers/acceptance/tests/pipeline_shared_examples.rb' require 'helpers/acceptance/tests/plugin_shared_examples.rb' require 'helpers/acceptance/tests/plugin_upgrade_shared_examples.rb' require 'helpers/acceptance/tests/snapshot_repository_shared_examples.rb' require 'helpers/acceptance/tests/datadir_shared_examples.rb' require 'helpers/acceptance/tests/package_url_shared_examples.rb' require 'helpers/acceptance/tests/hiera_shared_examples.rb' require 'helpers/acceptance/tests/usergroup_shared_examples.rb' require 'helpers/acceptance/tests/security_shared_examples.rb' describe "elasticsearch v#{v[:elasticsearch_full_version]} class" do es_config = { 'cluster.name' => v[:cluster_name], 'http.bind_host' => '0.0.0.0', 'http.port' => 9200, 'node.name' => 'elasticsearch01' } let(:elastic_repo) { not v[:is_snapshot] } let(:manifest) do package = if not v[:is_snapshot] <<-MANIFEST # Hard version set here due to plugin incompatibilities. version => '#{v[:elasticsearch_full_version]}', MANIFEST else <<-MANIFEST manage_repo => false, package_url => '#{v[:snapshot_package]}', MANIFEST end <<-MANIFEST api_timeout => 60, config => { #{es_config.map { |k, v| " '#{k}' => '#{v}'," }.join("\n")} }, jvm_options => [ '-Xms128m', '-Xmx128m', ], oss => #{v[:oss]}, #{package} MANIFEST end context 'testing with' do describe 'simple config' do include_examples('basic acceptance tests', es_config) end include_examples('module removal', es_config) end include_examples('template operations', es_config, v[:template]) include_examples('pipeline operations', es_config, v[:pipeline]) - include_examples('plugin acceptance tests', es_config, v[:elasticsearch_plugins]) unless v[:elasticsearch_plugins].empty? + include_examples( + 'plugin acceptance tests', + es_config, + v[:elasticsearch_plugins] + ) unless v[:elasticsearch_plugins].empty? include_examples('snapshot repository acceptance tests') include_examples('datadir acceptance tests', es_config) # Skip this for snapshot testing, as we only have package files anyway. include_examples('package_url acceptance tests', es_config) unless v[:is_snapshot] include_examples('hiera acceptance tests', es_config, v[:elasticsearch_plugins]) # Security-related tests (shield/x-pack). # # Skip OSS-only distributions since they do not bundle x-pack, and skip # snapshots since we they don't recognize prod licenses. include_examples('security acceptance tests', es_config) unless v[:oss] or v[:is_snapshot] end diff --git a/spec/classes/001_hiera_spec.rb b/spec/classes/001_hiera_spec.rb index d48ba1b..6125a01 100644 --- a/spec/classes/001_hiera_spec.rb +++ b/spec/classes/001_hiera_spec.rb @@ -1,217 +1,214 @@ require 'spec_helper' describe 'elasticsearch', :type => 'class' do default_params = { :config => { 'node.name' => 'foo' } } let(:params) do default_params.merge({}) end on_supported_os( :hardwaremodels => ['x86_64'], :supported_os => [ { 'operatingsystem' => 'CentOS', 'operatingsystemrelease' => ['7'] } ] ).each do |os, facts| context "on #{os}" do context 'hiera' do describe 'indices' do context 'single indices' do let(:facts) { facts.merge(:scenario => 'singleindex') } it { should contain_elasticsearch__index('baz') .with( :ensure => 'present', :settings => { 'index' => { 'number_of_shards' => 1 } } ) } it { should contain_elasticsearch_index('baz') } it { should contain_es_instance_conn_validator( 'baz-index-conn-validator' ) } end context 'no indices' do let(:facts) { facts.merge(:scenario => '') } it { should_not contain_elasticsearch__index('baz') } end end context 'config' do let(:facts) { facts.merge(:scenario => 'singleinstance') } # TODO: Fix this - # it { should contain_augeas('defaults') } + it { should contain_augeas('init_defaults') } + it { should contain_file('/etc/elasticsearch/elasticsearch.yml') } it { should contain_datacat('/etc/elasticsearch/elasticsearch.yml') } it { should contain_datacat_fragment('main_config') } it { should contain_service('elasticsearch').with( :ensure => 'running', :enable => true ) } - - %w[elasticsearch.yml log4j2.properties].each do |file| - it { should contain_file("/etc/elasticsearch/#{file}") } - end end # of config describe 'pipelines' do context 'single pipeline' do let(:facts) { facts.merge(:scenario => 'singlepipeline') } it { should contain_elasticsearch__pipeline('testpipeline') .with( :ensure => 'present', :content => { 'description' => 'Add the foo field', 'processors' => [ { 'set' => { 'field' => 'foo', 'value' => 'bar' } } ] } ) } it { should contain_elasticsearch_pipeline('testpipeline') } end context 'no pipelines' do let(:facts) { facts.merge(:scenario => '') } it { should_not contain_elasticsearch__pipeline('testpipeline') } end end describe 'plugins' do context 'single plugin' do let(:facts) { facts.merge(:scenario => 'singleplugin') } it { should contain_elasticsearch__plugin('mobz/elasticsearch-head') .with( :ensure => 'present', :module_dir => 'head' ) } it { should contain_elasticsearch_plugin('mobz/elasticsearch-head') } end context 'no plugins' do let(:facts) { facts.merge(:scenario => '') } it { should_not contain_elasticsearch__plugin( 'mobz/elasticsearch-head/1.0.0' ) } end end describe 'roles' do context 'single roles' do let(:facts) { facts.merge(:scenario => 'singlerole') } let(:params) do default_params end it { should contain_elasticsearch__role('admin') .with( :ensure => 'present', :privileges => { 'cluster' => 'monitor', 'indices' => { '*' => 'all' } }, :mappings => [ 'cn=users,dc=example,dc=com' ] ) } it { should contain_elasticsearch_role('admin') } it { should contain_elasticsearch_role_mapping('admin') } end context 'no roles' do let(:facts) { facts.merge(:scenario => '') } it { should_not contain_elasticsearch__role('admin') } end end describe 'scripts' do context 'single scripts' do let(:facts) { facts.merge(:scenario => 'singlescript') } it { should contain_elasticsearch__script('myscript') .with( :ensure => 'present', :source => 'puppet:///file/here' ) } it { should contain_file('/usr/share/elasticsearch/scripts/here') } end context 'no roles' do let(:facts) { facts.merge(:scenario => '') } it { should_not contain_elasticsearch__script('myscript') } end end describe 'templates' do context 'single template' do let(:facts) { facts.merge(:scenario => 'singletemplate') } it { should contain_elasticsearch__template('foo') .with( :ensure => 'present', :content => { 'template' => 'foo-*', 'settings' => { 'index' => { 'number_of_replicas' => 0 } } } ) } it { should contain_elasticsearch_template('foo') } end context 'no templates' do let(:facts) { facts.merge(:scenario => '') } it { should_not contain_elasticsearch__template('foo') } end end describe 'users' do context 'single users' do let(:facts) { facts.merge(:scenario => 'singleuser') } let(:params) do default_params end it { should contain_elasticsearch__user('elastic') .with( :ensure => 'present', :roles => ['admin'], :password => 'password' ) } it { should contain_elasticsearch_user('elastic') } end context 'no users' do let(:facts) { facts.merge(:scenario => '') } it { should_not contain_elasticsearch__user('elastic') } end end end end end end diff --git a/spec/classes/002_elasticsearch_config_security_logging_spec.rb b/spec/classes/002_elasticsearch_config_security_logging_spec.rb deleted file mode 100644 index 7ae896a..0000000 --- a/spec/classes/002_elasticsearch_config_security_logging_spec.rb +++ /dev/null @@ -1,83 +0,0 @@ -require 'spec_helper' - -shared_examples 'security plugin logging' do |plugin, logfile, tests| - describe "security logging configuration file for #{plugin}" do - tests.each_pair do |param_type, params| - context "with no security plugin defined for #{param_type}" do - let(:params) do - { "security_logging_#{param_type}" => params[:manifest] } - end - - it { should_not compile.with_all_deps } - end - - context "parameter #{param_type}" do - let(:params) do - { - :security_plugin => plugin, - "security_logging_#{param_type}" => params[:manifest] - } - end - - it { should contain_file("/etc/elasticsearch/#{plugin}") - .with_ensure('directory')} - - case param_type - when 'source' - it 'sets the source for the file resource' do - should contain_file("/etc/elasticsearch/#{plugin}/#{logfile}") - .with_source(params[:value]) - end - when 'content' - it 'sets logging file yaml content' do - should contain_file("/etc/elasticsearch/#{plugin}/#{logfile}") - .with_content(params[:value]) - end - end - end - end - end -end - -describe 'elasticsearch', :type => 'class' do - on_supported_os( - :hardwaremodels => ['x86_64'], - :supported_os => [ - { - 'operatingsystem' => 'CentOS', - 'operatingsystemrelease' => ['6'] - } - ] - ).each do |os, facts| - context "on #{os}" do - let(:facts) { facts.merge( - :scenario => '', - :common => '' - ) } - - include_examples 'security plugin logging', - 'shield', - 'logging.yml', - 'content' => { - :manifest => "one: two\nfoo: bar\n", - :value => "one: two\nfoo: bar\n" - }, - 'source' => { - :manifest => '/foo/bar.yml', - :value => '/foo/bar.yml' - } - - include_examples 'security plugin logging', - 'x-pack', - 'log4j2.properties', - 'content' => { - :manifest => "one = two\nfoo = bar\n", - :value => "one = two\nfoo = bar\n" - }, - 'source' => { - :manifest => '/foo/bar.properties', - :value => '/foo/bar.properties' - } - end - end -end diff --git a/spec/classes/099_coverage_spec.rb b/spec/classes/099_coverage_spec.rb index 6ecc73e..03491b5 100644 --- a/spec/classes/099_coverage_spec.rb +++ b/spec/classes/099_coverage_spec.rb @@ -1,2 +1 @@ -# TODO: Re-enable this test -# at_exit { RSpec::Puppet::Coverage.report! 100 } +at_exit { RSpec::Puppet::Coverage.report! 100 } diff --git a/spec/defines/007_elasticsearch_user_spec.rb b/spec/defines/007_elasticsearch_user_spec.rb index 20e5bda..c8b3103 100644 --- a/spec/defines/007_elasticsearch_user_spec.rb +++ b/spec/defines/007_elasticsearch_user_spec.rb @@ -1,145 +1,120 @@ require 'spec_helper' describe 'elasticsearch::user' do let(:title) { 'elastic' } let(:pre_condition) do <<-EOS - class { 'elasticsearch': - security_plugin => 'shield', - } + class { 'elasticsearch': } EOS end on_supported_os( :hardwaremodels => ['x86_64'], :supported_os => [ { 'operatingsystem' => 'CentOS', 'operatingsystemrelease' => ['7'] } ] ).each do |os, facts| context "on #{os}" do let(:facts) { facts.merge( :scenario => '', :common => '' ) } context 'with default parameters' do let(:params) do { :password => 'foobar', :roles => %w[monitor user] } end it { should contain_elasticsearch__user('elastic') } it { should contain_elasticsearch_user('elastic') } it do should contain_elasticsearch_user_roles('elastic').with( 'ensure' => 'present', 'roles' => %w[monitor user] ) end end describe 'collector ordering' do describe 'when present' do let(:pre_condition) do <<-EOS - class { 'elasticsearch': - security_plugin => 'shield', - } - elasticsearch::plugin { 'shield': } + class { 'elasticsearch': } elasticsearch::template { 'foo': content => {"foo" => "bar"} } elasticsearch::role { 'test_role': privileges => { 'cluster' => 'monitor', 'indices' => { '*' => 'all', }, }, } EOS end let(:params) do { :password => 'foobar', :roles => %w[monitor user] } end it { should contain_elasticsearch__role('test_role') } it { should contain_elasticsearch_role('test_role') } it { should contain_elasticsearch_role_mapping('test_role') } - it { should contain_elasticsearch__plugin('shield') } - it { should contain_elasticsearch_plugin('shield') } - it { should contain_file( - '/usr/share/elasticsearch/plugins/shield' - ) } it { should contain_elasticsearch__user('elastic') .that_comes_before([ 'Elasticsearch::Template[foo]' ]).that_requires([ - 'Elasticsearch::Plugin[shield]', 'Elasticsearch::Role[test_role]' ])} include_examples 'class', :systemd - it { should contain_file( - '/etc/elasticsearch/shield' - ) } end describe 'when absent' do let(:pre_condition) do <<-EOS - class { 'elasticsearch': - security_plugin => 'shield', - } - elasticsearch::plugin { 'shield': - ensure => 'absent', - } + class { 'elasticsearch': } elasticsearch::template { 'foo': content => {"foo" => "bar"} } elasticsearch::role { 'test_role': privileges => { 'cluster' => 'monitor', 'indices' => { '*' => 'all', }, }, } EOS end let(:params) do { :password => 'foobar', :roles => %w[monitor user] } end it { should contain_elasticsearch__role('test_role') } it { should contain_elasticsearch_role('test_role') } it { should contain_elasticsearch_role_mapping('test_role') } - it { should contain_elasticsearch__plugin('shield') } - it { should contain_elasticsearch_plugin('shield') } - it { should contain_file( - '/usr/share/elasticsearch/plugins/shield' - ) } it { should contain_elasticsearch__user('elastic') .that_comes_before([ - 'Elasticsearch::Template[foo]', - 'Elasticsearch::Plugin[shield]' + 'Elasticsearch::Template[foo]' ]).that_requires([ 'Elasticsearch::Role[test_role]' ])} include_examples 'class', :systemd end end end end end diff --git a/spec/defines/008_elasticsearch_role_spec.rb b/spec/defines/008_elasticsearch_role_spec.rb index 6afd1d9..2275d57 100644 --- a/spec/defines/008_elasticsearch_role_spec.rb +++ b/spec/defines/008_elasticsearch_role_spec.rb @@ -1,126 +1,109 @@ require 'spec_helper' describe 'elasticsearch::role' do let(:title) { 'elastic_role' } let(:pre_condition) do <<-EOS - class { 'elasticsearch': - security_plugin => 'shield', - } + class { 'elasticsearch': } EOS end let(:params) do { :privileges => { 'cluster' => '*' }, :mappings => [ 'cn=users,dc=example,dc=com', 'cn=admins,dc=example,dc=com', 'cn=John Doe,cn=other users,dc=example,dc=com' ] } end on_supported_os( :hardwaremodels => ['x86_64'], :supported_os => [ { 'operatingsystem' => 'CentOS', 'operatingsystemrelease' => ['7'] } ] ).each do |os, facts| context "on #{os}" do let(:facts) { facts.merge( :scenario => '', :common => '' ) } context 'with an invalid role name' do context 'too long' do let(:title) { 'A' * 31 } it { should raise_error(Puppet::Error, /expected length/i) } end end context 'with default parameters' do it { should contain_elasticsearch__role('elastic_role') } it { should contain_elasticsearch_role('elastic_role') } it do should contain_elasticsearch_role_mapping('elastic_role').with( 'ensure' => 'present', 'mappings' => [ 'cn=users,dc=example,dc=com', 'cn=admins,dc=example,dc=com', 'cn=John Doe,cn=other users,dc=example,dc=com' ] ) end end describe 'collector ordering' do describe 'when present' do let(:pre_condition) do <<-EOS - class { 'elasticsearch': - security_plugin => 'shield', - } - elasticsearch::plugin { 'shield': } + class { 'elasticsearch': } elasticsearch::template { 'foo': content => {"foo" => "bar"} } elasticsearch::user { 'elastic': password => 'foobar', roles => ['elastic_role'], } EOS end - it { should contain_elasticsearch__plugin('shield') } it { should contain_elasticsearch__role('elastic_role') .that_comes_before([ 'Elasticsearch::Template[foo]', 'Elasticsearch::User[elastic]' - ]).that_requires([ - 'Elasticsearch::Plugin[shield]' ])} include_examples 'class', :systemd - it { should contain_file( - '/etc/elasticsearch/shield' - ) } end describe 'when absent' do let(:pre_condition) do <<-EOS - class { 'elasticsearch': - security_plugin => 'shield', - } - elasticsearch::plugin { 'shield': - ensure => 'absent', - } + class { 'elasticsearch': } elasticsearch::template { 'foo': content => {"foo" => "bar"} } elasticsearch::user { 'elastic': password => 'foobar', roles => ['elastic_role'], } EOS end - it { should contain_elasticsearch__plugin('shield') } include_examples 'class', :systemd # TODO: Uncomment once upstream issue is fixed. # https://github.com/rodjek/rspec-puppet/issues/418 # it { should contain_elasticsearch__shield__role('elastic_role') # .that_comes_before([ # 'Elasticsearch::Template[foo]', # 'Elasticsearch::Plugin[shield]', # 'Elasticsearch::Shield::User[elastic]' # ])} end end end end end diff --git a/spec/helpers/acceptance/tests/basic_shared_examples.rb b/spec/helpers/acceptance/tests/basic_shared_examples.rb index e05ff09..e9a3e39 100644 --- a/spec/helpers/acceptance/tests/basic_shared_examples.rb +++ b/spec/helpers/acceptance/tests/basic_shared_examples.rb @@ -1,66 +1,67 @@ require 'json' require 'helpers/acceptance/tests/manifest_shared_examples' shared_examples 'basic acceptance tests' do |es_config| include_examples('manifest application') describe package("elasticsearch#{v[:oss] ? '-oss' : ''}") do - it { should be_installed } + it { should be_installed + .with_version(v[:elasticsearch_full_version]) } end %w[ /etc/elasticsearch /usr/share/elasticsearch /var/lib/elasticsearch ].each do |dir| describe file(dir) do it { should be_directory } end end describe 'resources' do describe service('elasticsearch') do it { send(es_config.empty? ? :should_not : :should, be_enabled) } it { send(es_config.empty? ? :should_not : :should, be_running) } end unless es_config.empty? describe file(pid_file) do it { should be_file } its(:content) { should match(/[0-9]+/) } end describe file('/etc/elasticsearch/elasticsearch.yml') do it { should be_file } it { should contain "name: #{es_config['node.name']}" } end end unless es_config.empty? es_port = es_config['http.port'] describe port(es_port) do it 'open', :with_retries do should be_listening end end describe server :container do describe http("http://localhost:#{es_port}/_nodes/_local") do it 'serves requests', :with_retries do expect(response.status).to eq(200) end it 'uses the default data path', :with_retries do json = JSON.parse(response.body)['nodes'].values.first data_dir = ['/var/lib/elasticsearch'] expect( json['settings']['path'] ).to include( 'data' => data_dir ) end end end end end end diff --git a/spec/helpers/acceptance/tests/security_shared_examples.rb b/spec/helpers/acceptance/tests/security_shared_examples.rb index 915d9c9..6557371 100644 --- a/spec/helpers/acceptance/tests/security_shared_examples.rb +++ b/spec/helpers/acceptance/tests/security_shared_examples.rb @@ -1,244 +1,231 @@ require 'json' require 'spec_utilities' require 'helpers/acceptance/tests/manifest_shared_examples' shared_examples 'security plugin manifest' do |credentials| let(:extra_manifest) do users = credentials.map do |username, meta| <<-USER #{meta[:changed] ? "notify { 'password change for #{username}' : } ~>" : ''} elasticsearch::user { '#{username}': password => '#{meta[:hash] ? meta[:hash] : meta[:plaintext]}', roles => #{meta[:roles].reduce({}) { |a, e| a.merge(e) }.keys}, } USER end.join("\n") roles = credentials.values.reduce({}) do |sum, user_metadata| # Collect all roles across users sum.merge user_metadata end[:roles].reduce({}) do |all_roles, role| all_roles.merge role end.reject do |_role, permissions| permissions.empty? end.map do |role, rights| <<-ROLE elasticsearch::role { '#{role}': privileges => #{rights} } ROLE end.join("\n") <<-MANIFEST - #{security_plugins} - #{users} #{roles} MANIFEST end include_examples( 'manifest application', not(credentials.values.map { |p| p[:changed] }.any?) ) end shared_examples 'secured request' do |test_desc, es_config, path, http_test, expected, user = nil, pass = nil| es_port = es_config['http.port'] describe port(es_port) do it 'open', :with_retries do should be_listening end end describe server :container do describe http( "https://localhost:#{es_port}#{path}", { :ssl => { :verify => false } }.merge((user and pass) ? { :basic_auth => [user, pass] } : {}) ) do it test_desc, :with_retries do expect(http_test.call(response)).to eq(expected) end end end end shared_examples 'security acceptance tests' do |es_config| describe 'security plugin operations', :if => vault_available?, :then_purge => true, :with_license => true, :with_certificates => true do rand_string = lambda { [*('a'..'z')].sample(8).join } admin_user = rand_string.call admin_password = rand_string.call admin = { admin_user => { :plaintext => admin_password, :roles => [{ 'superuser' => [] }] } } let(:manifest_class_parameters) do <<-MANIFEST api_basic_auth_password => '#{admin_password}', api_basic_auth_username => '#{admin_user}', api_ca_file => '#{@tls[:ca][:cert][:path]}', api_protocol => 'https', ca_certificate => '#{@tls[:ca][:cert][:path]}', certificate => '#{@tls[:clients].first[:cert][:path]}', keystore_password => '#{@keystore_password}', license => file('#{v[:elasticsearch_license_path]}'), private_key => '#{@tls[:clients].first[:key][:path]}', restart_on_change => true, - security_plugin => 'x-pack', ssl => true, validate_tls => true, MANIFEST end - let(:security_plugins) do - if semver(v[:elasticsearch_full_version].split('-').first) < semver('6.3.0') - <<-MANIFEST - elasticsearch::plugin { 'x-pack' : } - MANIFEST - else - '' - end - end - describe 'over tls' do user_one = rand_string.call user_two = rand_string.call user_one_pw = rand_string.call user_two_pw = rand_string.call describe 'user authentication' do username_passwords = { user_one => { :plaintext => user_one_pw, :roles => [{ 'superuser' => [] }] }, user_two => { :plaintext => user_two_pw, :roles => [{ 'superuser' => [] }] } }.merge(admin) username_passwords[user_two][:hash] = bcrypt(username_passwords[user_two][:plaintext]) include_examples('security plugin manifest', username_passwords) include_examples( 'secured request', 'denies unauthorized access', es_config, '/_cluster/health', lambda { |r| r.status }, 401 ) include_examples( 'secured request', "permits user #{user_one} access", es_config, '/_cluster/health', lambda { |r| r.status }, 200, user_one, user_one_pw ) include_examples( 'secured request', "permits user #{user_two} access", es_config, '/_cluster/health', lambda { |r| r.status }, 200, user_two, user_two_pw ) end describe 'changing passwords' do new_password = rand_string.call username_passwords = { user_one => { :plaintext => new_password, :changed => true, :roles => [{ 'superuser' => [] }] } } include_examples('security plugin manifest', username_passwords) include_examples( 'secured request', 'denies unauthorized access', es_config, '/_cluster/health', lambda { |r| r.status }, 401 ) include_examples( 'secured request', "permits user #{user_two} access with new password", es_config, '/_cluster/health', lambda { |r| r.status }, 200, user_one, new_password ) end describe 'roles' do password = rand_string.call username = rand_string.call user = { username => { :plaintext => password, :roles => [{ rand_string.call => { 'cluster' => [ 'cluster:monitor/health' ] } }] } } include_examples('security plugin manifest', user) include_examples( 'secured request', 'denies unauthorized access', es_config, '/_snapshot', lambda { |r| r.status }, 403, username, password ) include_examples( 'secured request', 'permits authorized access', es_config, '/_cluster/health', lambda { |r| r.status }, 200, username, password ) end end # describe 'with two instances' do # let(:ssl_params) do # @tls[:clients].each_with_index.map do |cert, i| # format(%( # Elasticsearch::Instance['es-%02d'] { # ca_certificate => '#{@tls[:ca][:cert][:path]}', # certificate => '#{cert[:cert][:path]}', # private_key => '#{cert[:key][:path]}', # keystore_password => '#{@keystore_password}', # } # ), i + 1) # end.join("\n") # end # ssl_instances = default_instances.map do |instance, meta| # new_config = if v[:elasticsearch_major_version] > 2 # { 'xpack.ssl.verification_mode' => 'none' } # else # { 'shield.ssl.hostname_verification' => false } # end # [ # instance, # { # 'config' => meta['config'].merge(new_config).merge( # 'discovery.zen.minimum_master_nodes' => default_instances.keys.size # ), # 'ssl' => true # } # ] # end.to_h # username = rand_string.call # password = rand_string.call # include_examples( # 'security plugin manifest', # ssl_instances, # username => { # :plaintext => password, # :roles => [{ 'superuser' => [] }] # } # ) # include_examples( # 'secured request', 'clusters between two nodes', # ssl_instances, '/_nodes', # lambda { |r| JSON.parse(r.body)['nodes'].size }, 2, # username, password # ) # end end end diff --git a/spec/helpers/class_shared_examples.rb b/spec/helpers/class_shared_examples.rb index e8cdc58..701de97 100644 --- a/spec/helpers/class_shared_examples.rb +++ b/spec/helpers/class_shared_examples.rb @@ -1,22 +1,19 @@ shared_examples 'class' do |init| it { should compile.with_all_deps } - # it { should contain_augeas('defaults') } + it { should contain_augeas('init_defaults') } + it { should contain_file('/etc/elasticsearch/elasticsearch.yml') } it { should contain_datacat('/etc/elasticsearch/elasticsearch.yml') } it { should contain_datacat_fragment('main_config') } it { should contain_service('elasticsearch') } - %w[elasticsearch.yml log4j2.properties].each do |file| - it { should contain_file("/etc/elasticsearch/#{file}") } - end - case init when :sysv # it { should contain_elasticsearch__service__init(name) } # it { should contain_elasticsearch_service_file("/etc/init.d/elasticsearch-#{name}") } # it { should contain_file('/etc/init.d/elasticsearch') } when :systemd # it { should contain_elasticsearch__service__systemd(name) } # it { should contain_file('/lib/systemd/system/elasticsearch.service') } # it { should contain_exec('systemd_reload') } end end