Fix #217, by introducing class for comparison of releases

This commit is contained in:
Anders Ingemann 2015-04-29 20:55:55 +02:00
parent 2d3a0a0ce3
commit 71c7d445ad
30 changed files with 164 additions and 84 deletions

View file

@ -16,8 +16,12 @@ def validate_manifest(data, validator, error):
schema_path = os.path.normpath(os.path.join(os.path.dirname(__file__), 'manifest-schema.yml'))
validator(data, schema_path)
from bootstrapvz.common.tools import get_codename
codename = get_codename(data['system']['release'])
from bootstrapvz.common.releases import get_release
from bootstrapvz.common.releases import squeeze
release = get_release(data['system']['release'])
if release < squeeze:
error('Only Debian squeeze and later is supported', ['system', 'release'])
# Check the bootloader/partitioning configuration.
# Doing this via the schema is a pain and does not output a useful error message.
@ -26,5 +30,5 @@ def validate_manifest(data, validator, error):
if data['volume']['partitions']['type'] == 'none':
error('Grub cannot boot from unpartitioned disks', ['system', 'bootloader'])
if codename == 'squeeze':
if release == squeeze:
error('Grub installation on squeeze is not supported', ['system', 'bootloader'])

View file

@ -31,9 +31,6 @@ class BootstrapInformation(object):
# The default apt mirror
self.apt_mirror = self.manifest.packages.get('mirror', 'http://http.debian.net/debian')
from bootstrapvz.common.tools import get_codename
self.release_codename = get_codename(self.manifest.system['release'])
# Create the manifest_vars dictionary
self.manifest_vars = self.__create_manifest_vars(self.manifest, {'apt_mirror': self.apt_mirror})

View file

@ -52,16 +52,7 @@ properties:
type: string
pattern: ^\S+$
locale: {type: string}
release:
enum:
- squeeze
- wheezy
- jessie
- sid
- oldstable
- stable
- testing
- unstable
release: {type: string}
timezone: {type: string}
required:
- release

View file

@ -103,6 +103,8 @@ class Manifest(object):
self.image = self.data['image']
self.volume = self.data['volume']
self.system = self.data['system']
from bootstrapvz.common.releases import get_release
self.release = get_release(self.system['release'])
# The packages and plugins section is not required
self.packages = self.data['packages'] if 'packages' in self.data else {}
self.plugins = self.data['plugins'] if 'plugins' in self.data else {}

View file

@ -1,23 +0,0 @@
---
# This is a mapping of Debian release names to their respective codenames
unstable: sid
testing: stretch
stable: jessie
oldstable: wheezy
jessie: jessie
wheezy: wheezy
squeeze: squeeze
# The following release names are not supported, but included for completeness sake
lenny: lenny
etch: etch
sarge: sarge
woody: woody
potato: potato
slink: slink
hamm: hamm
bo: bo
rex: rex
buzz: buz

View file

@ -0,0 +1,68 @@
class _Release(object):
def __init__(self, codename, version):
self.codename = codename
self.version = version
def __cmp__(self, other):
return self.version - other.version
def __str__(self):
return self.codename
def __getstate__(self):
state = self.__dict__.copy()
state['__class__'] = self.__module__ + '.' + self.__class__.__name__
return state
def __setstate__(self, state):
for key in state:
self.__dict__[key] = state[key]
class _ReleaseAlias(_Release):
def __init__(self, alias, release):
self.alias = alias
self.release = release
super(_ReleaseAlias, self).__init__(self.release.codename, self.release.version)
def __str__(self):
return self.alias
sid = _Release('sid', 10)
stretch = _Release('stretch', 9)
jessie = _Release('jessie', 8)
wheezy = _Release('wheezy', 7)
squeeze = _Release('squeeze', 6.0)
lenny = _Release('lenny', 5.0)
etch = _Release('etch', 4.0)
sarge = _Release('sarge', 3.1)
woody = _Release('woody', 3.0)
potato = _Release('potato', 2.2)
slink = _Release('slink', 2.1)
hamm = _Release('hamm', 2.0)
bo = _Release('bo', 1.3)
rex = _Release('rex', 1.2)
buzz = _Release('buzz', 1.1)
unstable = _ReleaseAlias('unstable', sid)
testing = _ReleaseAlias('testing', stretch)
stable = _ReleaseAlias('stable', jessie)
oldstable = _ReleaseAlias('oldstable', wheezy)
def get_release(release_name):
"""Normalizes the release codenames
This allows tasks to query for release codenames rather than 'stable', 'unstable' etc.
"""
from . import releases
release = getattr(releases, release_name, None)
if release is None or not isinstance(release, _Release):
raise UnknownReleaseException('The release `{name}\' is unknown'.format(name=release))
return release
class UnknownReleaseException(Exception):
pass

View file

@ -136,18 +136,18 @@ locale_group = [locale.LocaleBootstrapPackage,
def get_bootloader_group(manifest):
from bootstrapvz.common.tools import get_codename
from bootstrapvz.common.releases import jessie
group = []
if manifest.system['bootloader'] == 'grub':
group.extend([grub.AddGrubPackage,
grub.ConfigureGrub])
if get_codename(manifest.system['release']) in ['squeeze', 'wheezy']:
if manifest.release < jessie:
group.append(grub.InstallGrub_1_99)
else:
group.append(grub.InstallGrub_2)
if manifest.system['bootloader'] == 'extlinux':
group.append(extlinux.AddExtlinuxPackage)
if get_codename(manifest.system['release']) in ['squeeze', 'wheezy']:
if manifest.release < jessie:
group.extend([extlinux.ConfigureExtlinux,
extlinux.InstallExtlinux])
else:

View file

@ -24,12 +24,13 @@ class AddDefaultSources(Task):
@classmethod
def run(cls, info):
from bootstrapvz.common.releases import sid
include_src = info.manifest.packages.get('include-source-type', False)
components = ' '.join(info.manifest.packages.get('components', ['main']))
info.source_lists.add('main', 'deb {apt_mirror} {system.release} ' + components)
if include_src:
info.source_lists.add('main', 'deb-src {apt_mirror} {system.release} ' + components)
if info.release_codename != 'sid':
if info.manifest.release != sid:
info.source_lists.add('main', 'deb http://security.debian.org/ {system.release}/updates ' + components)
if include_src:
info.source_lists.add('main', 'deb-src http://security.debian.org/ {system.release}/updates ' + components)
@ -45,10 +46,11 @@ class AddBackports(Task):
@classmethod
def run(cls, info):
from bootstrapvz.common.releases import sid
if info.source_lists.target_exists('{system.release}-backports'):
msg = ('{system.release}-backports target already exists').format(**info.manifest_vars)
logging.getLogger(__name__).info(msg)
elif info.release_codename == 'sid':
elif info.manifest.release == sid:
logging.getLogger(__name__).info('There are no backports for sid/unstable')
else:
info.source_lists.add('backports', 'deb {apt_mirror} {system.release}-backports main')

View file

@ -34,9 +34,9 @@ class DisableGetTTYs(Task):
@classmethod
def run(cls, info):
# Forward compatible check for jessie,
# we should probably get some version numbers up in dis bitch
if info.release_codename in ['squeeze', 'wheezy']:
# Forward compatible check for jessie
from bootstrapvz.common.releases import jessie
if info.manifest.release < jessie:
from ..tools import sed_i
inittab_path = os.path.join(info.root, 'etc/inittab')
tty1 = '1:2345:respawn:/sbin/getty 38400 tty1'

View file

@ -27,7 +27,8 @@ class ConfigureExtlinux(Task):
@classmethod
def run(cls, info):
if info.release_codename == 'squeeze':
from bootstrapvz.common.releases import squeeze
if info.manifest.release == squeeze:
# On squeeze /etc/default/extlinux is generated when running extlinux-update
log_check_call(['chroot', info.root,
'extlinux-update'])

View file

@ -44,8 +44,9 @@ class RemoveHWClock(Task):
@classmethod
def run(cls, info):
from bootstrapvz.common.releases import squeeze
info.initd['disable'].append('hwclock.sh')
if info.release_codename == 'squeeze':
if info.manifest.release == squeeze:
info.initd['disable'].append('hwclockfirst.sh')

View file

@ -47,7 +47,7 @@ class ConfigureNetworkIF(Task):
def run(cls, info):
network_config_path = os.path.join(os.path.dirname(__file__), 'network-configuration.yml')
from ..tools import config_get
if_config = config_get(network_config_path, [info.release_codename])
if_config = config_get(network_config_path, [info.manifest.release.codename])
interfaces_path = os.path.join(info.root, 'etc/network/interfaces')
with open(interfaces_path, 'a') as interfaces:

View file

@ -30,7 +30,8 @@ class AddSSHKeyGeneration(Task):
try:
log_check_call(['chroot', info.root,
'dpkg-query', '-W', 'openssh-server'])
if info.release_codename == 'squeeze':
from bootstrapvz.common.releases import squeeze
if info.manifest.release == squeeze:
install['generate-ssh-hostkeys'] = os.path.join(init_scripts_dir, 'squeeze/generate-ssh-hostkeys')
else:
install['generate-ssh-hostkeys'] = os.path.join(init_scripts_dir, 'generate-ssh-hostkeys')
@ -102,7 +103,8 @@ class ShredHostkeys(Task):
def run(cls, info):
ssh_hostkeys = ['ssh_host_dsa_key',
'ssh_host_rsa_key']
if info.release_codename != 'squeeze':
from bootstrapvz.common.releases import wheezy
if info.manifest.release >= wheezy:
ssh_hostkeys.append('ssh_host_ecdsa_key')
private = [os.path.join(info.root, 'etc/ssh', name) for name in ssh_hostkeys]

View file

@ -118,15 +118,6 @@ def config_get(path, config_path):
return config
def get_codename(release):
"""Normalizes the release codenames
This allows tasks to query for release codenames rather than 'stable', 'unstable' etc.
"""
release_codenames_path = os.path.join(os.path.dirname(__file__), 'release-codenames.yml')
from bootstrapvz.common.tools import config_get
return config_get(release_codenames_path, [release])
def copy_tree(from_path, to_path):
from shutil import copy
for abs_prefix, dirs, files in os.walk(from_path):

View file

@ -13,8 +13,8 @@ def resolve_tasks(taskset, manifest):
if initd.AddEC2InitScripts in taskset:
taskset.add(tasks.AdminUserCredentials)
from bootstrapvz.common.tools import get_codename
if get_codename(manifest.system['release']) in ['wheezy', 'squeeze']:
from bootstrapvz.common.releases import jessie
if manifest.release < jessie:
taskset.update([ssh.DisableRootLogin])
taskset.update([tasks.AddSudoPackage,

View file

@ -13,8 +13,8 @@ def resolve_tasks(taskset, manifest):
from bootstrapvz.common.tasks import initd
from bootstrapvz.common.tasks import ssh
from bootstrapvz.common.tools import get_codename
if get_codename(manifest.system['release']) == 'wheezy':
from bootstrapvz.common.releases import wheezy
if manifest.release == wheezy:
taskset.add(apt.AddBackports)
taskset.update([tasks.SetMetadataSource,

View file

@ -15,7 +15,8 @@ class AddCloudInitPackages(Task):
@classmethod
def run(cls, info):
target = None
if info.release_codename == 'wheezy':
from bootstrapvz.common.releases import wheezy
if info.manifest.release == wheezy:
target = '{system.release}-backports'
info.packages.add('cloud-init', target)
info.packages.add('sudo')

View file

@ -1,13 +1,14 @@
import os.path
import tasks
from bootstrapvz.common.tasks import apt
from bootstrapvz.common.tools import get_codename
from bootstrapvz.common.releases import wheezy
def validate_manifest(data, validator, error):
schema_path = os.path.normpath(os.path.join(os.path.dirname(__file__), 'manifest-schema.yml'))
validator(data, schema_path)
if get_codename(data['system']['release']) == 'wheezy':
from bootstrapvz.common.releases import get_release
if get_release(data['system']['release']) == wheezy:
# prefs is a generator of apt preferences across files in the manifest
prefs = (item for vals in data.get('packages', {}).get('preferences', {}).values() for item in vals)
if not any('linux-image' in item['package'] and 'wheezy-backports' in item['pin'] for item in prefs):
@ -16,7 +17,7 @@ def validate_manifest(data, validator, error):
def resolve_tasks(taskset, manifest):
if get_codename(manifest.system['release']) == 'wheezy':
if manifest.release == wheezy:
taskset.add(apt.AddBackports)
taskset.add(tasks.AddDockerDeps)
taskset.add(tasks.AddDockerBinary)

View file

@ -3,7 +3,7 @@
def resolve_tasks(taskset, manifest):
import tasks
from bootstrapvz.common.tasks import apt
from bootstrapvz.common.tools import get_codename
if get_codename(manifest.system['release']) == 'wheezy':
from bootstrapvz.common.releases import wheezy
if manifest.release == wheezy:
taskset.add(apt.AddBackports)
taskset.update([tasks.AddONEContextPackage])

View file

@ -11,6 +11,7 @@ class AddONEContextPackage(Task):
@classmethod
def run(cls, info):
target = None
if info.release_codename == 'wheezy':
from bootstrapvz.common.releases import wheezy
if info.manifest.release == wheezy:
target = '{system.release}-backports'
info.packages.add('opennebula-context', target)

View file

@ -19,7 +19,7 @@ class DefaultPackages(Task):
import os.path
kernel_packages_path = os.path.join(os.path.dirname(__file__), 'packages-kernels.yml')
from bootstrapvz.common.tools import config_get
kernel_package = config_get(kernel_packages_path, [info.release_codename,
kernel_package = config_get(kernel_packages_path, [info.manifest.release.codename,
info.manifest.system['architecture']])
info.packages.add(kernel_package)

View file

@ -13,7 +13,8 @@ class EnableDHCPCDDNS(Task):
def run(cls, info):
# The dhcp client that ships with debian sets the DNS servers per default.
# For dhcpcd in Wheezy and earlier we need to configure it to do that.
if info.release_codename not in {'jessie', 'sid'}:
from bootstrapvz.common.releases import wheezy
if info.manifest.release <= wheezy:
from bootstrapvz.common.tools import sed_i
dhcpcd = os.path.join(info.root, 'etc/default/dhcpcd')
sed_i(dhcpcd, '^#*SET_DNS=.*', 'SET_DNS=\'yes\'')

View file

@ -12,7 +12,8 @@ class DefaultPackages(Task):
def run(cls, info):
info.packages.add('file') # Needed for the init scripts
# isc-dhcp-client doesn't work properly with ec2
if info.release_codename in {'jessie', 'sid'}:
from bootstrapvz.common.releases import jessie
if info.manifest.release >= jessie:
info.packages.add('dhcpcd5')
else:
info.packages.add('dhcpcd')
@ -23,6 +24,6 @@ class DefaultPackages(Task):
import os.path
kernel_packages_path = os.path.join(os.path.dirname(__file__), 'packages-kernels.yml')
from bootstrapvz.common.tools import config_get
kernel_package = config_get(kernel_packages_path, [info.release_codename,
kernel_package = config_get(kernel_packages_path, [info.manifest.release.codename,
info.manifest.system['architecture']])
info.packages.add(kernel_package)

View file

@ -24,7 +24,7 @@ class DefaultPackages(Task):
kernel_packages_path = os.path.join(os.path.dirname(__file__), 'packages-kernels.yml')
from bootstrapvz.common.tools import config_get
kernel_package = config_get(kernel_packages_path, [info.release_codename,
kernel_package = config_get(kernel_packages_path, [info.manifest.release.codename,
info.manifest.system['architecture']])
info.packages.add(kernel_package)

View file

@ -13,6 +13,6 @@ class DefaultPackages(Task):
import os.path
kernel_packages_path = os.path.join(os.path.dirname(__file__), 'packages-kernels.yml')
from bootstrapvz.common.tools import config_get
kernel_package = config_get(kernel_packages_path, [info.release_codename,
kernel_package = config_get(kernel_packages_path, [info.manifest.release.codename,
info.manifest.system['architecture']])
info.packages.add(kernel_package)

View file

@ -13,6 +13,6 @@ class DefaultPackages(Task):
import os.path
kernel_packages_path = os.path.join(os.path.dirname(__file__), 'packages-kernels.yml')
from bootstrapvz.common.tools import config_get
kernel_package = config_get(kernel_packages_path, [info.release_codename,
kernel_package = config_get(kernel_packages_path, [info.manifest.release.codename,
info.manifest.system['architecture']])
info.packages.add(kernel_package)

View file

@ -79,8 +79,8 @@ def run_instance(image, manifest, instance_type, ec2_connection, vpc_connection)
if not waituntil(lambda: instance.get_console_output().output is not None, timeout=600, interval=3):
raise EC2InstanceStartupException('Timeout while fetching console output')
from bootstrapvz.common.tools import get_codename
if get_codename(manifest.system['release']) in ['squeeze', 'wheezy']:
from bootstrapvz.common.releases import wheezy
if manifest.release <= wheezy:
termination_string = 'INIT: Entering runlevel: 2'
else:
termination_string = 'Debian GNU/Linux'

View file

@ -43,7 +43,7 @@ def boot_image(manifest, build_server, bootstrap_info):
def run_instance(image, instance_name, manifest):
from instance import VirtualBoxInstance
instance = VirtualBoxInstance(image, instance_name,
manifest.system['architecture'], manifest.system['release'])
manifest.system['architecture'], manifest.release)
try:
instance.create()
try:

View file

@ -63,8 +63,8 @@ class VirtualBoxInstance(object):
# Gotta figure out a more reliable way to check when the system is done booting.
# Maybe bootstrapped unit test images should have a startup script that issues
# a callback to the host.
from bootstrapvz.common.tools import get_codename
if get_codename(self.release) in ['squeeze', 'wheezy']:
from bootstrapvz.common.releases import wheezy
if self.release <= wheezy:
termination_string = 'INIT: Entering runlevel: 2'
else:
termination_string = 'Debian GNU/Linux'

View file

@ -0,0 +1,39 @@
from nose.tools import raises
from bootstrapvz.common import releases
def test_gt():
assert releases.wheezy > releases.squeeze
def test_lt():
assert releases.wheezy < releases.stretch
def test_eq():
assert releases.wheezy == releases.wheezy
def test_neq():
assert releases.wheezy != releases.jessie
def test_identity():
assert releases.wheezy is releases.wheezy
def test_not_identity():
assert releases.wheezy is not releases.stable
assert releases.stable is not releases.jessie
def test_alias():
assert releases.oldstable == releases.wheezy
assert releases.stable == releases.jessie
assert releases.testing == releases.stretch
assert releases.unstable == releases.sid
@raises(releases.UnknownReleaseException)
def test_bogus_releasename():
releases.get_release('nemo')