diff --git a/README.md b/README.md index 49741dbf..4d0d5e2b 100644 --- a/README.md +++ b/README.md @@ -391,6 +391,22 @@ Specifies a product key for SQL Server. Valid options: a string containing a val Default: `undef`. +##### `polybase_svc_account` + +*Only applicable 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` + +*Only applicable 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. diff --git a/lib/puppet/provider/sqlserver_instance/mssql.rb b/lib/puppet/provider/sqlserver_instance/mssql.rb index c2f48223..bb290de0 100644 --- a/lib/puppet/provider/sqlserver_instance/mssql.rb +++ b/lib/puppet/provider/sqlserver_instance/mssql.rb @@ -3,11 +3,28 @@ 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')) -INSTANCE_RESERVED_SWITCHES = - %w(AGTSVCACCOUNT AGTSVCPASSWORD ASSVCACCOUNT AGTSVCPASSWORD PID - RSSVCACCOUNT RSSVCPASSWORD SAPWD SECURITYMODE SQLSYSADMINACCOUNTS 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) @@ -74,6 +91,12 @@ def create 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? @@ -88,7 +111,7 @@ 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 + 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) @@ -121,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_instance.rb b/lib/puppet/type/sqlserver_instance.rb index 13e1d62c..66904bd3 100644 --- a/lib/puppet/type/sqlserver_instance.rb +++ b/lib/puppet/type/sqlserver_instance.rb @@ -109,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/spec/unit/puppet/provider/sqlserver_instance_spec.rb b/spec/unit/puppet/provider/sqlserver_instance_spec.rb index 09ef7a34..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,32 +38,6 @@ 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 = {}| - it { - execute_args = args.merge(munged_values) - @resource = Puppet::Type::Sqlserver_instance.new(args) - @provider = provider_class.new(@resource) - - stub_powershell_call(subject) - stub_source_which_call args[:source] - - cmd_args = ["#{execute_args[:source]}/setup.exe", - "/ACTION=install", - '/Q', - '/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]}\"" - 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) - @provider.create - } - end shared_examples 'create' do |exit_code, warning_matcher| it { execute_args = args.merge(munged_values) @@ -64,7 +54,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" @@ -106,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" @@ -154,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 @@ -178,6 +169,33 @@ def stub_uninstall(args, installed_features, exit_code = 0) 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/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_instance_spec.rb b/spec/unit/puppet/type/sqlserver_instance_spec.rb index fb93d3dd..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) @@ -54,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