Commit d861415f authored by Christian Ehrhardt's avatar Christian Ehrhardt Committed by Scott Moser

Apt: add new apt configuration format

This adds an improved apt configuration format that is fully backwards
compatible with previous behavior. This is mostly copied from curtin's
implementation.

It does:
 * clean up and centralizes many of the top level 'apt_*' values that
   previously existed into a single top level 'apt'key.
 * support a 'source' in apt/sources/entry that has only a key
 * documents new features and adds tests.

See the added doc/examples/cloud-config-apt.txt for more information.
parent 648dbbf6
This diff is collapsed.
......@@ -36,11 +36,11 @@ def export_armour(key):
return armour
def receive_key(key, keyserver):
def recv_key(key, keyserver):
"""Receive gpg key from the specified keyserver"""
LOG.debug('Receive gpg key "%s"', key)
try:
util.subp(["gpg", "--keyserver", keyserver, "--recv-keys", key],
util.subp(["gpg", "--keyserver", keyserver, "--recv", key],
capture=True)
except util.ProcessExecutionError as error:
raise ValueError(('Failed to import key "%s" '
......@@ -57,12 +57,12 @@ def delete_key(key):
LOG.warn('Failed delete key "%s": %s', key, error)
def get_key_by_id(keyid, keyserver="keyserver.ubuntu.com"):
def getkeybyid(keyid, keyserver='keyserver.ubuntu.com'):
"""get gpg keyid from keyserver"""
armour = export_armour(keyid)
if not armour:
try:
receive_key(keyid, keyserver=keyserver)
recv_key(keyid, keyserver=keyserver)
armour = export_armour(keyid)
except ValueError:
LOG.exception('Failed to obtain gpg key %s', keyid)
......
......@@ -61,6 +61,10 @@ from cloudinit import version
from cloudinit.settings import (CFG_BUILTIN)
try:
string_types = (basestring,)
except NameError:
string_types = (str,)
_DNS_REDIRECT_IP = None
LOG = logging.getLogger(__name__)
......@@ -82,6 +86,71 @@ CONTAINER_TESTS = (['systemd-detect-virt', '--quiet', '--container'],
PROC_CMDLINE = None
_LSB_RELEASE = {}
def get_architecture(target=None):
out, _ = subp(['dpkg', '--print-architecture'], capture=True,
target=target)
return out.strip()
def _lsb_release(target=None):
fmap = {'Codename': 'codename', 'Description': 'description',
'Distributor ID': 'id', 'Release': 'release'}
data = {}
try:
out, _ = subp(['lsb_release', '--all'], capture=True, target=target)
for line in out.splitlines():
fname, _, val = line.partition(":")
if fname in fmap:
data[fmap[fname]] = val.strip()
missing = [k for k in fmap.values() if k not in data]
if len(missing):
LOG.warn("Missing fields in lsb_release --all output: %s",
','.join(missing))
except ProcessExecutionError as err:
LOG.warn("Unable to get lsb_release --all: %s", err)
data = {v: "UNAVAILABLE" for v in fmap.values()}
return data
def lsb_release(target=None):
if target_path(target) != "/":
# do not use or update cache if target is provided
return _lsb_release(target)
global _LSB_RELEASE
if not _LSB_RELEASE:
data = _lsb_release()
_LSB_RELEASE.update(data)
return _LSB_RELEASE
def target_path(target, path=None):
# return 'path' inside target, accepting target as None
if target in (None, ""):
target = "/"
elif not isinstance(target, string_types):
raise ValueError("Unexpected input for target: %s" % target)
else:
target = os.path.abspath(target)
# abspath("//") returns "//" specifically for 2 slashes.
if target.startswith("//"):
target = target[1:]
if not path:
return target
# os.path.join("/etc", "/foo") returns "/foo". Chomp all leading /.
while len(path) and path[0] == "/":
path = path[1:]
return os.path.join(target, path)
def decode_binary(blob, encoding='utf-8'):
# Converts a binary type into a text type using given encoding.
......@@ -1688,10 +1757,20 @@ def delete_dir_contents(dirname):
def subp(args, data=None, rcs=None, env=None, capture=True, shell=False,
logstring=False):
logstring=False, decode="replace", target=None):
# not supported in cloud-init (yet), for now kept in the call signature
# to ease maintaining code shared between cloud-init and curtin
if target is not None:
raise ValueError("target arg not supported by cloud-init")
if rcs is None:
rcs = [0]
devnull_fp = None
try:
if target_path(target) != "/":
args = ['chroot', target] + list(args)
if not logstring:
LOG.debug(("Running command %s with allowed return codes %s"
......@@ -1700,33 +1779,52 @@ def subp(args, data=None, rcs=None, env=None, capture=True, shell=False,
LOG.debug(("Running hidden command to protect sensitive "
"input/output logstring: %s"), logstring)
if not capture:
stdout = None
stderr = None
else:
stdin = None
stdout = None
stderr = None
if capture:
stdout = subprocess.PIPE
stderr = subprocess.PIPE
stdin = subprocess.PIPE
kws = dict(stdout=stdout, stderr=stderr, stdin=stdin,
env=env, shell=shell)
if six.PY3:
# Use this so subprocess output will be (Python 3) str, not bytes.
kws['universal_newlines'] = True
sp = subprocess.Popen(args, **kws)
if data is None:
# using devnull assures any reads get null, rather
# than possibly waiting on input.
devnull_fp = open(os.devnull)
stdin = devnull_fp
else:
stdin = subprocess.PIPE
if not isinstance(data, bytes):
data = data.encode()
sp = subprocess.Popen(args, stdout=stdout,
stderr=stderr, stdin=stdin,
env=env, shell=shell)
(out, err) = sp.communicate(data)
# Just ensure blank instead of none.
if not out and capture:
out = b''
if not err and capture:
err = b''
if decode:
def ldecode(data, m='utf-8'):
if not isinstance(data, bytes):
return data
return data.decode(m, errors=decode)
out = ldecode(out)
err = ldecode(err)
except OSError as e:
raise ProcessExecutionError(cmd=args, reason=e,
errno=e.errno)
finally:
if devnull_fp:
devnull_fp.close()
rc = sp.returncode
if rc not in rcs:
raise ProcessExecutionError(stdout=out, stderr=err,
exit_code=rc,
cmd=args)
# Just ensure blank instead of none?? (iff capturing)
if not out and capture:
out = ''
if not err and capture:
err = ''
return (out, err)
......@@ -2251,3 +2349,18 @@ def message_from_string(string):
if sys.version_info[:2] < (2, 7):
return email.message_from_file(six.StringIO(string))
return email.message_from_string(string)
def get_installed_packages(target=None):
(out, _) = subp(['dpkg-query', '--list'], target=target, capture=True)
pkgs_inst = set()
for line in out.splitlines():
try:
(state, pkg, _) = line.split(None, 2)
except ValueError:
continue
if state.startswith("hi") or state.startswith("ii"):
pkgs_inst.add(re.sub(":.*", "", pkg))
return pkgs_inst
......@@ -4,18 +4,21 @@
#
# Default: auto select based on cloud metadata
# in ec2, the default is <region>.archive.ubuntu.com
# apt_mirror:
# use the provided mirror
# apt_mirror_search:
# search the list for the first mirror.
# this is currently very limited, only verifying that
# the mirror is dns resolvable or an IP address
# apt:
# primary:
# - arches [default]
# uri:
# use the provided mirror
# search:
# search the list for the first mirror.
# this is currently very limited, only verifying that
# the mirror is dns resolvable or an IP address
#
# if neither apt_mirror nor apt_mirror search is set (the default)
# if neither mirror is set (the default)
# then use the mirror provided by the DataSource found.
# In EC2, that means using <region>.ec2.archive.ubuntu.com
#
# if no mirror is provided by the DataSource, and 'apt_mirror_search_dns' is
#
# if no mirror is provided by the DataSource, but 'search_dns' is
# true, then search for dns names '<distro>-mirror' in each of
# - fqdn of this host per cloud metadata
# - localdomain
......@@ -27,8 +30,19 @@
# up and expose them only by creating dns entries.
#
# if none of that is found, then the default distro mirror is used
apt_mirror: http://us.archive.ubuntu.com/ubuntu/
apt_mirror_search:
- http://local-mirror.mydomain
- http://archive.ubuntu.com
apt_mirror_search_dns: False
apt:
primary:
- arches: [default]
uri: http://us.archive.ubuntu.com/ubuntu/
# or
apt:
primary:
- arches: [default]
search:
- http://local-mirror.mydomain
- http://archive.ubuntu.com
# or
apt:
primary:
- arches: [default]
search_dns: True
This diff is collapsed.
......@@ -11,39 +11,40 @@
# The default is to install from packages.
# Key from http://apt.opscode.com/packages@opscode.com.gpg.key
apt_sources:
- source: "deb http://apt.opscode.com/ $RELEASE-0.10 main"
key: |
-----BEGIN PGP PUBLIC KEY BLOCK-----
Version: GnuPG v1.4.9 (GNU/Linux)
mQGiBEppC7QRBADfsOkZU6KZK+YmKw4wev5mjKJEkVGlus+NxW8wItX5sGa6kdUu
twAyj7Yr92rF+ICFEP3gGU6+lGo0Nve7KxkN/1W7/m3G4zuk+ccIKmjp8KS3qn99
dxy64vcji9jIllVa+XXOGIp0G8GEaj7mbkixL/bMeGfdMlv8Gf2XPpp9vwCgn/GC
JKacfnw7MpLKUHOYSlb//JsEAJqao3ViNfav83jJKEkD8cf59Y8xKia5OpZqTK5W
ShVnNWS3U5IVQk10ZDH97Qn/YrK387H4CyhLE9mxPXs/ul18ioiaars/q2MEKU2I
XKfV21eMLO9LYd6Ny/Kqj8o5WQK2J6+NAhSwvthZcIEphcFignIuobP+B5wNFQpe
DbKfA/0WvN2OwFeWRcmmd3Hz7nHTpcnSF+4QX6yHRF/5BgxkG6IqBIACQbzPn6Hm
sMtm/SVf11izmDqSsQptCrOZILfLX/mE+YOl+CwWSHhl+YsFts1WOuh1EhQD26aO
Z84HuHV5HFRWjDLw9LriltBVQcXbpfSrRP5bdr7Wh8vhqJTPjrQnT3BzY29kZSBQ
YWNrYWdlcyA8cGFja2FnZXNAb3BzY29kZS5jb20+iGAEExECACAFAkppC7QCGwMG
CwkIBwMCBBUCCAMEFgIDAQIeAQIXgAAKCRApQKupg++Caj8sAKCOXmdG36gWji/K
+o+XtBfvdMnFYQCfTCEWxRy2BnzLoBBFCjDSK6sJqCu5Ag0ESmkLtBAIAIO2SwlR
lU5i6gTOp42RHWW7/pmW78CwUqJnYqnXROrt3h9F9xrsGkH0Fh1FRtsnncgzIhvh
DLQnRHnkXm0ws0jV0PF74ttoUT6BLAUsFi2SPP1zYNJ9H9fhhK/pjijtAcQwdgxu
wwNJ5xCEscBZCjhSRXm0d30bK1o49Cow8ZIbHtnXVP41c9QWOzX/LaGZsKQZnaMx
EzDk8dyyctR2f03vRSVyTFGgdpUcpbr9eTFVgikCa6ODEBv+0BnCH6yGTXwBid9g
w0o1e/2DviKUWCC+AlAUOubLmOIGFBuI4UR+rux9affbHcLIOTiKQXv79lW3P7W8
AAfniSQKfPWXrrcAAwUH/2XBqD4Uxhbs25HDUUiM/m6Gnlj6EsStg8n0nMggLhuN
QmPfoNByMPUqvA7sULyfr6xCYzbzRNxABHSpf85FzGQ29RF4xsA4vOOU8RDIYQ9X
Q8NqqR6pydprRFqWe47hsAN7BoYuhWqTtOLSBmnAnzTR5pURoqcquWYiiEavZixJ
3ZRAq/HMGioJEtMFrvsZjGXuzef7f0ytfR1zYeLVWnL9Bd32CueBlI7dhYwkFe+V
Ep5jWOCj02C1wHcwt+uIRDJV6TdtbIiBYAdOMPk15+VBdweBXwMuYXr76+A7VeDL
zIhi7tKFo6WiwjKZq0dzctsJJjtIfr4K4vbiD9Ojg1iISQQYEQIACQUCSmkLtAIb
DAAKCRApQKupg++CauISAJ9CxYPOKhOxalBnVTLeNUkAHGg2gACeIsbobtaD4ZHG
0GLl8EkfA8uhluM=
=zKAm
-----END PGP PUBLIC KEY BLOCK-----
apt:
sources:
- source: "deb http://apt.opscode.com/ $RELEASE-0.10 main"
key: |
-----BEGIN PGP PUBLIC KEY BLOCK-----
Version: GnuPG v1.4.9 (GNU/Linux)
mQGiBEppC7QRBADfsOkZU6KZK+YmKw4wev5mjKJEkVGlus+NxW8wItX5sGa6kdUu
twAyj7Yr92rF+ICFEP3gGU6+lGo0Nve7KxkN/1W7/m3G4zuk+ccIKmjp8KS3qn99
dxy64vcji9jIllVa+XXOGIp0G8GEaj7mbkixL/bMeGfdMlv8Gf2XPpp9vwCgn/GC
JKacfnw7MpLKUHOYSlb//JsEAJqao3ViNfav83jJKEkD8cf59Y8xKia5OpZqTK5W
ShVnNWS3U5IVQk10ZDH97Qn/YrK387H4CyhLE9mxPXs/ul18ioiaars/q2MEKU2I
XKfV21eMLO9LYd6Ny/Kqj8o5WQK2J6+NAhSwvthZcIEphcFignIuobP+B5wNFQpe
DbKfA/0WvN2OwFeWRcmmd3Hz7nHTpcnSF+4QX6yHRF/5BgxkG6IqBIACQbzPn6Hm
sMtm/SVf11izmDqSsQptCrOZILfLX/mE+YOl+CwWSHhl+YsFts1WOuh1EhQD26aO
Z84HuHV5HFRWjDLw9LriltBVQcXbpfSrRP5bdr7Wh8vhqJTPjrQnT3BzY29kZSBQ
YWNrYWdlcyA8cGFja2FnZXNAb3BzY29kZS5jb20+iGAEExECACAFAkppC7QCGwMG
CwkIBwMCBBUCCAMEFgIDAQIeAQIXgAAKCRApQKupg++Caj8sAKCOXmdG36gWji/K
+o+XtBfvdMnFYQCfTCEWxRy2BnzLoBBFCjDSK6sJqCu5Ag0ESmkLtBAIAIO2SwlR
lU5i6gTOp42RHWW7/pmW78CwUqJnYqnXROrt3h9F9xrsGkH0Fh1FRtsnncgzIhvh
DLQnRHnkXm0ws0jV0PF74ttoUT6BLAUsFi2SPP1zYNJ9H9fhhK/pjijtAcQwdgxu
wwNJ5xCEscBZCjhSRXm0d30bK1o49Cow8ZIbHtnXVP41c9QWOzX/LaGZsKQZnaMx
EzDk8dyyctR2f03vRSVyTFGgdpUcpbr9eTFVgikCa6ODEBv+0BnCH6yGTXwBid9g
w0o1e/2DviKUWCC+AlAUOubLmOIGFBuI4UR+rux9affbHcLIOTiKQXv79lW3P7W8
AAfniSQKfPWXrrcAAwUH/2XBqD4Uxhbs25HDUUiM/m6Gnlj6EsStg8n0nMggLhuN
QmPfoNByMPUqvA7sULyfr6xCYzbzRNxABHSpf85FzGQ29RF4xsA4vOOU8RDIYQ9X
Q8NqqR6pydprRFqWe47hsAN7BoYuhWqTtOLSBmnAnzTR5pURoqcquWYiiEavZixJ
3ZRAq/HMGioJEtMFrvsZjGXuzef7f0ytfR1zYeLVWnL9Bd32CueBlI7dhYwkFe+V
Ep5jWOCj02C1wHcwt+uIRDJV6TdtbIiBYAdOMPk15+VBdweBXwMuYXr76+A7VeDL
zIhi7tKFo6WiwjKZq0dzctsJJjtIfr4K4vbiD9Ojg1iISQQYEQIACQUCSmkLtAIb
DAAKCRApQKupg++CauISAJ9CxYPOKhOxalBnVTLeNUkAHGg2gACeIsbobtaD4ZHG
0GLl8EkfA8uhluM=
=zKAm
-----END PGP PUBLIC KEY BLOCK-----
chef:
......
......@@ -11,39 +11,40 @@
# The default is to install from packages.
# Key from http://apt.opscode.com/packages@opscode.com.gpg.key
apt_sources:
- source: "deb http://apt.opscode.com/ $RELEASE-0.10 main"
key: |
-----BEGIN PGP PUBLIC KEY BLOCK-----
Version: GnuPG v1.4.9 (GNU/Linux)
mQGiBEppC7QRBADfsOkZU6KZK+YmKw4wev5mjKJEkVGlus+NxW8wItX5sGa6kdUu
twAyj7Yr92rF+ICFEP3gGU6+lGo0Nve7KxkN/1W7/m3G4zuk+ccIKmjp8KS3qn99
dxy64vcji9jIllVa+XXOGIp0G8GEaj7mbkixL/bMeGfdMlv8Gf2XPpp9vwCgn/GC
JKacfnw7MpLKUHOYSlb//JsEAJqao3ViNfav83jJKEkD8cf59Y8xKia5OpZqTK5W
ShVnNWS3U5IVQk10ZDH97Qn/YrK387H4CyhLE9mxPXs/ul18ioiaars/q2MEKU2I
XKfV21eMLO9LYd6Ny/Kqj8o5WQK2J6+NAhSwvthZcIEphcFignIuobP+B5wNFQpe
DbKfA/0WvN2OwFeWRcmmd3Hz7nHTpcnSF+4QX6yHRF/5BgxkG6IqBIACQbzPn6Hm
sMtm/SVf11izmDqSsQptCrOZILfLX/mE+YOl+CwWSHhl+YsFts1WOuh1EhQD26aO
Z84HuHV5HFRWjDLw9LriltBVQcXbpfSrRP5bdr7Wh8vhqJTPjrQnT3BzY29kZSBQ
YWNrYWdlcyA8cGFja2FnZXNAb3BzY29kZS5jb20+iGAEExECACAFAkppC7QCGwMG
CwkIBwMCBBUCCAMEFgIDAQIeAQIXgAAKCRApQKupg++Caj8sAKCOXmdG36gWji/K
+o+XtBfvdMnFYQCfTCEWxRy2BnzLoBBFCjDSK6sJqCu5Ag0ESmkLtBAIAIO2SwlR
lU5i6gTOp42RHWW7/pmW78CwUqJnYqnXROrt3h9F9xrsGkH0Fh1FRtsnncgzIhvh
DLQnRHnkXm0ws0jV0PF74ttoUT6BLAUsFi2SPP1zYNJ9H9fhhK/pjijtAcQwdgxu
wwNJ5xCEscBZCjhSRXm0d30bK1o49Cow8ZIbHtnXVP41c9QWOzX/LaGZsKQZnaMx
EzDk8dyyctR2f03vRSVyTFGgdpUcpbr9eTFVgikCa6ODEBv+0BnCH6yGTXwBid9g
w0o1e/2DviKUWCC+AlAUOubLmOIGFBuI4UR+rux9affbHcLIOTiKQXv79lW3P7W8
AAfniSQKfPWXrrcAAwUH/2XBqD4Uxhbs25HDUUiM/m6Gnlj6EsStg8n0nMggLhuN
QmPfoNByMPUqvA7sULyfr6xCYzbzRNxABHSpf85FzGQ29RF4xsA4vOOU8RDIYQ9X
Q8NqqR6pydprRFqWe47hsAN7BoYuhWqTtOLSBmnAnzTR5pURoqcquWYiiEavZixJ
3ZRAq/HMGioJEtMFrvsZjGXuzef7f0ytfR1zYeLVWnL9Bd32CueBlI7dhYwkFe+V
Ep5jWOCj02C1wHcwt+uIRDJV6TdtbIiBYAdOMPk15+VBdweBXwMuYXr76+A7VeDL
zIhi7tKFo6WiwjKZq0dzctsJJjtIfr4K4vbiD9Ojg1iISQQYEQIACQUCSmkLtAIb
DAAKCRApQKupg++CauISAJ9CxYPOKhOxalBnVTLeNUkAHGg2gACeIsbobtaD4ZHG
0GLl8EkfA8uhluM=
=zKAm
-----END PGP PUBLIC KEY BLOCK-----
apt:
sources:
- source: "deb http://apt.opscode.com/ $RELEASE-0.10 main"
key: |
-----BEGIN PGP PUBLIC KEY BLOCK-----
Version: GnuPG v1.4.9 (GNU/Linux)
mQGiBEppC7QRBADfsOkZU6KZK+YmKw4wev5mjKJEkVGlus+NxW8wItX5sGa6kdUu
twAyj7Yr92rF+ICFEP3gGU6+lGo0Nve7KxkN/1W7/m3G4zuk+ccIKmjp8KS3qn99
dxy64vcji9jIllVa+XXOGIp0G8GEaj7mbkixL/bMeGfdMlv8Gf2XPpp9vwCgn/GC
JKacfnw7MpLKUHOYSlb//JsEAJqao3ViNfav83jJKEkD8cf59Y8xKia5OpZqTK5W
ShVnNWS3U5IVQk10ZDH97Qn/YrK387H4CyhLE9mxPXs/ul18ioiaars/q2MEKU2I
XKfV21eMLO9LYd6Ny/Kqj8o5WQK2J6+NAhSwvthZcIEphcFignIuobP+B5wNFQpe
DbKfA/0WvN2OwFeWRcmmd3Hz7nHTpcnSF+4QX6yHRF/5BgxkG6IqBIACQbzPn6Hm
sMtm/SVf11izmDqSsQptCrOZILfLX/mE+YOl+CwWSHhl+YsFts1WOuh1EhQD26aO
Z84HuHV5HFRWjDLw9LriltBVQcXbpfSrRP5bdr7Wh8vhqJTPjrQnT3BzY29kZSBQ
YWNrYWdlcyA8cGFja2FnZXNAb3BzY29kZS5jb20+iGAEExECACAFAkppC7QCGwMG
CwkIBwMCBBUCCAMEFgIDAQIeAQIXgAAKCRApQKupg++Caj8sAKCOXmdG36gWji/K
+o+XtBfvdMnFYQCfTCEWxRy2BnzLoBBFCjDSK6sJqCu5Ag0ESmkLtBAIAIO2SwlR
lU5i6gTOp42RHWW7/pmW78CwUqJnYqnXROrt3h9F9xrsGkH0Fh1FRtsnncgzIhvh
DLQnRHnkXm0ws0jV0PF74ttoUT6BLAUsFi2SPP1zYNJ9H9fhhK/pjijtAcQwdgxu
wwNJ5xCEscBZCjhSRXm0d30bK1o49Cow8ZIbHtnXVP41c9QWOzX/LaGZsKQZnaMx
EzDk8dyyctR2f03vRSVyTFGgdpUcpbr9eTFVgikCa6ODEBv+0BnCH6yGTXwBid9g
w0o1e/2DviKUWCC+AlAUOubLmOIGFBuI4UR+rux9affbHcLIOTiKQXv79lW3P7W8
AAfniSQKfPWXrrcAAwUH/2XBqD4Uxhbs25HDUUiM/m6Gnlj6EsStg8n0nMggLhuN
QmPfoNByMPUqvA7sULyfr6xCYzbzRNxABHSpf85FzGQ29RF4xsA4vOOU8RDIYQ9X
Q8NqqR6pydprRFqWe47hsAN7BoYuhWqTtOLSBmnAnzTR5pURoqcquWYiiEavZixJ
3ZRAq/HMGioJEtMFrvsZjGXuzef7f0ytfR1zYeLVWnL9Bd32CueBlI7dhYwkFe+V
Ep5jWOCj02C1wHcwt+uIRDJV6TdtbIiBYAdOMPk15+VBdweBXwMuYXr76+A7VeDL
zIhi7tKFo6WiwjKZq0dzctsJJjtIfr4K4vbiD9Ojg1iISQQYEQIACQUCSmkLtAIb
DAAKCRApQKupg++CauISAJ9CxYPOKhOxalBnVTLeNUkAHGg2gACeIsbobtaD4ZHG
0GLl8EkfA8uhluM=
=zKAm
-----END PGP PUBLIC KEY BLOCK-----
chef:
......
This diff is collapsed.
......@@ -3,9 +3,6 @@
#apt_upgrade: true
packages: [ bzr, pastebinit, ubuntu-dev-tools, ccache, bzr-builddeb, vim-nox, git-core, lftp ]
#apt_sources:
# - source: ppa:smoser/ppa
#disable_root: False
# mounts:
......
......@@ -226,8 +226,5 @@ class TestGenericDistro(helpers.FilesystemMockingTestCase):
os.symlink('/', '/run/systemd/system')
self.assertFalse(d.uses_systemd())
# def _get_package_mirror_info(mirror_info, availability_zone=None,
# mirror_filter=util.search_for_mirror):
# vi: ts=4 expandtab
......@@ -27,7 +27,7 @@ class TestAptProxyConfig(TestCase):
contents, flags=re.IGNORECASE)
def test_apt_proxy_written(self):
cfg = {'apt_proxy': 'myproxy'}
cfg = {'proxy': 'myproxy'}
cc_apt_configure.apply_apt_config(cfg, self.pfile, self.cfile)
self.assertTrue(os.path.isfile(self.pfile))
......@@ -37,7 +37,7 @@ class TestAptProxyConfig(TestCase):
self.assertTrue(self._search_apt_config(contents, "http", "myproxy"))
def test_apt_http_proxy_written(self):
cfg = {'apt_http_proxy': 'myproxy'}
cfg = {'http_proxy': 'myproxy'}
cc_apt_configure.apply_apt_config(cfg, self.pfile, self.cfile)
self.assertTrue(os.path.isfile(self.pfile))
......@@ -47,13 +47,13 @@ class TestAptProxyConfig(TestCase):
self.assertTrue(self._search_apt_config(contents, "http", "myproxy"))
def test_apt_all_proxy_written(self):
cfg = {'apt_http_proxy': 'myproxy_http_proxy',
'apt_https_proxy': 'myproxy_https_proxy',
'apt_ftp_proxy': 'myproxy_ftp_proxy'}
cfg = {'http_proxy': 'myproxy_http_proxy',
'https_proxy': 'myproxy_https_proxy',
'ftp_proxy': 'myproxy_ftp_proxy'}
values = {'http': cfg['apt_http_proxy'],
'https': cfg['apt_https_proxy'],
'ftp': cfg['apt_ftp_proxy'],
values = {'http': cfg['http_proxy'],
'https': cfg['https_proxy'],
'ftp': cfg['ftp_proxy'],
}
cc_apt_configure.apply_apt_config(cfg, self.pfile, self.cfile)
......@@ -74,7 +74,7 @@ class TestAptProxyConfig(TestCase):
def test_proxy_replaced(self):
util.write_file(self.cfile, "content doesnt matter")
cc_apt_configure.apply_apt_config({'apt_proxy': "foo"},
cc_apt_configure.apply_apt_config({'proxy': "foo"},
self.pfile, self.cfile)
self.assertTrue(os.path.isfile(self.pfile))
contents = load_tfile_or_url(self.pfile)
......@@ -82,7 +82,7 @@ class TestAptProxyConfig(TestCase):
def test_config_written(self):
payload = 'this is my apt config'
cfg = {'apt_config': payload}
cfg = {'conf': payload}
cc_apt_configure.apply_apt_config(cfg, self.pfile, self.cfile)
......@@ -93,13 +93,13 @@ class TestAptProxyConfig(TestCase):
def test_config_replaced(self):
util.write_file(self.pfile, "content doesnt matter")
cc_apt_configure.apply_apt_config({'apt_config': "foo"},
cc_apt_configure.apply_apt_config({'conf': "foo"},
self.pfile, self.cfile)
self.assertTrue(os.path.isfile(self.cfile))
self.assertEqual(load_tfile_or_url(self.cfile), "foo")
def test_config_deleted(self):
# if no 'apt_config' is provided, delete any previously written file
# if no 'conf' is provided, delete any previously written file
util.write_file(self.pfile, "content doesnt matter")
cc_apt_configure.apply_apt_config({}, self.pfile, self.cfile)
self.assertFalse(os.path.isfile(self.pfile))
......
......@@ -79,6 +79,15 @@ class TestAptSourceConfigSourceList(t_help.FilesystemMockingTestCase):
self.new_root = tempfile.mkdtemp()
self.addCleanup(shutil.rmtree, self.new_root)
rpatcher = mock.patch("cloudinit.util.lsb_release")
get_rel = rpatcher.start()
get_rel.return_value = {'codename': "fakerelease"}
self.addCleanup(rpatcher.stop)
apatcher = mock.patch("cloudinit.util.get_architecture")
get_arch = apatcher.start()
get_arch.return_value = 'amd64'
self.addCleanup(apatcher.stop)
def _get_cloud(self, distro, metadata=None):
self.patchUtils(self.new_root)
paths = helpers.Paths({})
......@@ -102,25 +111,38 @@ class TestAptSourceConfigSourceList(t_help.FilesystemMockingTestCase):
cfg = {'apt_mirror': mirror}
mycloud = self._get_cloud(distro)
with mock.patch.object(templater, 'render_to_file') as mocktmpl:
with mock.patch.object(os.path, 'isfile',
return_value=True) as mockisfile:
with mock.patch.object(util, 'rename'):
cc_apt_configure.handle("notimportant", cfg, mycloud,
LOG, None)
with mock.patch.object(util, 'write_file') as mockwf:
with mock.patch.object(util, 'load_file',
return_value="faketmpl") as mocklf:
with mock.patch.object(os.path, 'isfile',
return_value=True) as mockisfile:
with mock.patch.object(templater, 'render_string',
return_value="fake") as mockrnd:
with mock.patch.object(util, 'rename'):
cc_apt_configure.handle("test", cfg, mycloud,
LOG, None)
mockisfile.assert_any_call(
('/etc/cloud/templates/sources.list.%s.tmpl' % distro))
mocktmpl.assert_called_once_with(
('/etc/cloud/templates/sources.list.%s.tmpl' % distro),
'/etc/apt/sources.list',
{'codename': '', 'primary': mirrorcheck, 'mirror': mirrorcheck})
def test_apt_source_list_debian(self):
mocklf.assert_any_call(
('/etc/cloud/templates/sources.list.%s.tmpl' % distro))
mockrnd.assert_called_once_with('faketmpl',
{'RELEASE': 'fakerelease',
'PRIMARY': mirrorcheck,
'MIRROR': mirrorcheck,
'SECURITY': mirrorcheck,
'codename': 'fakerelease',
'primary': mirrorcheck,
'mirror': mirrorcheck,
'security': mirrorcheck})
mockwf.assert_called_once_with('/etc/apt/sources.list', 'fake',
mode=0o644)
def test_apt_v1_source_list_debian(self):
"""Test rendering of a source.list from template for debian"""
self.apt_source_list('debian', 'http://httpredir.debian.org/debian')
def test_apt_source_list_ubuntu(self):
def test_apt_v1_source_list_ubuntu(self):
"""Test rendering of a source.list from template for ubuntu"""
self.apt_source_list('ubuntu', 'http://archive.ubuntu.com/ubuntu/')
......@@ -134,7 +156,7 @@ class TestAptSourceConfigSourceList(t_help.FilesystemMockingTestCase):
print("Faking SUCCESS for '%s'" % name)
return True
def test_apt_srcl_debian_mirrorfail(self):
def test_apt_v1_srcl_debian_mirrorfail(self):
"""Test rendering of a source.list from template for debian"""
with mock.patch.object(util, 'is_resolvable',
side_effect=self.myresolve) as mockresolve:
......@@ -145,7 +167,7 @@ class TestAptSourceConfigSourceList(t_help.FilesystemMockingTestCase):
mockresolve.assert_any_call("does.not.exist")
mockresolve.assert_any_call("httpredir.debian.org")
def test_apt_srcl_ubuntu_mirrorfail(self):
def test_apt_v1_srcl_ubuntu_mirrorfail(self):
"""Test rendering of a source.list from template for ubuntu"""
with mock.patch.object(util, 'is_resolvable',
side_effect=self.myresolve) as mockresolve:
......@@ -156,7 +178,7 @@ class TestAptSourceConfigSourceList(t_help.FilesystemMockingTestCase):
mockresolve.assert_any_call("does.not.exist")
mockresolve.assert_any_call("archive.ubuntu.com")
def test_apt_srcl_custom(self):
def test_apt_v1_srcl_custom(self):
"""Test rendering from a custom source.list template"""
cfg = util.load_yaml(YAML_TEXT_CUSTOM_SL)
mycloud = self._get_cloud('ubuntu')
......@@ -164,12 +186,10 @@ class TestAptSourceConfigSourceList(t_help.FilesystemMockingTestCase):
# the second mock restores the original subp
with mock.patch.object(util, 'write_file') as mockwrite:
with mock.patch.object(util, 'subp', self.subp):
with mock.patch.object(cc_apt_configure, 'get_release',
return_value='fakerelease'):
with mock.patch.object(Distro, 'get_primary_arch',
return_value='amd64'):
cc_apt_configure.handle("notimportant", cfg, mycloud,
LOG, None)
with mock.patch.object(Distro, 'get_primary_arch',
return_value='amd64'):
cc_apt_configure.handle("notimportant", cfg, mycloud,
LOG, None)
mockwrite.assert_called_once_with(
'/etc/apt/sources.list',
......
""" test_apt_custom_sources_list
Test templating of custom sources list
"""
import logging
import os
import shutil
import tempfile
try:
from unittest import mock
except ImportError:
import mock
from mock import call
from cloudinit import cloud
from cloudinit import distros
from cloudinit import helpers
from cloudinit import util
from cloudinit.config import cc_apt_configure
from cloudinit.sources import DataSourceNone
from cloudinit.distros.debian import Distro
from .. import helpers as t_help
LOG = logging.getLogger(__name__)
TARGET = "/"
# Input and expected output for the custom template
YAML_TEXT_CUSTOM_SL = """
apt:
primary:
- arches: [default]
uri: http://test.ubuntu.com/ubuntu/
security:
- arches: [default]
uri: http://testsec.ubuntu.com/ubuntu/
sources_list: |
# Note, this file is written by cloud-init at install time. It should not
# end up on the installed system itself.
# See http://help.ubuntu.com/community/UpgradeNotes for how to upgrade to
# newer versions of the distribution.
deb $MIRROR $RELEASE main restricted
deb-src $MIRROR $RELEASE main restricted
deb $PRIMARY $RELEASE universe restricted
deb $SECURITY $RELEASE-security multiverse
# FIND_SOMETHING_SPECIAL
"""
EXPECTED_CONVERTED_CONTENT = """
# Note, this file is written by cloud-init at install time. It should not
# end up on the installed system itself.
# See http://help.ubuntu.com/community/UpgradeNotes for how to upgrade to
# newer versions of the distribution.
deb http://test.ubuntu.com/ubuntu/ fakerel main restricted
deb-src http://test.ubuntu.com/ubuntu/ fakerel main restricted
deb http://test.ubuntu.com/ubuntu/ fakerel universe restricted
deb http://testsec.ubuntu.com/ubuntu/ fakerel-security multiverse
# FIND_SOMETHING_SPECIAL
"""
# mocked to be independent to the unittest system
MOCKED_APT_SRC_LIST = """
deb http://test.ubuntu.com/ubuntu/ notouched main restricted
deb-src http://test.ubuntu.com/ubuntu/ notouched main restricted
deb http://test.ubuntu.com/ubuntu/ notouched-updates main restricted
deb http://testsec.ubuntu.com/ubuntu/ notouched-security main restricted
"""
EXPECTED_BASE_CONTENT = ("""
deb http://test.ubuntu.com/ubuntu/ notouched main restricted
deb-src http://test.ubuntu.com/ubuntu/ notouched main restricted
deb http://test.ubuntu.com/ubuntu/ notouched-updates main restricted
deb http://testsec.ubuntu.com/ubuntu/ notouched-security main restricted
""")
EXPECTED_MIRROR_CONTENT = ("""
deb http://test.ubuntu.com/ubuntu/ notouched main restricted
deb-src http://test.ubuntu.com/ubuntu/ notouched main restricted
deb http://test.ubuntu.com/ubuntu/ notouched-updates main restricted
deb http://test.ubuntu.com/ubuntu/ notouched-security main restricted
""")