diff --git a/.fixtures.yml b/.fixtures.yml index bbca4d63..4b983500 100644 --- a/.fixtures.yml +++ b/.fixtures.yml @@ -4,5 +4,7 @@ fixtures: puppet_agent: 'https://github.com/puppetlabs/puppetlabs-puppet_agent.git' provision: 'https://github.com/puppetlabs/provision.git' stdlib: 'https://github.com/puppetlabs/puppetlabs-stdlib.git' + mount_iso: 'https://github.com/puppetlabs/puppetlabs-mount_iso.git' + archive: 'https://github.com/voxpupuli/puppet-archive.git' symlinks: sqlserver: "#{source_dir}" diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml new file mode 100644 index 00000000..2893114d --- /dev/null +++ b/.github/workflows/nightly.yml @@ -0,0 +1,111 @@ +name: "nightly" + +on: + schedule: + - cron: '0 0 * * *' + +env: + SERVICE_URL: https://facade-maint-config-windows-use-ssh-6f3kfepqcq-ew.a.run.app/v1/provision + +jobs: + setup_matrix: + name: "Setup Test Matrix" + runs-on: ubuntu-latest + outputs: + matrix: ${{ steps.get-matrix.outputs.matrix }} + + steps: + + - name: Checkout Source + uses: actions/checkout@v2 + if: ${{ github.repository_owner == 'puppetlabs' }} + + - name: Activate Ruby 2.7 + uses: ruby/setup-ruby@v1 + if: ${{ github.repository_owner == 'puppetlabs' }} + with: + ruby-version: "2.7" + bundler-cache: true + + - name: Print bundle environment + if: ${{ github.repository_owner == 'puppetlabs' }} + run: | + echo ::group::bundler environment + bundle env + echo ::endgroup:: + + - name: Setup Acceptance Test Matrix + id: get-matrix + run: | + bundle exec matrix_from_metadata_v2 + bundle exec matrix_from_metadata_v2 + + Acceptance: + name: "${{matrix.platforms.label}}, ${{matrix.collection}}" + needs: + - setup_matrix + if: ${{ needs.setup_matrix.outputs.matrix != '{}' }} + + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: ${{fromJson(needs.setup_matrix.outputs.matrix)}} + + + steps: + + - name: Checkout Source + uses: actions/checkout@v2 + + - name: Activate Ruby 2.7 + uses: ruby/setup-ruby@v1 + with: + ruby-version: "2.7" + bundler-cache: true + + - name: Print bundle environment + run: | + echo ::group::bundler environment + bundle env + echo ::endgroup:: + + - name: Provision test environment + run: | + bundle exec rake "litmus:provision[${{matrix.platforms.provider}},${{ matrix.platforms.image }}]" + + - name: Install agent + run: | + bundle exec rake 'litmus:install_agent[${{ matrix.collection }}]' + + - name: Install module + run: | + bundle exec rake 'litmus:install_module' + + - name: Authenitcate with GCP + run: | + echo '${{ secrets.GCP_CONNECTION }}' >> creds.json + bundle exec bolt file upload creds.json C:\\creds.json --targets ssh_nodes --inventoryfile spec/fixtures/litmus_inventory.yaml + bundle exec bolt command run "gcloud auth activate-service-account --key-file C:\\creds.json" --targets ssh_nodes --inventoryfile spec/fixtures/litmus_inventory.yaml + + - name: Download OS ISO + run: | + bundle exec bolt command run 'gsutil -q cp gs://artifactory-modules/windows/en_windows_server_2019_updated_july_2020_x64_dvd_94453821.iso C:\\' --targets ssh_nodes --inventoryfile spec/fixtures/litmus_inventory.yaml + + - name: Download SQLServer ISO + run: | + bundle exec bolt command run 'gsutil -q cp gs://artifactory-modules/puppetlabs-sqlserver/SQLServer2019CTP2.4-x64-ENU.iso C:\\' --targets ssh_nodes --inventoryfile spec/fixtures/litmus_inventory.yaml + + - name: Set Environment Variable + run: | + pass=`grep -oP '(?<=password: ).*' spec/fixtures/litmus_inventory.yaml` + bundle exec bolt command run "[Environment]::SetEnvironmentVariable('pass', '$pass', 'Machine')" --targets ssh_nodes --inventoryfile spec/fixtures/litmus_inventory.yaml + + - name: Run acceptance tests + run: | + bundle exec rake 'litmus:acceptance:parallel' + + - name: Remove test environment + if: ${{ always() }} + continue-on-error: true + run: | + bundle exec rake 'litmus:tear_down' diff --git a/.github/workflows/pr_test.yml b/.github/workflows/pr_test.yml new file mode 100644 index 00000000..e8657086 --- /dev/null +++ b/.github/workflows/pr_test.yml @@ -0,0 +1,109 @@ +name: "PR Testing" + +on: [pull_request] + +env: + SERVICE_URL: https://facade-maint-config-windows-use-ssh-6f3kfepqcq-ew.a.run.app/v1/provision + +jobs: + setup_matrix: + name: "Setup Test Matrix" + runs-on: ubuntu-latest + outputs: + matrix: ${{ steps.get-matrix.outputs.matrix }} + + steps: + + - name: Checkout Source + uses: actions/checkout@v2 + if: ${{ github.repository_owner == 'puppetlabs' }} + + - name: Activate Ruby 2.7 + uses: ruby/setup-ruby@v1 + if: ${{ github.repository_owner == 'puppetlabs' }} + with: + ruby-version: "2.7" + bundler-cache: true + + - name: Print bundle environment + if: ${{ github.repository_owner == 'puppetlabs' }} + run: | + echo ::group::bundler environment + bundle env + echo ::endgroup:: + + - name: Setup Acceptance Test Matrix + id: get-matrix + run: | + bundle exec matrix_from_metadata_v2 + bundle exec matrix_from_metadata_v2 + + Acceptance: + name: "${{matrix.platforms.label}}, ${{matrix.collection}}" + needs: + - setup_matrix + if: ${{ needs.setup_matrix.outputs.matrix != '{}' }} + + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: ${{fromJson(needs.setup_matrix.outputs.matrix)}} + + + steps: + + - name: Checkout Source + uses: actions/checkout@v2 + + - name: Activate Ruby 2.7 + uses: ruby/setup-ruby@v1 + with: + ruby-version: "2.7" + bundler-cache: true + + - name: Print bundle environment + run: | + echo ::group::bundler environment + bundle env + echo ::endgroup:: + + - name: Provision test environment + run: | + bundle exec rake "litmus:provision[${{matrix.platforms.provider}},${{ matrix.platforms.image }}]" + + - name: Install agent + run: | + bundle exec rake 'litmus:install_agent[${{ matrix.collection }}]' + + - name: Install module + run: | + bundle exec rake 'litmus:install_module' + + - name: Authenitcate with GCP + run: | + echo '${{ secrets.GCP_CONNECTION }}' >> creds.json + bundle exec bolt file upload creds.json C:\\creds.json --targets ssh_nodes --inventoryfile spec/fixtures/litmus_inventory.yaml + bundle exec bolt command run "gcloud auth activate-service-account --key-file C:\\creds.json" --targets ssh_nodes --inventoryfile spec/fixtures/litmus_inventory.yaml + + - name: Download OS ISO + run: | + bundle exec bolt command run 'gsutil -q cp gs://artifactory-modules/windows/en_windows_server_2019_updated_july_2020_x64_dvd_94453821.iso C:\\' --targets ssh_nodes --inventoryfile spec/fixtures/litmus_inventory.yaml + + - name: Download SQLServer ISO + run: | + bundle exec bolt command run 'gsutil -q cp gs://artifactory-modules/puppetlabs-sqlserver/SQLServer2019CTP2.4-x64-ENU.iso C:\\' --targets ssh_nodes --inventoryfile spec/fixtures/litmus_inventory.yaml + + - name: Set Environment Variable + run: | + pass=`grep -oP '(?<=password: ).*' spec/fixtures/litmus_inventory.yaml` + bundle exec bolt command run "[Environment]::SetEnvironmentVariable('pass', '$pass', 'Machine')" --targets ssh_nodes --inventoryfile spec/fixtures/litmus_inventory.yaml + + - name: Run acceptance tests + run: | + bundle exec rake 'litmus:acceptance:parallel' + + - name: Remove test environment + if: ${{ always() }} + continue-on-error: true + run: | + bundle exec rake 'litmus:tear_down' diff --git a/lib/puppet/provider/sqlserver.rb b/lib/puppet/provider/sqlserver.rb index 199e75c8..9cf5f9bf 100644 --- a/lib/puppet/provider/sqlserver.rb +++ b/lib/puppet/provider/sqlserver.rb @@ -19,7 +19,8 @@ class Puppet::Provider::Sqlserver < Puppet::Provider # rubocop:disable Style/Doc end def try_execute(command, msg = nil, obfuscate_strings = nil, acceptable_exit_codes = [0]) - res = execute(command.compact, failonfail: false) + command&.compact + res = execute(command, failonfail: false) unless acceptable_exit_codes.include?(res.exitstatus) msg = "Failure occured when trying to install SQL Server #{@resource[:name]}" if msg.nil? diff --git a/lib/puppet/provider/sqlserver_features/mssql.rb b/lib/puppet/provider/sqlserver_features/mssql.rb index 5f84fe2e..64e4e559 100644 --- a/lib/puppet/provider/sqlserver_features/mssql.rb +++ b/lib/puppet/provider/sqlserver_features/mssql.rb @@ -65,6 +65,7 @@ def modify_features(action, features) '/IACCEPTSQLSERVERLICENSETERMS', "/FEATURES=#{features.join(',')}"] if action == 'install' + cmd_args << '/UPDATEENABLED=False' if not_nil_and_not_empty?(@resource[:is_svc_account]) cmd_args << "/ISSVCACCOUNT=#{@resource[:is_svc_account]}" end diff --git a/lib/puppet/provider/sqlserver_instance/mssql.rb b/lib/puppet/provider/sqlserver_instance/mssql.rb index 06324771..4fd04c8a 100644 --- a/lib/puppet/provider/sqlserver_instance/mssql.rb +++ b/lib/puppet/provider/sqlserver_instance/mssql.rb @@ -142,6 +142,8 @@ def basic_cmd_args(features, action) '/IACCEPTSQLSERVERLICENSETERMS', "/INSTANCENAME=#{@resource[:name]}"] cmd_args << "/FEATURES=#{features.join(',')}" unless features.empty? + cmd_args << '/UPDATEENABLED=False' if action == 'install' + cmd_args end def build_cmd_args(features, action = 'install') diff --git a/lib/puppet_x/sqlserver/features.rb b/lib/puppet_x/sqlserver/features.rb index 7a1cdae2..b388ed77 100644 --- a/lib/puppet_x/sqlserver/features.rb +++ b/lib/puppet_x/sqlserver/features.rb @@ -149,7 +149,7 @@ def self.get_shared_features(version) '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_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 diff --git a/lib/puppet_x/sqlserver/sql_connection.rb b/lib/puppet_x/sqlserver/sql_connection.rb index 98ba18ff..c35c86e2 100644 --- a/lib/puppet_x/sqlserver/sql_connection.rb +++ b/lib/puppet_x/sqlserver/sql_connection.rb @@ -31,7 +31,7 @@ def open(config) def get_connection_string(config) params = { - 'Provider' => 'SQLNCLI11', + 'Provider' => 'MSOLEDBSQL', 'Initial Catalog' => config[:database] || 'master', 'Application Name' => 'Puppet', 'Data Source' => '.', @@ -50,8 +50,8 @@ def get_connection_string(config) # SQL Server based authentication raise ArgumentError, _('admin_user must not be empty or nil') unless admin_user != '' raise ArgumentError, _('admin_pass must not be empty or nil') unless admin_pass != '' - params.store('User ID', admin_user) - params.store('Password', admin_pass) + params.store('UID', admin_user) + params.store('PWD', admin_pass) end if !config[:instance_name].nil? && config[:instance_name] !~ %r{^MSSQLSERVER$} diff --git a/spec/acceptance/sqlserver_config_spec.rb b/spec/acceptance/sqlserver_config_spec.rb index df42f495..8768be73 100644 --- a/spec/acceptance/sqlserver_config_spec.rb +++ b/spec/acceptance/sqlserver_config_spec.rb @@ -11,18 +11,18 @@ describe 'sqlserver::config test' do def ensure_sqlserver_instance(inst_name, ensure_val = 'present') + user = Helper.instance.run_shell('$env:UserName').stdout.chomp pp = <<-MANIFEST sqlserver_instance{'#{inst_name}': ensure => '#{ensure_val}', source => 'H:', features => ['DQ', 'FullText', 'Replication', 'SQLEngine'], - sql_sysadmin_accounts => ['Administrator'], + sql_sysadmin_accounts => ['#{user}'], security_mode => 'SQL', sa_pwd => 'Pupp3t1@', windows_feature_source => 'I:\\sources\\sxs', } MANIFEST - apply_manifest(pp, catch_failures: true) end @@ -32,7 +32,7 @@ def ensure_sqlserver_instance(inst_name, ensure_val = 'present') ensure_sqlserver_instance(inst_name) # get credentials for new config - @admin_user = 'admin' + SecureRandom.hex(2) + @admin_user = 'test_user' + SecureRandom.hex(2) @admin_pass = 'Pupp3t1@' # get database user @@ -55,14 +55,14 @@ def ensure_sqlserver_instance(inst_name, ensure_val = 'present') instance => '#{inst_name}', login_type => 'SQL_LOGIN', login => '#{@admin_user}', - password => Sensitive('#{@admin_pass}'), + password => '#{@admin_pass}', svrroles => {'sysadmin' => 1}, } MANIFEST apply_manifest(pp, catch_failures: true) end - it 'Validate New Config WITH using instance_name in sqlserver::config' do + it 'Validate New Config WITH instance_name in sqlserver::config' do pp = <<-MANIFEST sqlserver::config{'#{inst_name}': admin_user => Sensitive('#{@admin_user}'), @@ -77,11 +77,11 @@ def ensure_sqlserver_instance(inst_name, ensure_val = 'present') end it 'Validate new login and database actualy created' do - hostname = ENV['TARGET_HOST'] + hostname = Helper.instance.run_shell('hostname').stdout.upcase.strip query = "USE #{db_name}; SELECT * from master..sysdatabases WHERE name = '#{db_name}'" run_sql_query(query: query, server: hostname, instance: inst_name, \ - sql_admin_user: @admin_user, sql_admin_pass: @admin_pass, expected_row_count: 1) + 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 diff --git a/spec/acceptance/sqlserver_database_spec.rb b/spec/acceptance/sqlserver_database_spec.rb index 9de86d9f..a49bc6d2 100644 --- a/spec/acceptance/sqlserver_database_spec.rb +++ b/spec/acceptance/sqlserver_database_spec.rb @@ -47,8 +47,7 @@ def run_sql_query_opts(query, expected_row_count) } sqlserver_tsql{'testsqlserver_tsql': instance => 'MSSQLSERVER', - database => '#{@db_name}', - command => "CREATE TABLE #{@table_name} (id INT, name VARCHAR(20), email VARCHAR(20));", + command => "use #{@db_name}; CREATE TABLE #{@table_name} (id INT, name VARCHAR(20), email VARCHAR(20));", require => Sqlserver::Database['#{@db_name}'], } MANIFEST @@ -101,8 +100,7 @@ def run_sql_query_opts(query, expected_row_count) } sqlserver_tsql{'testsqlserver_tsql': instance => 'MSSQLSERVER', - database => '#{@db_name}', - command => "CREATE TABLE #{@table_name} (id INT, name VARCHAR(20), email VARCHAR(20));", + command => "use #{@db_name};CREATE TABLE #{@table_name} (id INT, name VARCHAR(20), email VARCHAR(20));", require => Sqlserver::Database['#{@db_name}'], } MANIFEST @@ -132,8 +130,7 @@ def run_sql_query_opts(query, expected_row_count) } sqlserver_tsql{'testsqlserver_tsql': instance => 'MSSQLSERVER', - database => '#{@db_name}', - command => "CREATE TABLE #{@table_name} (id INT, name VARCHAR(20), email VARCHAR(20));", + command => "use #{@db_name};CREATE TABLE #{@table_name} (id INT, name VARCHAR(20), email VARCHAR(20));", require => Sqlserver::Database['#{@db_name}'], } MANIFEST @@ -170,8 +167,7 @@ def run_sql_query_opts(query, expected_row_count) } sqlserver_tsql{'testsqlserver_tsql': instance => 'MSSQLSERVER', - database => '#{@db_name}', - command => "CREATE TABLE #{@table_name} (id INT, name VARCHAR(20), email VARCHAR(20));", + command => "use #{@db_name};CREATE TABLE #{@table_name} (id INT, name VARCHAR(20), email VARCHAR(20));", require => Sqlserver::Database['#{@db_name}'], } MANIFEST @@ -209,8 +205,7 @@ def run_sql_query_opts(query, expected_row_count) } sqlserver_tsql{'testsqlserver_tsql': instance => 'MSSQLSERVER', - database => '#{@db_name}', - command => "CREATE TABLE #{@table_name} (id INT, name VARCHAR(20), email VARCHAR(20));", + command => "use #{@db_name};CREATE TABLE #{@table_name} (id INT, name VARCHAR(20), email VARCHAR(20));", require => Sqlserver::Database['#{@db_name}'], } MANIFEST @@ -248,8 +243,7 @@ def run_sql_query_opts(query, expected_row_count) } sqlserver_tsql{'testsqlserver_tsql': instance => 'MSSQLSERVER', - database => '#{@db_name}', - command => "CREATE TABLE #{@table_name} (id INT, name VARCHAR(20), email VARCHAR(20));", + command => "use #{@db_name};CREATE TABLE #{@table_name} (id INT, name VARCHAR(20), email VARCHAR(20));", require => Sqlserver::Database['#{@db_name}'], } MANIFEST @@ -287,8 +281,7 @@ def run_sql_query_opts(query, expected_row_count) } sqlserver_tsql{'testsqlserver_tsql': instance => 'MSSQLSERVER', - database => '#{@db_name}', - command => "CREATE TABLE #{@table_name} (id INT, name VARCHAR(20), email VARCHAR(20));", + command => "use #{@db_name};CREATE TABLE #{@table_name} (id INT, name VARCHAR(20), email VARCHAR(20));", require => Sqlserver::Database['#{@db_name}'], } MANIFEST diff --git a/spec/acceptance/sqlserver_instance_spec.rb b/spec/acceptance/sqlserver_instance_spec.rb index ed59d2d2..b9e25369 100644 --- a/spec/acceptance/sqlserver_instance_spec.rb +++ b/spec/acceptance/sqlserver_instance_spec.rb @@ -11,7 +11,13 @@ def new_random_instance_name describe 'sqlserver_instance' do version = sql_version? - def ensure_sqlserver_instance(features, inst_name, ensure_val = 'present', sysadmin_accounts = "['Administrator']") + def ensure_sqlserver_instance(features, inst_name, ensure_val = 'present', sysadmin_accounts = []) + user = Helper.instance.run_shell('$env:UserName').stdout.chomp + sysadmin_accounts << user + # If no password env variable set (by CI), then default to vagrant + password = Helper.instance.run_shell('$env:pass').stdout.chomp + password = password.empty? ? 'vagrant' : password + pp = <<-MANIFEST sqlserver_instance{'#{inst_name}': name => '#{inst_name}', @@ -21,8 +27,8 @@ def ensure_sqlserver_instance(features, inst_name, ensure_val = 'present', sysad sa_pwd => 'Pupp3t1@', features => #{features}, sql_sysadmin_accounts => #{sysadmin_accounts}, - agt_svc_account => 'Administrator', - agt_svc_password => 'Qu@lity!', + agt_svc_account => '#{user}', + agt_svc_password => '#{password}', windows_feature_source => 'I:\\sources\\sxs', } MANIFEST @@ -54,7 +60,8 @@ def sql_query_is_user_sysadmin(username) context 'Create an instance' do before(:context) do # Use a username with a space to test argument parsing works correctly - @extra_admin_user = 'Extra SQLAdmin' + @extra_admin_user = 'ExtraSQLAdmin' + @user = Helper.instance.run_shell('$env:UserName').stdout.chomp pp = <<-MANIFEST user { '#{@extra_admin_user}': ensure => present, @@ -78,15 +85,15 @@ def sql_query_is_user_sysadmin(username) it "create #{inst_name} instance" do host_computer_name = run_shell('CMD /C ECHO %COMPUTERNAME%').stdout.chomp - ensure_sqlserver_instance(features, inst_name, 'present', "['Administrator','#{host_computer_name}\\#{@extra_admin_user}']") + ensure_sqlserver_instance(features, inst_name, 'present', ["#{host_computer_name}\\#{@extra_admin_user}"]) validate_sql_install(version: version) do |r| expect(r.stdout).to match(%r{#{Regexp.new(inst_name)}}) end end - it "#{inst_name} instance has Administrator as a sysadmin" do - run_sql_query(run_sql_query_opts(inst_name, sql_query_is_user_sysadmin('Administrator'), 1)) + it "#{inst_name} instance has logged in user as an Administrator" do + run_sql_query(run_sql_query_opts(inst_name, sql_query_is_user_sysadmin(@user), 1)) end it "#{inst_name} instance has ExtraSQLAdmin as a sysadmin" do diff --git a/spec/acceptance/sqlserver_login_spec.rb b/spec/acceptance/sqlserver_login_spec.rb index b0682aa5..992901e4 100644 --- a/spec/acceptance/sqlserver_login_spec.rb +++ b/spec/acceptance/sqlserver_login_spec.rb @@ -82,7 +82,7 @@ def create_login_manifest(testcase, login_name, login_password, options = {}) @windows_user = 'User' + SecureRandom.hex(4) @windows_group = 'Group' + SecureRandom.hex(4) - host_shortname = run_shell('hostname').stdout.upcase.strip # Require the NETBIOS name for later database searches + host_shortname = Helper.instance.run_shell('$env:computername').stdout.upcase.strip # Require the NETBIOS name for later database searches @login_windows_user = host_shortname + '\\' + @windows_user @login_windows_group = host_shortname + '\\' + @windows_group diff --git a/spec/acceptance/sqlserver_role_spec.rb b/spec/acceptance/sqlserver_role_spec.rb index 7fc672d3..b05da34b 100644 --- a/spec/acceptance/sqlserver_role_spec.rb +++ b/spec/acceptance/sqlserver_role_spec.rb @@ -4,7 +4,7 @@ require 'securerandom' require 'erb' -hostname = ENV['TARGET_HOST'] +hostname = Helper.instance.run_shell('hostname').stdout.upcase.strip # database name db_name = ('DB' + SecureRandom.hex(4)).upcase diff --git a/spec/acceptance/sqlserver_user_spec.rb b/spec/acceptance/sqlserver_user_spec.rb index 97d91cc4..4ea795c9 100644 --- a/spec/acceptance/sqlserver_user_spec.rb +++ b/spec/acceptance/sqlserver_user_spec.rb @@ -4,7 +4,7 @@ require 'securerandom' require 'erb' -hostname = ENV['TARGET_HOST'] +hostname = Helper.instance.run_shell('hostname').stdout.upcase.strip # database name db_name = ('DB' + SecureRandom.hex(4)).upcase diff --git a/spec/acceptance/z_last_sqlserver_features_spec.rb b/spec/acceptance/z_last_sqlserver_features_spec.rb index a7fa8cd9..7ea29b7c 100644 --- a/spec/acceptance/z_last_sqlserver_features_spec.rb +++ b/spec/acceptance/z_last_sqlserver_features_spec.rb @@ -8,6 +8,11 @@ describe 'sqlserver_features', if: version.to_i != 2012 do def ensure_sql_features(features, ensure_val = 'present') + user = Helper.instance.run_shell('$env:UserName').stdout.chomp + # If no password env variable set (by CI), then default to vagrant + password = Helper.instance.run_shell('$env:pass').stdout.chomp + password = password.empty? ? 'vagrant' : password + pp = <<-MANIFEST sqlserver::config{ 'MSSQLSERVER': admin_pass => '<%= SQL_ADMIN_PASS %>', @@ -16,8 +21,8 @@ def ensure_sql_features(features, ensure_val = 'present') sqlserver_features{ 'MSSQLSERVER': ensure => #{ensure_val}, source => 'H:', - is_svc_account => "$::hostname\\\\Administrator", - is_svc_password => 'Qu@lity!', + is_svc_account => "#{user}", + is_svc_password => '#{password}', features => #{features}, windows_feature_source => 'I:\\sources\\sxs', } @@ -27,6 +32,7 @@ def ensure_sql_features(features, ensure_val = 'present') end def bind_and_apply_failing_manifest(features, ensure_val = 'present') + user = Helper.instance.run_shell('$env:UserName').stdout.chomp pp = <<-MANIFEST sqlserver::config{ 'MSSQLSERVER': admin_pass => '<%= SQL_ADMIN_PASS %>', @@ -35,7 +41,7 @@ def bind_and_apply_failing_manifest(features, ensure_val = 'present') sqlserver_features{ 'MSSQLSERVER': ensure => #{ensure_val}, source => 'H:', - is_svc_account => "$::hostname\\\\Administrator", + is_svc_account => "#{user}", features => #{features}, } MANIFEST @@ -136,7 +142,7 @@ def bind_and_apply_failing_manifest(features, ensure_val = 'present') bind_and_apply_failing_manifest(features) end - it 'fails when ADV_SSMS is supplied but SSMS is not - FM-2712' do + it 'fails when ADV_SSMS is supplied but SSMS is not - FM-2712', unless: version.to_i >= 2016 do pending('error not shown on Sql Server 2014') if version .to_i == 2014 features = ['BC', 'Conn', 'ADV_SSMS', 'SDK'] bind_and_apply_failing_manifest(features) @@ -149,11 +155,12 @@ def bind_and_apply_failing_manifest(features, ensure_val = 'present') features = ['BC', 'Conn', 'SDK', 'IS', 'MDS', 'DQC'] def remove_sql_instance + user = Helper.instance.run_shell('$env:UserName').stdout.chomp pp = <<-MANIFEST sqlserver_instance{'MSSQLSERVER': ensure => absent, source => 'H:', - sql_sysadmin_accounts => ['Administrator'], + sql_sysadmin_accounts => ['#{user}'], } MANIFEST idempotent_apply(pp) @@ -171,7 +178,7 @@ def remove_sql_instance ensure_sql_features(features) validate_sql_install(version: version) do |r| - # SQL Server 2016 will not install the client tools features. + # SQL Server 2016+ will not install the client tools features. expect(r.stdout).not_to match(%r{MSSQLSERVER\s+Database Engine Services}) expect(r.stdout).not_to match(%r{MSSQLSERVER\s+SQL Server Replication}) expect(r.stdout).not_to match(%r{MSSQLSERVER\s+Data Quality Services}) diff --git a/spec/spec_helper_acceptance_local.rb b/spec/spec_helper_acceptance_local.rb index ab0aa62d..5a827a27 100644 --- a/spec/spec_helper_acceptance_local.rb +++ b/spec/spec_helper_acceptance_local.rb @@ -9,7 +9,7 @@ class Helper end WIN_ISO_ROOT = 'https://artifactory.delivery.puppetlabs.net/artifactory/generic__iso/iso/windows' -WIN_2012R2_ISO = 'en_windows_server_2012_r2_with_update_x64_dvd_6052708.iso' +WIN_2019_ISO = 'en_windows_server_2019_updated_july_2020_x64_dvd_94453821.iso' QA_RESOURCE_ROOT = 'https://artifactory.delivery.puppetlabs.net/artifactory/generic__iso/iso/SQLServer' SQL_2019_ISO = 'SQLServer2019CTP2.4-x64-ENU.iso' SQL_2017_ISO = 'SQLServer2017-x64-ENU.iso' @@ -18,6 +18,7 @@ class Helper SQL_2012_ISO = 'SQLServer2012SP1-FullSlipstream-ENU-x64.iso' SQL_ADMIN_USER = 'sa' SQL_ADMIN_PASS = 'Pupp3t1@' +USER = Helper.instance.run_shell('$env:UserName').stdout.chomp RSpec.configure do |c| c.before(:suite) do @@ -26,9 +27,13 @@ class Helper iso_opts = { folder: WIN_ISO_ROOT, - file: WIN_2012R2_ISO, + file: WIN_2019_ISO, drive_letter: 'I', } + # Allows litmus to use SSH, by explicitly setting specinfra + # os family to windows (would fail when using ssh on windows) + set :os, family: 'windows', release: nil, arch: nil + mount_iso(iso_opts) base_install(sql_version?) @@ -49,14 +54,18 @@ def node_vars? def sql_version? vars = node_vars? - - if vars['sqlversion'] - return vars['sqlversion'].match(%r{sqlserver_(.*)})[1] + unless vars.nil? + if vars['sqlversion'] + return vars['sqlversion'].match(%r{sqlserver_(.*)})[1] + end end # Return's a default version if none was given - '2016' + '2019' end +# this mounts the OS and SQL iso +# OS iso mounts to I drive +# SQL iso mounts to H drive def mount_iso(opts = {}) folder = opts[:folder] file = opts[:file] @@ -127,8 +136,9 @@ def install_sqlserver(features) features => #{features}, security_mode => 'SQL', sa_pwd => 'Pupp3t1@', - sql_sysadmin_accounts => ['Administrator'], + sql_sysadmin_accounts => ['#{USER}'], install_switches => { + 'UPDATEENABLED' => 'False', 'TCPENABLED' => 1, 'SQLBACKUPDIR' => 'C:\\MSSQLSERVER\\backupdir', 'SQLTEMPDBDIR' => 'C:\\MSSQLSERVER\\tempdbdir', @@ -145,7 +155,7 @@ def install_sqlserver(features) def run_sql_query(opts = {}, &block) query = opts[:query] - server = opts[:server] + server = opts[:server] ||= '.' instance = opts[:instance] sql_admin_pass = opts[:sql_admin_pass] ||= SQL_ADMIN_PASS sql_admin_user = opts[:sql_admin_user] ||= SQL_ADMIN_USER @@ -162,16 +172,16 @@ def run_sql_query(opts = {}, &block) # sqlcmd has problem authenticate to sqlserver if the instance is the default one MSSQLSERVER # Below is a work-around for it (remove "-S server\instance" from the connection string) if instance.nil? || instance == 'MSSQLSERVER' - powershell.gsub!("-S #{server}\\#{instance}", '') + powershell.dup.gsub!("-S #{server}\\#{instance}", '') end Tempfile.open 'tmp.ps1' do |tempfile| File.open(tempfile.path, 'w') { |file| file.puts powershell } - bolt_upload_file(tempfile.path, 'C:/cygwin64/home/Administrator/tmp.ps1') + bolt_upload_file(tempfile.path, "c:\\users\\#{USER}\\tmp.ps1") end # create_remote_file('tmp.ps1', powershell) - Helper.instance.run_shell('powershell -NonInteractive -NoLogo -File "C:\\cygwin64\\home\\Administrator\\tmp.ps1"') do |r| + Helper.instance.run_shell("powershell -NonInteractive -NoLogo -File 'c:\\users\\#{USER}\\tmp.ps1'") do |r| match = %r{(\d*) rows affected}.match(r.stdout) raise 'Could not match number of rows for SQL query' unless match rows_observed = match[1] @@ -192,11 +202,11 @@ def validate_sql_install(opts = {}, &block) # NOTE: executing a fully qualified setup.exe quoted this way fails # but that can be circumvented by first changing directories - cmd = "cd \"#{setup_dir}\" && setup.exe /Action=RunDiscovery /q" - Helper.instance.run_shell("cmd.exe /c '#{cmd}'") + cmd = "cd \"#{setup_dir}\"; ./setup.exe /Action=RunDiscovery /q" + Helper.instance.run_shell(cmd) cmd = "type \"#{bootstrap_dir}\\Log\\Summary.txt\"" - result = Helper.instance. run_shell("cmd.exe /c '#{cmd}'") + result = Helper.instance.run_shell(cmd) return unless block case block.arity when 0 @@ -211,9 +221,15 @@ def get_install_paths(version) raise _('Valid version must be specified') unless vers.keys.include?(version) - dir = "%ProgramFiles%/Microsoft SQL Server/#{vers[version]}/Setup Bootstrap" - sql_directory = 'SQL' - sql_directory += 'Server' if version != '2017' - - [dir, "#{dir}\\#{sql_directory}#{version}"] + dir = "C://Program Files/Microsoft SQL Server/#{vers[version]}/Setup Bootstrap" + sql_directory = case version + when '2019' + "SQL#{version}CTP2.4" + when '2017' + "SQL#{version}" + else + "SQLServer#{version}" + end + + [dir, "#{dir}\\#{sql_directory}"] end diff --git a/spec/sql_testing_helpers.rb b/spec/sql_testing_helpers.rb index 0a0b0db3..4f147bf5 100644 --- a/spec/sql_testing_helpers.rb +++ b/spec/sql_testing_helpers.rb @@ -22,6 +22,7 @@ def mount_iso(host, opts = {}) end def install_sqlserver(host, opts = {}) + user = Helper.instance.run_shell('$env:UserName').stdout.chomp # this method installs SQl server on a given host features = opts[:features].map { |x| "'#{x}'" }.join(', ') pp = <<-MANIFEST @@ -30,8 +31,9 @@ def install_sqlserver(host, opts = {}) features => [ #{features} ], security_mode => 'SQL', sa_pwd => 'Pupp3t1@', - sql_sysadmin_accounts => ['Administrator'], + sql_sysadmin_accounts => ['#{user}'], install_switches => { + 'UPDATEENABLED' => 'False', 'TCPENABLED' => 1, 'SQLBACKUPDIR' => 'C:\\MSSQLSERVER\\backupdir', 'SQLTEMPDBDIR' => 'C:\\MSSQLSERVER\\tempdbdir', diff --git a/spec/unit/puppet/provider/sqlserver_instance_spec.rb b/spec/unit/puppet/provider/sqlserver_instance_spec.rb index 1ffcb38c..2378cd0e 100644 --- a/spec/unit/puppet/provider/sqlserver_instance_spec.rb +++ b/spec/unit/puppet/provider/sqlserver_instance_spec.rb @@ -57,7 +57,8 @@ def stub_uninstall(args, installed_features, exit_code = 0) '/Q', '/IACCEPTSQLSERVERLICENSETERMS', "/INSTANCENAME=#{execute_args[:name]}", - "/FEATURES=#{execute_args[:features].join(',')}"] + "/FEATURES=#{execute_args[:features].join(',')}", + '/UPDATEENABLED=False'] (execute_args.keys - ['ensure', 'loglevel', 'features', 'name', 'source', 'sql_sysadmin_accounts', 'sql_security_mode', 'install_switches'].map(&:to_sym)).sort.map do |key| cmd_args << "/#{resourcekey_to_cmdarg[key.to_s]}=\"#{@resource[key]}\"" end @@ -95,6 +96,7 @@ def stub_uninstall(args, installed_features, exit_code = 0) '/ACTION=install', '/Q', '/IACCEPTSQLSERVERLICENSETERMS', + '/UPDATEENABLED=False', "/INSTANCENAME=#{execute_args[:name]}", "/FEATURES=#{execute_args[:features].join(',')}"] (execute_args.keys - ['ensure', 'loglevel', 'features', 'name', 'source', 'sql_sysadmin_accounts', 'sql_security_mode', 'install_switches'].map(&:to_sym)).sort.map do |key| diff --git a/spec/unit/puppet/sqlserver_spec_helper.rb b/spec/unit/puppet/sqlserver_spec_helper.rb index 37186fdd..564ceb6a 100644 --- a/spec/unit/puppet/sqlserver_spec_helper.rb +++ b/spec/unit/puppet/sqlserver_spec_helper.rb @@ -23,6 +23,7 @@ def stub_modify_features(action, args, features, additional_switches = [], exit_ '/Q', '/IACCEPTSQLSERVERLICENSETERMS', "/FEATURES=#{features.join(',')}"] + cmds << '/UPDATEENABLED=False' if action == 'install' cmds << "/ISSVCACCOUNT=#{args[:is_svc_account]}" if args.key?(:is_svc_account) if args.key?(:is_svc_password) cmds << "/ISSVCPASSWORD=#{args[:is_svc_password]}" diff --git a/spec/unit/puppet_x/sql_connection_spec.rb b/spec/unit/puppet_x/sql_connection_spec.rb index 1f13fd1b..cca63f48 100644 --- a/spec/unit/puppet_x/sql_connection_spec.rb +++ b/spec/unit/puppet_x/sql_connection_spec.rb @@ -24,7 +24,7 @@ def stub_connection context 'command execution' do before :each do stub_connection - allow(@connection).to receive(:Open).with('Provider=SQLNCLI11;Initial Catalog=master;Application Name=Puppet;Data Source=.;DataTypeComptibility=80;User ID=sa;Password=Pupp3t1@') + allow(@connection).to receive(:Open).with('Provider=MSOLEDBSQL;Initial Catalog=master;Application Name=Puppet;Data Source=.;DataTypeComptibility=80;User ID=sa;Password=Pupp3t1@') end it 'does not raise an error but populate has_errors with message' do allow(@connection.Errors).to receive(:count).and_return(2) @@ -51,7 +51,7 @@ def stub_connection context 'Use default authentication' do it 'defauls to SQL_LOGIN if admin_login_type is not set' do - expect(@connection).to receive(:Open).with('Provider=SQLNCLI11;Initial Catalog=master;Application Name=Puppet;Data Source=.;DataTypeComptibility=80;User ID=sa;Password=Pupp3t1@') + expect(@connection).to receive(:Open).with('Provider=MSOLEDBSQL;Initial Catalog=master;Application Name=Puppet;Data Source=.;DataTypeComptibility=80;UID=sa;PWD=Pupp3t1@') subject.open_and_run_command('query', admin_user: 'sa', admin_pass: 'Pupp3t1@') end end @@ -74,11 +74,11 @@ def stub_connection end it 'does not add the default instance of MSSQLSERVER to connection string' do - expect(@connection).to receive(:Open).with('Provider=SQLNCLI11;Initial Catalog=master;Application Name=Puppet;Data Source=.;DataTypeComptibility=80;User ID=sa;Password=Pupp3t1@') + expect(@connection).to receive(:Open).with('Provider=MSOLEDBSQL;Initial Catalog=master;Application Name=Puppet;Data Source=.;DataTypeComptibility=80;UID=sa;PWD=Pupp3t1@') subject.open_and_run_command('query', admin_user: 'sa', admin_pass: 'Pupp3t1@', instance_name: 'MSSQLSERVER') end it 'adds a non default instance to connection string' do - expect(@connection).to receive(:Open).with('Provider=SQLNCLI11;Initial Catalog=master;Application Name=Puppet;Data Source=.\\LOGGING;DataTypeComptibility=80;User ID=sa;Password=Pupp3t1@') + expect(@connection).to receive(:Open).with('Provider=MSOLEDBSQL;Initial Catalog=master;Application Name=Puppet;Data Source=.\\LOGGING;DataTypeComptibility=80;UID=sa;PWD=Pupp3t1@') subject.open_and_run_command('query', admin_user: 'sa', admin_pass: 'Pupp3t1@', instance_name: 'LOGGING') end end @@ -101,12 +101,12 @@ def stub_connection end it 'adds integrated security to the connection string if admin and password are empty' do - expect(@connection).to receive(:Open).with('Provider=SQLNCLI11;Initial Catalog=master;Application Name=Puppet;Data Source=.;DataTypeComptibility=80;Integrated Security=SSPI') + expect(@connection).to receive(:Open).with('Provider=MSOLEDBSQL;Initial Catalog=master;Application Name=Puppet;Data Source=.;DataTypeComptibility=80;Integrated Security=SSPI') subject.open_and_run_command('query', admin_user: '', admin_pass: '', admin_login_type: 'WINDOWS_LOGIN') end it 'adds integrated security to the connection string if admin and password are not defined' do - expect(@connection).to receive(:Open).with('Provider=SQLNCLI11;Initial Catalog=master;Application Name=Puppet;Data Source=.;DataTypeComptibility=80;Integrated Security=SSPI') + expect(@connection).to receive(:Open).with('Provider=MSOLEDBSQL;Initial Catalog=master;Application Name=Puppet;Data Source=.;DataTypeComptibility=80;Integrated Security=SSPI') subject.open_and_run_command('query', admin_login_type: 'WINDOWS_LOGIN') end end