Manage a single file (directory) from multiple modules

1,632 views
Skip to first unread message

aar...@gmail.com

unread,
Sep 19, 2014, 5:09:51 PM9/19/14
to puppet...@googlegroups.com
I have a single class for every application to be installed on our Macintosh clients.  The problem I am having is in the section of the class below.  This copies files to the User Template folder so when users login they get predefined preferences.  

file { "System/Library/User Template/English.lproj/Library/Preferences/":
    source  => "puppet:///modules/office_2011/Preferences",
    owner   => "root",
    group   => "wheel",
    mode    => 600,
    recurse => true,
    require => Package["$main_package"],
    }

This would work perfect if I had only one application in which I wanted managed preferences.  I am getting an error that the User Template folder is being managed by another class.  I understand why.  I also understand the purpose.  This code however only copies files into the Template Folder, it doesn't overwrite, or purge the folder.  So, in theory, what I want to do should be a valid approach to managing preferences.  

I have an idea, but don't know how to execute it.  So I hope someone can help.

In the Puppetlabs/Firewall module, you can access it from another module.  So if I have an apache module, I can tell the Firewall module I want to open port 80.

I would like to make a Macintosh Managed Preferences module that will manage the User Template folder, and in each application module, I can provide that module with files.  

How can I accomplish this?

Thanks in advance.  


Dan White

unread,
Sep 19, 2014, 6:06:15 PM9/19/14
to puppet...@googlegroups.com
Use the same approach the firewall module uses:  A main class, declared at a global level, creates/manages the folder. A defined type is used to create/manage imdividual prefs files and is involed inside the module for the individual app.

A basic pronciple of object-oriented code. Pull out the common bits and manage them in one place - in this case: the common folder. 

"Sometimes I think the surest sign that intelligent life exists elsewhere in the universe is that none of it has tried to contact us."

Bill Waterson (Calvin & Hobbes)

--
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 puppet-users...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/puppet-users/7fe782a1-fba4-4cef-aeab-4cf85ed170d2%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

aar...@gmail.com

unread,
Sep 19, 2014, 6:34:08 PM9/19/14
to puppet...@googlegroups.com
Glad the concept is correct, but I've been playing with it and am not sure which part of the firewall class processes the passed on code. In the firewall/apache analogy, you use the following code.

class apache {
  firewall { '100 allow http and https access':
    port   => [80, 443],
    proto  => tcp,
    action => accept,
}

What I am not seeing is what part of the firewall class receives this information and knows what to do with those parameters.  If I understood this, I could probably figure out my example.  

I tried in my situation

managed_preferences { 
       source  => "puppet:///modules/office_2011/Preferences",
       require => Package["$main_package"],
     }

thinking that it would send these two parameters to the newly create managed_preferences class.  Again, I don't know how to receive the information to process it in the managed_preferences class.   Is the managed_preferences class a different type of class than the office_2011 class?  If I knew the official terminology I could look it up and probably figure this out.  

Thanks again

aar...@gmail.com

unread,
Sep 19, 2014, 7:59:19 PM9/19/14
to puppet...@googlegroups.com
It appears I'm looking for a defined type to do this.   https://docs.puppetlabs.com/learning/definedtypes.html#special-little-flowers

I'll see if I can get this to work for my purpose though this article mentions duplicate definitions and I'm guessing I'll have the same problem.  

aar...@gmail.com

unread,
Sep 20, 2014, 12:46:05 PM9/20/14
to puppet...@googlegroups.com
I figured out how to create a define type and again it works for the first module, but when I add a second I get the exact same error which is what I expected based on the link below.

So, does anyone know how to accomplish this?  

Neil

unread,
Sep 20, 2014, 1:11:28 PM9/20/14
to PuppetList

Hi

You should not define it twice. You use it twice.

As in your example the apache uses the firewall define. It does not define the file resource again.

Define managed_preference in a class that you'll use as a library and use that in the modules that need it.

Note the resources using the define
each have their own name

Neil

aar...@gmail.com

unread,
Sep 20, 2014, 3:33:34 PM9/20/14
to puppet...@googlegroups.com
Thanks Neil,

I am not sure I understand exactly what you mean, so I will post the code I have done for testing (I am using different paths, but the concept is the same as the original post). 

Here is the class with the define.  It is in a module called copy_directory.

class copy_directory {
}

define managed_preferences ($source = undef) {
  file {"/tmp/":
    source  => "puppet:///modules/${source}/tmp",
    recurse => true,
    owner   => "root",
    group   => "root",
    mode    => 600,
  }
}

Here is the module that calls it.  

include copy_directory

class test_module {
  managed_preferences { 'title':
  source => "$module_name",
  }
}

I also created a module called test_module2 with the same code.  I know that file {"/tmp/": is causing the problem, but the entire point is different modules copy files into that directory. To me I'm defining it once, and using it twice.  As I defined it in the copy_directory module, and I am using it in the test_module and test_module2.   What am I doing wrong?

jcbollinger

unread,
Sep 22, 2014, 10:33:04 AM9/22/14
to puppet...@googlegroups.com


On Saturday, September 20, 2014 2:33:34 PM UTC-5, aar...@gmail.com wrote:
Thanks Neil,

I am not sure I understand exactly what you mean, so I will post the code I have done for testing (I am using different paths, but the concept is the same as the original post). 

Here is the class with the define.  It is in a module called copy_directory.

class copy_directory {
}

define managed_preferences ($source = undef) {
  file {"/tmp/":
    source  => "puppet:///modules/${source}/tmp",
    recurse => true,
    owner   => "root",
    group   => "root",
    mode    => 600,
  }
}

Here is the module that calls it.  

include copy_directory

class test_module {
  managed_preferences { 'title':
  source => "$module_name",
  }
}

I also created a module called test_module2 with the same code.  I know that file {"/tmp/": is causing the problem, but the entire point is different modules copy files into that directory. To me I'm defining it once, and using it twice.  As I defined it in the copy_directory module, and I am using it in the test_module and test_module2.   What am I doing wrong?



A class may be declared multiple times (though the resource-like syntax, if used, must be the first one evaluated), with the same effect as declaring it just once.  That is, classes are idempotent.  Ordinary resources, including defined-type instance, may be declared only once.  Given that defined types are usually meant to allow multiple instances, it follows that defined type instances must declare only resources that wholly belong to them; they must not not declare shared resources.  Instead, they can rely on a class to declare the shared resources.

For example:

class my_module::preferences_base {
  # A shared resource:
  file { '/etc/preferences':
    ensure => 'directory',
    owner  => 'root',
    owner  => 'group',
    mode   => '0644'
  }
}

define my_module::preference_file($source) {
  # relies on the preferences base directory:
  include 'my_module::preferences_base'

  # A file in the preferences base directory:
  file { "/etc/preferences/${title}":
    ensure => 'file',
    source => $source,
    owner  => 'root',
    owner  => 'group',
    mode   => '0644'
  }
}


That should also work if the the shared directory is managed recursively, or if the defined type declares a directory instead of a file -- even a recursively managed one.  Does that help?


John

aar...@gmail.com

unread,
Sep 22, 2014, 12:30:59 PM9/22/14
to puppet...@googlegroups.com
Thanks John,

By putting the /etc/preferences/${title} in the file line, aren't you putting a subfolder in the preferences folder on the puppet agent?  I tried to follow your example and it happened to me.  

What I am looking to do is to have different modules writing into the base folder, in your example "/etc/preferences/".  The modules won't ever have the same file names, so overwritting isn't a concern.

Is this possible?  

Neil - Puppet List

unread,
Sep 22, 2014, 3:27:44 PM9/22/14
to PuppetList
Hello

I do not think that is possible or really desirable. Puppet is about desired state so you can not say the directory should both be
/pathtodir
├── fileA
└── fileB

and /tmp should be
/pathtodir
├── fileB
└── fileC

as the first says must not have fileC and the second says must have fileC.  Those are conflicting.

Back to what you could do, which is to write to two separate directories and then merge them together with a separate command that is triggered by either directory changing. But you should not go down that route especially in a situation like this where you can specify exactly what files you want.

Going to your original post what you are trying to do is write the preferences for a particular application in the User Template for that application. Trouble is, looking on my mac, there are multiple plist files there for each application

so transform your
file { "System/Library/User Template/English.lproj/Library/Preferences/":
    source  => "puppet:///modules/office_2011/Preferences",
    owner   => "root",
    group   => "wheel",
    mode    => 600,
    recurse => true,
    require => Package["$main_package"],
    }

into

$components = ['com.microsoft.Excel.plist','com.microsoft.Outlook.plist',]

macprefs::usertemplate{ $components:
  require    => Package[$main_package],
  sourcebase => 'puppet:///modules/office_2011/Preferences',
}

(or that can be wrapped up into a define for this set of prefs,)

now you need to implement macprefs::usertemplate. (I've ignored the language/locale issue which could complicate matters)

so now you make a module and in there you do similar things to John's post

in a file macprefs/manifest/init.pp start by making "/System/Library/User Template"

class macprefs {
  # A shared resource:
  file { '/System/Library/User Template/English.lproj/Library/Preferences':
    ensure => 'directory',
    owner  => 'root',
    owner  => 'wheel',
    mode   => '0700'
  }
}

in a file macprefs/manifest/usertemplate.pp

define macprefs::usertemplate($sourcebase, $require) {

  # relies on the preferences base directory:
  include 'macprefs'

  # A file in the templates directory:
  file { "/System/Library/User Template/English.lproj/Library/Preferences/${title}":
    ensure  => 'file',
    source  => "$sourcebase/$title",
    require => $require,
    owner   => 'root',
    group   => 'wheel',
    mode    => '0600'
  }
}

----
Neil

--
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 puppet-users...@googlegroups.com.

aar...@gmail.com

unread,
Sep 22, 2014, 3:58:52 PM9/22/14
to puppet...@googlegroups.com, maillis...@iamafreeman.com
Thanks Neil,

In yours and Johns example you both have ${title} at the end of Preferences.  When I tried this (after his post, I haven't tried your code yet), I ended up with a folder called Preferences/office_2011 which defeats the purpose of what I am trying to do.  Am I misunderstanding that line?  I understand why two modules can't manage the same folder (from a Puppet perspective) but was thinking that all of my modules would send a list of files to the one module that had the ability to write to that folder and it would work.  

I really appreciate everyone trying to help me, but I guess there is something that I am not understanding, or perhaps I am not explaining myself properly.  If I have a variable after Preferences (which I need to prevent the duplicate resource) then how do I get all of my files into the root of that Preference folder?  

I apologize if your code works the way I am asking, but if it does, then I don't understand what the variable is doing after the word Preference and could use some clarification on that. Also, what is the $components variable mean?  Is that a type of function or feature that I can read up on in the documentation?

aar...@gmail.com

unread,
Sep 22, 2014, 5:51:38 PM9/22/14
to puppet...@googlegroups.com, maillis...@iamafreeman.com
I did the following to see if it would work, and I got (for me anyway) a surprising result.  It may be the source of some of my confusion and reason why I'm finding this so difficult.  Note, I don't want to do this this way.  I just did it as an experiment.  

define mac_managed_preferences ($source) {
  include managed_preferences

  file { "/System/Library/User Template/English.lproj/Library/Preferences/${source}" :
    source  => "puppet:///modules/${source}/Preferences/",
    owner   => "root",
    group   => "wheel",
    mode    => "600",
    recurse => "true",
    }

  exec { "Move Preferences":
    command => "mv $source/* ../Preferences/",
    path    => "/bin",
    cwd     => "/System/Library/User Template/English.lproj/Library/Preferences/",
    require => File["/System/Library/User Template/English.lproj/Library/Preferences/${source}"],
   }

  exec { "Delete Folder":
    command => "rm -rf $source",
    path    => "/bin/",
    cwd     => "/System/Library/User Template/English.lproj/Library/Preferences/",
    require => Exec["Move Preferences"],

  }
}

Here is the relevant portion of the module that installs the application.

    mac_managed_preferences { "$module_name":
    source => "$module_name",
    }


I got an error:  Duplicate declaration [Move Preferences] is already declared in file (path to managed_preferences file) cannot redeclare at (path to same file).  In my previous posts I said that I though that I accessed the module multiple times, but declared it once, but this message is making me understand that puppet says I was declaring it multiple times, but I am unsure how.  I know two modules are accessing it, but felt that was what I was supposed to do.  To make things more confusing, the command has a variable in it (which is why it didn't error on the file part), but the working directory is static and is causing the problem.  But what if my command was just "ls" would it complain about the same cwd when it would cause no harm?  How does puppet know what my command would do anyway?  What am I not understanding in this example that is causing the redeclaring error?  Is it the same principle in the other posts where the file resource was causing the problem?

jcbollinger

unread,
Sep 23, 2014, 9:40:06 AM9/23/14
to puppet...@googlegroups.com


On Monday, September 22, 2014 2:58:52 PM UTC-5, aar...@gmail.com wrote:
Thanks Neil,

In yours and Johns example you both have ${title} at the end of Preferences.  When I tried this (after his post, I haven't tried your code yet), I ended up with a folder called Preferences/office_2011 which defeats the purpose of what I am trying to do.  Am I misunderstanding that line?


I'm not sure exactly what you are misunderstanding.  Myself, I am evidently having trouble understanding either exactly what it is you are trying to do, or else why you cannot use the approach I outlined to do it.

Yes, the point of adding "/${title}" to the end of the name of the managed File resource is to make that resource manage a subdirectory of the Preferences directory (or a plain file in that directory) instead of the Preferences directory itself.  This supposes that each defined type instance will manage some one or more files in that directory, unique to that instance, rather than the directory itself.  That is the only way it can work to split the management across multiple defined type instance, as no two instances can manage the same resource. 

 
 I understand why two modules can't manage the same folder (from a Puppet perspective) but was thinking that all of my modules would send a list of files to the one module that had the ability to write to that folder and it would work.  



There is no "sending around lists of files".  Each File resource specifies state for exactly one file/directory, and there must not be multiple specifications for the same file (even if they are identical).  The declaration of a recursive File resource causes additional File resources to be declared implicitly, based on the File's source tree on the master, where explicit File resources corresponding to those files are not declared.

 
I really appreciate everyone trying to help me, but I guess there is something that I am not understanding, or perhaps I am not explaining myself properly.  If I have a variable after Preferences (which I need to prevent the duplicate resource) then how do I get all of my files into the root of that Preference folder?  


You are missing the point.  Duplicate resources are not fundamentally about resource titles.  That's ancillary.  They are about having exactly one specification for each target resource to be managed on the client.
 


I apologize if your code works the way I am asking, but if it does, then I don't understand what the variable is doing after the word Preference and could use some clarification on that. Also, what is the $components variable mean?  Is that a type of function or feature that I can read up on in the documentation?



Neil's example code demonstrates another variation on the same approach I presented.  The general idea is that each for each application for which preferences are to be managed, the corresponding defined type instance must declare only the files / subdirectories of the user's Preferences folder that belong to that application.

Neil and I both suppose that the preferences for each application are in fact segregated from one another in separate files or subdirectories of the Preferences directory.  If that's not the case then you need a different approach.


John

jcbollinger

unread,
Sep 23, 2014, 9:49:10 AM9/23/14
to puppet...@googlegroups.com


On Monday, September 22, 2014 4:51:38 PM UTC-5, aar...@gmail.com wrote:
I did the following to see if it would work, and I got (for me anyway) a surprising result.  It may be the source of some of my confusion and reason why I'm finding this so difficult.  Note, I don't want to do this this way.  I just did it as an experiment.  

define mac_managed_preferences ($source) {
  include managed_preferences

  file { "/System/Library/User Template/English.lproj/Library/Preferences/${source}" :
    source  => "puppet:///modules/${source}/Preferences/",
    owner   => "root",
    group   => "wheel",
    mode    => "600",
    recurse => "true",
    }

  exec { "Move Preferences":
    command => "mv $source/* ../Preferences/",
    path    => "/bin",
    cwd     => "/System/Library/User Template/English.lproj/Library/Preferences/",
    require => File["/System/Library/User Template/English.lproj/Library/Preferences/${source}"],
   }

  exec { "Delete Folder":
    command => "rm -rf $source",
    path    => "/bin/",
    cwd     => "/System/Library/User Template/English.lproj/Library/Preferences/",
    require => Exec["Move Preferences"],

  }
}

Here is the relevant portion of the module that installs the application.

    mac_managed_preferences { "$module_name":
    source => "$module_name",
    }


I got an error:  Duplicate declaration [Move Preferences] is already declared in file (path to managed_preferences file) cannot redeclare at (path to same file).  In my previous posts I said that I though that I accessed the module multiple times, but declared it once, but this message is making me understand that puppet says I was declaring it multiple times, but I am unsure how.


Multiple declaration is not about how many times a resource appears in your manifests files; it is about how many times it appears in the catalog built from the manifests.  Every instance of a defined type added to the catalog carries a declaration of each resource declared in the type definition's body.  Each declaration of a defined type creates a separate instance.  E.g.

# two instances of defined type
# my_module::my_type:
my_module::my_type { 'foo': }
my_module::my_type { 'bar': }

That's why I said earlier that "defined type instances must declare only resources that wholly belong to them; they must not not declare shared resources".  Let me now augment that by pointing out that any resource that is not specific to a defined-type instance cannot wholly belong to that instance, at least when multiple instances of the type are declared.


John

aar...@gmail.com

unread,
Sep 23, 2014, 11:23:36 AM9/23/14
to puppet...@googlegroups.com
John,

I would like to re-explain the problem I am trying to solve to make sure that what I want to do is possible, and that it can be done in the way you are trying to help me.

In my first post, I mentioned wanting different modules to write to the /System/Library/User Template/English.lproj/Library/Preferences/ folder.  Which I will now just call preferences.

I have two modules I am working on, one installs Office 2011, and one installs Maya 2012.  Office has three files that I want to put in the preferences folder, and Maya has one.

So the tree on each client would be:

/preferences/com.microsoft.*
/preferences/com.autodesk.*

As you can see, the files are in the root of the preferences folder, not subfolders of it.  

It is possible other modules (applications I install) may have individual files in the root of preferences and a subfolder, or just a subfolder with preferences inside.

I envision on my master I would have the preferences that I need copied to the preferences of the client to be in the modules/office_2012/files/preferences folder.  

I hope this clears the problem up and I have an idea for another solution if the above approach won't work.

Is it possible for puppet to read the files in the preferences folder on the master in a loop and the file name be a variable so that the file type (/Preferences/${variable}) would be the correct destination?  

aar...@gmail.com

unread,
Sep 23, 2014, 1:46:51 PM9/23/14
to puppet...@googlegroups.com
I thought I had it, but I don't.  I will post my code and explain what I think it is doing.  Perhaps someone can see my misunderstanding.

This is init.pp in a module called managed_preferences.

class managed_preferences {
}

define mac_managed_preferences ($application) {  #application variable is the name of the application from the module which installs the application
  include managed_preferences

  file { "/System/Library/User Template/English.lproj/Library/Preferences/${source}" :  # the source is the file from the line directly below, this seems true as the correct files copy when I have only one application
    source  => "puppet:///modules/${application}/Preferences/",  #application from the module with the install command, finds the files I want to copy to the client in the /Preferences folder.  In this example office_2011
    owner   => "root",
    group   => "wheel",
    mode    => "600",
    recurse => "true",
    }

This is code from init.pp from a module called office_2011 which installs the application. 

    mac_managed_preferences { "$module_name":    
      application => $module_name,     # resolves to the folder or class name of the application, allows path to be resolved for the Preferences folder on the server to be located.
    }

When I have both applications assigned to the client, I get the duplicate declaration.  When only one is assigned, it works as expected.

The duplication error is as follows:  Duplicate declaration: File[/System/Library/User Template/English.lproj/Library/Preferences/]  

Having variables in the source and destination is what I though I needed.  I feel that the declaration is duplicate because I am declaring file ..../Preferences/$(source) which is different for each module that communicates with this one.  

I know it has been explained, and I just don't understand.  How is mac_managed_preferences declared twice?

Sorry and thanks.

Neil - Puppet List

unread,
Sep 23, 2014, 7:35:53 PM9/23/14
to PuppetList

file { "/System/Library/User Template/English.lproj/Library/Preferences/${source}" :  # the source is the file from the line directly below, this seems true as the correct files copy when I have only

No its just not like that

$source would need to be defined in or passed to the scope your file resource is in.
It does not refer to the source parameter to the file resource itself.

To try and get back on the right track i suggest you try just putting two files in place one from each calling class.

I really don't think a recursive directory is what you want. And I don't think having two of them will ever work.

You should be specifying the names of the individual files.

Please post your complete code

Neil

--
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 puppet-users...@googlegroups.com.

jcbollinger

unread,
Sep 24, 2014, 10:52:40 AM9/24/14
to puppet...@googlegroups.com


On Tuesday, September 23, 2014 6:35:53 PM UTC-5, Neil - Puppet List wrote:

file { "/System/Library/User Template/English.lproj/Library/Preferences/${source}" :  # the source is the file from the line directly below, this seems true as the correct files copy when I have only

No its just not like that

$source would need to be defined in or passed to the scope your file resource is in.
It does not refer to the source parameter to the file resource itself.


In particular, in the example code it evaluates to an empty string.  That's why the files are going to the expected location when the define is used only once, and why there is a duplicate resource when the define is used more than once.

 

To try and get back on the right track i suggest you try just putting two files in place one from each calling class.

I really don't think a recursive directory is what you want. And I don't think having two of them will ever work.


No, having two declarations of the Preferences directory will never work, but see below.

 

You should be specifying the names of the individual files.


That is the way I would recommend doing it.  Recursive directories cause no end of headaches.

Nevertheless, although you cannot declare the directory multiple times, you can give it it multiple source directories and instruct it to use them all.  That would be closer to the approach currently being attempted.  It might look something like this:

#
# Class parameter $applications is expected to provide either
# a single application (module) name or an array of such names.
#
class mac_managed_preferences($applications) {
  $preference_sources = regsubst($applications, '^.*$', 'puppet:///modules/\0/Preferences')

  file { '/System/Library/User Template/English.lproj/Library/Preferences':
    ensure => 'directory',

    owner => 'root',
    group => 'wheel',
    mode => '0600',
    recurse => true,
    source => $preference_sources,
    sourceselect => 'all'
  }
}


John

Message has been deleted

aar...@gmail.com

unread,
Sep 24, 2014, 3:38:40 PM9/24/14
to puppet...@googlegroups.com
Neil and John,

You guys both agree on using individual files and I was thinking of how I could do this.  If I have one file or three, it may not be a big deal to do individually, but some applications could need dozens or more.  

I don't know if it is possible, some searching makes me believe it isn't, but I'll share my thought.

Can you do something like a for loop in a bash script.  So it would be something like...


for files in .../module_name/Preferences/  # get a list of files and folders in the Preferences subfolder on the Puppet Master
  file { '/System/Library/User Template/English.lproj/Library/Preferences/${files}':  #Directory on Puppet agent, the variable would make this a unique resource and it would work for a file and folder
  source => "puppet:///modules/${module_name}/Preferences/${files}", # Since it is looping every file on the Puppet Master would match perfectly to the same exact file/folder on the agent.  No duplicate declaration.
done

John,

I think I understand what your code is trying to do, but am not sure I understand how to implement.  I think that $preference_sources is making a list of paths (puppet paths) to the Preferences folder of each module, and I know that sourceselect all means that it will use all sources instead of stopping at the first match.  I also see that the preferences_sources is being created from a regex expression that I don't completely understand, though that shouldn't matter in getting this to work.  

Is this still a defined type?  Should class mac_managed_preferences be defined or are we making a new class for each applications preferences (assuming that's the $applications variable in the class name)?  I tried it as a defined type, and I once again got it to work with one application but not two.  Which makes sense as two applications are calling it.  If it is a unique class and not a defined type, then wouldn't this class apply preferences for applications on machines that don't have them?   

If it still is a defined type, then this is the code that caused a duplicate declaration below.  Perhaps a syntax issue?   This is the only syntax where I could get things to run.  If this code is for a unique class, then I think I need a bit more description in how it is to work.  

class managed_preferences {}
  define mac_managed_preferences ($application) {
  include managed_preferences

  $preference_sources = regsubst($application, '^.*$', 'puppet:///modules/\0/Preferences')

jcbollinger

unread,
Sep 25, 2014, 9:49:20 AM9/25/14
to puppet...@googlegroups.com


On Wednesday, September 24, 2014 2:38:40 PM UTC-5, aar...@gmail.com wrote:
Neil and John,

You guys both agree on using individual files and I was thinking of how I could do this.  If I have one file or three, it may not be a big deal to do individually, but some applications could need dozens or more.  

I don't know if it is possible, some searching makes me believe it isn't, but I'll share my thought.

Can you do something like a for loop in a bash script.  So it would be something like...


You can if you turn on the future parser, but there's an old-school way to approach the problem that might actually be better in your case, because it would be reusable across multiple application modules.  The old-school way revolves around Puppet's behavior when an array is provided as a resource title, which is to declare a separate resource for each element of the array.  When the declared resource is of a defined type, it's body is evaluated once for each title, which yields a pretty good analog of iteration.  Example:

define my_module::mac_preference_file ($application) {
  include my_module::mac_preferences_base

  file { "/System/Library/User Template/English.lproj/Library/Preferences/${title}":
    ensure => 'file',

    owner => 'root',
    group => 'wheel',
    mode => '0600'
    source => "puppet:///modules/${application}/Preferences/${title}"
  }
}


# Manages the base preferences directory.
# Must be a class so that it can be declared multiple times..
class my_module::mac_preferences_base {
  # Note: not recursive.  In this version, all managed contents
  # are managed by separate File resources.

  file { "/System/Library/User Template/English.lproj/Library/Preferences":
    ensure => 'directory',
    owner => 'root',
    group => 'wheel',
    mode => '0600'
  }
}


That might be used like so:

class some_application {
  $my_preference_files = [ 'some.plist', 'another.plist', 'random.plist' ]

  my_module::mac_preference_file { $my_preference_files:
    application => 'some_application'
  }

  # ...
}

 


for files in .../module_name/Preferences/  # get a list of files and folders in the Preferences subfolder on the Puppet Master
  file { '/System/Library/User Template/English.lproj/Library/Preferences/${files}':  #Directory on Puppet agent, the variable would make this a unique resource and it would work for a file and folder
  source => "puppet:///modules/${module_name}/Preferences/${files}", # Since it is looping every file on the Puppet Master would match perfectly to the same exact file/folder on the agent.  No duplicate declaration.
done

John,

I think I understand what your code is trying to do, but am not sure I understand how to implement.  I think that $preference_sources is making a list of paths (puppet paths) to the Preferences folder of each module,


Yes, the approach depends on creating an array of all the source directories from which to sync files into the master Preferences folder.  You cannot manage the Preferences folder more than once, but you can give it multiple sources.

 
and I know that sourceselect all means that it will use all sources instead of stopping at the first match.  I also see that the preferences_sources is being created from a regex expression that I don't completely understand, though that shouldn't matter in getting this to work.  

Is this still a defined type?


No, it is a class.  That's what "class" instead of "define" at the beginning means.  There's no point to making it a defined type, because any attempt to declare it (as a resource) more than once will fail with a duplicate resource error.  Also, if you make it a class then you have the option of using automated data binding to assign the parameter value (via Hiera).

 
 Should class mac_managed_preferences be defined


Well, yes.  Its definition is what I presented.  Do you mean to ask whether it should be declared?  Also yes.  A class definition by itself does nothing for you.  In this case, you make an array of the names of all the application modules whose preferences you want to manage, and assign that array as the class's parameter.  If you do that via Hiera and automated data binding, then you can declare the mac_managed_preferences class as many times as you like, using the 'include' function.  If you use a resource-like class declaration, however, then it must not be evaluated more than once.

 
or are we making a new class for each applications preferences (assuming that's the $applications variable in the class name)?


There is no variable in the class name.  The class has a parameter named $applications, but it is not part of the class name.  You are already supposing that there is a separate (and different) class for each managed application, however.  In fact, you are supposing that there is an entirely separate module for each managed application.  Class mac_managed_preferences would be a single class, separate from all the per-application classes.  Whatever class or node block declares all the per-application classes (and which therefore knows which ones are being declared) would also declare class mac_managed_preferences.


Overall, you seem to be really struggling with a lot of fundamental Puppet concepts.  Puppet DSL is substantially unlike any programming language you may have used (and indeed, it is not itself a programming language), so it would be to your advantage to read the language reference section of the Puppet reference manual, and to at least skim the resource type reference and the function reference.  The language reference is an easy read, and quite good.


John

aar...@gmail.com

unread,
Sep 25, 2014, 2:06:38 PM9/25/14
to puppet...@googlegroups.com
John,

I got it to work with the array.  I had to remove the base class in your code otherwise that folder gets declared multiple times.  After I did that, it pretty much worked as expected.

I like the idea of the loop and looked up future parser.  I couldn't find any examples that I was comfortable with so again, the array is great for now.  I will revisit this thread when I have a better grasp of puppet overall as I feel you gave great advice and examples that for right now I am just not getting.  Thanks for your patience with me on this as I learned a lot in the process. 
Reply all
Reply to author
Forward
0 new messages