Puppet does not run managedsoftwareupdate or check its return status. It trusts Munki to do its job. Puppet manages Munki, and trusts Munki to do its part, not micromanaging it.This is the only way currently to integrate Puppet with Munki without stomping all over the interface niceities of Munki - things like notifying users of updates, not installing updates that require a logout or restart without the user's consent, and not updating applications the user is currently using.One refinement of this might be to have Puppet run managedsoftwareupdate --auto if Puppet has made changes to the local manifest. In this case, if the machine is at the login window, the installs and removals will happen right away. If there is a user logged in, they _may_ get a notification of available updates, depending on notification settings and how long it's been since the last notification.Now, experienced Puppeteers might be thinking "this isn't like existing package providers for Puppet at all!" and they'd be right. But Munki is not a package manager, so it can't be integrated like one.I think this proposal is a good first step at integrating Puppet and Munki. More might be done in the future as we figure out how best to reconcile the very different approaches of Puppet and Munki.
I think this proposal is a good first step at integrating Puppet and Munki. More might be done in the future as we figure out how best to reconcile the very different approaches of Puppet and Munki.The main difference that Greg's referring to is that Puppet's job is to ensure an end-state. If you tell it to install a package, it will check to see if it exists, install it (if it doesn't exist), and check AGAIN after installation to ensure that it has actually been installed. If the final check fails, then it reports an error and it will try again at the next run (And all resources that DEPEND on that package will NOT get executed on this run).To create a package provider in Puppet that is Ensurable (i.e. that can ensure a package absent and present), you need at least three methods: create(), exists?(), and destroy(). These methods will be used, respectively, to install the package, check for its presence on the system, and uninstall the package. A package provider doesn't need to be ensurable (i.e. it can have a method to INSTALL but not to UNINSTALL - this is what the current pkgdmg provider does). Basically, if you implement what Greg recommends then you would only be checking the local manifest shipped to Munki and not the status of the package itself. Is it in the manifest? Then the exists?() method will return true. And vice-versa.
I agree that this would be a good starting point, but there's also room to wreck Munki's interface niceities too (and treat it like a bastard-package-manager-stepchild) ;)
Here's some code to get someone started writing a better provider for Puppet that can use Munki's infrastructure. Tested with the current Git revision:
#!/usr/bin/python
# encoding: utf-8
#
# Copyright 2009-2011 Greg Neagle.
#
# Licensed under the Apache License, Version 2.0 (the 'License');
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an 'AS IS' BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""
mean_munki
"""
import sys
import subprocess
from munkilib import updatecheck
from munkilib import installer
localmanifestpath = None
if len(sys.argv) == 2:
localmanifestpath = sys.argv[1]
updatecheckresult = updatecheck.check(localmanifestpath=localmanifestpath)
if updatecheckresult == 1:
need_to_restart = installer.run()
if need_to_restart:
unused_retcode = subprocess.call(['/sbin/shutdown', '-r', 'now'])
I agree that munki is not a package manager which requires processes
for installing, upgrading, configuring, and removing software packages
[ http://en.wikipedia.org/wiki/Package_management_system ]. Munki does
do three out of the four however very well. One could choose to inject
configuration of the software as packages, which means they do not
need any other tool. I find this way of doing configuration too
cumbersome because it means even simple configs have to be put through
a packaging process before being deployed. Being able to change a
single file on hundreds of Macs by changing one file in a version
control system and pushing a button is a much more desirable work
flow.
I politely disagree with Greg N. that adding a package to
managed_installs or unmanaged_installs is the limit of what munki can
do for a cfgmgt system. By defining the receipts needed to install the
vast universe of install "packages" that munki can handle, it makes it
possible use tools instead of mouse clicks to manage software on a
Mac. The MSU.app is just a nice bonus compared to this inovation
(THANK YOU! THANK YOU!).
A replacement for managedsoftwareupdate could be written that let's
the cfgmgt tool issue the command to install a package and leverage
the pkgsinfo files to do the install and verification. The cfgmgt tool
could also use munki methods and the pkgsinfo files to get the status
of an install. And finally, the cfgmgt tool could use munki methods to
uninstall a package. This senario ignores the user of the machine.
A local manifest would be a way of making similar changes but not
ignoring the user. In this case verification would have to be a
function of time as installation would not necessarily happen
immediately. The verification method would need to know when the local
manifest changed, when the last munki run occurred, and other cases
that would allow it to decide how to report the state of the managed
machine (is it in compliance or not?).
<joke>I'm just wondering when will Apple make a hostel take over of
Munki and some cfgtmgt tool and incorporate it in to an Enterprise App
Store.</joke>
Raúl
I politely disagree with Greg N. that adding a package to
managed_installs or unmanaged_installs is the limit of what munki can
do for a cfgmgt system. By defining the receipts needed to install the
vast universe of install "packages" that munki can handle, it makes it
possible use tools instead of mouse clicks to manage software on a
Mac. The MSU.app is just a nice bonus compared to this inovation
(THANK YOU! THANK YOU!).
A replacement for managedsoftwareupdate could be written
On 2012-03-29, at 6:49 AM, Allister Banks wrote:
> Purpose: When Mac workstations are being prepared or repurposed, and
> ongoing during maintenance periods for servers, Munki should be relied
> upon as Puppets package provider. While Munki already offers packages
> to clients and enables some level of enforcement, this will allow
> Puppet to have a better method of deploying/ensuring/deactivating
> services, and ensure Puppet is aware of the most critical packages
> when in simulation mode.
I am getting ready to deploy Puppet for our Macs, but we have already been running Munki for a long time.
After reading this, I am still not clear what the advantage of a munki provider would be.
> Implementation: Puppet would maintain two files:
> ManagedInstalls.plist(Munki's primary config file)
Something like this is what Puppet is designed to do. Other than the fact ManagedInstall.plist will be a binary plist,
you should have no difficulties managing this file with Puppet using a number of techniques...
- wholesale (using the File type)
- by key (using Exec type)
- by mcx (using Mcx type)
- some combo of these
> and a 'LocalMunkiManifest' file that Munki would operate with.
So, the Puppet provider would manage this file?
How is this different from simply distributing said manifest as file using Puppet?
> The admin would specify the name of the package, and Puppet would ensure that
> setting is present in the LocalMunkiManifest file and ask Munki to
> operate with that configuration. When Puppet is restricted from
> causing changes on the system it's managing, Munki should still be
> allowed to operate with its GUI client, and this facet would
> effectively be dormant unless deemed necessary(i.e. if orchestration
> is requested via MCollective calling Puppet).
So, now you have two places where you can model package deployment?
Perhaps I misunderstand?
--
B.
./munki_do.py --catalog production --install Firefox
Checking for available updates...
Retreiving catalog "production"...
0..20..40..60..80..100
Downloading Firefox 10.0.3esr.dmg...
0..20..40..60..80..100
Verifying package integrity...
Downloading DA_FirefoxConfig-6.0.pkg.dmg...
0..20..40..60..80..100
Verifying package integrity...
The following items will be installed or upgraded:
+ Firefox-10.0.3
Web browser from Mozilla
+ DA_FirefoxConfig-6.0
Configures Firefox for use at Disney Animation. Installs Timmy search plugin.
Installing Mozilla Firefox (1 of 2)...
Mounting disk image Firefox 10.0.3esr.dmg...
Copying Firefox.app to /Applications...
The software was successfully installed.
Running postinstall_script for Firefox...
Installing Disney Animation Firefox Configuration (2 of 2)...
Mounting disk image DA_FirefoxConfig-6.0.pkg.dmg...
Preparing for installation…
Preparing the disk…
Preparing Disney Animation Firefox Configuration…
Waiting for other installations to complete…
Configuring the installation…
97.75 percent complete...
Finishing the Installation…
100.0 percent complete...
The software was successfully installed.
-------------------
#!/usr/bin/python
# encoding: utf-8
#
# Copyright 2009-2011 Greg Neagle.
#
# Licensed under the Apache License, Version 2.0 (the 'License');
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an 'AS IS' BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""
munki_do
"""
import optparse
import subprocess
import sys
from munkilib import FoundationPlist
from munkilib import updatecheck
from munkilib import installer
p = optparse.OptionParser()
p.add_option('--catalog', '-c', action="append",
help='Which catalog to consult. May be specified multiple times.')
p.add_option('--install', '-i', action="append",
help='An item to install. May be specified multiple times.')
p.add_option('--uninstall', '-u', action="append",
help='An item to uninstall. May be specified multiple times.')
options, arguments = p.parse_args()
manifest = {}
manifest['catalogs'] = options.catalog or ['production']
manifest['managed_installs'] = options.install or []
manifest['managed_uninstalls'] = options.uninstall or []
FoundationPlist.writePlist(manifest, '/tmp/localmanifest.plist')
updatecheckresult = updatecheck.check(
localmanifestpath='/tmp/localmanifest.plist')
if updatecheckresult == 1:
need_to_restart = installer.run()
if need_to_restart:
print "Please restart immediately!"
...and with that, I think I've demonstrated all the essential functionality for a Munki Puppet package provider. Puppeteers: go forth and do good works.
-Greg
--------------------
#!/usr/bin/python
# encoding: utf-8
#
# Copyright 2009-2012 Greg Neagle.
#
# Licensed under the Apache License, Version 2.0 (the 'License');
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an 'AS IS' BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""
munki_do
"""
import optparse
import subprocess
import sys
from munkilib import FoundationPlist
from munkilib import updatecheck
from munkilib import installer
from munkilib import munkicommon
p = optparse.OptionParser()
p.add_option('--catalog', '-c', action="append",
help='Which catalog to consult. May be specified multiple times.')
p.add_option('--install', '-i', action="append",
help='An item to install. May be specified multiple times.')
p.add_option('--uninstall', '-u', action="append",
help='An item to uninstall. May be specified multiple times.')
p.add_option('--checkstate', action="append",
help='Check the state of an item. May be specified multiple times.')
options, arguments = p.parse_args()
cataloglist = options.catalog or ['production']
if options.checkstate:
updatecheck.MACHINE = munkicommon.getMachineFacts()
updatecheck.CONDITIONS = munkicommon.getConditions()
updatecheck.getCatalogs(cataloglist)
for check_item in options.checkstate:
installed_state = 'unknown'
item_pl = updatecheck.getItemDetail(check_item, cataloglist)
if item_pl:
if updatecheck.installedState(item_pl):
installed_state = "installed"
else:
installed_state = "not installed"
print "%s: %s" % (check_item, installed_state)
if not options.install and not options.uninstall:
exit()
manifest = {}
manifest['catalogs'] = cataloglist
'twas a proof of concept with demo-quality code. I have no idea what Puppet expects or is looking for. Someone more familiar with Puppet can take my example and go from there.