diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index fe7a8b12..cdd65d22 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -5,7 +5,7 @@ "settings": { "terminal.integrated.profiles.linux": { "bash": { - "path": "bash", + "path": "bash" } } }, 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/auto_release.yml b/.github/workflows/auto_release.yml index f4aed440..f3ee602e 100644 --- a/.github/workflows/auto_release.yml +++ b/.github/workflows/auto_release.yml @@ -28,13 +28,13 @@ jobs: echo STEP_START=$(date +%s) >> $GITHUB_ENV - name: "Checkout Source" if: ${{ github.repository_owner == 'puppetlabs' }} - uses: actions/checkout@v2 + uses: actions/checkout@v3 with: fetch-depth: 0 persist-credentials: false - name: "PDK Release prep" - uses: docker://puppet/iac_release:ci + uses: docker://puppet/pdk:2.6.1.0 with: args: 'release prep --force' env: diff --git a/.github/workflows/mend.yml b/.github/workflows/mend.yml new file mode 100644 index 00000000..b4100a5a --- /dev/null +++ b/.github/workflows/mend.yml @@ -0,0 +1,15 @@ +name: "mend" + +on: + pull_request: + branches: + - "main" + schedule: + - cron: "0 0 * * *" + workflow_dispatch: + +jobs: + + mend: + uses: "puppetlabs/cat-github-actions/.github/workflows/mend_ruby.yml@main" + secrets: "inherit" 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/.github/workflows/release.yml b/.github/workflows/release.yml index 1509f6e9..d9604ad6 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -9,7 +9,7 @@ jobs: runs-on: ubuntu-20.04 steps: - name: Checkout code - uses: actions/checkout@v2 + uses: actions/checkout@v3 with: ref: ${{ github.ref }} clean: true @@ -33,7 +33,7 @@ jobs: runs-on: ubuntu-20.04 steps: - name: Checkout code - uses: actions/checkout@v2 + uses: actions/checkout@v3 with: ref: ${{ github.ref }} clean: true diff --git a/.github/workflows/spec.yml b/.github/workflows/spec.yml index 6c1ae10d..6da74842 100644 --- a/.github/workflows/spec.yml +++ b/.github/workflows/spec.yml @@ -32,7 +32,7 @@ jobs: echo STEP_ID=setup-environment >> $GITHUB_ENV echo STEP_START=$(date +%s) >> $GITHUB_ENV - name: Checkout Source - uses: actions/checkout@v2 + uses: actions/checkout@v3 if: ${{ github.repository_owner == 'puppetlabs' }} - name: Activate Ruby 2.7 @@ -106,7 +106,7 @@ jobs: job-status: ${{ job.status }} matrix-key: ${{ env.SANITIZED_PUPPET_VERSION }} - name: Checkout Source - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: "Activate Ruby ${{ matrix.ruby_version }}" uses: ruby/setup-ruby@v1 diff --git a/.sync.yml b/.sync.yml index ef0d174c..6b3451a8 100644 --- a/.sync.yml +++ b/.sync.yml @@ -11,6 +11,7 @@ Gemfile: ":development": - gem: ruby-pwsh - gem: github_changelog_generator + version: '= 1.15.2' spec/spec_helper.rb: mock_with: ":rspec" coverage_report: true diff --git a/Gemfile b/Gemfile index 9c7c3aec..8298eb74 100644 --- a/Gemfile +++ b/Gemfile @@ -14,27 +14,31 @@ def location_for(place_or_version, fake_version = nil) end group :development do - gem "json", '~> 2.0', require: false - gem "voxpupuli-puppet-lint-plugins", '~> 3.0', require: false - gem "facterdb", '~> 1.18', require: false - gem "metadata-json-lint", '>= 2.0.2', '< 4.0.0', require: false - gem "puppetlabs_spec_helper", '>= 3.0.0', '< 5.0.0', require: false - gem "rspec-puppet-facts", '~> 2.0', require: false - gem "codecov", '~> 0.2', require: false - gem "dependency_checker", '~> 0.2', require: false - gem "parallel_tests", '~> 3.4', require: false - gem "pry", '~> 0.10', require: false - gem "simplecov-console", '~> 0.5', require: false - gem "puppet-debugger", '~> 1.0', require: false - gem "rubocop", '= 1.6.1', require: false - gem "rubocop-performance", '= 1.9.1', require: false - gem "rubocop-rspec", '= 2.0.1', require: false - gem "rb-readline", '= 0.5.5', require: false, platforms: [:mswin, :mingw, :x64_mingw] - gem "ruby-pwsh", require: false - gem "github_changelog_generator", require: false + gem "json", '= 2.1.0', require: false if Gem::Requirement.create(['>= 2.5.0', '< 2.7.0']).satisfied_by?(Gem::Version.new(RUBY_VERSION.dup)) + gem "json", '= 2.3.0', require: false if Gem::Requirement.create(['>= 2.7.0', '< 3.0.0']).satisfied_by?(Gem::Version.new(RUBY_VERSION.dup)) + gem "json", '= 2.5.1', require: false if Gem::Requirement.create(['>= 3.0.0', '< 3.0.5']).satisfied_by?(Gem::Version.new(RUBY_VERSION.dup)) + gem "json", '= 2.6.1', require: false if Gem::Requirement.create(['>= 3.1.0', '< 3.1.3']).satisfied_by?(Gem::Version.new(RUBY_VERSION.dup)) + gem "json", '= 2.6.3', require: false if Gem::Requirement.create(['>= 3.2.0', '< 4.0.0']).satisfied_by?(Gem::Version.new(RUBY_VERSION.dup)) + gem "voxpupuli-puppet-lint-plugins", '~> 4.0', require: false + gem "facterdb", '~> 1.18', require: false + gem "metadata-json-lint", '>= 2.0.2', '< 4.0.0', require: false + gem "puppetlabs_spec_helper", '~> 5.0', require: false + gem "rspec-puppet-facts", '~> 2.0', require: false + gem "codecov", '~> 0.2', require: false + gem "dependency_checker", '~> 0.2', require: false + gem "parallel_tests", '~> 3.4', require: false + gem "pry", '~> 0.10', require: false + gem "simplecov-console", '~> 0.5', require: false + gem "puppet-debugger", '~> 1.0', require: false + gem "rubocop", '= 1.6.1', require: false + gem "rubocop-performance", '= 1.9.1', require: false + gem "rubocop-rspec", '= 2.0.1', require: false + gem "rb-readline", '= 0.5.5', require: false, platforms: [:mswin, :mingw, :x64_mingw] + gem "ruby-pwsh", require: false + gem "github_changelog_generator", '= 1.15.2', require: false end group :system_tests do - gem "puppet_litmus", '< 1.0.0', require: false, platforms: [:ruby] + gem "puppet_litmus", '< 1.0.0', require: false, platforms: [:ruby, :x64_mingw] gem "serverspec", '~> 2.41', require: false end diff --git a/README.md b/README.md index 3625ee7f..bb86804a 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ ## Overview -The sqlserver module installs and manages Microsoft SQL Server 2012, 2014, 2016, 2017, 2019 on Windows systems. +The sqlserver module installs and manages Microsoft SQL Server 2012, 2014, 2016, 2017, 2019 and 2022 on Windows systems. ## Module Description @@ -272,9 +272,9 @@ For information on the classes and types, see the [REFERENCE.md](https://github. ## Limitations -SQL 2017 and 2019 detection support has been added. This support is limited to functionality already present for other versions. No new SQL 2017 or above specific functionality has been added in this release. +SQL 2017, 2019 and 2022 detection support has been added. This support is limited to functionality already present for other versions. -This module can manage only a single version of SQL Server on a given host (one and only one of SQL Server 2012, 2014, 2016, 2017, or 2019). The module is able to manage multiple SQL Server instances of the same version. +This module can manage only a single version of SQL Server on a given host (one and only one of SQL Server 2012, 2014, 2016, 2017, 2019 or 2022). 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. 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..cbcfb415 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 @@ -127,7 +128,7 @@ def create 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? - install_net_35(@resource[:windows_feature_source]) unless [SQL_2016, SQL_2017, SQL_2019].include? instance_version + install_net_35(@resource[:windows_feature_source]) if [SQL_2012, SQL_2014].include? instance_version debug "Installing features #{@resource[:features]}" add_features(@resource[:features]) diff --git a/lib/puppet/provider/sqlserver_instance/mssql.rb b/lib/puppet/provider/sqlserver_instance/mssql.rb index 06324771..e3733915 100644 --- a/lib/puppet/provider/sqlserver_instance/mssql.rb +++ b/lib/puppet/provider/sqlserver_instance/mssql.rb @@ -105,7 +105,7 @@ def create 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? - install_net_35(@resource[:windows_feature_source]) unless [SQL_2016, SQL_2017, SQL_2019].include? instance_version + install_net_35(@resource[:windows_feature_source]) if [SQL_2012, SQL_2014].include? instance_version add_features(@resource[:features]) end @@ -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/type/sqlserver_features.rb b/lib/puppet/type/sqlserver_features.rb index 3e26967d..3d87983b 100644 --- a/lib/puppet/type/sqlserver_features.rb +++ b/lib/puppet/type/sqlserver_features.rb @@ -36,9 +36,7 @@ newproperty(:features, array_matching: :all) do desc "Specifies features to install, uninstall, or upgrade. The list of top-level features include - BC, Conn, SSMS, ADV_SSMS, SDK, IS and MDS. - - The 'Tools' feature is deprecated. Instead specify 'BC', 'SSMS', 'ADV_SSMS', 'Conn', and 'SDK' explicitly." + BC, Conn, SSMS, ADV_SSMS, SDK, IS and MDS. The 'Tools' feature is deprecated. Instead specify 'BC', 'SSMS', 'ADV_SSMS', 'Conn', and 'SDK' explicitly." newvalues(:Tools, :BC, :Conn, :SSMS, :ADV_SSMS, :SDK, :IS, :MDS, :BOL, :DREPLAY_CTLR, :DREPLAY_CLT, :DQC) munge do |value| if PuppetX::Sqlserver::ServerHelper.is_super_feature(value) diff --git a/lib/puppet/type/sqlserver_instance.rb b/lib/puppet/type/sqlserver_instance.rb index fa633e7c..ca80dbd9 100644 --- a/lib/puppet/type/sqlserver_instance.rb +++ b/lib/puppet/type/sqlserver_instance.rb @@ -26,9 +26,7 @@ newproperty(:features, array_matching: :all) do desc "Specifies features to install, uninstall, or upgrade. The list of top-level features include - SQLEngine, Replication, FullText, DQ AS, and RS. - - The 'SQL' feature is deprecated. Instead specify 'DQ', 'FullText', 'Replication', and 'SQLEngine' explicitly." + SQLEngine, Replication, FullText, DQ AS, and RS. The 'SQL' feature is deprecated. Instead specify 'DQ', 'FullText', 'Replication', and 'SQLEngine' explicitly." newvalues(:SQL, :SQLEngine, :Replication, :FullText, :DQ, :AS, :RS, :POLYBASE, :ADVANCEDANALYTICS) munge do |value| if PuppetX::Sqlserver::ServerHelper.is_super_feature(value) diff --git a/lib/puppet_x/sqlserver/features.rb b/lib/puppet_x/sqlserver/features.rb index 7a1cdae2..1cb249c8 100644 --- a/lib/puppet_x/sqlserver/features.rb +++ b/lib/puppet_x/sqlserver/features.rb @@ -7,8 +7,9 @@ SQL_2016 ||= 'SQL_2016' SQL_2017 ||= 'SQL_2017' SQL_2019 ||= 'SQL_2019' +SQL_2022 ||= 'SQL_2022' -ALL_SQL_VERSIONS ||= [SQL_2012, SQL_2014, SQL_2016, SQL_2017, SQL_2019].freeze +ALL_SQL_VERSIONS ||= [SQL_2012, SQL_2014, SQL_2016, SQL_2017, SQL_2019, SQL_2022].freeze # rubocop:disable Style/ClassAndModuleChildren module PuppetX @@ -39,6 +40,10 @@ class Features # rubocop:disable Style/Documentation major_version: 15, registry_path: '150', }, + SQL_2022 => { + major_version: 16, + registry_path: '160', + }, }.freeze SQL_REG_ROOT ||= 'Software\Microsoft\Microsoft SQL Server' @@ -145,18 +150,20 @@ def self.get_instance_features(reg_root, instance_name) def self.get_shared_features(version) shared_features = { + # Client tools support removed with SQLServer 2022 + # (ref https://learn.microsoft.com/en-us/sql/database-engine/install-windows/install-sql-server-on-server-core?view=sql-server-ver16#BK_SupportedFeatures) '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_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) - + # SQL Client Connectivity SDK (Installed by default) # also WMI: SqlService WHERE SQLServiceType = 4 # MsDtsServer 'SQL_DTS_Full' => 'IS', # Integration Services # currently ignoring Reporting Services Shared diff --git a/lib/puppet_x/sqlserver/server_helper.rb b/lib/puppet_x/sqlserver/server_helper.rb index e2733d39..26128485 100644 --- a/lib/puppet_x/sqlserver/server_helper.rb +++ b/lib/puppet_x/sqlserver/server_helper.rb @@ -44,6 +44,7 @@ def self.sql_version_from_install_source(source_dir) ver = content.match('"(.+)"') return nil if ver.nil? + return SQL_2022 if ver[1].start_with?('16.') return SQL_2019 if ver[1].start_with?('15.') return SQL_2017 if ver[1].start_with?('14.') return SQL_2016 if ver[1].start_with?('13.') 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/metadata.json b/metadata.json index 219a021b..7e5cb00a 100644 --- a/metadata.json +++ b/metadata.json @@ -2,7 +2,7 @@ "name": "puppetlabs-sqlserver", "version": "3.2.1", "author": "puppetlabs", - "summary": "The `sqlserver` module installs and manages MS SQL Server 2012, 2014, 2016, 2017, and 2019 on Windows systems.", + "summary": "The `sqlserver` module installs and manages MS SQL Server 2012, 2014, 2016, 2017, 2019 and 2022 on Windows systems.", "license": "proprietary", "source": "https://github.com/puppetlabs/puppetlabs-sqlserver", "project_page": "https://github.com/puppetlabs/puppetlabs-sqlserver", @@ -45,10 +45,11 @@ "sql2016", "sql2017", "sql2019", + "sql2022", "tsql", "database" ], - "pdk-version": "2.5.0", + "pdk-version": "2.6.1", "template-url": "https://github.com/puppetlabs/pdk-templates.git#main", - "template-ref": "tags/2.6.0-0-gd0490b9" + "template-ref": "heads/main-0-g6e2b72d" } 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..6c5bf20f 100644 --- a/spec/acceptance/z_last_sqlserver_features_spec.rb +++ b/spec/acceptance/z_last_sqlserver_features_spec.rb @@ -8,16 +8,21 @@ 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 %>', - admin_user => '<%= SQL_ADMIN_USER %>', + admin_pass => '<%= SQL_ADMIN_PASS %>', + admin_user => '<%= SQL_ADMIN_USER %>', } 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,15 +32,16 @@ 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 %>', - admin_user => '<%= SQL_ADMIN_USER %>', + admin_pass => '<%= SQL_ADMIN_PASS %>', + admin_user => '<%= SQL_ADMIN_USER %>', } sqlserver_features{ 'MSSQLSERVER': ensure => #{ensure_val}, source => 'H:', - is_svc_account => "$::hostname\\\\Administrator", + is_svc_account => "#{user}", features => #{features}, } MANIFEST @@ -44,7 +50,10 @@ def bind_and_apply_failing_manifest(features, ensure_val = 'present') end context 'can install' do - features = if version.to_i >= 2016 + # Client Tools removed in Server2022 (Backwards Compatibility, Connectivity, SDK) + features = if version.to_i == 2022 + ['IS', 'MDS', 'DQC'] + elsif version.to_i >= 2016 && version.to_i < 2022 ['BC', 'Conn', 'SDK', 'IS', 'MDS', 'DQC'] else ['BC', 'Conn', 'SSMS', 'ADV_SSMS', 'SDK', 'IS', 'MDS', 'DQC'] @@ -58,9 +67,12 @@ def bind_and_apply_failing_manifest(features, ensure_val = 'present') ensure_sql_features(features) validate_sql_install(version: version) do |r| - expect(r.stdout).to match(%r{Client Tools Connectivity}) - expect(r.stdout).to match(%r{Client Tools Backwards Compatibility}) - expect(r.stdout).to match(%r{Client Tools SDK}) + # Client Tools removed in Server2022 + unless version.to_i == 2022 + expect(r.stdout).to match(%r{Client Tools Connectivity}) + expect(r.stdout).to match(%r{Client Tools Backwards Compatibility}) + expect(r.stdout).to match(%r{Client Tools SDK}) + end expect(r.stdout).to match(%r{Integration Services}) expect(r.stdout).to match(%r{Master Data Services}) end @@ -68,7 +80,9 @@ def bind_and_apply_failing_manifest(features, ensure_val = 'present') end context 'can remove' do - features = if version.to_i >= 2016 + features = if version.to_i == 2022 + ['IS', 'MDS', 'DQC'] + elsif version.to_i >= 2016 && version.to_i < 2022 ['BC', 'Conn', 'SDK', 'IS', 'MDS', 'DQC'] else ['BC', 'Conn', 'SSMS', 'ADV_SSMS', 'SDK', 'IS', 'MDS', 'DQC'] @@ -78,9 +92,12 @@ def bind_and_apply_failing_manifest(features, ensure_val = 'present') ensure_sql_features(features, 'absent') validate_sql_install(version: version) do |r| - expect(r.stdout).not_to match(%r{Client Tools Connectivity}) - expect(r.stdout).not_to match(%r{Client Tools Backwards Compatibility}) - expect(r.stdout).not_to match(%r{Client Tools SDK}) + # Client Tools removed in Server2022 + unless version.to_i == 2022 + expect(r.stdout).not_to match(%r{Client Tools Connectivity}) + expect(r.stdout).not_to match(%r{Client Tools Backwards Compatibility}) + expect(r.stdout).not_to match(%r{Client Tools SDK}) + end expect(r.stdout).not_to match(%r{Integration Services}) expect(r.stdout).not_to match(%r{Master Data Services}) end @@ -88,7 +105,9 @@ def bind_and_apply_failing_manifest(features, ensure_val = 'present') end context 'can remove independent feature' do - features = if version.to_i >= 2016 + features = if version.to_i == 2022 + ['IS', 'MDS', 'DQC'] + elsif version.to_i >= 2016 && version.to_i < 2022 ['BC', 'Conn', 'SDK', 'IS', 'MDS', 'DQC'] else ['BC', 'Conn', 'SSMS', 'ADV_SSMS', 'SDK', 'IS', 'MDS', 'DQC'] @@ -102,7 +121,7 @@ def bind_and_apply_failing_manifest(features, ensure_val = 'present') ensure_sql_features(features, 'absent') end - it "'BC'" do + it "'BC'", unless: version.to_i == 2022 do ensure_sql_features(features - ['BC']) validate_sql_install(version: version) do |r| @@ -121,7 +140,7 @@ def bind_and_apply_failing_manifest(features, ensure_val = 'present') end end - it "'SDK' + 'IS" do + it "'SDK' + 'IS", unless: version.to_i == 2022 do ensure_sql_features(features - ['SDK', 'IS']) validate_sql_install(version: version) do |r| @@ -136,7 +155,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) @@ -146,14 +165,21 @@ def bind_and_apply_failing_manifest(features, ensure_val = 'present') context 'with no installed instances' do # Currently this test can only be run on a machine once and will error if run a second time context 'can install' do - features = ['BC', 'Conn', 'SDK', 'IS', 'MDS', 'DQC'] + features = if version.to_i == 2022 + ['IS', 'MDS', 'DQC'] + elsif version.to_i >= 2016 && version.to_i < 2022 + ['BC', 'Conn', 'SDK', 'IS', 'MDS', 'DQC'] + else + ['BC', 'Conn', 'SSMS', 'ADV_SSMS', 'SDK', 'IS', 'MDS', 'DQC'] + end 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 +197,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..eb68be52 100644 --- a/spec/spec_helper_acceptance_local.rb +++ b/spec/spec_helper_acceptance_local.rb @@ -9,8 +9,9 @@ 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_2022_ISO = 'SQLServer2022-x64-ENU-Dev.iso' SQL_2019_ISO = 'SQLServer2019CTP2.4-x64-ENU.iso' SQL_2017_ISO = 'SQLServer2017-x64-ENU.iso' SQL_2016_ISO = 'en_sql_server_2016_enterprise_with_service_pack_1_x64_dvd_9542382.iso' @@ -18,6 +19,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 +28,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 +55,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] @@ -111,6 +121,12 @@ def base_install(sql_version) file: SQL_2019_ISO, drive_letter: 'H', } + when 2022 + iso_opts = { + folder: QA_RESOURCE_ROOT, + file: SQL_2022_ISO, + drive_letter: 'H' + } end # Mount the ISO on the agent mount_iso(iso_opts) @@ -127,8 +143,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 +162,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 +179,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 +209,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 @@ -207,13 +224,21 @@ def validate_sql_install(opts = {}, &block) end def get_install_paths(version) - vers = { '2012' => '110', '2014' => '120', '2016' => '130', '2017' => '140', '2019' => '150' } + vers = { '2012' => '110', '2014' => '120', '2016' => '130', '2017' => '140', '2019' => '150', '2022' => '160' } 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 '2022' + "SQL#{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..79d215f6 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', @@ -118,6 +120,12 @@ def base_install(sql_version) file: SQL_2019_ISO, drive_letter: 'H', } + when 2022 + iso_opts = { + folder: QA_RESOURCE_ROOT, + file: SQL_2022_ISO, + drive_letter: 'H' + } end host = find_only_one('sql_host') # Mount the ISO on the agent @@ -160,15 +168,23 @@ def remove_sql_instances(host, opts = {}) end def get_install_paths(version) - vers = { '2012' => '110', '2014' => '120', '2016' => '130', '2017' => '140', '2019' => '150' } + vers = { '2012' => '110', '2014' => '120', '2016' => '130', '2017' => '140', '2019' => '150', '2022' => '160' } 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 '2022' + "SQL#{version}" + when '2019' + "SQL#{version}CTP2.4" + when '2017' + "SQL#{version}" + else + "SQLServer#{version}" + end + + [dir, "#{dir}\\#{sql_directory}"] end def install_pe_license(host) 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..e685715f 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;UID=sa;PWD=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