From f0bf83a1abac6c2ee7fe7257a37514319a96f0fa Mon Sep 17 00:00:00 2001 From: Sascha Doering Date: Thu, 6 Sep 2018 15:57:52 +0200 Subject: [PATCH] Add parameters to upstream and upstreammembers Add parameters to nginx::resource::upstream and nginx::resource::upstream::member which allows more configuration on upstreams as before. The only thing that we broke is that the members of an upstream must now be passed as a hash rather than an array. This also makes the sorting of keepalive to the end no longer necessary because there is now a parameter for it. And values for a nginx::resource::upstream::member can now be set as default for all members of an upstream or individually for each member inside the members hash. Of course, the explicit specification overrides the defaults. In general the changes have made more parameters available to nginx::resource::upstream and nginx::resource::upstream::member. In addition, one of the two templates for nginx::resource::upstream::member was disposed since it is no longer needed. Fixes GH-1222 --- README.md | 75 ++- manifests/init.pp | 3 +- manifests/resource/upstream.pp | 176 ++++-- manifests/resource/upstream/member.pp | 87 ++- spec/acceptance/nginx_proxy_spec.rb | 27 +- spec/acceptance/nginx_upstream_spec.rb | 113 ++++ spec/classes/nginx_spec.rb | 2 +- spec/defines/resource_upstream_spec.rb | 542 ++++++++++++++---- spec/type_aliases/size.rb | 19 + spec/type_aliases/time.rb | 19 + spec/type_aliases/upstreamcustomparameters.rb | 25 + spec/type_aliases/upstreammemberserver.rb | 15 + spec/type_aliases/upstreamstickyzone.rb | 28 + spec/type_aliases/upstreamzone.rb | 29 + templates/upstream/upstream_footer.epp | 85 +++ templates/upstream/upstream_footer.erb | 21 - templates/upstream/upstream_header.epp | 28 + templates/upstream/upstream_header.erb | 22 - templates/upstream/upstream_member.epp | 28 + templates/upstream/upstream_member.erb | 1 - templates/upstream/upstream_members.erb | 2 - types/size.pp | 1 + types/time.pp | 1 + types/upstreamcustomparameters.pp | 24 + types/upstreamdefaults.pp | 20 + types/upstreamleasttime.pp | 1 + types/upstreamleasttimehttp.pp | 1 + types/upstreamleasttimestream.pp | 1 + types/upstreammember.pp | 17 + types/upstreammemberdefaults.pp | 15 + types/upstreammembers.pp | 1 + types/upstreammemberserver.pp | 1 + types/upstreamsticky.pp | 28 + types/upstreamstickyzone.pp | 1 + types/upstreamzone.pp | 1 + 35 files changed, 1220 insertions(+), 240 deletions(-) create mode 100644 spec/acceptance/nginx_upstream_spec.rb create mode 100644 spec/type_aliases/size.rb create mode 100644 spec/type_aliases/time.rb create mode 100644 spec/type_aliases/upstreamcustomparameters.rb create mode 100644 spec/type_aliases/upstreammemberserver.rb create mode 100644 spec/type_aliases/upstreamstickyzone.rb create mode 100644 spec/type_aliases/upstreamzone.rb create mode 100644 templates/upstream/upstream_footer.epp delete mode 100644 templates/upstream/upstream_footer.erb create mode 100644 templates/upstream/upstream_header.epp delete mode 100644 templates/upstream/upstream_header.erb create mode 100644 templates/upstream/upstream_member.epp delete mode 100644 templates/upstream/upstream_member.erb delete mode 100644 templates/upstream/upstream_members.erb create mode 100644 types/size.pp create mode 100644 types/time.pp create mode 100644 types/upstreamcustomparameters.pp create mode 100644 types/upstreamdefaults.pp create mode 100644 types/upstreamleasttime.pp create mode 100644 types/upstreamleasttimehttp.pp create mode 100644 types/upstreamleasttimestream.pp create mode 100644 types/upstreammember.pp create mode 100644 types/upstreammemberdefaults.pp create mode 100644 types/upstreammembers.pp create mode 100644 types/upstreammemberserver.pp create mode 100644 types/upstreamsticky.pp create mode 100644 types/upstreamstickyzone.pp create mode 100644 types/upstreamzone.pp diff --git a/README.md b/README.md index 5daf844d7..7f8892a21 100644 --- a/README.md +++ b/README.md @@ -53,11 +53,20 @@ nginx::resource::server { 'www.puppetlabs.com': ```puppet nginx::resource::upstream { 'puppet_rack_app': - members => [ - 'localhost:3000', - 'localhost:3001', - 'localhost:3002', - ], + members => { + 'localhost:3000': + server => 'localhost' + port => 3000 + weight => 1 + 'localhost:3001': + server => 'localhost' + port => 3001 + weight => 1 + 'localhost:3002': + server => 'localhost' + port => 3002 + weight => 2 + }, } nginx::resource::server { 'rack.puppetlabs.com': @@ -85,6 +94,38 @@ nginx::resource::mailhost { 'domain1.example': } ``` +### Convert upstream members from Array to Hash + +The datatype Array for members of a nginx::resource::upstream is replaced by a Hash. The following configuration is no longer valid: + +```puppet +nginx::resource::upstream { 'puppet_rack_app': + members => [ + 'localhost:3000', + 'localhost:3001', + 'localhost:3002', + ], +} +``` + +From now on, the configuration must look like this: + +```puppet +nginx::resource::upstream { 'puppet_rack_app': + members => { + 'localhost:3000': + server => 'localhost' + port => 3000 + 'localhost:3001': + server => 'localhost' + port => 3001 + 'localhost:3002': + server => 'localhost' + port => 3002 + }, +} +``` + ## SSL configuration By default, creating a server resource will only create a HTTP server. To also @@ -137,9 +178,15 @@ nginx::nginx_upstreams: 'puppet_rack_app': ensure: present members: - - localhost:3000 - - localhost:3001 - - localhost:3002 + 'localhost:3000': + server: 'localhost' + port: 3000 + 'localhost:3001': + server: 'localhost' + port: 3001 + 'localhost:3002': + server: 'localhost' + port: 3002 nginx::nginx_servers: 'www.puppetlabs.com': www_root: '/var/www/www.puppetlabs.com' @@ -185,9 +232,15 @@ nginx::nginx_upstreams: 'syslog': upstream_context: 'stream' members: - - '10.0.0.1:514' - - '10.0.0.2:514' - - '10.0.0.3:514' + '10.0.0.1:514' + server: '10.0.0.1' + port: '514' + '10.0.0.2:514' + server: '10.0.0.2' + port: '514' + '10.0.0.3:514' + server: '10.0.0.3' + port: '514' ``` ## Nginx with precompiled Passenger diff --git a/manifests/init.pp b/manifests/init.pp index 8b04d01a3..521164132 100644 --- a/manifests/init.pp +++ b/manifests/init.pp @@ -169,6 +169,7 @@ $nginx_mailhosts_defaults = {}, $nginx_streamhosts = {}, $nginx_upstreams = {}, + Nginx::UpstreamMemberDefaults $nginx_upstream_defaults = {}, $nginx_servers = {}, $nginx_servers_defaults = {}, Boolean $purge_passenger_repo = true, @@ -180,7 +181,7 @@ contain 'nginx::config' contain 'nginx::service' - create_resources('nginx::resource::upstream', $nginx_upstreams) + create_resources('nginx::resource::upstream', $nginx_upstreams, $nginx_upstream_defaults) create_resources('nginx::resource::server', $nginx_servers, $nginx_servers_defaults) create_resources('nginx::resource::location', $nginx_locations, $nginx_locations_defaults) create_resources('nginx::resource::mailhost', $nginx_mailhosts, $nginx_mailhosts_defaults) diff --git a/manifests/resource/upstream.pp b/manifests/resource/upstream.pp index 28c61123f..d3665b2d0 100644 --- a/manifests/resource/upstream.pp +++ b/manifests/resource/upstream.pp @@ -3,13 +3,28 @@ # This definition creates a new upstream proxy entry for NGINX # # Parameters: -# [*members*] - Array of member URIs for NGINX to connect to. Must follow valid NGINX syntax. -# If omitted, individual members should be defined with nginx::resource::upstream::member # [*ensure*] - Enables or disables the specified location (present|absent) -# [*upstream_cfg_append*] - Hash of custom directives to put after other directives in upstream -# [*upstream_cfg_prepend*] - It expects a hash with custom directives to put before anything else inside upstream -# [*upstream_fail_timeout*] - Set the fail_timeout for the upstream. Default is 10 seconds - As that is what Nginx does normally. -# [*upstream_max_fails*] - Set the max_fails for the upstream. Default is to use nginx default value which is 1. +# [*context*] - Set the type of this upstream (http|stream). +# [*members*] - Hash of member URIs for NGINX to connect to. Must follow valid NGINX syntax. +# If omitted, individual members should be defined with nginx::resource::upstream::member +# [*members_tag*] - Restrict collecting the exported members for this upstream with a tag. +# [*member_defaults*] - Specify default settings added to each member of this upstream. +# [*hash*] - Activate the hash load balancing method (https://nginx.org/en/docs/http/ngx_http_upstream_module.html#hash). +# [*ip_hash*] - Activate ip_hash for this upstream (https://nginx.org/en/docs/http/ngx_http_upstream_module.html#ip_hash). +# [*keepalive*] - Set the maximum number of idle keepalive connections (https://nginx.org/en/docs/http/ngx_http_upstream_module.html#keepalive). +# [*keepalive_requests*] - Sets the maximum number of requests that can be served through one keepalive connection (https://nginx.org/en/docs/http/ngx_http_upstream_module.html#keepalive_requests). +# [*keepalive_timeout*] - Sets a timeout during which an idle keepalive connection to an upstream server will stay open (https://nginx.org/en/docs/http/ngx_http_upstream_module.html#keepalive_timeout). +# [*least_conn*] - Activate the least_conn load balancing method (https://nginx.org/en/docs/http/ngx_http_upstream_module.html#least_conn). +# [*least_time*] - Activate the least_time load balancing method (https://nginx.org/en/docs/http/ngx_http_upstream_module.html#least_time). +# [*ntlm*] - Allow NTLM authentication (https://nginx.org/en/docs/http/ngx_http_upstream_module.html#ntlm). +# [*queue_max*] - Set the maximum number of queued requests (https://nginx.org/en/docs/http/ngx_http_upstream_module.html#queue). +# [*queue_timeout*] - Set the timeout for the queue (https://nginx.org/en/docs/http/ngx_http_upstream_module.html#queue). +# [*random*] - Activate the random load balancing method (https://nginx.org/en/docs/http/ngx_http_upstream_module.html#random). +# [*statefile*] - Specifies a file that keeps the state of the dynamically configurable group (https://nginx.org/en/docs/http/ngx_http_upstream_module.html#state). +# [*sticky*] - Enables session affinity (https://nginx.org/en/docs/http/ngx_http_upstream_module.html#sticky). +# [*zone*] - Defines the name and optional the size of the shared memory zone (https://nginx.org/en/docs/http/ngx_http_upstream_module.html#zone). +# [*cfg_append*] - Hash of custom directives to put after other directives in upstream +# [*cfg_prepend*] - It expects a hash with custom directives to put before anything else inside upstream # # Actions: # @@ -18,82 +33,121 @@ # Sample Usage: # nginx::resource::upstream { 'proxypass': # ensure => present, -# members => [ -# 'localhost:3000', -# 'localhost:3001', -# 'localhost:3002', -# ], +# members => { +# 'localhost:3001' => { +# server => 'localhost', +# port => 3000, +# }, +# 'localhost:3002' => { +# server => 'localhost', +# port => 3002, +# }, +# 'localhost:3003' => { +# server => 'localhost', +# port => 3003, +# }, +# }, # } # # Custom config example to use ip_hash, and 20 keepalive connections # create a hash with any extra custom config you want. -# $my_config = { -# 'ip_hash' => '', -# 'keepalive' => '20', -# } # nginx::resource::upstream { 'proxypass': -# ensure => present, -# members => [ -# 'localhost:3000', -# 'localhost:3001', -# 'localhost:3002', -# ], -# upstream_cfg_prepend => $my_config, +# ensure => present, +# members => { +# 'localhost:3001' => { +# server => 'localhost', +# port => 3000, +# }, +# 'localhost:3002' => { +# server => 'localhost', +# port => 3002, +# }, +# 'localhost:3003' => { +# server => 'localhost', +# port => 3003, +# }, +# }, +# ip_hash => true, +# keepalive => 20, # } +# define nginx::resource::upstream ( - Optional[Array] $members = undef, - $members_tag = undef, - Enum['present', 'absent'] $ensure = 'present', - Optional[Hash] $upstream_cfg_append = undef, - Optional[Hash] $upstream_cfg_prepend = undef, - $upstream_fail_timeout = '10s', - $upstream_max_fails = undef, - Enum['http', 'stream'] $upstream_context = 'http', + Enum['present', 'absent'] $ensure = 'present', + Enum['http', 'stream'] $context = 'http', + Nginx::UpstreamMembers $members = {}, + Optional[String[1]] $members_tag = undef, + Nginx::UpstreamMemberDefaults $member_defaults = {}, + Optional[String[1]] $hash = undef, + Boolean $ip_hash = false, + Optional[Integer[1]] $keepalive = undef, + Optional[Integer[1]] $keepalive_requests = undef, + Optional[Nginx::Time] $keepalive_timeout = undef, + Boolean $least_conn = false, + Optional[Nginx::UpstreamLeastTime] $least_time = undef, + Boolean $ntlm = false, + Optional[Integer] $queue_max = undef, + Optional[Nginx::Time] $queue_timeout = undef, + Optional[String[1]] $random = undef, + Optional[Stdlib::Unixpath] $statefile = undef, + Optional[Nginx::UpstreamSticky] $sticky = undef, + Optional[Nginx::UpstreamZone] $zone = undef, + Nginx::UpstreamCustomParameters $cfg_append = {}, + Nginx::UpstreamCustomParameters $cfg_prepend = {}, ) { if ! defined(Class['nginx']) { fail('You must include the nginx base class before using any defined resources') } - $root_group = $nginx::root_group - - $ensure_real = $ensure ? { - 'absent' => absent, - default => present, + if $least_time { + if $context == 'http' and ! ($least_time =~ Nginx::UpstreamLeastTimeHttp) { + fail('The parameter "least_time" does not match the datatype "Nginx::UpstreamLeastTimeHttp"') + } + if $context == 'stream' and ! ($least_time =~ Nginx::UpstreamLeastTimeStream) { + fail('The parameter "least_time" does not match the datatype "Nginx::UpstreamLeastTimeStream"') + } } - $conf_dir_real = $upstream_context ? { - 'stream' => 'conf.stream.d', - default => 'conf.d', + $conf_dir = $context ? { + 'stream' => "${nginx::config::conf_dir}/conf.stream.d", + default => "${nginx::config::conf_dir}/conf.d", } - $conf_dir = "${nginx::config::conf_dir}/${conf_dir_real}" - Concat { owner => 'root', - group => $root_group, + group => $nginx::root_group, mode => '0644', } - concat { "${nginx::conf_dir}/${conf_dir_real}/${name}-upstream.conf": - ensure => $ensure_real, + concat { "${conf_dir}/${name}-upstream.conf": + ensure => $ensure, notify => Class['::nginx::service'], require => File[$conf_dir], } - # Uses: $name, $upstream_cfg_prepend concat::fragment { "${name}_upstream_header": - target => "${nginx::conf_dir}/${conf_dir_real}/${name}-upstream.conf", + target => "${conf_dir}/${name}-upstream.conf", order => '10', - content => template('nginx/upstream/upstream_header.erb'), + content => epp('nginx/upstream/upstream_header.epp', { + cfg_prepend => $cfg_prepend, + name => $name, + }), } if $members != undef { - # Uses: $members, $upstream_fail_timeout - concat::fragment { "${name}_upstream_members": - target => "${nginx::conf_dir}/${conf_dir_real}/${name}-upstream.conf", - order => '50', - content => template('nginx/upstream/upstream_members.erb'), + $members.each |$member,$values| { + $member_values = merge($member_defaults,$values,{'upstream' => $name,'context' => $context}) + + if $context == 'stream' and $member_values['route'] { + fail('The parameter "route" is not available for upstreams with context "stream"') + } + if $context == 'stream' and $member_values['state'] and $member_values['state'] == 'drain' { + fail('The state "drain" is not available for upstreams with context "stream"') + } + + nginx::resource::upstream::member { $member: + * => $member_values, + } } } else { # Collect exported members: @@ -105,8 +159,24 @@ } concat::fragment { "${name}_upstream_footer": - target => "${nginx::conf_dir}/${conf_dir_real}/${name}-upstream.conf", + target => "${conf_dir}/${name}-upstream.conf", order => '90', - content => template('nginx/upstream/upstream_footer.erb'), + content => epp('nginx/upstream/upstream_footer.epp', { + cfg_append => $cfg_append, + hash => $hash, + ip_hash => $ip_hash, + keepalive => $keepalive, + keepalive_requests => $keepalive_requests, + keepalive_timeout => $keepalive_timeout, + least_conn => $least_conn, + least_time => $least_time, + ntlm => $ntlm, + queue_max => $queue_max, + queue_timeout => $queue_timeout, + random => $random, + statefile => $statefile, + sticky => $sticky, + zone => $zone, + }), } } diff --git a/manifests/resource/upstream/member.pp b/manifests/resource/upstream/member.pp index 7ce6ea5a9..075f564a1 100644 --- a/manifests/resource/upstream/member.pp +++ b/manifests/resource/upstream/member.pp @@ -9,46 +9,95 @@ # # # Parameters: -# [*ensure*] - Enables or disables the specified member (present|absent) -# [*upstream*] - The name of the upstream resource -# [*server*] - Hostname or IP of the upstream member server -# [*port*] - Port of the listening service on the upstream member -# [*upstream_fail_timeout*] - Set the fail_timeout for the upstream. Default is 10 seconds -# +# [*upstream*] - The name of the upstream resource +# [*ensure*] - Enables or disables the specified member (present|absent) +# [*context*] - Set the type of this upstream (http|stream). +# [*server*] - Hostname or IP of the upstream member server +# [*port*] - Port of the listening service on the upstream member +# [*weight*] - Set the weight for this upstream member +# [*max_conns*] - Set the max_conns for this upstream member +# [*max_fails*] - Set the max_fails for this upstream member +# [*fail_timeout*] - Set the fail_timeout for this upstream member +# [*backup*] - Activate backup for this upstream member +# [*resolve*] - Activate resolve for this upstream member +# [*route*] - Set the route for this upstream member +# [*service*] - Set the service for this upstream member +# [*slow_start*] - Set the slow_start for this upstream member +# [*state*] - Set the state for this upstream member +# [*params_prepend*] - prepend a parameter for this upstream member +# [*params_append*] - append a paremeter for this upstream member +# [*comment*] - Add a comment for this upstream member # # Examples: # # Exporting the resource on a upstream member server: # # @@nginx::resource::upstream::member { $trusted['certname']: -# ensure => present, -# upstream => 'proxypass', -# server => $facts['networking']['ip'], -# port => 3000, +# ensure => present, +# upstream => 'proxypass', +# server => $facts['networking']['ip'], +# port => 3000, # } # # # Collecting the resource on the NGINX server: # # nginx::resource::upstream { 'proxypass': -# ensure => present, +# ensure => present, # } # define nginx::resource::upstream::member ( - $upstream, - $server, - Enum['present', 'absent'] $ensure = 'present', - Integer $port = 80, - $upstream_fail_timeout = '10s', + String[1] $upstream, + Enum['present', 'absent'] $ensure = 'present', + Enum['http', 'stream'] $context = 'http', + Optional[Nginx::UpstreamMemberServer] $server = $name, + Stdlib::Port $port = 80, + Optional[Integer[1]] $weight = undef, + Optional[Integer[1]] $max_conns = undef, + Optional[Integer[1]] $max_fails = undef, + Optional[Nginx::Time] $fail_timeout = undef, + Boolean $backup = false, + Boolean $resolve = false, + Optional[String[1]] $route = undef, + Optional[String[1]] $service = undef, + Optional[Nginx::Time] $slow_start = undef, + Optional[Enum['drain','down']] $state = undef, + Optional[String[1]] $params_prepend = undef, + Optional[String[1]] $params_append = undef, + Optional[String[1]] $comment = undef, ) { if ! defined(Class['nginx']) { fail('You must include the nginx base class before using any defined resources') } - # Uses: $server, $port, $upstream_fail_timeout + $conf_dir = $context ? { + 'stream' => "${nginx::config::conf_dir}/conf.stream.d", + default => "${nginx::config::conf_dir}/conf.d", + } + + $_server = ($server =~ Pattern[/^unix:\/([^\/\0]+\/*)*$/]) ? { + true => $server, + false => "${server}:${port}", + } + concat::fragment { "${upstream}_upstream_member_${name}": - target => "${nginx::conf_dir}/conf.d/${upstream}-upstream.conf", + target => "${conf_dir}/${upstream}-upstream.conf", order => 40, - content => template('nginx/upstream/upstream_member.erb'), + content => epp('nginx/upstream/upstream_member.epp', { + _server => $_server, + backup => $backup, + comment => $comment, + fail_timeout => $fail_timeout, + max_conns => $max_conns, + max_fails => $max_fails, + params_append => $params_append, + params_prepend => $params_prepend, + resolve => $resolve, + route => $route, + service => $service, + slow_start => $slow_start, + state => $state, + weight => $weight, + }), } } diff --git a/spec/acceptance/nginx_proxy_spec.rb b/spec/acceptance/nginx_proxy_spec.rb index 839f854bc..9a80269a3 100644 --- a/spec/acceptance/nginx_proxy_spec.rb +++ b/spec/acceptance/nginx_proxy_spec.rb @@ -6,11 +6,20 @@ class { 'nginx': } nginx::resource::upstream { 'puppet_rack_app': ensure => present, - members => [ - 'localhost:3000', - 'localhost:3001', - 'localhost:3002', - ], + members => { + 'localhost:3000' => { + server => 'localhost', + port => 3000, + }, + 'localhost:3001' => { + server => 'localhost', + port => 3001, + }, + 'localhost:3002' => { + server => 'localhost', + port => 3002, + }, + }, } nginx::resource::server { 'rack.puppetlabs.com': ensure => present, @@ -23,10 +32,10 @@ class { 'nginx': } describe file('/etc/nginx/conf.d/puppet_rack_app-upstream.conf') do it { is_expected.to be_file } - it { is_expected.to contain 'server localhost:3000' } - it { is_expected.to contain 'server localhost:3001' } - it { is_expected.to contain 'server localhost:3002' } - it { is_expected.not_to contain 'server localhost:3003' } + it { is_expected.to contain 'server localhost:3000' } + it { is_expected.to contain 'server localhost:3001' } + it { is_expected.to contain 'server localhost:3002' } + it { is_expected.not_to contain 'server localhost:3003' } end describe file('/etc/nginx/sites-available/rack.puppetlabs.com.conf') do diff --git a/spec/acceptance/nginx_upstream_spec.rb b/spec/acceptance/nginx_upstream_spec.rb new file mode 100644 index 000000000..c4c6b35a7 --- /dev/null +++ b/spec/acceptance/nginx_upstream_spec.rb @@ -0,0 +1,113 @@ +require 'spec_helper_acceptance' + +describe 'nginx::resource::upstream define:' do + it 'runs successfully' do + pp = " + class { 'nginx': } + nginx::resource::upstream { 'production': + ensure => present, + ip_hash => true, + keepalive => 16, + member_defaults => { + max_conns => 20, + max_fails => 20, + fail_timeout => '20s', + }, + members => { + 'appserver_01' => { + server => '10.10.10.1', + port => 80, + weight => 2, + max_conns => 10, + max_fails => 10, + fail_timeout => '10s', + comment => 'Appserver 01', + }, + 'appserver_02' => { + server => '10.10.10.2', + port => 80, + weight => 3, + max_conns => 15, + max_fails => 15, + fail_timeout => '15s', + comment => 'Appserver 02', + }, + 'appserver_03' => { + server => '10.10.10.3', + port => 80, + backup => true, + comment => 'Appserver 03', + }, + }, + zone => 'production 64k', + } + nginx::resource::upstream { 'socket': + ensure => present, + member_defaults => { + max_conns => 20, + max_fails => 20, + fail_timeout => '20s', + }, + members => { + 'socket_01' => { + server => 'unix:/var/run/socket_01.sock', + }, + 'socket_02' => { + server => 'unix:/var/run/socket_02.sock', + }, + }, + zone => 'socket 64k', + } + + nginx::resource::server { 'www.puppetlabs.com': + ensure => present, + proxy => 'http://production', + } + nginx::resource::server { 'socket.puppetlabs.com': + ensure => present, + proxy => 'http://socket', + } + " + apply_manifest(pp, catch_failures: true) + end + + describe file('/etc/nginx/conf.d/production-upstream.conf') do + it { is_expected.to be_file } + it { is_expected.to contain '# MANAGED BY PUPPET' } + it { is_expected.to contain 'upstream production {' } + it { is_expected.to contain ' server 10.10.10.1:80 weight=2 max_conns=10 max_fails=10 fail_timeout=10s; # Appserver 01' } + it { is_expected.to contain ' server 10.10.10.2:80 weight=3 max_conns=15 max_fails=15 fail_timeout=15s; # Appserver 02' } + it { is_expected.to contain ' server 10.10.10.3:80 max_conns=20 max_fails=20 fail_timeout=20s backup; # Appserver 03' } + it { is_expected.to contain ' ip_hash;' } + it { is_expected.to contain ' zone production 64k;' } + it { is_expected.to contain ' keepalive 16;' } + end + describe file('/etc/nginx/sites-available/www.puppetlabs.com.conf') do + it { is_expected.to be_file } + it { is_expected.to contain '# MANAGED BY PUPPET' } + it { is_expected.to contain ' proxy_pass http://production;' } + end + + describe file('/etc/nginx/conf.d/socket-upstream.conf') do + it { is_expected.to be_file } + it { is_expected.to contain '# MANAGED BY PUPPET' } + it { is_expected.to contain 'upstream socket {' } + it { is_expected.to contain ' server unix:/var/run/socket_01.sock max_conns=20 max_fails=20 fail_timeout=20s;' } + it { is_expected.to contain ' server unix:/var/run/socket_02.sock max_conns=20 max_fails=20 fail_timeout=20s;' } + it { is_expected.to contain ' zone socket 64k;' } + end + describe file('/etc/nginx/sites-available/socket.puppetlabs.com.conf') do + it { is_expected.to be_file } + it { is_expected.to contain '# MANAGED BY PUPPET' } + it { is_expected.to contain ' proxy_pass http://socket;' } + end + + describe service('nginx') do + it { is_expected.to be_running } + it { is_expected.to be_enabled } + end + + describe port(80) do + it { is_expected.to be_listening } + end +end diff --git a/spec/classes/nginx_spec.rb b/spec/classes/nginx_spec.rb index 6c6633229..9d8b4ec95 100644 --- a/spec/classes/nginx_spec.rb +++ b/spec/classes/nginx_spec.rb @@ -9,7 +9,7 @@ let :params do { - nginx_upstreams: { 'upstream1' => { 'members' => ['localhost:3000'] } }, + nginx_upstreams: { 'upstream1' => { 'members' => { 'localhost' => { 'port' => 3000 } } } }, nginx_servers: { 'test2.local' => { 'www_root' => '/' } }, nginx_servers_defaults: { 'listen_options' => 'default_server' }, nginx_locations: { 'test2.local' => { 'server' => 'test2.local', 'www_root' => '/' } }, diff --git a/spec/defines/resource_upstream_spec.rb b/spec/defines/resource_upstream_spec.rb index 314b93029..454a71508 100644 --- a/spec/defines/resource_upstream_spec.rb +++ b/spec/defines/resource_upstream_spec.rb @@ -12,7 +12,21 @@ let :default_params do { - members: ['test'] + http: { + context: 'http', + members: { 'member-http' => {} } + }, + stream: { + context: 'stream', + members: { 'member-stream' => {} } + } + } + end + + let :conf_d_pathes do + { + http: '/etc/nginx/conf.d', + stream: '/etc/nginx/conf.stream.d' } end @@ -23,117 +37,445 @@ end describe 'os-independent items' do - describe 'basic assumptions' do - let(:params) { default_params } + ## + ## check that http is the default + ## + describe 'basic assumptions for default upstreams' do + let(:params) { default_params[:http] } - it { is_expected.to contain_concat("/etc/nginx/conf.d/#{title}-upstream.conf").that_requires('File[/etc/nginx/conf.d]') } - it { is_expected.to contain_concat__fragment("#{title}_upstream_header").with_content(%r{upstream #{title}}) } + it { + is_expected.to compile.with_all_deps + } + it { + is_expected.to contain_concat("/etc/nginx/conf.d/#{title}-upstream.conf"). + that_requires('File[/etc/nginx/conf.d]') + } + it { + is_expected.to contain_concat__fragment("#{title}_upstream_header"). + with_content(%r{upstream #{title}}). + with( + 'target' => "/etc/nginx/conf.d/#{title}-upstream.conf", + 'order' => 10 + ) + } + it { + is_expected.to contain_concat__fragment("#{title}_upstream_member_#{params[:members].keys[0]}"). + with( + 'target' => "/etc/nginx/conf.d/#{title}-upstream.conf", + 'order' => 40 + ) + } + it { + is_expected.to contain_concat__fragment("#{title}_upstream_footer"). + with( + 'target' => "/etc/nginx/conf.d/#{title}-upstream.conf", + 'order' => 90 + ). + with_content("}\n") + } + end - it do - is_expected.to contain_concat__fragment("#{title}_upstream_header").with( - 'target' => "/etc/nginx/conf.d/#{title}-upstream.conf", - 'order' => 10 - ) - end + ## + ## check http and stream upstreams + ## + %w[http stream].each do |upstreamcontext| + describe "basic assumptions for #{upstreamcontext} upstreams" do + let(:params) { default_params[upstreamcontext.to_sym] } + let(:conf_d_path) { conf_d_pathes[upstreamcontext.to_sym] } - it do - is_expected.to contain_concat__fragment("#{title}_upstream_members").with( - 'target' => "/etc/nginx/conf.d/#{title}-upstream.conf", - 'order' => 50 - ) + it { + is_expected.to compile.with_all_deps + } + it { + is_expected.to contain_concat("#{conf_d_path}/#{title}-upstream.conf"). + that_requires("File[#{conf_d_path}]") + } + it { + is_expected.to contain_concat__fragment("#{title}_upstream_header"). + with_content(%r{upstream #{title}}). + with( + 'target' => "#{conf_d_path}/#{title}-upstream.conf", + 'order' => 10 + ) + } + it { + is_expected.to contain_concat__fragment("#{title}_upstream_member_#{params[:members].keys[0]}"). + with( + 'target' => "#{conf_d_path}/#{title}-upstream.conf", + 'order' => 40 + ) + } + it { + is_expected.to contain_concat__fragment("#{title}_upstream_footer"). + with( + 'target' => "#{conf_d_path}/#{title}-upstream.conf", + 'order' => 90 + ). + with_content("}\n") + } end - it do - is_expected.to contain_concat__fragment("#{title}_upstream_footer").with( - 'target' => "/etc/nginx/conf.d/#{title}-upstream.conf", - 'order' => 90 - ).with_content("}\n") - end - end + ## + ## check the upstream template + ## + describe 'upstream.conf template content' do + ## + ## check the default + ## + context "when only a server is specified in a #{upstreamcontext} upstream" do + let(:params) { default_params[upstreamcontext.to_sym] } + let(:conf_d_path) { conf_d_pathes[upstreamcontext.to_sym] } - describe 'upstream.conf template content' do - [ - { - title: 'should contain ordered prepended directives', - attr: 'upstream_cfg_prepend', - fragment: 'header', - value: { - 'test3' => 'test value 3', - 'test6' => { 'subkey1' => %w[subvalue1 subvalue2] }, - 'keepalive' => 'keepalive 1', - 'test2' => 'test value 2', - 'test5' => { 'subkey1' => 'subvalue1' }, - 'test4' => ['test value 1', 'test value 2'] - }, - match: [ - ' test2 test value 2;', - ' test3 test value 3;', - ' test4 test value 1;', - ' test4 test value 2;', - ' test5 subkey1 subvalue1;', - ' test6 subkey1 subvalue1;', - ' test6 subkey1 subvalue2;', - ' keepalive keepalive 1;' - ] - }, - { - title: 'should set server', - attr: 'members', - fragment: 'members', - value: %w[test3 test1 test2], - match: [ - ' server test3 fail_timeout=10s;', - ' server test1 fail_timeout=10s;', - ' server test2 fail_timeout=10s;' - ] - }, - { - title: 'should contain ordered appended directives', - attr: 'upstream_cfg_append', - fragment: 'footer', - value: { - 'test3' => 'test value 3', - 'test6' => { 'subkey1' => %w[subvalue1 subvalue2] }, - 'keepalive' => 'keepalive 1', - 'test2' => 'test value 2', - 'test5' => { 'subkey1' => 'subvalue1' }, - 'test4' => ['test value 1', 'test value 2'] - }, - match: [ - ' test2 test value 2;', - ' test3 test value 3;', - ' test4 test value 1;', - ' test4 test value 2;', - ' test5 subkey1 subvalue1;', - ' test6 subkey1 subvalue1;', - ' test6 subkey1 subvalue2;', - ' keepalive keepalive 1;' - ] - } - ].each do |param| - context "when #{param[:attr]} is #{param[:value]}" do - let(:params) { default_params.merge(param[:attr].to_sym => param[:value]) } - - it { is_expected.to contain_concat("/etc/nginx/conf.d/#{title}-upstream.conf").with_mode('0644') } - it { is_expected.to contain_concat__fragment("#{title}_upstream_#{param[:fragment]}") } - it param[:title] do - lines = catalogue.resource('concat::fragment', "#{title}_upstream_#{param[:fragment]}").send(:parameters)[:content].split("\n") - expect(lines & Array(param[:match])).to eq(Array(param[:match])) - Array(param[:notmatch]).each do |item| - is_expected.to contain_concat__fragment("#{title}_upstream_#{param[:fragment]}").without_content(item) + it { + is_expected.to compile.with_all_deps + } + it { + is_expected.to contain_concat("#{conf_d_path}/#{title}-upstream.conf"). + with_mode('0644') + } + it { + is_expected.to contain_concat__fragment("#{title}_upstream_header"). + with_content("# MANAGED BY PUPPET\nupstream #{title} {\n") + } + it { + is_expected.to contain_concat__fragment("#{title}_upstream_member_#{params[:members].keys[0]}"). + with_content(" server #{params[:members].keys[0]}:80;\n") + } + it { + is_expected.to contain_concat__fragment("#{title}_upstream_footer"). + with_content("}\n") + } + end + + ## + ## check the upstream parameters + ## + [ + { + value: { hash: '$remote_addr consistent' }, + match: 'hash $remote_addr consistent' + }, + { + value: { keepalive: 20 }, + match: 'keepalive 20' + }, + { + value: { keepalive_requests: 20 }, + match: 'keepalive_requests 20' + }, + { + value: { keepalive_timeout: '20s' }, + match: 'keepalive_timeout 20s' + }, + { + value: { least_conn: true }, + match: 'least_conn' + }, + { + value: { least_conn: false }, + match: false + }, + { + value: { least_time: 'last_byte inflight' }, + match: 'least_time last_byte inflight' + }, + { + value: { least_time: 'header inflight' }, + match: 'least_time header inflight', + fails: { stream: 'The parameter "least_time" does not match the datatype "Nginx::UpstreamLeastTimeStream"' } + }, + { + value: { least_time: 'first_byte inflight' }, + match: 'least_time first_byte inflight', + fails: { http: 'The parameter "least_time" does not match the datatype "Nginx::UpstreamLeastTimeHttp"' } + }, + { + value: { ntlm: true }, + match: 'ntlm' + }, + { + value: { ntlm: false }, + match: false + }, + { + value: { queue_max: 20 }, + match: 'queue 20' + }, + { + value: { queue_max: 20, queue_timeout: '20s' }, + match: 'queue 20 timeout=20s' + }, + { + value: { random: 'two least_conn' }, + match: 'random two least_conn' + }, + { + value: { statefile: '/var/lib/nginx/state/servers.conf' }, + match: 'state /var/lib/nginx/state/servers.conf' + }, + { + value: { sticky: { cookie: { name: 'srv_id', expires: '1h', domain: '.example.com', httponly: true, secure: true, path: '/' } } }, + match: 'sticky cookie name=srv_id expires=1h domain=.example.com httponly secure path=/' + }, + { + value: { sticky: { route: '$route_cookie $route_uri' } }, + match: 'sticky route $route_cookie $route_uri' + }, + { + value: { sticky: { learn: { create: '$upstream_cookie_examplecookie', lookup: '$cookie_examplecookie', zone: 'client_sessions:1m' } } }, + match: 'sticky learn create=$upstream_cookie_examplecookie lookup=$cookie_examplecookie zone=client_sessions:1m' + }, + { + value: { zone: 'frontend 1M' }, + match: 'zone frontend 1M' + }, + { + value: { zone: 'backend 64k' }, + match: 'zone backend 64k' + } + ].each do |upstream_parameter| + context "when #{upstream_parameter[:value].keys[0]} is set to #{upstream_parameter[:value]} in #{upstreamcontext} upstream" do + let(:params) { default_params[upstreamcontext.to_sym].merge(upstream_parameter[:value]) } + let(:conf_d_path) { conf_d_pathes[upstreamcontext.to_sym] } + + if upstream_parameter.key?(:fails) && upstream_parameter[:fails].key?(upstreamcontext.to_sym) + it { + is_expected.to raise_error(Puppet::Error, %r{#{upstream_parameter[:fails][upstreamcontext.to_sym]}}) + } + next + end + + it { + is_expected.to compile.with_all_deps + } + it { + is_expected.to contain_concat("#{conf_d_path}/#{title}-upstream.conf"). + with_mode('0644') + } + it { + is_expected.to contain_concat__fragment("#{title}_upstream_header"). + with_content("# MANAGED BY PUPPET\nupstream #{title} {\n") + } + it { + is_expected.to contain_concat__fragment("#{title}_upstream_member_#{params[:members].keys[0]}"). + with_content(" server #{params[:members].keys[0]}:80;\n") + } + + if upstream_parameter[:match] != false + it { + is_expected.to contain_concat__fragment("#{title}_upstream_footer"). + with_content(" #{upstream_parameter[:match]};\n}\n") + } + else + it { + is_expected.to contain_concat__fragment("#{title}_upstream_footer"). + with_content("}\n") + } end end end - end - context 'when ensure => absent' do - let :params do - default_params.merge( - ensure: 'absent' - ) + ## + ## check the upstream member parameters + ## + [ + { + value: { unix: { server: 'unix:/tmp/backend3' } }, + match: 'unix:/tmp/backend3;' + }, + { + value: { member1: {} }, + match: 'member1:80;' + }, + { + value: { member1: { server: '127.0.0.1' } }, + match: '127.0.0.1:80;' + }, + { + value: { member1: { server: '127.0.0.1', port: 8080 } }, + match: '127.0.0.1:8080;' + }, + { + value: { member1: { weight: 20 } }, + match: 'member1:80 weight=20;' + }, + { + value: { member1: { max_conns: 20 } }, + match: 'member1:80 max_conns=20;' + }, + { + value: { member1: { max_fails: 20 } }, + match: 'member1:80 max_fails=20;' + }, + { + value: { member1: { fail_timeout: '20s' } }, + match: 'member1:80 fail_timeout=20s;' + }, + { + value: { member1: { backup: true } }, + match: 'member1:80 backup;' + }, + { + value: { member1: { backup: false } }, + match: 'member1:80;' + }, + { + value: { member1: { resolve: true } }, + match: 'member1:80 resolve;' + }, + { + value: { member1: { resolve: false } }, + match: 'member1:80;' + }, + { + value: { member1: { route: 'a' } }, + match: 'member1:80 route=a;', + fails: { stream: 'The parameter "route" is not available for upstreams with context "stream"' } + }, + { + value: { member1: { service: 'member1.backend' } }, + match: 'member1:80 service=member1.backend;' + }, + { + value: { member1: { slow_start: '20s' } }, + match: 'member1:80 slow_start=20s;' + }, + { + value: { member1: { state: 'drain' } }, + match: 'member1:80 drain;', + fails: { stream: 'The state "drain" is not available for upstreams with context "stream"' } + }, + { + value: { member1: { state: 'down' } }, + match: 'member1:80 down;' + }, + { + value: { member1: { params_prepend: 'member=1', weight: 20 } }, + match: 'member1:80 member=1 weight=20;' + }, + { + value: { member1: { params_append: 'member=1', weight: 20 } }, + match: 'member1:80 weight=20 member=1;' + }, + { + value: { member1: { comment: 'member1' } }, + match: 'member1:80; # member1' + } + ].each do |upstream_member_parameter| + context "when members is set to #{upstream_member_parameter[:value]}" do + let(:params) { default_params[upstreamcontext.to_sym].merge(members: upstream_member_parameter[:value]) } + let(:conf_d_path) { conf_d_pathes[upstreamcontext.to_sym] } + + if upstream_member_parameter.key?(:fails) && upstream_member_parameter[:fails].key?(upstreamcontext.to_sym) + it { + is_expected.to raise_error(Puppet::Error, %r{#{upstream_member_parameter[:fails][upstreamcontext.to_sym]}}) + } + next + end + + it { + is_expected.to compile.with_all_deps + } + it { + is_expected.to contain_concat("#{conf_d_path}/#{title}-upstream.conf"). + with_mode('0644') + } + it { + is_expected.to contain_concat__fragment("#{title}_upstream_header"). + with_content("# MANAGED BY PUPPET\nupstream #{title} {\n") + } + it { + is_expected.to contain_concat__fragment("#{title}_upstream_member_#{upstream_member_parameter[:value].keys[0]}"). + with_content(" server #{upstream_member_parameter[:match]}\n") + } + it { + is_expected.to contain_concat__fragment("#{title}_upstream_footer"). + with_content("}\n") + } + end end - it { is_expected.to contain_concat("/etc/nginx/conf.d/#{title}-upstream.conf").with_ensure('absent') } + ## + ## check cfg_prepend and cfg_append + ## + [ + { + parameter: 'cfg_prepend', + values: { + 'k2' => 'v2', + 'k5' => { 'k51' => %w[v51 v52] }, + 'k1' => 'v2', + 'k4' => { 'k41' => 'v41' }, + 'k3' => %w[v31 v32] + }, + match: " k2 v2;\n k5 k51 v51;\n k5 k51 v52;\n k1 v2;\n k4 k41 v41;\n k3 v31;\n k3 v32;\n", + fragment: 'header' + }, + { + parameter: 'cfg_append', + values: { + 'k2' => 'v2', + 'k5' => { 'k51' => %w[v51 v52] }, + 'k1' => 'v2', + 'k4' => { 'k41' => 'v41' }, + 'k3' => %w[v31 v32] + }, + match: " k2 v2;\n k5 k51 v51;\n k5 k51 v52;\n k1 v2;\n k4 k41 v41;\n k3 v31;\n k3 v32;\n", + fragment: 'footer' + } + ].each do |upstream_cfg_extension| + context "when #{upstream_cfg_extension[:parameter]} is set to #{upstream_cfg_extension[:values]} in #{upstreamcontext} upstream" do + let(:params) { default_params[upstreamcontext.to_sym].merge(upstream_cfg_extension[:parameter].to_sym => upstream_cfg_extension[:values]) } + let(:conf_d_path) { conf_d_pathes[upstreamcontext.to_sym] } + + it { + is_expected.to compile.with_all_deps + } + it { + is_expected.to contain_concat("#{conf_d_path}/#{title}-upstream.conf"). + with_mode('0644') + } + if upstream_cfg_extension[:fragment] == 'header' + it { + is_expected.to contain_concat__fragment("#{title}_upstream_header"). + with_content("# MANAGED BY PUPPET\nupstream #{title} {\n#{upstream_cfg_extension[:match]}") + } + else + it { + is_expected.to contain_concat__fragment("#{title}_upstream_header"). + with_content("# MANAGED BY PUPPET\nupstream #{title} {\n") + } + end + it { + is_expected.to contain_concat__fragment("#{title}_upstream_member_#{params[:members].keys[0]}"). + with_content(" server #{params[:members].keys[0]}:80;\n") + } + if upstream_cfg_extension[:fragment] == 'footer' + it { + is_expected.to contain_concat__fragment("#{title}_upstream_footer"). + with_content("#{upstream_cfg_extension[:match]}}\n") + } + else + it { + is_expected.to contain_concat__fragment("#{title}_upstream_footer"). + with_content("}\n") + } + end + end + end + + context 'when ensure => absent' do + let(:params) { default_params[upstreamcontext.to_sym].merge(ensure: 'absent') } + let(:conf_d_path) { conf_d_pathes[upstreamcontext.to_sym] } + + it { + is_expected.to compile.with_all_deps + } + it { + is_expected.to contain_concat("#{conf_d_path}/#{title}-upstream.conf"). + with_ensure('absent') + } + end end end end diff --git a/spec/type_aliases/size.rb b/spec/type_aliases/size.rb new file mode 100644 index 000000000..fc5ed1be4 --- /dev/null +++ b/spec/type_aliases/size.rb @@ -0,0 +1,19 @@ +require 'spec_helper' + +describe 'Nginx::Size' do + it { is_expected.to allow_value('1024k') } + it { is_expected.to allow_value('1024K') } + it { is_expected.to allow_value('1m') } + it { is_expected.to allow_value('1M') } + + it { is_expected.not_to allow_value(:undef) } + it { is_expected.not_to allow_value(1) } + it { is_expected.not_to allow_value(1024) } + it { is_expected.not_to allow_value('') } + it { is_expected.not_to allow_value('0.1k') } + it { is_expected.not_to allow_value('0.1K') } + it { is_expected.not_to allow_value('0.1m') } + it { is_expected.not_to allow_value('0.1M') } + it { is_expected.not_to allow_value('1g') } + it { is_expected.not_to allow_value('1G') } +end diff --git a/spec/type_aliases/time.rb b/spec/type_aliases/time.rb new file mode 100644 index 000000000..e768f4f1d --- /dev/null +++ b/spec/type_aliases/time.rb @@ -0,0 +1,19 @@ +require 'spec_helper' + +describe 'Nginx::Time' do + it { is_expected.to allow_value('10ms') } + it { is_expected.to allow_value('10s') } + it { is_expected.to allow_value('10m') } + it { is_expected.to allow_value('10h') } + it { is_expected.to allow_value('1d') } + it { is_expected.to allow_value('1M') } + it { is_expected.to allow_value('1y') } + + it { is_expected.not_to allow_value(:undef) } + it { is_expected.not_to allow_value(1) } + it { is_expected.not_to allow_value(10) } + it { is_expected.not_to allow_value('') } + it { is_expected.not_to allow_value('10S') } + it { is_expected.not_to allow_value('10.0s') } + it { is_expected.not_to allow_value('10,0s') } +end diff --git a/spec/type_aliases/upstreamcustomparameters.rb b/spec/type_aliases/upstreamcustomparameters.rb new file mode 100644 index 000000000..9b194b7c0 --- /dev/null +++ b/spec/type_aliases/upstreamcustomparameters.rb @@ -0,0 +1,25 @@ +require 'spec_helper' + +describe 'Nginx::UpstreamCustomParameters' do + it { is_expected.to allow_value('key' => 'value') } + it { is_expected.to allow_value('key' => 20) } + it { is_expected.to allow_value('key' => %w[value1 value2]) } + it { is_expected.to allow_value('key' => %w[20 21]) } + it { is_expected.to allow_value('key' => %w[value1 20]) } + it { is_expected.to allow_value('key' => { 'subkey' => 'value' }) } + it { is_expected.to allow_value('key' => { 'subkey' => 20 }) } + it { is_expected.to allow_value('key' => { 'subkey' => %w[subvalue1 subvalue2] }) } + it { is_expected.to allow_value('key' => { 'subkey' => %w[20 21] }) } + it { is_expected.to allow_value('key' => { 'subkey' => %w[subvalue1 20] }) } + + it { is_expected.not_to allow_value(:undef) } + it { is_expected.not_to allow_value(20 => 'value') } + it { is_expected.not_to allow_value('key' => '') } + it { is_expected.not_to allow_value('key' => { '' => 'value' }) } + it { is_expected.not_to allow_value('key' => { 20 => 'value' }) } + it { is_expected.not_to allow_value('key' => { 'subkey' => { 'subsubkey' => 'value' } }) } + it { is_expected.not_to allow_value('key' => { 'subkey' => { 'subsubkey' => 20 } }) } + it { is_expected.not_to allow_value('key' => { 'subkey' => { 'subsubkey' => %w[subvalue1 subvalue2] } }) } + it { is_expected.not_to allow_value('key' => { 'subkey' => { 'subsubkey' => %w[20 21] } }) } + it { is_expected.not_to allow_value('key' => { 'subkey' => { 'subsubkey' => %w[subvalue1 20] } }) } +end diff --git a/spec/type_aliases/upstreammemberserver.rb b/spec/type_aliases/upstreammemberserver.rb new file mode 100644 index 000000000..599f2d94f --- /dev/null +++ b/spec/type_aliases/upstreammemberserver.rb @@ -0,0 +1,15 @@ +require 'spec_helper' + +describe 'Nginx::UpstreamMemberServer' do + it { is_expected.to allow_value('10.10.10.10') } + it { is_expected.to allow_value('backend.example.com') } + it { is_expected.to allow_value('unix:/tmp/backend') } + + it { is_expected.not_to allow_value(:undef) } + it { is_expected.not_to allow_value('') } + it { is_expected.not_to allow_value(1) } + it { is_expected.not_to allow_value('10.10.10.10:80') } + it { is_expected.not_to allow_value('backend.example.com:80') } + it { is_expected.not_to allow_value('unix:/tmp/backend:80') } + it { is_expected.not_to allow_value('linux:/tmp/backend') } +end diff --git a/spec/type_aliases/upstreamstickyzone.rb b/spec/type_aliases/upstreamstickyzone.rb new file mode 100644 index 000000000..e8008d638 --- /dev/null +++ b/spec/type_aliases/upstreamstickyzone.rb @@ -0,0 +1,28 @@ +require 'spec_helper' + +describe 'Nginx::UpstreamStickyZone' do + it { is_expected.to allow_value('live:64k') } + it { is_expected.to allow_value('live:64K') } + it { is_expected.to allow_value('stage:1m') } + it { is_expected.to allow_value('stage:1M') } + + it { is_expected.not_to allow_value(:undef) } + it { is_expected.not_to allow_value(1) } + it { is_expected.not_to allow_value(1024) } + it { is_expected.not_to allow_value('live') } + it { is_expected.not_to allow_value('stage:') } + it { is_expected.not_to allow_value('live:64') } + it { is_expected.not_to allow_value('live 64') } + it { is_expected.not_to allow_value('stage:64.0') } + it { is_expected.not_to allow_value('stage 64.0') } + it { is_expected.not_to allow_value('live:1g') } + it { is_expected.not_to allow_value('live 1g') } + it { is_expected.not_to allow_value('stage:1G') } + it { is_expected.not_to allow_value('stage 1G') } + it { is_expected.not_to allow_value('live:1.0G') } + it { is_expected.not_to allow_value('live 1.0G') } + it { is_expected.not_to allow_value('stage:1.0M') } + it { is_expected.not_to allow_value('stage 1.0M') } + it { is_expected.not_to allow_value('live 1024k') } + it { is_expected.not_to allow_value('stage 1M') } +end diff --git a/spec/type_aliases/upstreamzone.rb b/spec/type_aliases/upstreamzone.rb new file mode 100644 index 000000000..0d3201d5c --- /dev/null +++ b/spec/type_aliases/upstreamzone.rb @@ -0,0 +1,29 @@ +require 'spec_helper' + +describe 'Nginx::UpstreamZone' do + it { is_expected.to compile.with_all_deps } + it { is_expected.to allow_value('live 64k') } + it { is_expected.to allow_value('live 64K') } + it { is_expected.to allow_value('stage 1m') } + it { is_expected.to allow_value('stage 1M') } + + it { is_expected.not_to allow_value(:undef) } + it { is_expected.not_to allow_value(1) } + it { is_expected.not_to allow_value(1024) } + it { is_expected.not_to allow_value('live') } + it { is_expected.not_to allow_value('stage:') } + it { is_expected.not_to allow_value('live:64') } + it { is_expected.not_to allow_value('live 64') } + it { is_expected.not_to allow_value('stage:64.0') } + it { is_expected.not_to allow_value('stage 64.0') } + it { is_expected.not_to allow_value('live:1g') } + it { is_expected.not_to allow_value('live 1g') } + it { is_expected.not_to allow_value('stage:1G') } + it { is_expected.not_to allow_value('stage 1G') } + it { is_expected.not_to allow_value('live:1.0G') } + it { is_expected.not_to allow_value('live 1.0G') } + it { is_expected.not_to allow_value('stage:1.0M') } + it { is_expected.not_to allow_value('stage 1.0M') } + it { is_expected.not_to allow_value('live:1024k') } + it { is_expected.not_to allow_value('stage:1M') } +end diff --git a/templates/upstream/upstream_footer.epp b/templates/upstream/upstream_footer.epp new file mode 100644 index 000000000..9c09dd4e5 --- /dev/null +++ b/templates/upstream/upstream_footer.epp @@ -0,0 +1,85 @@ +<%- | Hash $cfg_append = {}, + Optional[String[1]] $hash = undef, + Boolean $ip_hash = false, + Optional[Integer[1]] $keepalive = undef, + Optional[Integer[1]] $keepalive_requests = undef, + Optional[Nginx::Time] $keepalive_timeout = undef, + Boolean $least_conn = false, + Optional[Nginx::UpstreamLeastTime] $least_time = undef, + Boolean $ntlm = false, + Optional[Integer] $queue_max = undef, + Optional[Nginx::Time] $queue_timeout = undef, + Optional[String[1]] $random = undef, + Optional[Stdlib::Unixpath] $statefile = undef, + Optional[Nginx::UpstreamSticky] $sticky = undef, + Optional[Nginx::UpstreamZone] $zone = undef, +| -%> +<% if $hash { -%> + hash <%= $hash %>; +<% } -%> +<% if $ip_hash { -%> + ip_hash; +<% } -%> +<% if $least_conn { -%> + least_conn; +<% } -%> +<% if $least_time { -%> + least_time <%= $least_time %>; +<% } -%> +<% if $ntlm { -%> + ntlm; +<% } -%> +<% if $random { -%> + random <%= $random %>; +<% } -%> +<% if $statefile { -%> + state <%= $statefile %>; +<% } -%> +<% if $sticky { -%> + <%- $sticky.each |$type,$values| { -%> + <%- if $type != 'route' { -%> + sticky <%= $type %><% $values.each |$key,$value| { %> <%= $key %><% if $value != true { %>=<%= $value %><% } %><% } %>; + <%- } else { -%> + sticky <%= $type %> <%= $values %>; + <%- } -%> + <%- } -%> +<% } -%> +<% if $zone { -%> + zone <%= $zone %>; +<% } -%> +<% if $keepalive { -%> + keepalive <%= $keepalive %>; +<% } -%> +<% if $keepalive_requests { -%> + keepalive_requests <%= $keepalive_requests %>; +<% } -%> +<% if $keepalive_timeout { -%> + keepalive_timeout <%= $keepalive_timeout %>; +<% } -%> +<% if $queue_max { -%> + queue <%= $queue_max %><% if $queue_timeout { %> timeout=<%= $queue_timeout %><% } %>; +<% } -%> +<% if $cfg_append { -%> + <%- $cfg_append.each |$key,$value| { -%> + <%- if $value =~ Hash { -%> + <%- $value.each |$subkey,$subvalue| { -%> + <%- if $subvalue =~ Array { -%> + <%- Array($subvalue).each |$asubvalue| { -%> + <%= $key %> <%= $subkey %> <%= $asubvalue %>; + <%- } -%> + <%- } else { -%> + <%= $key %> <%= $subkey %> <%= $subvalue %>; + <%- } -%> + <%- } -%> + <%- } else { -%> + <%- if $value =~ Array { -%> + <%- $value.each |$asubvalue| { -%> + <%= $key %> <%= $asubvalue %>; + <%- } -%> + <%- } else { -%> + <%= $key %> <%= $value %>; + <%- } -%> + <%- } -%> + <%- } -%> +<% } -%> +} diff --git a/templates/upstream/upstream_footer.erb b/templates/upstream/upstream_footer.erb deleted file mode 100644 index 33b0bcd30..000000000 --- a/templates/upstream/upstream_footer.erb +++ /dev/null @@ -1,21 +0,0 @@ -<% if @upstream_cfg_append %> -<%# Slightly less obtuse way to sort but put keepalive at end -%> -<% - @upstream_cfg_append = Hash[@upstream_cfg_append.sort] - @upstream_cfg_append['keepalive'] = @upstream_cfg_append.delete('keepalive') --%> - <%- @upstream_cfg_append.each do |key,value| -%> - <%- if value.is_a?(Hash) -%> - <%- value.each do |subkey,subvalue| -%> - <%- Array(subvalue).each do |asubvalue| -%> - <%= key %> <%= subkey %> <%= asubvalue %>; - <%- end -%> - <%- end -%> - <%- else -%> - <%- Array(value).each do |asubvalue| -%> - <%= key %> <%= asubvalue %>; - <%- end -%> - <%- end -%> - <%- end -%> -<% end -%> -} diff --git a/templates/upstream/upstream_header.epp b/templates/upstream/upstream_header.epp new file mode 100644 index 000000000..133baf3a8 --- /dev/null +++ b/templates/upstream/upstream_header.epp @@ -0,0 +1,28 @@ +<%- | Hash $cfg_prepend = {}, + String[1] $name, +| -%> +# MANAGED BY PUPPET +upstream <%= $name %> { +<% if $cfg_prepend { -%> + <%- $cfg_prepend.each |$key,$value| { -%> + <%- if $value =~ Hash { -%> + <%- $value.each |$subkey,$subvalue| { -%> + <%- if $subvalue =~ Array { -%> + <%- Array($subvalue).each |$asubvalue| { -%> + <%= $key %> <%= $subkey %> <%= $asubvalue %>; + <%- } -%> + <%- } else { -%> + <%= $key %> <%= $subkey %> <%= $subvalue %>; + <%- } -%> + <%- } -%> + <%- } else { -%> + <%- if $value =~ Array { -%> + <%- $value.each |$asubvalue| { -%> + <%= $key %> <%= $asubvalue %>; + <%- } -%> + <%- } else { -%> + <%= $key %> <%= $value %>; + <%- } -%> + <%- } -%> + <%- } -%> +<% } -%> diff --git a/templates/upstream/upstream_header.erb b/templates/upstream/upstream_header.erb deleted file mode 100644 index 7aed428d2..000000000 --- a/templates/upstream/upstream_header.erb +++ /dev/null @@ -1,22 +0,0 @@ -# MANAGED BY PUPPET -upstream <%= @name %> { -<% if @upstream_cfg_prepend -%> -<%# Slightly less obtuse way to sort but put keepalive at end -%> -<% - @upstream_cfg_prepend = Hash[@upstream_cfg_prepend.sort] - @upstream_cfg_prepend['keepalive'] = @upstream_cfg_prepend.delete('keepalive') --%> - <%- @upstream_cfg_prepend.each do |key,value| -%> - <%- if value.is_a?(Hash) -%> - <%- value.each do |subkey,subvalue| -%> - <%- Array(subvalue).each do |asubvalue| -%> - <%= key %> <%= subkey %> <%= asubvalue %>; - <%- end -%> - <%- end -%> - <%- else -%> - <%- Array(value).each do |asubvalue| -%> - <%= key %> <%= asubvalue %>; - <%- end -%> - <%- end -%> - <%- end -%> -<% end -%> diff --git a/templates/upstream/upstream_member.epp b/templates/upstream/upstream_member.epp new file mode 100644 index 000000000..4bfed2db7 --- /dev/null +++ b/templates/upstream/upstream_member.epp @@ -0,0 +1,28 @@ +<%- | $_server, + Boolean $backup = false, + Optional[String[1]] $comment = undef, + Optional[Nginx::Time] $fail_timeout = undef, + Optional[Integer[1]] $max_conns = undef, + Optional[Integer[1]] $max_fails = undef, + Optional[String[1]] $params_append = undef, + Optional[String[1]] $params_prepend = undef, + Boolean $resolve = false, + Optional[String[1]] $route = undef, + Optional[String[1]] $service = undef, + Optional[Nginx::Time] $slow_start = undef, + Optional[Enum['drain','down']] $state = undef, + Optional[Integer[1]] $weight = undef, +| -%> + server <%= $_server -%> +<%- if $params_prepend { %> <%= $params_prepend %><% } -%> +<%- if $state { %> <%= $state %><% } -%> +<%- if $weight { %> weight=<%= $weight %><% } -%> +<%- if $max_conns { %> max_conns=<%= $max_conns %><% } -%> +<%- if $max_fails { %> max_fails=<%= $max_fails %><% } -%> +<%- if $fail_timeout { %> fail_timeout=<%= $fail_timeout %><% } -%> +<%- if $slow_start { %> slow_start=<%= $slow_start %><% } -%> +<%- if $service { %> service=<%= $service %><% } -%> +<%- if $route { %> route=<%= $route %><% } -%> +<%- if $resolve { %> resolve<% } -%> +<%- if $backup { %> backup<% } -%> +<%- if $params_append { %> <%= $params_append %><% } %>;<% if $comment { %> # <%= $comment %><% } %> diff --git a/templates/upstream/upstream_member.erb b/templates/upstream/upstream_member.erb deleted file mode 100644 index 5f44b54ee..000000000 --- a/templates/upstream/upstream_member.erb +++ /dev/null @@ -1 +0,0 @@ - server <%= @server %>:<%= @port %> fail_timeout=<%= @upstream_fail_timeout %><% if @upstream_max_fails -%> max_fails=<%=@upstream_max_fails %><% end %>; diff --git a/templates/upstream/upstream_members.erb b/templates/upstream/upstream_members.erb deleted file mode 100644 index 0f5c30626..000000000 --- a/templates/upstream/upstream_members.erb +++ /dev/null @@ -1,2 +0,0 @@ - <% @members.each do |i| %> - server <%= i %> fail_timeout=<%= @upstream_fail_timeout %><% if @upstream_max_fails -%> max_fails=<%=@upstream_max_fails %><% end %>;<% end %> diff --git a/types/size.pp b/types/size.pp new file mode 100644 index 000000000..580476686 --- /dev/null +++ b/types/size.pp @@ -0,0 +1 @@ +type Nginx::Size = Pattern[/^\d+[k|K|m|M]?$/] diff --git a/types/time.pp b/types/time.pp new file mode 100644 index 000000000..f6c24edc8 --- /dev/null +++ b/types/time.pp @@ -0,0 +1 @@ +type Nginx::Time = Pattern[/^\d+(ms|s|m|h|d|w|M|y)?$/] diff --git a/types/upstreamcustomparameters.pp b/types/upstreamcustomparameters.pp new file mode 100644 index 000000000..2bcd5ec02 --- /dev/null +++ b/types/upstreamcustomparameters.pp @@ -0,0 +1,24 @@ +type Nginx::UpstreamCustomParameters = Hash[String[1], + Variant[ + String[1], + Integer, + Array[ + Variant[ + String[1], + Integer + ] + ], + Hash[String[1], + Variant[ + String[1], + Integer, + Array[ + Variant[ + String[1], + Integer, + ] + ] + ] + ] + ] +] diff --git a/types/upstreamdefaults.pp b/types/upstreamdefaults.pp new file mode 100644 index 000000000..4adf692e7 --- /dev/null +++ b/types/upstreamdefaults.pp @@ -0,0 +1,20 @@ +type Nginx::UpstreamDefaults = Struct[{ + context => Optional[Enum['http', 'stream']], + member_defaults => Optional[Nginx::UpstreamMemberDefaults], + hash => Optional[String], + ip_hash => Optional[Boolean], + keepalive => Optional[Integer[1]], + kepalive_requests => Optional[Integer[1]], + keepalive_timeout => Optional[Nginx::Time], + least_conn => Optional[Boolean], + least_time => Optional[Nginx::UpstreamLeastTime], + ntlm => Optional[Boolean], + queue_max => Optional[Integer], + queue_timeout => Optional[Nginx::Time], + random => Optional[String], + statefile => Optional[Stdlib::Unixpath], + sticky => Optional[Nginx::UpstreamSticky], + zone => Optional[Nginx::UpstreamZone], + cfg_append => Optional[Hash], + cfg_prepend => Optional[Hash], +}] diff --git a/types/upstreamleasttime.pp b/types/upstreamleasttime.pp new file mode 100644 index 000000000..daeab96b8 --- /dev/null +++ b/types/upstreamleasttime.pp @@ -0,0 +1 @@ +type Nginx::UpstreamLeastTime = Variant[Nginx::UpstreamLeastTimeHttp,Nginx::UpstreamLeastTimeStream] diff --git a/types/upstreamleasttimehttp.pp b/types/upstreamleasttimehttp.pp new file mode 100644 index 000000000..5299f4524 --- /dev/null +++ b/types/upstreamleasttimehttp.pp @@ -0,0 +1 @@ +type Nginx::UpstreamLeastTimeHttp = Enum['header','header inflight','last_byte','last_byte inflight'] diff --git a/types/upstreamleasttimestream.pp b/types/upstreamleasttimestream.pp new file mode 100644 index 000000000..35ff9fcfd --- /dev/null +++ b/types/upstreamleasttimestream.pp @@ -0,0 +1 @@ +type Nginx::UpstreamLeastTimeStream = Enum['connect','connect inflight','first_byte','first_byte inflight','last_byte','last_byte inflight'] diff --git a/types/upstreammember.pp b/types/upstreammember.pp new file mode 100644 index 000000000..10dcc42e8 --- /dev/null +++ b/types/upstreammember.pp @@ -0,0 +1,17 @@ +type Nginx::UpstreamMember = Struct[{ + server => Optional[Nginx::UpstreamMemberServer], + port => Optional[Stdlib::Port], + weight => Optional[Integer[1]], + max_conns => Optional[Integer[1]], + max_fails => Optional[Integer[1]], + fail_timeout => Optional[Nginx::Time], + backup => Optional[Boolean], + resolve => Optional[Boolean], + route => Optional[String], + service => Optional[String], + slow_start => Optional[Nginx::Time], + state => Optional[Enum['drain','down']], + params_prepend => Optional[String], + params_append => Optional[String], + comment => Optional[String], +}] diff --git a/types/upstreammemberdefaults.pp b/types/upstreammemberdefaults.pp new file mode 100644 index 000000000..0bb1ce7c1 --- /dev/null +++ b/types/upstreammemberdefaults.pp @@ -0,0 +1,15 @@ +type Nginx::UpstreamMemberDefaults = Struct[{ + port => Optional[Stdlib::Port], + weight => Optional[Integer[1]], + max_conns => Optional[Integer[1]], + max_fails => Optional[Integer[1]], + fail_timeout => Optional[Nginx::Time], + backup => Optional[Boolean], + resolve => Optional[Boolean], + route => Optional[String], + service => Optional[String], + slow_start => Optional[Nginx::Time], + state => Optional[Enum['drain','down']], + params_prepend => Optional[String], + params_append => Optional[String], +}] diff --git a/types/upstreammembers.pp b/types/upstreammembers.pp new file mode 100644 index 000000000..ee6e1448b --- /dev/null +++ b/types/upstreammembers.pp @@ -0,0 +1 @@ +type Nginx::UpstreamMembers = Hash[String,Nginx::UpstreamMember] diff --git a/types/upstreammemberserver.pp b/types/upstreammemberserver.pp new file mode 100644 index 000000000..b5415f17b --- /dev/null +++ b/types/upstreammemberserver.pp @@ -0,0 +1 @@ +type Nginx::UpstreamMemberServer = Variant[Stdlib::Host,Pattern[/^unix:\/([^\/\0]+\/*)[^:]*$/]] diff --git a/types/upstreamsticky.pp b/types/upstreamsticky.pp new file mode 100644 index 000000000..f6a7c5c03 --- /dev/null +++ b/types/upstreamsticky.pp @@ -0,0 +1,28 @@ +type Nginx::UpstreamSticky = Variant[ + Hash[ + Enum['cookie'], + Struct[{ + name => String, + expires => Optional[Variant[Nginx::Time,Enum['max']]], + domain => Optional[String], + httponly => Optional[Boolean], + secure => Optional[Boolean], + path => Optional[String], + }] + ], + Hash[ + Enum['route'], + String + ], + Hash[ + Enum['learn'], + Struct[{ + create => String, + lookup => String, + zone => Nginx::UpstreamStickyZone, + timeout => Optional[Nginx::Time], + header => Optional[Boolean], + sync => Optional[Boolean], + }] + ] +] diff --git a/types/upstreamstickyzone.pp b/types/upstreamstickyzone.pp new file mode 100644 index 000000000..635ccba5f --- /dev/null +++ b/types/upstreamstickyzone.pp @@ -0,0 +1 @@ +type Nginx::UpstreamStickyZone = Pattern[/^[-_\.A-Za-z0-9]*:\d+[k|K|m|M]$/] diff --git a/types/upstreamzone.pp b/types/upstreamzone.pp new file mode 100644 index 000000000..b1cbe57ba --- /dev/null +++ b/types/upstreamzone.pp @@ -0,0 +1 @@ +type Nginx::UpstreamZone = Pattern[/^[-_\.A-Za-z0-9]* \d+[k|K|m|M]$/]