diff --git a/.fixtures.yml b/.fixtures.yml index 619c5f74..ae707da9 100644 --- a/.fixtures.yml +++ b/.fixtures.yml @@ -1,6 +1,5 @@ fixtures: repositories: - "stdlib": - "repo": "git://github.com/puppetlabs/puppetlabs-stdlib.git" + "stdlib": "https://github.com/puppetlabs/puppetlabs-stdlib.git" symlinks: sqlserver: "#{source_dir}" diff --git a/.sync.yml b/.sync.yml index 1628d51e..e67c20eb 100644 --- a/.sync.yml +++ b/.sync.yml @@ -5,14 +5,14 @@ Gemfile: optional: ':development': - gem: yard + ':system_tests': + - gem: beaker-testmode_switcher + version: '<= 0.2.0' + - gem: master_manipulator Rakefile: unmanaged: true spec/spec_helper.rb: unmanaged: true -appveyor.yml: - matrix_extras: - - PUPPET_GEM_VERSION: 4.2.3 - RUBY_VER: 21-x64 LICENSE: license_type: 'puppetpe' NOTICE: diff --git a/.travis.yml b/.travis.yml index 7fd97158..a76b1f61 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,10 +10,10 @@ before_install: matrix: fast_finish: true include: - - rvm: 2.3.1 + - rvm: 2.4.0 bundler_args: --without system_tests - env: PUPPET_GEM_VERSION="~> 4.0" - - rvm: 2.1.7 + env: PUPPET_GEM_VERSION="~> 5.0" + - rvm: 2.1.9 bundler_args: --without system_tests env: PUPPET_GEM_VERSION="~> 4.0" notifications: diff --git a/CHANGELOG.md b/CHANGELOG.md index 7b64f19e..cc45d633 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,13 +1,41 @@ +## 2017-08-10 - Supported Release 2.0.0 + +### Summary + +This major release adds support for Microsoft SQL Server 2016 + +#### Features + +- Deprecates the use of `Tools` and `SQL` as installation features ([MODULES-4257](https://tickets.puppet.com/browse/MODULES-4257)) +- Added more detailed examples to the README +- Updated with Puppet 4 data types ([MODULES-5126](https://tickets.puppet.com/browse/MODULES-5126)) +- Added parameters to manage PolyBase ([MODULES-5070](https://tickets.puppet.com/browse/MODULES-5070)) + +#### Bug Fixes + +- Make .Net installation errors more obvious ([MODULES-5092](https://tickets.puppet.com/browse/MODULES-5092)) +- Remove service_ensure parameter as it had no use ([MODULES-5030](https://tickets.puppet.com/browse/MODULES-5030)) +- Using as_sysadmin_accounts without AS feature will error ([MODULES-2386](https://tickets.puppet.com/browse/MODULES-2386)) +- SNAC_SDK shared feature can not be managed by the module ([FM-5389](https://tickets.puppet.com/browse/FM-5389)) +- Purge members from SQL Server Role should actually purge ([MODULES-2543](https://tickets.puppet.com/browse/MODULES-2543)) +- Identifiers are properly escaped during database creation ([FM-5021](https://tickets.puppet.com/browse/FM-5021)) +- Remove forced TCP connection for SQL management ([MODULES-4915](https://tickets.puppet.com/browse/MODULES-4915)) +- Update metadata for Puppet 4 and Puppet 5 ([MODULES-4842](https://tickets.puppet.com/browse/MODULES-4842), [MODULES-5144](https://tickets.puppet.com/browse/MODULES-5144)) +- Add test tiering and test mode switcher ([FM-5062](https://tickets.puppet.com/browse/FM-5062), [FM-6141](https://tickets.puppet.com/browse/FM-6141)) + ## 2017-05-08 - Supported Release 1.2.0 + ### Summary This release adds support for internationalization of the module. It also contains Japanese translations for the README, summary and description of the metadata.json and major cleanups in the README. Additional folders have been introduced called locales and readmes where translation files can be found. A number of features and bug fixes are also included in this release. #### Features + - (MODULES-4334) - Adding locales directory, config.yaml and POT file for i18n. - Puppet-module-gems now implemented #### Bug Fixes + - (MODULES-4528) Replace Puppet.version comparison with Puppet::Util::Package.versioncmp - (MODULES-4667) Update beaker tests for Jenkins CI diff --git a/Gemfile b/Gemfile index 3ed195f5..084a67db 100644 --- a/Gemfile +++ b/Gemfile @@ -43,14 +43,14 @@ minor_version = "#{ruby_version_segments[0]}.#{ruby_version_segments[1]}" #end group :development do - gem "puppet-module-posix-default-r#{minor_version}", :require => false, :platforms => "ruby" - gem "puppet-module-win-default-r#{minor_version}", :require => false, :platforms => ["mswin", "mingw", "x64_mingw"] - gem "puppet-module-posix-dev-r#{minor_version}", :require => false, :platforms => "ruby" - gem "puppet-module-win-dev-r#{minor_version}", :require => false, :platforms => ["mswin", "mingw", "x64_mingw"] - gem "json_pure", '<= 2.0.1', :require => false if Gem::Version.new(RUBY_VERSION.dup) < Gem::Version.new('2.0.0') - gem "fast_gettext", '1.1.0', :require => false if Gem::Version.new(RUBY_VERSION.dup) < Gem::Version.new('2.1.0') - gem "fast_gettext", :require => false if Gem::Version.new(RUBY_VERSION.dup) >= Gem::Version.new('2.1.0') - gem "yard", :require => false + gem "puppet-module-posix-default-r#{minor_version}", :require => false, :platforms => "ruby" + gem "puppet-module-win-default-r#{minor_version}", :require => false, :platforms => ["mswin", "mingw", "x64_mingw"] + gem "puppet-module-posix-dev-r#{minor_version}", :require => false, :platforms => "ruby" + gem "puppet-module-win-dev-r#{minor_version}", '0.0.7', :require => false, :platforms => ["mswin", "mingw", "x64_mingw"] + gem "json_pure", '<= 2.0.1', :require => false if Gem::Version.new(RUBY_VERSION.dup) < Gem::Version.new('2.0.0') + gem "fast_gettext", '1.1.0', :require => false if Gem::Version.new(RUBY_VERSION.dup) < Gem::Version.new('2.1.0') + gem "fast_gettext", :require => false if Gem::Version.new(RUBY_VERSION.dup) >= Gem::Version.new('2.1.0') + gem "yard", :require => false end group :system_tests do @@ -61,6 +61,9 @@ group :system_tests do gem "beaker-rspec", *location_for(ENV['BEAKER_RSPEC_VERSION']) gem "beaker-hostgenerator", *location_for(ENV['BEAKER_HOSTGENERATOR_VERSION']) gem "beaker-abs", *location_for(ENV['BEAKER_ABS_VERSION'] || '~> 0.1') + gem "puppet-blacksmith", '~> 3.4', :require => false + gem "beaker-testmode_switcher", '<= 0.2.0', :require => false + gem "master_manipulator", :require => false end gem 'puppet', *location_for(ENV['PUPPET_GEM_VERSION']) diff --git a/README.md b/README.md index 35a54b36..2a4c5334 100644 --- a/README.md +++ b/README.md @@ -80,13 +80,6 @@ This example creates the same MS SQL instance as shown above with additional opt ### Install SQL Server tools and features not specific to a SQL Server instance -```puppet -sqlserver_features { 'Generic Features': - source => 'E:/', - features => ['Tools'], -} -``` - ```puppet sqlserver_features { 'Generic Features': source => 'E:/', @@ -184,6 +177,81 @@ sqlserver_tsql{ 'Always running': } ``` +### Advanced example + +This advanced example: + +* Installs the basic SQL Server Engine from installation media mounted at 'D:\' with TCP Enabled and various directories set. + +* Uses only Windows-based authentication and installs with only the user that Puppet is executing as. Note that the 'sql_sysadmin_accounts' is only applicable during the instance installation and is not actively enforced. + +* Creates a `sqlserver::config` resource, which is used in later resources to connect to the newly created instance. As we support only Windows-based authentication, a username and password is not required. + +* Creates a local group called 'DB Administrators' and ensures that it is SQL System Administrator (sysadmin role); also creates the account that Puppet uses to install and manage the instance. + +* Ensures that the advanced options for `sp_configure` are enabled, so that Puppet can manage the `max memory` setting for the instance. + +* Ensure that the `max memory` (MB) configuration item is set to 2048 megabytes. + +```puppet +$sourceloc = 'D:/' + +# Install a SQL Server default instance +sqlserver_instance{'MSSQLSERVER': + source => $sourceloc, + features => ['SQLEngine'], + sql_sysadmin_accounts => [$facts['id']], + install_switches => { + 'TCPENABLED' => 1, + 'SQLBACKUPDIR' => 'C:\\MSSQLSERVER\\backupdir', + 'SQLTEMPDBDIR' => 'C:\\MSSQLSERVER\\tempdbdir', + 'INSTALLSQLDATADIR' => 'C:\\MSSQLSERVER\\datadir', + 'INSTANCEDIR' => 'C:\\Program Files\\Microsoft SQL Server', + 'INSTALLSHAREDDIR' => 'C:\\Program Files\\Microsoft SQL Server', + 'INSTALLSHAREDWOWDIR' => 'C:\\Program Files (x86)\\Microsoft SQL Server' + } +} + +# Resource to connect to the DB instance +sqlserver::config { 'MSSQLSERVER': + admin_login_type => 'WINDOWS_LOGIN' +} + +# Enforce SQL Server Administrators +$local_dba_group_name = 'DB Administrators' +$local_dba_group_netbios_name = "${facts['hostname']}\\DB Administrators" + +group { $local_dba_group_name: + ensure => present +} + +-> sqlserver::login { $local_dba_group_netbios_name : + login_type => 'WINDOWS_LOGIN', +} + +-> sqlserver::role { 'sysadmin': + ensure => 'present', + instance => 'MSSQLSERVER', + type => 'SERVER', + members => [$local_dba_group_netbios_name, $facts['id']], +} + +# Enforce memory consumption +sqlserver_tsql {'check advanced sp_configure': + command => 'EXEC sp_configure \'show advanced option\', \'1\'; RECONFIGURE;', + onlyif => 'sp_configure @configname=\'max server memory (MB)\'', + instance => 'MSSQLSERVER' +} + +-> sqlserver::sp_configure { 'MSSQLSERVER-max memory': + config_name => 'max server memory (MB)', + instance => 'MSSQLSERVER', + reconfigure => true, + restart => true, + value => 2048 +} +``` + ## Reference ### Types @@ -203,8 +271,10 @@ Default: 'present'. ##### `features` *Required.* - -Specifies one or more features to manage. Valid options: 'BC', 'Conn', 'SSMS', 'ADV_SSMS', 'SDK', 'IS', 'MDS', and 'Tools' (the Tools feature includes SSMS, ADV_SSMS, and Conn). + +Specifies one or more features to manage. Valid options: 'BC', 'Conn', 'SSMS', 'ADV_SSMS', 'SDK', 'IS', 'MDS', 'BOL', 'DREPLAY_CTLR', 'DREPLAY_CLT'. + +The 'Tools' value for this setting is deprecated. Specify 'BC', 'SSMS', 'ADV_SSMS', 'Conn', and 'SDK' explicitly. ##### `install_switches` @@ -294,7 +364,9 @@ Default: 'present'. ##### `features` -*Required.* Specifies one or more features to manage. The list of top-level features includes 'SQL', 'AS', and 'RS'. The 'SQL' feature includes the Database Engine, Replication, Full-Text, and Data Quality Services (DQS) server. Valid options: an array containing one or more of the strings 'SQL', 'SQLEngine', 'Replication', 'FullText', 'DQ', 'AS', and 'RS'. +*Required.* Specifies one or more features to manage. The list of top-level features includes 'AS' and 'RS'. Valid options: an array containing one or more of the strings 'SQL', 'SQLEngine', 'Replication', 'FullText', 'DQ', 'AS', 'RS', 'POLYBASE', and 'ADVANCEDANALYTICS'. + +The 'SQL' value for this setting is deprecated. Specify 'DQ', 'FullText', 'Replication', and 'SQLEngine' explicitly. ##### `install_switches` @@ -319,6 +391,22 @@ Specifies a product key for SQL Server. Valid options: a string containing a val Default: `undef`. +##### `polybase_svc_account` + +**Applicable only if the POLYBASE feature for SQL Server 2016 is being installed.** + +Specifies a domain or system account for the Polybase Engine service. + +Valid options: a string specifying an existing username. + +##### `polybase_svc_password` + +**Applicable only if the POLYBASE feature for SQL Server 2016 is being installed.** + +Specifies the password for the Polybase Engine service + +Valid options: a string specifying a valid password. + ##### `rs_svc_account` Specifies a domain or system account to be used by the report service. Valid options: a string; cannot include any of the following characters: `'"/ \ [ ] : ; | = , + * ? < >'`. If you specify a domain user account, the domain must be less than 254 characters and the username must be less than 20 characters. @@ -343,10 +431,6 @@ Specifies a security mode for SQL Server. Valid options: 'SQL'. If not specified Default: `undef`. -##### `service_ensure` - -Specifies whether the SQL Server service should be running. Valid options: 'automatic' (Puppet starts the service if it's not running), 'manual' (Puppet takes no action), and 'disable' (Puppet stops the service if it's running). - ##### `source` *Required.* @@ -985,7 +1069,9 @@ Terminology differs somewhat between various database systems; please refer to t ## Limitations -This module is available only for Windows Server 2012 or 2012 R2, and works with Puppet Enterprise 3.7 and later. +This module can manage only a single version of SQL Server on a given host (one and only one of SQL Server 2012, 2014 or 2016). The module is able to manage multiple SQL Server instances of the same version. + +This module cannot manage the SQL Server Native Client SDK (also known as SNAC_SDK). The SQL Server installation media can install the SDK, but it is not able to uninstall the SDK. Note that the 'sqlserver_features' fact detects the presence of the SDK. ## Development diff --git a/Rakefile b/Rakefile index 0ea2f7b9..073bf39c 100644 --- a/Rakefile +++ b/Rakefile @@ -21,3 +21,25 @@ task :validate do sh "erb -P -x -T '-' #{template} | ruby -c" end end + +require 'rspec/core/rake_task' +desc 'test tiering' +RSpec::Core::RakeTask.new(:test_tier) do |t| + # Setup rspec opts + t.rspec_opts = ['--color'] + + # TEST_TIERS env variable is a comma separated list of tiers to run. e.g. low, medium, high + if ENV['TEST_TIERS'] + test_tiers = ENV['TEST_TIERS'].split(',') + raise 'TEST_TIERS env variable must have at least 1 tier specified. low, medium or high (comma separated).' if test_tiers.count == 0 + test_tiers.each do |tier| + raise "#{tier} not a valid test tier." unless %w(low medium high).include?(tier) + t.rspec_opts.push("--tag tier_#{tier}") + end + else + puts 'TEST_TIERS env variable not defined. Defaulting to run all tests.' + end + + # Implement an override for the pattern with BEAKER_PATTERN env variable. + t.pattern = ENV['BEAKER_PATTERN'] ? ENV['BEAKER_PATTERN'] : 'spec/acceptance' +end diff --git a/appveyor.yml b/appveyor.yml index 32f29471..7e05880b 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -14,18 +14,28 @@ environment: RUBY_VER: 21 - PUPPET_GEM_VERSION: ~> 4.0 RUBY_VER: 21-x64 - - PUPPET_GEM_VERSION: ~> 4.0 - RUBY_VER: 23 - - PUPPET_GEM_VERSION: ~> 4.0 - RUBY_VER: 23-x64 - - PUPPET_GEM_VERSION: 4.2.3 - RUBY_VER: 21-x64 - - PUPPET_GEM_VERSION: 4.2.3 + - PUPPET_GEM_VERSION: ~> 5.0 + RUBY_VER: 24 + - PUPPET_GEM_VERSION: ~> 5.0 + RUBY_VER: 24-x64 + - PUPPET_GEM_VERSION: 4.7.1 RUBY_VER: 21-x64 matrix: fast_finish: true install: - SET PATH=C:\Ruby%RUBY_VER%\bin;%PATH% +- ps: | + # AppVeyor appears to have OpenSSL headers available already + # which msys2 would normally install with: + # pacman -S mingw-w64-x86_64-openssl --noconfirm + # + if ( $(ruby --version) -match "^ruby\s+2\.4" ) { + Write-Output "Building OpenSSL gem ~> 2.0.4 to fix Ruby 2.4 / AppVeyor issue" + gem install openssl --version '~> 2.0.4' --no-ri --no-rdoc + } + + gem list openssl + ruby -ropenssl -e 'puts \"OpenSSL Version - #{OpenSSL::OPENSSL_VERSION}\"; puts \"OpenSSL Library Version - #{OpenSSL::OPENSSL_LIBRARY_VERSION}\"' - bundle install --jobs 4 --retry 2 --without system_tests - type Gemfile.lock build: off diff --git a/lib/puppet/parser/functions/sqlserver_validate_instance_name.rb b/lib/puppet/parser/functions/sqlserver_validate_instance_name.rb index 152876e2..e9cb7327 100644 --- a/lib/puppet/parser/functions/sqlserver_validate_instance_name.rb +++ b/lib/puppet/parser/functions/sqlserver_validate_instance_name.rb @@ -10,6 +10,7 @@ # @raise [Puppet::ParserError] Instance name can not be larger than 16 characters # @raise [Puppet::ParserError] Instance name can not start or end with underscore (_) # + module Puppet::Parser::Functions newfunction(:sqlserver_validate_instance_name, :docs => < e - raise Puppet::ParseError, "An exception was raised while trying to parse the svrrole hash key #{k} in #{value}: Exception is #{e}" - end - end - end -end diff --git a/lib/puppet/provider/sqlserver.rb b/lib/puppet/provider/sqlserver.rb index 2ac2682c..5ac415ba 100644 --- a/lib/puppet/provider/sqlserver.rb +++ b/lib/puppet/provider/sqlserver.rb @@ -59,7 +59,7 @@ def self.run_install_dot_net(source_location = nil) Else { Write-Host "Installing .Net Framework 3.5, do not close this prompt..." - DISM /Online /Enable-Feature /FeatureName:NetFx3 /All /NoRestart /Quiet /LimitAccess #{ "/Source:\"#{source_location}\"" unless source_location.nil? } | Out-Null + $InstallResult = DISM /Online /Enable-Feature /FeatureName:NetFx3 /All /NoRestart /Quiet /LimitAccess #{ "/Source:\"#{source_location}\"" unless source_location.nil? } $Result = Dism /online /Get-featureinfo /featurename:NetFx3 If($Result -contains "State : Enabled") { @@ -68,6 +68,10 @@ def self.run_install_dot_net(source_location = nil) Else { Write-Host "Failed to install Install .Net Framework 3.5#{ ", please make sure the windows_feature_source is correct" unless source_location.nil?}." + Write-Host "DISM Install Result" + Write-Host "-----------" + Write-Host ($InstallResult -join "`n") + #exit 1 } } DOTNET diff --git a/lib/puppet/provider/sqlserver_features/mssql.rb b/lib/puppet/provider/sqlserver_features/mssql.rb index e5c7ad79..45acb5fc 100644 --- a/lib/puppet/provider/sqlserver_features/mssql.rb +++ b/lib/puppet/provider/sqlserver_features/mssql.rb @@ -1,5 +1,7 @@ require 'json' require File.expand_path(File.join(File.dirname(__FILE__), '..', 'sqlserver')) +require File.expand_path(File.join(File.dirname(__FILE__), '..', '..', '..', 'puppet_x/sqlserver/server_helper')) +require File.expand_path(File.join(File.dirname(__FILE__), '..', '..', '..', 'puppet_x/sqlserver/features')) FEATURE_RESERVED_SWITCHES = %w(AGTSVCACCOUNT AGTSVCPASSWORD ASSVCACCOUNT AGTSVCPASSWORD PID @@ -10,18 +12,28 @@ def self.instances instances = [] result = Facter.value(:sqlserver_features) debug "Parsing result #{result}" - result = !result[SQL_2014].empty? ? result[SQL_2014] : result[SQL_2012] - if !result.empty? - existing_instance = {:name => "Generic Features", - :ensure => :present, - :features => result.sort - } - debug "Parsed features = #{existing_instance[:features]}" - instance = new(existing_instance) - debug "Created instance #{instance}" - instances << instance + # Due to MODULES-5060 we can only output one feature set. If we output + # multiple then it is not possible to install or uninstall due to multiple + # resources with the same name. Also due to the SQL Native Client not + # being unique across SQL Server versions (e.g. SQL 2016 installs Native Client + # with a version that matches for SQL 2012) the features need to be collated + # across all versions and then aggregated into a single resource + featurelist = [] + ALL_SQL_VERSIONS.each do |sql_version| + next if result[sql_version].empty? + featurelist += result[sql_version] + end + + unless featurelist.count.zero? + instance_props = {:name => "Generic Features", + :ensure => :present, + :features => featurelist.uniq.sort + } + debug "Parsed features = #{instance_props[:features]}" + instances = [new(instance_props)] end + instances end @@ -82,8 +94,7 @@ def create_temp_for_install_switch config_file = ["[OPTIONS]"] @resource[:install_switches].each_pair do |k, v| if FEATURE_RESERVED_SWITCHES.include? k - warn("Reserved switch [#{k}] found for `install_switches`, please know the provided value -may be overridden by some command line arguments") + warn("Reserved switch [#{k}] found for `install_switches`, please know the provided value may be overridden by some command line arguments") end if v.is_a?(Numeric) || (v.is_a?(String) && v =~ /^(true|false|1|0)$/i) config_file << "#{k}=#{v}" @@ -111,7 +122,11 @@ def create warn "Uninstalling all sql server features not tied into an instance because an empty array was passed, please use ensure absent instead." destroy else - installNet35(@resource[:windows_feature_source]) + instance_version = PuppetX::Sqlserver::ServerHelper.sql_version_from_install_source(@resource[:source]) + Puppet.debug("Installation source detected as version #{instance_version}") unless instance_version.nil? + + installNet35(@resource[:windows_feature_source]) unless instance_version == SQL_2016 + debug "Installing features #{@resource[:features]}" add_features(@resource[:features]) @property_hash[:features] = @resource[:features] diff --git a/lib/puppet/provider/sqlserver_instance/mssql.rb b/lib/puppet/provider/sqlserver_instance/mssql.rb index 34b1c5bb..bb290de0 100644 --- a/lib/puppet/provider/sqlserver_instance/mssql.rb +++ b/lib/puppet/provider/sqlserver_instance/mssql.rb @@ -1,12 +1,30 @@ require 'json' require File.expand_path(File.join(File.dirname(__FILE__), '..', 'sqlserver')) - - -INSTANCE_RESERVED_SWITCHES = - %w(AGTSVCACCOUNT AGTSVCPASSWORD ASSVCACCOUNT AGTSVCPASSWORD PID - RSSVCACCOUNT RSSVCPASSWORD SAPWD SECURITYMODE SQLSYSADMINACCOUNTS FEATURES) +require File.expand_path(File.join(File.dirname(__FILE__), '..', '..', '..', 'puppet_x/sqlserver/server_helper')) +require File.expand_path(File.join(File.dirname(__FILE__), '..', '..', '..', 'puppet_x/sqlserver/features')) Puppet::Type::type(:sqlserver_instance).provide(:mssql, :parent => Puppet::Provider::Sqlserver) do + RESOURCEKEY_TO_CMDARG = { + 'agt_svc_account' => 'AGTSVCACCOUNT', + 'agt_svc_password' => 'AGTSVCPASSWORD', + 'as_svc_account' => 'ASSVCACCOUNT', + 'as_svc_password' => 'ASSVCPASSWORD', + 'pid' => 'PID', + 'rs_svc_account' => 'RSSVCACCOUNT', + 'rs_svc_password' => 'RSSVCPASSWORD', + 'polybase_svc_account' => 'PBENGSVCACCOUNT', + 'polybase_svc_password' => 'PBDMSSVCPASSWORD', + 'sa_pwd' => 'SAPWD', + 'security_mode' => 'SECURITYMODE', + 'sql_svc_account' => 'SQLSVCACCOUNT', + 'sql_svc_password' => 'SQLSVCPASSWORD', + } + + def instance_reserved_switches + # List of all puppet managed install switches + RESOURCEKEY_TO_CMDARG.values + ['FEATURES','SQLSYSADMINACCOUNTS'] + end + def self.instances instances = [] result = Facter.value(:sqlserver_instances) @@ -70,19 +88,22 @@ def create warn "Uninstalling all features for instance #{@resource[:name]} because an empty array was passed, please use ensure absent instead." destroy else - installNet35(@resource[:windows_feature_source]) + unless @resource[:as_sysadmin_accounts].nil? || @resource[:features].include?('AS') + fail('The parameter as_sysadmin_accounts was specified however the AS feature was not included in the installed features. Either remove the as_sysadmin_accounts parameter or add AS as a feature to the instance.') + end + unless @resource[:polybase_svc_account].nil? || @resource[:features].include?('POLYBASE') + fail('The parameter polybase_svc_account was specified however the POLYBASE feature was not included in the installed features. Either remove the polybase_svc_account parameter or add POLYBASE as a feature to the instance.') + end + unless @resource[:polybase_svc_password].nil? || @resource[:features].include?('POLYBASE') + fail('The parameter polybase_svc_password was specified however the POLYBASE feature was not included in the installed features. Either remove the polybase_svc_password parameter or add POLYBASE as a feature to the instance.') + end + + instance_version = PuppetX::Sqlserver::ServerHelper.sql_version_from_install_source(@resource[:source]) + Puppet.debug("Installation source detected as version #{instance_version}") unless instance_version.nil? + + installNet35(@resource[:windows_feature_source]) unless instance_version == SQL_2016 + add_features(@resource[:features]) - # cmd_args = build_cmd_args(@resource[:features]) - # begin - # config_file = create_temp_for_install_switch - # cmd_args << "/ConfigurationFile=\"#{config_file.path}\"" unless config_file.nil? - # try_execute(cmd_args) - # ensure - # if config_file - # config_file.close - # config_file.unlink - # end - # end end end @@ -90,9 +111,8 @@ def create_temp_for_install_switch if not_nil_and_not_empty? @resource[:install_switches] config_file = ["[OPTIONS]"] @resource[:install_switches].each_pair do |k, v| - if INSTANCE_RESERVED_SWITCHES.include? k - warn("Reserved switch [#{k}] found for `install_switches`, please know the provided value -may be overridden by some command line arguments") + if instance_reserved_switches.include? k + warn("Reserved switch [#{k}] found for `install_switches`, please know the provided value may be overridden by some command line arguments") end if v.is_a?(Numeric) || (v.is_a?(String) && v =~ /^(true|false|1|0)$/i) config_file << "#{k}=#{v}" @@ -124,9 +144,9 @@ def build_cmd_args(features, action="install") cmd_args = basic_cmd_args(features, action) obfuscated_strings = [] if action == 'install' - %w(pid sa_pwd sql_svc_account sql_svc_password agt_svc_account agt_svc_password as_svc_account as_svc_password rs_svc_account rs_svc_password security_mode).map(&:to_sym).sort.collect do |key| + RESOURCEKEY_TO_CMDARG.keys.sort.collect do |key| if not_nil_and_not_empty? @resource[key] - cmd_args << "/#{key.to_s.gsub(/_/, '').upcase}=\"#{@resource[key]}\"" + cmd_args << "/#{RESOURCEKEY_TO_CMDARG[key]}=\"#{@resource[key.to_sym]}\"" if key.to_s =~ /(_pwd|_password)$/i obfuscated_strings.push(@resource[key]) end diff --git a/lib/puppet/type/sqlserver_features.rb b/lib/puppet/type/sqlserver_features.rb index 0e325ae0..166b295e 100644 --- a/lib/puppet/type/sqlserver_features.rb +++ b/lib/puppet/type/sqlserver_features.rb @@ -36,11 +36,11 @@ newproperty(:features, :array_matching => :all) do desc 'Specifies features to install, uninstall, or upgrade. The list of top-level features include - Tools, BC, Conn, SSMS, ADV_SSMS, SDK, IS and MDS. The Tools feature will install Management - Tools, Books online components, SQL Server Data Tools, and other shared components.' - newvalues(:Tools, :BC, :Conn, :SSMS, :ADV_SSMS, :SDK, :IS, :MDS) + BC, Conn, SSMS, ADV_SSMS, SDK, IS and MDS.' + newvalues(:Tools, :BC, :Conn, :SSMS, :ADV_SSMS, :SDK, :IS, :MDS, :BOL, :DREPLAY_CTLR, :DREPLAY_CLT) munge do |value| if PuppetX::Sqlserver::ServerHelper.is_super_feature(value) + Puppet.deprecation_warning("Using #{value} is deprecated for features in sql_features resources") PuppetX::Sqlserver::ServerHelper.get_sub_features(value).collect { |v| v.to_s } else value diff --git a/lib/puppet/type/sqlserver_instance.rb b/lib/puppet/type/sqlserver_instance.rb index 179ff6c8..66904bd3 100644 --- a/lib/puppet/type/sqlserver_instance.rb +++ b/lib/puppet/type/sqlserver_instance.rb @@ -27,11 +27,11 @@ newproperty(:features, :array_matching => :all) do desc 'Specifies features to install, uninstall, or upgrade. The list of top-level features include - SQL, SQLEngine, Replication, FullText, DQ AS, and RS. The SQL feature will install the Database Engine, - Replication, Full-Text, and Data Quality Services (DQS) server.' - newvalues(:SQL, :SQLEngine, :Replication, :FullText, :DQ, :AS, :RS) + SQLEngine, Replication, FullText, DQ AS, and RS.' + newvalues(:SQL, :SQLEngine, :Replication, :FullText, :DQ, :AS, :RS, :POLYBASE, :ADVANCEDANALYTICS) munge do |value| if PuppetX::Sqlserver::ServerHelper.is_super_feature(value) + Puppet.deprecation_warning("Using #{value} is deprecated for features in sql_instance resources") PuppetX::Sqlserver::ServerHelper.get_sub_features(value).collect { |v| v.to_s } else value @@ -44,11 +44,6 @@ end - newparam(:service_ensure) do - desc 'Automatic will ensure running if stopped, Manual will set to manual and take no action on current state, :diable will stop and change to service disabled' - newvalues(:automatic, :manual, :disable) - end - newparam(:sql_svc_account, :parent => Puppet::Property::SqlserverLogin) do desc 'Account for SQL Server service: Domain\User or system account.' # Default to "NT Service\SQLAGENT$#{instance_name}" @@ -114,6 +109,16 @@ end end + newparam(:polybase_svc_account, :parent => Puppet::Property::SqlserverLogin) do + desc 'The account used by the Polybase Engine service. Only applicable for SQL Server 2016.' + + end + + newparam(:polybase_svc_password) do + desc 'The password for the Polybase Engine service account. Only applicable for SQL Server 2016.' + + end + newparam(:security_mode) do desc 'Specifies the security mode for SQL Server. If this parameter is not supplied, then Windows-only authentication mode is supported. diff --git a/lib/puppet_x/sqlserver/features.rb b/lib/puppet_x/sqlserver/features.rb index bd3a3f02..17921226 100644 --- a/lib/puppet_x/sqlserver/features.rb +++ b/lib/puppet_x/sqlserver/features.rb @@ -1,5 +1,8 @@ SQL_2012 ||= 'SQL_2012' SQL_2014 ||= 'SQL_2014' +SQL_2016 ||= 'SQL_2016' + +ALL_SQL_VERSIONS ||= [SQL_2012, SQL_2014, SQL_2016] module PuppetX module Sqlserver @@ -7,9 +10,22 @@ module Sqlserver class Features private - SQL_WMI_PATH ||= { - SQL_2012 => 'ComputerManagement11', - SQL_2014 => 'ComputerManagement12', + SQL_CONFIGURATION ||= { + SQL_2012 => { + :major_version => 11, + :wmi_path => 'ComputerManagement11', + :registry_path => '110', + }, + SQL_2014 => { + :major_version => 12, + :wmi_path => 'ComputerManagement12', + :registry_path => '120', + }, + SQL_2016 => { + :major_version => 13, + :wmi_path => 'ComputerManagement13', + :registry_path => '130', + } } SQL_REG_ROOT ||= 'Software\Microsoft\Microsoft SQL Server' @@ -20,11 +36,12 @@ class Features def self.connect(version) require 'win32ole' - ver = SQL_WMI_PATH[version] + ver = SQL_CONFIGURATION[version][:wmi_path] context = WIN32OLE.new('WbemScripting.SWbemNamedValueSet') context.Add("__ProviderArchitecture", 64) locator = WIN32OLE.new('WbemScripting.SWbemLocator') - locator.ConnectServer('', "root/Microsoft/SqlServer/#{ver}", '', '', nil, nil, nil, context) + # WMI Path must use backslashes. Ruby 2.3 can cause crashes with forward slashes + locator.ConnectServer(nil, "root\\Microsoft\\SqlServer\\#{ver}", nil, nil, nil, nil, nil, context) end def self.get_parent_path(key_path) @@ -118,9 +135,11 @@ def self.get_instance_features(reg_root, instance_name) # also reg Replication/IsInstalled set to 1 'SQL_Replication_Core_Inst' => 'Replication', # SQL Server Replication # also WMI: SqlService WHERE SQLServiceType = 1 # MSSQLSERVER - 'SQL_Engine_Core_Inst' => 'SQLEngine', # Database Engine Services - 'SQL_FullText_Adv' => 'FullText', # Full-Text and Semantic Extractions for Search - 'SQL_DQ_Full' => 'DQ', # Data Quality Services + 'SQL_Engine_Core_Inst' => 'SQLEngine', # Database Engine Services + 'SQL_FullText_Adv' => 'FullText', # Full-Text and Semantic Extractions for Search + 'SQL_DQ_Full' => 'DQ', # Data Quality Services + 'sql_inst_mr' => 'ADVANCEDANALYTICS', # R Services (In-Database) + 'SQL_Polybase_Core_Inst' => 'POLYBASE', # PolyBase Query Service for External Data } feat_root = "#{reg_root}\\ConfigurationState" @@ -144,18 +163,25 @@ def self.get_instance_features(reg_root, instance_name) def self.get_shared_features(version) shared_features = { - 'Connectivity_Full' => 'Conn', # Client Tools Connectivity - 'SDK_Full' => 'SDK', # Client Tools SDK - 'MDSCoreFeature' => 'MDS', # Master Data Services - 'Tools_Legacy_Full' => 'BC', # Client Tools Backwards Compatibility - 'SQL_SSMS_Full' => 'ADV_SSMS', # Management Tools - Complete - 'SQL_SSMS_Adv' => 'SSMS', # Management Tools - Basic + 'Connectivity_Full' => 'Conn', # Client Tools Connectivity + 'SDK_Full' => 'SDK', # Client Tools SDK + 'MDSCoreFeature' => 'MDS', # Master Data Services + 'Tools_Legacy_Full' => 'BC', # Client Tools Backwards Compatibility + 'SQL_SSMS_Full' => 'ADV_SSMS', # Management Tools - Complete (Does not exist in SQL 2016) + 'SQL_SSMS_Adv' => 'SSMS', # Management Tools - Basic (Does not exist in SQL 2016) + 'SQL_DQ_CLIENT_Full' => 'DQC', # Data Quality Client + 'SQL_BOL_Components' => 'BOL', # Documentation Components + 'SQL_DReplay_Controller' => 'DREPLAY_CTLR', # Distributed Replay Controller + 'SQL_DReplay_Client' => 'DREPLAY_CLT', # Distributed Replay Client + 'sql_shared_mr' => 'SQL_SHARED_MR', # R Server (Standalone) + # also WMI: SqlService WHERE SQLServiceType = 4 # MsDtsServer - 'SQL_DTS_Full' => 'IS', # Integration Services + 'SQL_DTS_Full' => 'IS', # Integration Services # currently ignoring Reporting Services Shared + # currently ignoring R Server Standalone } - reg_ver = (version == SQL_2014 ? '120' : '110') + reg_ver = SQL_CONFIGURATION[version][:registry_path] reg_root = "#{SQL_REG_ROOT}\\#{reg_ver}\\ConfigurationState" get_sql_reg_val_features(reg_root, shared_features) @@ -185,11 +211,27 @@ def self.get_shared_features(version) # } # } def self.get_instances - version_instance_map = [SQL_2012, SQL_2014] + version_instance_map = ALL_SQL_VERSIONS .map do |version| + major_version = SQL_CONFIGURATION[version][:major_version] + instances = get_instance_names_by_ver(version) .map { |name| [ name, get_instance_info(version, name) ] } + # Instance names are unique on a single host, but not for a particular SQL Server version therefore + # it's possible to request information for a valid instance_name but not for version. In this case + # we just reject any instances that have no information + instances.reject! { |value| value[1].nil? } + + # Unfortunately later SQL versions can return previous version SQL instances. We can weed these out + # by inspecting the major version of the instance_version + instances.reject! do |value| + return true if value[1]['version'].nil? + ver = Gem::Version.new(value[1]['version']) + # Segment 0 is the major version number of the SQL Instance + ver.segments[0] != major_version + end + [ version, Hash[instances] ] end @@ -203,10 +245,9 @@ def self.get_instances # "SQL_2014" => [] # } def self.get_features - { - SQL_2012 => get_shared_features(SQL_2012), - SQL_2014 => get_shared_features(SQL_2014), - } + features = {} + ALL_SQL_VERSIONS.each { |version| features[version] = get_shared_features(version) } + features end # returns a hash containing instance details @@ -225,8 +266,13 @@ def self.get_features # "RS" # ] # } - def self.get_instance_info(version = SQL_2014, instance_name) + def self.get_instance_info(version, instance_name) + return nil if version.nil? sql_instance = get_wmi_instance_info(version, instance_name) + # Instance names are unique on a single host, but not for a particular SQL Server version therefore + # it's possible to request information for a valid instance_name but not for version. In this case + # we just return nil. + return nil if sql_instance['reg_root'].nil? feats = get_instance_features(sql_instance['reg_root'], sql_instance['name']) sql_instance.merge({'features' => feats}) end diff --git a/lib/puppet_x/sqlserver/server_helper.rb b/lib/puppet_x/sqlserver/server_helper.rb index 38eca0b4..2313c256 100644 --- a/lib/puppet_x/sqlserver/server_helper.rb +++ b/lib/puppet_x/sqlserver/server_helper.rb @@ -21,6 +21,33 @@ def self.is_domain_or_local_user?(user, hostname) true end end + + # Returns either SQL_2016, SQL_2014 or SQL_2012 if it can determine the SQL Version from the install source + # Returns nil if it can not be determined + def self.sql_version_from_install_source(source_dir) + # Attempt to read the Mediainfo.xml file in the root of the install media + media_file = File.expand_path("#{source_dir}/MediaInfo.xml") + return nil unless File.exist?(media_file) + # As we don't have a XML parser easily, just use simple text matching to find the following XML element. This + # also means we can just ignore BOM markers etc. + # + content = File.read(media_file) + index1 = content.index('"BaselineVersion"') + return nil if index1.nil? + index2 = content.index('/>',index1) + return nil if index2.nil? + content = content.slice(index1 + 18, index2 - index1 - 18) + # Extract the version number from the text snippet + # Value="xx.yyy.zz." + ver = content.match('"(.+)"') + return nil if ver.nil? + + return SQL_2016 if ver[1].start_with?('13.') + return SQL_2014 if ver[1].start_with?('12.') + return SQL_2012 if ver[1].start_with?('11.') + + nil + end end end end diff --git a/lib/puppet_x/sqlserver/sql_connection.rb b/lib/puppet_x/sqlserver/sql_connection.rb index 84a3c764..cbe3fbf3 100644 --- a/lib/puppet_x/sqlserver/sql_connection.rb +++ b/lib/puppet_x/sqlserver/sql_connection.rb @@ -32,7 +32,7 @@ def get_connection_string(config) 'Provider' => 'SQLOLEDB.1', 'Initial Catalog' => config[:database] || 'master', 'Application Name' => 'Puppet', - 'Data Source' => 'localhost' + 'Data Source' => '.' } admin_user = config[:admin_user] || '' @@ -52,7 +52,7 @@ def get_connection_string(config) end if config[:instance_name] != nil && config[:instance_name] !~ /^MSSQLSERVER$/ - params['Data Source'] = "localhost\\#{config[:instance_name]}" + params['Data Source'] = ".\\#{config[:instance_name]}" end params.map { |k, v| "#{k}=#{v}" }.join(';') diff --git a/manifests/config.pp b/manifests/config.pp index 81c72e7b..a33c41c6 100644 --- a/manifests/config.pp +++ b/manifests/config.pp @@ -23,10 +23,10 @@ # } # define sqlserver::config ( - $admin_user = '', - $admin_pass = '', - $admin_login_type = 'SQL_LOGIN', - $instance_name = $title, + Optional[String] $admin_user = '', + Optional[String] $admin_pass = '', + Enum['SQL_LOGIN', 'WINDOWS_LOGIN'] $admin_login_type = 'SQL_LOGIN', + String[1,16] $instance_name = $title, ) { ##This config is a catalog requirement for sqlserver_tsql and is looked up to retrieve the admin_user, ## admin_pass and admin_login_type for a given instance_name diff --git a/manifests/database.pp b/manifests/database.pp index a2fcbd0b..31c41347 100644 --- a/manifests/database.pp +++ b/manifests/database.pp @@ -83,32 +83,32 @@ # @see http://msdn.microsoft.com/en-us/library/ms190303.aspx System Languages # define sqlserver::database ( - $db_name = $title, - $instance = 'MSSQLSERVER', - $ensure = present, - $compatibility = 100, - $collation_name = undef, - $filestream_non_transacted_access = undef, - $filestream_directory_name = undef, - $filespec_name = undef, - $filespec_filename = undef, - $filespec_size = undef, - $filespec_maxsize = undef, - $filespec_filegrowth = undef, - $log_name = undef, - $log_filename = undef, - $log_size = undef, - $log_maxsize = undef, - $log_filegrowth = undef, - $containment = 'NONE', + String[1,128] $db_name = $title, + String[1,16] $instance = 'MSSQLSERVER', + Enum['present', 'absent'] $ensure = 'present', + Integer $compatibility = 100, + Optional[String[1]] $collation_name = undef, + Optional[Enum['OFF', 'READ_ONLY', 'FULL']] $filestream_non_transacted_access = undef, + Optional[Pattern[/^[\w|\s]+$/]] $filestream_directory_name = undef, + Optional[String[1,128]] $filespec_name = undef, + Optional[Stdlib::Absolutepath] $filespec_filename = undef, + Optional[String[1]] $filespec_size = undef, + Optional[String[1]] $filespec_maxsize = undef, + Optional[String[1]] $filespec_filegrowth = undef, + Optional[String[1,128]] $log_name = undef, + Optional[Stdlib::Absolutepath] $log_filename = undef, + Optional[String[1]] $log_size = undef, + Optional[String[1]] $log_maxsize = undef, + Optional[String[1]] $log_filegrowth = undef, + Enum['PARTIAL', 'NONE'] $containment = 'NONE', #require Containment = 'PARTIAL' for the following params to be executed - $default_fulltext_language = 'English', - $default_language = 'us_english', - $nested_triggers = undef, - $transform_noise_words = undef, - $two_digit_year_cutoff = 2049, - $db_chaining = 'OFF', - $trustworthy = 'OFF', + String[1] $default_fulltext_language = 'English', + String[1] $default_language = 'us_english', + Optional[Enum['ON', 'OFF']] $nested_triggers = undef, + Optional[Enum['ON', 'OFF']] $transform_noise_words = undef, + Integer[1753, 9999] $two_digit_year_cutoff = 2049, + Enum['ON', 'OFF'] $db_chaining = 'OFF', + Enum['ON', 'OFF'] $trustworthy = 'OFF', ){ ## # validate max size @@ -123,49 +123,38 @@ if $filespec_maxsize and $filespec_maxsize != 'UNLIMITED' { sqlserver_validate_size($filespec_maxsize) } + if $filespec_filename or $filespec_name { - validate_re($filespec_filename, '^.+$', 'filespec_filename must not be null if specifying filespec_name') - validate_re($filespec_name, '^.+$', 'filespec_name must not be null if specifying filespec_filename') - sqlserver_validate_range($filespec_name, 1, 128, 'filespec_name can not be more than 128 characters and must be at least 1 character in length') - validate_absolute_path($filespec_filename) - } - if $log_filename { - sqlserver_validate_range($log_name, 1, 128, "${log_name} can not be more than 128 characters and must be at least 1 character in length") - validate_absolute_path($log_filename) + assert_type(String[1], $filespec_filename) |$expected, $actual| { + fail('filespec_filename must also be specified when specifying filespec_name') + } + assert_type(String[1], $filespec_name) |$expected, $actual| { + fail('filespec_name must also be specified when specifying filespec_filename') + } } + if $log_size { sqlserver_validate_size($log_size) } if $log_maxsize { sqlserver_validate_size($log_maxsize) } - if $log_filename or $log_filegrowth or $log_maxsize or $log_name or $log_size { - sqlserver_validate_range($filespec_filename, 1, 128, 'filespec_name and filespec_filename must be specified when specifying any log attributes') - validate_absolute_path($filespec_filename) - } -## VALIDATE FILESTREAM - if $filestream_non_transacted_access { - validate_re($filestream_non_transacted_access, '^(OFF|READ_ONLY|FULL)$', - "filestream_non_transacted_access can be OFF|READ_ONLY|FULL only, you provided ${filestream_non_transacted_access}") + if $log_filename or $log_name { + assert_type(String[1], $log_filename) |$expected, $actual| { + fail('log_filename must also be specified when specifying log_name') + } + assert_type(String[1], $log_name) |$expected, $actual| { + fail('log_name must also be specified when specifying log_filename') + } } - if $filestream_directory_name { - validate_re($filestream_directory_name,'^[\w|\s]+$', - "Filestream Directory Name should not be an absolute path but a directory name only, you provided ${filestream_directory_name}") - } - - sqlserver_validate_instance_name($instance) - - validate_re($containment, '^(PARTIAL|NONE)$', "Containment must be either PARTIAL or NONE, you provided ${containment}") -## Validate PARTIAL required variables switches - if $containment == 'PARTIAL' { - if $db_chaining { sqlserver_validate_on_off($db_chaining) } - if $nested_triggers { sqlserver_validate_on_off($nested_triggers) } - if $transform_noise_words { sqlserver_validate_on_off($transform_noise_words) } - if $trustworthy { sqlserver_validate_on_off($trustworthy) } - sqlserver_validate_range($two_digit_year_cutoff, 1753, 9999, - "Two digit year cutoff must be between 1753 and 9999, you provided ${two_digit_year_cutoff}") + if $log_filename or $log_filegrowth or $log_maxsize or $log_name or $log_size { + assert_type(String[1], $filespec_filename) |$expected, $actual| { + fail('filespec_filename must also be specified when specifying any log attribute') + } + assert_type(String[1], $filespec_name) |$expected, $actual| { + fail('filespec_name must also be specified when specifying any log attribute') + } } - - validate_re($ensure,['^present$','^absent$'],"Ensure must be either present or absent, you provided ${ensure}") + sqlserver_validate_instance_name($instance) $create_delete = $ensure ? { present => 'create', @@ -178,6 +167,4 @@ onlyif => template('sqlserver/query/database_exists.sql.erb'), require => Sqlserver::Config[$instance], } - - } diff --git a/manifests/login.pp b/manifests/login.pp index e712902f..6b6d0fa8 100644 --- a/manifests/login.pp +++ b/manifests/login.pp @@ -51,23 +51,21 @@ ## define sqlserver::login ( $login = $title, - $instance = 'MSSQLSERVER', - $ensure = 'present', - $password = undef, - $svrroles = { }, - $login_type = 'SQL_LOGIN', - $default_database = 'master', - $default_language = 'us_english', - $check_expiration = false, - $check_policy = true, - $disabled = false, - $permissions = { }, + String[1,16] $instance = 'MSSQLSERVER', + Enum['SQL_LOGIN', 'WINDOWS_LOGIN'] $login_type = 'SQL_LOGIN', + Enum['present', 'absent'] $ensure = 'present', + Optional[String] $password = undef, + Optional[Hash] $svrroles = { }, + String $default_database = 'master', + String $default_language = 'us_english', + Boolean $check_expiration = false, + Boolean $check_policy = true, + Boolean $disabled = false, + Optional[Hash] $permissions = { }, ) { sqlserver_validate_instance_name($instance) - validate_re($login_type,['^(SQL_LOGIN|WINDOWS_LOGIN)$']) - if $check_expiration and !$check_policy { fail ('Can not have check expiration enabled when check_policy is disabled') } @@ -85,7 +83,6 @@ } if $ensure == present { - validate_hash($permissions) $_upermissions = sqlserver_upcase($permissions) sqlserver_validate_hash_uniq_values($_upermissions, "Duplicate permissions found for sqlserver::login[${title}]") diff --git a/manifests/login/permissions.pp b/manifests/login/permissions.pp index 0e498360..c25752a2 100644 --- a/manifests/login/permissions.pp +++ b/manifests/login/permissions.pp @@ -21,25 +21,16 @@ # ## define sqlserver::login::permissions ( - $login, - $permissions, - $state = 'GRANT', - $with_grant_option = false, - $instance = 'MSSQLSERVER', + String[1,128] $login, + Array[String[4,128]] $permissions, + Pattern[/(?i)^(GRANT|REVOKE|DENY)$/] $state = 'GRANT', + Optional[Boolean] $with_grant_option = false, + String[1,16] $instance = 'MSSQLSERVER', ){ sqlserver_validate_instance_name($instance) -## Validate Permissions - sqlserver_validate_range($permissions, 4, 128, 'Permission must be between 4 and 128 characters') - validate_array($permissions) - - sqlserver_validate_range($login, 1, 128, 'Login must be between 1 and 128 characters') - -## Validate state $_state = upcase($state) - validate_re($_state,'^(GRANT|REVOKE|DENY)$', "State parameter can only be one of 'GRANT', 'REVOKE' or 'DENY', you passed a value of ${state}") - validate_bool($with_grant_option) $_grant_option = $with_grant_option ? { true => '-WITH_GRANT_OPTION', default => '' diff --git a/manifests/role.pp b/manifests/role.pp index 70acce1d..dc7e8136 100644 --- a/manifests/role.pp +++ b/manifests/role.pp @@ -37,22 +37,18 @@ # Whether we should purge any members not listed in the members parameter. Default: false ## define sqlserver::role( - $ensure = present, - $role = $title, - $instance = 'MSSQLSERVER', - $authorization = undef, - $type = 'SERVER', - $database = 'master', - $permissions = { }, - $members = [], - $members_purge = false, + String[1,128] $role = $title, + String[1,16] $instance = 'MSSQLSERVER', + Enum['present', 'absent'] $ensure = 'present', + Optional[String] $authorization = undef, + Enum['SERVER', 'DATABASE'] $type = 'SERVER', + String[1,128] $database = 'master', + Optional[Hash] $permissions = { }, + Array[String] $members = [], + Boolean $members_purge = false, ){ sqlserver_validate_instance_name($instance) - sqlserver_validate_range($role, 1, 128, 'Role names must be between 1 and 128 characters') - validate_re($type, ['^SERVER$','^DATABASE$'], "Type must be either 'SERVER' or 'DATABASE', provided '${type}'") - - sqlserver_validate_range($database, 1, 128, 'Database name must be between 1 and 128 characters') if $type == 'SERVER' and $database != 'master' { fail('Can not specify a database other than master when managing SERVER ROLES') } @@ -75,7 +71,6 @@ } if $ensure == present { - validate_hash($permissions) $_upermissions = sqlserver_upcase($permissions) Sqlserver::Role::Permissions{ @@ -111,7 +106,6 @@ } } - validate_array($members) if size($members) > 0 or $members_purge == true { sqlserver_tsql{ "role-${role}-members": command => template('sqlserver/create/role/members.sql.erb'), diff --git a/manifests/role/permissions.pp b/manifests/role/permissions.pp index cb80072c..b1bc5dd9 100644 --- a/manifests/role/permissions.pp +++ b/manifests/role/permissions.pp @@ -31,32 +31,21 @@ # ## define sqlserver::role::permissions ( - $role, - $permissions, - $state = 'GRANT', - $with_grant_option = false, - $type = 'SERVER', - $database = 'master', - $instance = 'MSSQLSERVER', + String[1,128] $role, + Array[String[4,128]] $permissions, + Pattern[/(?i)^(GRANT|REVOKE|DENY)$/] $state = 'GRANT', + Optional[Boolean] $with_grant_option = false, + Enum['SERVER','DATABASE'] $type = 'SERVER', + String[1,128] $database = 'master', + String[1,16] $instance = 'MSSQLSERVER', ){ - validate_array($permissions) if size($permissions) < 1 { warning("Received an empty set of permissions for ${title}, no further action will be taken") } else{ sqlserver_validate_instance_name($instance) - #Validate state $_state = upcase($state) - validate_re($_state,'^(GRANT|REVOKE|DENY)$',"State can only be of 'GRANT', 'REVOKE' or 'DENY' you passed ${state}") - validate_bool($with_grant_option) - - #Validate role - sqlserver_validate_range($role, 1, 128, 'Role names must be between 1 and 128 characters') - - #Validate permissions - sqlserver_validate_range($permissions, 4, 128, 'Permissions must be between 4 and 128 characters') $_upermissions = upcase($permissions) - $_grant_option = $with_grant_option ? { true => '-WITH_GRANT_OPTION', false => '', diff --git a/manifests/sp_configure.pp b/manifests/sp_configure.pp index 32322221..4af52040 100644 --- a/manifests/sp_configure.pp +++ b/manifests/sp_configure.pp @@ -27,22 +27,14 @@ # @see http://msdn.microsoft.com/en-us/library/ms189631.aspx Server Configuration Options ## define sqlserver::sp_configure ( - $value, - $config_name = $title, - $instance = 'MSSQLSERVER', - $reconfigure = true, - $with_override = false, - $restart = false, + Integer $value, + Pattern['^\w+'] $config_name = $title, + String[1,16] $instance = 'MSSQLSERVER', + Boolean $reconfigure = true, + Boolean $with_override = false, + Boolean $restart = false, ){ sqlserver_validate_instance_name($instance) - validate_re($config_name,'^\w+') - if !is_integer($value) { - fail("Value for ${config_name}, for instance ${instance}, must be a integer value, you provided ${value}") - } - - validate_bool($reconfigure) - validate_bool($with_override) - validate_bool($restart) $service_name = $instance ? { 'MSSQLSERVER' => 'MSSQLSERVER', diff --git a/manifests/user.pp b/manifests/user.pp index 29139e83..c7bde386 100644 --- a/manifests/user.pp +++ b/manifests/user.pp @@ -39,14 +39,14 @@ # ## define sqlserver::user ( - $database, - $ensure = 'present', - $user = $title, - $default_schema = undef, - $instance = 'MSSQLSERVER', - $login = undef, - $password = undef, - $permissions = { }, + String[1,128] $database, + Enum['present', 'absent'] $ensure = 'present', + String[1] $user = $title, + Optional[String] $default_schema = undef, + String[1,16] $instance = 'MSSQLSERVER', + Optional[String[1]] $login = undef, + Optional[String[1,128]] $password = undef, + Optional[Hash] $permissions = { }, ) { sqlserver_validate_instance_name($instance) @@ -54,12 +54,10 @@ $is_windows_user = sqlserver_is_domain_or_local_user($login) if $password { - sqlserver_validate_range($password, 1, 128, 'Password must be equal or less than 128 characters') if $is_windows_user and $login != undef{ fail('Can not provide password when using a Windows Login') } } - sqlserver_validate_range($database, 1, 128, 'Database name must be between 1 and 128 characters') $create_delete = $ensure ? { present => 'create', @@ -74,7 +72,6 @@ } if $ensure == present { - validate_hash($permissions) $_upermissions = sqlserver_upcase($permissions) sqlserver_validate_hash_uniq_values($_upermissions, "Duplicate permissions found for sqlserver::user[${title}]") diff --git a/manifests/user/permissions.pp b/manifests/user/permissions.pp index e6d6c077..7ab14f78 100644 --- a/manifests/user/permissions.pp +++ b/manifests/user/permissions.pp @@ -27,32 +27,20 @@ # ## define sqlserver::user::permissions ( - $user, - $database, - $permissions, - $state = 'GRANT', - $with_grant_option = false, - $instance = 'MSSQLSERVER', + String[1,128] $user, + Array[String[4,128]] $permissions, + String[1,128] $database = 'master', + Pattern[/(?i)^(GRANT|REVOKE|DENY)$/] $state = 'GRANT', + Optional[Boolean] $with_grant_option = false, + String[1,16] $instance = 'MSSQLSERVER', ){ sqlserver_validate_instance_name($instance) -## Validate Permissions - sqlserver_validate_range($permissions, 4, 128, 'Permission must be between 4 and 128 characters') - validate_array($permissions) - -## Validate state $_state = upcase($state) - validate_re($_state,'^(GRANT|REVOKE|DENY)$',"State can only be of 'GRANT', 'REVOKE' or 'DENY' you passed ${state}") - - validate_bool($with_grant_option) if $with_grant_option and $_state != 'GRANT' { fail("Can not use with_grant_option and state ${_state}, must be 'GRANT'") } - sqlserver_validate_range($database, 1, 128, 'Database must be between 1 and 128 characters') - - sqlserver_validate_range($user, 1, 128, 'User must be between 1 and 128 characters') - $_grant_option = $with_grant_option ? { true => '-WITH_GRANT_OPTION', default => '' diff --git a/metadata.json b/metadata.json index 8a378281..403e3fa8 100644 --- a/metadata.json +++ b/metadata.json @@ -1,8 +1,8 @@ { "name": "puppetlabs-sqlserver", - "version": "1.2.0", + "version": "2.0.0", "author": "Puppet Inc", - "summary": "The `sqlserver` module installs and manages MS SQL Server 2012 and 2014 on Windows systems.", + "summary": "The `sqlserver` module installs and manages MS SQL Server 2012, 2014 and 2016 on Windows systems.", "license": "proprietary", "source": "https://tickets.puppet.com/browse/MODULES/component/12400", "project_page": "https://github.com/puppetlabs/puppetlabs-sqlserver", @@ -14,6 +14,7 @@ "microsoft", "sql2012", "sql2014", + "sql2016", "tsql", "database" ], @@ -22,17 +23,18 @@ "operatingsystem": "Windows", "operatingsystemrelease": [ "Server 2012", - "Server 2012 R2" + "Server 2012 R2", + "Server 2016" ] } ], "requirements": [ { "name": "puppet", - "version_requirement": ">=3.7.0 < 5.0.0" + "version_requirement": ">=4.7.0 < 6.0.0" } ], "dependencies": [ - { "name": "puppetlabs-stdlib", "version_requirement": "4.x" } + { "name":"puppetlabs/stdlib","version_requirement":">= 4.13.1 < 5.0.0" } ] } diff --git a/readmes/README_ja_JP.md b/readmes/README_ja_JP.md index 56392f46..fc64e087 100644 --- a/readmes/README_ja_JP.md +++ b/readmes/README_ja_JP.md @@ -5,7 +5,7 @@ 1. [概要](#概要) 2. [モジュールの説明 - モジュールの機能とその有益性](#モジュールの説明) 3. [セットアップ - sqlserver導入の基本](#セットアップ) - * [セットアップ要件](#setup-requirements) + * [セットアップ要件](#セットアップ要件) * [sqlserverを開始する](#sqlserverを開始する) 4. [使用方法 - 構成オプションと追加機能](#使用方法) * [SQL Serverツールおよび機能のインストール](#sql-serverインスタンス専用ではないsql-serverツールおよび機能をインストールする) @@ -80,13 +80,6 @@ sqlserver_instance{ 'MSSQLSERVER': ### SQL Serverインスタンス専用ではないSQL Serverツールおよび機能をインストールする -```puppet -sqlserver_features { 'Generic Features': - source => 'E:/', - features => ['Tools'], -} -``` - ```puppet sqlserver_features { 'Generic Features': source => 'E:/', @@ -184,7 +177,82 @@ sqlserver_tsql{ 'Always running': } ``` -## リファレンス +### 高度な例 + +この高度な例について: + +* 基本的なSQL Server Engineを、'D:\'にマウントされたインストールメディアを使ってインストールします。インストールの際は、TCPを有効にし、各種ディレクトリを設定します。 + +* Windowsベースの認証のみを使用し、Puppetを実行しているユーザとしてのみインストールします。'sql_sysadmin_accounts'はインスタンスのインストール時にのみ適用され、その後は選択しない限り適用されないので注意が必要です。 + +* 後続のリソースが新規作成されたリソースに接続するために使われる、`sqlserver::config`リソースを作成します。Windowsベースの認証のみサポートしているため、ユーザ名とパスワードは必要ありません。 + +* 'DB Administrators'という名前のローカルグループを作成し、このグループがSQLシステム管理者(sysadminロール)であることを確認します。また、Puppetがインスタンスのインストールと管理に使用するアカウントも作成します。 + +* Puppetがインスタンスの'max memory'設定を管理できるように、`sp_configure`の詳細オプションが有効になっていることを確認します。 + +* `max memory` (MB)構成アイテムが2048メガバイトに設定されていることを確認します。 + +```puppet +$sourceloc = 'D:/' + +# SQL Serverのデフォルトインスタンスをインストールする +sqlserver_instance{'MSSQLSERVER': + source => $sourceloc, + features => ['SQLEngine'], + sql_sysadmin_accounts => [$facts['id']], + install_switches => { + 'TCPENABLED' => 1, + 'SQLBACKUPDIR' => 'C:\\MSSQLSERVER\\backupdir', + 'SQLTEMPDBDIR' => 'C:\\MSSQLSERVER\\tempdbdir', + 'INSTALLSQLDATADIR' => 'C:\\MSSQLSERVER\\datadir', + 'INSTANCEDIR' => 'C:\\Program Files\\Microsoft SQL Server', + 'INSTALLSHAREDDIR' => 'C:\\Program Files\\Microsoft SQL Server', + 'INSTALLSHAREDWOWDIR' => 'C:\\Program Files (x86)\\Microsoft SQL Server' + } +} + +# DBインスタンスに接続するためのリソース +sqlserver::config { 'MSSQLSERVER': + admin_login_type => 'WINDOWS_LOGIN' +} + +# SQL Server Administratorsを適用する +$local_dba_group_name = 'DB Administrators' +$local_dba_group_netbios_name = "${facts['hostname']}\\DB Administrators" + +group { $local_dba_group_name: + ensure => present +} + +-> sqlserver::login { $local_dba_group_netbios_name : + login_type => 'WINDOWS_LOGIN', +} + +-> sqlserver::role { 'sysadmin': + ensure => 'present', + instance => 'MSSQLSERVER', + type => 'SERVER', + members => [$local_dba_group_netbios_name, $facts['id']], +} + +# メモリ消費を適用する +sqlserver_tsql {'check advanced sp_configure': + command => 'EXEC sp_configure \'show advanced option\', \'1\'; RECONFIGURE;', + onlyif => 'sp_configure @configname=\'max server memory (MB)\'', + instance => 'MSSQLSERVER' +} + +-> sqlserver::sp_configure { 'MSSQLSERVER-max memory': + config_name => 'max server memory (MB)', + instance => 'MSSQLSERVER', + reconfigure => true, + restart => true, + value => 2048 +} +``` + +## 参考 ### タイプ @@ -202,15 +270,17 @@ SSMSやMaster Data Serviceなどの機能をインストールおよび構成し ##### `features` -*必須指定です。* - -管理する機能を1つまたは複数指定します。有効なオプション: 'BC'、'Conn'、'SSMS'、'ADV_SSMS'、'SDK'、'IS'、'MDS'、'Tools' (Toolsの機能にはSSMS、ADV_SSMS、Connが含まれます)。 +*必須。* + +管理する機能を1つまたは複数指定します。有効なオプション: 'BC'、'Conn'、'SSMS'、'ADV_SSMS'、'SDK'、'IS'、'MDS'、'BOL'、'DREPLAY_CTLR'、'DREPLAY_CLT'。 + +この設定の'Tools'値は廃止されました。'BC'、'SSMS'、'ADV_SSMS'、'Conn'、'SDK'のいずれかのみを指定してください。 ##### `install_switches` -1つまたは複数のインストーラスイッチをSQL Serverセットアップに受け渡します。あるオプションが個別のパラメータと`install_switches`の両方で指定されている場合、個別に指定されたパラメータが優先されます。たとえば、`pid`と`install_switches`の両方にプロダクトキーが設定されている場合、SQL Serverは`pid`パラメータを優先します。有効なオプション: 配列。 +1つまたは複数のインストーラスイッチをSQL Serverセットアップに受け渡します。あるオプションが個別のパラメータと`install_switches`の両方で指定されている場合、個別に指定されたパラメータが優先されます。例えば、`pid`と`install_switches`の両方にプロダクトキーが設定されている場合、SQL Serverは`pid`パラメータを優先します。有効なオプション: 配列。 -> **注**: あるオプションが個別のパラメータと`install_switches`の両方で指定されている場合、個別に指定されたパラメータが優先されます。たとえば、`pid`と`install_switches`の両方にプロダクトキーが設定されている場合、SQL Serverは`pid`パラメータを優先します。 +> **注**: あるオプションが個別のパラメータと`install_switches`の両方で指定されている場合、個別に指定されたパラメータが優先されます。例えば、`pid`と`install_switches`の両方にプロダクトキーが設定されている場合、SQL Serverは`pid`パラメータを優先します。 > > インストーラスイッチの詳細とSQL Serverの構成方法については、次のリンクを参照してください。 > @@ -238,7 +308,7 @@ SQL Serverのプロダクトキーを指定します。有効なオプション: ##### `source` -*必須指定です。* +*必須。* SQL Server installerの場所を指定します。有効なオプション: 実行ファイルへのパスを含む文字列。Puppetは、インストーラを実行可能なパーミッションを持つ必要があります。 @@ -294,13 +364,15 @@ sysadminステータスを受け取る1つまたは複数のSQLアカウント ##### `features` -*必須指定です。* 1つまたは複数の管理対象の機能を指定します。最上位機能のリストには、'SQL'、'AS'、'RS'が含まれます。'SQL'機能には、Database Engine、Replication、Full-Text、Data Quality Services (DQS)サーバが含まれます。有効なオプション: 'SQL'、'SQLEngine'、'Replication'、'FullText'、'DQ'、'AS'、'RS'の文字列うち1つまたは複数を含む配列。 +*必須。* 管理する機能を1つまたは複数指定します。最上位機能のリストには、AS'と'RS'が含まれます。有効なオプション: 'SQL'、'SQLEngine'、'Replication'、'FullText'、'DQ'、'AS'、'RS'、'POLYBASE'、'ADVANCEDANALYTICS'の文字列のうち、1つまたは複数を含む配列。 + +この設定の'SQL'値は廃止されました。'DQ'、'FullText'、'Replication'、'SQLEngine'のいずれかのみを指定してください。 ##### `install_switches` SQL Server Instance Setupに1つまたは複数の追加インストーラスイッチを受け渡します。有効なオプション: [インストーラスイッチ](https://msdn.microsoft.com/en-us/library/ms144259.aspx)のハッシュ値。 -> **注**: あるオプションが個別のパラメータと`install_switches`の両方で指定されている場合、個別に指定されたパラメータが優先されます。たとえば、`pid`と`install_switches`の両方にプロダクトキーが設定されている場合、SQL Serverは`pid`パラメータを優先します。 +> **注**: あるオプションが個別のパラメータと`install_switches`の両方で指定されている場合、個別に指定されたパラメータが優先されます。例えば、`pid`と`install_switches`の両方にプロダクトキーが設定されている場合、SQL Serverは`pid`パラメータを優先します。 > > インストーラスイッチの詳細とSQL Serverの構成方法については、次のリンクを参照してください。 > @@ -319,6 +391,22 @@ SQL Serverのプロダクトキーを指定します。有効なオプション: デフォルト値: `undef`。 +##### `polybase_svc_account` + +** SQL Server 2016のPOLYBASE 機能がインストールされている場合にのみ該当します。** + +Polybase Engineサービスによって使用されるドメインアカウントまたはシステムアカウントを指定します。 + +有効なオプション: 既存のユーザ名を指定する文字列。 + +##### `polybase_svc_password` + +** SQL Server 2016のPOLYBASE 機能がインストールされている場合にのみ該当します。** + +Polybase Engineサービスのパスワードを指定します。 + +有効なオプション: 有効なパスワードを指定する文字列。 + ##### `rs_svc_account` レポートサービスによって使用されるドメインアカウントまたはシステムアカウントを指定します。有効なオプション: `'"/ \ [ ] : ; | = , + * ? < >'`を含まない文字列。ドメインユーザアカウントを指定する場合、ドメインは254文字未満、ユーザ名は20文字未満である必要があります。 @@ -343,13 +431,9 @@ SQL Serverのセキュリティモードを指定します。有効なオプシ デフォルト値: `undef`。 -##### `service_ensure` - -SQL Serverサービスを実行中にするかどうかを指定します。有効なオプション: 'automatic' (実行されていない場合、Puppetがサービスを起動)、'manual' (Puppetは何もしない)、'disable' (実行されている場合、Puppetがサービスを停止)。 - ##### `source` -*必須指定です。* +*必須。* SQL Server installerの場所を指定します。有効なオプション: 実行ファイルへのパスを含む文字列。Puppetは、インストーラを実行可能なパーミッションを持つ必要があります。 @@ -367,7 +451,7 @@ SQL Serverサービスユーザアカウントのパスワードを提供しま ##### `sql_sysadmin_accounts` -*必須指定です。* +*必須。* sysadminステータスを受け取る1つまたは複数のSQLアカウントまたはシステムアカウントを指定します。有効なオプション: 1つまたは複数の有効なユーザ名を指定する配列。 @@ -387,7 +471,7 @@ SQL Serverインスタンスに対し、TSQLクエリを実行します。 ##### `instance` -*必須指定です。* +*必須。* ステートメントを実行するSQL Serverインスタンスを指定します。有効なオプション: 既存のインスタンス名を含む文字列。 @@ -417,9 +501,9 @@ SQL Serverインスタンスを管理するために使用するログインの - SQL Serverベースの認証を使用する場合 - `SQL_LOGIN` - * `admin_pass`: *必須指定です。* 指定された`admin_user`アカウントのパスワードを提供します。有効なオプション: 有効なパスワードを指定する文字列。 + * `admin_pass`: *必須。* 指定された`admin_user`アカウントのパスワードを提供します。有効なオプション: 有効なパスワードを指定する文字列。 - * `admin_user`: *必須指定です。* サーバのsysadmin権限をもつログインアカウントを指定します。これは、インスタンスの管理に使用します。有効なオプション: ユーザ名を含む文字列。 + * `admin_user`: *必須。* サーバのsysadmin権限をもつログインアカウントを指定します。これは、インスタンスの管理に使用します。有効なオプション: ユーザ名を含む文字列。 - Windowsベースの認証を使用する場合 - `WINDOWS_LOGIN` @@ -447,11 +531,11 @@ SQL Serverインスタンスを管理するために使用するログインの ##### `compatibility`: -データベースが互換性をもつSQL Serverのバージョンを1つまたは複数指定します。有効なオプション: 互換性レベル番号(例: SQL Server 2008~SQL Server 2014では100)。値の一覧については[SQL Serverドキュメンテーション](http://msdn.microsoft.com/en-us/library/bb510680.aspx)を参照してください。 +データベースが互換性をもつSQL Serverのバージョンを1つまたは複数指定します。有効なオプション: 互換性レベル番号(例: SQL Server 2008~SQL Server 2014では100)。値の一覧については[SQL Serverドキュメント](http://msdn.microsoft.com/en-us/library/bb510680.aspx)を参照してください。 ##### `containment` -データベースのコンテインメントの種類を設定します。コンテインメントの詳細については、[SQL Serverドキュメンテーション](http://msdn.microsoft.com/en-us/library/ff929071.aspx)を参照してください。有効なオプション: 'NONE'および'PARTIAL' ('PARTIAL'には`sqlserver::sp_configure`定義タイプが必要です)。 +データベースのコンテインメントの種類を設定します。コンテインメントの詳細については、[SQL Serverドキュメント](http://msdn.microsoft.com/en-us/library/ff929071.aspx)を参照してください。有効なオプション: 'NONE'および'PARTIAL' ('PARTIAL'には`sqlserver::sp_configure`定義タイプが必要です)。 デフォルト値: 'NONE'。 @@ -461,7 +545,7 @@ SQL Serverインスタンスを管理するために使用するログインの デフォルト値: 'OFF'。 -##### `db_name`: *必須指定です。* +##### `db_name`: *必須。* 管理対象のデータベースを指定します。有効なオプション: データベース名を含む文字列。 @@ -469,13 +553,13 @@ SQL Serverインスタンスを管理するために使用するログインの ##### `default_fulltext_language` -デフォルトのフルテキスト言語を設定します。`containment`が'PARTIAL'に設定されている場合のみ有効です。有効なオプション: [SQL Serverドキュメンテーション](http://msdn.microsoft.com/en-us/library/ms190303.aspx)を参照してください。 +デフォルトのフルテキスト言語を設定します。`containment`が'PARTIAL'に設定されている場合のみ有効です。有効なオプション: [SQL Serverドキュメント](http://msdn.microsoft.com/en-us/library/ms190303.aspx)を参照してください。 デフォルト値: 'English'。 ##### `default_language` -デフォルト言語を設定します。`containment`が'PARTIAL'に設定されている場合のみ有効です。有効なオプション: [SQL Serverドキュメンテーション](http://msdn.microsoft.com/en-us/library/ms190303.aspx)を参照してください。 +デフォルト言語を設定します。`containment`が'PARTIAL'に設定されている場合のみ有効です。有効なオプション: [SQL Serverドキュメント](http://msdn.microsoft.com/en-us/library/ms190303.aspx)を参照してください。 デフォルト値: 'us_english'。 @@ -515,7 +599,7 @@ SQL Serverインスタンス内のfilespecオブジェクトの論理名を指 ##### `filespec_size` -filespecファイルのサイズを指定します。このパラメータは作成時のみに設定され、アップデートの影響を受けません。有効なオプション: 2147483647以下の数値。オプションで接尾文字'KB'、'MB'、'GB'または'TB'を含めることができます。接尾文字を付けない場合、SQL Serverは単位をMBとみなします。 +filespecファイルのサイズを指定します。このパラメータは作成時のみに設定され、アップデートの影響を受けません。有効なオプション: 2147483647以下の数値。オプションで接尾文字'KB'、'MB'、'GB'または'TB'を含めることができます。接尾文字を付けない場合、SQL Serverでは単位をMBとみなされます。 デフォルト値: `undef`。 @@ -572,7 +656,7 @@ SQL Server内のログオブジェクトの論理名を指定します。この ##### `nested_triggers` -カスケーディングトリガを有効にするかどうかを指定します。`containment`が'PARTIAL'に設定されている場合のみ有効です。トリガのネストの詳細については、[SQL Serverドキュメンテーション](http://msdn.microsoft.com/en-us/library/ms178101.aspx)を参照してください。有効なオプション: 'ON'および'OFF'。 +カスケーディングトリガを有効にするかどうかを指定します。`containment`が'PARTIAL'に設定されている場合のみ有効です。トリガのネストの詳細については、[SQL Serverドキュメント](http://msdn.microsoft.com/en-us/library/ms178101.aspx)を参照してください。有効なオプション: 'ON'および'OFF'。マニュアル デフォルト値: `undef`。 @@ -601,7 +685,7 @@ SQL Server内のログオブジェクトの論理名を指定します。この > * [データベースのTSQLを変更する](http://msdn.microsoft.com/en-us/library/ms174269.aspx) > * [システム言語](http://msdn.microsoft.com/en-us/library/ms190303.aspx) > -> FILESTREAMを使用するには、SQL Serverの手作業による構成が必要となる場合があります。詳細については、[FILESTREAMの有効化と構成](http://msdn.microsoft.com/en-us/library/cc645923.aspx)を参照してください。 +> FILESTREAMを使用するには、SQL Serverを手動で構成する作業が必要になる場合があります。詳細については、[FILESTREAMの有効化と構成](http://msdn.microsoft.com/en-us/library/cc645923.aspx)を参照してください。 #### `sqlserver::login` @@ -627,7 +711,7 @@ SQL Server内のログオブジェクトの論理名を指定します。この ##### `default_language` -デフォルト言語を指定します。有効なオプション: [SQL Serverドキュメンテーション](http://msdn.microsoft.com/en-us/library/ms190303.aspx)を参照してください。 +デフォルト言語を指定します。有効なオプション: [SQL Serverドキュメント](http://msdn.microsoft.com/en-us/library/ms190303.aspx)を参照してください。 デフォルト値: 'us_english'。 @@ -651,7 +735,7 @@ SQL Server内のログオブジェクトの論理名を指定します。この ##### `login` -*必須指定です。* +*必須。* 管理対象のWindowsログインまたはSQLログインを指定します。有効なオプション: 既存のログインを含む文字列。 @@ -669,7 +753,7 @@ SQL Server内のログオブジェクトの論理名を指定します。この ##### `svrroles` -ログインに1つまたは複数の事前インストールされたサーバロールを割り当てます。有効なオプション: `permission => value`ペアのハッシュ値。ここで値0は無効、値1は有効であることを示します。たとえば、`{'diskadmin' => 1、'dbcreator' => 1、'sysadmin' => 0}`などです。有効なパーミッションの一覧については、[SQL Serverドキュメンテーション](http://msdn.microsoft.com/en-us/library/ms188659.aspx)を参照してください。 +ログインに1つまたは複数の事前インストールされたサーバロールを割り当てます。有効なオプション: `permission => value`ペアのハッシュ値。ここで値0は無効、値1は有効であることを示します。たとえば、`{'diskadmin' => 1、'dbcreator' => 1、'sysadmin' => 0}`などです。有効なパーミッションの一覧については、[SQL Serverドキュメント](http://msdn.microsoft.com/en-us/library/ms188659.aspx)を参照してください。 > **SQL Serverにおけるこれらの設定の詳細については、以下を参照してください。** > @@ -689,13 +773,13 @@ SQL Server内のログオブジェクトの論理名を指定します。この ##### `login` -*必須指定です。* +*必須。* 管理対象のSQLまたはWindowsログインを指定します。有効なオプション: 既存のログインを含む文字列。 ##### `permissions` -*必須指定です。* +*必須。* 管理対象の1つまたは複数のパーミッションを指定します。有効なオプション: 文字列または文字列の配列。各文字列には[SQL Serverパーミッション](https://technet.microsoft.com/en-us/library/ms191291%28v=sql.105%29.aspx) (例: 'SELECT'、'INSERT'、'UPDATE'、'DELETE'など)を含みます。 @@ -719,7 +803,7 @@ SQL Server内のログオブジェクトの論理名を指定します。この ##### `database` -*必須指定です。* +*必須。* ユーザを管理するデータベースを指定します。有効なオプション: 既存のデータベース名を含む文字列。 @@ -753,7 +837,7 @@ SQL Server内のログオブジェクトの論理名を指定します。この ##### `user` -*必須指定です。* +*必須。* 管理対象のユーザを指定します。有効なオプション: ユーザ名を含む文字列。 @@ -767,7 +851,7 @@ SQL Server内のログオブジェクトの論理名を指定します。この ##### `database` -*必須指定です。* +*必須。* ユーザのパーミッションを管理するデータベースを指定します。有効なオプション: 既存のデータベースの名前を含む文字列。 @@ -779,7 +863,7 @@ SQL Server内のログオブジェクトの論理名を指定します。この ##### `permissions` -*必須指定です。* +*必須。* 管理対象の1つまたは複数のパーミッションを指定します。有効なオプション: 1つまたは複数の文字列形式の[SQL Serverパーミッション](https://technet.microsoft.com/en-us/library/ms191291%28v=sql.105%29.aspx)を含む配列(例: `['SELECT', 'INSERT', 'UPDATE', 'DELETE']`)。 @@ -791,7 +875,7 @@ SQL Server内のログオブジェクトの論理名を指定します。この ##### `user` -*必須指定です。* +*必須。* パーミッションを管理するユーザを指定します。有効なオプション: ユーザ名を含む文字列。 @@ -852,7 +936,7 @@ SQL Server内のログオブジェクトの論理名を指定します。この ##### `permissions` -*必須指定です。* +*必須。* 1つまたは複数のパーミッションをそのロールに関連付けます。有効なオプション: 1つまたは複数のkey => valueペアのハッシュ値。ここで、各キーは望ましいパーミッション状態で、各値は管理対象のパーミッションを指定する文字列の配列です。 @@ -898,13 +982,13 @@ SQL Server内のログオブジェクトの論理名を指定します。この ##### `permissions` -*必須指定です。* +*必須。* 管理対象の1つ又は複数のパーミッションを指定します。有効なオプション: 1つまたは複数の[SQL Serverパーミッション](https://technet.microsoft.com/en-us/library/ms191291%28v=sql.105%29.aspx)(例: 'SELECT'、'INSERT'、'UPDATE'、'DELETE')を含む配列。 ##### `role` -*必須指定です。* +*必須。* パーミッションを管理するロールを指定します。有効なオプション: 既存のロール名を含む文字列。 @@ -958,7 +1042,7 @@ sys.configurationsで管理するオプションを指定します。有効な ##### `value` -*必須指定です。* +*必須。* 指定されたオプションの値を提供します。有効なオプション: 整数値。 @@ -975,7 +1059,7 @@ sys.configurationsで管理するオプションを指定します。有効な ### Microsoft SQL Serverの用語 -各種データベースシステムにより、用語が少し異なる場合があります。明確な定義については、以下のリストを参照してください。 +使用される用語はデータベースシステムごとに若干異なる場合があります。明確な定義については、以下のリストを参照してください。 * **データベース:** 関係性のあるデータテーブルとして整理された情報の集まりとデータオブジェクトの定義。 * **インスタンス:** インストールされ、実行されているデータベースサービス。 @@ -987,9 +1071,13 @@ sys.configurationsで管理するオプションを指定します。有効な 本モジュールは、Windows Server 2012または2012 R2のみで使用でき、Puppet Enterprise 3.7以降でのみ動作します。 +このモジュールは、指定ホスト上のSQL Serverの単独バージョンのみ管理できます(SQL Server 2012、2014、2016のうちいずれか1つのみ)。このモジュールでは同一バージョンの複数のSQL Serverインスタンスを管理できます。 + +このモジュールは、SQL Server Native Client SDK (別名SNAC_SDK)を管理できません。SQL ServerのインストールメディアはSDKをインストールできますが、SDKをアンインストールすることはできません。'sqlserver_features' factはSDKの存在を検出します。 + ## 開発 -本モジュールは、Puppet Enterprise (PE)用にPuppetによって設計されました。 +本モジュールは、PuppetによってPuppet Enterprise (PE)用に設計されました。 本モジュールで問題が発生した場合、またはリクエストしたい機能がある場合、[チケットを送信](https://tickets.puppet.com/browse/MODULES/)してください。 diff --git a/spec/README.md b/spec/README.md new file mode 100644 index 00000000..cc350543 --- /dev/null +++ b/spec/README.md @@ -0,0 +1,82 @@ +# sqlserver testing + +## Overview + +The sqlserver module is tested at the unit and acceptance level. Those tests are found in the spec/unit and spec/acceptance directory respectively. Acceptance level tests are run using either a master and agent setup or just an aget. This is accomplished via use of testmode-switcher technology. The module's tests also utilize test-tiers. + +## Running Module Tests +This section will instruct you on how to run the unit and acceptance tests. + +### General Setup Steps: +Option 1 - using Bundler: +1. Install Bundler +``` +gem install bundler +``` +2. Install dependent gems +``` +bundle install --path .bundle/gems +``` +3. To see what tasks are available run +``` +bundle exec rake -T +``` + +Option 2 - if not using Bundler, then install the dependent gems. + +Puppet's default is to use Bundler, as such the rest of this document will assume use thereof. + + +### Unit Tests +To run the unit tests simply type +``` +bundle exec rake spec +``` + +### Acceptance Tests + +#### System Under Test +The Acceptance tets run on either a master and agent system or just on a stand-alone agent machine depending on what test mode you have delcared. With BEAKER_TESTMODE=agent the tests run using a master and an agent. With BEAKER_TESTMODE=apply the tests will run on only the agent. + +#### Generate Hosts File +First use beaker-hostgenerator to set up a hosts.yml to feed to the acceptance tests by running: + +``` +bundle exec beaker-hostgenerator windows2012r2-64sql_host%2Cdefault.a%7Bsql_version=2012%7D-redhat7-64mdca --hypervisor abs > spec/acceptance/nodesets/hosts.yml +``` +This command assumes you're running it from the root of the sqlserver module repo. + +#### Environment Variables +Several environment variables should be set in order to as closely mimic Puppet's Jenkins continuous test system as possible. + +``` +export ABS_RESOURCE_HOSTS="[{\"hostname\":\"\",\"type\":\"win-2012r2-x86_64\",\"engine\":\"vmpooler\"},{\"hostname\":\"\",\"type\":\"redhat-7-x86_64\",\"engine\":\"vmpooler\"}]" +``` + +e.g. replace "\" with "fyzskxlt6edanll.delivery.puppetlabs.net\" + +``` +export ABS_RESOURCE_REQUESTS_beaker="[{\"windows2012r2-64sql_host.a%7Bsql_version=2012%7D-redhat7-64mdca\":{\"win-2012r2-x86_64\":1,\"redhat-7-x86_64\":1}, \"windows2012r2-64sql_host.a%7Bsql_version=2014%7D-redhat7-64mdca\":{\"win-2012r2-x86_64\":1,\"redhat-7-x86_64\":1}}]" + +export BEAKER_setfile=spec/acceptance/nodesets/hosts.yml +export BEAKER_keyfile=/var/lib/jenkins/.ssh/id_rsa-acceptance +export BEAKER_destroy=always +export BEAKER_debug=true +export BEAKER_PE_DIR=http://enterprise.delivery.puppetlabs.net/2017.1/ci-ready +export BEAKER_PE_VER=2017.1.1 +export PUPPET_INSTALL_TYPE=pe +export INSTALLATION_TYPE=pe +export BEAKER_TESTMODE=agent +export BUNDLE_PATH=.bundle/gems +export BUNDLE_BIN=.bundle/bin +export TEST_FRAMEWORK=beaker-rspec +export TEST_TIERS=low +``` +Where "BEAKER_PE_DIR" should be set to whatever version of PE you're testing against. + +#### Test Tiers +This module uses test-tiering a technology that allows only subsets of acceptance tests to run depending on how much risk they have been evaluated to carry among other factors such as run-time etc. The "TEST_TIERS" environment variable specifies what set of the acceptance tests to run. Since at the time of writing (7/16/17) all sqlserver module tests are tiered at the "low" level, the only way to run the acceptance tests is to specify "TEST_TIERS=low". Alternatively, if the environment variable TEST_TIERS is absent the module tests will default to runnin all the tests. + +#### Executing the Acceptance Tests + +bundle exec rake beaker:hosts --verbose \ No newline at end of file diff --git a/spec/acceptance/sqlserver_config_spec.rb b/spec/acceptance/sqlserver_config_spec.rb index 68560a78..c9a2135e 100644 --- a/spec/acceptance/sqlserver_config_spec.rb +++ b/spec/acceptance/sqlserver_config_spec.rb @@ -5,14 +5,14 @@ host = find_only_one("sql_host") # Get instance name -inst_name = ("MSSQL" + SecureRandom.hex(4)).upcase +inst_name = ('MSSQL' + SecureRandom.hex(4)).upcase # Get database name -db_name = ("DB" + SecureRandom.hex(4)).upcase +db_name = ('DB' + SecureRandom.hex(4)).upcase describe "sqlserver::config test", :node => host do - def ensure_sqlserver_instance(host,inst_name, ensure_val = 'present') + def ensure_sqlserver_instance(inst_name, ensure_val = 'present') create_new_instance= <<-MANIFEST sqlserver_instance{'#{inst_name}': ensure => '#{ensure_val}', @@ -25,31 +25,31 @@ def ensure_sqlserver_instance(host,inst_name, ensure_val = 'present') } MANIFEST - apply_manifest_on(host, create_new_instance) do |r| + execute_manifest(create_new_instance) do |r| expect(r.stderr).not_to match(/Error/i) end end - context "Testing sqlserver::config", {:testrail => ['89070', '89071', '89072', '89073']} do + context 'Testing sqlserver::config', {:testrail => ['89070', '89071', '89072', '89073']} do before(:all) do # Create new instance - ensure_sqlserver_instance(host, inst_name) + ensure_sqlserver_instance(inst_name) # get credentials for new config - @admin_user = "admin" + SecureRandom.hex(2) + @admin_user = 'admin' + SecureRandom.hex(2) @admin_pass = 'Pupp3t1@' # get database user - @db_user = "dbuser" + SecureRandom.hex(2) + @db_user = 'dbuser' + SecureRandom.hex(2) end after(:all) do # remove the newly created instance - ensure_sqlserver_instance(host, 'absent') + ensure_sqlserver_instance('absent') end - it "Create New Admin Login:" do + it 'Create New Admin Login:', :tier_low => true do create_new_login = <<-MANIFEST sqlserver::config{'#{inst_name}': instance_name => '#{inst_name}', @@ -64,12 +64,12 @@ def ensure_sqlserver_instance(host,inst_name, ensure_val = 'present') svrroles => {'sysadmin' => 1}, } MANIFEST - apply_manifest_on(host, create_new_login) do |r| + execute_manifest(create_new_login) do |r| expect(r.stderr).not_to match(/Error/i) end end - it "Validate New Config WITH using instance_name in sqlserver::config" do + it 'Validate New Config WITH using instance_name in sqlserver::config', :tier_low => true do pp = <<-MANIFEST sqlserver::config{'#{inst_name}': admin_user => '#{@admin_user}', @@ -80,12 +80,12 @@ def ensure_sqlserver_instance(host,inst_name, ensure_val = 'present') instance => '#{inst_name}', } MANIFEST - apply_manifest_on(host, pp) do |r| + execute_manifest(pp) do |r| expect(r.stderr).not_to match(/Error/i) end end - it "Validate new login and database actualy created" do + it 'Validate new login and database actualy created', :tier_low => true do hostname = host.hostname query = "USE #{db_name}; SELECT * from master..sysdatabases WHERE name = '#{db_name}'" @@ -93,7 +93,7 @@ def ensure_sqlserver_instance(host,inst_name, ensure_val = 'present') :sql_admin_user => @admin_user, :sql_admin_pass => @admin_pass, :expected_row_count => 1}) end - it "Validate New Config WITHOUT using instance_name in sqlserver::config" do + it 'Validate New Config WITHOUT using instance_name in sqlserver::config', :tier_low => true do pp = <<-MANIFEST sqlserver::config{'#{inst_name}': admin_user => '#{@admin_user}', @@ -103,7 +103,7 @@ def ensure_sqlserver_instance(host,inst_name, ensure_val = 'present') instance => '#{inst_name}', } MANIFEST - apply_manifest_on(host, pp) do |r| + execute_manifest(pp) do |r| expect(r.stderr).not_to match(/Error/i) end end diff --git a/spec/acceptance/sqlserver_database_spec.rb b/spec/acceptance/sqlserver_database_spec.rb index ae23df6d..b3c52eeb 100644 --- a/spec/acceptance/sqlserver_database_spec.rb +++ b/spec/acceptance/sqlserver_database_spec.rb @@ -5,8 +5,8 @@ describe "Test sqlserver::database", :node => host do - def ensure_sqlserver_database(host, pp) - apply_manifest_on(host, pp) do |r| + def ensure_sqlserver_database(pp) + execute_manifest(pp) do |r| expect(r.stderr).not_to match(/Error/i) end end @@ -39,10 +39,10 @@ def run_sql_query_opts (query, expected_row_count) ensure => 'absent', } MANIFEST - ensure_sqlserver_database(host, pp) + ensure_sqlserver_database(pp) end - it "Test Case C89019: Create a database" do + it "Test Case C89019: Create a database", :tier_low => true do pp = <<-MANIFEST sqlserver::config{'MSSQLSERVER': admin_user => 'sa', @@ -57,14 +57,14 @@ def run_sql_query_opts (query, expected_row_count) require => Sqlserver::Database['#{@db_name}'], } MANIFEST - ensure_sqlserver_database(host, pp) + ensure_sqlserver_database(pp) puts "Validate the Database '#{@db_name}' and table '#{@table_name}' are successfully created:" query = "USE #{@db_name}; SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE table_name = '#{@table_name}';" run_sql_query(host, run_sql_query_opts(query, 1)) end - it "Delete a database" do + it "Delete a database", :tier_low => true do pp = <<-MANIFEST sqlserver::config{'MSSQLSERVER': admin_user => 'sa', @@ -73,7 +73,7 @@ def run_sql_query_opts (query, expected_row_count) sqlserver::database{'#{@db_name}': } MANIFEST - ensure_sqlserver_database(host, pp) + ensure_sqlserver_database(pp) puts "Validate the Database '#{@db_name}' exists" query = "SELECT database_id from sys.databases WHERE name = '#{@db_name}';" @@ -88,14 +88,14 @@ def run_sql_query_opts (query, expected_row_count) ensure => absent, } MANIFEST - ensure_sqlserver_database(host, pp) + ensure_sqlserver_database(pp) puts "Validate the Database '#{@db_name}' does not exist" query = "SELECT database_id from sys.databases WHERE name = '#{@db_name}';" run_sql_query(host, run_sql_query_opts(query, 0)) end - it "Test Case C89076: Create database with optional collation_name" do + it "Test Case C89076: Create database with optional collation_name", :tier_low => true do pp = <<-MANIFEST sqlserver::config{'MSSQLSERVER': admin_user => 'sa', @@ -111,7 +111,7 @@ def run_sql_query_opts (query, expected_row_count) require => Sqlserver::Database['#{@db_name}'], } MANIFEST - ensure_sqlserver_database(host, pp) + ensure_sqlserver_database(pp) puts "Validate that a table can be created in the database:" query = "USE #{@db_name}; SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE table_name = '#{@table_name}';" @@ -126,7 +126,7 @@ def run_sql_query_opts (query, expected_row_count) run_sql_query(host, run_sql_query_opts(query, 1)) end - it "Test Case C89077: Create database with optional compatibility" do + it "Test Case C89077: Create database with optional compatibility", :tier_low => true do pp = <<-MANIFEST sqlserver::config{'MSSQLSERVER': admin_user => 'sa', @@ -142,7 +142,7 @@ def run_sql_query_opts (query, expected_row_count) require => Sqlserver::Database['#{@db_name}'], } MANIFEST - ensure_sqlserver_database(host, pp) + ensure_sqlserver_database(pp) puts "Validate that a table can be created in the database:" query = "USE #{@db_name}; SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE table_name = '#{@table_name}';" @@ -157,7 +157,7 @@ def run_sql_query_opts (query, expected_row_count) run_sql_query(host, run_sql_query_opts(query, 1)) end - it "Test Case C89078: Create database with optional containment" do + it "Test Case C89078: Create database with optional containment", :tier_low => true do pp = <<-MANIFEST sqlserver::config{'MSSQLSERVER': admin_user => 'sa', @@ -180,7 +180,7 @@ def run_sql_query_opts (query, expected_row_count) require => Sqlserver::Database['#{@db_name}'], } MANIFEST - ensure_sqlserver_database(host, pp) + ensure_sqlserver_database(pp) puts "Validate that a table can be created in the database:" query = "USE #{@db_name}; SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE table_name = '#{@table_name}';" @@ -195,7 +195,7 @@ def run_sql_query_opts (query, expected_row_count) run_sql_query(host, run_sql_query_opts(query, 1)) end - it "Test Case C89079: Create database with optional db_chaining" do + it "Test Case C89079: Create database with optional db_chaining", :tier_low => true do pp = <<-MANIFEST sqlserver::config{'MSSQLSERVER': admin_user => 'sa', @@ -219,7 +219,7 @@ def run_sql_query_opts (query, expected_row_count) require => Sqlserver::Database['#{@db_name}'], } MANIFEST - ensure_sqlserver_database(host, pp) + ensure_sqlserver_database(pp) puts "Validate that a table can be created in the database:" query = "USE #{@db_name}; SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE table_name = '#{@table_name}';" @@ -234,7 +234,7 @@ def run_sql_query_opts (query, expected_row_count) run_sql_query(host, run_sql_query_opts(query, 1)) end - it "Test Case C89080: Create database with optional default_fulltext_language" do + it "Test Case C89080: Create database with optional default_fulltext_language", :tier_low => true do pp = <<-MANIFEST sqlserver::config{'MSSQLSERVER': admin_user => 'sa', @@ -258,7 +258,7 @@ def run_sql_query_opts (query, expected_row_count) require => Sqlserver::Database['#{@db_name}'], } MANIFEST - ensure_sqlserver_database(host, pp) + ensure_sqlserver_database(pp) puts "Validate that a table can be created in the database:" query = "USE #{@db_name}; SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE table_name = '#{@table_name}';" @@ -273,7 +273,7 @@ def run_sql_query_opts (query, expected_row_count) run_sql_query(host, run_sql_query_opts(query, 1)) end - it "Test Case C89081: Create database with optional default_language" do + it "Test Case C89081: Create database with optional default_language", :tier_low => true do pp = <<-MANIFEST sqlserver::config{'MSSQLSERVER': admin_user => 'sa', @@ -297,7 +297,7 @@ def run_sql_query_opts (query, expected_row_count) require => Sqlserver::Database['#{@db_name}'], } MANIFEST - ensure_sqlserver_database(host, pp) + ensure_sqlserver_database(pp) puts "Validate that a table can be created in the database:" query = "USE #{@db_name}; SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE table_name = '#{@table_name}';" diff --git a/spec/acceptance/sqlserver_instance_spec.rb b/spec/acceptance/sqlserver_instance_spec.rb index db3ec01b..36e61958 100644 --- a/spec/acceptance/sqlserver_instance_spec.rb +++ b/spec/acceptance/sqlserver_instance_spec.rb @@ -11,7 +11,7 @@ def new_random_instance_name describe "sqlserver_instance", :node => host do version = host['sql_version'].to_s - def ensure_sqlserver_instance(host, features, inst_name, ensure_val = 'present', sysadmin_accounts = "['Administrator']") + def ensure_sqlserver_instance(features, inst_name, ensure_val = 'present', sysadmin_accounts = "['Administrator']") manifest = <<-MANIFEST sqlserver_instance{'#{inst_name}': name => '#{inst_name}', @@ -31,7 +31,7 @@ def ensure_sqlserver_instance(host, features, inst_name, ensure_val = 'present', pp = ERB.new(manifest).result(binding) - apply_manifest_on(host, pp) do |r| + execute_manifest(pp) do |r| expect(r.stderr).not_to match(/Error/i) end end @@ -68,7 +68,7 @@ def sql_query_is_user_sysadmin(username) password => 'Puppet01!', } MANIFEST - apply_manifest_on(host,pp) + execute_manifest(pp) end after(:context) do @@ -77,30 +77,30 @@ def sql_query_is_user_sysadmin(username) ensure => absent, } MANIFEST - apply_manifest_on(host,pp) + execute_manifest(pp) end inst_name = new_random_instance_name - features = ['SQL', 'SQLEngine', 'Replication', 'FullText', 'DQ'] + features = ['SQLEngine', 'Replication', 'FullText', 'DQ'] - it "create #{inst_name} instance" do - ensure_sqlserver_instance(host, features, inst_name,'present',"['Administrator','ExtraSQLAdmin']") + it "create #{inst_name} instance", :tier_low => true do + ensure_sqlserver_instance(features, inst_name,'present',"['Administrator','ExtraSQLAdmin']") validate_sql_install(host, {:version => version}) do |r| expect(r.stdout).to match(/#{Regexp.new(inst_name)}/) end end - it "#{inst_name} instance has Administrator as a sysadmin" do + it "#{inst_name} instance has Administrator as a sysadmin", :tier_low => true do run_sql_query(host, run_sql_query_opts(inst_name, sql_query_is_user_sysadmin('Administrator'), expected_row_count = 1)) end - it "#{inst_name} instance has ExtraSQLAdmin as a sysadmin" do + it "#{inst_name} instance has ExtraSQLAdmin as a sysadmin", :tier_low => true do run_sql_query(host, run_sql_query_opts(inst_name, sql_query_is_user_sysadmin('ExtraSQLAdmin'), expected_row_count = 1)) end - it "remove #{inst_name} instance" do - ensure_sqlserver_instance(host, features, inst_name, 'absent') + it "remove #{inst_name} instance", :tier_low => true do + ensure_sqlserver_instance(features, inst_name, 'absent') # Ensure all features for this instance are removed and the defaults are left alone validate_sql_install(host, {:version => version}) do |r| @@ -114,35 +114,16 @@ def sql_query_is_user_sysadmin(username) end end - context "Feature has only one 'SQL'", {:testrail => ['89032']} do - inst_name = new_random_instance_name - features = ['SQL'] - - after(:all) do - ensure_sqlserver_instance(host, features, inst_name, 'absent') - end - - it "create #{inst_name} instance with only one SQL feature" do - ensure_sqlserver_instance(host, features, inst_name) - - validate_sql_install(host, {:version => version}) do |r| - expect(r.stdout).to match(/#{inst_name}\s+Database Engine Services/) - expect(r.stdout).to match(/#{inst_name}\s+SQL Server Replication/) - expect(r.stdout).to match(/#{inst_name}\s+Data Quality Services/) - end - end - end - context "Feature has only one 'RS'", {:testrail => ['89034']} do - inst_name = new_random_instance_name - features = ['RS'] + inst_name = new_random_instance_name + features = ['RS'] after(:all) do - ensure_sqlserver_instance(host, features, inst_name, 'absent') + ensure_sqlserver_instance(features, inst_name, 'absent') end - it "create #{inst_name} instance with only one RS feature" do - ensure_sqlserver_instance(host, features, inst_name) + it "create #{inst_name} instance with only one RS feature", :tier_low => true do + ensure_sqlserver_instance(features, inst_name) validate_sql_install(host, {:version => version}) do |r| expect(r.stdout).to match(/#{inst_name}\s+Reporting Services/) @@ -155,13 +136,13 @@ def sql_query_is_user_sysadmin(username) features = ['AS'] after(:all) do - ensure_sqlserver_instance(host, features, inst_name, 'absent') + ensure_sqlserver_instance(features, inst_name, 'absent') end #skip below test due to ticket MODULES-2379, when the ticket was resolved # will change xit to it xit "create #{inst_name} instance with only one AS feature" do - ensure_sqlserver_instance(host, features, inst_name) + ensure_sqlserver_instance(features, inst_name) validate_sql_install(host, {:version => version}) do |r| expect(r.stdout).to match(/#{Regexp.new(inst_name)}/) diff --git a/spec/acceptance/sqlserver_login_spec.rb b/spec/acceptance/sqlserver_login_spec.rb index 4af7e4d5..b15efd31 100644 --- a/spec/acceptance/sqlserver_login_spec.rb +++ b/spec/acceptance/sqlserver_login_spec.rb @@ -8,8 +8,8 @@ # Covers testrail => ['89118', '89119', '89120', '89121', '89122', '89123', '89124', '89125', '89540'] describe "Test sqlserver::login", :node => host do - def ensure_manifest_apply(host, pp) - apply_manifest_on(host, pp) do |r| + def ensure_manifest_execute(pp) + execute_manifest(pp) do |r| expect(r.stderr).not_to match(/Error/i) end end @@ -46,7 +46,7 @@ def remove_test_logins(host) ensure => 'absent', } MANIFEST - ensure_manifest_apply(host, pp) + ensure_manifest_execute(pp) end def create_login_manifest (testcase,login_name,login_password,options = {}) @@ -114,7 +114,7 @@ def create_login_manifest (testcase,login_name,login_password,options = {}) ensure => 'present', } MANIFEST - ensure_manifest_apply(host, pp) + ensure_manifest_execute(pp) end # Delete all test fixtures @@ -137,7 +137,7 @@ def create_login_manifest (testcase,login_name,login_password,options = {}) ensure => 'absent', } MANIFEST - ensure_manifest_apply(host, pp) + ensure_manifest_execute(pp) end ['SQL_LOGIN user', 'WINDOWS_LOGIN user', 'WINDOWS_LOGIN group'].each do |testcase| @@ -161,27 +161,27 @@ def create_login_manifest (testcase,login_name,login_password,options = {}) describe "Create deafult #{testcase} login" do before(:all) { remove_test_logins(host) } - it "can create a default #{testcase}" do + it "can create a default #{testcase}", :tier_low => true do pp = create_login_manifest(testcase,@login_under_test,@login_passwd) - ensure_manifest_apply(host, pp) + ensure_manifest_execute(pp) end - it "should exist in the principals table" do + it "should exist in the principals table", :tier_low => true do query = "SELECT principal_id FROM SYS.server_principals WHERE name = '#{@login_under_test}' AND [type] = '#{@sql_principal_type}'"; run_sql_query(host, run_sql_query_opts_as_sa(query, expected_row_count = 1)) end if testcase == 'SQL_LOGIN user' - it "can login to SQL Server" do + it "can login to SQL Server", :tier_low => true do puts "Validate the login '#{@login_under_test}' is successfully created and able to access database '#{db_name}':" query = "USE #{db_name}; SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE table_name = '#{table_name}';" run_sql_query(host, run_sql_query_opts(@login_under_test, @login_passwd, query, expected_row_count = 1)) end end - it "is idempotent" do + it "is idempotent", :tier_low => true do pp = create_login_manifest(testcase,@login_under_test,@login_passwd) - ensure_manifest_apply(host, pp) + ensure_manifest_execute(pp) end end @@ -189,18 +189,18 @@ def create_login_manifest (testcase,login_name,login_password,options = {}) # check_expiration and check_policy are only applicable to SQL based logins before(:all) { remove_test_logins(host) } - it "can create an #{testcase} with 'check_expiration','check_policy' set" do + it "can create an #{testcase} with 'check_expiration','check_policy' set", :tier_low => true do options = { 'check_expiration' => true, 'check_policy' => true } pp = create_login_manifest(testcase,@login_under_test,@login_passwd,options) - ensure_manifest_apply(host, pp) + ensure_manifest_execute(pp) end - it "should have is_expiration_checked set" do + it "should have is_expiration_checked set", :tier_low => true do query = "SELECT name as LOGIN_NAME, is_expiration_checked FROM SYS.SQL_LOGINS WHERE is_expiration_checked = '1' AND name = '#{@login_under_test}';" run_sql_query(host, run_sql_query_opts_as_sa(query, expected_row_count = 1)) end - it "should have is_policy_checked set" do + it "should have is_policy_checked set", :tier_low => true do query = "SELECT name as LOGIN_NAME, is_policy_checked FROM SYS.SQL_LOGINS WHERE is_policy_checked = '1' AND name = '#{@login_under_test}';" run_sql_query(host, run_sql_query_opts_as_sa(query, expected_row_count = 1)) end @@ -209,23 +209,23 @@ def create_login_manifest (testcase,login_name,login_password,options = {}) describe "Create #{testcase} login with 'default_database','default_language'" do before(:all) { remove_test_logins(host) } - it "can create a #{testcase} with 'default_database','default_language'" do + it "can create a #{testcase} with 'default_database','default_language'", :tier_low => true do options = { 'default_database' => "#{db_name}", 'default_language' => 'Spanish' } pp = create_login_manifest(testcase,@login_under_test,@login_passwd,options) - ensure_manifest_apply(host, pp) + ensure_manifest_execute(pp) end - it "should exist in the principals table" do + it "should exist in the principals table", :tier_low => true do query = "SELECT principal_id FROM SYS.server_principals WHERE name = '#{@login_under_test}' AND [type] = '#{@sql_principal_type}'"; run_sql_query(host, run_sql_query_opts_as_sa(query, expected_row_count = 1)) end - it "should have the specified default database" do + it "should have the specified default database", :tier_low => true do query = "SELECT principal_id FROM SYS.server_principals WHERE name = '#{@login_under_test}' AND [type] = '#{@sql_principal_type}' AND default_database_name = '#{db_name}'"; run_sql_query(host, run_sql_query_opts_as_sa(query, expected_row_count = 1)) end - it "should have the specified default langauge" do + it "should have the specified default langauge", :tier_low => true do query = "SELECT principal_id FROM SYS.server_principals WHERE name = '#{@login_under_test}' AND [type] = '#{@sql_principal_type}' AND default_language_name = 'Spanish'"; run_sql_query(host, run_sql_query_opts_as_sa(query, expected_row_count = 1)) end @@ -234,14 +234,14 @@ def create_login_manifest (testcase,login_name,login_password,options = {}) describe "Create #{testcase} login with 'disabled'" do before(:all) { remove_test_logins(host) } - it "can create #{testcase} with optional 'disabled'" do + it "can create #{testcase} with optional 'disabled'", :tier_low => true do options = { 'disabled' => true } pp = create_login_manifest(testcase,@login_under_test,@login_passwd,options) - ensure_manifest_apply(host, pp) + ensure_manifest_execute(pp) end if testcase == 'WINDOWS_LOGIN group' - it "should have DENY CONNECT SQL set" do + it "should have DENY CONNECT SQL set", :tier_low => true do query = "SELECT sp.[state] FROM sys.server_principals p INNER JOIN sys.server_permissions sp ON p.principal_id = sp.grantee_principal_id WHERE sp.permission_name = 'CONNECT SQL' AND sp.class = 100 AND sp.state = 'D' AND p.name = '#{@login_under_test}' AND p.[type] = '#{@sql_principal_type}'" @@ -258,36 +258,36 @@ def create_login_manifest (testcase,login_name,login_password,options = {}) describe "Modify a #{testcase} login" do before(:all) { remove_test_logins(host) } - it "should create an initial #{testcase}" do + it "should create an initial #{testcase}", :tier_low => true do options = { 'svrroles' => '{\'sysadmin\' => 1}' } pp = create_login_manifest(testcase,@login_under_test,@login_passwd,options) - ensure_manifest_apply(host, pp) + ensure_manifest_execute(pp) end - it "should exist in the principals table on creation" do + it "should exist in the principals table on creation", :tier_low => true do query = "SELECT principal_id FROM SYS.server_principals WHERE name = '#{@login_under_test}' AND [type] = '#{@sql_principal_type}'"; run_sql_query(host, run_sql_query_opts_as_sa(query, expected_row_count = 1)) end - it "should modify a #{testcase} login" do + it "should modify a #{testcase} login", :tier_low => true do options = { 'disabled' => true, 'default_database' => "#{db_name}", 'default_language' => 'Spanish', 'check_expiration' => true, 'check_policy' => true, 'svrroles' => '{\'sysadmin\' => 1, \'serveradmin\' => 1}' } pp = create_login_manifest(testcase,@login_under_test,@login_passwd,options) - ensure_manifest_apply(host, pp) + ensure_manifest_execute(pp) end if testcase == 'SQL_LOGIN user' - it "should have is_expiration_checked set" do + it "should have is_expiration_checked set", :tier_low => true do query = "SELECT name as LOGIN_NAME, is_expiration_checked FROM SYS.SQL_LOGINS WHERE is_expiration_checked = '1' AND name = '#{@login_under_test}';" run_sql_query(host, run_sql_query_opts_as_sa(query, expected_row_count = 1)) end - it "should have is_policy_checked set" do + it "should have is_policy_checked set", :tier_low => true do query = "SELECT name as LOGIN_NAME, is_policy_checked FROM SYS.SQL_LOGINS WHERE is_policy_checked = '1' AND name = '#{@login_under_test}';" run_sql_query(host, run_sql_query_opts_as_sa(query, expected_row_count = 1)) end end - it "should have the specified sysadmin role" do + it "should have the specified sysadmin role", :tier_low => true do # Note - IS_SRVROLEMEMBER always returns false for a disabled WINDOWS_LOGIN user login query = "SELECT pri.name from sys.server_role_members member JOIN sys.server_principals rol ON member.role_principal_id = rol.principal_id @@ -297,7 +297,7 @@ def create_login_manifest (testcase,login_name,login_password,options = {}) AND pri.name = '#{@login_under_test}'" run_sql_query(host, run_sql_query_opts_as_sa(query, expected_row_count = 1)) end - it "should have the specified serveradmin role" do + it "should have the specified serveradmin role", :tier_low => true do # Note - IS_SRVROLEMEMBER always returns false for a disabled WINDOWS_LOGIN user login query = "SELECT pri.name from sys.server_role_members member JOIN sys.server_principals rol ON member.role_principal_id = rol.principal_id @@ -308,25 +308,25 @@ def create_login_manifest (testcase,login_name,login_password,options = {}) run_sql_query(host, run_sql_query_opts_as_sa(query, expected_row_count = 1)) end - it "should have the specified default database" do + it "should have the specified default database", :tier_low => true do query = "SELECT principal_id FROM SYS.server_principals WHERE name = '#{@login_under_test}' AND [type] = '#{@sql_principal_type}' AND default_database_name = '#{db_name}'"; run_sql_query(host, run_sql_query_opts_as_sa(query, expected_row_count = 1)) end - it "should have the specified default langauge" do + it "should have the specified default langauge", :tier_low => true do query = "SELECT principal_id FROM SYS.server_principals WHERE name = '#{@login_under_test}' AND [type] = '#{@sql_principal_type}' AND default_language_name = 'Spanish'"; run_sql_query(host, run_sql_query_opts_as_sa(query, expected_row_count = 1)) end if testcase == 'WINDOWS_LOGIN group' - it "should have DENY CONNECT SQL set" do + it "should have DENY CONNECT SQL set", :tier_low => true do query = "SELECT sp.[state] FROM sys.server_principals p INNER JOIN sys.server_permissions sp ON p.principal_id = sp.grantee_principal_id WHERE sp.permission_name = 'CONNECT SQL' AND sp.class = 100 AND sp.state = 'D' AND p.name = '#{@login_under_test}' AND p.[type] = '#{@sql_principal_type}'" run_sql_query(host, run_sql_query_opts_as_sa(query, expected_row_count = 1)) end else - it "should have is_disabled set" do + it "should have is_disabled set", :tier_low => true do query = "SELECT principal_id FROM SYS.server_principals WHERE name = '#{@login_under_test}' AND [type] = '#{@sql_principal_type}' AND is_disabled = '1';" run_sql_query(host, run_sql_query_opts_as_sa(query, expected_row_count = 1)) end @@ -336,23 +336,23 @@ def create_login_manifest (testcase,login_name,login_password,options = {}) describe "Delete #{testcase} login" do before(:all) { remove_test_logins(host) } - it "should create an initial #{testcase}" do + it "should create an initial #{testcase}", :tier_low => true do pp = create_login_manifest(testcase,@login_under_test,@login_passwd) - ensure_manifest_apply(host, pp) + ensure_manifest_execute(pp) end - it "should exist in the principals table on creation" do + it "should exist in the principals table on creation", :tier_low => true do query = "SELECT principal_id FROM SYS.server_principals WHERE name = '#{@login_under_test}' AND [type] = '#{@sql_principal_type}'"; run_sql_query(host, run_sql_query_opts_as_sa(query, expected_row_count = 1)) end - it "should remove a #{testcase} on ensure => absent" do + it "should remove a #{testcase} on ensure => absent", :tier_low => true do options = { 'ensure' => 'absent' } pp = create_login_manifest(testcase,@login_under_test,@login_passwd,options) - ensure_manifest_apply(host, pp) + ensure_manifest_execute(pp) end - it "should not exist in the principals table after deletion" do + it "should not exist in the principals table after deletion", :tier_low => true do query = "SELECT principal_id FROM SYS.server_principals WHERE name = '#{@login_under_test}' AND [type] = '#{@sql_principal_type}'"; run_sql_query(host, run_sql_query_opts_as_sa(query, expected_row_count = 0)) end diff --git a/spec/acceptance/sqlserver_role_spec.rb b/spec/acceptance/sqlserver_role_spec.rb index 69275a40..64996f57 100644 --- a/spec/acceptance/sqlserver_role_spec.rb +++ b/spec/acceptance/sqlserver_role_spec.rb @@ -14,7 +14,7 @@ describe "Test sqlserver::role", :node => host do - def ensure_sqlserver_logins_users(host, db_name) + def ensure_sqlserver_logins_users(db_name) pp = <<-MANIFEST sqlserver::config{'MSSQLSERVER': admin_user => 'sa', @@ -42,7 +42,7 @@ def ensure_sqlserver_logins_users(host, db_name) require => Sqlserver::Login['#{LOGIN1}'], } MANIFEST - apply_manifest_on(host, pp) do |r| + execute_manifest(pp, opts = {}) do |r| expect(r.stderr).not_to match(/Error/i) end end @@ -50,7 +50,7 @@ def ensure_sqlserver_logins_users(host, db_name) context "Start testing sqlserver::role", {:testrail => ['89161', '89162', '89163', '89164', '89165']} do before(:all) do # Create database users - ensure_sqlserver_logins_users(host, db_name) + ensure_sqlserver_logins_users(db_name) end before(:each) do @role = "Role_" + SecureRandom.hex(2) @@ -65,7 +65,7 @@ def ensure_sqlserver_logins_users(host, db_name) ensure => 'absent', } MANIFEST - apply_manifest_on(host, pp) do |r| + execute_manifest(pp, opts = {}) do |r| expect(r.stderr).not_to match(/Error/i) end end @@ -82,12 +82,12 @@ def ensure_sqlserver_logins_users(host, db_name) ensure => 'absent', } MANIFEST - apply_manifest_on(host, pp) do |r| + execute_manifest(pp, opts = {}) do |r| expect(r.stderr).not_to match(/Error/i) end end - it "Create server role #{@role} with optional authorization" do + it "Create server role #{@role} with optional authorization", :tier_low => true do pp = <<-MANIFEST sqlserver::config{'MSSQLSERVER': admin_user => 'sa', @@ -101,7 +101,7 @@ def ensure_sqlserver_logins_users(host, db_name) type => 'SERVER', } MANIFEST - apply_manifest_on(host, pp) do |r| + execute_manifest(pp, opts = {}) do |r| expect(r.stderr).not_to match(/Error/i) end @@ -127,7 +127,7 @@ def ensure_sqlserver_logins_users(host, db_name) run_sql_query(host, { :query => query, :server => hostname, :expected_row_count => 1 }) end - it "Create database-specific role #{@role}" do + it "Create database-specific role #{@role}", :tier_low => true do pp = <<-MANIFEST sqlserver::config{'MSSQLSERVER': admin_user => 'sa', @@ -141,7 +141,7 @@ def ensure_sqlserver_logins_users(host, db_name) type => 'DATABASE', } MANIFEST - apply_manifest_on(host, pp) do |r| + execute_manifest(pp, opts = {}) do |r| expect(r.stderr).not_to match(/Error/i) end @@ -157,8 +157,7 @@ def ensure_sqlserver_logins_users(host, db_name) run_sql_query(host, { :query => query, :server => hostname, :expected_row_count => 6 }) end - # temporarily skip this test because of ticket MODULES-2543 - xit "Create server role #{@role} with optional members and optional members-purge" do + it "Create server role #{@role} with optional members and optional members-purge", :tier_low => true do pp = <<-MANIFEST sqlserver::config{'MSSQLSERVER': admin_user => 'sa', @@ -173,7 +172,7 @@ def ensure_sqlserver_logins_users(host, db_name) members => ['#{LOGIN1}', '#{LOGIN2}', '#{LOGIN3}'], } MANIFEST - apply_manifest_on(host, pp) do |r| + execute_manifest(pp, opts = {}) do |r| expect(r.stderr).not_to match(/Error/i) end @@ -217,7 +216,7 @@ def ensure_sqlserver_logins_users(host, db_name) members_purge => true, } MANIFEST - apply_manifest_on(host, pp) do |r| + execute_manifest(pp, opts = {}) do |r| expect(r.stderr).not_to match(/Error/i) end diff --git a/spec/acceptance/sqlserver_tsql_spec.rb b/spec/acceptance/sqlserver_tsql_spec.rb index 62997679..b78780b0 100644 --- a/spec/acceptance/sqlserver_tsql_spec.rb +++ b/spec/acceptance/sqlserver_tsql_spec.rb @@ -12,7 +12,7 @@ describe "sqlserver_tsql test", :node => host do - def ensure_sqlserver_database(host,db_name, ensure_val = 'present') + def ensure_sqlserver_database(db_name, ensure_val = 'present') pp = <<-MANIFEST sqlserver::config{'MSSQLSERVER': admin_user => 'sa', @@ -23,7 +23,7 @@ def ensure_sqlserver_database(host,db_name, ensure_val = 'present') } MANIFEST - apply_manifest_on(host, pp) do |r| + execute_manifest(pp) do |r| expect(r.stderr).not_to match(/Error/i) end end @@ -35,15 +35,15 @@ def ensure_sqlserver_database(host,db_name, ensure_val = 'present') @table_name = 'Tables_' + SecureRandom.hex(3) @query = "USE #{db_name}; SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE table_name = '#{@table_name}';" - ensure_sqlserver_database(host, db_name) + ensure_sqlserver_database(db_name) end after(:all) do # remove the newly created instance - ensure_sqlserver_database(host, 'absent') + ensure_sqlserver_database('absent') end - it "Run a simple tsql command via sqlserver_tsql:" do + it "Run a simple tsql command via sqlserver_tsql:", :tier_low => true do pp = <<-MANIFEST sqlserver::config{'MSSQLSERVER': instance_name => 'MSSQLSERVER', @@ -55,7 +55,7 @@ def ensure_sqlserver_database(host,db_name, ensure_val = 'present') command => "CREATE TABLE #{@table_name} (id INT, name VARCHAR(20), email VARCHAR(20));", } MANIFEST - apply_manifest_on(host, pp) do |r| + execute_manifest(pp) do |r| expect(r.stderr).not_to match(/Error/i) end @@ -77,15 +77,15 @@ def ensure_sqlserver_database(host,db_name, ensure_val = 'present') @table_name = 'Tables_' + SecureRandom.hex(3) @query = "USE #{db_name}; SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE table_name = '#{@table_name}';" - ensure_sqlserver_database(host, db_name) + ensure_sqlserver_database(db_name) end after(:all) do # remove the newly created instance - ensure_sqlserver_database(host, 'absent') + ensure_sqlserver_database('absent') end - it "Run a simple tsql command via sqlserver_tsql:" do + it "Run a simple tsql command via sqlserver_tsql:", :tier_low => true do pp = <<-MANIFEST sqlserver::config{'MSSQLSERVER': instance_name => 'MSSQLSERVER', @@ -98,7 +98,7 @@ def ensure_sqlserver_database(host,db_name, ensure_val = 'present') command => "CREATE TABLE #{@table_name} (id INT, name VARCHAR(20), email VARCHAR(20));", } MANIFEST - apply_manifest_on(host, pp) do |r| + execute_manifest(pp) do |r| expect(r.stderr).not_to match(/Error/i) end @@ -112,7 +112,7 @@ def ensure_sqlserver_database(host,db_name, ensure_val = 'present') run_sql_query(host, run_sql_query_opts) end - it "Run sqlserver_tsql WITH onlyif is true:" do + it "Run sqlserver_tsql WITH onlyif is true:", :tier_low => true do #Initilize a new table name: @table_name = 'Table_' + SecureRandom.hex(3) @query = "USE #{db_name}; SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE table_name = '#{@table_name}';" @@ -130,7 +130,7 @@ def ensure_sqlserver_database(host,db_name, ensure_val = 'present') } MANIFEST - apply_manifest_on(host, pp) do |r| + execute_manifest(pp) do |r| expect(r.stderr).not_to match(/Error/i) end @@ -144,7 +144,7 @@ def ensure_sqlserver_database(host,db_name, ensure_val = 'present') run_sql_query(host, run_sql_query_opts) end - it "Run sqlserver_tsql WITH onlyif is false:" do + it "Run sqlserver_tsql WITH onlyif is false:", :tier_low => true do #Initilize a new table name: @table_name = 'Table_' + SecureRandom.hex(3) @query = "USE #{db_name}; SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE table_name = '#{@table_name}';" @@ -163,7 +163,7 @@ def ensure_sqlserver_database(host,db_name, ensure_val = 'present') } MANIFEST - apply_manifest_on(host, pp) do |r| + execute_manifest(pp) do |r| expect(r.stderr).not_to match(/Error/i) end @@ -177,7 +177,7 @@ def ensure_sqlserver_database(host,db_name, ensure_val = 'present') run_sql_query(host, run_sql_query_opts) end - it "Negative test: Run tsql with invalid command:" do + it "Negative test: Run tsql with invalid command:", :tier_low => true do pp = <<-MANIFEST sqlserver::config{'MSSQLSERVER': instance_name => 'MSSQLSERVER', @@ -190,12 +190,12 @@ def ensure_sqlserver_database(host,db_name, ensure_val = 'present') command => "invalid-tsql-command", } MANIFEST - apply_manifest_on(host, pp, {:acceptable_exit_codes => [0,1]}) do |r| + execute_manifest(pp, {:acceptable_exit_codes => [0,1]}) do |r| expect(r.stderr).to match(/Error/i) end end - it "Negative test: Run tsql with non-existing database:" do + it "Negative test: Run tsql with non-existing database:", :tier_low => true do @table_name = 'Table_' + SecureRandom.hex(3) pp = <<-MANIFEST sqlserver::config{'MSSQLSERVER': @@ -209,7 +209,7 @@ def ensure_sqlserver_database(host,db_name, ensure_val = 'present') command => "CREATE TABLE #{@table_name} (id INT, name VARCHAR(20), email VARCHAR(20));", } MANIFEST - apply_manifest_on(host, pp, {:acceptable_exit_codes => [0,1]}) do |r| + execute_manifest(pp, {:acceptable_exit_codes => [0,1]}) do |r| expect(r.stderr).to match(/Error/i) end end diff --git a/spec/acceptance/sqlserver_user_spec.rb b/spec/acceptance/sqlserver_user_spec.rb index b7074433..f5f6178e 100644 --- a/spec/acceptance/sqlserver_user_spec.rb +++ b/spec/acceptance/sqlserver_user_spec.rb @@ -10,7 +10,7 @@ describe "sqlserver::user test", :node => host do - def ensure_sqlserver_database(host, db_name, ensure_val = 'present') + def ensure_sqlserver_database(db_name, ensure_val = 'present') pp = <<-MANIFEST sqlserver::config{'MSSQLSERVER': admin_user => 'sa', @@ -25,13 +25,13 @@ def ensure_sqlserver_database(host, db_name, ensure_val = 'present') sqlserver::database{ '#{db_name}': instance => 'MSSQLSERVER', collation_name => 'SQL_Estonian_CP1257_CS_AS', - compatibility => '100', + compatibility => 100, containment => 'PARTIAL', require => Sqlserver::Sp_configure['spconfig1'] } MANIFEST - apply_manifest_on(host, pp) do |r| + execute_manifest(pp) do |r| expect(r.stderr).not_to match(/Error/i) end end @@ -39,7 +39,7 @@ def ensure_sqlserver_database(host, db_name, ensure_val = 'present') context "Create database users with optional attributes", {:testrail => ['89143', '89144', '89145', '89146', '89149']} do before(:all) do # Create new database - ensure_sqlserver_database(host, db_name) + ensure_sqlserver_database(db_name) end before(:each) do @new_sql_login = "Login" + SecureRandom.hex(2) @@ -52,7 +52,7 @@ def ensure_sqlserver_database(host, db_name, ensure_val = 'present') #ensure_sqlserver_database(host, 'absent') end - it "Create database user with optional default_schema" do + it "Create database user with optional default_schema", :tier_low => true do pp = <<-MANIFEST sqlserver::config{'MSSQLSERVER': admin_user => 'sa', @@ -70,7 +70,7 @@ def ensure_sqlserver_database(host, db_name, ensure_val = 'present') require => Sqlserver::Login['#{@db_user}'], } MANIFEST - apply_manifest_on(host, pp) do |r| + execute_manifest(pp) do |r| expect(r.stderr).not_to match(/Error/i) end @@ -84,7 +84,7 @@ def ensure_sqlserver_database(host, db_name, ensure_val = 'present') run_sql_query(host, { :query => query, :server => hostname, :expected_row_count => 1 }) end - it "Create database user with optional instance" do + it "Create database user with optional instance", :tier_low => true do pp = <<-MANIFEST sqlserver::config{'MSSQLSERVER': admin_user => 'sa', @@ -102,7 +102,7 @@ def ensure_sqlserver_database(host, db_name, ensure_val = 'present') require => Sqlserver::Login['#{@db_user}'], } MANIFEST - apply_manifest_on(host, pp) do |r| + execute_manifest(pp) do |r| expect(r.stderr).not_to match(/Error/i) end @@ -114,7 +114,7 @@ def ensure_sqlserver_database(host, db_name, ensure_val = 'present') run_sql_query(host, { :query => query, :server => hostname, :expected_row_count => 1 }) end - it "Create database user with optional login" do + it "Create database user with optional login", :tier_low => true do pp = <<-MANIFEST sqlserver::config{'MSSQLSERVER': admin_user => 'sa', @@ -133,7 +133,7 @@ def ensure_sqlserver_database(host, db_name, ensure_val = 'present') require => Sqlserver::Login['#{@new_sql_login}'], } MANIFEST - apply_manifest_on(host, pp) do |r| + execute_manifest(pp) do |r| expect(r.stderr).not_to match(/Error/i) end @@ -146,7 +146,7 @@ def ensure_sqlserver_database(host, db_name, ensure_val = 'present') run_sql_query(host, { :query => query, :server => hostname, :expected_row_count => 1 }) end - it "Create database user with optional password" do + it "Create database user with optional password", :tier_low => true do pp = <<-MANIFEST sqlserver::config{'MSSQLSERVER': admin_user => 'sa', @@ -166,7 +166,7 @@ def ensure_sqlserver_database(host, db_name, ensure_val = 'present') require => Sqlserver::Login['#{@new_sql_login}'], } MANIFEST - apply_manifest_on(host, pp) do |r| + execute_manifest(pp) do |r| expect(r.stderr).not_to match(/Error/i) end @@ -175,7 +175,7 @@ def ensure_sqlserver_database(host, db_name, ensure_val = 'present') run_sql_query(host, { :query => query, :server => hostname, :expected_row_count => 1 }) end - it "Delete database user" do + it "Delete database user", :tier_low => true do pp = <<-MANIFEST sqlserver::config{'MSSQLSERVER': admin_user => 'sa', @@ -191,7 +191,7 @@ def ensure_sqlserver_database(host, db_name, ensure_val = 'present') require => Sqlserver::Login['#{@db_user}'], } MANIFEST - apply_manifest_on(host, pp) do |r| + execute_manifest(pp) do |r| expect(r.stderr).not_to match(/Error/i) end @@ -209,7 +209,7 @@ def ensure_sqlserver_database(host, db_name, ensure_val = 'present') database => '#{db_name}', } MANIFEST - apply_manifest_on(host, pp) do |r| + execute_manifest(pp) do |r| expect(r.stderr).not_to match(/Error/i) end #validate that the database user '#{@db_user}' should be deleted: diff --git a/spec/acceptance/z_last_sqlserver_features_spec.rb b/spec/acceptance/z_last_sqlserver_features_spec.rb index 3f5cb8b1..f85142bc 100644 --- a/spec/acceptance/z_last_sqlserver_features_spec.rb +++ b/spec/acceptance/z_last_sqlserver_features_spec.rb @@ -4,9 +4,9 @@ host = find_only_one("sql_host") describe "sqlserver_features", :node => host do - version = host['sql_version'].to_s + sql_version = host['sql_version'].to_s - def ensure_sql_features(host, features, ensure_val = 'present') + def ensure_sql_features(features, ensure_val = 'present') manifest = <<-MANIFEST sqlserver::config{ 'MSSQLSERVER': admin_pass => '<%= SQL_ADMIN_PASS %>', @@ -27,29 +27,26 @@ def ensure_sql_features(host, features, ensure_val = 'present') pp = ERB.new(manifest).result(binding) - apply_manifest_on(host, pp) do |r| + execute_manifest(pp) do |r| expect(r.stderr).not_to match(/Error/i) end end context 'can install' do - - features = ['Tools', 'BC', 'Conn', 'SSMS', 'ADV_SSMS', 'SDK', 'IS', 'MDS'] + features = ['BC', 'Conn', 'SDK', 'IS', 'MDS'] before(:all) do - remove_sql_features(host, {:features => features, :version => version}) + remove_sql_features(host, {:features => features, :version => sql_version}) end after(:all) do - remove_sql_features(host, {:features => features, :version => version}) + remove_sql_features(host, {:features => features, :version => sql_version}) end - it 'all possible features' do - ensure_sql_features(host, features) + it 'all possible features', :tier_low => true do + ensure_sql_features(features) - validate_sql_install(host, {:version => version}) do |r| - expect(r.stdout).to match(/Management Tools - Basic/) - expect(r.stdout).to match(/Management Tools - Complete/) + validate_sql_install(host, {:version => sql_version}) do |r| expect(r.stdout).to match(/Client Tools Connectivity/) expect(r.stdout).to match(/Client Tools Backwards Compatibility/) expect(r.stdout).to match(/Client Tools SDK/) @@ -61,23 +58,21 @@ def ensure_sql_features(host, features, ensure_val = 'present') context 'can remove' do - features = ['Tools', 'BC', 'Conn', 'SSMS', 'ADV_SSMS', 'SDK', 'IS', 'MDS'] + features = ['BC', 'Conn', 'SDK', 'IS', 'MDS'] before(:all) do - ensure_sql_features(host, features) + ensure_sql_features(features) end after(:all) do # redundant but necessary in case our manifest fails - remove_sql_features(host, {:features => features, :version => version}) + remove_sql_features(host, {:features => features, :version => sql_version}) end - it 'all possible features' do - ensure_sql_features(host, features, 'absent') + it 'all possible features', :tier_low => true do + ensure_sql_features(features, 'absent') - validate_sql_install(host, {:version => version}) do |r| - expect(r.stdout).not_to match(/Management Tools - Basic/) - expect(r.stdout).not_to match(/Management Tools - Complete/) + validate_sql_install(host, {:version => sql_version}) do |r| expect(r.stdout).not_to match(/Client Tools Connectivity/) expect(r.stdout).not_to match(/Client Tools Backwards Compatibility/) expect(r.stdout).not_to match(/Client Tools SDK/) @@ -87,85 +82,50 @@ def ensure_sql_features(host, features, ensure_val = 'present') end end - context 'can remove aggregate feature' do - - all_possible_features = ['Tools', 'BC', 'Conn', 'SSMS', 'ADV_SSMS', 'SDK', 'IS', 'MDS'] - aggregate_features = ['Tools', 'ADV_SSMS'] - - before(:all) do - remove_sql_features(host, {:features => all_possible_features, :version => version}) - end - - before(:each) do - ensure_sql_features(host, aggregate_features) - end - - after(:all) do - # only aggregate should be installed, but wipe all in case manifest misbehaves - remove_sql_features(host, {:features => all_possible_features, :version => version}) - end - - it "'Tools', which includes the 'Conn', 'SDK', 'BC', 'SSMS' and 'ADV_SSMS' features" do - ensure_sql_features(host, ['Tools'], 'absent') - - validate_sql_install(host, {:version => version}) do |r| - expect(r.stdout).not_to match(/Client Tools Connectivity/) - expect(r.stdout).not_to match(/Client Tools SDK/) - expect(r.stdout).not_to match(/Client Tools Backwards Compatibility/) - expect(r.stdout).not_to match(/Management Tools - Basic/) - expect(r.stdout).not_to match(/Management Tools - Complete/) - end - end - - it "'SSMS', which removes the dependent 'ADV_SSMS' feature" do - ensure_sql_features(host, ['SSMS'], 'absent') - - validate_sql_install(host, {:version => version}) do |r| - expect(r.stdout).to_not match(/Management Tools - Basic/) - expect(r.stdout).to_not match(/Management Tools - Complete/) - end - end - - end context 'can remove independent feature' do - - all_possible_features = ['Tools', 'BC', 'Conn', 'SSMS', 'ADV_SSMS', 'SDK', 'IS', 'MDS'] - features = ['BC', 'Conn', 'SSMS', 'ADV_SSMS', 'SDK', 'IS', 'MDS'] + if sql_version == '2016' + all_possible_features = ['BC', 'Conn', 'SDK', 'IS', 'MDS'] + features = ['BC', 'Conn', 'SDK', 'IS', 'MDS'] + else + all_possible_features = ['BC', 'Conn', 'SSMS', 'ADV_SSMS', 'SDK', 'IS', 'MDS'] + features = ['BC', 'Conn', 'SSMS', 'ADV_SSMS', 'SDK', 'IS', 'MDS'] + end before(:all) do - remove_sql_features(host, {:features => all_possible_features, :version => version}) + remove_sql_features(host, {:features => all_possible_features, :version => sql_version}) end before(:each) do - ensure_sql_features(host, features) + ensure_sql_features(features) end after(:all) do # only lower-level should be installed, but wipe all in case manifest misbehaves - remove_sql_features(host, {:features => all_possible_features, :version => version}) + remove_sql_features(host, {:features => all_possible_features, :version => sql_version}) end - it "'BC'" do - ensure_sql_features(host, features - ['BC']) + it "'BC'", :tier_low => true do + ensure_sql_features(features - ['BC']) - validate_sql_install(host, {:version => version}) do |r| + validate_sql_install(host, {:version => sql_version}) do |r| expect(r.stdout).not_to match(/Client Tools Backwards Compatibility/) end end - it "'Conn'" do - ensure_sql_features(host, features - ['Conn']) + it "'Conn'", :tier_low => true do + ensure_sql_features(features - ['Conn']) - validate_sql_install(host, {:version => version}) do |r| + validate_sql_install(host, {:version => sql_version}) do |r| expect(r.stdout).not_to match(/Client Tools Connectivity/) end end - it "'ADV_SSMS'" do - ensure_sql_features(host, features - ['ADV_SSMS']) + # TODO: Guard on SQL 2016 + it "'ADV_SSMS'", :unless => sql_version == '2016', :tier_low => true do + ensure_sql_features(features - ['ADV_SSMS']) - validate_sql_install(host, {:version => version}) do |r| + validate_sql_install(host, {:version => sql_version}) do |r| expect(r.stdout).not_to match(/Management Tools - Complete/) # also verify SSMS is still present @@ -173,33 +133,33 @@ def ensure_sql_features(host, features, ensure_val = 'present') end end - it "'SDK'" do - ensure_sql_features(host, features - ['SDK']) + it "'SDK'", :tier_low => true do + ensure_sql_features(features - ['SDK']) - validate_sql_install(host, {:version => version}) do |r| + validate_sql_install(host, {:version => sql_version}) do |r| expect(r.stdout).not_to match(/Client Tools SDK/) end end - it "'IS'" do - ensure_sql_features(host, features - ['IS']) + it "'IS'", :tier_low => true do + ensure_sql_features(features - ['IS']) - validate_sql_install(host, {:version => version}) do |r| + validate_sql_install(host, {:version => sql_version}) do |r| expect(r.stdout).not_to match(/Integration Services/) end end - it "'MDS'" do - ensure_sql_features(host, features - ['MDS']) + it "'MDS'", :tier_low => true do + ensure_sql_features(features - ['MDS']) - validate_sql_install(host, {:version => version}) do |r| + validate_sql_install(host, {:version => sql_version}) do |r| expect(r.stdout).not_to match(/Master Data Services/) end end end context 'with negative test cases' do - def bind_and_apply_failing_manifest(host, features, ensure_val = 'present') + def bind_and_apply_failing_manifest(features, ensure_val = 'present') failing_manifest = <<-MANIFEST sqlserver::config{ 'MSSQLSERVER': @@ -219,20 +179,20 @@ def bind_and_apply_failing_manifest(host, features, ensure_val = 'present') pp = ERB.new(failing_manifest).result(binding) - apply_manifest_on(host, pp) do |r| + execute_manifest(pp) do |r| expect(r.stderr).to match(/error/i) end end - it 'fails when an is_svc_account is supplied and a password is not' do - features = ['Tools', 'IS'] - bind_and_apply_failing_manifest(host, features) + it 'fails when an is_svc_account is supplied and a password is not', :tier_low => true do + features = ['IS'] + bind_and_apply_failing_manifest(features) end - it 'fails when ADV_SSMS is supplied but SSMS is not' do + it 'fails when ADV_SSMS is supplied but SSMS is not', :tier_low => true do skip('This test is blocked by FM-2712') features = ['BC', 'Conn', 'ADV_SSMS', 'SDK'] - ensure_sql_features(host, features) + ensure_sql_features(features) end end @@ -240,7 +200,7 @@ def bind_and_apply_failing_manifest(host, features, ensure_val = 'present') context 'can install' do - features = ['Tools', 'BC', 'Conn', 'SSMS', 'ADV_SSMS', 'SDK', 'IS', 'MDS'] + features = ['BC', 'Conn', 'SDK', 'IS', 'MDS'] before(:all) do puppet_version = (on host, puppet('--version')).stdout.chomp @@ -255,19 +215,17 @@ def bind_and_apply_failing_manifest(host, features, ensure_val = 'present') names = eval(fact_on(host, 'sqlserver_instances', facter_opts)).values.inject(:merge).keys end - remove_sql_instances(host, {:version => version, :instance_names => names}) + remove_sql_instances(host, {:version => sql_version, :instance_names => names}) end after(:all) do - remove_sql_features(host, {:features => features, :version => version}) + remove_sql_features(host, {:features => features, :version => sql_version}) end - it 'all possible features' do - ensure_sql_features(host, features) + it 'all possible features', :tier_low => true do + ensure_sql_features(features) - validate_sql_install(host, {:version => version}) do |r| - expect(r.stdout).to match(/Management Tools - Basic/) - expect(r.stdout).to match(/Management Tools - Complete/) + validate_sql_install(host, {:version => sql_version}) do |r| expect(r.stdout).to match(/Client Tools Connectivity/) expect(r.stdout).to match(/Client Tools Backwards Compatibility/) expect(r.stdout).to match(/Client Tools SDK/) diff --git a/spec/defines/database_spec.rb b/spec/defines/database_spec.rb index cb4235f5..6a370c91 100644 --- a/spec/defines/database_spec.rb +++ b/spec/defines/database_spec.rb @@ -9,26 +9,26 @@ :db_name => 'myTestDb', :instance => 'MSSQLSERVER', } } - end - - describe 'Minimal Params' do let(:pre_condition) { <<-EOF define sqlserver::config{} sqlserver::config {'MSSQLSERVER': } EOF } + end + + describe 'Minimal Params' do it_behaves_like 'compile' end describe 'Providing log filespec it should compile with valid log on params and' do it_behaves_like 'validation error' do let(:additional_params) { {:log_filename => "c:/test/logfile.ldf", :log_name => "myCrazyLog"} } - let(:raise_error_check) { 'filespec_name and filespec_filename must be specified when specifying any log attributes' } + let(:raise_error_check) { /(filespec_filename|filespec_name)/ } end it_behaves_like 'validation error' do let(:additional_params) { { :filespec_filename => 'c:/test/test.mdf'} } - let(:raise_error_check) { /(input needs to be a String|filespec_name must not be null if specifying filespec_filename)/ } + let(:raise_error_check) { /filespec_name must also be specified when specifying filespec_filename/ } end describe 'filespec_name can not be more than 128 characters' do it_behaves_like 'validation error' do @@ -36,14 +36,20 @@ :filespec_filename => 'c:/test/test.mdf', :filespec_name => 'OMGthisISsoReallyLongAndBoringProcessImeanAReallyOMGthisISsoReallyLongAndBoringProcessMakeItOMGthisISsoReallyLongAndBoringProcess'} } - let(:raise_error_check) { 'filespec_name can not be more than 128 characters' } + let(:raise_error_check) { "'filespec_name' expects" } end end it_behaves_like 'sqlserver_tsql command' do let(:additional_params) { { - :filespec_filename => 'c:/test/test.mdf', :filespec_name => 'myCreCre', - :log_filename => "c:/test/logfile.ldf", :log_name => "myCrazyLog"} } - let(:should_contain_command) { [/c\:\/test\/logfile\.ldf/] } + :filespec_filename => 'c:/test/test.mdf', :filespec_name => 'myCre-Cre', + :log_filename => "c:/test/logfile.ldf", :log_name => "myCrazy_Log"} } + # Ensure that the parameters are in the TSQL and are correctly escaped + let(:should_contain_command) {[ + /NAME = N'myCre-Cre'/, + /FILENAME = N'c\:\/test\/test\.mdf'/, + /NAME = N'myCrazy_Log'/, + /FILENAME = N'c\:\/test\/logfile\.ldf'/ + ]} end end describe 'collation_name' do diff --git a/spec/defines/login/permissions_spec.rb b/spec/defines/login/permissions_spec.rb index 3cc41f45..b63a3791 100644 --- a/spec/defines/login/permissions_spec.rb +++ b/spec/defines/login/permissions_spec.rb @@ -2,7 +2,11 @@ require File.expand_path(File.join(File.dirname(__FILE__), '..', 'manifest_shared_examples.rb')) describe 'sqlserver::login::permissions' do - let(:facts) { {:osfamily => 'windows'} } + let(:pre_condition) { <<-EOF + define sqlserver::config{} + sqlserver::config {'MSSQLSERVER': } + EOF + } context 'validation errors' do include_context 'manifests' do let(:title) { 'myTitle' } @@ -12,7 +16,7 @@ let(:params) { { :permissions => ['SELECT'], } } - let(:raise_error_check) { 'Login must be between 1 and 128 characters' } + let(:raise_error_check) { /'login' expects a String.+ value/ } describe 'missing' do if Puppet::Util::Package.versioncmp(Puppet.version, '4.3.0') < 0 let(:raise_error_check) { 'Must pass login to Sqlserver::Login::Permissions[myTitle]' } @@ -30,11 +34,11 @@ it_behaves_like 'validation error' end end - context 'permission' do + context 'permissions' do let(:params) { { :login => 'loggingUser', } } - let(:raise_error_check) { 'Permission must be between 4 and 128 characters' } + let(:raise_error_check) { /'permissions' .+ expects a String.+ value/ } describe 'empty' do let(:additional_params) { {:permissions => ['']} } it_behaves_like 'validation error' @@ -55,7 +59,7 @@ } } describe 'invalid' do let(:additional_params) { {:state => 'invalid'} } - let(:raise_error_check) { "State parameter can only be one of 'GRANT', 'REVOKE' or 'DENY', you passed a value of invalid" } + let(:raise_error_check) { "'state' expects" } it_behaves_like 'validation error' end end diff --git a/spec/defines/login_spec.rb b/spec/defines/login_spec.rb index 56c49206..be15539d 100644 --- a/spec/defines/login_spec.rb +++ b/spec/defines/login_spec.rb @@ -9,6 +9,11 @@ :login => 'myTitle', :instance => 'MSSQLSERVER', } } + let(:pre_condition) { <<-EOF + define sqlserver::config{} + sqlserver::config {'MSSQLSERVER': } + EOF + } end describe 'Minimal Params' do diff --git a/spec/defines/role_spec.rb b/spec/defines/role_spec.rb index f37a7be8..b8983b24 100644 --- a/spec/defines/role_spec.rb +++ b/spec/defines/role_spec.rb @@ -12,7 +12,7 @@ let(:additional_params) { { :type => 'invalid', } } - let(:raise_error_check) { "Type must be either 'SERVER' or 'DATABASE', provided 'invalid'" } + let(:raise_error_check) { "'type' expects" } it_behaves_like 'validation error' end describe 'SERVER' do @@ -80,7 +80,7 @@ end describe 'empty instance' do let(:additional_params) { {'instance' => ''} } - let(:raise_error_check) { 'Instance name must be between 1 to 16 characters' } + let(:raise_error_check) { "instance' expects a String[1, 16]" } it_behaves_like 'validation error' end end @@ -171,17 +171,10 @@ END" ] } let(:should_contain_onlyif) { [ - "DECLARE @purge_members TABLE ( -ID int IDENTITY(1,1), -member varchar(128) -)", - "INSERT INTO @purge_members (member) ( -SELECT m.name FROM sys.server_role_members rm +"SELECT m.name FROM sys.server_role_members rm JOIN sys.server_principals r ON rm.role_principal_id = r.principal_id - JOIN sys.server_principals m ON rm.member_principal_id = m.principal_id - WHERE r.name = 'myCustomRole'", - "IF 0 != (SELECT COUNT(*) FROM @purge_members) - THROW 51000, 'Unlisted Members in Role, will be purged', 10", + JOIN sys.server_principals m ON rm.member_principal_id = m.principal_id + WHERE r.name = 'myCustomRole'" ] } it_behaves_like 'sqlserver_tsql command' it_behaves_like 'sqlserver_tsql onlyif' @@ -201,17 +194,10 @@ END" ] } let(:should_contain_onlyif) { [ - "DECLARE @purge_members TABLE ( -ID int IDENTITY(1,1), -member varchar(128) -)", - "INSERT INTO @purge_members (member) ( -SELECT m.name FROM sys.database_role_members rm +"SELECT m.name FROM sys.database_role_members rm JOIN sys.database_principals r ON rm.role_principal_id = r.principal_id - JOIN sys.database_principals m ON rm.member_principal_id = m.principal_id - WHERE r.name = 'myCustomRole'", - "IF 0 != (SELECT COUNT(*) FROM @purge_members) - THROW 51000, 'Unlisted Members in Role, will be purged', 10", + JOIN sys.database_principals m ON rm.member_principal_id = m.principal_id + WHERE r.name = 'myCustomRole'", ] } it_behaves_like 'sqlserver_tsql command' it_behaves_like 'sqlserver_tsql onlyif' diff --git a/spec/defines/sp_configure_spec.rb b/spec/defines/sp_configure_spec.rb index ac4c5223..af5be7a1 100644 --- a/spec/defines/sp_configure_spec.rb +++ b/spec/defines/sp_configure_spec.rb @@ -9,6 +9,11 @@ :config_name => 'filestream access level', :value => 1, } } + let(:pre_condition) { <<-EOF + define sqlserver::config{} + sqlserver::config {'MSSQLSERVER': } + EOF + } end describe 'basic usage' do let(:should_contain_command) { [ @@ -38,13 +43,13 @@ describe 'reconfigure => invalid' do let(:additional_params) { {:reconfigure => 'invalid'} } - let(:raise_error_check) {'"invalid" is not a boolean. It looks to be a String'} + let(:raise_error_check) { "'reconfigure' expects a Boolean value" } it_behaves_like 'validation error' end describe 'restart => invalid' do let(:additional_params) { {:restart => 'invalid'} } - let(:raise_error_check) {'"invalid" is not a boolean. It looks to be a String'} + let(:raise_error_check) { "'restart' expects a Boolean value" } it_behaves_like 'validation error' end @@ -62,7 +67,7 @@ describe 'with_override => invalid' do let(:additional_params) { {:with_override => 'invalid'} } - let(:raise_error_check) {'"invalid" is not a boolean. It looks to be a String'} + let(:raise_error_check) { "'with_override' expects a Boolean value" } it_behaves_like 'validation error' end diff --git a/spec/defines/user/permissions_spec.rb b/spec/defines/user/permissions_spec.rb index 36934bae..057d23c0 100644 --- a/spec/defines/user/permissions_spec.rb +++ b/spec/defines/user/permissions_spec.rb @@ -2,7 +2,12 @@ require File.expand_path(File.join(File.dirname(__FILE__), '..', 'manifest_shared_examples.rb')) describe 'sqlserver::user::permissions' do - let(:facts) { {:osfamily => 'windows'} } + let(:pre_condition) { <<-EOF + define sqlserver::config{} + sqlserver::config {'MSSQLSERVER': } + EOF + } + context 'validation errors' do include_context 'manifests' do let(:title) { 'myTitle' } @@ -13,13 +18,9 @@ :permissions => ['SELECT'], :database => 'loggingDb', } } - let(:raise_error_check) { 'User must be between 1 and 128 characters' } + let(:raise_error_check) { "'user' expects a String[1, 128] value" } describe 'missing' do - if Puppet::Util::Package.versioncmp(Puppet.version, '4.3.0') < 0 - let(:raise_error_check) { 'Must pass user to Sqlserver::User::Permissions[myTitle]' } - else - let(:raise_error_check) { "expects a value for parameter 'user'" } - end + let(:raise_error_check) { "expects a value for parameter 'user'" } it_behaves_like 'validation error' end @@ -37,9 +38,10 @@ :user => 'loggingUser', :database => 'loggingDb', } } - let(:raise_error_check) { 'Permission must be between 4 and 128 characters' } + let(:raise_error_check) { /'permissions' .+ expects a String.+ value/ } describe 'empty' do let(:additional_params) { {:permissions => ''} } + let(:raise_error_check) { /'permissions' expects an Array value/ } it_behaves_like 'validation error' end describe 'under limit' do @@ -59,7 +61,7 @@ } } describe 'invalid' do let(:additional_params) { {:state => 'invalide'} } - let(:raise_error_check) { "State can only be of 'GRANT', 'REVOKE' or 'DENY' you passed invalide" } + let(:raise_error_check) { "'state' expects" } it_behaves_like 'validation error' end end @@ -77,7 +79,7 @@ end describe 'invalid' do let(:additional_params) { {:with_grant_option => 'invalid'} } - let(:raise_error_check) { '"invalid" is not a boolean' } + let(:raise_error_check) { "'with_grant_option' expects" } it_behaves_like 'validation error' end end diff --git a/spec/defines/user_spec.rb b/spec/defines/user_spec.rb index 54b77183..c8b236a5 100644 --- a/spec/defines/user_spec.rb +++ b/spec/defines/user_spec.rb @@ -6,13 +6,18 @@ let(:title) { 'loggingUser' } let(:sqlserver_tsql_title) { 'user-MSSQLSERVER-myDatabase-loggingUser' } let(:params) { {:user => 'loggingUser', :database => 'myDatabase'} } + let(:pre_condition) { <<-EOF + define sqlserver::config{} + sqlserver::config {'MSSQLSERVER': } + EOF + } end describe 'should fail when password above 128 characters' do o = [('a'..'z'), ('A'..'Z'), (0..9)].map { |i| i.to_a }.flatten string = (0...129).map { o[rand(o.length)] }.join let(:additional_params) { {:password => string} } - let(:raise_error_check) { 'Password must be equal or less than 128 characters' } + let(:raise_error_check) { "'password' expects" } it_should_behave_like 'validation error' end @@ -20,7 +25,7 @@ o = [('a'..'z'), ('A'..'Z'), (0..9)].map { |i| i.to_a }.flatten string = (0...129).map { o[rand(o.length)] }.join let(:additional_params) { {:database => string} } - let(:raise_error_check) { 'Database name must be between 1 and 128 characters' } + let(:raise_error_check) { "'database' expects a String[1, 128]" } let(:sqlserver_tsql_title) { "user-MSSQLSERVER-#{string}-loggingUser" } it_should_behave_like 'validation error' end @@ -181,7 +186,6 @@ :permissions => {'GRANT' => ['CONNECT SQL'], 'REVOKE' => ['CONNECT SQL']} } } let(:raise_error_check) { "Duplicate permissions found for sqlserver::user[#{title}" } - let(:raise_error_check) { "Duplicate permissions found for sqlserver::user[#{title}" } it_behaves_like 'validation error' end end diff --git a/spec/functions/sqlserver_validate_svrroles_hash_spec.rb b/spec/functions/sqlserver_validate_svrroles_hash_spec.rb deleted file mode 100644 index 6ac2d2f7..00000000 --- a/spec/functions/sqlserver_validate_svrroles_hash_spec.rb +++ /dev/null @@ -1,46 +0,0 @@ -require 'spec_helper' -require 'puppet/error' - -RSpec.describe 'sqlserver_validate_svrroles_hash function' do - possible_roles = %w(sysadmin serveradmin securityadmin processadmin setupadmin bulkadmin diskadmin dbcreator) - - shared_examples 'compile' do |value| - it { - scope.function_sqlserver_validate_svrroles_hash([value]) - } - end - - shared_examples 'failure' do - - end - - describe 'should validate an empty hash' do - it_should_behave_like 'compile', {} - end - - describe 'should compile and validate the correct hash' do - it_should_behave_like 'compile', {'sysadmin' => 1} - end - - describe 'should fail when invalid role' do - let(:arguments) { [{'bogus' => 1}] } - let(:msg) { /svrrole requires a value of/ } - it { - expect { - scope.function_sqlserver_validate_svrroles_hash(arguments) - }.to raise_error(Puppet::Error, msg) - - } - end - - describe 'should fail with more than one parameter' do - let(:arguments) { [{'sysadmin' => 1}, 'whoops'] } - let(:msg) { /sqlserver_validate_svcrole_hash\(\): wrong number of arguments/ } - it { - expect { - scope.function_sqlserver_validate_svrroles_hash(arguments) - }.to raise_error(Puppet::Error, msg) - } - end - -end diff --git a/spec/spec_helper_acceptance.rb b/spec/spec_helper_acceptance.rb index 0636568b..d88a94da 100644 --- a/spec/spec_helper_acceptance.rb +++ b/spec/spec_helper_acceptance.rb @@ -2,10 +2,14 @@ require 'beaker-rspec/helpers/serverspec' require 'sql_testing_helpers' require 'beaker/puppet_install_helper' +require 'beaker/testmode_switcher' +require 'beaker/testmode_switcher/dsl' + WIN_ISO_ROOT = "http://int-resources.ops.puppetlabs.net/ISO/Windows/2012" WIN_2012R2_ISO = "en_windows_server_2012_r2_with_update_x64_dvd_6052708.iso" QA_RESOURCE_ROOT = "http://int-resources.ops.puppetlabs.net/QA_resources/microsoft_sql/iso/" +SQL_2016_ISO = "en_sql_server_2016_enterprise_with_service_pack_1_x64_dvd_9542382.iso" SQL_2014_ISO = "SQLServer2014-x64-ENU.iso" SQL_2012_ISO = "SQLServer2012SP1-FullSlipstream-ENU-x64.iso" SQL_ADMIN_USER = 'sa' @@ -71,6 +75,7 @@ # Install sqlserver dependencies. on(agent, puppet('module install puppetlabs-stdlib')) + on(master, puppet('module install puppetlabs-stdlib')) # Mount windows 2012R2 ISO to allow install of .NET 3.5 Windows Feature iso_opts = { @@ -83,5 +88,6 @@ # Install sqlserver module from local source. # See FM-5062 for more details. copy_module_to(agent, local) + copy_module_to(master, local) end end diff --git a/spec/sql_testing_helpers.rb b/spec/sql_testing_helpers.rb index 0b599dba..48c6e6e1 100644 --- a/spec/sql_testing_helpers.rb +++ b/spec/sql_testing_helpers.rb @@ -54,6 +54,8 @@ def run_sql_query(host, opts = {}, &block) powershell = <<-EOS $Env:Path +=\";C:\\Program Files\\Microsoft SQL Server\\Client SDK\\ODBC\\110\\Tools\\Binn;C:\\Program Files\\Microsoft SQL Server\\110\\Tools\\Binn\\" + $Env:Path +=\";C:\\Program Files\\Microsoft SQL Server\\Client SDK\\ODBC\\120\\Tools\\Binn;C:\\Program Files\\Microsoft SQL Server\\120\\Tools\\Binn\\" + $Env:Path +=\";C:\\Program Files\\Microsoft SQL Server\\Client SDK\\ODBC\\130\\Tools\\Binn;C:\\Program Files\\Microsoft SQL Server\\130\\Tools\\Binn\\" sqlcmd.exe -S #{server}\\#{instance} -U #{sql_admin_user} -P #{sql_admin_pass} -Q \"#{query}\" EOS # sqlcmd has problem authenticate to sqlserver if the instance is the default one MSSQLSERVER @@ -95,6 +97,12 @@ def base_install(sql_version) :file => SQL_2014_ISO, :drive_letter => 'H' } + when 2016 + iso_opts = { + :folder => QA_RESOURCE_ROOT, + :file => SQL_2016_ISO, + :drive_letter => 'H' + } end host = find_only_one('sql_host') # Mount the ISO on the agent @@ -138,7 +146,7 @@ def remove_sql_instances(host, opts = {}) end def get_install_paths(version) - vers = { '2012' => '110', '2014' => '120' } + vers = { '2012' => '110', '2014' => '120', '2016' => '130' } raise 'Valid version must be specified' if ! vers.keys.include?(version) diff --git a/spec/unit/puppet/provider/sqlserver_features_spec.rb b/spec/unit/puppet/provider/sqlserver_features_spec.rb index 2d33646a..f5d7c9c6 100644 --- a/spec/unit/puppet/provider/sqlserver_features_spec.rb +++ b/spec/unit/puppet/provider/sqlserver_features_spec.rb @@ -59,12 +59,12 @@ end end - context 'it should expand the superset for features' do - include_context 'features' - let(:additional_params) { {:features => %w(Tools)} } - let(:munged_args) { {:features => %w(ADV_SSMS BC Conn SDK SSMS)} } - it_should_behave_like 'create' - end + # context 'it should expand the superset for features' do + # include_context 'features' + # let(:additional_params) { {:features => %w(Tools)} } + # let(:munged_args) { {:features => %w(ADV_SSMS BC Conn SDK SSMS)} } + # it_should_behave_like 'create' + # end shared_examples 'features=' do |args, exit_code, warning_matcher| it { @@ -124,20 +124,13 @@ it_should_behave_like 'features=', @feature_params, 3010, /reboot required/i end - context 'it should install the expanded tools set' do - include_context 'features' - @feature_params[:features] = %w(Tools) - let(:feature_add) { %w(ADV_SSMS BC Conn SDK SSMS) } - it_should_behave_like 'features=', @feature_params - end + # context 'it should install the expanded tools set' do + # include_context 'features' + # @feature_params[:features] = %w(Tools) + # let(:feature_add) { %w(ADV_SSMS BC Conn SDK SSMS) } + # it_should_behave_like 'features=', @feature_params + # end - context 'it should' do - include_context 'features' - @feature_params[:features] = %w(Tools IS) - @feature_params[:is_svc_account] = 'nexus/domainuser' - # let(:feature_params) { @feature_params } - it_should_behave_like 'fail on', @feature_params - end describe 'it should call destroy on empty array' do it { feature_params = { diff --git a/spec/unit/puppet/provider/sqlserver__instance_spec.rb b/spec/unit/puppet/provider/sqlserver_instance_spec.rb similarity index 72% rename from spec/unit/puppet/provider/sqlserver__instance_spec.rb rename to spec/unit/puppet/provider/sqlserver_instance_spec.rb index 7cb995ec..34c12b9c 100644 --- a/spec/unit/puppet/provider/sqlserver__instance_spec.rb +++ b/spec/unit/puppet/provider/sqlserver_instance_spec.rb @@ -10,6 +10,22 @@ subject { provider_class } let(:additional_install_switches) { [] } + let(:resourcekey_to_cmdarg) {{ + 'agt_svc_account' => 'AGTSVCACCOUNT', + 'agt_svc_password' => 'AGTSVCPASSWORD', + 'as_svc_account' => 'ASSVCACCOUNT', + 'as_svc_password' => 'ASSVCPASSWORD', + 'pid' => 'PID', + 'rs_svc_account' => 'RSSVCACCOUNT', + 'rs_svc_password' => 'RSSVCPASSWORD', + 'polybase_svc_account' => 'PBENGSVCACCOUNT', + 'polybase_svc_password' => 'PBDMSSVCPASSWORD', + 'sa_pwd' => 'SAPWD', + 'security_mode' => 'SECURITYMODE', + 'sql_svc_account' => 'SQLSVCACCOUNT', + 'sql_svc_password' => 'SQLSVCPASSWORD', + }} + def stub_uninstall(args, installed_features, exit_code = 0) cmd_args = ["#{args[:source]}/setup.exe", "/ACTION=uninstall", @@ -22,7 +38,7 @@ def stub_uninstall(args, installed_features, exit_code = 0) Puppet::Util::Execution.stubs(:execute).with(cmd_args.compact, failonfail: false).returns(result) end - shared_examples 'run' do |args, munged_values = {}| + shared_examples 'create' do |exit_code, warning_matcher| it { execute_args = args.merge(munged_values) @resource = Puppet::Type::Sqlserver_instance.new(args) @@ -37,18 +53,34 @@ def stub_uninstall(args, installed_features, exit_code = 0) '/IACCEPTSQLSERVERLICENSETERMS', "/INSTANCENAME=#{execute_args[:name]}", "/FEATURES=#{execute_args[:features].join(',')}",] - (execute_args.keys - %w(ensure loglevel features name source sql_sysadmin_accounts sql_security_mode install_switches).map(&:to_sym)).sort.collect do |key| - cmd_args << "/#{key.to_s.gsub(/_/, '').upcase}=\"#{@resource[key]}\"" + (execute_args.keys - %w( ensure loglevel features name source sql_sysadmin_accounts sql_security_mode install_switches).map(&:to_sym)).sort.collect do |key| + cmd_args << "/#{resourcekey_to_cmdarg[key.to_s]}=\"#{@resource[key]}\"" end if execute_args[:sql_security_mode] cmd_args << "/SECURITYMODE=SQL" end - cmd_args << "/SQLSYSADMINACCOUNTS=#{ Array.new(@resource[:sql_sysadmin_accounts]).collect { |account| "\"#{account}\"" }.join(' ')}" - Puppet::Util::Execution.stubs(:execute).with(cmd_args.compact).returns(0) + + # wrap each arg in doublequotes + admin_args = execute_args[:sql_sysadmin_accounts].map { |a| "\"#{a}\"" } + # prepend first arg only with CLI switch + admin_args[0] = "/SQLSYSADMINACCOUNTS=" + admin_args[0] + cmd_args += admin_args + + additional_install_switches.each do |switch| + cmd_args << switch + end + + # If warning_matcher supplied ensure warnings raised match, otherwise no warnings raised + @provider.stubs(:warn).with(regexp_matches(warning_matcher)).returns(nil).times(1) if warning_matcher + @provider.stubs(:warn).with(anything).times(0) unless warning_matcher + + result = Puppet::Util::Execution::ProcessOutput.new('', exit_code || 0) + Puppet::Util::Execution.stubs(:execute).with(cmd_args.compact, failonfail: false).returns(result) @provider.create } end - shared_examples 'create' do |exit_code, warning_matcher| + + shared_examples 'create_failure' do |exit_code, error_matcher| it { execute_args = args.merge(munged_values) @resource = Puppet::Type::Sqlserver_instance.new(args) @@ -64,7 +96,7 @@ def stub_uninstall(args, installed_features, exit_code = 0) "/INSTANCENAME=#{execute_args[:name]}", "/FEATURES=#{execute_args[:features].join(',')}",] (execute_args.keys - %w( ensure loglevel features name source sql_sysadmin_accounts sql_security_mode install_switches).map(&:to_sym)).sort.collect do |key| - cmd_args << "/#{key.to_s.gsub(/_/, '').upcase}=\"#{@resource[key]}\"" + cmd_args << "/#{resourcekey_to_cmdarg[key.to_s]}=\"#{@resource[key]}\"" end if execute_args[:sql_security_mode] cmd_args << "/SECURITYMODE=SQL" @@ -80,17 +112,14 @@ def stub_uninstall(args, installed_features, exit_code = 0) cmd_args << switch end - # If warning_matcher supplied ensure warnings raised match, otherwise no warnings raised - @provider.stubs(:warn).with(regexp_matches(warning_matcher)).returns(nil).times(1) if warning_matcher - @provider.stubs(:warn).with(anything).times(0) unless warning_matcher + @provider.stubs(:warn).with(anything).times(0) result = Puppet::Util::Execution::ProcessOutput.new('', exit_code || 0) Puppet::Util::Execution.stubs(:execute).with(cmd_args.compact, failonfail: false).returns(result) - @provider.create + expect{ @provider.create }.to raise_error(error_matcher) } end - shared_examples 'destroy' do |exit_code, warning_matcher| it { @resource = Puppet::Type::Sqlserver_instance.new(args) @@ -115,6 +144,7 @@ def stub_uninstall(args, installed_features, exit_code = 0) provider.create } end + describe 'it should provide the correct command default command' do it_behaves_like 'create' do args = get_basic_args @@ -127,6 +157,45 @@ def stub_uninstall(args, installed_features, exit_code = 0) end end + describe 'it should raise error if as_sysadmin_accounts is specified without AS feature' do + it_behaves_like 'create_failure', 1, /as_sysadmin_accounts was specified however the AS feature was not included/i do + args = get_basic_args + args[:features] = ['SQLEngine'] + args[:as_sysadmin_accounts] = 'username' + + let(:args) { args } + munged = {:features => Array.new(args[:features])} + let(:munged_values) { munged } + end + end + + describe 'it should raise error if polybase_svc_account is specified without POLYBASE feature' do + it_behaves_like 'create_failure', 1, /polybase_svc_account was specified however the POLYBASE feature was not included/i do + args = get_basic_args + args[:features] = ['SQLEngine'] + args[:polybase_svc_account] = 'username' + args.delete(:polybase_svc_password) + + let(:args) { args } + munged = {:features => Array.new(args[:features])} + let(:munged_values) { munged } + end + end + + describe 'it should raise error if polybase_svc_password is specified without POLYBASE feature' do + it_behaves_like 'create_failure', 1, /polybase_svc_password was specified however the POLYBASE feature was not included/i do + args = get_basic_args + args[:features] = ['SQLEngine'] + args.delete(:polybase_svc_account) + args[:polybase_svc_password] = 'password' + + let(:args) { args } + munged = {:features => Array.new(args[:features])} + let(:munged_values) { munged } + end + end + + describe 'it should raise warning on install when 1641 exit code returned' do it_behaves_like 'create', 1641, /reboot initiated/i do args = get_basic_args diff --git a/spec/unit/puppet/provider/sqlserver__tsql_spec.rb b/spec/unit/puppet/provider/sqlserver_tsql_spec.rb similarity index 100% rename from spec/unit/puppet/provider/sqlserver__tsql_spec.rb rename to spec/unit/puppet/provider/sqlserver_tsql_spec.rb diff --git a/spec/unit/puppet/sqlserver_install_context.rb b/spec/unit/puppet/sqlserver_install_context.rb index 975e56b5..5f12d199 100644 --- a/spec/unit/puppet/sqlserver_install_context.rb +++ b/spec/unit/puppet/sqlserver_install_context.rb @@ -1,26 +1,8 @@ -RSpec.shared_context 'install_arguments' do - @install_args = { - :source => 'C:\myinstallexecs', - :pid => 'areallyCrazyLongPid', - :features => %w(SQL AS RS), - :name => 'MYSQLSERVER_HOST', - :agt_svc_account => 'nexus\travis', - :agt_svc_password => 'P@ssword1', - :as_svc_account => 'analysisAccount', - :as_svc_password => 'CrazySimpleP@ssword', - :rs_svc_account => 'reportUserAccount', #always local user - :rs_svc_password => 'reportP@ssword1', - :sql_svc_account => 'NT Service\MSSQLSERVER', - :sql_sysadmin_accounts => ['localAdminAccount', 'nexus\domainUser'] - } - let(:args) { @install_args } -end - def get_basic_args return { :source => 'C:\myinstallexecs', :pid => 'areallyCrazyLongPid', - :features => %w(SQL AS RS), + :features => %w(SQL AS RS POLYBASE), :name => 'MYSQLSERVER_HOST', :agt_svc_account => 'nexus\travis', :agt_svc_password => 'P@ssword1', @@ -28,6 +10,8 @@ def get_basic_args :as_svc_password => 'CrazySimpleP@ssword', :rs_svc_account => 'reportUserAccount', #always local user :rs_svc_password => 'reportP@ssword1', + :polybase_svc_account => 'nexus\polyuser', + :polybase_svc_password => 'P@ssword1', :sql_svc_account => 'NT Service\MSSQLSERVER', :sql_sysadmin_accounts => ['localAdminAccount', 'nexus\domainUser'] } diff --git a/spec/unit/puppet/type/sqlserver_features_spec.rb b/spec/unit/puppet/type/sqlserver_features_spec.rb new file mode 100644 index 00000000..fb0e0923 --- /dev/null +++ b/spec/unit/puppet/type/sqlserver_features_spec.rb @@ -0,0 +1,20 @@ +require 'spec_helper' +require File.expand_path(File.join(File.dirname(__FILE__), '..', 'sqlserver_install_context.rb')) + +RSpec.describe Puppet::Type.type(:sqlserver_features) do + let(:error_class) { Puppet::Error } + + describe "features" do + ['Tools'].each do |feature_name| + it "should raise deprecation warning with super feature #{feature_name}" do + args = { + :name => 'Generic Features', + :ensure => 'present', + :features => [feature_name], + } + Puppet.expects(:deprecation_warning).at_least_once + subject = Puppet::Type.type(:sqlserver_features).new(args) + end + end + end +end diff --git a/spec/unit/puppet/type/sqlserver_instance_spec.rb b/spec/unit/puppet/type/sqlserver_instance_spec.rb index dd8638a6..83eedd88 100644 --- a/spec/unit/puppet/type/sqlserver_instance_spec.rb +++ b/spec/unit/puppet/type/sqlserver_instance_spec.rb @@ -19,7 +19,7 @@ end # Failed validation examples - shared_examples 'fail validation' do #|args, messages = ['must be set'], error_class = Puppet::Error| + shared_examples 'fail validation' do it 'should fail with' do expect { Puppet::Type.type(:sqlserver_instance).new(args) @@ -31,6 +31,20 @@ end end + describe "features" do + ['SQL'].each do |feature_name| + it "should raise deprecation warning with super feature #{feature_name}" do + args = { + :name => 'MSSQLSERVER', + :ensure => 'present', + :features => [feature_name], + } + Puppet.expects(:deprecation_warning).at_least_once + subject = Puppet::Type.type(:sqlserver_instance).new(args) + end + end + end + describe "agt_svc_password required when using domain account" do it_should_behave_like 'fail validation' do args = get_basic_args @@ -40,27 +54,30 @@ end end - - describe 'should fail when rs_svc_account contains an invalid character' do + describe "rs_svc_account" do %w(/ \ [ ] : ; | = , + * ? < > ).each do |v| - it_should_behave_like 'fail validation' do - args = get_basic_args - args[:rs_svc_account] = "crazy#{v}User" - let(:args) { args } - let(:messages) { ['rs_svc_account can not contain any of the special characters,'] } + context "contains invalid character #{v}" do + it_should_behave_like 'fail validation' do + args = get_basic_args + args[:rs_svc_account] = "crazy#{v}User" + let(:args) { args } + let(:messages) { ['rs_svc_account can not contain any of the special characters,'] } + end end end end - context 'must be at least 8 characters long' do - it_behaves_like 'fail validation' do - args = get_basic_args - args[:rs_svc_password] = 'hrt' - let(:args) { args } - let(:messages) { ['must be at least 8 characters long', 'must contain uppercase letters', - 'must contain numbers', - 'must contain a special character'] } - + describe "rs_svc_password" do + context 'when less than 8 characters long' do + it_behaves_like 'fail validation' do + args = get_basic_args + args[:rs_svc_password] = 'hrt' + let(:args) { args } + let(:messages) { ['must be at least 8 characters long', + 'must contain uppercase letters', + 'must contain numbers', + 'must contain a special character'] } + end end end end diff --git a/spec/unit/puppet_x/sql_connection_spec.rb b/spec/unit/puppet_x/sql_connection_spec.rb index 69a6db3b..d8ada844 100644 --- a/spec/unit/puppet_x/sql_connection_spec.rb +++ b/spec/unit/puppet_x/sql_connection_spec.rb @@ -17,7 +17,7 @@ def stub_connection context 'command execution' do before :each do stub_connection - @connection.stubs(:Open).with('Provider=SQLOLEDB.1;Initial Catalog=master;Application Name=Puppet;Data Source=localhost;User ID=sa;Password=Pupp3t1@') + @connection.stubs(:Open).with('Provider=SQLOLEDB.1;Initial Catalog=master;Application Name=Puppet;Data Source=.;User ID=sa;Password=Pupp3t1@') end it 'should not raise an error but populate has_errors with message' do subject.stubs(:execute).raises(Exception.new("SQL Server\n error has happened")) @@ -43,7 +43,7 @@ def stub_connection context 'Use default authentication' do it 'should defaul to SQL_LOGIN if admin_login_type is not set' do - @connection.expects(:Open).with('Provider=SQLOLEDB.1;Initial Catalog=master;Application Name=Puppet;Data Source=localhost;User ID=sa;Password=Pupp3t1@') + @connection.expects(:Open).with('Provider=SQLOLEDB.1;Initial Catalog=master;Application Name=Puppet;Data Source=.;User ID=sa;Password=Pupp3t1@') subject.open_and_run_command('query', {:admin_user => 'sa', :admin_pass => 'Pupp3t1@' }) end end @@ -66,11 +66,11 @@ def stub_connection end it 'should not add the default instance of MSSQLSERVER to connection string' do - @connection.expects(:Open).with('Provider=SQLOLEDB.1;Initial Catalog=master;Application Name=Puppet;Data Source=localhost;User ID=sa;Password=Pupp3t1@') + @connection.expects(:Open).with('Provider=SQLOLEDB.1;Initial Catalog=master;Application Name=Puppet;Data Source=.;User ID=sa;Password=Pupp3t1@') subject.open_and_run_command('query', {:admin_user => 'sa', :admin_pass => 'Pupp3t1@', :instance_name => 'MSSQLSERVER'}) end it 'should add a non default instance to connection string' do - @connection.expects(:Open).with('Provider=SQLOLEDB.1;Initial Catalog=master;Application Name=Puppet;Data Source=localhost\\LOGGING;User ID=sa;Password=Pupp3t1@') + @connection.expects(:Open).with('Provider=SQLOLEDB.1;Initial Catalog=master;Application Name=Puppet;Data Source=.\\LOGGING;User ID=sa;Password=Pupp3t1@') subject.open_and_run_command('query', {:admin_user => 'sa', :admin_pass => 'Pupp3t1@', :instance_name => 'LOGGING'}) end end @@ -93,12 +93,12 @@ def stub_connection end it 'should add integrated security to the connection string if admin and password are empty' do - @connection.expects(:Open).with('Provider=SQLOLEDB.1;Initial Catalog=master;Application Name=Puppet;Data Source=localhost;Integrated Security=SSPI') + @connection.expects(:Open).with('Provider=SQLOLEDB.1;Initial Catalog=master;Application Name=Puppet;Data Source=.;Integrated Security=SSPI') subject.open_and_run_command('query', {:admin_user => '', :admin_pass => '', :admin_login_type => 'WINDOWS_LOGIN'}) end it 'should add integrated security to the connection string if admin and password are not defined' do - @connection.expects(:Open).with('Provider=SQLOLEDB.1;Initial Catalog=master;Application Name=Puppet;Data Source=localhost;Integrated Security=SSPI') + @connection.expects(:Open).with('Provider=SQLOLEDB.1;Initial Catalog=master;Application Name=Puppet;Data Source=.;Integrated Security=SSPI') subject.open_and_run_command('query', { :admin_login_type => 'WINDOWS_LOGIN' }) end end diff --git a/templates/create/database.sql.erb b/templates/create/database.sql.erb index 1ed52cc9..3ffdcbdd 100644 --- a/templates/create/database.sql.erb +++ b/templates/create/database.sql.erb @@ -11,7 +11,7 @@ CREATE DATABASE [<%= @db_name %>] CONTAINMENT = <%= @containment %> <% if @filespec_name && @filespec_filename -%> ON ( - NAME = <%= @filespec_name %>, + NAME = N'<%= @filespec_name %>', FILENAME = N'<%= @filespec_filename %>' <% if @filespec_size %>, SIZE = <%= @filespec_size %><% end %> <% if @filespec_maxsize %>, MAXSIZE = <%= @filespec_maxsize %><% end %> @@ -21,7 +21,7 @@ CREATE DATABASE [<%= @db_name %>] <% if @log_name && @log_filename -%> LOG ON ( - NAME = <%= @log_name %>, + NAME = N'<%= @log_name %>', FILENAME = N'<%= @log_filename %>' <% if @log_size %>, SIZE = <%= @log_size %> <% end %> <% if @log_maxsize %>, MAXSIZE = <%= @log_maxsize %><% end %> diff --git a/templates/delete/role.sql.erb b/templates/delete/role.sql.erb index 925fe6d3..1352aa7e 100644 --- a/templates/delete/role.sql.erb +++ b/templates/delete/role.sql.erb @@ -1,5 +1,18 @@ USE [<%= @database %>]; BEGIN + DECLARE @cmd AS NVARCHAR(MAX) = N''; + + SELECT @cmd = @cmd + ' + ALTER <% if @type == 'SERVER' %>SERVER <% end %>ROLE [<%= @role %>] DROP MEMBER ' + QUOTENAME(members.[name]) + ';' + FROM sys.<%= @type.downcase %>_role_members AS rolemembers + JOIN sys.<%= @type.downcase %>_principals AS roles + ON roles.[principal_id] = rolemembers.[role_principal_id] + JOIN sys.<%= @type.downcase %>_principals AS members + ON members.[principal_id] = rolemembers.[member_principal_id] + WHERE roles.name = '<%= @role %>' + + EXEC(@cmd); + DROP <% if @type == 'SERVER' %>SERVER <% end %>ROLE [<%= @role %>]; END <%= scope.function_template(['sqlserver/query/role_exists.sql.erb']) %> diff --git a/templates/query/role/member_exists.sql.erb b/templates/query/role/member_exists.sql.erb index 6072a0da..d532083b 100644 --- a/templates/query/role/member_exists.sql.erb +++ b/templates/query/role/member_exists.sql.erb @@ -8,11 +8,15 @@ DECLARE SET @member = '<%= member %>'; SET @error_msg = 'The member [<%= member %>] is <% if @ensure == 'present'%>not <% end %>a member of the role [<%=@role %>]'; <%= scope.function_template(['sqlserver/snippets/role/member_exists.sql.erb']) -%> - THROW 51000, @error_msg, 10 -<% end %> + THROW 51000, @error_msg, 10; +<% end -%> <% if @members_purge %> -<%= scope.function_template(['sqlserver/snippets/role/populate_purge_members.sql.erb']) %> -IF 0 != (SELECT COUNT(*) FROM @purge_members) - THROW 51000, 'Unlisted Members in Role, will be purged', 10 -<% end %> +IF EXISTS( +SELECT m.name FROM sys.<%= @type.downcase %>_role_members rm + JOIN sys.<%= @type.downcase %>_principals r ON rm.role_principal_id = r.principal_id + JOIN sys.<%= @type.downcase %>_principals m ON rm.member_principal_id = m.principal_id + WHERE r.name = '<%= @role %>' + <% if !@members.empty? %>AND m.name NOT IN (<%= @members.collect{|m| "'#{m}'"}.join(',') %>)<% end %> +) THROW 51000, 'Unlisted Members in Role, will be purged', 10; +<% end -%>