diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 27d87ff8..5d1405c5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -29,11 +29,11 @@ jobs: ref: ${{ github.event.pull_request.head.sha }} if: ${{ github.repository_owner == 'puppetlabs' }} - - name: Activate Ruby 2.7 + - name: Activate Ruby 3.1 uses: ruby/setup-ruby@v1 if: ${{ github.repository_owner == 'puppetlabs' }} with: - ruby-version: "2.7" + ruby-version: "3.1" bundler-cache: true - name: Print bundle environment @@ -69,10 +69,10 @@ jobs: with: ref: ${{ github.event.pull_request.head.sha }} - - name: Activate Ruby 2.7 + - name: Activate Ruby 3.1 uses: ruby/setup-ruby@v1 with: - ruby-version: "2.7" + ruby-version: "3.1" bundler-cache: true - name: Print bundle environment @@ -93,7 +93,7 @@ jobs: run: | bundle exec rake 'litmus:install_module' - - name: Authenitcate with GCP + - name: Authenticate 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 diff --git a/lib/puppet/type/sqlserver_instance.rb b/lib/puppet/type/sqlserver_instance.rb index b33f4d09..5afb7dcd 100644 --- a/lib/puppet/type/sqlserver_instance.rb +++ b/lib/puppet/type/sqlserver_instance.rb @@ -131,7 +131,8 @@ def validate end def set?(key) - !self[key].nil? && !self[key].empty? + self_key = resolve_deferred_value(self[key]) + !self_key.nil? && !self_key.empty? end def validate_user_password_required(account, pass) @@ -144,11 +145,11 @@ def validate_user_password_required(account, pass) end def domain_or_local_user?(user) - PuppetX::Sqlserver::ServerHelper.is_domain_or_local_user?(user, Facter.value(:hostname)) + PuppetX::Sqlserver::ServerHelper.is_domain_or_local_user?(resolve_deferred_value(user), Facter.value(:hostname)) end def strong_password?(key) - password = self[key] + password = resolve_deferred_value(self[key]) return unless password message_start = "Password for #{key} is not strong" @@ -162,4 +163,12 @@ def strong_password?(key) true end + + # When preprocess_deferred is false, deferred values remain unresolved at the time of validation, causing it to fail. + # To address this, following logic is added to explicitly resolve deferred values during validation + def resolve_deferred_value(value) + return value unless value.is_a?(Puppet::Pops::Evaluator::DeferredValue) + + value.resolve + end end diff --git a/spec/acceptance/sqlserver_instance_spec.rb b/spec/acceptance/sqlserver_instance_spec.rb index d6934415..f9af473b 100644 --- a/spec/acceptance/sqlserver_instance_spec.rb +++ b/spec/acceptance/sqlserver_instance_spec.rb @@ -134,4 +134,73 @@ def sql_query_is_user_sysadmin(username) end end end + + # Ensure that the instance can be created with deferred values + # for service account and password, which are resolved at the time of + # the instance creation. + # This is useful for scenarios where the values are not known at the time + # of the Puppet run, such as when using Hiera to fetch values from a + # secure vault or when the values are dynamically generated. + def ensure_sqlserver_instance_with_deferred_values(inst_name) + features = ['SQLEngine', 'Replication', 'FullText', 'DQ'] + host_computer_name = run_shell('CMD /C ECHO %COMPUTERNAME%').stdout.chomp + sysadmin_accounts = ["#{host_computer_name}\\travis"] + user = Helper.instance.run_shell('$env:UserName').stdout.chomp + password = Helper.instance.run_shell('$env:pass').stdout.chomp + password = 'Hunter-2' if password.empty? + + pp = <<-MANIFEST + sqlserver_instance{'#{inst_name}': + name => '#{inst_name}', + ensure => 'present', + source => 'H:', + security_mode => 'SQL', + sa_pwd => 'Pupp3t1@', + features => #{features}, + sql_sysadmin_accounts => #{sysadmin_accounts}, + agt_svc_account => Deferred('pick', ['#{user}']), + agt_svc_password => Deferred('pick', ['#{password}']), + windows_feature_source => 'I:\\sources\\sxs', + install_switches => { + 'UpdateEnabled' => 'false', + }, + } + MANIFEST + + idempotent_apply(pp) + end + + context 'Deferred values' do + before(:context) do + @deferred_user = 'travis' + pp = <<-MANIFEST + user { '#{@deferred_user}': + ensure => present, + password => 'Puppet01!', + } + MANIFEST + apply_manifest(pp, catch_failures: true) + end + + after(:context) do + pp = <<-MANIFEST + user { '#{@deferred_user}': + ensure => absent, + } + MANIFEST + apply_manifest(pp, catch_failures: true) + end + + it 'validate deferred values' do + inst_name = new_random_instance_name + expect { ensure_sqlserver_instance_with_deferred_values(inst_name) }.not_to raise_error + end + + it 'apply deferred values' do + inst_name = new_random_instance_name + ensure_sqlserver_instance_with_deferred_values(inst_name) + + run_sql_query(run_sql_query_opts(inst_name, sql_query_is_user_sysadmin(@deferred_user), 1)) + end + end end diff --git a/spec/unit/puppet/type/sqlserver_instance_spec.rb b/spec/unit/puppet/type/sqlserver_instance_spec.rb index e3d87bda..de04ecf6 100644 --- a/spec/unit/puppet/type/sqlserver_instance_spec.rb +++ b/spec/unit/puppet/type/sqlserver_instance_spec.rb @@ -84,4 +84,28 @@ end end end + + describe 'agt_svc_account' do + context 'when value is a deferred value' do + let(:args) do + basic_args.merge({ agt_svc_account: Puppet::Pops::Evaluator::DeferredValue.new(proc { 'nexus\\travis' }) }) + end + + it 'validate' do + subject = Puppet::Type.type(:sqlserver_instance).new(args) + expect(subject.resolve_deferred_value(subject[:agt_svc_account])).to eq('nexus\\travis') + end + end + + context 'when value is not a deferred value' do + let(:args) do + basic_args.merge({ agt_svc_account: 'nexus\\travis' }) + end + + it 'validate' do + subject = Puppet::Type.type(:sqlserver_instance).new(args) + expect(subject.resolve_deferred_value(subject[:agt_svc_account])).to eq('nexus\\travis') + end + end + end end