15

(this is a copy of a question I asked on the salt-users group, FWIW)

I need to write a state that installs some Windows server features, reboots the minion, and then installs some more software. It seems like all the pieces are there (cmd.wait, system.reboot), but I'm having trouble connecting the dots.

For example, I have a "webserver" state that is supposed to install IIS on a machine. It needs to enable some server features, reboot (because Windows), and then continue on. We do this with a custom module named "website" which lets me install a website, and another module for installing app pools. Our state file looks something like this:

my_website:
  website.installed:
    - name: example.com 
    - version: alpha-1.0.0
        - type: Website
        - bindings:
           - hostheader: localhost
           - port: 1234
        - installdir: c:\\wwwroot\\example.com
        - apppool: static
    - require:
          - sls: serverstate.webserver
          - sls: apppool.static

The above works great, except for the fact we need to do a reboot between the "serverstate.webserver" and "apppool.static" requirements. As it stands, the state fails, we manually reboot, and then rerun the state and it works. Naturally we want to omit the manual reboot step.

Is there a common pattern to solve this type of problem?

Bryan Oakley
  • 370,779
  • 53
  • 539
  • 685

2 Answers2

17

I've run into this in the past. What I've done to get the needed behavior is use jinja to decide at run time if a section of your sls file needs to be run. I do this by having each section that needs a reboot set a custom grain once it succeeds. Then on the following runs it will skip the section that has already been completed.

Here's an example sls file.

{% if grains.get('myapp_done') != 'completed' %}

myapp:
  pkg.installed:
    - source: salt:/windows/myapp.exe

myapp_done:
  module.run:
    - name: grains.setval
    - key: myapp
    - val: complete

system.reboot:
  module.run:
    - watch:
      - pkg: myapp

{% endif %}

{% if grains.get('myotherapp_done') != 'completed' %}

myotherapp:
  pkg.installed:
    - source: salt:/windows/myotherapp.exe

myotherapp_done:
  module.run:
    - name: grains.setval
    - key: myotherapp
    - val: complete


system.reboot:
  module.run:
    - watch:
      - pkg: myotherapp

{% endif %}

Then either run a highstate multiple times or set the following in your minion's config to run a highstate upon startup.

startup_states: highstate
Utah_Dave
  • 4,531
  • 24
  • 23
  • 3
    I came on this question from google, and I like the answer. For others finding this, I suggest using a 'requires' directive in the module.run states, so the grain doesn't get set unless the pkg.install is successful. (I tried to edit, but was rejected for "changing your intent".) – John Hazen Mar 13 '16 at 06:09
  • I think John Hazen's suggestion to have the module.run states require the pkg.install is a very good one. In the interest of brevity I haven't updated my example, but will do so if someone would is interested. – Utah_Dave May 18 '16 at 19:00
0

This may have been a feature added after the answer above, but here's an example of what worked for me:

/boot/cmdline.txt:
  file.managed:
    - source: salt://cmdline.txt
    - user: root
    - group: root
    - mode: 755
    - backup: minion

system.reboot:
  module.run:
    - onchanges:
      - file: /boot/cmdline.txt

The onchanges requisite will make the system.reboot only run when the dependency changes. That is the dependency I want; if cmdline.txt changes, I need to reboot.

If future states need to run or depend on the reboot occurring as was mentioned in the original post description, then Utah_Dave's idea of using

startup_states: highstate

should work nicely.

devguydavid
  • 3,917
  • 1
  • 19
  • 18