There's a lot of great design advice in here. Thank you for taking the time 
to write it up; I'll definitely do a deeper dive on this when I've solved 
my immediate problem and check this advice against some of the other rules 
I've written.  I often find Puppet to be as much of a mind-bender as 
writing Lisp if you are used to a straight procedural mindset.  Especially 
hard has been the lack of iteration.  I've avoided the temptation of 
turning on the future parser to get maps and lambdas since I think it would 
lead down the road of more straight scripting, as you've warned me, instead 
of staying in a declarative mindset.

Going through the Puppet Types and Providers book, it became obvious that 
'ensure' was what I really wanted instead of 'command'.  In the provider, 
ensure=>present would mean 'create',  ensure=>absent would mean 'delete' 
and your thoughts on doing some sort of replacement are interesting.  I 
might simplify it in my provider so the combination of ensure=>present and 
an is/should mismatch would be an implicit replace.  Maybe a value 
parameter for doing simple string replacements and a tree parameter  for 
adding an arbitrary path would be a nice addition.

For the moment, I just need to replace some existing values in an existing 
XML file, so I'm following YAGNI and probably won't implement the rest.  
This type is also hidden behind a type that models a java.util.Preference 
key and value, so the higher level type is much more Puppet-y.  The plan is 
ultimately to replace the xpath type with a simple Augeas call once its 
parsing bug is fixed. Augeas is a lot easier to deal with than straight 
XPath and doing a full featured XPath is way beyond the scope of what I'm 
doing.

I also did get the compound namevar working.  This time through it was much 
easier.  My first custom type was learning a lot of framework stuff and 
adding that undocumented bit was just too much.  My other type also 
implements prefetch and instances, which I think interacted badly with 
compound namevars in some fashion.  Now that I have a good working example, 
I might go back and play with this some more.

Thanks again for your help!

On Friday, February 13, 2015 at 11:47:42 AM UTC-5, jcbollinger wrote:
>
>
>
> On Thursday, February 12, 2015 at 1:53:25 PM UTC-6, [email protected] 
> wrote:
>>
>> Well, I was thinking of doing a type similar to what xmlstarlet does, 
>> including being able to add and remove nodes.  I don't really need the 
>> added behavior of adding and removing nodes from the file right now, so I 
>> left command as a future expansion parameter, but it basically only accepts 
>> 'replace' and is set to default to that.
>>
>
>
> You ran off the rails at "what xmlstarlet *does*" (emphsis added), and 
> went rumbling across the countryside with talk about "need[ing ...] added 
> *behavior*" (emphsis mine).  You are trying to model action rather than 
> state, and that tends to fit poorly into Puppet's scheme of things.
>
> It appears that the resource you want to model is a node (or maybe nodes) 
> in a user-specified XML file, as identified by an XPath expression.  That's 
> fine.  But what details of the state of such a resource can you / should 
> you model?  Here are some possibilities:
>
>    - presence / absence
>    - node value (== content for element nodes)
>    - attribute presence / absence / value when the resource is an element 
>    node
>
> In the event that you specify an instance absent, you could perhaps rely 
> on an optional parameter to support matched nodes being replaced with an 
> alternative node instead of being.
>
> In no case does a 'command' have anything to do with the state you are 
> (should be) modeling.  That's something that the type's provider should 
> determine internally in the event that a resource instance is out of sync.
>  
> Thus, you might support any or all of these, and perhaps more:
>
> # Matching nodes must not appear in the target file;
> # the mechanism for removing any that are present is to delete them
> xpath { 'Some node':
>       file => '/path/to/file.xml',
>       xpath => '/some/xpath',
>       ensure => 'absent'
> }
>
> or
>
> # Matching nodes must not appear in the target file;
> # the mechanism for removing any that are present is to replace them
> # with the specified alternative.
> xpath { 'Some node':
>       file => '/path/to/file.xml',
>       xpath => '/some/xpath',
>       ensure => 'absent',
>       replacement => '<different_node/>',
> }
>
> or
>
> # The target file must contain (one / all possible) matching element
> # nodes, their content must be as specified, and they must have
> # (at least / exactly) the specified attributes.
> xpath { 'Some node':
>   file => '/path/to/file.xml',
>   xpath => '/some/xpath',
>   ensure => 'present',
>   value => 'element content',
>   attributes => { 'a' => 'val1', 'b' => 'val2' }  
> }
>
> Types should focus on identifying resources and describing their 
> properties.  How to get from here to there (and how to determine whether 
> you are already there) is the domain of your type's provider(s), and should 
> be exposed via the type as little as possible.
>
>
> I'm only doing this type because Augeas doesn't like the particular XML 
>> file that has the value I need to change, but REXML parses it just fine.  I 
>> really just want to do what Augeas can do to files, just using XPaths 
>> instead.  I understand your point about the Puppet way of doing things.  
>> This type should really be saying "this XML document should have a path 
>> with this value" and, as my example is written, it looks more imperative 
>> than that.  In practice, I think will basically be declarative since the 
>> only operation the type can do is retrieving an existing XPath, comparing 
>> it to the desired value, and then setting it if it doesn't match.
>>
>
>
> Then you don't need a 'command' parameter at all.  It serves no useful 
> purpose, as the needed action, if any, is always determined by the current 
> state and the specified target state.  "[L]ooks more imperative than that" 
> is not an excuse, it's a red flag.
>
>  
>
>>
>> I haven't had a lot of success getting compound namevars to work.  I've 
>> added title_patterns to a different custom type before and done all of the 
>> basically undocumented steps for making it happen and hit a snag in the 
>> Puppet support code that meant my provider didn't get the values.  I have 
>> this type doing the XPath operations properly now, so I'll try to go back 
>> and tweak it according to the postgres example that Raphink linked and see 
>> if I can get compound namevars to work.  Hopefully you or the group will be 
>> able to get me on the right path if I can't get them working. :-)
>>
>
>
> Compound namevars are a feature that hasn't seen a lot of love, so I'm not 
> surprised to hear that it's uncomfortable to work with.  That doesn't 
> change that fact that it's by far the best match for what you describe.
>
> I'm glad you have your type functioning adequately.  You are welcome to 
> return for more advice later, but don't be surprised if you hear the same 
> thing when you do.
>
>
> John
>
>

-- 
You received this message because you are subscribed to the Google Groups 
"Puppet Users" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/puppet-users/eae681af-c961-452b-bc9e-33d672baec74%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to