1

I'm a very new to Puppet, but I cannot wrap my head around this simple problem: I want to define a resource that simply execute a sequence of scripts, one after the other, waiting for one's execution to finish before launching the next.

Here is what I tried:

$ cat test.pp
define my_test {
   exec { "my test task $name":
        provider => shell,
        logoutput => true,
        command => "sleep $name && echo slept for $name seconds",
    }
}

my_test { [3, 2, 1, 0]: }

$ puppet apply test.pp
Notice: Compiled catalog for my.domain.fr in environment production in 0.05 seconds
Notice: /Stage[main]/Main/My_test[0]/Exec[my test task 0]/returns: slept for 0 seconds
Notice: /Stage[main]/Main/My_test[0]/Exec[my test task 0]/returns: executed successfully
Notice: /Stage[main]/Main/My_test[3]/Exec[my test task 3]/returns: slept for 3 seconds
Notice: /Stage[main]/Main/My_test[3]/Exec[my test task 3]/returns: executed successfully
Notice: /Stage[main]/Main/My_test[1]/Exec[my test task 1]/returns: slept for 1 seconds
Notice: /Stage[main]/Main/My_test[1]/Exec[my test task 1]/returns: executed successfully
Notice: /Stage[main]/Main/My_test[2]/Exec[my test task 2]/returns: slept for 2 seconds
Notice: /Stage[main]/Main/My_test[2]/Exec[my test task 2]/returns: executed successfully
Notice: Finished catalog run in 6.27 seconds

I have also tried with a [3, 2, 1, 0].each |$var| { ... } construct, and read the docs and ttboj's very detail article on loops in Puppet, but I still don't get it.

My issue has probably more to do with how Puppet resources work in the end, but could someone explain what is the simplest way to write such a simple task ?

Lucas Cimon
  • 1,859
  • 2
  • 24
  • 33
  • You have to [order](https://docs.puppetlabs.com/puppet/latest/reference/lang_relationships.html) your resources. – Felix Frank Aug 10 '15 at 13:20
  • Ok, I've already read that. But how to that in a loop ? Or else: how to enforce execution order on an array of resources ? – Lucas Cimon Aug 10 '15 at 14:16

1 Answers1

3

I don't think that you will be able to do this with array notation only.

If you stick to your declaration, you will still need to add the relationships one by one.

My_test['3'] -> My_test['2'] -> My_test['1'] -> My_test['0']

Hardly worthwhile. Much simpler to walk the extra mile right from the start.

my_test { '3': }
->
my_test { '2': }
->
my_test { '1': }
->
my_test { '0': }

Sure, this does not allow you tricks like moving the titles to Hiera or somewhere else.

I suppose you could concoct an awful(ly clever) function to generate the resources and add the relationships in one go, but this will be a high maintenance approach. (Kind regards to your colleagues/successors.)

I advise you check your motivation here. Using Puppet to running static sequences of scripts means that you don't take advantage of the tool's strengths (system abstraction and state modeling) and run into some of its weaknesses, as you have noticed. Would it not be simpler to just run one scripts that calls all the others in order? (You could even make a defined type for that.)

Felix Frank
  • 8,125
  • 1
  • 23
  • 30
  • +1 to advocate "explicit" code over "smart" code. I ended up moving this sequential logic into a shell script. What kind of "defined type" are you refering to here ? Just a `define { exec ... }` or something else ? – Lucas Cimon Aug 13 '15 at 14:12
  • Also: my real-world use case here is that I get a list of tasks from a comma-separated string as input. Hence I cannot statically define the tasks as you did in your code snippet – Lucas Cimon Aug 13 '15 at 14:14
  • Yes, the defined type should 1) generate the script using a template that consumes the comma separated list and 2) declare the `exec` resource to run that script. – Felix Frank Aug 14 '15 at 13:03