Answers: <p>You have a problem of compile vs converge times as you guessed.</p>
  
  <p>You code between the execute resource and the ruby_block resource is executed before the execute resource is converged.</p>
  
  <p>The easiest way to circumvent that is to put that code within a ruby_block, and to avoid scoping problem use node.run_state hash to store the value to be checked.</p>
  
  <p>A rewrite of your recipe which should work would be:</p>
  
  <pre><code>#
  # Cookbook:: test_cookbook
  # Recipe:: check-vpn-ip
  #
  # Copyright:: 2019, The Authors, All Rights Reserved.
  
  #Getting the IP address using the ruby's Socket class.
  
  ip_list = Socket.ip_address_list
  vpn_ip_list = ip_list.select{ |ip| ip.ip_address.match(/^10.12/) }
  !vpn_ip_list.empty? ? ip_addr = vpn_ip_list.first.ip_address : ip_addr = ""
  
  execute 'manually_start_open_vpn' do
    command "sudo openvpn #{node['openvpn-conf-path']}/#{host}.conf &amp;"
    action :nothing
    only_if {ip_addr.length.eql?(0)}
  end
  
  ruby_block 'check_vpn_ip_list' do
    block do
      new_ip_list = Socket.ip_address_list
      new_vpn_ip_list = new_ip_list.select{ |ip| ip.ip_address.match(/^10.12/) }
      node.run_state['newvpn_ip_addr'] = !new_vpn_ip_list.empty? ?  new_vpn_ip_list.first.ip_address : ""
    end
  end
  
  ruby_block 'chat-bot' do
    block do
      machine_data = {text: "OpenVPN IP not assigned to #{host} 
          software_version: 18.4.4 \n This is a test message please 
          ignore @all"}.to_json 
      header = {'Content-Type': 'text/json'}
      http = Net::HTTP.new(google_chat_uri.host,                       google_chat_uri.port)
      http.use_ssl = true
      request = Net::HTTP::Post.new(google_chat_uri.request_uri, header)
      request.body = machine_data
      response = http.request(request)
    end
    only_if {node.run_state['newvpn_ip_addr'].length.eql?(0)}
    action :nothing
  end 
  </code></pre>
  
  <p>There's no real use of response = in the last line as you don't use it later, but I let it as is in case you wish to log in the ruby block the response.</p>
  
Answers: CC BY-SA 4.0
Answers: 2019-01-31T17:37:32.573Z
Answers: Thanks for the reply @Tensibai . Been struggling with the assignment of variable value due to the compile vs converge time properties of Chef. I have read that any code within the ruby_block, guard statement and lazy are evaluated only at run time/converge phase. I tried using a ruby block to get the value of variable `newvpn_ip_addr` but I didn't make use of the node.run_state. I will try the solution and get back with the results.
Answers: Yuge
Answers: CC BY-SA 4.0
Answers: 2019-01-31T17:41:29.730Z
Answers: Also I have come up an alternative solution that makes use of the property of guard statements. i.e. Any code with the guard statement will be executed only at the converge phase or run-time. I removed the entire code that checks and assign the value for `newvpn_ip_addr` variable and instead put the code directly inside the only_if guard block like shown below so that the resources are executed based on the values created after run time. I don't know if this is the correct approach but it works.
  
  `only_if {Socket.ip_address_list.select{ |ip| ip.ip_address.match(/^10.12/) }.empty?}`
Answers: Yuge
Answers: CC BY-SA 4.0
Answers: 2019-01-31T21:30:57.370Z
Answers: It works for this case, but look at it, it's hard to grasp for your future self in 9 month :)
Answers: Tensibai
Answers: CC BY-SA 4.0
Answers: 2019-01-31T21:32:26.020Z
Answers: And for the problem you got, a variable within a ruby block is only available within the block, you can workaround with a global variable, but that's even more complex to understand after
Answers: Tensibai
Answers: CC BY-SA 4.0
Answers: 2019-03-08T01:39:34.433Z
Answers: Thanks for the node.runstate[:variable] tip. I used that to pass data between two ruby_block in a recipe. But I have another problem, the code inside the ruby_block is not executed at run-time. Please have a look at my code, I have made changes to the question @Tensibai
Answers: Yuge
Answers: CC BY-SA 4.0
Answers: 2019-03-08T06:11:09.003Z
Answers: @Yuge don't do that, that's called a Chameleon question and it invalidates answers, that's unclear for future readers, it's better to raise another question referencing the previous one when needed.
Answers: Tensibai
Answers: CC BY-SA 4.0
Answers: 2019-03-08T14:25:28.807Z
Answers: Thanks will create a new question referencing the current one.
Answers: Yuge
Answers: CC BY-SA 4.0
Answers: 2019-01-31T08:56:56.727Z
Answers: 2019-01-31T08:56:56.727Z
Answers: Tensibai
Body: <p>I am creating a recipe that has two resources. The initial VPN IP address is a empty valued string. Upon execution of the first resource, a IP address within the range 10.12.xx.xx is set. Then a second resource is executed with a guard block that checks for the VPN IP. </p>
  
  <pre><code>#
  # Cookbook:: test_cookbook
  # Recipe:: check-vpn-ip
  #
  # Copyright:: 2019, The Authors, All Rights Reserved.
  
  #Getting the IP address using the ruby's Socket class.
  
  ip_list = Socket.ip_address_list
  vpn_ip_list = ip_list.select{ |ip| ip.ip_address.match(/^10.12/) }
  !vpn_ip_list.empty? ? ip_addr = vpn_ip_list.first.ip_address : ip_addr = ""
  
  execute 'manually_start_open_vpn' do
  command "sudo openvpn #{node['openvpn-conf-path']}/#{host}.conf &amp;"
  action :nothing
  only_if {ip_addr.length.eql?(0)}
  end
  
  new_ip_list = Socket.ip_address_list
  new_vpn_ip_list = new_ip_list.select{ |ip| 
                                     ip.ip_address.match(/^10.12/) }
  !new_vpn_ip_list.empty? ? newvpn_ip_addr = 
                  new_vpn_ip_list.first.ip_address : newvpn_ip_addr = ""
  
  ruby_block 'chat-bot' do
     block do
          machine_data = {text: "OpenVPN IP not assigned to #{host} 
              software_version: 18.4.4 \n This is a test message please 
                ignore @all"}.to_json 
          header = {'Content-Type': 'text/json'}
          http = Net::HTTP.new(google_chat_uri.host, 
                     google_chat_uri.port)
          http.use_ssl = true
          request = Net::HTTP::Post.new(google_chat_uri.request_uri, header)
          request.body = machine_data
          response = http.request(request)
      end 
  
     end
  only_if {newvpn_ip_addr.length.eql?(0)}
  action :nothing
  end 
  </code></pre>
  
  <p>Since there is no vpn ip address of the range 10.12.xx.xx during compile phase, the value of <code>ip_addr.length.eql?(0)</code> is true and the guard block condition in first resource is satisfied and it's executed.</p>
  
  <p>As the vpn ip has already been assigned, the <code>newvpn_ip_addr.length.eql?(0)</code> should be false and the 2nd ruby block should not be executed. But this returns a false value as the code <code>newvpn_ip_addr</code> is executed during compile time itself.</p>
  
  <p>Is there any way to make sure that the value for <code>newvpn_ip_addr</code> in the below code is assigned and checked during the execution phase post the execution of first resource?</p>
  
  <pre><code>    new_ip_list = Socket.ip_address_list
      new_vpn_ip_list = new_ip_list.select{ |ip| 
                                     ip.ip_address.match(/^10.12/) }
      !new_vpn_ip_list.empty? ? newvpn_ip_addr = 
                  new_vpn_ip_list.first.ip_address : newvpn_ip_addr = ""
  </code></pre>
  
  <p>So that the guard block in 2nd resource is not executed if the expected IP address is not assigned.
  <code>only_if {newvpn_ip_addr.length.eql?(0)}</code></p>
  
Comments: CC BY-SA 4.0
Comments: 2019-03-08T08:25:11.463Z
Comments: I've rolled back you changes, you can see your modifications in the question history (the 'edited X ago' under the question) to use it as a new question. StackExchange sites are not a forum, follow up questions are to be asked separately.
Comments: Tensibai
Tags: <chef>
Title: Chef - assign a value to variable in execution or converge phase
UserDisplayName: Yuge
ThreadText: # Chef - assign a value to variable in execution or converge phase
  
  (Asked by: username_0 on 2019-01-29)
  
  I am creating a recipe that has two resources. The initial VPN IP address is a empty valued string. Upon execution of the first resource, a IP address within the range 10.12.xx.xx is set. Then a second resource is executed with a guard block that checks for the VPN IP.
  
  ```
  #
  # Cookbook:: test_cookbook
  # Recipe:: check-vpn-ip
  #
  # Copyright:: 2019, The Authors, All Rights Reserved.
  
  #Getting the IP address using the ruby's Socket class.
  
  ip_list = Socket.ip_address_list
  vpn_ip_list = ip_list.select{ |ip| ip.ip_address.match(/^10.12/) }
  !vpn_ip_list.empty? ? ip_addr = vpn_ip_list.first.ip_address : ip_addr = ""
  
  execute 'manually_start_open_vpn' do
  command "sudo openvpn #{node['openvpn-conf-path']}/#{host}.conf &"
  action :nothing
  only_if {ip_addr.length.eql?(0)}
  end
  
  new_ip_list = Socket.ip_address_list
  new_vpn_ip_list = new_ip_list.select{ |ip| 
                                     ip.ip_address.match(/^10.12/) }
  !new_vpn_ip_list.empty? ? newvpn_ip_addr = 
                  new_vpn_ip_list.first.ip_address : newvpn_ip_addr = ""
  
  ruby_block 'chat-bot' do
     block do
          machine_data = {text: "OpenVPN IP not assigned to #{host} 
              software_version: 18.4.4 \n This is a test message please 
                ignore @all"}.to_json 
          header = {'Content-Type': 'text/json'}
          http = Net::HTTP.new(google_chat_uri.host, 
                     google_chat_uri.port)
          http.use_ssl = true
          request = Net::HTTP::Post.new(google_chat_uri.request_uri, header)
          request.body = machine_data
          response = http.request(request)
      end 
  
     end
  only_if {newvpn_ip_addr.length.eql?(0)}
  action :nothing
  end 
  
  ```
  
  Since there is no vpn ip address of the range 10.12.xx.xx during compile phase, the value of `ip_addr.length.eql?(0)` is true and the guard block condition in first resource is satisfied and it's executed.
  
  As the vpn ip has already been assigned, the `newvpn_ip_addr.length.eql?(0)` should be false and the 2nd ruby block should not be executed. But this returns a false value as the code `newvpn_ip_addr` is executed during compile time itself.
  
  Is there any way to make sure that the value for `newvpn_ip_addr` in the below code is assigned and checked during the execution phase post the execution of first resource?
  
  ```
      new_ip_list = Socket.ip_address_list
      new_vpn_ip_list = new_ip_list.select{ |ip| 
                                     ip.ip_address.match(/^10.12/) }
      !new_vpn_ip_list.empty? ? newvpn_ip_addr = 
                  new_vpn_ip_list.first.ip_address : newvpn_ip_addr = ""
  
  ```
  
  So that the guard block in 2nd resource is not executed if the expected IP address is not assigned.
  `only_if {newvpn_ip_addr.length.eql?(0)}`
  
  **Tags:** chef
  
  > **Comments:**
  > **username_1**: I've rolled back you changes, you can see your modifications in the question history (the 'edited X ago' under the question) to use it as a new question. StackExchange sites are not a forum, follow up questions are to be asked separately.  
  
  ---
  
  ## 1 answers
  
  ### Answer by username_1 on 2019-01-31. Score: 1
  
  You have a problem of compile vs converge times as you guessed.
  
  You code between the execute resource and the ruby\_block resource is executed before the execute resource is converged.
  
  The easiest way to circumvent that is to put that code within a ruby\_block, and to avoid scoping problem use node.run\_state hash to store the value to be checked.
  
  A rewrite of your recipe which should work would be:
  
  ```
  #
  # Cookbook:: test_cookbook
  # Recipe:: check-vpn-ip
  #
  # Copyright:: 2019, The Authors, All Rights Reserved.
  
  #Getting the IP address using the ruby's Socket class.
  
  ip_list = Socket.ip_address_list
  vpn_ip_list = ip_list.select{ |ip| ip.ip_address.match(/^10.12/) }
  !vpn_ip_list.empty? ? ip_addr = vpn_ip_list.first.ip_address : ip_addr = ""
  
  execute 'manually_start_open_vpn' do
    command "sudo openvpn #{node['openvpn-conf-path']}/#{host}.conf &"
    action :nothing
    only_if {ip_addr.length.eql?(0)}
  end
  
  ruby_block 'check_vpn_ip_list' do
    block do
      new_ip_list = Socket.ip_address_list
      new_vpn_ip_list = new_ip_list.select{ |ip| ip.ip_address.match(/^10.12/) }
      node.run_state['newvpn_ip_addr'] = !new_vpn_ip_list.empty? ?  new_vpn_ip_list.first.ip_address : ""
    end
  end
  
  ruby_block 'chat-bot' do
    block do
      machine_data = {text: "OpenVPN IP not assigned to #{host} 
          software_version: 18.4.4 \n This is a test message please 
          ignore @all"}.to_json 
      header = {'Content-Type': 'text/json'}
      http = Net::HTTP.new(google_chat_uri.host,                       google_chat_uri.port)
      http.use_ssl = true
      request = Net::HTTP::Post.new(google_chat_uri.request_uri, header)
      request.body = machine_data
      response = http.request(request)
    end
    only_if {node.run_state['newvpn_ip_addr'].length.eql?(0)}
    action :nothing
  end 
  
  ```
  
  There's no real use of response = in the last line as you don't use it later, but I let it as is in case you wish to log in the ruby block the response.
  
  > **Comments:**
  > **username_0**: Thanks for the reply @username_1 . Been struggling with the assignment of variable value due to the compile vs converge time properties of Chef. I have read that any code within the ruby_block, guard statement and lazy are evaluated only at run time/converge phase. I tried using a ruby block to get the value of variable `newvpn_ip_addr` but I didn't make use of the node.run_state. I will try the solution and get back with the results.  
  > **username_0**: Also I have come up an alternative solution that makes use of the property of guard statements. i.e. Any code with the guard statement will be executed only at the converge phase or run-time. I removed the entire code that checks and assign the value for `newvpn_ip_addr` variable and instead put the code directly inside the only_if guard block like shown below so that the resources are executed based on the values created after run time. I don't know if this is the correct approach but it works.  `only_if {Socket.ip_address_list.select{ |ip| ip.ip_address.match(/^10.12/) }.empty?}`  
  > **username_1**: It works for this case, but look at it, it's hard to grasp for your future self in 9 month :)  
  > **username_1**: And for the problem you got, a variable within a ruby block is only available within the block, you can workaround with a global variable, but that's even more complex to understand after  
  > **username_0**: Thanks for the node.runstate[:variable] tip. I used that to pass data between two ruby_block in a recipe. But I have another problem, the code inside the ruby_block is not executed at run-time. Please have a look at my code, I have made changes to the question @username_1  
  > **username_1**: @username_0 don't do that, that's called a Chameleon question and it invalidates answers, that's unclear for future readers, it's better to raise another question referencing the previous one when needed.  
  > **username_0**: Thanks will create a new question referencing the current one.  
  
  ---
  
  