Merge ~paelzer/cloud-init:fix-bug-1616831-new-and-old-apt-config into cloud-init:master

Proposed by Christian Ehrhardt 
Status: Merged
Merged at revision: 763f403c7b848b31780ef869fb7728b0d5e571a2
Proposed branch: ~paelzer/cloud-init:fix-bug-1616831-new-and-old-apt-config
Merge into: cloud-init:master
Diff against target: 195 lines (+117/-31)
2 files modified
cloudinit/config/cc_apt_configure.py (+36/-25)
tests/unittests/test_handler/test_handler_apt_source_v1.py (+81/-6)
Reviewer Review Type Date Requested Status
cloud-init Commiters Pending
Review via email: mp+304064@code.launchpad.net

Description of the change

apt-config: Prefer V3 format if specified together with V1/2 (LP: #1616831)

This set of changes contains the code addressing an issue if V1/2 AND V3 configs are specified at the same time. With these changes the V3 config is preferred now.

There are some sanity checks in place that kick in if the old (now dropped due to the preference) values differ from those that will be used (there would be too much danger to "silently" ignore something of the V1 spec and the user wondering why his changes take no effect.

It also extends and adds unitests in that area to cover even more of the existing and now added format conversion logic.

Passed make check, tox and bddeb+sbuild and thereby I hope ready for review.

To post a comment you must log in.

Update scan failed

At least one of the branches involved have failed to scan. You can manually schedule a rescan if required.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
diff --git a/cloudinit/config/cc_apt_configure.py b/cloudinit/config/cc_apt_configure.py
index 609dbb5..42c5641 100644
--- a/cloudinit/config/cc_apt_configure.py
+++ b/cloudinit/config/cc_apt_configure.py
@@ -464,13 +464,19 @@ def convert_mirror(oldcfg, aptcfg):
464464
465def convert_v2_to_v3_apt_format(oldcfg):465def convert_v2_to_v3_apt_format(oldcfg):
466 """convert old to new keys and adapt restructured mirror spec"""466 """convert old to new keys and adapt restructured mirror spec"""
467 oldkeys = ['apt_sources', 'apt_mirror', 'apt_mirror_search',467 mapoldkeys = {'apt_sources': 'sources',
468 'apt_mirror_search_dns', 'apt_proxy', 'apt_http_proxy',468 'apt_mirror': None,
469 'apt_ftp_proxy', 'apt_https_proxy',469 'apt_mirror_search': None,
470 'apt_preserve_sources_list', 'apt_custom_sources_list',470 'apt_mirror_search_dns': None,
471 'add_apt_repo_match']471 'apt_proxy': 'proxy',
472 'apt_http_proxy': 'http_proxy',
473 'apt_ftp_proxy': 'https_proxy',
474 'apt_https_proxy': 'ftp_proxy',
475 'apt_preserve_sources_list': 'preserve_sources_list',
476 'apt_custom_sources_list': 'sources_list',
477 'add_apt_repo_match': 'add_apt_repo_match'}
472 needtoconvert = []478 needtoconvert = []
473 for oldkey in oldkeys:479 for oldkey in mapoldkeys:
474 if oldcfg.get(oldkey, None) is not None:480 if oldcfg.get(oldkey, None) is not None:
475 needtoconvert.append(oldkey)481 needtoconvert.append(oldkey)
476482
@@ -480,32 +486,37 @@ def convert_v2_to_v3_apt_format(oldcfg):
480 LOG.debug("apt config: convert V2 to V3 format for keys '%s'",486 LOG.debug("apt config: convert V2 to V3 format for keys '%s'",
481 ", ".join(needtoconvert))487 ", ".join(needtoconvert))
482488
483 if oldcfg.get('apt', None) is not None:489 # if old AND new config are provided, prefer the new one (LP #1616831)
484 msg = ("Error in apt configuration: "490 newaptcfg = oldcfg.get('apt', None)
485 "old and new format of apt features are mutually exclusive "491 if newaptcfg is not None:
486 "('apt':'%s' vs '%s' key)" % (oldcfg.get('apt', None),492 LOG.debug("apt config: V1/2 and V3 format specified, preferring V3")
487 ", ".join(needtoconvert)))493 for oldkey in needtoconvert:
488 LOG.error(msg)494 newkey = mapoldkeys[oldkey]
489 raise ValueError(msg)495 verify = oldcfg[oldkey] # drop, but keep a ref for verification
496 del oldcfg[oldkey]
497 if newkey is None or newaptcfg.get(newkey, None) is None:
498 # no simple mapping or no collision on this particular key
499 continue
500 if verify != newaptcfg[newkey]:
501 raise ValueError("Old and New apt format defined with unequal "
502 "values %s vs %s @ %s" % (verify,
503 newaptcfg[newkey],
504 oldkey))
505 # return conf after clearing conflicting V1/2 keys
506 return oldcfg
490507
491 # create new format from old keys508 # create new format from old keys
492 aptcfg = {}509 aptcfg = {}
493510
494 # renames / moves under the apt key511 # simple renames / moves under the apt key
495 convert_key(oldcfg, aptcfg, 'add_apt_repo_match', 'add_apt_repo_match')512 for oldkey in mapoldkeys:
496 convert_key(oldcfg, aptcfg, 'apt_proxy', 'proxy')513 if mapoldkeys[oldkey] is not None:
497 convert_key(oldcfg, aptcfg, 'apt_http_proxy', 'http_proxy')514 convert_key(oldcfg, aptcfg, oldkey, mapoldkeys[oldkey])
498 convert_key(oldcfg, aptcfg, 'apt_https_proxy', 'https_proxy')
499 convert_key(oldcfg, aptcfg, 'apt_ftp_proxy', 'ftp_proxy')
500 convert_key(oldcfg, aptcfg, 'apt_custom_sources_list', 'sources_list')
501 convert_key(oldcfg, aptcfg, 'apt_preserve_sources_list',
502 'preserve_sources_list')
503 # dict format not changed since v2, just renamed and moved
504 convert_key(oldcfg, aptcfg, 'apt_sources', 'sources')
505515
516 # mirrors changed in a more complex way
506 convert_mirror(oldcfg, aptcfg)517 convert_mirror(oldcfg, aptcfg)
507518
508 for oldkey in oldkeys:519 for oldkey in mapoldkeys:
509 if oldcfg.get(oldkey, None) is not None:520 if oldcfg.get(oldkey, None) is not None:
510 raise ValueError("old apt key '%s' left after conversion" % oldkey)521 raise ValueError("old apt key '%s' left after conversion" % oldkey)
511522
diff --git a/tests/unittests/test_handler/test_handler_apt_source_v1.py b/tests/unittests/test_handler/test_handler_apt_source_v1.py
index d96779c..ddff434 100644
--- a/tests/unittests/test_handler/test_handler_apt_source_v1.py
+++ b/tests/unittests/test_handler/test_handler_apt_source_v1.py
@@ -528,6 +528,7 @@ class TestAptSourceConfig(TestCase):
528 'filename': self.aptlistfile2}528 'filename': self.aptlistfile2}
529 cfg3 = {'source': 'deb $MIRROR $RELEASE universe',529 cfg3 = {'source': 'deb $MIRROR $RELEASE universe',
530 'filename': self.aptlistfile3}530 'filename': self.aptlistfile3}
531 cfg = {'apt_sources': [cfg1, cfg2, cfg3]}
531 checkcfg = {self.aptlistfile: {'filename': self.aptlistfile,532 checkcfg = {self.aptlistfile: {'filename': self.aptlistfile,
532 'source': 'deb $MIRROR $RELEASE '533 'source': 'deb $MIRROR $RELEASE '
533 'multiverse'},534 'multiverse'},
@@ -537,15 +538,89 @@ class TestAptSourceConfig(TestCase):
537 'source': 'deb $MIRROR $RELEASE '538 'source': 'deb $MIRROR $RELEASE '
538 'universe'}}539 'universe'}}
539540
540 newcfg = cc_apt_configure.convert_v1_to_v2_apt_format([cfg1, cfg2,541 newcfg = cc_apt_configure.convert_to_v3_apt_format(cfg)
541 cfg3])542 self.assertEqual(newcfg['apt']['sources'], checkcfg)
542 self.assertEqual(newcfg, checkcfg)
543543
544 newcfg2 = cc_apt_configure.convert_v1_to_v2_apt_format(newcfg)544 # convert again, should stay the same
545 self.assertEqual(newcfg2, checkcfg)545 newcfg2 = cc_apt_configure.convert_to_v3_apt_format(newcfg)
546 self.assertEqual(newcfg2['apt']['sources'], checkcfg)
546547
548 # should work without raising an exception
549 cc_apt_configure.convert_to_v3_apt_format({})
550
551 with self.assertRaises(ValueError):
552 cc_apt_configure.convert_to_v3_apt_format({'apt_sources': 5})
553
554 def test_convert_to_new_format_collision(self):
555 """Test the conversion of old to new format with collisions
556 That matches e.g. the MAAS case specifying old and new config"""
557 cfg_1_and_3 = {'apt': {'proxy': 'http://192.168.122.1:8000/'},
558 'apt_proxy': 'http://192.168.122.1:8000/'}
559 cfg_3_only = {'apt': {'proxy': 'http://192.168.122.1:8000/'}}
560 cfgconflict = {'apt': {'proxy': 'http://192.168.122.1:8000/'},
561 'apt_proxy': 'ftp://192.168.122.1:8000/'}
562
563 # collision (equal)
564 newcfg = cc_apt_configure.convert_to_v3_apt_format(cfg_1_and_3)
565 self.assertEqual(newcfg, cfg_3_only)
566 # collision (equal, so ok to remove)
567 newcfg = cc_apt_configure.convert_to_v3_apt_format(cfg_3_only)
568 self.assertEqual(newcfg, cfg_3_only)
569 # collision (unequal)
570 with self.assertRaises(ValueError):
571 cc_apt_configure.convert_to_v3_apt_format(cfgconflict)
572
573 def test_convert_to_new_format_dict_collision(self):
574 cfg1 = {'source': 'deb $MIRROR $RELEASE multiverse',
575 'filename': self.aptlistfile}
576 cfg2 = {'source': 'deb $MIRROR $RELEASE main',
577 'filename': self.aptlistfile2}
578 cfg3 = {'source': 'deb $MIRROR $RELEASE universe',
579 'filename': self.aptlistfile3}
580 fullv3 = {self.aptlistfile: {'filename': self.aptlistfile,
581 'source': 'deb $MIRROR $RELEASE '
582 'multiverse'},
583 self.aptlistfile2: {'filename': self.aptlistfile2,
584 'source': 'deb $MIRROR $RELEASE main'},
585 self.aptlistfile3: {'filename': self.aptlistfile3,
586 'source': 'deb $MIRROR $RELEASE '
587 'universe'}}
588 cfg_3_only = {'apt': {'sources': fullv3}}
589 cfg_1_and_3 = {'apt_sources': [cfg1, cfg2, cfg3]}
590 cfg_1_and_3.update(cfg_3_only)
591
592 # collision (equal, so ok to remove)
593 newcfg = cc_apt_configure.convert_to_v3_apt_format(cfg_1_and_3)
594 self.assertEqual(newcfg, cfg_3_only)
595 # no old spec (same result)
596 newcfg = cc_apt_configure.convert_to_v3_apt_format(cfg_3_only)
597 self.assertEqual(newcfg, cfg_3_only)
598
599 diff = {self.aptlistfile: {'filename': self.aptlistfile,
600 'source': 'deb $MIRROR $RELEASE '
601 'DIFFERENTVERSE'},
602 self.aptlistfile2: {'filename': self.aptlistfile2,
603 'source': 'deb $MIRROR $RELEASE main'},
604 self.aptlistfile3: {'filename': self.aptlistfile3,
605 'source': 'deb $MIRROR $RELEASE '
606 'universe'}}
607 cfg_3_only = {'apt': {'sources': diff}}
608 cfg_1_and_3_different = {'apt_sources': [cfg1, cfg2, cfg3]}
609 cfg_1_and_3_different.update(cfg_3_only)
610
611 # collision (unequal by dict having a different entry)
612 with self.assertRaises(ValueError):
613 cc_apt_configure.convert_to_v3_apt_format(cfg_1_and_3_different)
614
615 missing = {self.aptlistfile: {'filename': self.aptlistfile,
616 'source': 'deb $MIRROR $RELEASE '
617 'multiverse'}}
618 cfg_3_only = {'apt': {'sources': missing}}
619 cfg_1_and_3_missing = {'apt_sources': [cfg1, cfg2, cfg3]}
620 cfg_1_and_3_missing.update(cfg_3_only)
621 # collision (unequal by dict missing an entry)
547 with self.assertRaises(ValueError):622 with self.assertRaises(ValueError):
548 cc_apt_configure.convert_v1_to_v2_apt_format(5)623 cc_apt_configure.convert_to_v3_apt_format(cfg_1_and_3_missing)
549624
550625
551# vi: ts=4 expandtab626# vi: ts=4 expandtab

Subscribers

People subscribed via source and target branches