Best way to run something only once?


#1

Does chef have some built-in way to run some recipes (or parts of it)
once only?

Say, I have the following (here, bash snippet, but could be anything):

bash ‘adjust-supervisor’ do
code <<-EOF
…some_code…
EOF
end

How would you approach to make sure it only runs once?

I figured I could use something like this in the recipe:

not_if { ::File.exists?("/etc/chef/stamps/some-description.stamp") }

followed by:

file “/etc/chef/stamps/some-description.stamp” do
owner "root"
group "root"
mode "0644"
action :create
end

It allows me to easily re-run by removing the stamp file, if needed.

But not sure it’s the optimal way to make sure we run a given thing
once.

How do you approach this?


Tomasz Chmielewski
http://www.sslrack.com


#2

Hello Tomasz,

I think it sort of depends on why you only want to do it once. Checking to
see if you need to run the code is a common way. Wrapping that up in a
resource and provider then letting it figure out whether or not the
supervisor needs to be updated or not might be idiomatic.

For stuff that’s slow/expensive to check for I store the fact that it has
been run in the node mash.

The way you have done it could be an option as well, but I would suggest
having the bash resource notify the file resource to :create. This will
then only create the file if the bash resource’s action is run successfully:
file “/etc/chef/stamps/some-description.stamp” do
owner "root"
group "root"
mode "0644"
action :nothing
end

bash ‘adjust-supervisor’ do
not_if { ::File.exists?("/etc/chef/stamps/some-description.stamp") }
code <<-EOF
…some_code…
EOF
notifies :create, "file[/etc/chef/stamps/some-description.stamp]"
end

In any case it can be tricky to run something ONLY once without checking
the side effects of it. What if the bash resource runs but chef dies for
some reason before creating the file? For this reason I think it’s
important to make sure stuff CAN run twice safely, as much as possible.

Cheers,
-Greg

On Thu, Jun 19, 2014 at 10:34 AM, Tomasz Chmielewski tch@wpkg.org wrote:

Does chef have some built-in way to run some recipes (or parts of it)
once only?

Say, I have the following (here, bash snippet, but could be anything):

bash ‘adjust-supervisor’ do
code <<-EOF
…some_code…
EOF
end

How would you approach to make sure it only runs once?

I figured I could use something like this in the recipe:

not_if { ::File.exists?("/etc/chef/stamps/some-description.stamp") }

followed by:

file “/etc/chef/stamps/some-description.stamp” do
owner "root"
group "root"
mode "0644"
action :create
end

It allows me to easily re-run by removing the stamp file, if needed.

But not sure it’s the optimal way to make sure we run a given thing
once.

How do you approach this?


Tomasz Chmielewski
http://www.sslrack.com


#3

not if clauses work in some cases, but our team leverages chef tags as sentinels for controlling deployments as well. We just wrap the ‘run once’ code within an unless tagged(’’) block to prevent it from running if the node is tagged.

This works for larger concerns such as wrapping an application resource block from deploying new versions of custom applications, but also works in the smaller cases like yours.

Just remove the chef tag from the node and the next chef-client run will execute the controlled block. A few tips with this technique:

  1. just make sure your recipe adds the tag back to the node object if it’s successfully run. If you don’t add the tag back it will execute on every chef-client run and what use is all that pretty code then, right?

  2. If you use the recipe in more then one environment, you may want to use a compound condition in your if/unless block that checks for the existence of the tag as well as a the correct chef_environment. This allows you to control say the prod environment from continuously redeploying/restarting an app (especially if the devs make you use the HEAD of a git branch instead of a git tag) while still giving you the rapid updating in your dev envs without the hassle of having to run knife tag delete… each time.

Greg

Sent from my iPhone

On Jun 18, 2014, at 6:34 PM, Tomasz Chmielewski tch@wpkg.org wrote:

Does chef have some built-in way to run some recipes (or parts of it)
once only?

Say, I have the following (here, bash snippet, but could be anything):

bash ‘adjust-supervisor’ do
code <<-EOF
…some_code…
EOF
end

How would you approach to make sure it only runs once?

I figured I could use something like this in the recipe:

not_if { ::File.exists?("/etc/chef/stamps/some-description.stamp") }

followed by:

file “/etc/chef/stamps/some-description.stamp” do
owner "root"
group "root"
mode "0644"
action :create
end

It allows me to easily re-run by removing the stamp file, if needed.

But not sure it’s the optimal way to make sure we run a given thing
once.

How do you approach this?


Tomasz Chmielewski
http://www.sslrack.com


#4

Hi Tomasz,

Another thing you can do is touch a file in your bash code block, and in
your bash resource, use ‘creates’. If the file is found, the code block
will be skipped.

An example, from http://docs.opscode.com/chef/resources.html:

execute “upgrade script” do
command "php upgrade-application.php && touch /var/application/.upgraded"
creates "/var/application/.upgraded"
action :run
end

More info:
https://tickets.opscode.com/browse/CHEF-3098

Thanks,
Ameir

On Wed, Jun 18, 2014 at 6:34 PM, Tomasz Chmielewski tch@wpkg.org wrote:

Does chef have some built-in way to run some recipes (or parts of it)
once only?

Say, I have the following (here, bash snippet, but could be anything):

bash ‘adjust-supervisor’ do
code <<-EOF
…some_code…
EOF
end

How would you approach to make sure it only runs once?

I figured I could use something like this in the recipe:

not_if { ::File.exists?("/etc/chef/stamps/some-description.stamp") }

followed by:

file “/etc/chef/stamps/some-description.stamp” do
owner "root"
group "root"
mode "0644"
action :create
end

It allows me to easily re-run by removing the stamp file, if needed.

But not sure it’s the optimal way to make sure we run a given thing
once.

How do you approach this?


Tomasz Chmielewski
http://www.sslrack.com


#5

Another commonly-used option, rather than writing random garbage to
the filesystem, is to persist some flag in the node object itself
using “set” (one of the few instances in which set/normal is actually
desirable). Example:

https://github.com/hw-cookbooks/postgresql/blob/develop/recipes/server.rb#L47

  • Julian

On Wed, Jun 18, 2014 at 3:34 PM, Tomasz Chmielewski tch@wpkg.org wrote:

Does chef have some built-in way to run some recipes (or parts of it)
once only?

Say, I have the following (here, bash snippet, but could be anything):

bash ‘adjust-supervisor’ do
code <<-EOF
…some_code…
EOF
end

How would you approach to make sure it only runs once?

I figured I could use something like this in the recipe:

not_if { ::File.exists?("/etc/chef/stamps/some-description.stamp") }

followed by:

file “/etc/chef/stamps/some-description.stamp” do
owner "root"
group "root"
mode "0644"
action :create
end

It allows me to easily re-run by removing the stamp file, if needed.

But not sure it’s the optimal way to make sure we run a given thing
once.

How do you approach this?


Tomasz Chmielewski
http://www.sslrack.com


[ Julian C. Dunn jdunn@aquezada.com * Sorry, I’m ]
[ WWW: http://www.aquezada.com/staff/julian * only Web 1.0 ]
[ gopher://sdf.org/1/users/keymaker/ * compliant! ]
[ PGP: 91B3 7A9D 683C 7C16 715F 442C 6065 D533 FDC2 05B9 ]