summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog1
-rw-r--r--cloudinit/config/cc_landscape.py2
-rw-r--r--cloudinit/config/cc_puppet.py10
-rw-r--r--cloudinit/config/cc_salt_minion.py2
-rw-r--r--cloudinit/distros/debian.py17
-rw-r--r--cloudinit/distros/rhel.py16
-rw-r--r--cloudinit/util.py32
-rw-r--r--doc/examples/cloud-config-install-packages.txt4
8 files changed, 69 insertions, 15 deletions
diff --git a/ChangeLog b/ChangeLog
index be861119..e1b08d30 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -33,6 +33,7 @@
to operate on the family (redhat or debian) rather than the distro (ubuntu,
debian, fedora, rhel) (LP: #1100029)
- fix /etc/hosts writing when templates are used (LP: #1100036)
+ - add package versioning logic to package installation functionality (LP: #1108047)
0.7.1:
- sysvinit: fix missing dependency in cloud-init job for RHEL 5.6
- config-drive: map hostname to local-hostname (LP: #1061964)
diff --git a/cloudinit/config/cc_landscape.py b/cloudinit/config/cc_landscape.py
index 02610dd0..2efdff79 100644
--- a/cloudinit/config/cc_landscape.py
+++ b/cloudinit/config/cc_landscape.py
@@ -62,7 +62,7 @@ def handle(_name, cfg, cloud, log, _args):
if not ls_cloudcfg:
return
- cloud.distro.install_packages(["landscape-client"])
+ cloud.distro.install_packages(('landscape-client',))
merge_data = [
LSC_BUILTIN_CFG,
diff --git a/cloudinit/config/cc_puppet.py b/cloudinit/config/cc_puppet.py
index e9a0a0f4..471a1a8a 100644
--- a/cloudinit/config/cc_puppet.py
+++ b/cloudinit/config/cc_puppet.py
@@ -59,8 +59,14 @@ def handle(name, cfg, cloud, log, _args):
# Start by installing the puppet package if necessary...
install = util.get_cfg_option_bool(puppet_cfg, 'install', True)
- if install:
- cloud.distro.install_packages(["puppet"])
+ version = util.get_cfg_option_str(puppet_cfg, 'version', None)
+ if not install and version:
+ log.warn(("Puppet install set false but version supplied,"
+ " doing nothing."))
+ elif install:
+ log.debug(("Attempting to install puppet %s,"),
+ version if version else 'latest')
+ cloud.distro.install_packages(('puppet', version))
# ... and then update the puppet configuration
if 'conf' in puppet_cfg:
diff --git a/cloudinit/config/cc_salt_minion.py b/cloudinit/config/cc_salt_minion.py
index f3eede18..53013dcb 100644
--- a/cloudinit/config/cc_salt_minion.py
+++ b/cloudinit/config/cc_salt_minion.py
@@ -31,7 +31,7 @@ def handle(name, cfg, cloud, log, _args):
salt_cfg = cfg['salt_minion']
# Start by installing the salt package ...
- cloud.distro.install_packages(["salt-minion"])
+ cloud.distro.install_packages(('salt-minion',))
# Ensure we can configure files at the right dir
config_dir = salt_cfg.get("config_dir", '/etc/salt')
diff --git a/cloudinit/distros/debian.py b/cloudinit/distros/debian.py
index 49b73477..1a8e927b 100644
--- a/cloudinit/distros/debian.py
+++ b/cloudinit/distros/debian.py
@@ -65,7 +65,7 @@ class Distro(distros.Distro):
def install_packages(self, pkglist):
self.update_package_sources()
- self.package_command('install', pkglist)
+ self.package_command('install', pkgs=pkglist)
def _write_network(self, settings):
util.write_file(self.network_conf_fn, settings)
@@ -142,15 +142,24 @@ class Distro(distros.Distro):
# This ensures that the correct tz will be used for the system
util.copy(tz_file, self.tz_local_fn)
- def package_command(self, command, args=None):
+ def package_command(self, command, args=None, pkgs=[]):
e = os.environ.copy()
# See: http://tiny.cc/kg91fw
# Or: http://tiny.cc/mh91fw
e['DEBIAN_FRONTEND'] = 'noninteractive'
cmd = ['apt-get', '--option', 'Dpkg::Options::=--force-confold',
- '--assume-yes', '--quiet', command]
- if args:
+ '--assume-yes', '--quiet']
+
+ if args and isinstance(args, str):
+ cmd.append(args)
+ elif args and isinstance(args, list):
cmd.extend(args)
+
+ cmd.append(command)
+
+ pkglist = util.expand_package_list('%s=%s', pkgs)
+ cmd.extend(pkglist)
+
# Allow the output of this to flow outwards (ie not be captured)
util.subp(cmd, env=e, capture=False)
diff --git a/cloudinit/distros/rhel.py b/cloudinit/distros/rhel.py
index e65be8d7..2f91e386 100644
--- a/cloudinit/distros/rhel.py
+++ b/cloudinit/distros/rhel.py
@@ -63,7 +63,7 @@ class Distro(distros.Distro):
self.osfamily = 'redhat'
def install_packages(self, pkglist):
- self.package_command('install', pkglist)
+ self.package_command('install', pkgs=pkglist)
def _adjust_resolve(self, dns_servers, search_servers):
try:
@@ -208,7 +208,7 @@ class Distro(distros.Distro):
# This ensures that the correct tz will be used for the system
util.copy(tz_file, self.tz_local_fn)
- def package_command(self, command, args=None):
+ def package_command(self, command, args=None, pkgs=[]):
cmd = ['yum']
# If enabled, then yum will be tolerant of errors on the command line
# with regard to packages.
@@ -219,9 +219,17 @@ class Distro(distros.Distro):
# Determines whether or not yum prompts for confirmation
# of critical actions. We don't want to prompt...
cmd.append("-y")
- cmd.append(command)
- if args:
+
+ if args and isinstance(args, str):
+ cmd.append(args)
+ elif args and isinstance(args, list):
cmd.extend(args)
+
+ cmd.append(command)
+
+ pkglist = util.expand_package_list('%s-%s', pkgs)
+ cmd.extend(pkglist)
+
# Allow the output of this to flow outwards (ie not be captured)
util.subp(cmd, capture=False)
diff --git a/cloudinit/util.py b/cloudinit/util.py
index c0ea8d91..ffe844b2 100644
--- a/cloudinit/util.py
+++ b/cloudinit/util.py
@@ -402,10 +402,9 @@ def get_cfg_option_list(yobj, key, default=None):
return []
val = yobj[key]
if isinstance(val, (list)):
- # Should we ensure they are all strings??
- cval = [str(v) for v in val]
+ cval = [v for v in val]
return cval
- if not isinstance(val, (str, basestring)):
+ if not isinstance(val, (basestring)):
val = str(val)
return [val]
@@ -1560,3 +1559,30 @@ def is_partition(device):
device = device[5:]
return os.path.isfile("/sys/class/block/%s/partition" % device)
+
+
+def expand_package_list(version_fmt, pkgs):
+ # we will accept tuples, lists of tuples, or just plain lists
+ if not isinstance(pkgs, list):
+ pkgs = [pkgs]
+
+ pkglist = []
+ for pkg in pkgs:
+ if isinstance(pkg, basestring):
+ pkglist.append(pkg)
+ continue
+
+ if isinstance(pkg, (tuple, list)):
+ if len(pkg) < 1 or len(pkg) > 2:
+ raise RuntimeError("Invalid package & version tuple.")
+
+ if len(pkg) == 2 and pkg[1]:
+ pkglist.append(version_fmt % tuple(pkg))
+ continue
+
+ pkglist.append(pkg[0])
+
+ else:
+ raise RuntimeError("Invalid package type.")
+
+ return pkglist
diff --git a/doc/examples/cloud-config-install-packages.txt b/doc/examples/cloud-config-install-packages.txt
index 4984818f..2edc63da 100644
--- a/doc/examples/cloud-config-install-packages.txt
+++ b/doc/examples/cloud-config-install-packages.txt
@@ -6,6 +6,10 @@
#
# if packages are specified, this apt_update will be set to true
#
+# packages may be supplied as a single package name or as a list
+# with the format [<package>, <version>] wherein the specifc
+# package version will be installed.
packages:
- pwgen
- pastebinit
+ - [libpython2.7, 2.7.3-0ubuntu3.1]