0

I'm trying to parse a value from a file in order to set it as an attribute for use further down the recipe (to set as a subdirectory name).

The file is downloaded from a jenkins server and parsed in a ruby block to get the value - so far so good. However, if I try to assign that to the node attribute, it doesn't work. I thought I'd found the answer here: How to lazily evaluate an arbitrary variable with Chef, but none of the methods mentioned there work for me. What am I doing wrong?

ruby_block "get build number" do
  block do
    f = File.open("/tmp/MyappJenkinsBuildInfo.txt")
    f.each {|line|
      line_arr = line.split('=')
      if line_arr[0] == 'jenkins.build.number'
        node.default['myapp']['jenkins']['build'] = line_arr[1]
        break
      end
    }
    f.close
  end
end

build = DelayedEvaluator.new { node['myapp']['jenkins']['build'] }

release_dir = "#{node['myapp']['dir']['main']}/releases/#{build.call}"

This "works" in that there's no syntax error, but the value of #{build.call} is an empty string. The file definitely exists, and I've already tested that line_arr[1] inside the ruby block is getting the right value (with a puts statement inside the RB). I've also tried using lambda in place of DelayedEvaluator.new.

Community
  • 1
  • 1
linxit
  • 3
  • 3

1 Answers1

1

The problem here is that your release_dir = line is getting executed at compile time, before the code in your ruby_block has run. Obviously at that point in time, build.call is just going to return an empty string, because the code that sets node['myapp']['jenkins']['build'] hasn't run yet.

Put another way, the code in the recipe is getting executed like this:

# Compile time

ruby_block "get build number" do
  block do
    # Random stuff here that will get executed at converge time
  end
end

build = DelayedEvaluator.new { node['myapp']['jenkins']['build'] }

release_dir = "#{node['myapp']['dir']['main']}/releases/#{build.call}"

# ...

# Okay, everything's compiled. Now we converge...

f = File.open("/tmp/MyappJenkinsBuildInfo.txt")
f.each {|line|
  line_arr = line.split('=')
  if line_arr[0] == 'jenkins.build.number'
    node.default['myapp']['jenkins']['build'] = line_arr[1]
    break
  end
}
f.close

So to answer your question, you are setting a node attribute at converge time. If you do it that way though, you have to realize that it's impossible to access that variable at compile time, since at compile time the variable hasn't even been set yet. Either set the variable at compile time, or don't try accessing it until converge time. Unfortunately, you can't have both.

Ajedi32
  • 45,670
  • 22
  • 127
  • 172
  • Ah, I understand - I was expecting #{build.call} to automatically get the new value. However, underneath the build = ... line I've added a log statement (which does get run at converge time), and that also came up blank. e.g. – `log "Build is #{build.call}" do level :info end` produced log[Build is ] on output – linxit Jul 22 '15 at 15:02
  • @linxit The log operation itself runs at converge time, but not the Ruby code that configures it. `log "Build is #{build.call}"` is really no different from `some_variable = build.call; log "Build is #{some_varaible}"` from the perspective of the Ruby interpreter. – Ajedi32 Jul 22 '15 at 15:05
  • Hmm, it's impossible to set the variable at compile time, so I basically turned the whole recipe into a ruby_block. Ugly, but it's working now! – linxit Jul 23 '15 at 13:08
  • That really depends on what you're doing with that variable. If you can show us where it is used, we may be able to show you a better way. – Tejay Cardon Jul 23 '15 at 13:17
  • It's stored in a file on a separate server, so I have to download it, parse the file and extract the string (actually a Jenkins build number). So I was doing that in a ruby block (see code above), but then need to use the variable to create a subdirectory and unzip a file into it, then go into that directory tree to execute a shell script, so this `#{build}` variable is used in several places. – linxit Jul 23 '15 at 13:32
  • @linxit Well, you *could* do that at compile time. Not sure it makes a big difference at this point though. Either way it's going to be a bit awkward. – Ajedi32 Jul 23 '15 at 13:34