Search code examples
rubychef-infracookbooklwrp

LWRP pure ruby code vs other cookbook resources execution order


I'm having troubles trying to understand what's going on, why pure ruby code is executed first despite that the code is put last, this is a part of what the action :install contains:

action :install do

    ...

    windows_package "#{installation_name}" do
        source "#{Chef::Config[:file_cache_path]}\\#{installer_filename}"
        options "INSTALLDIR=\"#{installation_path}\""
        action :install
        not_if {::Dir.exists?("#{installation_path}\\bin")}
    end

    env "MYSQL_PATH" do
        value "#{installation_path}"
    end

    windows_path "#{installation_path}\\bin" do
        action :add
    end

    windows_batch "Installing Service" do
        code <<-EOH
            set MYSQL_PATH="#{installation_path}"
            call %MYSQL_PATH%\\bin\\mysqld-nt.exe --install MySQL
        EOH
    end

    service "MySQL" do
        action :start
    end

    service "MySQL" do
        action :enable
    end

    change_pass_str = "call \"#{installation_path}\\bin\\mysql.exe\" -u root --execute \"UPDATE mysql.user SET Password=PASSWORD('#{root_password}') WHERE User='root';FLUSH PRIVILEGES;\""
    puts change_pass_str

    password_set_result = system(change_pass_str)
    log !password_set_result ? "Password wasn't changed since root already have a password defined. Maybe there's still data from a previous installation." : "Password has been set!"

end

Please ignore the fact that i didn't put the variable definition, and know that they are well defined. The thing is that when this part of the lwrp is executed

change_pass_str = "call \"#{installation_path}\\bin\\mysql.exe\" -u root --execute \"UPDATE mysql.user SET Password=PASSWORD('#{root_password}') WHERE User='root';FLUSH PRIVILEGES;\""
puts change_pass_str
password_set_result = system(change_pass_str)

it can't find #{installation_path}\\bin\\mysql.exe since it is not yet installed, in spite of that the block is at the end of the action.

Can anyone point me what is my mistake? why the other (already defined in this case in windows LWRP) resources are executed at the end instead of the begining? How can i fix it?


Solution

  • LWRPs are no different than recipes with regards to execution order. So, just like in a recipe, any ruby code that is not inside a resource will be executed during the 'resource gathering' phase. In your case, you need to wrap your code in a ruby_block or execute resource like this:

    ruby_block 'change the password' do
      block {
        change_pass_str = "call \"#{installation_path}\\bin\\mysql.exe\" -u root --execute \"UPDATE mysql.user SET Password=PASSWORD('#{root_password}') WHERE User='root';FLUSH PRIVILEGES;\""
        puts change_pass_str
        password_set_result = system(change_pass_str)
      end
    end
    

    OR

    execute'change the password' do
      command "call \"#{installation_path}\\bin\\mysql.exe\" -u root --execute \"UPDATE mysql.user SET Password=PASSWORD('#{root_password}') WHERE User='root';FLUSH PRIVILEGES;\""
    end    
    

    That will cause this code to evaluate at run-time. You'll also want to add an only_if, not_if, or action :nothing to that ruby_block to ensure it only runs when needed. Basically, a not_if or only_if would run SQL code to check if the password was set or not, and if that block returned false, then the password change wouldn't run.

    Alternately, you can use action :nothing to set the resource to not run, and then use a notification or subscription to trigger the resource to run only after mysql is installed.