-
Notifications
You must be signed in to change notification settings - Fork 416
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Adds NG states #18
Adds NG states #18
Conversation
Nicely implemented! I like your I made some changes to the README in 3494514. I hope you don't mind -- feel free to adjust as you see fit. |
I also really like your conf file management approach. Putting something like the Pondering aloud: I wonder how much overlap in formats there is between config formats for various tools. (In other words: whether it would be worth having a "library" of macros for common formats, or if that is instead a road to madness...) |
(Still) pondering aloud: that said, there's a lot of value in seeing the formatting there at the top of the file. It removes any ambiguity as to what will be written to the file system. It also keeps the templating logic quite clean. I prefer your approach compared to my approach. |
Hi Seth. No problem: re changes. I'm very curious to get your opinion on the whole pattern. The general pattern is a pseudo-mvc approach. Expanded, the mapfile is the model and the 'sole source' of data from whatever backend it exposes (pillar, grains, etc). The states are controllers and should not contain anything other than programmatic logic (states) and transactional controls (requisites). The conf files are obviously the views and operate much the same way any view would work. I much prefer the slim view like vhost_config, if only because it saves us the hell of updating a bunch of formulas when the underlying config file params change and it also saves formula authors from having to have 1:1 parity with config file options/params. I've had the same thought you had re: macro libraries and I think it's completely do-able. My ntp.ng state uses a similar pattern (I don't think I've sent a PR for that one for some reason or another). |
The other reason to use the macro was recuriseness -- many config file formats can and do recurse fairly easily and indentation is not necessary but also not difficult to preserve. I've been working on another formula for a php app we use in-house that has standard php variable declarations for its config options, eg:
Even that was simple to map from python constructs. |
er, recursiveness |
One more note here. (Writing it down somewhere to solicit comments...and also so I don't forget.) A pattern that came up on IRC last week for denoting that a file is managed by Salt and also including the source file so you know where to find the original. Files templated through the
Which will output the |
That is extremely cool. |
That's a perfect idea (re: comments). Yes, good addition. Another pattern worth considering including in the formal best practices is what's diving the sls_block bit. More than once I had headaches using formulas in a situation where, for example, I wanted to install nginx but I needed it pinned to a specific version. Expecting a formula author to re-implement every variable in pillar of every state method would be ridiculous but using a simple dictionary in pillar that can be expanded or not is a soft expose with hard overrides below it (relying on state compilation taking the last declaration of a particular key). It's not terribly complex to implement and makes the formula so much more flexible to individual use-cases. |
I agree whole-heartedly. I've run into this myself more than once. And using |
I agree. I played with doing overrides in the map file itself (as I did with the user.name at the bottom) and basically constructing fully-operational state declarations as dictionaries in the map.jinja but it felt like overkill. It's not fundamentally flawed but it does smell ever so slightly. As you said, I don't think there's a particularly good way around it. I would like the sls_block() macro to be unnecessary in the form of some type of generic addition to core states that accepts a dictionary and plays it back as default params for the state function that are then explicitly overridden by named parameters, eg:
Having that as a universal-starting point would reduce some of the formula clunk because we still want, or at least I still want, my map.jinja to be my sole source for data and specifically, the location I go to see what data defaults are being applied to my states. I like being able to look in there to see the big picture and know exactly what I want to override in pillar. It's easy for me because the merge approach ensures that the map.jinja is structured exactly the same as the pillar itself so it's a 1:1 api and only one type of comprehension split between two files (map and pillar). |
Well said. I'll shop the idea around the office of having a global override parameter of some sort. |
Since it's relevant, to see what the php macro looks like: https://github.com/spsoit/resourcespace-formula/blob/master/resourcespace/files/config.php |
Yeah, nice. Thanks for the link. |
FYI, we re-did the php formula with the same pattern (and added support for fpm pool management): https://github.com/spsoit/php-formula This one requires the use of odict([]) (added as a jinja global in Helium) because some of the php.ini variables are order-sensitive. I fear it's not enough because I don't think the data coming out of pillars preserves dict order (perhaps you could confirm). With this pattern, we really don't want to turn these into lists if only because it makes recursive merging impossible. I'm under the gun on a few projects so it might be a week or so before this one (and others) get properly doc'ed and submitted but thought you'd like a preview of the pattern at work again. |
Thanks for the preview. Using lists is simple and bullet-proof but point taken on that breaking merging values. Wonder if it's worthwhile making a merge algorithm that works for lists of single key/val dictionaries. (We may have one already for merging requisites.) |
I had wondered about that as a potential option but wanted to defer to your knowledge about it. Alternately, the blunt-instrument approach could be to make ordered dict the default mapping type. |
Hey Seth, I've got one more brainteaser for thought, a place where the current pattern falls a bit: How would you recommend implementing the vhosts management features of this state in other formulas? Here's the scenario: I have a webapp, "fooapp" and fooapp, needs to be hosted on some type of webserver. I can create an fooapp.nginx state that pulls in my nginx.ng states but I (currently) have no way to inject a vhost from my fooapp pillar/states into the dict of vhosts managed in the nginx.ng pillar. I can specify that pillar independently in the nginx pillar but it breaks the 'state must run by default' paradigm and means that I, as a fooapp user, have to know what to configure in that particular vhost config. The vhost management pieces are too complex for a simple extend and I can't inject anything directly because the current pattern uses direct imports from map.jinja. Do you k now of any way of instantiating the map variable once (eg, nginx), and treating it like some kind of global var where data could be modified? Any other ideas around this particular issue? |
Just thinking aloud of the larger limitations, I'm going to assume that jinja2 is reinstantiated for each template being rendered which means that jinja isn't the right place for any type of persistent store between state renders. How insane would it be to spin up another dunder var like salt and expose it in Jinja with write access, eg, Have any thoughts on that? |
Slow reply...and I'm a tad sleepy from yesterday's sprint. I'll think on this more but here's a short reply. There's no good way to have a global var that can be modified from different Pillar .sls files. Offhand I'd say the best way to approach it is to have a The "must run by default" without Pillar thing should be reworded to specify that applies to the base functionality in a formula. E.g. the expected thing (install a package, start a service). There are some things you simply can't do without some configuration; I think vhosts are a good example of this. |
Run into a hiccup while playing with this pattern today. The mapping test was introduced in Jinja 2.6 and Cent 6 ships with 2.2.1. A simple way to go would be to ship a duplicate Jinja test in Salt. |
Boo. Not awesome. As you said, it wouldn't be too difficult to pick that up. You should see the fun hoops I had to drive through because https://github.com/spsoit/php-formula/blob/master/php/ng/macro.jinja#L8 Added the macro file because it doesn't perform a pillar hit and doesn't require context. The only real issue I have with this approach is that it's not particularly recursion-friendly. After I go from an ordered dict into a list of single-key dicts, parsing a real list from a dict becomes quite the headache and would require a custom filter (not a macro) to deserialize since the return is a variable. Probably not a real project until late June or July but I do want to play with idea of spinning up some kind of singleton object that the templates can use for cross-talk (much in the same way that |
Interesting. Might be helpful to expose this function to Jinja. |
I haven't forgotten about this thread. Been swamped. Think I may have a good way to generate state yaml from a var that will work with an existing state... |
Really? I'm certainly interested, I've been on some other tasks myself in the last couple of weeks. Should be back on formulas in a day or two. The function you pointed out would be extremely helpful in jinja so I'm probably going to make that an early priority to see if I can jerry it in before the helium gets cut. |
I went to implement my idea...and it's already in there. {% set defaults = [
{'user': 'myuser'},
{'group': 'mygroup'},
] %}
myfile:
file:
- managed
- name: /tmp/myfile.txt
{{ defaults | yaml(flow_style=False) | indent(4) }} No where near as flexible as your |
Hah. Yep, I'm responsible for the flow_style patch. |
I added that anticipating helium but have been using sls_block() in formulas for compatibility with existing releases. It's been my hope to transition to flow_style | indent after helium. |
The reason I liked default_params was how explicit it is. Otherwise, what we rely upon is the parsing order (and the expectation that users know it). It is perhaps arguably more pythonic (and I say this as someone whose first language isn't python), to not be so explicit and use the flow_style option but just for readability, I prefer this:
to this:
|
By the way, flow_style is also amazing in config templates that are based in yaml. When you see my rewritten salt-formula I'm sure you can guess why. |
I like the sound of that. |
The whole config file template:
|
It's a thing of beauty. Death to Jinja! Long live Jinja! |
Hah. Yeah, I do feel a little guilty what with my heavy jinja formula patterns but I see it as a small evil for the much greater good of data-driven templates. Kinda flies in the face of the 'use less Jinja in states' practice recommendations. |
Yes, but there isn't a reasonable alternative that I can think of. ~50 lines of unreadable Jinja |
This PR adds the ng states for the nginx formula. This changes the pattern considerably and adds support for managing virtual hosts files. In the interest of disclosure, there are a couple caveats: