<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>

CC BY-SA 4.0
2019-01-31T17:37:32.573Z
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.
Yuge
CC BY-SA 4.0
2019-01-31T17:41:29.730Z
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?}`
Yuge
CC BY-SA 4.0
2019-01-31T21:30:57.370Z
It works for this case, but look at it, it's hard to grasp for your future self in 9 month :)
Tensibai
CC BY-SA 4.0
2019-01-31T21:32:26.020Z
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
Tensibai
CC BY-SA 4.0
2019-03-08T01:39:34.433Z
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
Yuge
CC BY-SA 4.0
2019-03-08T06:11:09.003Z
@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.
Tensibai
CC BY-SA 4.0
2019-03-08T14:25:28.807Z
Thanks will create a new question referencing the current one.
Yuge
CC BY-SA 4.0
2019-01-31T08:56:56.727Z
2019-01-31T08:56:56.727Z
Tensibai
<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>

CC BY-SA 4.0
2019-03-08T08:25:11.463Z
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.
Tensibai
<chef>
Chef - assign a value to variable in execution or converge phase
Yuge
# 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.  

---

