Merge lp:~paelzer/curtin/bug-1574113-derived-repositories into lp:~curtin-dev/curtin/trunk

Proposed by Christian Ehrhardt 
Status: Merged
Merged at revision: 410
Proposed branch: lp:~paelzer/curtin/bug-1574113-derived-repositories
Merge into: lp:~curtin-dev/curtin/trunk
Diff against target: 4008 lines (+3551/-146)
25 files modified
curtin/__init__.py (+2/-0)
curtin/commands/apt_config.py (+691/-0)
curtin/commands/curthooks.py (+11/-135)
curtin/commands/main.py (+1/-1)
curtin/gpg.py (+74/-0)
curtin/util.py (+144/-2)
doc/devel/README-vmtest.txt (+3/-3)
doc/topics/apt_source.rst (+152/-0)
examples/apt-source.yaml (+239/-0)
examples/tests/apt_config_command.yaml (+85/-0)
examples/tests/apt_source_custom.yaml (+97/-0)
examples/tests/apt_source_modify.yaml (+92/-0)
examples/tests/apt_source_modify_arches.yaml (+102/-0)
examples/tests/apt_source_modify_disable_suite.yaml (+92/-0)
examples/tests/apt_source_preserve.yaml (+98/-0)
examples/tests/apt_source_search.yaml (+97/-0)
examples/tests/apt_source_search_dns.yaml (+21/-0)
examples/tests/test_old_apt_features.yaml (+10/-0)
examples/tests/test_old_apt_features_ports.yaml (+10/-0)
tests/unittests/test_apt_custom_sources_list.py (+172/-0)
tests/unittests/test_apt_source.py (+927/-0)
tests/vmtests/__init__.py (+19/-5)
tests/vmtests/test_apt_config_cmd.py (+55/-0)
tests/vmtests/test_apt_source.py (+277/-0)
tests/vmtests/test_old_apt_features.py (+80/-0)
To merge this branch: bzr merge lp:~paelzer/curtin/bug-1574113-derived-repositories
Reviewer Review Type Date Requested Status
Server Team CI bot continuous-integration Approve
Ryan Harper (community) Approve
Review via email: mp+296118@code.launchpad.net

Commit message

Adding apt features to curtin.

This allows to define derived repositories by being able to control various apt configurations, proxies, mirrors, ppa's, sources.list content and pgp keys trusted by apt.
See examples/apt-source.yaml for examples and explanation of the individual capabilities.

Along the actual features a huge list of related unit- and vm-tests is added.

Description of the change

Adding apt_source features for the install stage of curtin.
This is inherited from cloud-init, but simplified (as we don't have to care about some features and old compatibility).
And then added plenty of unit- and vm-tests for the functionality.

To post a comment you must log in.
Revision history for this message
Server Team CI bot (server-team-bot) wrote :
review: Needs Fixing (continuous-integration)
442. By Christian Ehrhardt 

test_mir_apt_list_rename fix assertion of call

depending on the unittest environment the rename calls can differ in order
and amount. Convert the check to any_call

443. By Christian Ehrhardt 

test_mir_apt_list_rename mockup source filename

Depending on the unittest environment the source file could be wrong.
E.g. if a developer runs with a non default mirror.
Mock up the check for this file and by that, fixup the test in various
environments.

444. By Christian Ehrhardt 

fix premission error in unit test

depending on the environment it could have had the side effect of really trying
to rename files. That was bad for either the permission error or the actual
rename.
So we mock the rename to keep it free of side effects.

445. By Christian Ehrhardt 

drop reinstalling the mocked subp as it is not needed anymore

In the simplified test subp is not mocked to begin with, so there is no reason
to restore it.

446. By Christian Ehrhardt 

convert all apt_custom_sources_list tests to fakerelease

That makes them independent to the execution environment

Revision history for this message
Server Team CI bot (server-team-bot) wrote :
review: Approve (continuous-integration)
Revision history for this message
Christian Ehrhardt  (paelzer) wrote :

The jenkins bot had identified a few issues with the unittests in Trusty environments which are now fixed.

Revision history for this message
Ryan Harper (raharper) wrote :

This is really nice.

A couple of nits in the code.

Super happy to see unittests AND vmtests +1

One last request, might be a doc/topics/apt_source.rst

I think most of it can be copied out of your example files which included lots of
documentation, but in general, I'd like to see some documentation that we'd hand
to the MAAS team about how to use the new dictionary config to pass through the
landscape derived repo.

review: Needs Fixing
Revision history for this message
Christian Ehrhardt  (paelzer) wrote :

Hi,
thanks again for the review.

I added some comments to your feedback where appropriate.
ALmost all is accepted and integrated, just sometimes in slightly modified ways and the comments state the reasons.
Any non commented feedback is acked and integrated as suggested.

My own unit and vmtests are fine still, I'll run a full suite of vmtests and if successful push to the MP for the next round of review or acceptance.

447. By Christian Ehrhardt 

clarify docstring for mirror2lists_fileprefix

448. By Christian Ehrhardt 

make lists_d a global constant in the form of APT_LISTS

449. By Christian Ehrhardt 

fix whitespace issue

450. By Christian Ehrhardt 

fix docstrings to always use triple quotes

451. By Christian Ehrhardt 

degrade test_apt_srcl_custom to static method

452. By Christian Ehrhardt 

handle potential exception on os.rename

453. By Christian Ehrhardt 

replace get_release usage with already existing lsb_release

454. By Christian Ehrhardt 

add sanity checks and early exit to render_string_to_file

455. By Christian Ehrhardt 

move BASIC_MATCHER up to global variables

456. By Christian Ehrhardt 

improve error message if add_key_raw fails

457. By Christian Ehrhardt 

improve docstring of method add_key

458. By Christian Ehrhardt 

make default mirror and keyserver global variables

459. By Christian Ehrhardt 

add unittest for specifying a custom keyserver

460. By Christian Ehrhardt 

simplify find_apt_mirror_info by using DEFAULT_MIRRORS

This now shares a single default definition with rename_apt_lists

461. By Christian Ehrhardt 

dropping unrelated changes

FYI - rharper will carry those in his docu update branch

462. By Christian Ehrhardt 

optimize apt_source unittest imports

463. By Christian Ehrhardt 

apt_source: add doc in rst format and improve example yaml

Revision history for this message
Server Team CI bot (server-team-bot) wrote :
review: Approve (continuous-integration)
464. By Christian Ehrhardt 

improve rst formatting

Revision history for this message
Christian Ehrhardt  (paelzer) wrote :

Ok, I found an rst format checker and was able to improve that as well.
Should be available for another round of review towards acceptance now.

Revision history for this message
Server Team CI bot (server-team-bot) wrote :
review: Approve (continuous-integration)
Revision history for this message
Ryan Harper (raharper) :
Revision history for this message
Christian Ehrhardt  (paelzer) wrote :

Hi,
thanks again for all the feedback.
Every section not explicitly mentiones is acked and integrated.

For everything else see the comment why/how I have modified the suggestions.

Running a full round of tests again before next upload.

465. By Christian Ehrhardt 

convert gpg key handling to pure python

466. By Christian Ehrhardt 

rename mirror2lists_fileprefix to mirrorurl_to_apt_fileprefix

This much more reflects what it does, so it helps code readability

467. By Christian Ehrhardt 

include exception info on the OSError exception for file renaming

468. By Christian Ehrhardt 

move generic gpg functions to util

469. By Christian Ehrhardt 

move generic part of template rendering to util

470. By Christian Ehrhardt 

add apt name portion to name add_key[_raw]

471. By Christian Ehrhardt 

backup sources.list before writing to it

472. By Christian Ehrhardt 

rename add_sources to the more appropriate add_apt_sources

473. By Christian Ehrhardt 

simply control-flow in apt_source

474. By Christian Ehrhardt 

simplify error handling in handle_apt_source

475. By Christian Ehrhardt 

remove unused import

Revision history for this message
Server Team CI bot (server-team-bot) wrote :
review: Approve (continuous-integration)
476. By Christian Ehrhardt 

unify key tests for real keys in apt_src_keyid_real

477. By Christian Ehrhardt 

fix gpg key based unitests to work in networkless environments as well

478. By Christian Ehrhardt 

add test for alternative keyserver

479. By Christian Ehrhardt 

capture output of gpg calls to avoid messing up stdout/stderr

480. By Christian Ehrhardt 

fix fallback key import

481. By Christian Ehrhardt 

drop gpg activity from apt-source key tests

While the unittest gets a bit less real by that change, it will work in
protected environment (e.g. sbuild) and leave the developers .gpg keyring

Revision history for this message
Christian Ehrhardt  (paelzer) wrote :

This now also includes some extensions/fixes created and discussed on the cloud-init side of things.
There is no reason to wait and find them again in the curtin code.

I could build fine in sbuild, but was unable to test in a ppa for now (gets rejected for bin/source mismatch for some reason).

I started to mess around with the changes file trying to fix it, but I guess smoser has a much nicer and faster way to throw that at a ppa - I'd be happy about a discussion what was missing.

Ready for another round of review for getting merged.

Revision history for this message
Server Team CI bot (server-team-bot) wrote :
review: Approve (continuous-integration)
Revision history for this message
Ryan Harper (raharper) wrote :

This is really nice. Just a couple of minor issues with naming and logging.

review: Needs Fixing
482. By Christian Ehrhardt 

improve debug message

483. By Christian Ehrhardt 

report stacktrace on high level exception handler

484. By Christian Ehrhardt 

typo fix fule->file

485. By Christian Ehrhardt 

lower debug level of renaming apt list message

486. By Christian Ehrhardt 

fix exception handler that had a unreferenced %s

487. By Christian Ehrhardt 

prefix getkeybyid with gpg_ as the other related functions

488. By Christian Ehrhardt 

rename basic_render to basic_template_render for readability

Revision history for this message
Christian Ehrhardt  (paelzer) wrote :

All integrated, except one that I discussed on IRC

Revision history for this message
Christian Ehrhardt  (paelzer) wrote :

Ready for another review or acceptance

Revision history for this message
Server Team CI bot (server-team-bot) wrote :
review: Approve (continuous-integration)
489. By Christian Ehrhardt 

improve error handling of gpg functions

Revision history for this message
Christian Ehrhardt  (paelzer) wrote :

Added another fix that came up when working on the cloud-init portion.
Ready for review towards merging again.

Revision history for this message
Server Team CI bot (server-team-bot) wrote :
review: Approve (continuous-integration)
Revision history for this message
Ryan Harper (raharper) wrote :

Looks great. Should get this merged today or tomorrow.

review: Approve
Revision history for this message
Scott Moser (smoser) wrote :

mostly nitpicks
some to make it like what is in cloud-init trunk.

Revision history for this message
Christian Ehrhardt  (paelzer) wrote :

Hi,
thanks for the review - all accepted and integrated except one.
I commented below and catch you on IRC for that one.

490. By Christian Ehrhardt 

move docsting after copyright

491. By Christian Ehrhardt 

move gpg functions to their own file

492. By Christian Ehrhardt 

drop unused self.orig_gpg_recv_key

493. By Christian Ehrhardt 

add curtin/gpg.py

494. By Christian Ehrhardt 

drop rstrip from gpg function

Revision history for this message
Christian Ehrhardt  (paelzer) wrote :

That is the integration of the last feedback, ready for review/merge again

Revision history for this message
Server Team CI bot (server-team-bot) wrote :
review: Approve (continuous-integration)
Revision history for this message
Scott Moser (smoser) wrote :

further reading and thinking.
I think that the implementation doens't actually solve the thing we need solved.

apt_sources configuration to curtin declares what the target's apt should be configured with (much the same as the storage_config or 'kernel' configuration is about the installed environment not the host).

So, the apt_sources needs to apply to the target.
For maas, to get all of this working together, it has to
a.) declare to cloud-init that certain apt sources are necessary in the ephemeral environment
b.) declare to curtin that the target environment must include apt configured with a set of apt sources.

'a' is done in cloud-init user-data
'b' is done in curtin config.

Revision history for this message
Christian Ehrhardt  (paelzer) wrote :

Hi Scott,
I've implemented it the "new" way now and adapted all around that was needed.
Please read the comments I added below as answer to your feedback - to see if my assumptions when implementing made sense.

Everything else in the next round of code review (just doing a full round of tests and build before to be sure).

495. By Christian Ehrhardt 

Merge with upstream to avoid conflicts

pending merges:
  Ryan Harper 2016-06-14 [merge] reporting: set webhook handler level to DEBUG, no filtering
    Ryan Harper 2016-06-14 [merge] merge from trunk
    Ryan Harper 2016-06-14 reporting: default webhook handler level to DEBUG, no filtering
    Scott Moser 2016-06-14 [merge] improve net-meta network configuration
    Scott Moser 2016-06-13 spelling
    Scott Moser 2016-06-13 log cleanup
    Scott Moser 2016-06-13 improve net-meta network configuration
    Ryan Harper 2016-06-03 [merge] curtin/net: fix inet value for subnets, don't add interface attributes to alias
    Ryan Harper 2016-06-03 [merge] curtin/net: calculate the inet value for each subnet independently
    Mike Pontillo 2016-06-03 Address review comment.
    Mike Pontillo 2016-06-02 Don't append the '6' to IPv4 aliases if an IPv6 alias preceded it.
    Mike Pontillo 2016-06-02 Add failing test case for bug #1588547
    Ryan Harper 2016-06-03 [merge] curtin/net: Don't add interface attributes to alias interfaces
    Mike Pontillo 2016-06-03 Don't add additional parameters to alias interfaces. (Rebase from trunk and address review comments.)

496. By Christian Ehrhardt 

add docstring to apt_source.py's POPULATE_SUBCMD

497. By Christian Ehrhardt 

avoid python checker warnings by making POPULATE_SUBCMD lower case

498. By Christian Ehrhardt 

add --target parameter to apt-source command

This allows to modify the target environment as needed.
But given it is an argment can work in arbitrary chroots as long as the tools
like gpg, apt-key as well as the files it modifies are there.

499. By Christian Ehrhardt 

adapt apt-source tests to check the target environment

500. By Christian Ehrhardt 

report checked files in output_files_exist

This can help to quickly identify which file is missing in case a long list
of files is checked.

501. By Christian Ehrhardt 

lsb_release to support target

502. By Christian Ehrhardt 

propagate and use target throughout apt-source handling

503. By Christian Ehrhardt 

fix issue in ChrootableTarget if resolv.conf didn't yet exist in target

504. By Christian Ehrhardt 

ensure errorlist contains only strings

505. By Christian Ehrhardt 

improve error handling to not silently ignore program execution issues

506. By Christian Ehrhardt 

order config builtin in execution order

507. By Christian Ehrhardt 

extend builtin key of CONFIG_BUILTIN

It is a single namespace per stage - just "builtin" is too ambiguous.

508. By Christian Ehrhardt 

insert apt-source after extract

509. By Christian Ehrhardt 

remove useless warn if no source is specified

This can be a quite common case since we allo keys without surce now.
No need to flood the log with warnings that didn't even tell you much.

510. By Christian Ehrhardt 

revert rev 503: fix issue in ChrootableTarget if resolv.conf

511. By Christian Ehrhardt 

update apt data after changing configuration and/or source.lists

512. By Christian Ehrhardt 

mock out apt_update in apt-source unit tests

513. By Christian Ehrhardt 

allow a testcase to provide custom cloud-init data to the booted system

514. By Christian Ehrhardt 

apt-source vmtest now uses boot_cloudconf

This allows the vmtest to avoid having cloud-init to rewrite its just laid
down sources list. Just as a final user is expected to set this conf when
using curtin apt-source feature the test is using itnow as well.

515. By Christian Ehrhardt 

doc: update for apt-source now having a target specification

516. By Christian Ehrhardt 

don't skip cloud-init if we run in preserved mode

517. By Christian Ehrhardt 

take back individual builtin naming

518. By Christian Ehrhardt 

hook apt-source handling at curthooks

519. By Christian Ehrhardt 

doc: update new placement of apt-source in curthooks

520. By Christian Ehrhardt 

harden apt-source unittests for more test environments

In some environments the lower levels run a few extra udevadm settle.
This makes the unittests resilent against those slightly different, but
acceptable changes in behavior.

Revision history for this message
Christian Ehrhardt  (paelzer) wrote :

Ran full testsuite, tested build in sbuild and on ppa.
Ready for review and inclusion again.

Revision history for this message
Server Team CI bot (server-team-bot) wrote :
review: Approve (continuous-integration)
521. By Christian Ehrhardt 

remove gpg_ prefix not that related functions are in a gpg.py

522. By Christian Ehrhardt 

drop builtin sources.list

In case there is no custom source list specified we will fall back to modify
the one we find on the given target. That is done with as few dependencies to
the source.list content as possible, to avoid issues in case that changes.
If expected content isn't found curtin will warn about it but not fail.
That means we have time to adapt/fix in case of changes and in the worst case
one can always fix things with a custom template.

523. By Christian Ehrhardt 

drop unused method load_tfile_or_url in tests

524. By Christian Ehrhardt 

adapt documentation and examples

Revision history for this message
Christian Ehrhardt  (paelzer) wrote :

Integrated the changes due to our discussion about having no "builtin" source.list.

If no custom template is provided we now fall back to modify whatever we find in the target.
All tests that should be affected passed already as well as an sbuild.

I'm running a ppa build and a full test-suite just to be sure and give this thread another ping then.

Revision history for this message
Server Team CI bot (server-team-bot) wrote :
review: Approve (continuous-integration)
Revision history for this message
Christian Ehrhardt  (paelzer) wrote :

All tests and ppa build went well, ready for re-review and inclusion.

525. By Christian Ehrhardt 

merge with latest trunk to avoid later merge conflicts

526. By Christian Ehrhardt 

restore populate_subcmd to old coding style, ignoring pylint complaint

This should keep the MP free of unrelated changes

527. By Christian Ehrhardt 

remove unrelated change in curtin/commands/install.py

That was needed/intended when we still wired it up as extra builtin command.
But no more now that we move to curthooks integration.

528. By Christian Ehrhardt 

remove stale merge artifacts

529. By Christian Ehrhardt 

integrate apt in curthooks

This makes the apt feature run by curthooks (but a noop of no config provided).
And additionally keeps the standalone command for other configs/targets.

530. By Christian Ehrhardt 

fix apt handling if no config is provided

531. By Christian Ehrhardt 

fix debconf if only provided for unconfigurable packages

532. By Christian Ehrhardt 

log apt_proxy configuration

533. By Christian Ehrhardt 

fix apt conf to match upper/lower case of apt-config

534. By Christian Ehrhardt 

add Testcases for old curtin apt features

535. By Christian Ehrhardt 

match aptproxy config path as specified

536. By Christian Ehrhardt 

translate old curtin apt features to new format

This includes (re-)moving of old curtin code.
All apt related configuration is handled in apt.py now.
In a config that specified old&new content it fails with a proper error
message as they are mutually exclusive (curtin can't decide which one to keep).

537. By Christian Ehrhardt 

modify existing functionality and tests to follow the new apt config format

538. By Christian Ehrhardt 

improve mirror and source_list handling

Replace the full string found in config, set the full specified string.
This gets rid of // or no / in testcases.
Set the mirror to what was specified, that is what has to be done.
Also fixup some references to the old custom_source_list to source_list

539. By Christian Ehrhardt 

add search and search_dns features

This inherits the cloud-init capabilities for these two features.
But it follows the new apt config format with split options for
primary/secondary for that as well.
Code could be simplified and unified a lot so that despite the extra split
primary/secondary it is mre readable now.
As discussed some fallbacks of cloud-init that rely on cloud-datasource are
dropped in the curtin handling of that configuration.

540. By Christian Ehrhardt 

add unittest test_mirror_search

541. By Christian Ehrhardt 

add unittest test_mirror_search_dns

542. By Christian Ehrhardt 

add unittests test_mirror_search_many2 and test_mirror_search_many3

543. By Christian Ehrhardt 

drop second subcheck that is already covered by test_mirror_search_many3

Would require some extra mocking as now search is hit for dns and non_dns cases.
Since it is covered already drop that part.

544. By Scott Moser

list feature APT_CONFIG_V1

545. By Christian Ehrhardt 

fixed urlparse call path

546. By Christian Ehrhardt 

fix py2/py3 compatibility import of urlparse

547. By Christian Ehrhardt 

add unittests for is_resolvable_url

548. By Christian Ehrhardt 

add vmtest for mirror_search features

549. By Christian Ehrhardt 

make dns search resilent against fqdn not being avavilable

550. By Christian Ehrhardt 

add vmtest for dns_search feature

551. By Christian Ehrhardt 

adapt example to cover search and search_dns features

552. By Christian Ehrhardt 

move primary/security mirror specifications into a dict - code

553. By Christian Ehrhardt 

move primary/security mirror specifications into a dict - examples

554. By Christian Ehrhardt 

move primary/security mirror specifications into a dict - testcases

555. By Christian Ehrhardt 

fix TestAptSrcModify reference to test yaml

556. By Christian Ehrhardt 

make mirror specs per acrhitecture - code

557. By Christian Ehrhardt 

make mirror specs per acrhitecture - examples

558. By Christian Ehrhardt 

make mirror specs per acrhitecture - testcases

559. By Christian Ehrhardt 

arches and repositioned mirrors adapted in doc

560. By Christian Ehrhardt 

add unittest test_mirror

561. By Christian Ehrhardt 

add unittest test_mirror_default

562. By Christian Ehrhardt 

add unittest test_mirror_arches

563. By Christian Ehrhardt 

add unittest test_mirror_arches_default

564. By Christian Ehrhardt 

add unittest test_mirror_arches_sysdefault

565. By Christian Ehrhardt 

add vmtest TestAptSrcModifyArches

566. By Christian Ehrhardt 

make replaced archive arch dependent

567. By Christian Ehrhardt 

make tetsing old apt features work cross platforms

568. By Christian Ehrhardt 

make use of new arch-dependent mirrors in unittests

This should also make the tests more likely to work on non amd64 architectures.

569. By Christian Ehrhardt 

make find apt mirror info tests work on ubuntu ports

570. By Christian Ehrhardt 

disable_pockets feature - code

571. By Christian Ehrhardt 

disable_pockets feature - example

572. By Christian Ehrhardt 

disable_pockets feature - unittest

573. By Christian Ehrhardt 

disable_pockets feature - vmtest

574. By Christian Ehrhardt 

update example in rst doc with disable_pockets feature

575. By Christian Ehrhardt 

update docu with some more use cases

576. By Christian Ehrhardt 

adapt old apt feature entry path to new mirrr config

Revision history for this message
Christian Ehrhardt  (paelzer) wrote :

Ready for review - curtin implementation of the new definition as in the gdoc.
I need to update the commit message thou ..

Revision history for this message
Server Team CI bot (server-team-bot) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Server Team CI bot (server-team-bot) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Christian Ehrhardt  (paelzer) wrote :

That seems to be stuff that only triggers in an environment even more
restricted than I have tested on horse.
I'll try to fix and push over the day, ...

Christian Ehrhardt
Software Engineer, Ubuntu Server
Canonical Ltd

On Wed, Jul 20, 2016 at 8:08 PM, Server Team CI bot <
<email address hidden>> wrote:

> Review: Needs Fixing continuous-integration
>
> FAILED: Continuous integration, rev:576
> https://server-team-jenkins.canonical.com/job/curtin-ci/318/
> Executed test runs:
> None:
> https://server-team-jenkins.canonical.com/job/generic-update-mp/332/console
>
> Click here to trigger a rebuild:
> https://server-team-jenkins.canonical.com/job/curtin-ci/318/rebuild
>
> --
>
> https://code.launchpad.net/~paelzer/curtin/bug-1574113-derived-repositories/+merge/296118
> You are the owner of lp:~paelzer/curtin/bug-1574113-derived-repositories.
>

Revision history for this message
Christian Ehrhardt  (paelzer) wrote :

Hmm,
seems to be about mocking out an object that it hard fetched not from mock
context or so.
What might be interesting for CI in general is that I can reproduce with:
"nosetests tests/unittests" (as called by jenkins), while "make check" runs
just fine.

Christian Ehrhardt
Software Engineer, Ubuntu Server
Canonical Ltd

On Thu, Jul 21, 2016 at 11:16 AM, Christian Ehrhardt <
<email address hidden>> wrote:

> That seems to be stuff that only triggers in an environment even more
> restricted than I have tested on horse.
> I'll try to fix and push over the day, ...
>
> Christian Ehrhardt
> Software Engineer, Ubuntu Server
> Canonical Ltd
>
> On Wed, Jul 20, 2016 at 8:08 PM, Server Team CI bot <
> <email address hidden>> wrote:
>
>> Review: Needs Fixing continuous-integration
>>
>> FAILED: Continuous integration, rev:576
>> https://server-team-jenkins.canonical.com/job/curtin-ci/318/
>> Executed test runs:
>> None:
>> https://server-team-jenkins.canonical.com/job/generic-update-mp/332/console
>>
>> Click here to trigger a rebuild:
>> https://server-team-jenkins.canonical.com/job/curtin-ci/318/rebuild
>>
>> --
>>
>> https://code.launchpad.net/~paelzer/curtin/bug-1574113-derived-repositories/+merge/296118
>> You are the owner of lp:~paelzer/curtin/bug-1574113-derived-repositories.
>>
>
>

Revision history for this message
Christian Ehrhardt  (paelzer) wrote :

The magic switch seems to be "--nologcapture" which is kind of unexpected
...
With it enabled things work, without it seems that the mocked call to
get_mirror like:
pmirror = get_mirror(cfg, "primary", arch)
Gets "mocked wrong"

Christian Ehrhardt
Software Engineer, Ubuntu Server
Canonical Ltd

On Thu, Jul 21, 2016 at 11:24 AM, Christian Ehrhardt <
<email address hidden>> wrote:

> Hmm,
> seems to be about mocking out an object that it hard fetched not from mock
> context or so.
> What might be interesting for CI in general is that I can reproduce with:
> "nosetests tests/unittests" (as called by jenkins), while "make check" runs
> just fine.
>
> Christian Ehrhardt
> Software Engineer, Ubuntu Server
> Canonical Ltd
>
> On Thu, Jul 21, 2016 at 11:16 AM, Christian Ehrhardt <
> <email address hidden>> wrote:
>
>> That seems to be stuff that only triggers in an environment even more
>> restricted than I have tested on horse.
>> I'll try to fix and push over the day, ...
>>
>> Christian Ehrhardt
>> Software Engineer, Ubuntu Server
>> Canonical Ltd
>>
>> On Wed, Jul 20, 2016 at 8:08 PM, Server Team CI bot <
>> <email address hidden>> wrote:
>>
>>> Review: Needs Fixing continuous-integration
>>>
>>> FAILED: Continuous integration, rev:576
>>> https://server-team-jenkins.canonical.com/job/curtin-ci/318/
>>> Executed test runs:
>>> None:
>>> https://server-team-jenkins.canonical.com/job/generic-update-mp/332/console
>>>
>>> Click here to trigger a rebuild:
>>> https://server-team-jenkins.canonical.com/job/curtin-ci/318/rebuild
>>>
>>> --
>>>
>>> https://code.launchpad.net/~paelzer/curtin/bug-1574113-derived-repositories/+merge/296118
>>> You are the owner of lp:~paelzer/curtin/bug-1574113-derived-repositories.
>>>
>>
>>
>

Revision history for this message
Christian Ehrhardt  (paelzer) wrote :

Ok, things make sense now.
Mocking without retvale sets pmirror to the mock.
That without disabled log leads to "LOG.info("got primary mirror: %s",
pmirror)" be a constructor call.

Ok, that should be fixable ...

Christian Ehrhardt
Software Engineer, Ubuntu Server
Canonical Ltd

On Thu, Jul 21, 2016 at 11:54 AM, Christian Ehrhardt <
<email address hidden>> wrote:

> The magic switch seems to be "--nologcapture" which is kind of unexpected
> ...
> With it enabled things work, without it seems that the mocked call to
> get_mirror like:
> pmirror = get_mirror(cfg, "primary", arch)
> Gets "mocked wrong"
>
> Christian Ehrhardt
> Software Engineer, Ubuntu Server
> Canonical Ltd
>
> On Thu, Jul 21, 2016 at 11:24 AM, Christian Ehrhardt <
> <email address hidden>> wrote:
>
>> Hmm,
>> seems to be about mocking out an object that it hard fetched not from
>> mock context or so.
>> What might be interesting for CI in general is that I can reproduce with:
>> "nosetests tests/unittests" (as called by jenkins), while "make check" runs
>> just fine.
>>
>> Christian Ehrhardt
>> Software Engineer, Ubuntu Server
>> Canonical Ltd
>>
>> On Thu, Jul 21, 2016 at 11:16 AM, Christian Ehrhardt <
>> <email address hidden>> wrote:
>>
>>> That seems to be stuff that only triggers in an environment even more
>>> restricted than I have tested on horse.
>>> I'll try to fix and push over the day, ...
>>>
>>> Christian Ehrhardt
>>> Software Engineer, Ubuntu Server
>>> Canonical Ltd
>>>
>>> On Wed, Jul 20, 2016 at 8:08 PM, Server Team CI bot <
>>> <email address hidden>> wrote:
>>>
>>>> Review: Needs Fixing continuous-integration
>>>>
>>>> FAILED: Continuous integration, rev:576
>>>> https://server-team-jenkins.canonical.com/job/curtin-ci/318/
>>>> Executed test runs:
>>>> None:
>>>> https://server-team-jenkins.canonical.com/job/generic-update-mp/332/console
>>>>
>>>> Click here to trigger a rebuild:
>>>> https://server-team-jenkins.canonical.com/job/curtin-ci/318/rebuild
>>>>
>>>> --
>>>>
>>>> https://code.launchpad.net/~paelzer/curtin/bug-1574113-derived-repositories/+merge/296118
>>>> You are the owner of
>>>> lp:~paelzer/curtin/bug-1574113-derived-repositories.
>>>>
>>>
>>>
>>
>

577. By Christian Ehrhardt 

make metohd of apt commandline handling less ambiguous

578. By Christian Ehrhardt 

add docstring to clean_cloud_init

579. By Christian Ehrhardt 

follow the "logging-not-lazy" warning of checkers and change string extension

580. By Christian Ehrhardt 

fix unittests for cases without --nologcapture

Revision history for this message
Server Team CI bot (server-team-bot) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Christian Ehrhardt  (paelzer) wrote :

Hmm, that is tox now with a special case on urllib which changed py2->py3.
General question - should we make a tox run part of make check ?

581. By Christian Ehrhardt 

avoid py2 pylint issue due to renamed urllib import

Revision history for this message
Server Team CI bot (server-team-bot) wrote :
review: Approve (continuous-integration)
Revision history for this message
Server Team CI bot (server-team-bot) wrote :
review: Approve (continuous-integration)
Revision history for this message
Scott Moser (smoser) wrote :

Started a vmtest run at
https://server-team-jenkins.canonical.com/job/curtin-vmtest-venonat-devel/33/

I dont think we have 'preserve_sources_list' implemented as intended.

Our doc says:
|  preserve_sources_list: <true|false|keep>
|    # For cloud-init this defaults to false. By default, cloud-init renders
|    # /etc/apt/sources.list from its provided templates on first boot.
|    # For Curtin this defaults to false. By default, curtin will always attempt to
|    # replace mirrors inside the sources.list it finds in the image.
|    # Curtin will configure cloud-init in the target via
|    # /etc/cloud/cloud.cfg.d/curtin-preserve-sources.list with True,
|    # which causes cloud-init to not touch /etc/apt/sources.list by default.
|    # Unless explicitly  requested by cloud-init userdata (overwriting to False).
|    # Effectively this changes the default from False to True

I thought that in discussion you and I had agreed that this was in fact 'boolean', and that curtin would *always* replace /etc/apt/sources.list . I'm not terribly opposed to a tri-state, where it can be told to leave it untouched though.

That said, i do not see where curtin is configuring cloud-init in the target to set. curtin should always set cloud-init to preserve sources.list unless there is another use case that i'm missing.

I'm not done with the review yet, but wanted to hit 'submit' before losing context and losing what i had done.

Revision history for this message
Scott Moser (smoser) wrote :

for disable_pockets, it looks to me like the string provided always has the release prepended.
for example:
 disable_pockets = [updates]
will disable 'trusty-updates'.

that makes sense.
but for:
  disable_pockets = ["mypocket", "unstable"]
this doesnt make so much sense.
I dont know how easily to do this other than special casing the words:
 updates, release, security, backports

also, might want to just check that this content from my /etc/apt/sources.list.d/google-chrome.list works as expected:
 deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main

Revision history for this message
Scott Moser (smoser) wrote :

I thought that 'uri' would override 'search', does it?

Revision history for this message
Christian Ehrhardt  (paelzer) wrote :

> Started a vmtest run at
> https://server-team-jenkins.canonical.com/job/curtin-vmtest-venonat-devel/33/

Thanks, that one worked at least.

> I dont think we have 'preserve_sources_list' implemented as intended.

Yeah the tristate was dropped in our discussion yet the doc needs update (DONE now).
The placing of the config for cloud-init was missing (I missed to add to our todo's after the discussion).
Pushed in the next update to the MP.

Also all vmtests that take care to check.
So far I provided "preserve_sources_list" via the vmtest, now that it is implemented I dropped it there and it is still working the same way.

It seems I need something to make cloud-init to pick up the config thou - will contact you later today as I guess that is talked in 3 or searched in 15 minutes :-)

Revision history for this message
Christian Ehrhardt  (paelzer) wrote :

> I thought that 'uri' would override 'search', does it?

Yes it does:
see curtin/commands/apt.py:get_mirror

1. uri
2. search
3. search_dns

As soon as one of 1-3 hits the others aren't checked anymore.

Also see the following unittests that ensure this is true:
test_mirror_search_many3 (uri beats search & search_dns)
test_mirror_search_many2 (search beats search_dns)

Revision history for this message
Christian Ehrhardt  (paelzer) wrote :

> for disable_pockets, it looks to me like the string provided always has the
> release prepended.
> for example:
> disable_pockets = [updates]
> will disable 'trusty-updates'.
>
> that makes sense.
> but for:
> disable_pockets = ["mypocket", "unstable"]
> this doesnt make so much sense.
> I dont know how easily to do this other than special casing the words:
> updates, release, security, backports

I didn't think of "custom" pockets yet, but I see no other easy way than "special casing" them.

> also, might want to just check that this content from my
> /etc/apt/sources.list.d/google-chrome.list works as expected:
> deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main

Hrm, yeah man sources.list states this (options) in the section "ONE-LINE-STYLE FORMAT".
I need to adapt to work with them as well.
Although it is highly unlikely to find some in a source.list (disabling doesn't care about other files in sources.list.d) these days we should look ahead and be prepared.
Working on it ...

Revision history for this message
Christian Ehrhardt  (paelzer) wrote :

> > I dont know how easily to do this other than special casing the words:
> > updates, release, security, backports
>
> I didn't think of "custom" pockets yet, but I see no other easy way than
> "special casing" them.

I was missing an "... either" here.

But giving it some thought - if we go as far as to allow custom pocket names.
We need to cover custom pocket names that match the known names.
So what if someone has a "updates" pocket and it shall not become xenial-updates.

We already have a way to specify these and I think we should stick to that.
We can allow $RELEASE in there and replace it, just as we do when rendering sources.list in the first place.

That allows one to differentiate between: "$RELEASE-updates" and "updates"

That also covers the sepcial case of the main release pocket being "".
One can just specify "$RELEASE" and gets e.g. "xenial" which is just what we need in that case as well.

Revision history for this message
Christian Ehrhardt  (paelzer) wrote :

Reading more into it we should think about:
- what we called "pockets" is actually defined as "suite" - should we rename our option?
- long term (not for this bug & MP) think about supporting deb822 format as well

582. By Christian Ehrhardt 

leave cloud-init config to prevent clobbering sources.list

583. By Christian Ehrhardt 

add $RELEASE replacement to disable_pockets

584. By Christian Ehrhardt 

improve doc wording

585. By Christian Ehrhardt 

check for old apt_preserve_list config as we select th eold for compatibility

586. By Christian Ehrhardt 

fix typo

587. By Christian Ehrhardt 

support source.list lines with options

588. By Christian Ehrhardt 

cover options in source.list lines in unittests

589. By Christian Ehrhardt 

fix test_old_apt_features vmtest

590. By Christian Ehrhardt 

switch apt_preserve_sources_list to True

Now that all tests have succeeded we switch the apt_preserve_sources_list
that is passed to cloud-init to True to keep curtin rendered sources.list
intact unless overwritten by user data.

591. By Christian Ehrhardt 

rename pocket to suite following man sources.list

592. By Christian Ehrhardt 

rename curtin-preserve-sources.list to curtin-preserve-sources.cfg

The example in the gdoc was uncommon, clout-init usually has .cfg files

593. By Christian Ehrhardt 

rename vmtets yaml following the pockets to suite rename

594. By Christian Ehrhardt 

fix preserve check in vmtest TestAptSrcPreserve

In this path the preserving of the target is not triggered, so the respective
checks need to be adapted.

Revision history for this message
Christian Ehrhardt  (paelzer) wrote :

All Feedback handled and a bit more (according to my comments to the review feedback before).
Ready again.

Revision history for this message
Server Team CI bot (server-team-bot) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Server Team CI bot (server-team-bot) wrote :
review: Needs Fixing (continuous-integration)
595. By Christian Ehrhardt 

make unittests runnable as non-privileged user in tox

596. By Christian Ehrhardt 

let curtin exit with failure if protecting sources.list didn't work

597. By Christian Ehrhardt 

make trusty pep8 happy which even complains about raw string length

Revision history for this message
Christian Ehrhardt  (paelzer) wrote :

de-toxed the unitests for disable_suites

Revision history for this message
Server Team CI bot (server-team-bot) wrote :
review: Approve (continuous-integration)
Revision history for this message
Server Team CI bot (server-team-bot) wrote :
review: Approve (continuous-integration)
Revision history for this message
Scott Moser (smoser) wrote :

> > > I dont know how easily to do this other than special casing the words:
> > > updates, release, security, backports
> >
> > I didn't think of "custom" pockets yet, but I see no other easy way than
> > "special casing" them.
>
> I was missing an "... either" here.
>
> But giving it some thought - if we go as far as to allow custom pocket names.
> We need to cover custom pocket names that match the known names.
> So what if someone has a "updates" pocket and it shall not become xenial-
> updates.
>
> We already have a way to specify these and I think we should stick to that.
> We can allow $RELEASE in there and replace it, just as we do when rendering
> sources.list in the first place.
>
> That allows one to differentiate between: "$RELEASE-updates" and "updates"
>
> That also covers the sepcial case of the main release pocket being "".
> One can just specify "$RELEASE" and gets e.g. "xenial" which is just what we
> need in that case as well.

I'd really prefer to handle let the user specify 'updates', 'release' literally rather than '$RELEASE-updates'.

that does mean that these "well known names" block the user from using pockets literally named 'release'. I usually dont like such holes in design, but i think it might be worth it here for readability. The pocket names 'release', 'updates', 'proposed' and 'backports' are well known in Ubuntu (example https://wiki.ubuntu.com/SecurityTeam/FAQ).

given the same url there, i use that for justification of the name 'pockets'. If you'd like we can also support 'suites', but i think pocket is fine.

Revision history for this message
Scott Moser (smoser) wrote :

I think you must not have seen my in-line comments in my review that starts with
'for disable_pockets, '

598. By Christian Ehrhardt 

auto extend known ubunut suites by $RELEASE-

599. By Christian Ehrhardt 

reduce default verbosity of apt feature by moving some info to debug

600. By Christian Ehrhardt 

make old apt features less verbose

601. By Christian Ehrhardt 

move old apt feature config conversion into own method and out of curthooks

602. By Christian Ehrhardt 

rework apt standalone command entry path

This is dropping a lot of old artifacts from the command entry path.

603. By Christian Ehrhardt 

minimal commandline call test for apt features

604. By Christian Ehrhardt 

add spaced around string concat plus operators

605. By Christian Ehrhardt 

merge rev 400: move uefi boot knowledge from launch and vmtest to xkvm

Revision history for this message
Christian Ehrhardt  (paelzer) wrote :

All we discussed yesterday night is in now, ready for review/merge

606. By Christian Ehrhardt 

fix config of ChrootableTarget for apt command

607. By Christian Ehrhardt 

adapt standalong command example to new invocation

608. By Christian Ehrhardt 

change standalong apt command test to be vmtest based

609. By Christian Ehrhardt 

rename apt to apt_config for stanalone command

That implies to rename the module name as well.

Revision history for this message
Server Team CI bot (server-team-bot) wrote :
review: Approve (continuous-integration)
610. By Christian Ehrhardt 

change apt to apt-config in vmtest

611. By Christian Ehrhardt 

change ppa's and keys used to something under our control

612. By Christian Ehrhardt 

change the preserve_sources_list default to True in curtin

613. By Christian Ehrhardt 

rework testing of standalone command

614. By Christian Ehrhardt 

make copy in testcases release independent

615. By Christian Ehrhardt 

add fallback if user did not add .list to source keys

Includes modification to one of the testcastes to cover that.

616. By Christian Ehrhardt 

fixup test to check for content added by apt-add-repository

Revision history for this message
Server Team CI bot (server-team-bot) wrote :
review: Approve (continuous-integration)
Revision history for this message
Christian Ehrhardt  (paelzer) wrote :

Since we co-work on a branch that we can jointly push to now there is a new MP at https://code.launchpad.net/~ubuntu-server/curtin/bug-1574113-derived-repositories/+merge/301362

This old MP here is kept open to have the discussion history available (not sure if it would go away when rejecting the old MP).

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'curtin/__init__.py'
--- curtin/__init__.py 2015-10-26 17:35:29 +0000
+++ curtin/__init__.py 2016-07-26 19:53:43 +0000
@@ -33,6 +33,8 @@
33 'SUBCOMMAND_SYSTEM_INSTALL',33 'SUBCOMMAND_SYSTEM_INSTALL',
34 # subcommand 'system-upgrade' is present34 # subcommand 'system-upgrade' is present
35 'SUBCOMMAND_SYSTEM_UPGRADE',35 'SUBCOMMAND_SYSTEM_UPGRADE',
36 # supports new format of apt configuration
37 'APT_CONFIG_V1',
36]38]
3739
38# vi: ts=4 expandtab syntax=python40# vi: ts=4 expandtab syntax=python
3941
=== added file 'curtin/commands/apt_config.py'
--- curtin/commands/apt_config.py 1970-01-01 00:00:00 +0000
+++ curtin/commands/apt_config.py 2016-07-26 19:53:43 +0000
@@ -0,0 +1,691 @@
1# Copyright (C) 2016 Canonical Ltd.
2#
3# Author: Christian Ehrhardt <christian.ehrhardt@canonical.com>
4#
5# Curtin is free software: you can redistribute it and/or modify it under
6# the terms of the GNU Affero General Public License as published by the
7# Free Software Foundation, either version 3 of the License, or (at your
8# option) any later version.
9#
10# Curtin is distributed in the hope that it will be useful, but WITHOUT ANY
11# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
12# FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for
13# more details.
14#
15# You should have received a copy of the GNU Affero General Public License
16# along with Curtin. If not, see <http://www.gnu.org/licenses/>.
17"""
18apt.py
19Handling the setup of apt related tasks like proxies, PGP keys, repositories.
20"""
21
22import argparse
23import glob
24import os
25import re
26import sys
27import yaml
28
29from curtin.log import LOG
30from curtin import (config, util, gpg)
31
32from . import populate_one_subcmd
33
34# this will match 'XXX:YYY' (ie, 'cloud-archive:foo' or 'ppa:bar')
35ADD_APT_REPO_MATCH = r"^[\w-]+:\w"
36
37# place where apt stores cached repository data
38APT_LISTS = "/var/lib/apt/lists"
39
40# Files to store proxy information
41APT_CONFIG_FN = "/etc/apt/apt.conf.d/94curtin-config"
42APT_PROXY_FN = "/etc/apt/apt.conf.d/90curtin-aptproxy"
43
44# Default keyserver to use
45DEFAULT_KEYSERVER = "keyserver.ubuntu.com"
46
47# Default archive mirrors
48PRIMARY_ARCH_MIRRORS = {"PRIMARY": "http://archive.ubuntu.com/ubuntu/",
49 "SECURITY": "http://security.ubuntu.com/ubuntu/"}
50PORTS_MIRRORS = {"PRIMARY": "http://ports.ubuntu.com/ubuntu-ports",
51 "SECURITY": "http://ports.ubuntu.com/ubuntu-ports"}
52PRIMARY_ARCHES = ['amd64', 'i386']
53PORTS_ARCHES = ['s390x', 'arm64', 'armhf', 'powerpc', 'ppc64el']
54
55
56def get_default_mirrors(target=None):
57 """returns the default mirrors for the target. These depend on the
58 architecture, for more see:
59 https://wiki.ubuntu.com/UbuntuDevelopment/PackageArchive#Ports"""
60 arch = util.get_architecture(target)
61 if arch in PRIMARY_ARCHES:
62 return PRIMARY_ARCH_MIRRORS
63 if arch in PORTS_ARCHES:
64 return PORTS_MIRRORS
65 raise ValueError("No default mirror known for arch %s" % arch)
66
67
68def handle_apt(cfg, target):
69 """ handle_apt
70 process the config for apt_config. This can be called from
71 curthooks if a global apt config was provided or via the "apt"
72 standalone command.
73 """
74 release = util.lsb_release(target=target)['codename']
75 mirrors = find_apt_mirror_info(cfg, target)
76 LOG.debug("Apt Mirror info: %s", mirrors)
77
78 apply_debconf_selections(cfg, target)
79
80 if not config.value_as_boolean(cfg.get('preserve_sources_list',
81 True)):
82 generate_sources_list(cfg, release, mirrors, target)
83 rename_apt_lists(mirrors, target)
84
85 try:
86 apply_apt_proxy_config(cfg, target + APT_PROXY_FN,
87 target + APT_CONFIG_FN)
88 except (IOError, OSError):
89 LOG.exception("Failed to apply proxy or apt config info:")
90
91 # Process 'apt_source -> sources {dict}'
92 if 'sources' in cfg:
93 params = mirrors
94 params['RELEASE'] = release
95 params['MIRROR'] = mirrors["MIRROR"]
96
97 matcher = None
98 matchcfg = cfg.get('add_apt_repo_match', ADD_APT_REPO_MATCH)
99 if matchcfg:
100 matcher = re.compile(matchcfg).search
101
102 add_apt_sources(cfg['sources'], target,
103 template_params=params, aa_repo_match=matcher)
104
105
106def apply_debconf_selections(cfg, target):
107 """apply_debconf_selections - push content to debconf"""
108 # debconf_selections:
109 # set1: |
110 # cloud-init cloud-init/datasources multiselect MAAS
111 # set2: pkg pkg/value string bar
112 selsets = cfg.get('debconf_selections')
113 if not selsets:
114 LOG.debug("debconf_selections was not set in config")
115 return
116
117 # for each entry in selections, chroot and apply them.
118 # keep a running total of packages we've seen.
119 pkgs_cfgd = set()
120 for key, content in selsets.items():
121 LOG.debug("setting for %s, %s", key, content)
122 util.subp(['chroot', target, 'debconf-set-selections'],
123 data=content.encode())
124 for line in content.splitlines():
125 if line.startswith("#"):
126 continue
127 pkg = re.sub(r"[:\s].*", "", line)
128 pkgs_cfgd.add(pkg)
129
130 pkgs_installed = util.get_installed_packages(target)
131
132 LOG.debug("pkgs_cfgd: %s", pkgs_cfgd)
133 LOG.debug("pkgs_installed: %s", pkgs_installed)
134 need_reconfig = pkgs_cfgd.intersection(pkgs_installed)
135
136 if len(need_reconfig) == 0:
137 LOG.debug("no need for reconfig")
138 return
139
140 # For any packages that are already installed, but have preseed data
141 # we populate the debconf database, but the filesystem configuration
142 # would be preferred on a subsequent dpkg-reconfigure.
143 # so, what we have to do is "know" information about certain packages
144 # to unconfigure them.
145 unhandled = []
146 to_config = []
147 for pkg in need_reconfig:
148 if pkg in CONFIG_CLEANERS:
149 LOG.debug("unconfiguring %s", pkg)
150 CONFIG_CLEANERS[pkg](target)
151 to_config.append(pkg)
152 else:
153 unhandled.append(pkg)
154
155 if len(unhandled):
156 LOG.warn("The following packages were installed and preseeded, "
157 "but cannot be unconfigured: %s", unhandled)
158
159 if len(to_config):
160 util.subp(['chroot', target, 'dpkg-reconfigure',
161 '--frontend=noninteractive'] +
162 list(to_config), data=None)
163
164
165def clean_cloud_init(target):
166 """clean out any local cloud-init config"""
167 flist = glob.glob(
168 os.path.sep.join([target, "/etc/cloud/cloud.cfg.d/*dpkg*"]))
169
170 LOG.debug("cleaning cloud-init config from: %s", flist)
171 for dpkg_cfg in flist:
172 os.unlink(dpkg_cfg)
173
174
175def mirrorurl_to_apt_fileprefix(mirror):
176 """ mirrorurl_to_apt_fileprefix
177 Convert a mirror url to the file prefix used by apt on disk to
178 store cache information for that mirror.
179 To do so do:
180 - take off ???://
181 - drop tailing /
182 - convert in string / to _
183 """
184 string = mirror
185 if string.endswith("/"):
186 string = string[0:-1]
187 pos = string.find("://")
188 if pos >= 0:
189 string = string[pos + 3:]
190 string = string.replace("/", "_")
191 return string
192
193
194def rename_apt_lists(new_mirrors, target):
195 """rename_apt_lists - rename apt lists to preserve old cache data"""
196 default_mirrors = get_default_mirrors(target)
197 for (name, omirror) in default_mirrors.items():
198 nmirror = new_mirrors.get(name)
199 if not nmirror:
200 continue
201 oprefix = os.path.join(target, APT_LISTS,
202 mirrorurl_to_apt_fileprefix(omirror))
203 nprefix = os.path.join(target, APT_LISTS,
204 mirrorurl_to_apt_fileprefix(nmirror))
205 if oprefix == nprefix:
206 continue
207 olen = len(oprefix)
208 for filename in glob.glob("%s_*" % oprefix):
209 newname = "%s%s" % (nprefix, filename[olen:])
210 LOG.debug("Renaming apt list %s to %s", filename, newname)
211 try:
212 os.rename(filename, newname)
213 except OSError:
214 # since this is a best effort task, warn with but don't fail
215 LOG.warn("Failed to rename apt list:", exc_info=True)
216
217
218def mirror_to_placeholder(tmpl, mirror, placeholder):
219 """ mirror_to_placeholder
220 replace the specified mirror in a template with a placeholder string
221 Checks for existance of the expected mirror and warns if not found
222 """
223 if mirror not in tmpl:
224 LOG.warn("Expected mirror '%s' not found in: %s", mirror, tmpl)
225 return tmpl.replace(mirror, placeholder)
226
227
228def map_known_suites(suite):
229 """there are a few default names which will be auto-extended.
230 This comes at the inability to use those names literally as suites,
231 but on the other hand increases readability of the cfg quite a lot"""
232 mapping = {'updates': '$RELEASE-updates',
233 'backports': '$RELEASE-backports',
234 'security': '$RELEASE-security',
235 'proposed': '$RELEASE-proposed',
236 'release': '$RELEASE'}
237 try:
238 retsuite = mapping[suite]
239 except KeyError:
240 retsuite = suite
241 return retsuite
242
243
244def disable_suites(cfg, src, release):
245 """reads the config for suites to be disabled and removes those
246 from the template"""
247 retsrc = src
248 suites_to_disable = cfg.get('disable_suites', None)
249 if suites_to_disable is not None:
250 for suite in suites_to_disable:
251 suite = map_known_suites(suite)
252 releasesuite = util.render_string(suite, {'RELEASE': release})
253 LOG.debug("Disabling suite %s as %s", suite, releasesuite)
254
255 newsrc = ""
256 for line in retsrc.splitlines(True):
257 if line.startswith("#"):
258 newsrc += line
259 continue
260
261 # sources.list allow options in cols[1] which can have spaces
262 # so the actual suite can be [2] or later
263 cols = line.split()
264 pcol = 2
265 if cols[1].startswith("["):
266 for col in cols[1:]:
267 pcol += 1
268 if col.endswith("]"):
269 break
270
271 if cols[pcol] == releasesuite:
272 line = '# suite disabled by curtin: %s' % line
273 newsrc += line
274 retsrc = newsrc
275
276 return retsrc
277
278
279def generate_sources_list(cfg, release, mirrors, target):
280 """ generate_sources_list
281 create a source.list file based on a custom or default template
282 by replacing mirrors and release in the template
283 """
284 default_mirrors = get_default_mirrors(target)
285 aptsrc = "/etc/apt/sources.list"
286 params = {'RELEASE': release}
287 for k in mirrors:
288 params[k] = mirrors[k]
289
290 tmpl = cfg.get('sources_list', None)
291 if tmpl is None:
292 LOG.info("No custom template provided, fall back to modify"
293 "mirrors in %s on the target system", aptsrc)
294 tmpl = util.load_file(target + aptsrc)
295 # Strategy if no custom template was provided:
296 # - Only replacing mirrors
297 # - no reason to replace "release" as it is from target anyway
298 # - The less we depend upon, the more stable this is against changes
299 # - warn if expected original content wasn't found
300 tmpl = mirror_to_placeholder(tmpl, default_mirrors['PRIMARY'],
301 "$MIRROR")
302 tmpl = mirror_to_placeholder(tmpl, default_mirrors['SECURITY'],
303 "$SECURITY")
304 try:
305 os.rename(target + aptsrc,
306 target + aptsrc + ".curtin")
307 except OSError:
308 LOG.exception("failed to backup %s/%s", target, aptsrc)
309
310 rendered = util.render_string(tmpl, params)
311 disabled = disable_suites(cfg, rendered, release)
312 util.write_file(target + aptsrc, disabled, mode=0o644)
313
314 # protect the just generated sources.list from cloud-init
315 clouddir = "/etc/cloud/cloud.cfg.d"
316 cloudfile = clouddir + "/" + "curtin-preserve-sources.cfg"
317 # this has to work with older cloud-init as well, so use old key
318 cloudconf = yaml.dump({'apt_preserve_sources_list': True}, indent=1)
319 util.subp(['mkdir', '-p', target + clouddir], rcs=[0, 1])
320 try:
321 util.write_file(target + cloudfile, cloudconf, mode=0o644)
322 except IOError:
323 LOG.exception("Failed to protect source.list from cloud-init in (%s)",
324 target + cloudfile)
325 raise
326
327
328def add_apt_key_raw(key, target):
329 """
330 actual adding of a key as defined in key argument
331 to the system
332 """
333 LOG.debug("Adding key:\n'%s'", key)
334 try:
335 with util.RunInChroot(target, allow_daemons=True) as in_chroot:
336 in_chroot(['apt-key', 'add', '-'], data=key.encode())
337 except util.ProcessExecutionError:
338 LOG.exception("failed to add apt GPG Key to apt keyring")
339 raise
340
341
342def add_apt_key(ent, target):
343 """
344 Add key to the system as defined in ent (if any).
345 Supports raw keys or keyid's
346 The latter will as a first step fetched to get the raw key
347 """
348 if 'keyid' in ent and 'key' not in ent:
349 keyserver = DEFAULT_KEYSERVER
350 if 'keyserver' in ent:
351 keyserver = ent['keyserver']
352
353 ent['key'] = gpg.getkeybyid(ent['keyid'], keyserver)
354
355 if 'key' in ent:
356 add_apt_key_raw(ent['key'], target)
357
358
359def add_apt_sources(srcdict, target, template_params=None, aa_repo_match=None):
360 """
361 add entries in /etc/apt/sources.list.d for each abbreviated
362 sources.list entry in 'srcdict'. When rendering template, also
363 include the values in dictionary searchList
364 """
365 if template_params is None:
366 template_params = {}
367
368 if aa_repo_match is None:
369 raise ValueError('did not get a valid repo matcher')
370
371 if not isinstance(srcdict, dict):
372 raise TypeError('unknown apt format: %s' % (srcdict))
373
374 for filename in srcdict:
375 ent = srcdict[filename]
376 if 'filename' not in ent:
377 ent['filename'] = filename
378
379 add_apt_key(ent, target)
380
381 if 'source' not in ent:
382 continue
383 source = ent['source']
384 source = util.render_string(source, template_params)
385
386 if not ent['filename'].startswith("/"):
387 ent['filename'] = os.path.join("/etc/apt/sources.list.d/",
388 ent['filename'])
389 if not ent['filename'].endswith(".list"):
390 ent['filename'] += ".list"
391
392 if aa_repo_match(source):
393 try:
394 with util.RunInChroot(target, allow_daemons=True) as in_chroot:
395 in_chroot(["add-apt-repository", source])
396 except util.ProcessExecutionError:
397 LOG.exception("add-apt-repository failed.")
398 raise
399 continue
400
401 sourcefn = target + ent['filename']
402 try:
403 contents = "%s\n" % (source)
404 util.write_file(sourcefn, contents, omode="a")
405 except IOError as detail:
406 LOG.exception("failed write to file %s: %s", sourcefn, detail)
407 raise
408
409 util.apt_update(target=target, force=True,
410 comment="apt-source changed config")
411
412 return
413
414
415def search_for_mirror(candidates):
416 """
417 Search through a list of mirror urls for one that works
418 This needs to return quickly.
419 """
420 if candidates is None:
421 return None
422
423 for cand in candidates:
424 try:
425 if util.is_resolvable_url(cand):
426 return cand
427 except Exception:
428 pass
429 return None
430
431
432def search_for_mirror_dns(enabled, mirrortext):
433 "builds a list of potential mirror to check"
434 if enabled is None or not enabled:
435 return None
436
437 mydom = ""
438 doms = []
439
440 # curtin has no fqdn/hostname in config as cloud-init
441 # but if we got a hostname by dhcp, then search its domain portion first
442 try:
443 (fqdn, _) = util.subp(["hostname", "--fqdn"], rcs=[0], capture=True)
444 mydom = ".".join(fqdn.split(".")[1:])
445 if mydom:
446 doms.append(".%s" % mydom)
447 except util.ProcessExecutionError:
448 # this can happen if /etc/hostname isn't set up properly yet
449 # so log, but don't fail
450 LOG.exception("failed to get fqdn")
451
452 doms.extend((".localdomain", "",))
453
454 potential_mirror_list = []
455 # for curtin just ubuntu instead of fetching from datasource
456 distro = "ubuntu"
457 mirrorfmt = "http://%s-%s%s/%s" % (distro, mirrortext, "%s", distro)
458 for post in doms:
459 potential_mirror_list.append(mirrorfmt % (post))
460
461 return search_for_mirror(potential_mirror_list)
462
463
464def update_mirror_info(pmirror, smirror, target=None):
465 """sets security mirror to primary if not defined.
466 returns defaults if no mirrors are defined"""
467 if pmirror is not None:
468 if smirror is None:
469 smirror = pmirror
470 return {'PRIMARY': pmirror,
471 'SECURITY': smirror}
472 return get_default_mirrors(target)
473
474
475def get_arch_mirrorconfig(cfg, mirrortype, arch):
476 """out of a list of potential mirror configurations select
477 and return the one matching the architecture (or default)"""
478 # select the mirror specification (if-any)
479 mirror_cfg_list = cfg.get(mirrortype, None)
480 if mirror_cfg_list is None:
481 return None
482
483 # select the specification matching the target arch
484 default = None
485 for mirror_cfg_elem in mirror_cfg_list:
486 arches = mirror_cfg_elem.get("arches")
487 if arch in arches:
488 return mirror_cfg_elem
489 if "default" in arches:
490 default = mirror_cfg_elem
491 return default
492
493
494def get_mirror(cfg, mirrortype, arch):
495 """pass the three potential stages of mirror specification
496 returns None is neither of them found anything otherwise the first
497 hit is returned"""
498 mcfg = get_arch_mirrorconfig(cfg, mirrortype, arch)
499 if mcfg is None:
500 return None
501
502 # directly specified
503 mirror = mcfg.get("uri", None)
504 if mirror is None:
505 # list of mirrors to try to resolve
506 mirror = search_for_mirror(mcfg.get("search", None))
507
508 if mirror is None:
509 # search for predfined dns patterns
510 if mirrortype == "primary":
511 pattern = "mirror"
512 else:
513 pattern = "%s-mirror" % mirrortype
514 mirror = search_for_mirror_dns(mcfg.get("search_dns", None), pattern)
515
516 return mirror
517
518
519def find_apt_mirror_info(cfg, target=None):
520 """find_apt_mirror_info
521 find an apt_mirror given the cfg provided.
522 It can check for separate config of primary and security mirrors
523 If only primary is given security is assumed to be equal to primary
524 If the generic apt_mirror is given that is defining for both
525 """
526
527 arch = util.get_architecture(target)
528 LOG.debug("got arch for mirror selection: %s", arch)
529 pmirror = get_mirror(cfg, "primary", arch)
530 LOG.debug("got primary mirror: %s", pmirror)
531 smirror = get_mirror(cfg, "security", arch)
532 LOG.debug("got security mirror: %s", smirror)
533
534 # Note: curtin has no cloud-datasource fallback
535
536 mirror_info = update_mirror_info(pmirror, smirror, target)
537
538 # less complex replacements use only MIRROR, derive from primary
539 mirror_info["MIRROR"] = mirror_info["PRIMARY"]
540
541 return mirror_info
542
543
544def apply_apt_proxy_config(cfg, proxy_fname, config_fname):
545 """apply_apt_proxy_config
546 Applies any apt*proxy config from if specified
547 """
548 # Set up any apt proxy
549 cfgs = (('proxy', 'Acquire::http::Proxy "%s";'),
550 ('http_proxy', 'Acquire::http::Proxy "%s";'),
551 ('ftp_proxy', 'Acquire::ftp::Proxy "%s";'),
552 ('https_proxy', 'Acquire::https::Proxy "%s";'))
553
554 proxies = [fmt % cfg.get(name) for (name, fmt) in cfgs if cfg.get(name)]
555 if len(proxies):
556 LOG.debug("write apt proxy info to %s", proxy_fname)
557 util.write_file(proxy_fname, '\n'.join(proxies) + '\n')
558 elif os.path.isfile(proxy_fname):
559 util.del_file(proxy_fname)
560 LOG.debug("no apt proxy configured, removed %s", proxy_fname)
561
562 if cfg.get('conf', None):
563 LOG.debug("write apt config info to %s", config_fname)
564 util.write_file(config_fname, cfg.get('conf'))
565 elif os.path.isfile(config_fname):
566 util.del_file(config_fname)
567 LOG.debug("no apt config configured, removed %s", config_fname)
568
569
570def apt_command(args):
571 """ Main entry point for curtin apt-config standalone command
572 This does not read the global config as handled by curthooks, but
573 instead one can specify a different "target" and a new cfg via --config
574 """
575 cfg = config.load_command_config(args, {})
576
577 if args.target is not None:
578 target = args.target
579 else:
580 state = util.load_command_environment()
581 target = state['target']
582
583 if target is None:
584 sys.stderr.write("Unable to find target. "
585 "Use --target or set TARGET_MOUNT_POINT\n")
586 sys.exit(2)
587
588 apt_cfg = cfg.get("apt")
589 # if no apt config section is available, do nothing
590 if apt_cfg is not None:
591 LOG.debug("Handling apt to target %s with config %s",
592 target, apt_cfg)
593 try:
594 with util.ChrootableTarget(target, allow_daemons=False,
595 sys_resolvconf=True):
596 handle_apt(apt_cfg, target)
597 except (RuntimeError, TypeError, ValueError, IOError):
598 LOG.exception("Failed to configure apt features '%s'", apt_cfg)
599 sys.exit(1)
600 else:
601 LOG.info("No apt config provided, skipping")
602
603 sys.exit(0)
604
605
606def translate_old_apt_features(cfg):
607 """translate the few old apt related features into the new config format"""
608 predef_apt_cfg = cfg.get("apt")
609 if predef_apt_cfg is None:
610 cfg['apt'] = {}
611 predef_apt_cfg = cfg.get("apt")
612
613 if cfg.get('apt_proxy') is not None:
614 if predef_apt_cfg.get('proxy') is not None:
615 msg = ("Error in apt_proxy configuration: "
616 "old and new format of apt features "
617 "are mutually exclusive")
618 LOG.error(msg)
619 raise ValueError(msg)
620
621 cfg['apt']['proxy'] = cfg.get('apt_proxy')
622 LOG.debug("Transferred %s into new format: %s", cfg.get('apt_proxy'),
623 cfg.get('apte'))
624 del cfg['apt_proxy']
625
626 if cfg.get('apt_mirrors') is not None:
627 if predef_apt_cfg.get('mirrors') is not None:
628 msg = ("Error in apt_mirror configuration: "
629 "old and new format of apt features "
630 "are mutually exclusive")
631 LOG.error(msg)
632 raise ValueError(msg)
633
634 old = cfg.get('apt_mirrors')
635 cfg['apt']['primary'] = [{"arches": ["default"],
636 "uri": old.get('ubuntu_archive')}]
637 cfg['apt']['security'] = [{"arches": ["default"],
638 "uri": old.get('ubuntu_security')}]
639 LOG.debug("Transferred %s into new format: %s", cfg.get('apt_mirror'),
640 cfg.get('apt'))
641 del cfg['apt_mirrors']
642 # to work this also needs to disable the default protection
643 psl = predef_apt_cfg.get('preserve_sources_list')
644 if psl is not None:
645 if config.value_as_boolean(psl) is True:
646 msg = ("Error in apt_mirror configuration: "
647 "apt_mirrors and preserve_sources_list: True "
648 "are mutually exclusive")
649 LOG.error(msg)
650 raise ValueError(msg)
651 cfg['apt']['preserve_sources_list'] = False
652
653 if cfg.get('debconf_selections') is not None:
654 if predef_apt_cfg.get('debconf_selections') is not None:
655 msg = ("Error in debconf_selections configuration: "
656 "old and new format of apt features "
657 "are mutually exclusive")
658 LOG.error(msg)
659 raise ValueError(msg)
660
661 selsets = cfg.get('debconf_selections')
662 cfg['apt']['debconf_selections'] = selsets
663 LOG.info("Transferred %s into new format: %s",
664 cfg.get('debconf_selections'),
665 cfg.get('apt'))
666 del cfg['debconf_selections']
667
668 return cfg
669
670
671CMD_ARGUMENTS = (
672 ((('-c', '--config'),
673 {'help': 'read configuration from cfg', 'action': util.MergedCmdAppend,
674 'metavar': 'FILE', 'type': argparse.FileType("rb"),
675 'dest': 'cfgopts', 'default': []}),
676 (('-t', '--target'),
677 {'help': 'chroot to target. default is env[TARGET_MOUNT_POINT]',
678 'action': 'store', 'metavar': 'TARGET',
679 'default': os.environ.get('TARGET_MOUNT_POINT')}),)
680)
681
682
683def POPULATE_SUBCMD(parser):
684 """Populate subcommand option parsing for apt-config"""
685 populate_one_subcmd(parser, CMD_ARGUMENTS, apt_command)
686
687CONFIG_CLEANERS = {
688 'cloud-init': clean_cloud_init,
689}
690
691# vi: ts=4 expandtab syntax=python
0692
=== modified file 'curtin/commands/curthooks.py'
--- curtin/commands/curthooks.py 2016-06-24 19:27:52 +0000
+++ curtin/commands/curthooks.py 2016-07-26 19:53:43 +0000
@@ -16,10 +16,8 @@
16# along with Curtin. If not, see <http://www.gnu.org/licenses/>.16# along with Curtin. If not, see <http://www.gnu.org/licenses/>.
1717
18import copy18import copy
19import glob
20import os19import os
21import platform20import platform
22import re
23import sys21import sys
24import shutil22import shutil
25import textwrap23import textwrap
@@ -32,6 +30,7 @@
32from curtin import util30from curtin import util
33from curtin import net31from curtin import net
34from curtin.reporter import events32from curtin.reporter import events
33from curtin.commands import apt_config
3534
36from . import populate_one_subcmd35from . import populate_one_subcmd
3736
@@ -90,45 +89,15 @@
90 info.get('perms', "0644")))89 info.get('perms', "0644")))
9190
9291
93def apt_config(cfg, target):92def do_apt_config(cfg, target):
94 # cfg['apt_proxy']93 cfg = apt_config.translate_old_apt_features(cfg)
9594 apt_cfg = cfg.get("apt")
96 proxy_cfg_path = os.path.sep.join(95 if apt_cfg is not None:
97 [target, '/etc/apt/apt.conf.d/90curtin-aptproxy'])96 LOG.info("curthooks handling apt to target %s with config %s",
98 if cfg.get('apt_proxy'):97 target, apt_cfg)
99 util.write_file(98 apt_config.handle_apt(apt_cfg, target)
100 proxy_cfg_path,
101 content='Acquire::HTTP::Proxy "%s";\n' % cfg['apt_proxy'])
102 else:99 else:
103 if os.path.isfile(proxy_cfg_path):100 LOG.info("No apt config provided, skipping")
104 os.unlink(proxy_cfg_path)
105
106 # cfg['apt_mirrors']
107 # apt_mirrors:
108 # ubuntu_archive: http://local.archive/ubuntu
109 # ubuntu_security: http://local.archive/ubuntu
110 sources_list = os.path.sep.join([target, '/etc/apt/sources.list'])
111 if (isinstance(cfg.get('apt_mirrors'), dict) and
112 os.path.isfile(sources_list)):
113 repls = [
114 ('ubuntu_archive', r'http://\S*[.]*archive.ubuntu.com/\S*'),
115 ('ubuntu_security', r'http://security.ubuntu.com/\S*'),
116 ]
117 content = None
118 for name, regex in repls:
119 mirror = cfg['apt_mirrors'].get(name)
120 if not mirror:
121 continue
122
123 if content is None:
124 with open(sources_list) as fp:
125 content = fp.read()
126 util.write_file(sources_list + ".dist", content)
127
128 content = re.sub(regex, mirror + " ", content)
129
130 if content is not None:
131 util.write_file(sources_list, content)
132101
133102
134def disable_overlayroot(cfg, target):103def disable_overlayroot(cfg, target):
@@ -140,15 +109,6 @@
140 shutil.move(local_conf, local_conf + ".old")109 shutil.move(local_conf, local_conf + ".old")
141110
142111
143def clean_cloud_init(target):
144 flist = glob.glob(
145 os.path.sep.join([target, "/etc/cloud/cloud.cfg.d/*dpkg*"]))
146
147 LOG.debug("cleaning cloud-init config from: %s" % flist)
148 for dpkg_cfg in flist:
149 os.unlink(dpkg_cfg)
150
151
152def _maybe_remove_legacy_eth0(target,112def _maybe_remove_legacy_eth0(target,
153 path="/etc/network/interfaces.d/eth0.cfg"):113 path="/etc/network/interfaces.d/eth0.cfg"):
154 """Ubuntu cloud images previously included a 'eth0.cfg' that had114 """Ubuntu cloud images previously included a 'eth0.cfg' that had
@@ -293,85 +253,6 @@
293 " System may not boot.", package)253 " System may not boot.", package)
294254
295255
296def apply_debconf_selections(cfg, target):
297 # debconf_selections:
298 # set1: |
299 # cloud-init cloud-init/datasources multiselect MAAS
300 # set2: pkg pkg/value string bar
301 selsets = cfg.get('debconf_selections')
302 if not selsets:
303 LOG.debug("debconf_selections was not set in config")
304 return
305
306 # for each entry in selections, chroot and apply them.
307 # keep a running total of packages we've seen.
308 pkgs_cfgd = set()
309 for key, content in selsets.items():
310 LOG.debug("setting for %s, %s" % (key, content))
311 util.subp(['chroot', target, 'debconf-set-selections'],
312 data=content.encode())
313 for line in content.splitlines():
314 if line.startswith("#"):
315 continue
316 pkg = re.sub(r"[:\s].*", "", line)
317 pkgs_cfgd.add(pkg)
318
319 pkgs_installed = get_installed_packages(target)
320
321 LOG.debug("pkgs_cfgd: %s" % pkgs_cfgd)
322 LOG.debug("pkgs_installed: %s" % pkgs_installed)
323 need_reconfig = pkgs_cfgd.intersection(pkgs_installed)
324
325 if len(need_reconfig) == 0:
326 LOG.debug("no need for reconfig")
327 return
328
329 # For any packages that are already installed, but have preseed data
330 # we populate the debconf database, but the filesystem configuration
331 # would be preferred on a subsequent dpkg-reconfigure.
332 # so, what we have to do is "know" information about certain packages
333 # to unconfigure them.
334 unhandled = []
335 to_config = []
336 for pkg in need_reconfig:
337 if pkg in CONFIG_CLEANERS:
338 LOG.debug("unconfiguring %s" % pkg)
339 CONFIG_CLEANERS[pkg](target)
340 to_config.append(pkg)
341 else:
342 unhandled.append(pkg)
343
344 if len(unhandled):
345 LOG.warn("The following packages were installed and preseeded, "
346 "but cannot be unconfigured: %s", unhandled)
347
348 util.subp(['chroot', target, 'dpkg-reconfigure',
349 '--frontend=noninteractive'] +
350 list(to_config), data=None)
351
352
353def get_installed_packages(target=None):
354 cmd = []
355 if target is not None:
356 cmd = ['chroot', target]
357 cmd.extend(['dpkg-query', '--list'])
358
359 (out, _err) = util.subp(cmd, capture=True)
360 if isinstance(out, bytes):
361 out = out.decode()
362
363 pkgs_inst = set()
364 for line in out.splitlines():
365 try:
366 (state, pkg, other) = line.split(None, 2)
367 except ValueError:
368 continue
369 if state.startswith("hi") or state.startswith("ii"):
370 pkgs_inst.add(re.sub(":.*", "", pkg))
371
372 return pkgs_inst
373
374
375def setup_grub(cfg, target):256def setup_grub(cfg, target):
376 # target is the path to the mounted filesystem257 # target is the path to the mounted filesystem
377258
@@ -740,7 +621,7 @@
740 }621 }
741622
742 needed_packages = []623 needed_packages = []
743 installed_packages = get_installed_packages(target)624 installed_packages = util.get_installed_packages(target)
744 for cust_cfg, pkg_reqs in custom_configs.items():625 for cust_cfg, pkg_reqs in custom_configs.items():
745 if cust_cfg not in cfg:626 if cust_cfg not in cfg:
746 continue627 continue
@@ -820,7 +701,7 @@
820 name=stack_prefix, reporting_enabled=True, level="INFO",701 name=stack_prefix, reporting_enabled=True, level="INFO",
821 description="writing config files and configuring apt"):702 description="writing config files and configuring apt"):
822 write_files(cfg, target)703 write_files(cfg, target)
823 apt_config(cfg, target)704 do_apt_config(cfg, target)
824 disable_overlayroot(cfg, target)705 disable_overlayroot(cfg, target)
825706
826 # packages may be needed prior to installing kernel707 # packages may be needed prior to installing kernel
@@ -843,7 +724,6 @@
843 setup_zipl(cfg, target)724 setup_zipl(cfg, target)
844 install_kernel(cfg, target)725 install_kernel(cfg, target)
845 run_zipl(cfg, target)726 run_zipl(cfg, target)
846 apply_debconf_selections(cfg, target)
847727
848 restore_dist_interfaces(cfg, target)728 restore_dist_interfaces(cfg, target)
849729
@@ -906,8 +786,4 @@
906 populate_one_subcmd(parser, CMD_ARGUMENTS, curthooks)786 populate_one_subcmd(parser, CMD_ARGUMENTS, curthooks)
907787
908788
909CONFIG_CLEANERS = {
910 'cloud-init': clean_cloud_init,
911}
912
913# vi: ts=4 expandtab syntax=python789# vi: ts=4 expandtab syntax=python
914790
=== modified file 'curtin/commands/main.py'
--- curtin/commands/main.py 2016-04-04 20:12:01 +0000
+++ curtin/commands/main.py 2016-07-26 19:53:43 +0000
@@ -27,7 +27,7 @@
2727
28SUB_COMMAND_MODULES = [28SUB_COMMAND_MODULES = [
29 'apply_net', 'block-meta', 'block-wipe', 'curthooks', 'extract',29 'apply_net', 'block-meta', 'block-wipe', 'curthooks', 'extract',
30 'hook', 'in-target', 'install', 'mkfs', 'net-meta',30 'hook', 'in-target', 'install', 'mkfs', 'net-meta', 'apt-config',
31 'pack', 'swap', 'system-install', 'system-upgrade']31 'pack', 'swap', 'system-install', 'system-upgrade']
3232
3333
3434
=== added file 'curtin/gpg.py'
--- curtin/gpg.py 1970-01-01 00:00:00 +0000
+++ curtin/gpg.py 2016-07-26 19:53:43 +0000
@@ -0,0 +1,74 @@
1# Copyright (C) 2016 Canonical Ltd.
2#
3# Author: Scott Moser <scott.moser@canonical.com>
4# Christian Ehrhardt <christian.ehrhardt@canonical.com>
5#
6# Curtin is free software: you can redistribute it and/or modify it under
7# the terms of the GNU Affero General Public License as published by the
8# Free Software Foundation, either version 3 of the License, or (at your
9# option) any later version.
10#
11# Curtin is distributed in the hope that it will be useful, but WITHOUT ANY
12# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
13# FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for
14# more details.
15#
16# You should have received a copy of the GNU Affero General Public License
17# along with Curtin. If not, see <http://www.gnu.org/licenses/>.
18""" gpg.py
19gpg related utilities to get raw keys data by their id
20"""
21
22from curtin import util
23
24from .log import LOG
25
26
27def export_armour(key):
28 """Export gpg key, armoured key gets returned"""
29 try:
30 (armour, _) = util.subp(["gpg", "--export", "--armour", key],
31 capture=True)
32 except util.ProcessExecutionError as error:
33 # debug, since it happens for any key not on the system initially
34 LOG.debug('Failed to export armoured key "%s": %s', key, error)
35 armour = None
36 return armour
37
38
39def recv_key(key, keyserver):
40 """Receive gpg key from the specified keyserver"""
41 LOG.debug('Receive gpg key "%s"', key)
42 try:
43 util.subp(["gpg", "--keyserver", keyserver, "--recv", key],
44 capture=True)
45 except util.ProcessExecutionError as error:
46 raise ValueError(('Failed to import key "%s" '
47 'from server "%s" - error %s') %
48 (key, keyserver, error))
49
50
51def delete_key(key):
52 """Delete the specified key from the local gpg ring"""
53 try:
54 util.subp(["gpg", "--batch", "--yes", "--delete-keys", key],
55 capture=True)
56 except util.ProcessExecutionError as error:
57 LOG.warn('Failed delete key "%s": %s', key, error)
58
59
60def getkeybyid(keyid, keyserver='keyserver.ubuntu.com'):
61 """get gpg keyid from keyserver"""
62 armour = export_armour(keyid)
63 if not armour:
64 try:
65 recv_key(keyid, keyserver=keyserver)
66 armour = export_armour(keyid)
67 except ValueError:
68 LOG.exception('Failed to obtain gpg key %s', keyid)
69 raise
70 finally:
71 # delete just imported key to leave environment as it was before
72 delete_key(keyid)
73
74 return armour
075
=== modified file 'curtin/util.py'
--- curtin/util.py 2016-06-24 19:27:52 +0000
+++ curtin/util.py 2016-07-26 19:53:43 +0000
@@ -16,18 +16,30 @@
16# along with Curtin. If not, see <http://www.gnu.org/licenses/>.16# along with Curtin. If not, see <http://www.gnu.org/licenses/>.
1717
18import argparse18import argparse
19import collections
19import errno20import errno
20import glob21import glob
21import json22import json
22import os23import os
23import platform24import platform
25import re
24import shutil26import shutil
27import socket
25import subprocess28import subprocess
26import stat29import stat
27import sys30import sys
28import tempfile31import tempfile
29import time32import time
3033
34# avoid the dependency to python3-six as used in cloud-init
35try:
36 from urlparse import urlparse
37except ImportError:
38 # python3
39 # avoid triggering pylint, https://github.com/PyCQA/pylint/issues/769
40 # pylint:disable=import-error,no-name-in-module
41 from urllib.parse import urlparse
42
31from .log import LOG43from .log import LOG
3244
33_INSTALLED_HELPERS_PATH = '/usr/lib/curtin/helpers'45_INSTALLED_HELPERS_PATH = '/usr/lib/curtin/helpers'
@@ -35,6 +47,11 @@
3547
36_LSB_RELEASE = {}48_LSB_RELEASE = {}
3749
50_DNS_REDIRECT_IP = None
51
52# matcher used in template rendering functions
53BASIC_MATCHER = re.compile(r'\$\{([A-Za-z0-9_.]+)\}|\$([A-Za-z0-9_.]+)')
54
3855
39def _subp(args, data=None, rcs=None, env=None, capture=False, shell=False,56def _subp(args, data=None, rcs=None, env=None, capture=False, shell=False,
40 logstring=False, decode="replace"):57 logstring=False, decode="replace"):
@@ -486,6 +503,28 @@
486 return False503 return False
487504
488505
506def get_installed_packages(target=None):
507 cmd = []
508 if target is not None:
509 cmd = ['chroot', target]
510 cmd.extend(['dpkg-query', '--list'])
511
512 (out, _) = subp(cmd, capture=True)
513 if isinstance(out, bytes):
514 out = out.decode()
515
516 pkgs_inst = set()
517 for line in out.splitlines():
518 try:
519 (state, pkg, _) = line.split(None, 2)
520 except ValueError:
521 continue
522 if state.startswith("hi") or state.startswith("ii"):
523 pkgs_inst.add(re.sub(":.*", "", pkg))
524
525 return pkgs_inst
526
527
489def has_pkg_installed(pkg, target=None):528def has_pkg_installed(pkg, target=None):
490 chroot = []529 chroot = []
491 if target is not None:530 if target is not None:
@@ -846,14 +885,17 @@
846 return (isinstance(exc, IOError) and exc.errno == errno.ENOENT)885 return (isinstance(exc, IOError) and exc.errno == errno.ENOENT)
847886
848887
849def lsb_release():888def lsb_release(target=None):
850 fmap = {'Codename': 'codename', 'Description': 'description',889 fmap = {'Codename': 'codename', 'Description': 'description',
851 'Distributor ID': 'id', 'Release': 'release'}890 'Distributor ID': 'id', 'Release': 'release'}
891 chroot = []
892 if target is not None:
893 chroot = ['chroot', target]
852 global _LSB_RELEASE894 global _LSB_RELEASE
853 if not _LSB_RELEASE:895 if not _LSB_RELEASE:
854 data = {}896 data = {}
855 try:897 try:
856 out, err = subp(['lsb_release', '--all'], capture=True)898 out, err = subp(chroot + ['lsb_release', '--all'], capture=True)
857 for line in out.splitlines():899 for line in out.splitlines():
858 fname, tok, val = line.partition(":")900 fname, tok, val = line.partition(":")
859 if fname in fmap:901 if fname in fmap:
@@ -895,4 +937,104 @@
895 }937 }
896 return platform2arch.get(platform.machine(), platform.machine())938 return platform2arch.get(platform.machine(), platform.machine())
897939
940
941def basic_template_render(content, params):
942 """This does simple replacement of bash variable like templates.
943
944 It identifies patterns like ${a} or $a and can also identify patterns like
945 ${a.b} or $a.b which will look for a key 'b' in the dictionary rooted
946 by key 'a'.
947 """
948
949 def replacer(match):
950 """ replacer
951 replacer used in regex match to replace content
952 """
953 # Only 1 of the 2 groups will actually have a valid entry.
954 name = match.group(1)
955 if name is None:
956 name = match.group(2)
957 if name is None:
958 raise RuntimeError("Match encountered but no valid group present")
959 path = collections.deque(name.split("."))
960 selected_params = params
961 while len(path) > 1:
962 key = path.popleft()
963 if not isinstance(selected_params, dict):
964 raise TypeError("Can not traverse into"
965 " non-dictionary '%s' of type %s while"
966 " looking for subkey '%s'"
967 % (selected_params,
968 selected_params.__class__.__name__,
969 key))
970 selected_params = selected_params[key]
971 key = path.popleft()
972 if not isinstance(selected_params, dict):
973 raise TypeError("Can not extract key '%s' from non-dictionary"
974 " '%s' of type %s"
975 % (key, selected_params,
976 selected_params.__class__.__name__))
977 return str(selected_params[key])
978
979 return BASIC_MATCHER.sub(replacer, content)
980
981
982def render_string(content, params):
983 """ render_string
984 render a string following replacement rules as defined in
985 basic_template_render returning the string
986 """
987 if not params:
988 params = {}
989 return basic_template_render(content, params)
990
991
992def is_resolvable(name):
993 """determine if a url is resolvable, return a boolean
994 This also attempts to be resilent against dns redirection.
995
996 Note, that normal nsswitch resolution is used here. So in order
997 to avoid any utilization of 'search' entries in /etc/resolv.conf
998 we have to append '.'.
999
1000 The top level 'invalid' domain is invalid per RFC. And example.com
1001 should also not exist. The random entry will be resolved inside
1002 the search list.
1003 """
1004 global _DNS_REDIRECT_IP
1005 if _DNS_REDIRECT_IP is None:
1006 badips = set()
1007 badnames = ("does-not-exist.example.com.", "example.invalid.")
1008 badresults = {}
1009 for iname in badnames:
1010 try:
1011 result = socket.getaddrinfo(iname, None, 0, 0,
1012 socket.SOCK_STREAM,
1013 socket.AI_CANONNAME)
1014 badresults[iname] = []
1015 for (_, _, _, cname, sockaddr) in result:
1016 badresults[iname].append("%s: %s" % (cname, sockaddr[0]))
1017 badips.add(sockaddr[0])
1018 except (socket.gaierror, socket.error):
1019 pass
1020 _DNS_REDIRECT_IP = badips
1021 if badresults:
1022 LOG.debug("detected dns redirection: %s", badresults)
1023
1024 try:
1025 result = socket.getaddrinfo(name, None)
1026 # check first result's sockaddr field
1027 addr = result[0][4][0]
1028 if addr in _DNS_REDIRECT_IP:
1029 return False
1030 return True
1031 except (socket.gaierror, socket.error):
1032 return False
1033
1034
1035def is_resolvable_url(url):
1036 """determine if this url is resolvable (existing or ip)."""
1037 return is_resolvable(urlparse(url).hostname)
1038
1039
898# vi: ts=4 expandtab syntax=python1040# vi: ts=4 expandtab syntax=python
8991041
=== modified file 'doc/devel/README-vmtest.txt'
--- doc/devel/README-vmtest.txt 2016-01-08 17:37:01 +0000
+++ doc/devel/README-vmtest.txt 2016-07-26 19:53:43 +0000
@@ -90,13 +90,13 @@
90 The tests themselves don't actually have to run as root, but the90 The tests themselves don't actually have to run as root, but the
91 test setup does.91 test setup does.
92 * the 'tools' directory must be in your path.92 * the 'tools' directory must be in your path.
93 * test will set apt_proxy in the guests to the value of93 * test will set apt: { proxy } in the guests to the value of
94 'apt_proxy' environment variable. If that is not set it will 94 'apt_proxy' environment variable. If that is not set it will
95 look at the host's apt config and read 'Acquire::HTTP::Proxy'95 look at the host's apt config and read 'Acquire::HTTP::Proxy'
9696
97== Environment Variables ==97== Environment Variables ==
98Some environment variables affect the running of vmtest98Some environment variables affect the running of vmtest
99 * apt_proxy: 99 * apt_proxy:
100 test will set apt_proxy in the guests to the value of 'apt_proxy'.100 test will set apt_proxy in the guests to the value of 'apt_proxy'.
101 If that is not set it will look at the host's apt config and read101 If that is not set it will look at the host's apt config and read
102 'Acquire::HTTP::Proxy'102 'Acquire::HTTP::Proxy'
103103
=== added file 'doc/topics/apt_source.rst'
--- doc/topics/apt_source.rst 1970-01-01 00:00:00 +0000
+++ doc/topics/apt_source.rst 2016-07-26 19:53:43 +0000
@@ -0,0 +1,152 @@
1==========
2APT Source
3==========
4
5This part of curtin is meant to allow influencing the apt behaviour and configuration.
6
7By default - if no apt config is provided - it does nothing. That keeps behavior compatible on upgrades.
8
9The feature has a target argument which - by default - is used to modify the environment that curtin currently installs (@TARGET_MOUNT_POINT).
10
11Features
12--------
13
14* Add PGP keys to the APT trusted keyring
15
16 - add via short keyid
17
18 - add via long key fingerprint
19
20 - specify a custom keyserver to pull from
21
22 - add raw keys (which makes you independent of keyservers)
23
24* Influence global apt configuration
25
26 - adding ppa's
27
28 - replacing mirror, security mirror and release in sources.list
29
30 - able to provide a fully custom template for sources.list
31
32 - add arbitrary apt.conf settings
33
34
35Configuration
36-------------
37
38The general configuration of the apt feature is under an element called ``apt``.
39
40This can have various "global" subelements as listed in the examples below.
41These global configurations are valid throughput all of the apt feature.
42So for exmaple a global specification of ``primary`` for a mirror will apply to all rendered sources entries.
43
44Then there is a section ``sources`` which can hold a number of subelements itself.
45The key is the filename and will be prepended by /etc/apt/sources.list.d/ if it doesn't start with a ``/``.
46There are certain cases - where no content is written into a source.list file where the filename will be ignored - yet it can still be used as index for merging.
47
48The values inside the entries consist of the following optional entries::
49* ``source``: a sources.list entry (some variable replacements apply)
50
51* ``keyid``: providing a key to import via shortid or fingerprint
52
53* ``key``: providing a raw PGP key
54
55* ``keyserver``: specify an alternate keyserver to pull keys from that were specified by keyid
56
57* ``filename``: for compatibility with the older format (now the key to this dictionary is the filename). If specified this overwrites the filename given as key.
58
59The section "sources" is is a dictionary (unlike most block/net configs which are lists). This format allows merging between multiple input files than a list like::
60 sources:
61 s1: {'key': 'key1', 'source': 'source1'}
62
63 sources:
64 s2: {'key': 'key2'}
65 s1: {'filename': 'foo'}
66
67 This would be merged into
68 s1: {'key': 'key1', 'source': 'source1', filename: 'foo'}
69 s2: {'key': 'key2'}
70
71Here is just one of the most common examples that could be used to install with curtin in a closed environment (derived repository):
72
73What do we need for that:
74* insert the PGP key of the local repository to be trusted
75
76 - since you are locked down you can't pull from keyserver.ubuntu.com
77
78 - if you have an internal keyserver you could pull from there, but let us assume you don't even have that; so you have to provide the raw key
79
80 - in the example I'll use the key of the "Ubuntu CD Image Automatic Signing Key" which makes no sense as it is in the trusted keyring anyway, but it is a good example. (Also the key is shortened to stay readable)
81
82::
83
84 -----BEGIN PGP PUBLIC KEY BLOCK-----
85 Version: GnuPG v1
86 mQGiBEFEnz8RBAC7LstGsKD7McXZgd58oN68KquARLBl6rjA2vdhwl77KkPPOr3O
87 RwIbDAAKCRBAl26vQ30FtdxYAJsFjU+xbex7gevyGQ2/mhqidES4MwCggqQyo+w1
88 Twx6DKLF+3rF5nf1F3Q=
89 =PBAe
90 -----END PGP PUBLIC KEY BLOCK-----
91
92* replace the mirror from apt pulls repository data
93
94 - lets consider we have a local mirror at ``mymirror.local`` but otherwise following the usual paths
95
96 - make an example with a partial mirror that doesn't mirror the backports suite, so backports have to be disabled
97
98That would be specified as
99::
100
101 apt:
102 primary:
103 - arches [default]
104 uri: http://mymirror.local/ubuntu/
105 disable_suites: [backports]
106 sources:
107 localrepokey:
108 key: | # full key as block
109 -----BEGIN PGP PUBLIC KEY BLOCK-----
110 Version: GnuPG v1
111
112 mQGiBEFEnz8RBAC7LstGsKD7McXZgd58oN68KquARLBl6rjA2vdhwl77KkPPOr3O
113 RwIbDAAKCRBAl26vQ30FtdxYAJsFjU+xbex7gevyGQ2/mhqidES4MwCggqQyo+w1
114 Twx6DKLF+3rF5nf1F3Q=
115 =PBAe
116 -----END PGP PUBLIC KEY BLOCK-----
117
118Please also read the section ``Dependencies`` below to avoid loosing some of the configuration content on first boot.
119
120The file examples/apt-source.yaml holds various further examples that can be configured with this feature.
121
122Common snippets
123---------------
124This is a collection of additional ideas people can use the feature for customizing their to-be-installed system.
125
126* enable proposed on installing
127 apt:
128 sources:
129 proposed.list: deb $MIRROR $RELEASE-proposed main restricted universe multiverse
130
131* Make debug symbols available
132 apt:
133  sources:
134   ddebs.list: |
135   deb http://ddebs.ubuntu.com $RELEASE main restricted universe multiverse
136   deb http://ddebs.ubuntu.com $RELEASE-updates main restricted universe multiverse
137  deb http://ddebs.ubuntu.com $RELEASE-security main restricted universe multiverse
138 deb http://ddebs.ubuntu.com $RELEASE-proposed main restricted universe multiverse
139
140Timing
141------
142The feature is implemented at the stage of curthooks_commands, after which runs just after curtin has extracted the image to the target.
143It can be ran as standalong command "curtin -v --config <yourconfigfile> apt-config".
144
145This will pick up the target from the environment variable that is set by curtin, if you want to use it to a different target or outside of usual curtin handling you can add ``--target <path>`` to it to overwrite the target path.
146This target should have at least a minimal system with apt and dpkg installed for the functionality to work.
147
148
149Dependencies
150------------
151Cloud-init might need to resolve dependencies and install packages in the ephemeral environment to run curtin.
152Therefore it is recommended to not only configure curtin for the target, but also the install environment with proper apt configuration via cloud-init.
0153
=== added file 'examples/apt-source.yaml'
--- examples/apt-source.yaml 1970-01-01 00:00:00 +0000
+++ examples/apt-source.yaml 2016-07-26 19:53:43 +0000
@@ -0,0 +1,239 @@
1# YAML example of an apt config.
2apt:
3 sources:
4 # This is a dictionary (unlike most block/net which are lists)
5 # The key is the filename and will be prepended by /etc/apt/sources.list.d/
6 # if it doesn't start with a '/'.
7 # There are certain cases - where no content is written into a source.list
8 # file where the filename will be ignored - yet it can still be used as
9 # index for merging.
10 # The values inside the entries consost of the following optional entries:
11 # 'source': a sources.list entry (some variable replacements apply)
12 # 'keyid': providing a key to import via shortid or fingerprint
13 # 'key': providing a raw PGP key
14 # 'keyserver': specify an alternate keyserver to pull keys from that
15 # were specified by keyid
16 # 'filename': for compatibility with the older format (now the key to
17 # this dictionary is the filename). If specified this
18 # overwrites the filename given as key.
19
20 # The new format allows merging between multiple input files than a list
21 # like:
22 # cloud-config1
23 # sources:
24 # s1: {'key': 'key1', 'source': 'source1'}
25 # cloud-config2
26 # sources:
27 # s2: {'key': 'key2'}
28 # s1: {'filename': 'foo'}
29 # This would be merged to
30 # sources:
31 # s1:
32 # filename: foo
33 # key: key1
34 # source: source1
35 # s2:
36 # key: key2
37
38 curtin-dev-ppa.list:
39 source: "deb http://ppa.launchpad.net/curtin-dev/test-archive/ubuntu xenial main"
40 keyid: F430BBA5 # GPG key ID published on a key server
41 # adding a source.list line, importing a gpg key for a given key id and
42 # storing the deb entry in /etc/apt/sources.list.d/curtin-dev-ppa.list
43
44 # PPA shortcut:
45 # * Setup correct apt sources.list line
46 # * Import the signing key from LP
47 #
48 # See https://help.launchpad.net/Packaging/PPA for more information
49 # this requires 'add-apt-repository'
50 # due to that the filename key is ignored in this case
51 ignored1:
52 source: "ppa:curtin-dev/test-archive" # Quote the string
53
54 # additional custom deb source config:
55 # * Creates a file in /etc/apt/sources.list.d/ for the sources list entry
56 # * [optional] Import the apt signing key from the keyserver
57 # * Defaults:
58 # + keyserver: keyserver.ubuntu.com
59 #
60 # See sources.list man page for more information about the format
61 my-repo1.list:
62 source: deb http://archive.ubuntu.com/ubuntu xenial-backports main universe multiverse restricted
63
64 # sources can use $MIRROR and $RELEASE and they will be replaced
65 # with the default or specified mirror and the running release.
66 # The entry below would be possibly turned into:
67 # source: deb http://archive.ubuntu.com/ubuntu xenial multiverse
68 # see below at apt*mirror for more
69 my-repo2.list:
70 source: deb $MIRROR $RELEASE multiverse
71
72 # this would have the same end effect as 'ppa:curtin-dev/test-archive'
73 my-repo3.list:
74 source: "deb http://ppa.launchpad.net/curtin-dev/test-archive/ubuntu xenial main"
75 keyid: F430BBA5 # GPG key ID published on a key server
76 filename: curtin-dev-ppa.list
77
78 # this would only import the key without adding a ppa or other source spec
79 # since this doesn't generate a source.list file the filename key is ignored
80 ignored2:
81 keyid: F430BBA5 # GPG key ID published on a key server
82
83 # In general keyid's can also be specified via their long fingerprints
84 # since this doesn't generate a source.list file the filename key is ignored
85 ignored3:
86 keyid: B59D 5F15 97A5 04B7 E230 6DCA 0620 BBCF 0368 3F77
87
88 # Custom apt repository:
89 # * The apt signing key can also be specified
90 # by providing a pgp public key block
91 # * Providing the PGP key here is the most robust method for
92 # specifying a key, as it removes dependency on a remote key server
93 my-repo4.list:
94 source: deb http://ppa.launchpad.net/curtin-dev/test-archive/ubuntu xenial main
95 key: | # The value needs to start with -----BEGIN PGP PUBLIC KEY BLOCK-----
96 -----BEGIN PGP PUBLIC KEY BLOCK-----
97 Version: SKS 1.0.10
98
99 mI0ESpA3UQEEALdZKVIMq0j6qWAXAyxSlF63SvPVIgxHPb9Nk0DZUixn+akqytxG4zKCONz6
100 qLjoBBfHnynyVLfT4ihg9an1PqxRnTO+JKQxl8NgKGz6Pon569GtAOdWNKw15XKinJTDLjnj
101 9y96ljJqRcpV9t/WsIcdJPcKFR5voHTEoABE2aEXABEBAAG0GUxhdW5jaHBhZCBQUEEgZm9y
102 IEFsZXN0aWOItgQTAQIAIAUCSpA3UQIbAwYLCQgHAwIEFQIIAwQWAgMBAh4BAheAAAoJEA7H
103 5Qi+CcVxWZ8D/1MyYvfj3FJPZUm2Yo1zZsQ657vHI9+pPouqflWOayRR9jbiyUFIn0VdQBrP
104 t0FwvnOFArUovUWoKAEdqR8hPy3M3APUZjl5K4cMZR/xaMQeQRZ5CHpS4DBKURKAHC0ltS5o
105 uBJKQOZm5iltJp15cgyIkBkGe8Mx18VFyVglAZey
106 =Y2oI
107 -----END PGP PUBLIC KEY BLOCK-----
108
109 # Custom gpg key:
110 # * As with keyid, a key may also be specified without a related source.
111 # * all other facts mentioned above still apply
112 # since this doesn't generate a source.list file the filename key is ignored
113 ignored4:
114 key: | # The value needs to start with -----BEGIN PGP PUBLIC KEY BLOCK-----
115 -----BEGIN PGP PUBLIC KEY BLOCK-----
116 Version: SKS 1.0.10
117
118 mI0ESpA3UQEEALdZKVIMq0j6qWAXAyxSlF63SvPVIgxHPb9Nk0DZUixn+akqytxG4zKCONz6
119 qLjoBBfHnynyVLfT4ihg9an1PqxRnTO+JKQxl8NgKGz6Pon569GtAOdWNKw15XKinJTDLjnj
120 9y96ljJqRcpV9t/WsIcdJPcKFR5voHTEoABE2aEXABEBAAG0GUxhdW5jaHBhZCBQUEEgZm9y
121 IEFsZXN0aWOItgQTAQIAIAUCSpA3UQIbAwYLCQgHAwIEFQIIAwQWAgMBAh4BAheAAAoJEA7H
122 5Qi+CcVxWZ8D/1MyYvfj3FJPZUm2Yo1zZsQ657vHI9+pPouqflWOayRR9jbiyUFIn0VdQBrP
123 t0FwvnOFArUovUWoKAEdqR8hPy3M3APUZjl5K4cMZR/xaMQeQRZ5CHpS4DBKURKAHC0ltS5o
124 uBJKQOZm5iltJp15cgyIkBkGe8Mx18VFyVglAZey
125 =Y2oI
126 -----END PGP PUBLIC KEY BLOCK-----
127 # end of apt dictionary
128
129
130 #
131 # All of the following are auxillary configurations to the apt handling
132 #
133
134 # Preserve existing /etc/apt/sources.list
135 # Default: True - do not overwrite sources_list. If staying at true
136 # then any "mirrors" configuration above will have no effect
137 # Set to False to affect sources.list with the configuration. Without only
138 # extra source specifications will be written into /etc/apt/sources.list.d/.
139 preserve_sources_list: false
140
141 # disable_suites by default it is an empty list, so nothing is removed.
142 # If given, those suites are removed from source.list after all other
143 # modifications have been made.
144 # suites are even disabled if no other modification was made,
145 # but not if is preserve_sources_list is active.
146 # There is a special alias “$RELEASE” as in the sources that will be replace
147 # by the matching release.
148 # To ease configuration and improve readability the following common ubuntu
149 # suites will be automatically mapped to their full definition.
150 # updates => $RELEASE-updates
151 # backports => $RELEASE-backports
152 # security => $RELEASE-security
153 # proposed => $RELEASE-proposed
154 # release => $RELEASE
155 # Note: Lines don’t get deleted, but disabled by being converted to a comment.
156 # The following example disables all defaults except $RELEASE-security. On top
157 # it disables a custom suite called "mysuite"
158 disable_suites: [$RELEASE-updates, backports, $RELEASE, mysuite]
159
160 # a custom (e.g. localized) mirror that will be set in sources.list and
161 # deb / deb-src lines
162 # one can set primary and security mirror to different uri's
163 # the child elements to the keys primary and secondary are equivalent
164 primary:
165 # arches is list of architectures the following config applies to
166 # the special keyword "default" applies to any architecture not explicitly
167 # listed.
168 - arches: [amd64, i386, default]
169 # uri is just defining the target as-is
170 uri: http://us.archive.ubuntu.com/ubuntu
171 #
172 # via search one can define lists that are
173 # tried one by one. The first with a working DNS resolution (or if it is an
174 # IP) will be picked. That way one can keep one configuration for multiple
175 # subenvironments that select the working one.
176 search:
177 - http://cool.but-sometimes-unreachable.com/ubuntu
178 - http://us.archive.ubuntu.com/ubuntu
179 #
180 # This will search for <distro>-mirror locally and at the fqdn of the system.
181 # If resolving that will be used as archive. That allows configuring local
182 # mirrors via providing certin DNS names via a local nameserver.
183 # These can even be set to resolve to the public names of defaults like
184 # archive.ubuntu.com as long as they are reachable from the target.
185 # For security it will search <distro>-security-mirror
186 search_dns: True
187 #
188 # If multiple of a category are given
189 # 1. uri
190 # 2. search
191 # 3. search_dns
192 # are given the first defining a valid mirror wins (in the
193 # order as defined here, not the one it is listed in the config).
194 - arches: [s390x, arm64]
195 # as above, allowing to have one config for different per arch mirrors
196 # security is optional, if not defined it is set to the value of primary
197 security:
198 uri: http://security.ubuntu.com/ubuntu
199 [...]
200 #
201 # if no mirrors are specified at all, or all lookups fail it will use:
202 # primary: http://archive.ubuntu.com/ubuntu
203 # security: http://security.ubuntu.com/ubuntu
204
205 # Provide a custom template for rendering sources.list
206 # without one provided curtin will try to modify the sources.list it find
207 # in the target at /etc/apt/.
208 # Within these source.list templates you can use the following replacement
209 # variables (all have sane ubuntu defaults, see above for details):
210 # $RELEASE, $MIRROR, $PRIMARY, $SECURITY
211 sources_list: | # written by curtin custom template
212 deb $MIRROR $RELEASE main restricted
213 deb-src $MIRROR $RELEASE main restricted
214 deb $PRIMARY $RELEASE universe restricted
215 deb $SECURITY $RELEASE-security multiverse
216
217 # any apt config string that will be made available to apt
218 # see the APT.CONF(5) man page for details what can be specified
219 conf: | # APT config
220 APT {
221 Get {
222 Assume-Yes "true";
223 Fix-Broken "true";
224 };
225 };
226
227 # proxies are the most common conf option that is set there is a
228 # shortcut via apt*proxy. Those get automatically translated into the
229 # correct Acquire::*::Proxy statements.
230 proxy: http://[[user][:pass]@]host[:port]/
231 http_proxy: http://[[user][:pass]@]host[:port]/
232 ftp_proxy: ftp://[[user][:pass]@]host[:port]/
233 https_proxy: https://[[user][:pass]@]host[:port]/
234 # note: proxy actually being a short synonym to http_proxy
235
236 # 'source' entries in apt-sources that match this python regex
237 # expression will be passed to add-apt-repository
238 # The following example is also the builtin default if nothing is specified
239 add_apt_repo_match: '^[\w-]+:\w'
0240
=== added file 'examples/tests/apt_config_command.yaml'
--- examples/tests/apt_config_command.yaml 1970-01-01 00:00:00 +0000
+++ examples/tests/apt_config_command.yaml 2016-07-26 19:53:43 +0000
@@ -0,0 +1,85 @@
1# This pushes curtin through a automatic installation
2# where no storage configuration is necessary.
3# exercising the standalong curtin apt-config command
4-placeholder_simple_install: unused
5bucket:
6 - &run_with_stdin |
7 #!/bin/sh
8 input="$1"
9 shift
10 printf "%s\n" "$input" | "$@"
11
12 - &run_apt_config_file |
13 #!/bin/sh
14 # take the first argument, write it to a tmp file and execute
15 # curtin apt --config=<tmpfile> "$@"
16 set -e
17 config="$1"
18 shift
19 TEMP_D=$(mktemp -d "${TMPDIR:-/tmp}/${0##*/}.XXXXXX")
20 trap cleanup EXIT
21 cleanup() { [ -z "${TEMP_D}" || rm -Rf "${TEMP_D}"; }
22 cfg_file="${TEMP_D}/curtin-apt.conf"
23 printf "%s\n" "$config" > "${cfg_file}"
24 curtin apt-config "--config=${cfg_file}" "$@"
25
26 - &apt_config_ppa |
27 # this is just a large string
28 apt:
29 sources:
30 ignored:
31 source: "ppa:curtin-dev/test-archive"
32 curtin-test1.list:
33 source: "deb $MIRROR $RELEASE-proposed main"
34
35 - &apt_config_source |
36 apt:
37 preserve_sources_list: false
38 sources:
39 ignored:
40 source: "ppa:curtin-dev/test-archive"
41 # apt-add-repositroy adds the key anyway, but lets pass that code
42 # path of adding once more in this scope
43 key: |
44 -----BEGIN PGP PUBLIC KEY BLOCK-----
45 Version: GnuPG v1
46
47 mQINBFazYtEBEADXrW53tDOvwcnHwchLapTKK89+wBWR2qQKXx5Mymtjkrb688Fs
48 ciXcCsvClnNGJ9bEhrJTucyb7WF0KcDVQcvOd0C4HOSEAc0DANBu1Mdp/tmCWuiW
49 1TbbhomyHAcHNdbuSZeMDh5xi9M3DYPVq72PwYwjrE4lotVxHeX5nYEH304U+5nJ
50 tBNpVon91k3ItymQ6Jii+9gVoQ7ujiH1/Gw4/J/1/5zQ3C1mOjq68vLunz5iw1Kn
51 7TMVyID6qwq2UFEgudpseLfFZcb/p7KgI0m3S/OViwzSc44m63ggTPMmbeHW51xA
52 1rpUChSU+cm0cJ4tNtAcYHRYRltWAo/3J1OzB6Ut5P7vIC5r+QcCyyMbku9NjYaw
53 dWX4DDKqW3is3qJ/7EeOKPL4N8wuKwuWUC7s2wqsIZL8EmsvR+ZOnTJ3bHZFvsLg
54 p/OKqmhxMGYXiXOWDOEJ+vwboPxrvhD90JZl8weNGPnpla+EkxRDBSpEb31Vgt5X
55 AIoxE7XxwfuXS3MGMA7fSqkGPGHfSLYQFFk+CAIeTUV+ypKW94hIxXKgqRxa7dxz
56 Ymqs+wgIGaWJCnx7z1Kpd3HD9iTAYjyWyhlQ/Tjt43kwUBdALhTL0vYUTGQyTgKt
57 tAriVf5bqHb6Hj5PS5YZQ/+YoCUI2OTrAWWNyH9rIEZGsFc30oJFPHj3fQARAQAB
58 tCNMYXVuY2hwYWQgUFBBIGZvciBjdXJ0aW4gZGV2ZWxvcGVyc4kCOAQTAQIAIgUC
59 VrNi0QIbAwYLCQgHAwIGFQgCCQoLBBYCAwECHgECF4AACgkQVf58jAFlAT4TGg//
60 SV7vWmkJqr5TSlT9JqCBfmtFjgudxTGG8XM2zwnta+m/3YVOMo0ZjyGL4fUKjCmN
61 eh6eYihwpRtfdawziaEOydDxNfdjwscV4Qcy7FjHX+DQnNzQyzK+WgWRJwNWloCw
62 skg2tF+EDRajalTRjHJAn+5zAilXVn71T/hhOCxkF0PBiH9s/e7pW/KcgBEC1MYV
63 Fs0fLST8SYhsIxttVRWuRkJDrtEY1zeVhkvk+PN6UuCY6/gyRSQ1rhhBF3ePqiba
64 CmLiUjnJMEm1OJOkuD33IMNPKQi99TZhr8y3AGCcrmAQtJsYLvVDPcsOsjGQHXP4
65 2qQXK+jE/AAUycCQ6tgrAqCcUNQiClP8xUPkZOiDNvVMiPvIj/s79ShkoRaWLMb7
66 n9jyDOhs3L7dtmKQwHWq9qJ56fzx1L0/jxSanzm+ZJ/Q7t6E/GFxY1RsAk7xtI1C
67 SzSmrGKmtlbWlOyqqQb6zhULIJpaXvh/GaYyo0xI3rA+QvPDt/fgUJEBiSidwabW
68 Q8JU9iI5HXQxbVq1gSdy/z31fue5JuZSqjnjCjgho/UrXa4i1RPtqsY3FoTk7Hmo
69 C1z2cJc8HQI8JnEX/4qJXvPMRM2JsMD9DqvgsUJG5M9Qchy8cymYY+xeiBVYzJI+
70 WHCq6LHqnVxYZ+RM858lSsD6wetN44vguIjL3qJJ+wU=
71 curtin-test1.list:
72 source: "deb $MIRROR $RELEASE-proposed main"
73
74# into ephemeral environment
75early_commands:
76 00_add_archive: [sh, -c, *run_with_stdin, "curtin-apt",
77 *apt_config_ppa, curtin, apt-config, --config=-, --target=/]
78 # tests itself by installing a packet only available in that ppa
79 00_install_package: [apt-get, install, --assume-yes, smello]
80
81# into target environment
82late_commands:
83 00_add_archive: [sh, -c, *run_apt_config_file, "curtin-apt-file",
84 *apt_config_source]
85 00_install_package: [curtin, in-target, --, apt-get, install, --assume-yes, smello]
086
=== added file 'examples/tests/apt_source_custom.yaml'
--- examples/tests/apt_source_custom.yaml 1970-01-01 00:00:00 +0000
+++ examples/tests/apt_source_custom.yaml 2016-07-26 19:53:43 +0000
@@ -0,0 +1,97 @@
1showtrace: true
2apt:
3 preserve_sources_list: false
4 primary:
5 - arches: [default]
6 uri: http://us.archive.ubuntu.com/ubuntu
7 security:
8 - arches: [default]
9 uri: http://security.ubuntu.com/ubuntu
10 sources_list: | # written by curtin custom template
11 deb $MIRROR $RELEASE main restricted
12 deb-src $MIRROR $RELEASE main restricted
13 deb $PRIMARY $RELEASE universe restricted
14 deb $SECURITY $RELEASE-security multiverse
15 # nice line to check in test
16 conf: | # APT config
17 ACQUIRE {
18 Retries "3";
19 };
20 sources:
21 curtin-dev-ppa.list:
22 source: "deb http://ppa.launchpad.net/curtin-dev/test-archive/ubuntu xenial main"
23 keyid: F430BBA5
24 ignored1:
25 source: "ppa:curtin-dev/test-archive"
26 my-repo2.list:
27 source: deb $MIRROR $RELEASE multiverse
28 ignored3:
29 keyid: 0E72 9061 0D2F 6DC4 D65E A921 9A31 4EC5 F470 A0AC
30 my-repo4.list:
31 source: deb http://ppa.launchpad.net/curtin-dev/test-archive/ubuntu xenial main
32 key: |
33 -----BEGIN PGP PUBLIC KEY BLOCK-----
34 Version: GnuPG v1
35
36 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
95 7oh88hTkC1w=
96 =UNSw
97 -----END PGP PUBLIC KEY BLOCK-----
098
=== added file 'examples/tests/apt_source_modify.yaml'
--- examples/tests/apt_source_modify.yaml 1970-01-01 00:00:00 +0000
+++ examples/tests/apt_source_modify.yaml 2016-07-26 19:53:43 +0000
@@ -0,0 +1,92 @@
1showtrace: true
2apt:
3 preserve_sources_list: false
4 primary:
5 - arches: [default]
6 uri: http://us.archive.ubuntu.com/ubuntu
7 security:
8 - arches: [default]
9 uri: http://security.ubuntu.com/ubuntu
10 conf: | # APT config
11 ACQUIRE {
12 Retries "3";
13 };
14 sources:
15 curtin-dev-ppa.list:
16 source: "deb http://ppa.launchpad.net/curtin-dev/test-archive/ubuntu xenial main"
17 keyid: F430BBA5
18 ignored1:
19 source: "ppa:curtin-dev/test-archive"
20 # intentionally dropped the .list here, has to be added by the code
21 my-repo2:
22 source: deb $MIRROR $RELEASE multiverse
23 ignored3:
24 keyid: 0E72 9061 0D2F 6DC4 D65E A921 9A31 4EC5 F470 A0AC
25 my-repo4.list:
26 source: deb http://ppa.launchpad.net/curtin-dev/test-archive/ubuntu xenial main
27 key: |
28 -----BEGIN PGP PUBLIC KEY BLOCK-----
29 Version: GnuPG v1
30
31 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
90 7oh88hTkC1w=
91 =UNSw
92 -----END PGP PUBLIC KEY BLOCK-----
093
=== added file 'examples/tests/apt_source_modify_arches.yaml'
--- examples/tests/apt_source_modify_arches.yaml 1970-01-01 00:00:00 +0000
+++ examples/tests/apt_source_modify_arches.yaml 2016-07-26 19:53:43 +0000
@@ -0,0 +1,102 @@
1showtrace: true
2apt:
3 preserve_sources_list: false
4 primary:
5 # we don't know on which arch this will run, so we can't put the "right"
6 # config in an arch, but we can provide various confusing alternatives
7 # and orders and it has to pick default out of them
8 - arches: [x86_2048, x86_4096, x86_8192, amd18.5, "foobar"]
9 uri: http://notthis.com/ubuntu
10 - arches: ["*"]
11 uri: http://notthis.com/ubuntu
12 - arches: [default]
13 uri: http://us.archive.ubuntu.com/ubuntu
14 - arches: []
15 uri: http://notthis.com/ubuntu
16 security:
17 - arches: [default]
18 uri: http://security.ubuntu.com/ubuntu
19 - arches: ["supersecurearchthatdoesnexist"]
20 uri: http://notthat.com/ubuntu
21 conf: | # APT config
22 ACQUIRE {
23 Retries "3";
24 };
25 sources:
26 curtin-dev-ppa.list:
27 source: "deb http://ppa.launchpad.net/curtin-dev/test-archive/ubuntu xenial main"
28 keyid: F430BBA5
29 ignored1:
30 source: "ppa:curtin-dev/test-archive"
31 my-repo2.list:
32 source: deb $MIRROR $RELEASE multiverse
33 ignored3:
34 keyid: 0E72 9061 0D2F 6DC4 D65E A921 9A31 4EC5 F470 A0AC
35 my-repo4.list:
36 source: deb http://ppa.launchpad.net/curtin-dev/test-archive/ubuntu xenial main
37 key: |
38 -----BEGIN PGP PUBLIC KEY BLOCK-----
39 Version: GnuPG v1
40
41 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
100 7oh88hTkC1w=
101 =UNSw
102 -----END PGP PUBLIC KEY BLOCK-----
0103
=== added file 'examples/tests/apt_source_modify_disable_suite.yaml'
--- examples/tests/apt_source_modify_disable_suite.yaml 1970-01-01 00:00:00 +0000
+++ examples/tests/apt_source_modify_disable_suite.yaml 2016-07-26 19:53:43 +0000
@@ -0,0 +1,92 @@
1showtrace: true
2apt:
3 preserve_sources_list: false
4 primary:
5 - arches: [default]
6 uri: http://us.archive.ubuntu.com/ubuntu
7 security:
8 - arches: [default]
9 uri: http://security.ubuntu.com/ubuntu
10 disable_suites: [$RELEASE-updates]
11 conf: | # APT config
12 ACQUIRE {
13 Retries "3";
14 };
15 sources:
16 curtin-dev-ppa.list:
17 source: "deb http://ppa.launchpad.net/curtin-dev/test-archive/ubuntu xenial main"
18 keyid: F430BBA5
19 ignored1:
20 source: "ppa:curtin-dev/test-archive"
21 my-repo2.list:
22 source: deb $MIRROR $RELEASE multiverse
23 ignored3:
24 keyid: 0E72 9061 0D2F 6DC4 D65E A921 9A31 4EC5 F470 A0AC
25 my-repo4.list:
26 source: deb http://ppa.launchpad.net/curtin-dev/test-archive/ubuntu xenial main
27 key: |
28 -----BEGIN PGP PUBLIC KEY BLOCK-----
29 Version: GnuPG v1
30
31 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
90 7oh88hTkC1w=
91 =UNSw
92 -----END PGP PUBLIC KEY BLOCK-----
093
=== added file 'examples/tests/apt_source_preserve.yaml'
--- examples/tests/apt_source_preserve.yaml 1970-01-01 00:00:00 +0000
+++ examples/tests/apt_source_preserve.yaml 2016-07-26 19:53:43 +0000
@@ -0,0 +1,98 @@
1showtrace: true
2apt:
3 # this is like the other apt_source test but with preserve true
4 # this is the default now preserve_sources_list: true
5 primary:
6 - arches: [default]
7 uri: http://us.archive.ubuntu.com/ubuntu
8 security:
9 - arches: [default]
10 uri: http://security.ubuntu.com/ubuntu
11 sources_list: | # written by curtin custom template
12 deb $MIRROR $RELEASE main restricted
13 deb-src $MIRROR $RELEASE main restricted
14 deb $PRIMARY $RELEASE universe restricted
15 deb $SECURITY $RELEASE-security multiverse
16 # nice line to check in test
17 conf: | # APT config
18 ACQUIRE {
19 Retries "3";
20 };
21 sources:
22 curtin-dev-ppa.list:
23 source: "deb http://ppa.launchpad.net/curtin-dev/test-archive/ubuntu xenial main"
24 keyid: F430BBA5
25 ignored1:
26 source: "ppa:curtin-dev/test-archive"
27 my-repo2.list:
28 source: deb $MIRROR $RELEASE multiverse
29 ignored3:
30 keyid: 0E72 9061 0D2F 6DC4 D65E A921 9A31 4EC5 F470 A0AC
31 my-repo4.list:
32 source: deb http://ppa.launchpad.net/curtin-dev/test-archive/ubuntu xenial main
33 key: |
34 -----BEGIN PGP PUBLIC KEY BLOCK-----
35 Version: GnuPG v1
36
37 mQINBFXJ3NcBEAC85PMdaKdItkdjCT1vRJrdwNqj4lN5mu6z4dDVfeZlmozRDBGb
38 ENSOWCiYz3meANO7bKthQQCqAETSBV72rrDCqFZUpXeyG3zCN98Z/UdJ8zpQD9uw
39 mq2CaAqWMk6ty+PkHQ4gtIc390lGfRbHNoZ5HaWJNVOK7FCB2hBmnTZW7AViYiYa
40 YswOjYxaCkwQ/DsMOPD7S5OjwbLucs2YGjkBm7YF1nnXNzyt+BwieKQW/sQ2+ga1
41 mkgLW1BTQN3+JreBpeHy/yrRdK4dOZZUar4WPZitZzOW2eNpaaf6hKNA14LB/96a
42 tEguK8VazoqSQGvNV/R3PjIYmurVP3/Z9bEVgOKhMCflgwKCYgx+tBUypN3zFWv9
43 pgVq3iHx1MFCvoP9FsNB7I6jzOxlQP4z25BzR3ympx/QexkFw5CBFXhdrU+qNVBl
44 SSnz69aLEjCRXqBOnQEr0irs/e/35+yLJdEuw89vSwWwrzbV5r1Y7uxinEGWSydT
45 qddj97uKOWeMmnp20Be4+nhDDW/BMiTFI4Y3bYeDTrftbWMaSEmtSTw5HHxtAFtg
46 X9Hyx0Q3eN1w3gRZgIdm0xYTe7bNTofFRdfXzB/9wtNIcaW10+IlODShFHPCnh+d
47 i56a8LCdZcXiiLfCIhEcnqmM37BVvhjIQKSyOU1eMEgX148aVEz36OVuMwARAQAB
48 tCdDaHJpc3RpYW4gRWhyaGFyZHQgPGNwYWVsemVyQGdtYWlsLmNvbT6JAjgEEwEC
49 ACIFAlXJ3NcCGwMGCwkIBwMCBhUIAgkKCwQWAgMBAh4BAheAAAoJELo+KTOCgLJC
50 BugP/ir0ES3wCzvHMnkz2UlXt9FR4KqY0L9uFmwu9VYpmfAploEVIOi2HcuxpcRp
51 hgoQlUtkz3lRhUeZzCxuB1ljM2JKTJiezP1tFTTGCbVYhPyA0LmUiHDWylG7FzPb
52 TX96HY/G0jf+m4CfR8q3HNHjeDi4VeA2ppBxdHcVE5I7HihDgRPJd+CvCa3nYdAb
53 nXDKlQZz5aZc7AgrRVamr4mshkzWuwNNCwOt3AIgHDkU/HzA5xlXfwHxOoP6scWH
54 /T7vFsd/vOikBphGseWPgKm6w1zyQ5Dk/wjRL8UeSJZW+Rh4PuBMbxg01lAZpPTq
55 tu/bePeNty3g5bhwO6oHMpWhprn3dO37R680qo6UnBPzICeuBUnSYgpPnsQC9maz
56 FEjiBtMsXSanU5vww7TpxY1JHjk5KFcmKx4sBeablznsm+GuVaDFN8R4eDjrM14r
57 SOzA9cV0bSQr4dMqA9fZFSx6qLTacIeMfptybW3zaDX/pJOeBBWRAtoAfZIFbBnu
58 /ZxDDgiQtZzpVK4UkYk5rjjtV/CPVXx64AnTHi35YfUn14KkE+k3odHdvPfBiv9+
59 NxfkTuV/koOgpD3+lTIYXyVHS9gwvhfRD/YfdrnVGl7bRZe68j7bfWDuQuSqIhSA
60 jpeJslJCawnqv6fVB6buj6jjcgHIxqCVn99chaPFSblEIPfXtDVDaHJpc3RpYW4g
61 RWhyaGFyZHQgPGNocmlzdGlhbi5laHJoYXJkdEBjYW5vbmljYWwuY29tPokCOAQT
62 AQIAIgUCVsbUOgIbAwYLCQgHAwIGFQgCCQoLBBYCAwECHgECF4AACgkQuj4pM4KA
63 skJNPg/7BF/iXHHdSBigWKXCCvQz58uInoc/R4beIegxRCMq7wkYEey4B7Fd35zY
64 zv9CBOTV3hZePMCg9jxl4ki2kSsrZSCIEJw4L/aXDtJtx3HT18uTW0QKoU3nK/ro
65 OtthVqBqmiSEi40UUU+5MGrUjwLSm+PjaaSapjK/lddf0KbXBB78/BtR/XT0gxWM
66 +o68Oei9Nj1S3h6UndJwNAQ1xaDWmU2T7CRJet3F+cXZd3aDuS2axOTSTZbraSq7
67 zdl1xUiKtzXZIp8X1ewne+dzkewZuWj7DOwOBEFK26UhxCjKd5mUr7jpWQ4ampFX
68 6xfd/MK8SJFY+iHOBKyzq9po40tE23dqWuaHB+T3MxOgQ9JHCo9x22XNvEuKZW/V
69 4WaoGHVkR+jtWNC8Qv/xCMHL3CEvAklKJR68WDhozwUYTgNt5vCoJOviMlbhDSwf
70 0zVXpQwMR//4c0QSA0+BPpIEPDnx5vTIHBVXHy4bBBHU2Vi87QIDS0AtiBpNcspN
71 6AG0ktuldkE/pqfSTJ2A9HpHZyU+8boagRS5/z102Pjtmf/mzUkcHmfRb9o0DE15
72 X5fqpA3lYyx9eHIAgH4eaB1+G20Ez/EY5hr8IMS2nNBSem491UW6DXDYRu6eBLrR
73 sRmtrJ6DlTZFRFlqVZ47bce/SbeM/xljvRkBxWG6RtDRsTyNVI65Ag0EVcnc1wEQ
74 ANzk9W058tSHqf05UEtJGrN0K8DLriCvPd7QdFA8yVIZM3WD+m0AMBGXjd8BT5c2
75 lt0GmhB8klonHZvPiVLTRTLcSsc3NBopr1HL1bWsgOczwWiXSrc62oGAHUOQT/bv
76 vS6KIkZgez+qtCo/DCOGJrADaoJBiBCLSsZgowpzazZZDPUF7rAsfcryVCFvftK0
77 wAe1OdvUG77NHrMrE1oX3zh82hTqR5azBre6Y81lNwxxug/Xl/RHjNhEOYohcsLS
78 /xl0m2X831fHzcGGpoISRgrfel+M4RoC7KsLrwVhrF8koCD/ZQlevfLpuRl5LNpO
79 s1ZtEi8ZvLliih+H+BOlBD0zUc3zZrrks/NCpm1eZba0Z6L48r4TIHW08SGlHx7o
80 SrXgkq3mtoM8C4uDiLwjav5KxiF7n68s/9LF82aAr7YjNXd+xYZNjsmmFlYj9CGI
81 lL4jVt4v4EtTONa6pbtCNv5ezOLDZ6BBcQ36xdkrWzdpjQjL2mnh3sqIAGIPu7tH
82 N8euQ5L1zIvIjVqYlR1eJssp96QYPWYxF7TosfML4BUhCP631IWfuD9X/K2LzDmv
83 B2gVZo9fbhSC+P7GYVG+tV4VLAMbspAxRXXL69+j98aeV5g59f8OFQPbGpKE/SAY
84 eIXtq8DD+PYUXXq3VUI2brVLv42LBVdSJpKNKG3decIBABEBAAGJAh8EGAECAAkF
85 AlXJ3NcCGwwACgkQuj4pM4KAskKzeg/9FxXJLV3eWwY4nn2VhwYTHnHtSUpi8usk
86 RzIa3Mcj6OEVjU2LZaT3UQF8h6dLM9y+CemcwyjMqm1RQ5+ogfrItby1AaBXwCvm
87 XCUGw2zFOAnyzSHHoDFj27sllFxDmfSiBY5KP8M+/ywHKZDkRb6EjzMPx5oKFeGW
88 Hmqaj5FDmTeWChSIHd1ZxobashFauOZDbS/ijRRMsVGFulU2Nb/4QJK73g3orfhY
89 5mq1TMkQ5Kcbqh4OmYYYayLtJQcpa6ZVopaRhAJFe30P83zW9pM5LQDpP9JIyY+S
90 DjasEY4ekYtw6oCKAjpqlwaaNDjl27OkJ7R7laFKy4grZ2TSB/2KTjn/Ea3CH/pA
91 SrpVis1LvC90XytbBnsEKYXU55H943wmBc6oj+itQhx4WyIiv+UgtHI/DbnYbUru
92 71wpfapqGBXYfu/zAra8PITngOFuizeYu+idemu55ANO3keJPKr3ZBUSBBpNFauT
93 VUUCSnrLt+kpSLopYESiNdsPW/aQTFgFvA4BkBJTIMQsQZXicuXUePYlg5xFzXOv
94 XgiqkjRA9xBI5JAIUgLRk3ulVFt2bIsTG9XgtGyphEs86Q0MOIMo0WbZGtAYDrZO
95 DITbm2KzVLGVLn/ZJiW11RSHPNiwgg66/puKdFWrSogYYDJdDEUJtLIhypZ+ORxe
96 7oh88hTkC1w=
97 =UNSw
98 -----END PGP PUBLIC KEY BLOCK-----
099
=== added file 'examples/tests/apt_source_search.yaml'
--- examples/tests/apt_source_search.yaml 1970-01-01 00:00:00 +0000
+++ examples/tests/apt_source_search.yaml 2016-07-26 19:53:43 +0000
@@ -0,0 +1,97 @@
1showtrace: true
2apt:
3 preserve_sources_list: false
4 primary:
5 - arches: [default]
6 search:
7 - http://does.not.exist/ubuntu
8 - http://does.also.not.exist/ubuntu
9 - http://us.archive.ubuntu.com/ubuntu
10 security:
11 - arches: [default]
12 search:
13 - http://does.not.exist/ubuntu
14 - http://does.also.not.exist/ubuntu
15 - http://security.ubuntu.com/ubuntu
16 conf: | # APT config
17 ACQUIRE {
18 Retries "3";
19 };
20 sources:
21 curtin-dev-ppa.list:
22 source: "deb http://ppa.launchpad.net/curtin-dev/test-archive/ubuntu xenial main"
23 keyid: F430BBA5
24 ignored1:
25 source: "ppa:curtin-dev/test-archive"
26 my-repo2.list:
27 source: deb $MIRROR $RELEASE multiverse
28 ignored3:
29 keyid: 0E72 9061 0D2F 6DC4 D65E A921 9A31 4EC5 F470 A0AC
30 my-repo4.list:
31 source: deb http://ppa.launchpad.net/curtin-dev/test-archive/ubuntu xenial main
32 key: |
33 -----BEGIN PGP PUBLIC KEY BLOCK-----
34 Version: GnuPG v1
35
36 mQINBFXJ3NcBEAC85PMdaKdItkdjCT1vRJrdwNqj4lN5mu6z4dDVfeZlmozRDBGb
37 ENSOWCiYz3meANO7bKthQQCqAETSBV72rrDCqFZUpXeyG3zCN98Z/UdJ8zpQD9uw
38 mq2CaAqWMk6ty+PkHQ4gtIc390lGfRbHNoZ5HaWJNVOK7FCB2hBmnTZW7AViYiYa
39 YswOjYxaCkwQ/DsMOPD7S5OjwbLucs2YGjkBm7YF1nnXNzyt+BwieKQW/sQ2+ga1
40 mkgLW1BTQN3+JreBpeHy/yrRdK4dOZZUar4WPZitZzOW2eNpaaf6hKNA14LB/96a
41 tEguK8VazoqSQGvNV/R3PjIYmurVP3/Z9bEVgOKhMCflgwKCYgx+tBUypN3zFWv9
42 pgVq3iHx1MFCvoP9FsNB7I6jzOxlQP4z25BzR3ympx/QexkFw5CBFXhdrU+qNVBl
43 SSnz69aLEjCRXqBOnQEr0irs/e/35+yLJdEuw89vSwWwrzbV5r1Y7uxinEGWSydT
44 qddj97uKOWeMmnp20Be4+nhDDW/BMiTFI4Y3bYeDTrftbWMaSEmtSTw5HHxtAFtg
45 X9Hyx0Q3eN1w3gRZgIdm0xYTe7bNTofFRdfXzB/9wtNIcaW10+IlODShFHPCnh+d
46 i56a8LCdZcXiiLfCIhEcnqmM37BVvhjIQKSyOU1eMEgX148aVEz36OVuMwARAQAB
47 tCdDaHJpc3RpYW4gRWhyaGFyZHQgPGNwYWVsemVyQGdtYWlsLmNvbT6JAjgEEwEC
48 ACIFAlXJ3NcCGwMGCwkIBwMCBhUIAgkKCwQWAgMBAh4BAheAAAoJELo+KTOCgLJC
49 BugP/ir0ES3wCzvHMnkz2UlXt9FR4KqY0L9uFmwu9VYpmfAploEVIOi2HcuxpcRp
50 hgoQlUtkz3lRhUeZzCxuB1ljM2JKTJiezP1tFTTGCbVYhPyA0LmUiHDWylG7FzPb
51 TX96HY/G0jf+m4CfR8q3HNHjeDi4VeA2ppBxdHcVE5I7HihDgRPJd+CvCa3nYdAb
52 nXDKlQZz5aZc7AgrRVamr4mshkzWuwNNCwOt3AIgHDkU/HzA5xlXfwHxOoP6scWH
53 /T7vFsd/vOikBphGseWPgKm6w1zyQ5Dk/wjRL8UeSJZW+Rh4PuBMbxg01lAZpPTq
54 tu/bePeNty3g5bhwO6oHMpWhprn3dO37R680qo6UnBPzICeuBUnSYgpPnsQC9maz
55 FEjiBtMsXSanU5vww7TpxY1JHjk5KFcmKx4sBeablznsm+GuVaDFN8R4eDjrM14r
56 SOzA9cV0bSQr4dMqA9fZFSx6qLTacIeMfptybW3zaDX/pJOeBBWRAtoAfZIFbBnu
57 /ZxDDgiQtZzpVK4UkYk5rjjtV/CPVXx64AnTHi35YfUn14KkE+k3odHdvPfBiv9+
58 NxfkTuV/koOgpD3+lTIYXyVHS9gwvhfRD/YfdrnVGl7bRZe68j7bfWDuQuSqIhSA
59 jpeJslJCawnqv6fVB6buj6jjcgHIxqCVn99chaPFSblEIPfXtDVDaHJpc3RpYW4g
60 RWhyaGFyZHQgPGNocmlzdGlhbi5laHJoYXJkdEBjYW5vbmljYWwuY29tPokCOAQT
61 AQIAIgUCVsbUOgIbAwYLCQgHAwIGFQgCCQoLBBYCAwECHgECF4AACgkQuj4pM4KA
62 skJNPg/7BF/iXHHdSBigWKXCCvQz58uInoc/R4beIegxRCMq7wkYEey4B7Fd35zY
63 zv9CBOTV3hZePMCg9jxl4ki2kSsrZSCIEJw4L/aXDtJtx3HT18uTW0QKoU3nK/ro
64 OtthVqBqmiSEi40UUU+5MGrUjwLSm+PjaaSapjK/lddf0KbXBB78/BtR/XT0gxWM
65 +o68Oei9Nj1S3h6UndJwNAQ1xaDWmU2T7CRJet3F+cXZd3aDuS2axOTSTZbraSq7
66 zdl1xUiKtzXZIp8X1ewne+dzkewZuWj7DOwOBEFK26UhxCjKd5mUr7jpWQ4ampFX
67 6xfd/MK8SJFY+iHOBKyzq9po40tE23dqWuaHB+T3MxOgQ9JHCo9x22XNvEuKZW/V
68 4WaoGHVkR+jtWNC8Qv/xCMHL3CEvAklKJR68WDhozwUYTgNt5vCoJOviMlbhDSwf
69 0zVXpQwMR//4c0QSA0+BPpIEPDnx5vTIHBVXHy4bBBHU2Vi87QIDS0AtiBpNcspN
70 6AG0ktuldkE/pqfSTJ2A9HpHZyU+8boagRS5/z102Pjtmf/mzUkcHmfRb9o0DE15
71 X5fqpA3lYyx9eHIAgH4eaB1+G20Ez/EY5hr8IMS2nNBSem491UW6DXDYRu6eBLrR
72 sRmtrJ6DlTZFRFlqVZ47bce/SbeM/xljvRkBxWG6RtDRsTyNVI65Ag0EVcnc1wEQ
73 ANzk9W058tSHqf05UEtJGrN0K8DLriCvPd7QdFA8yVIZM3WD+m0AMBGXjd8BT5c2
74 lt0GmhB8klonHZvPiVLTRTLcSsc3NBopr1HL1bWsgOczwWiXSrc62oGAHUOQT/bv
75 vS6KIkZgez+qtCo/DCOGJrADaoJBiBCLSsZgowpzazZZDPUF7rAsfcryVCFvftK0
76 wAe1OdvUG77NHrMrE1oX3zh82hTqR5azBre6Y81lNwxxug/Xl/RHjNhEOYohcsLS
77 /xl0m2X831fHzcGGpoISRgrfel+M4RoC7KsLrwVhrF8koCD/ZQlevfLpuRl5LNpO
78 s1ZtEi8ZvLliih+H+BOlBD0zUc3zZrrks/NCpm1eZba0Z6L48r4TIHW08SGlHx7o
79 SrXgkq3mtoM8C4uDiLwjav5KxiF7n68s/9LF82aAr7YjNXd+xYZNjsmmFlYj9CGI
80 lL4jVt4v4EtTONa6pbtCNv5ezOLDZ6BBcQ36xdkrWzdpjQjL2mnh3sqIAGIPu7tH
81 N8euQ5L1zIvIjVqYlR1eJssp96QYPWYxF7TosfML4BUhCP631IWfuD9X/K2LzDmv
82 B2gVZo9fbhSC+P7GYVG+tV4VLAMbspAxRXXL69+j98aeV5g59f8OFQPbGpKE/SAY
83 eIXtq8DD+PYUXXq3VUI2brVLv42LBVdSJpKNKG3decIBABEBAAGJAh8EGAECAAkF
84 AlXJ3NcCGwwACgkQuj4pM4KAskKzeg/9FxXJLV3eWwY4nn2VhwYTHnHtSUpi8usk
85 RzIa3Mcj6OEVjU2LZaT3UQF8h6dLM9y+CemcwyjMqm1RQ5+ogfrItby1AaBXwCvm
86 XCUGw2zFOAnyzSHHoDFj27sllFxDmfSiBY5KP8M+/ywHKZDkRb6EjzMPx5oKFeGW
87 Hmqaj5FDmTeWChSIHd1ZxobashFauOZDbS/ijRRMsVGFulU2Nb/4QJK73g3orfhY
88 5mq1TMkQ5Kcbqh4OmYYYayLtJQcpa6ZVopaRhAJFe30P83zW9pM5LQDpP9JIyY+S
89 DjasEY4ekYtw6oCKAjpqlwaaNDjl27OkJ7R7laFKy4grZ2TSB/2KTjn/Ea3CH/pA
90 SrpVis1LvC90XytbBnsEKYXU55H943wmBc6oj+itQhx4WyIiv+UgtHI/DbnYbUru
91 71wpfapqGBXYfu/zAra8PITngOFuizeYu+idemu55ANO3keJPKr3ZBUSBBpNFauT
92 VUUCSnrLt+kpSLopYESiNdsPW/aQTFgFvA4BkBJTIMQsQZXicuXUePYlg5xFzXOv
93 XgiqkjRA9xBI5JAIUgLRk3ulVFt2bIsTG9XgtGyphEs86Q0MOIMo0WbZGtAYDrZO
94 DITbm2KzVLGVLn/ZJiW11RSHPNiwgg66/puKdFWrSogYYDJdDEUJtLIhypZ+ORxe
95 7oh88hTkC1w=
96 =UNSw
97 -----END PGP PUBLIC KEY BLOCK-----
098
=== added file 'examples/tests/apt_source_search_dns.yaml'
--- examples/tests/apt_source_search_dns.yaml 1970-01-01 00:00:00 +0000
+++ examples/tests/apt_source_search_dns.yaml 2016-07-26 19:53:43 +0000
@@ -0,0 +1,21 @@
1showtrace: true
2# fake up something that works in vmtets environment without dependency to external dns setup
3early_commands:
4 xx_prep_fake_dns_search0: 'echo foo.bar > /etc/hostname'
5 xx_prep_fake_dns_search1: 'echo "$(getent ahosts us.archive.ubuntu.com | cut --fields=1 --delimiter " " | grep -v : | head -n 1) ubuntu-mirror" >> /etc/hosts'
6 xx_prep_fake_dns_search2: 'echo "$(getent ahosts security.ubuntu.com | cut --fields=1 --delimiter " " | grep -v : | head -n 1) ubuntu-security-mirror" >> /etc/hosts'
7apt:
8 primary:
9 - arches: [default]
10 search_dns: True
11 security:
12 - arches: [default]
13 search_dns: True
14 # avoiding issues with these fake mirrors we only set them in a disabled list
15 # this is the default now preserve_sources_list: true
16 sources:
17 dnssearch.list.disabled:
18 source: |
19 deb $MIRROR $RELEASE multiverse
20 deb $PRIMARY $RELEASE universe
21 deb $SECURITY $RELEASE main
022
=== added file 'examples/tests/test_old_apt_features.yaml'
--- examples/tests/test_old_apt_features.yaml 1970-01-01 00:00:00 +0000
+++ examples/tests/test_old_apt_features.yaml 2016-07-26 19:53:43 +0000
@@ -0,0 +1,10 @@
1showtrace: true
2# apt_proxy gets configured by tools/launch and tests/vmtests/__init__.py
3apt_mirrors:
4# we need a mirror that works (even in CI) but isn't the default
5 ubuntu_archive: http://us.archive.ubuntu.com/ubuntu
6 ubuntu_security: http://archive.ubuntu.com/ubuntu
7# set some key that surely is available to a non-default value
8debconf_selections:
9 set1: |
10 debconf debconf/priority select low
011
=== added file 'examples/tests/test_old_apt_features_ports.yaml'
--- examples/tests/test_old_apt_features_ports.yaml 1970-01-01 00:00:00 +0000
+++ examples/tests/test_old_apt_features_ports.yaml 2016-07-26 19:53:43 +0000
@@ -0,0 +1,10 @@
1showtrace: true
2# apt_proxy gets configured by tools/launch and tests/vmtests/__init__.py
3apt_mirrors:
4# For ports there is no non-default alternative we could use
5 ubuntu_archive: http://ports.ubuntu.com/ubuntu-ports
6 ubuntu_security: http://ports.ubuntu.com/ubuntu-ports
7# set some key that surely is available to a non-default value
8debconf_selections:
9 set1: |
10 debconf debconf/priority select low
011
=== added file 'tests/unittests/test_apt_custom_sources_list.py'
--- tests/unittests/test_apt_custom_sources_list.py 1970-01-01 00:00:00 +0000
+++ tests/unittests/test_apt_custom_sources_list.py 2016-07-26 19:53:43 +0000
@@ -0,0 +1,172 @@
1""" test_apt_custom_sources_list
2Test templating of custom sources list
3"""
4import logging
5import os
6import shutil
7import tempfile
8
9from unittest import TestCase
10
11import yaml
12import mock
13from mock import call
14
15from curtin import util
16from curtin.commands import apt_config
17
18LOG = logging.getLogger(__name__)
19
20TARGET = "/"
21
22# Input and expected output for the custom template
23YAML_TEXT_CUSTOM_SL = """
24preserve_sources_list: false
25primary:
26 - arches: [default]
27 uri: http://test.ubuntu.com/ubuntu/
28security:
29 - arches: [default]
30 uri: http://testsec.ubuntu.com/ubuntu/
31sources_list: |
32
33 ## Note, this file is written by curtin at install time. It should not end
34 ## up on the installed system itself.
35 #
36 # See http://help.ubuntu.com/community/UpgradeNotes for how to upgrade to
37 # newer versions of the distribution.
38 deb $MIRROR $RELEASE main restricted
39 deb-src $MIRROR $RELEASE main restricted
40 deb $PRIMARY $RELEASE universe restricted
41 deb $SECURITY $RELEASE-security multiverse
42 # FIND_SOMETHING_SPECIAL
43"""
44
45EXPECTED_CONVERTED_CONTENT = """
46## Note, this file is written by curtin at install time. It should not end
47## up on the installed system itself.
48#
49# See http://help.ubuntu.com/community/UpgradeNotes for how to upgrade to
50# newer versions of the distribution.
51deb http://test.ubuntu.com/ubuntu/ fakerel main restricted
52deb-src http://test.ubuntu.com/ubuntu/ fakerel main restricted
53deb http://test.ubuntu.com/ubuntu/ fakerel universe restricted
54deb http://testsec.ubuntu.com/ubuntu/ fakerel-security multiverse
55# FIND_SOMETHING_SPECIAL
56"""
57
58# mocked to be independent to the unittest system
59MOCKED_APT_SRC_LIST = """
60deb http://test.ubuntu.com/ubuntu/ notouched main restricted
61deb-src http://test.ubuntu.com/ubuntu/ notouched main restricted
62deb http://test.ubuntu.com/ubuntu/ notouched-updates main restricted
63deb http://testsec.ubuntu.com/ubuntu/ notouched-security main restricted
64"""
65
66EXPECTED_BASE_CONTENT = ("""
67deb http://test.ubuntu.com/ubuntu/ notouched main restricted
68deb-src http://test.ubuntu.com/ubuntu/ notouched main restricted
69deb http://test.ubuntu.com/ubuntu/ notouched-updates main restricted
70deb http://testsec.ubuntu.com/ubuntu/ notouched-security main restricted
71""")
72
73EXPECTED_MIRROR_CONTENT = ("""
74deb http://test.ubuntu.com/ubuntu/ notouched main restricted
75deb-src http://test.ubuntu.com/ubuntu/ notouched main restricted
76deb http://test.ubuntu.com/ubuntu/ notouched-updates main restricted
77deb http://test.ubuntu.com/ubuntu/ notouched-security main restricted
78""")
79
80EXPECTED_PRIMSEC_CONTENT = ("""
81deb http://test.ubuntu.com/ubuntu/ notouched main restricted
82deb-src http://test.ubuntu.com/ubuntu/ notouched main restricted
83deb http://test.ubuntu.com/ubuntu/ notouched-updates main restricted
84deb http://testsec.ubuntu.com/ubuntu/ notouched-security main restricted
85""")
86
87
88class TestAptSourceConfigSourceList(TestCase):
89 """TestAptSourceConfigSourceList - Class to test sources list rendering"""
90 def setUp(self):
91 super(TestAptSourceConfigSourceList, self).setUp()
92 self.new_root = tempfile.mkdtemp()
93 self.addCleanup(shutil.rmtree, self.new_root)
94 # self.patchUtils(self.new_root)
95
96 @staticmethod
97 def _apt_source_list(cfg, expected):
98 "_apt_source_list - Test rendering from template (generic)"
99
100 arch = util.get_architecture()
101 # would fail inside the unittest context
102 with mock.patch.object(util, 'get_architecture',
103 return_value=arch) as mockga:
104 with mock.patch.object(util, 'write_file') as mockwrite:
105 # keep it side effect free and avoid permission errors
106 with mock.patch.object(os, 'rename'):
107 # make test independent to executing system
108 with mock.patch.object(util, 'load_file',
109 return_value=MOCKED_APT_SRC_LIST):
110 with mock.patch.object(util, 'lsb_release',
111 return_value={'codename':
112 'fakerel'}):
113 apt_config.handle_apt(cfg, TARGET)
114
115 mockga.assert_called_with("/")
116
117 cloudfile = '/etc/cloud/cloud.cfg.d/curtin-preserve-sources.cfg'
118 cloudconf = yaml.dump({'apt_preserve_sources_list': True}, indent=1)
119 calls = [call(TARGET + '/etc/apt/sources.list',
120 expected,
121 mode=0o644),
122 call(TARGET + cloudfile,
123 cloudconf,
124 mode=0o644)]
125 mockwrite.assert_has_calls(calls)
126
127 def test_apt_source_list(self):
128 """test_apt_source_list - Test with neither custom sources nor parms"""
129 cfg = {'preserve_sources_list': False}
130
131 self._apt_source_list(cfg, EXPECTED_BASE_CONTENT)
132
133 def test_apt_source_list_psm(self):
134 """test_apt_source_list_psm - Test specifying prim+sec mirrors"""
135 cfg = {'preserve_sources_list': False,
136 'primary': [{'arches': ["default"],
137 'uri': 'http://test.ubuntu.com/ubuntu/'}],
138 'security': [{'arches': ["default"],
139 'uri': 'http://testsec.ubuntu.com/ubuntu/'}]}
140
141 self._apt_source_list(cfg, EXPECTED_PRIMSEC_CONTENT)
142
143 @staticmethod
144 def test_apt_srcl_custom():
145 """test_apt_srcl_custom - Test rendering a custom source template"""
146 cfg = yaml.safe_load(YAML_TEXT_CUSTOM_SL)
147
148 arch = util.get_architecture()
149 # would fail inside the unittest context
150 with mock.patch.object(util, 'get_architecture',
151 return_value=arch) as mockga:
152 with mock.patch.object(util, 'write_file') as mockwrite:
153 # keep it side effect free and avoid permission errors
154 with mock.patch.object(os, 'rename'):
155 with mock.patch.object(util, 'lsb_release',
156 return_value={'codename':
157 'fakerel'}):
158 apt_config.handle_apt(cfg, TARGET)
159
160 mockga.assert_called_with("/")
161 cloudfile = '/etc/cloud/cloud.cfg.d/curtin-preserve-sources.cfg'
162 cloudconf = yaml.dump({'apt_preserve_sources_list': True}, indent=1)
163 calls = [call(TARGET + '/etc/apt/sources.list',
164 EXPECTED_CONVERTED_CONTENT,
165 mode=0o644),
166 call(TARGET + cloudfile,
167 cloudconf,
168 mode=0o644)]
169 mockwrite.assert_has_calls(calls)
170
171
172# vi: ts=4 expandtab
0173
=== added file 'tests/unittests/test_apt_source.py'
--- tests/unittests/test_apt_source.py 1970-01-01 00:00:00 +0000
+++ tests/unittests/test_apt_source.py 2016-07-26 19:53:43 +0000
@@ -0,0 +1,927 @@
1""" test_apt_source
2Testing various config variations of the apt_source custom config
3"""
4import glob
5import os
6import re
7import shutil
8import socket
9import tempfile
10
11from unittest import TestCase
12
13import mock
14from mock import call
15
16from curtin import util
17from curtin import gpg
18from curtin.commands import apt_config
19
20
21EXPECTEDKEY = u"""-----BEGIN PGP PUBLIC KEY BLOCK-----
22Version: GnuPG v1
23
24mI0ESuZLUgEEAKkqq3idtFP7g9hzOu1a8+v8ImawQN4TrvlygfScMU1TIS1eC7UQ
25NUA8Qqgr9iUaGnejb0VciqftLrU9D6WYHSKz+EITefgdyJ6SoQxjoJdsCpJ7o9Jy
268PQnpRttiFm4qHu6BVnKnBNxw/z3ST9YMqW5kbMQpfxbGe+obRox59NpABEBAAG0
27HUxhdW5jaHBhZCBQUEEgZm9yIFNjb3R0IE1vc2VyiLYEEwECACAFAkrmS1ICGwMG
28CwkIBwMCBBUCCAMEFgIDAQIeAQIXgAAKCRAGILvPA2g/d3aEA/9tVjc10HOZwV29
29OatVuTeERjjrIbxflO586GLA8cp0C9RQCwgod/R+cKYdQcHjbqVcP0HqxveLg0RZ
30FJpWLmWKamwkABErwQLGlM/Hwhjfade8VvEQutH5/0JgKHmzRsoqfR+LMO6OS+Sm
31S0ORP6HXET3+jC8BMG4tBWCTK/XEZw==
32=ACB2
33-----END PGP PUBLIC KEY BLOCK-----"""
34
35ADD_APT_REPO_MATCH = r"^[\w-]+:\w"
36
37TARGET = "/"
38
39
40def load_tfile(filename):
41 """ load_tfile
42 load file and return content after decoding
43 """
44 try:
45 content = util.load_file(filename, mode="r")
46 except Exception as error:
47 print('failed to load file content for test: %s' % error)
48 raise
49
50 return content
51
52
53class TestAptSourceConfig(TestCase):
54 """ TestAptSourceConfig
55 Main Class to test apt configs
56 """
57 def setUp(self):
58 super(TestAptSourceConfig, self).setUp()
59 self.tmp = tempfile.mkdtemp()
60 self.addCleanup(shutil.rmtree, self.tmp)
61 self.aptlistfile = os.path.join(self.tmp, "single-deb.list")
62 self.aptlistfile2 = os.path.join(self.tmp, "single-deb2.list")
63 self.aptlistfile3 = os.path.join(self.tmp, "single-deb3.list")
64 self.join = os.path.join
65 self.matcher = re.compile(ADD_APT_REPO_MATCH).search
66
67 @staticmethod
68 def _add_apt_sources(*args, **kwargs):
69 with mock.patch.object(util, 'apt_update'):
70 apt_config.add_apt_sources(*args, **kwargs)
71
72 @staticmethod
73 def _get_default_params():
74 """ get_default_params
75 Get the most basic default mrror and release info to be used in tests
76 """
77 params = {}
78 params['RELEASE'] = util.lsb_release()['codename']
79 params['MIRROR'] = apt_config.get_default_mirrors()["PRIMARY"]
80 return params
81
82 def _myjoin(self, *args, **kwargs):
83 """ _myjoin - redir into writable tmpdir"""
84 if (args[0] == "/etc/apt/sources.list.d/" and
85 args[1] == "cloud_config_sources.list" and
86 len(args) == 2):
87 return self.join(self.tmp, args[0].lstrip("/"), args[1])
88 else:
89 return self.join(*args, **kwargs)
90
91 def _apt_src_basic(self, filename, cfg):
92 """ _apt_src_basic
93 Test Fix deb source string, has to overwrite mirror conf in params
94 """
95 params = self._get_default_params()
96
97 self._add_apt_sources(cfg, TARGET, template_params=params,
98 aa_repo_match=self.matcher)
99
100 self.assertTrue(os.path.isfile(filename))
101
102 contents = load_tfile(filename)
103 self.assertTrue(re.search(r"%s %s %s %s\n" %
104 ("deb", "http://test.ubuntu.com/ubuntu",
105 "karmic-backports",
106 "main universe multiverse restricted"),
107 contents, flags=re.IGNORECASE))
108
109 def test_apt_src_basic(self):
110 """test_apt_src_basic - Test fix deb source string"""
111 cfg = {self.aptlistfile: {'source':
112 ('deb http://test.ubuntu.com/ubuntu'
113 ' karmic-backports'
114 ' main universe multiverse restricted')}}
115 self._apt_src_basic(self.aptlistfile, cfg)
116
117 def test_apt_src_basic_tri(self):
118 """test_apt_src_basic_tri - Test multiple fix deb source strings"""
119 cfg = {self.aptlistfile: {'source':
120 ('deb http://test.ubuntu.com/ubuntu'
121 ' karmic-backports'
122 ' main universe multiverse restricted')},
123 self.aptlistfile2: {'source':
124 ('deb http://test.ubuntu.com/ubuntu'
125 ' precise-backports'
126 ' main universe multiverse restricted')},
127 self.aptlistfile3: {'source':
128 ('deb http://test.ubuntu.com/ubuntu'
129 ' lucid-backports'
130 ' main universe multiverse restricted')}}
131 self._apt_src_basic(self.aptlistfile, cfg)
132
133 # extra verify on two extra files of this test
134 contents = load_tfile(self.aptlistfile2)
135 self.assertTrue(re.search(r"%s %s %s %s\n" %
136 ("deb", "http://test.ubuntu.com/ubuntu",
137 "precise-backports",
138 "main universe multiverse restricted"),
139 contents, flags=re.IGNORECASE))
140 contents = load_tfile(self.aptlistfile3)
141 self.assertTrue(re.search(r"%s %s %s %s\n" %
142 ("deb", "http://test.ubuntu.com/ubuntu",
143 "lucid-backports",
144 "main universe multiverse restricted"),
145 contents, flags=re.IGNORECASE))
146
147 def _apt_src_replacement(self, filename, cfg):
148 """ apt_src_replace
149 Test Autoreplacement of MIRROR and RELEASE in source specs
150 """
151 params = self._get_default_params()
152 self._add_apt_sources(cfg, TARGET, template_params=params,
153 aa_repo_match=self.matcher)
154
155 self.assertTrue(os.path.isfile(filename))
156
157 contents = load_tfile(filename)
158 self.assertTrue(re.search(r"%s %s %s %s\n" %
159 ("deb", params['MIRROR'], params['RELEASE'],
160 "multiverse"),
161 contents, flags=re.IGNORECASE))
162
163 def test_apt_src_replace(self):
164 """test_apt_src_replace - Test Autoreplacement of MIRROR and RELEASE"""
165 cfg = {self.aptlistfile: {'source': 'deb $MIRROR $RELEASE multiverse'}}
166 self._apt_src_replacement(self.aptlistfile, cfg)
167
168 def test_apt_src_replace_fn(self):
169 """test_apt_src_replace_fn - Test filename being overwritten in dict"""
170 cfg = {'ignored': {'source': 'deb $MIRROR $RELEASE multiverse',
171 'filename': self.aptlistfile}}
172 # second file should overwrite the dict key
173 self._apt_src_replacement(self.aptlistfile, cfg)
174
175 def _apt_src_replace_tri(self, cfg):
176 """ _apt_src_replace_tri
177 Test three autoreplacements of MIRROR and RELEASE in source specs with
178 generic part
179 """
180 self._apt_src_replacement(self.aptlistfile, cfg)
181
182 # extra verify on two extra files of this test
183 params = self._get_default_params()
184 contents = load_tfile(self.aptlistfile2)
185 self.assertTrue(re.search(r"%s %s %s %s\n" %
186 ("deb", params['MIRROR'], params['RELEASE'],
187 "main"),
188 contents, flags=re.IGNORECASE))
189 contents = load_tfile(self.aptlistfile3)
190 self.assertTrue(re.search(r"%s %s %s %s\n" %
191 ("deb", params['MIRROR'], params['RELEASE'],
192 "universe"),
193 contents, flags=re.IGNORECASE))
194
195 def test_apt_src_replace_tri(self):
196 """test_apt_src_replace_tri - Test multiple replacements/overwrites"""
197 cfg = {self.aptlistfile: {'source': 'deb $MIRROR $RELEASE multiverse'},
198 'notused': {'source': 'deb $MIRROR $RELEASE main',
199 'filename': self.aptlistfile2},
200 self.aptlistfile3: {'source': 'deb $MIRROR $RELEASE universe'}}
201 self._apt_src_replace_tri(cfg)
202
203 def _apt_src_keyid(self, filename, cfg, keynum):
204 """ _apt_src_keyid
205 Test specification of a source + keyid
206 """
207 params = self._get_default_params()
208
209 with mock.patch.object(util, 'subp',
210 return_value=('fakekey 1234', '')) as mockobj:
211 self._add_apt_sources(cfg, TARGET, template_params=params,
212 aa_repo_match=self.matcher)
213
214 # check if it added the right ammount of keys
215 calls = []
216 for _ in range(keynum):
217 calls.append(call(['apt-key', 'add', '-'], data=b'fakekey 1234'))
218 mockobj.assert_has_calls(calls, any_order=True)
219
220 self.assertTrue(os.path.isfile(filename))
221
222 contents = load_tfile(filename)
223 self.assertTrue(re.search(r"%s %s %s %s\n" %
224 ("deb",
225 ('http://ppa.launchpad.net/smoser/'
226 'cloud-init-test/ubuntu'),
227 "xenial", "main"),
228 contents, flags=re.IGNORECASE))
229
230 def test_apt_src_keyid(self):
231 """test_apt_src_keyid - Test source + keyid with filename being set"""
232 cfg = {self.aptlistfile: {'source': ('deb '
233 'http://ppa.launchpad.net/'
234 'smoser/cloud-init-test/ubuntu'
235 ' xenial main'),
236 'keyid': "03683F77"}}
237 self._apt_src_keyid(self.aptlistfile, cfg, 1)
238
239 def test_apt_src_keyid_tri(self):
240 """test_apt_src_keyid_tri - Test multiple src+keyid+filen overwrites"""
241 cfg = {self.aptlistfile: {'source': ('deb '
242 'http://ppa.launchpad.net/'
243 'smoser/cloud-init-test/ubuntu'
244 ' xenial main'),
245 'keyid': "03683F77"},
246 'ignored': {'source': ('deb '
247 'http://ppa.launchpad.net/'
248 'smoser/cloud-init-test/ubuntu'
249 ' xenial universe'),
250 'keyid': "03683F77",
251 'filename': self.aptlistfile2},
252 self.aptlistfile3: {'source': ('deb '
253 'http://ppa.launchpad.net/'
254 'smoser/cloud-init-test/ubuntu'
255 ' xenial multiverse'),
256 'keyid': "03683F77"}}
257
258 self._apt_src_keyid(self.aptlistfile, cfg, 3)
259 contents = load_tfile(self.aptlistfile2)
260 self.assertTrue(re.search(r"%s %s %s %s\n" %
261 ("deb",
262 ('http://ppa.launchpad.net/smoser/'
263 'cloud-init-test/ubuntu'),
264 "xenial", "universe"),
265 contents, flags=re.IGNORECASE))
266 contents = load_tfile(self.aptlistfile3)
267 self.assertTrue(re.search(r"%s %s %s %s\n" %
268 ("deb",
269 ('http://ppa.launchpad.net/smoser/'
270 'cloud-init-test/ubuntu'),
271 "xenial", "multiverse"),
272 contents, flags=re.IGNORECASE))
273
274 def test_apt_src_key(self):
275 """test_apt_src_key - Test source + key"""
276 params = self._get_default_params()
277 cfg = {self.aptlistfile: {'source': ('deb '
278 'http://ppa.launchpad.net/'
279 'smoser/cloud-init-test/ubuntu'
280 ' xenial main'),
281 'key': "fakekey 4321"}}
282
283 with mock.patch.object(util, 'subp') as mockobj:
284 self._add_apt_sources(cfg, TARGET, template_params=params,
285 aa_repo_match=self.matcher)
286
287 mockobj.assert_any_call(['apt-key', 'add', '-'], data=b'fakekey 4321')
288
289 self.assertTrue(os.path.isfile(self.aptlistfile))
290
291 contents = load_tfile(self.aptlistfile)
292 self.assertTrue(re.search(r"%s %s %s %s\n" %
293 ("deb",
294 ('http://ppa.launchpad.net/smoser/'
295 'cloud-init-test/ubuntu'),
296 "xenial", "main"),
297 contents, flags=re.IGNORECASE))
298
299 def test_apt_src_keyonly(self):
300 """test_apt_src_keyonly - Test key without source"""
301 params = self._get_default_params()
302 cfg = {self.aptlistfile: {'key': "fakekey 4242"}}
303
304 with mock.patch.object(util, 'subp') as mockobj:
305 self._add_apt_sources(cfg, TARGET, template_params=params,
306 aa_repo_match=self.matcher)
307
308 mockobj.assert_any_call(['apt-key', 'add', '-'], data=b'fakekey 4242')
309
310 # filename should be ignored on key only
311 self.assertFalse(os.path.isfile(self.aptlistfile))
312
313 def test_apt_src_keyidonly(self):
314 """test_apt_src_keyidonly - Test keyid without source"""
315 params = self._get_default_params()
316 cfg = {self.aptlistfile: {'keyid': "03683F77"}}
317
318 with mock.patch.object(util, 'subp',
319 return_value=('fakekey 1212', '')) as mockobj:
320 self._add_apt_sources(cfg, TARGET, template_params=params,
321 aa_repo_match=self.matcher)
322
323 mockobj.assert_any_call(['apt-key', 'add', '-'], data=b'fakekey 1212')
324
325 # filename should be ignored on key only
326 self.assertFalse(os.path.isfile(self.aptlistfile))
327
328 def apt_src_keyid_real(self, cfg, expectedkey):
329 """apt_src_keyid_real
330 Test specification of a keyid without source including
331 up to addition of the key (add_apt_key_raw mocked to keep the
332 environment as is)
333 """
334 params = self._get_default_params()
335
336 with mock.patch.object(apt_config, 'add_apt_key_raw') as mockkey:
337 with mock.patch.object(gpg, 'getkeybyid',
338 return_value=expectedkey) as mockgetkey:
339 self._add_apt_sources(cfg, TARGET, template_params=params,
340 aa_repo_match=self.matcher)
341
342 keycfg = cfg[self.aptlistfile]
343 mockgetkey.assert_called_with(keycfg['keyid'],
344 keycfg.get('keyserver',
345 'keyserver.ubuntu.com'))
346 mockkey.assert_called_with(expectedkey, TARGET)
347
348 # filename should be ignored on key only
349 self.assertFalse(os.path.isfile(self.aptlistfile))
350
351 def test_apt_src_keyid_real(self):
352 """test_apt_src_keyid_real - Test keyid including key add"""
353 keyid = "03683F77"
354 cfg = {self.aptlistfile: {'keyid': keyid}}
355
356 self.apt_src_keyid_real(cfg, EXPECTEDKEY)
357
358 def test_apt_src_longkeyid_real(self):
359 """test_apt_src_longkeyid_real Test long keyid including key add"""
360 keyid = "B59D 5F15 97A5 04B7 E230 6DCA 0620 BBCF 0368 3F77"
361 cfg = {self.aptlistfile: {'keyid': keyid}}
362
363 self.apt_src_keyid_real(cfg, EXPECTEDKEY)
364
365 def test_apt_src_longkeyid_ks_real(self):
366 """test_apt_src_longkeyid_ks_real Test long keyid from other ks"""
367 keyid = "B59D 5F15 97A5 04B7 E230 6DCA 0620 BBCF 0368 3F77"
368 cfg = {self.aptlistfile: {'keyid': keyid,
369 'keyserver': 'keys.gnupg.net'}}
370
371 self.apt_src_keyid_real(cfg, EXPECTEDKEY)
372
373 def test_apt_src_keyid_keyserver(self):
374 """test_apt_src_keyid_keyserver - Test custom keyserver"""
375 keyid = "03683F77"
376 params = self._get_default_params()
377 cfg = {self.aptlistfile: {'keyid': keyid,
378 'keyserver': 'test.random.com'}}
379
380 # in some test environments only *.ubuntu.com is reachable
381 # so mock the call and check if the config got there
382 with mock.patch.object(gpg, 'getkeybyid',
383 return_value="fakekey") as mockgetkey:
384 with mock.patch.object(apt_config, 'add_apt_key_raw') as mockadd:
385 self._add_apt_sources(cfg, TARGET, template_params=params,
386 aa_repo_match=self.matcher)
387
388 mockgetkey.assert_called_with('03683F77', 'test.random.com')
389 mockadd.assert_called_with('fakekey', TARGET)
390
391 # filename should be ignored on key only
392 self.assertFalse(os.path.isfile(self.aptlistfile))
393
394 def test_apt_src_ppa(self):
395 """test_apt_src_ppa - Test specification of a ppa"""
396 params = self._get_default_params()
397 cfg = {self.aptlistfile: {'source': 'ppa:smoser/cloud-init-test'}}
398
399 with mock.patch.object(util, 'subp') as mockobj:
400 self._add_apt_sources(cfg, TARGET, template_params=params,
401 aa_repo_match=self.matcher)
402 mockobj.assert_any_call(['add-apt-repository',
403 'ppa:smoser/cloud-init-test'])
404
405 # adding ppa should ignore filename (uses add-apt-repository)
406 self.assertFalse(os.path.isfile(self.aptlistfile))
407
408 def test_apt_src_ppa_tri(self):
409 """test_apt_src_ppa_tri - Test specification of multiple ppa's"""
410 params = self._get_default_params()
411 cfg = {self.aptlistfile: {'source': 'ppa:smoser/cloud-init-test'},
412 self.aptlistfile2: {'source': 'ppa:smoser/cloud-init-test2'},
413 self.aptlistfile3: {'source': 'ppa:smoser/cloud-init-test3'}}
414
415 with mock.patch.object(util, 'subp') as mockobj:
416 self._add_apt_sources(cfg, TARGET, template_params=params,
417 aa_repo_match=self.matcher)
418 calls = [call(['add-apt-repository', 'ppa:smoser/cloud-init-test']),
419 call(['add-apt-repository', 'ppa:smoser/cloud-init-test2']),
420 call(['add-apt-repository', 'ppa:smoser/cloud-init-test3'])]
421 mockobj.assert_has_calls(calls, any_order=True)
422
423 # adding ppa should ignore all filenames (uses add-apt-repository)
424 self.assertFalse(os.path.isfile(self.aptlistfile))
425 self.assertFalse(os.path.isfile(self.aptlistfile2))
426 self.assertFalse(os.path.isfile(self.aptlistfile3))
427
428 def test_mir_apt_list_rename(self):
429 """test_mir_apt_list_rename - Test find mirror and apt list renaming"""
430 pre = "/var/lib/apt/lists"
431 # filenames are archive dependent
432 arch = util.get_architecture()
433 if arch in apt_config.PRIMARY_ARCHES:
434 component = "ubuntu"
435 archive = "archive.ubuntu.com"
436 else:
437 component = "ubuntu-ports"
438 archive = "ports.ubuntu.com"
439
440 cfg = {'primary': [{'arches': ["default"],
441 'uri':
442 'http://test.ubuntu.com/%s/' % component}],
443 'security': [{'arches': ["default"],
444 'uri':
445 'http://testsec.ubuntu.com/%s/' % component}]}
446 post = ("%s_dists_%s-updates_InRelease" %
447 (component, util.lsb_release()['codename']))
448 fromfn = ("%s/%s_%s" % (pre, archive, post))
449 tofn = ("%s/test.ubuntu.com_%s" % (pre, post))
450
451 mirrors = apt_config.find_apt_mirror_info(cfg)
452
453 self.assertEqual(mirrors['MIRROR'],
454 "http://test.ubuntu.com/%s/" % component)
455 self.assertEqual(mirrors['PRIMARY'],
456 "http://test.ubuntu.com/%s/" % component)
457 self.assertEqual(mirrors['SECURITY'],
458 "http://testsec.ubuntu.com/%s/" % component)
459
460 # get_architecture would fail inside the unittest context
461 with mock.patch.object(util, 'get_architecture', return_value=arch):
462 with mock.patch.object(os, 'rename') as mockren:
463 with mock.patch.object(glob, 'glob',
464 return_value=[fromfn]):
465 apt_config.rename_apt_lists(mirrors, TARGET)
466
467 mockren.assert_any_call(fromfn, tofn)
468
469 @staticmethod
470 def test_apt_proxy():
471 """test_mir_apt_list_rename - Test apt_*proxy configuration"""
472 cfg = {"proxy": "foobar1",
473 "http_proxy": "foobar2",
474 "ftp_proxy": "foobar3",
475 "https_proxy": "foobar4"}
476
477 with mock.patch.object(util, 'write_file') as mockobj:
478 apt_config.apply_apt_proxy_config(cfg, "proxyfn", "notused")
479
480 mockobj.assert_called_with('proxyfn',
481 ('Acquire::http::Proxy "foobar1";\n'
482 'Acquire::http::Proxy "foobar2";\n'
483 'Acquire::ftp::Proxy "foobar3";\n'
484 'Acquire::https::Proxy "foobar4";\n'))
485
486 def test_mirror(self):
487 """test_mirror - Test defining a mirror"""
488 pmir = "http://us.archive.ubuntu.com/ubuntu/"
489 smir = "http://security.ubuntu.com/ubuntu/"
490 cfg = {"primary": [{'arches': ["default"],
491 "uri": pmir}],
492 "security": [{'arches': ["default"],
493 "uri": smir}]}
494
495 mirrors = apt_config.find_apt_mirror_info(cfg)
496
497 self.assertEqual(mirrors['MIRROR'],
498 pmir)
499 self.assertEqual(mirrors['PRIMARY'],
500 pmir)
501 self.assertEqual(mirrors['SECURITY'],
502 smir)
503
504 def test_mirror_default(self):
505 """test_mirror_default - Test without defining a mirror"""
506 default_mirrors = apt_config.get_default_mirrors()
507 pmir = default_mirrors["PRIMARY"]
508 smir = default_mirrors["SECURITY"]
509 mirrors = apt_config.find_apt_mirror_info({})
510
511 self.assertEqual(mirrors['MIRROR'],
512 pmir)
513 self.assertEqual(mirrors['PRIMARY'],
514 pmir)
515 self.assertEqual(mirrors['SECURITY'],
516 smir)
517
518 def test_mirror_arches(self):
519 """test_mirror_arches - Test arches selection of mirror"""
520 pmir = "http://us.archive.ubuntu.com/ubuntu/"
521 smir = "http://security.ubuntu.com/ubuntu/"
522 cfg = {"primary": [{'arches': ["default"],
523 "uri": "notthis"},
524 {'arches': [util.get_architecture()],
525 "uri": pmir}],
526 "security": [{'arches': [util.get_architecture()],
527 "uri": smir},
528 {'arches': ["default"],
529 "uri": "nothat"}]}
530
531 mirrors = apt_config.find_apt_mirror_info(cfg)
532
533 self.assertEqual(mirrors['MIRROR'],
534 pmir)
535 self.assertEqual(mirrors['PRIMARY'],
536 pmir)
537 self.assertEqual(mirrors['SECURITY'],
538 smir)
539
540 def test_mirror_arches_default(self):
541 """test_mirror_arches - Test falling back to default arch"""
542 pmir = "http://us.archive.ubuntu.com/ubuntu/"
543 smir = "http://security.ubuntu.com/ubuntu/"
544 cfg = {"primary": [{'arches': ["default"],
545 "uri": pmir},
546 {'arches': ["thisarchdoesntexist"],
547 "uri": "notthis"}],
548 "security": [{'arches': ["thisarchdoesntexist"],
549 "uri": "nothat"},
550 {'arches': ["default"],
551 "uri": smir}]}
552
553 mirrors = apt_config.find_apt_mirror_info(cfg)
554
555 self.assertEqual(mirrors['MIRROR'],
556 pmir)
557 self.assertEqual(mirrors['PRIMARY'],
558 pmir)
559 self.assertEqual(mirrors['SECURITY'],
560 smir)
561
562 def test_mirror_arches_sysdefault(self):
563 """test_mirror_arches - Test arches falling back to sys default"""
564 default_mirrors = apt_config.get_default_mirrors()
565 pmir = default_mirrors["PRIMARY"]
566 smir = default_mirrors["SECURITY"]
567 cfg = {"primary": [{'arches': ["thisarchdoesntexist_64"],
568 "uri": "notthis"},
569 {'arches': ["thisarchdoesntexist"],
570 "uri": "notthiseither"}],
571 "security": [{'arches': ["thisarchdoesntexist"],
572 "uri": "nothat"},
573 {'arches': ["thisarchdoesntexist_64"],
574 "uri": "nothateither"}]}
575
576 mirrors = apt_config.find_apt_mirror_info(cfg)
577
578 self.assertEqual(mirrors['MIRROR'],
579 pmir)
580 self.assertEqual(mirrors['PRIMARY'],
581 pmir)
582 self.assertEqual(mirrors['SECURITY'],
583 smir)
584
585 def test_mirror_search(self):
586 """test_mirror_search - Test searching mirrors in a list
587 mock checks to avoid relying on network connectivity"""
588 pmir = "http://us.archive.ubuntu.com/ubuntu/"
589 smir = "http://security.ubuntu.com/ubuntu/"
590 cfg = {"primary": [{'arches': ["default"],
591 "search": ["pfailme", pmir]}],
592 "security": [{'arches': ["default"],
593 "search": ["sfailme", smir]}]}
594
595 with mock.patch.object(apt_config, 'search_for_mirror',
596 side_effect=[pmir, smir]) as mocksearch:
597 mirrors = apt_config.find_apt_mirror_info(cfg)
598
599 calls = [call(["pfailme", pmir]),
600 call(["sfailme", smir])]
601 mocksearch.assert_has_calls(calls)
602
603 self.assertEqual(mirrors['MIRROR'],
604 pmir)
605 self.assertEqual(mirrors['PRIMARY'],
606 pmir)
607 self.assertEqual(mirrors['SECURITY'],
608 smir)
609
610 def test_mirror_search_dns(self):
611 """test_mirror_search_dns - Test searching dns patterns"""
612 pmir = "phit"
613 smir = "shit"
614 cfg = {"primary": [{'arches': ["default"],
615 "search_dns": True}],
616 "security": [{'arches': ["default"],
617 "search_dns": True}]}
618
619 with mock.patch.object(apt_config, 'get_mirror',
620 return_value="http://mocked/foo") as mockgm:
621 mirrors = apt_config.find_apt_mirror_info(cfg)
622 calls = [call(cfg, 'primary', util.get_architecture()),
623 call(cfg, 'security', util.get_architecture())]
624 mockgm.assert_has_calls(calls)
625
626 with mock.patch.object(apt_config, 'search_for_mirror_dns',
627 return_value="http://mocked/foo") as mocksdns:
628 mirrors = apt_config.find_apt_mirror_info(cfg)
629 calls = [call(True, 'mirror'),
630 call(True, 'security-mirror')]
631 mocksdns.assert_has_calls(calls)
632
633 # first return is for the non-dns call before
634 with mock.patch.object(apt_config, 'search_for_mirror',
635 side_effect=[None, pmir, None, smir]) as mockse:
636 with mock.patch.object(util, 'subp',
637 return_value=('host.sub.com', '')) as mocks:
638 mirrors = apt_config.find_apt_mirror_info(cfg)
639
640 calls = [call(None),
641 call(['http://ubuntu-mirror.sub.com/ubuntu',
642 'http://ubuntu-mirror.localdomain/ubuntu',
643 'http://ubuntu-mirror/ubuntu']),
644 call(None),
645 call(['http://ubuntu-security-mirror.sub.com/ubuntu',
646 'http://ubuntu-security-mirror.localdomain/ubuntu',
647 'http://ubuntu-security-mirror/ubuntu'])]
648 mockse.assert_has_calls(calls)
649 mocks.assert_called_with(['hostname', '--fqdn'], capture=True, rcs=[0])
650
651 self.assertEqual(mirrors['MIRROR'],
652 pmir)
653 self.assertEqual(mirrors['PRIMARY'],
654 pmir)
655 self.assertEqual(mirrors['SECURITY'],
656 smir)
657
658 def test_mirror_search_many3(self):
659 """test_mirror_search_many3 - Test all three mirrors specs at once"""
660 pmir = "http://us.archive.ubuntu.com/ubuntu/"
661 smir = "http://security.ubuntu.com/ubuntu/"
662 cfg = {"primary": [{'arches': ["default"],
663 "uri": pmir,
664 "search_dns": True,
665 "search": ["pfailme", "foo"]}],
666 "security": [{'arches': ["default"],
667 "uri": smir,
668 "search_dns": True,
669 "search": ["sfailme", "bar"]}]}
670
671 # should be called once per type, despite three configs each
672 with mock.patch.object(apt_config, 'get_mirror',
673 return_value="http://mocked/foo") as mockgm:
674 mirrors = apt_config.find_apt_mirror_info(cfg)
675 calls = [call(cfg, 'primary', util.get_architecture()),
676 call(cfg, 'security', util.get_architecture())]
677 mockgm.assert_has_calls(calls)
678
679 # should not be called, since primary is specified
680 with mock.patch.object(apt_config, 'search_for_mirror_dns') as mockdns:
681 mirrors = apt_config.find_apt_mirror_info(cfg)
682 mockdns.assert_not_called()
683
684 # should not be called, since primary is specified
685 with mock.patch.object(apt_config, 'search_for_mirror') as mockse:
686 mirrors = apt_config.find_apt_mirror_info(cfg)
687 mockse.assert_not_called()
688
689 self.assertEqual(mirrors['MIRROR'],
690 pmir)
691 self.assertEqual(mirrors['PRIMARY'],
692 pmir)
693 self.assertEqual(mirrors['SECURITY'],
694 smir)
695
696 def test_mirror_search_many2(self):
697 """test_mirror_search_many2 - Test the two search specs at once"""
698 pmir = "http://us.archive.ubuntu.com/ubuntu/"
699 smir = "http://security.ubuntu.com/ubuntu/"
700 cfg = {"primary": [{'arches': ["default"],
701 "search_dns": True,
702 "search": ["pfailme", pmir]}],
703 "security": [{'arches': ["default"],
704 "search_dns": True,
705 "search": ["sfailme", smir]}]}
706
707 # should be called once per type, despite three configs each
708 with mock.patch.object(apt_config, 'get_mirror',
709 return_value="http://mocked/foo") as mockgm:
710 mirrors = apt_config.find_apt_mirror_info(cfg)
711 calls = [call(cfg, 'primary', util.get_architecture()),
712 call(cfg, 'security', util.get_architecture())]
713 mockgm.assert_has_calls(calls)
714
715 # this should be the winner by priority, despite config order
716 with mock.patch.object(apt_config, 'search_for_mirror',
717 side_effect=[pmir, smir]) as mocksearch:
718 mirrors = apt_config.find_apt_mirror_info(cfg)
719 calls = [call(["pfailme", pmir]),
720 call(["sfailme", smir])]
721 mocksearch.assert_has_calls(calls)
722
723 self.assertEqual(mirrors['MIRROR'],
724 pmir)
725 self.assertEqual(mirrors['PRIMARY'],
726 pmir)
727 self.assertEqual(mirrors['SECURITY'],
728 smir)
729
730 def test_url_resolvable(self):
731 """test_url_resolvable - Test resolving urls"""
732
733 with mock.patch.object(util, 'is_resolvable') as mockresolve:
734 util.is_resolvable_url("http://1.2.3.4/ubuntu")
735 mockresolve.assert_called_with("1.2.3.4")
736
737 with mock.patch.object(util, 'is_resolvable') as mockresolve:
738 util.is_resolvable_url("http://us.archive.ubuntu.com/ubuntu")
739 mockresolve.assert_called_with("us.archive.ubuntu.com")
740
741 bad = [(None, None, None, "badname", ["10.3.2.1"])]
742 good = [(None, None, None, "goodname", ["10.2.3.4"])]
743 with mock.patch.object(socket, 'getaddrinfo',
744 side_effect=[bad, bad, good,
745 good]) as mocksock:
746 ret = util.is_resolvable_url("http://us.archive.ubuntu.com/ubuntu")
747 ret2 = util.is_resolvable_url("http://1.2.3.4/ubuntu")
748 calls = [call('does-not-exist.example.com.', None, 0, 0, 1, 2),
749 call('example.invalid.', None, 0, 0, 1, 2),
750 call('us.archive.ubuntu.com', None),
751 call('1.2.3.4', None)]
752 mocksock.assert_has_calls(calls)
753 self.assertTrue(ret)
754 self.assertTrue(ret2)
755
756 # side effect need only bad ret after initial call
757 with mock.patch.object(socket, 'getaddrinfo',
758 side_effect=[bad]) as mocksock:
759 ret3 = util.is_resolvable_url("http://failme.com/ubuntu")
760 calls = [call('failme.com', None)]
761 mocksock.assert_has_calls(calls)
762 self.assertFalse(ret3)
763
764 def test_disable_suites(self):
765 """test_disable_suites - disable_suites with many configurations"""
766 release = "xenial"
767 orig = """deb http://ubuntu.com//ubuntu xenial main
768deb http://ubuntu.com//ubuntu xenial-updates main
769deb http://ubuntu.com//ubuntu xenial-security main
770deb-src http://ubuntu.com//ubuntu universe multiverse
771deb http://ubuntu.com/ubuntu/ xenial-proposed main"""
772
773 # disable nothing
774 cfg = {"disable_suites": []}
775 expect = """deb http://ubuntu.com//ubuntu xenial main
776deb http://ubuntu.com//ubuntu xenial-updates main
777deb http://ubuntu.com//ubuntu xenial-security main
778deb-src http://ubuntu.com//ubuntu universe multiverse
779deb http://ubuntu.com/ubuntu/ xenial-proposed main"""
780 result = apt_config.disable_suites(cfg, orig, release)
781 self.assertEqual(expect, result)
782
783 # single disable release suite
784 cfg = {"disable_suites": ["$RELEASE"]}
785 expect = """\
786# suite disabled by curtin: deb http://ubuntu.com//ubuntu xenial main
787deb http://ubuntu.com//ubuntu xenial-updates main
788deb http://ubuntu.com//ubuntu xenial-security main
789deb-src http://ubuntu.com//ubuntu universe multiverse
790deb http://ubuntu.com/ubuntu/ xenial-proposed main"""
791 result = apt_config.disable_suites(cfg, orig, release)
792 self.assertEqual(expect, result)
793
794 # single disable other suite
795 cfg = {"disable_suites": ["$RELEASE-updates"]}
796 expect = """deb http://ubuntu.com//ubuntu xenial main
797# suite disabled by curtin: deb http://ubuntu.com//ubuntu xenial-updates main
798deb http://ubuntu.com//ubuntu xenial-security main
799deb-src http://ubuntu.com//ubuntu universe multiverse
800deb http://ubuntu.com/ubuntu/ xenial-proposed main"""
801 result = apt_config.disable_suites(cfg, orig, release)
802 self.assertEqual(expect, result)
803
804 # multi disable
805 cfg = {"disable_suites": ["$RELEASE-updates", "$RELEASE-security"]}
806 expect = """deb http://ubuntu.com//ubuntu xenial main
807# suite disabled by curtin: deb http://ubuntu.com//ubuntu xenial-updates main
808# suite disabled by curtin: deb http://ubuntu.com//ubuntu xenial-security main
809deb-src http://ubuntu.com//ubuntu universe multiverse
810deb http://ubuntu.com/ubuntu/ xenial-proposed main"""
811 result = apt_config.disable_suites(cfg, orig, release)
812 self.assertEqual(expect, result)
813
814 # multi line disable (same suite multiple times in input)
815 cfg = {"disable_suites": ["$RELEASE-updates", "$RELEASE-security"]}
816 orig = """deb http://ubuntu.com//ubuntu xenial main
817deb http://ubuntu.com//ubuntu xenial-updates main
818deb http://ubuntu.com//ubuntu xenial-security main
819deb-src http://ubuntu.com//ubuntu universe multiverse
820deb http://UBUNTU.com//ubuntu xenial-updates main
821deb http://UBUNTU.COM//ubuntu xenial-updates main
822deb http://ubuntu.com/ubuntu/ xenial-proposed main"""
823 expect = """deb http://ubuntu.com//ubuntu xenial main
824# suite disabled by curtin: deb http://ubuntu.com//ubuntu xenial-updates main
825# suite disabled by curtin: deb http://ubuntu.com//ubuntu xenial-security main
826deb-src http://ubuntu.com//ubuntu universe multiverse
827# suite disabled by curtin: deb http://UBUNTU.com//ubuntu xenial-updates main
828# suite disabled by curtin: deb http://UBUNTU.COM//ubuntu xenial-updates main
829deb http://ubuntu.com/ubuntu/ xenial-proposed main"""
830 result = apt_config.disable_suites(cfg, orig, release)
831 self.assertEqual(expect, result)
832
833 # comment in input
834 cfg = {"disable_suites": ["$RELEASE-updates", "$RELEASE-security"]}
835 orig = """deb http://ubuntu.com//ubuntu xenial main
836deb http://ubuntu.com//ubuntu xenial-updates main
837deb http://ubuntu.com//ubuntu xenial-security main
838deb-src http://ubuntu.com//ubuntu universe multiverse
839#foo
840#deb http://UBUNTU.com//ubuntu xenial-updates main
841deb http://UBUNTU.COM//ubuntu xenial-updates main
842deb http://ubuntu.com/ubuntu/ xenial-proposed main"""
843 expect = """deb http://ubuntu.com//ubuntu xenial main
844# suite disabled by curtin: deb http://ubuntu.com//ubuntu xenial-updates main
845# suite disabled by curtin: deb http://ubuntu.com//ubuntu xenial-security main
846deb-src http://ubuntu.com//ubuntu universe multiverse
847#foo
848#deb http://UBUNTU.com//ubuntu xenial-updates main
849# suite disabled by curtin: deb http://UBUNTU.COM//ubuntu xenial-updates main
850deb http://ubuntu.com/ubuntu/ xenial-proposed main"""
851 result = apt_config.disable_suites(cfg, orig, release)
852 self.assertEqual(expect, result)
853
854 # single disable custom suite
855 cfg = {"disable_suites": ["foobar"]}
856 orig = """deb http://ubuntu.com//ubuntu xenial main
857deb http://ubuntu.com//ubuntu xenial-updates main
858deb http://ubuntu.com//ubuntu xenial-security main
859deb http://ubuntu.com/ubuntu/ foobar main"""
860 expect = """deb http://ubuntu.com//ubuntu xenial main
861deb http://ubuntu.com//ubuntu xenial-updates main
862deb http://ubuntu.com//ubuntu xenial-security main
863# suite disabled by curtin: deb http://ubuntu.com/ubuntu/ foobar main"""
864 result = apt_config.disable_suites(cfg, orig, release)
865 self.assertEqual(expect, result)
866
867 # single disable non existing suite
868 cfg = {"disable_suites": ["foobar"]}
869 orig = """deb http://ubuntu.com//ubuntu xenial main
870deb http://ubuntu.com//ubuntu xenial-updates main
871deb http://ubuntu.com//ubuntu xenial-security main
872deb http://ubuntu.com/ubuntu/ notfoobar main"""
873 expect = """deb http://ubuntu.com//ubuntu xenial main
874deb http://ubuntu.com//ubuntu xenial-updates main
875deb http://ubuntu.com//ubuntu xenial-security main
876deb http://ubuntu.com/ubuntu/ notfoobar main"""
877 result = apt_config.disable_suites(cfg, orig, release)
878 self.assertEqual(expect, result)
879
880 # single disable suite with option
881 cfg = {"disable_suites": ["$RELEASE-updates"]}
882 orig = """deb http://ubuntu.com//ubuntu xenial main
883deb [a=b] http://ubu.com//ubu xenial-updates main
884deb http://ubuntu.com//ubuntu xenial-security main
885deb-src http://ubuntu.com//ubuntu universe multiverse
886deb http://ubuntu.com/ubuntu/ xenial-proposed main"""
887 expect = """deb http://ubuntu.com//ubuntu xenial main
888# suite disabled by curtin: deb [a=b] http://ubu.com//ubu xenial-updates main
889deb http://ubuntu.com//ubuntu xenial-security main
890deb-src http://ubuntu.com//ubuntu universe multiverse
891deb http://ubuntu.com/ubuntu/ xenial-proposed main"""
892 result = apt_config.disable_suites(cfg, orig, release)
893 self.assertEqual(expect, result)
894
895 # single disable suite with more options and auto $RELEASE expansion
896 cfg = {"disable_suites": ["updates"]}
897 orig = """deb http://ubuntu.com//ubuntu xenial main
898deb [a=b c=d] http://ubu.com//ubu xenial-updates main
899deb http://ubuntu.com//ubuntu xenial-security main
900deb-src http://ubuntu.com//ubuntu universe multiverse
901deb http://ubuntu.com/ubuntu/ xenial-proposed main"""
902 expect = """deb http://ubuntu.com//ubuntu xenial main
903# suite disabled by curtin: deb [a=b c=d] \
904http://ubu.com//ubu xenial-updates main
905deb http://ubuntu.com//ubuntu xenial-security main
906deb-src http://ubuntu.com//ubuntu universe multiverse
907deb http://ubuntu.com/ubuntu/ xenial-proposed main"""
908 result = apt_config.disable_suites(cfg, orig, release)
909 self.assertEqual(expect, result)
910
911 # single disable suite while options at others
912 cfg = {"disable_suites": ["$RELEASE-security"]}
913 orig = """deb http://ubuntu.com//ubuntu xenial main
914deb [arch=foo] http://ubuntu.com//ubuntu xenial-updates main
915deb http://ubuntu.com//ubuntu xenial-security main
916deb-src http://ubuntu.com//ubuntu universe multiverse
917deb http://ubuntu.com/ubuntu/ xenial-proposed main"""
918 expect = """deb http://ubuntu.com//ubuntu xenial main
919deb [arch=foo] http://ubuntu.com//ubuntu xenial-updates main
920# suite disabled by curtin: deb http://ubuntu.com//ubuntu xenial-security main
921deb-src http://ubuntu.com//ubuntu universe multiverse
922deb http://ubuntu.com/ubuntu/ xenial-proposed main"""
923 result = apt_config.disable_suites(cfg, orig, release)
924 self.assertEqual(expect, result)
925
926#
927# vi: ts=4 expandtab
0928
=== modified file 'tests/vmtests/__init__.py'
--- tests/vmtests/__init__.py 2016-07-19 19:25:08 +0000
+++ tests/vmtests/__init__.py 2016-07-26 19:53:43 +0000
@@ -355,6 +355,7 @@
355 extra_kern_args = None355 extra_kern_args = None
356 fstab_expected = {}356 fstab_expected = {}
357 image_store_class = ImageStore357 image_store_class = ImageStore
358 boot_cloudconf = None
358 install_timeout = 3000359 install_timeout = 3000
359 interactive = False360 interactive = False
360 multipath = False361 multipath = False
@@ -363,6 +364,7 @@
363 recorded_errors = 0364 recorded_errors = 0
364 recorded_failures = 0365 recorded_failures = 0
365 uefi = False366 uefi = False
367 proxy = None
366368
367 # these get set from base_vm_classes369 # these get set from base_vm_classes
368 release = None370 release = None
@@ -392,7 +394,8 @@
392 # set up tempdir394 # set up tempdir
393 cls.td = TempDir(395 cls.td = TempDir(
394 name=cls.__name__,396 name=cls.__name__,
395 user_data=generate_user_data(collect_scripts=cls.collect_scripts))397 user_data=generate_user_data(collect_scripts=cls.collect_scripts,
398 boot_cloudconf=cls.boot_cloudconf))
396 logger.info('Using tempdir: %s , Image: %s', cls.td.tmpdir,399 logger.info('Using tempdir: %s , Image: %s', cls.td.tmpdir,
397 img_verstr)400 img_verstr)
398 cls.install_log = os.path.join(cls.td.logs, 'install-serial.log')401 cls.install_log = os.path.join(cls.td.logs, 'install-serial.log')
@@ -492,11 +495,11 @@
492495
493 # proxy config496 # proxy config
494 configs = [cls.conf_file]497 configs = [cls.conf_file]
495 proxy = get_apt_proxy()498 cls.proxy = get_apt_proxy()
496 if get_apt_proxy is not None:499 if cls.proxy is not None:
497 proxy_config = os.path.join(cls.td.install, 'proxy.cfg')500 proxy_config = os.path.join(cls.td.install, 'proxy.cfg')
498 with open(proxy_config, "w") as fp:501 with open(proxy_config, "w") as fp:
499 fp.write(json.dumps({'apt_proxy': proxy}) + "\n")502 fp.write(json.dumps({'apt_proxy': cls.proxy}) + "\n")
500 configs.append(proxy_config)503 configs.append(proxy_config)
501504
502 uefi_flags = []505 uefi_flags = []
@@ -731,8 +734,14 @@
731 # Misc functions that are useful for many tests734 # Misc functions that are useful for many tests
732 def output_files_exist(self, files):735 def output_files_exist(self, files):
733 for f in files:736 for f in files:
737 logger.debug('checking file %s', f)
734 self.assertTrue(os.path.exists(os.path.join(self.td.collect, f)))738 self.assertTrue(os.path.exists(os.path.join(self.td.collect, f)))
735739
740 def output_files_dont_exist(self, files):
741 for f in files:
742 logger.debug('checking file %s', f)
743 self.assertFalse(os.path.exists(os.path.join(self.td.collect, f)))
744
736 def check_file_strippedline(self, filename, search):745 def check_file_strippedline(self, filename, search):
737 with open(os.path.join(self.td.collect, filename), "r") as fp:746 with open(os.path.join(self.td.collect, filename), "r") as fp:
738 data = list(i.strip() for i in fp.readlines())747 data = list(i.strip() for i in fp.readlines())
@@ -961,7 +970,8 @@
961 return None970 return None
962971
963972
964def generate_user_data(collect_scripts=None, apt_proxy=None):973def generate_user_data(collect_scripts=None, apt_proxy=None,
974 boot_cloudconf=None):
965 # this returns the user data for the *booted* system975 # this returns the user data for the *booted* system
966 # its a cloud-config-archive type, which is976 # its a cloud-config-archive type, which is
967 # just a list of parts. the 'x-shellscript' parts977 # just a list of parts. the 'x-shellscript' parts
@@ -986,6 +996,10 @@
986 'content': yaml.dump(base_cloudconfig, indent=1)},996 'content': yaml.dump(base_cloudconfig, indent=1)},
987 {'type': 'text/cloud-config', 'content': ssh_keys}]997 {'type': 'text/cloud-config', 'content': ssh_keys}]
988998
999 if boot_cloudconf is not None:
1000 parts.append({'type': 'text/cloud-config', 'content':
1001 yaml.dump(boot_cloudconf, indent=1)})
1002
989 output_dir = '/mnt/output'1003 output_dir = '/mnt/output'
990 output_dir_macro = 'OUTPUT_COLLECT_D'1004 output_dir_macro = 'OUTPUT_COLLECT_D'
991 output_device = '/dev/disk/by-id/virtio-%s' % OUTPUT_DISK_NAME1005 output_device = '/dev/disk/by-id/virtio-%s' % OUTPUT_DISK_NAME
9921006
=== added file 'tests/vmtests/test_apt_config_cmd.py'
--- tests/vmtests/test_apt_config_cmd.py 1970-01-01 00:00:00 +0000
+++ tests/vmtests/test_apt_config_cmd.py 2016-07-26 19:53:43 +0000
@@ -0,0 +1,55 @@
1""" test_apt_config_cmd
2 Collection of tests for the apt configuration features when called via the
3 apt-config standalone command.
4"""
5import textwrap
6
7from . import VMBaseClass
8from .releases import base_vm_classes as relbase
9
10
11class TestAptConfigCMD(VMBaseClass):
12 """TestAptConfigCMD - test standalone command"""
13 conf_file = "examples/tests/apt_config_command.yaml"
14 interactive = False
15 extra_disks = []
16 fstab_expected = {}
17 disk_to_check = []
18 collect_scripts = [textwrap.dedent("""
19 cd OUTPUT_COLLECT_D
20 cat /etc/fstab > fstab
21 ls /dev/disk/by-dname > ls_dname
22 find /etc/network/interfaces.d > find_interfacesd
23 cp /etc/apt/sources.list.d/curtin-dev-ubuntu-test-archive-*.list .
24 cp /etc/cloud/cloud.cfg.d/curtin-preserve-sources.cfg .
25 apt-cache policy | grep proposed > proposed-enabled
26 """)]
27
28 def test_cmd_proposed_enabled(self):
29 """check if proposed was enabled"""
30 self.output_files_exist(["proposed-enabled"])
31 self.check_file_regex("proposed-enabled",
32 r"500.*%s-proposed" % self.release)
33
34 def test_cmd_ppa_enabled(self):
35 """check if specified curtin-dev ppa was enabled"""
36 self.output_files_exist(
37 ["curtin-dev-ubuntu-test-archive-%s.list" % self.release])
38 self.check_file_regex("curtin-dev-ubuntu-test-archive-%s.list" %
39 self.release,
40 (r"http://ppa.launchpad.net/"
41 r"curtin-dev/test-archive/ubuntu"
42 r" %s main" % self.release))
43
44 def test_cmd_preserve_source(self):
45 """check if cloud-init was prevented from overwriting"""
46 self.output_files_exist(["curtin-preserve-sources.cfg"])
47 self.check_file_regex("curtin-preserve-sources.cfg",
48 "apt_preserve_sources_list.*true")
49
50
51class XenialTestAptConfigCMDCMD(relbase.xenial, TestAptConfigCMD):
52 """ XenialTestAptSrcModifyCMD
53 apt feature Test for Xenial using the standalone command
54 """
55 __test__ = True
056
=== added file 'tests/vmtests/test_apt_source.py'
--- tests/vmtests/test_apt_source.py 1970-01-01 00:00:00 +0000
+++ tests/vmtests/test_apt_source.py 2016-07-26 19:53:43 +0000
@@ -0,0 +1,277 @@
1""" test_apt_source
2 Collection of tests for the apt configuration features
3"""
4import textwrap
5
6from . import VMBaseClass
7from .releases import base_vm_classes as relbase
8
9from unittest import SkipTest
10from curtin import util
11
12
13class TestAptSrcAbs(VMBaseClass):
14 """TestAptSrcAbs - Basic tests for apt features of curtin"""
15 interactive = False
16 extra_disks = []
17 fstab_expected = {}
18 disk_to_check = []
19 collect_scripts = [textwrap.dedent("""
20 cd OUTPUT_COLLECT_D
21 cat /etc/fstab > fstab
22 ls /dev/disk/by-dname > ls_dname
23 find /etc/network/interfaces.d > find_interfacesd
24 apt-key list "F430BBA5" > keyid-F430BBA5
25 apt-key list "0165013E" > keyppa-0165013E
26 apt-key list "F470A0AC" > keylongid-F470A0AC
27 apt-key list "8280B242" > keyraw-8280B242
28 ls -laF /etc/apt/sources.list.d/ > sources.list.d
29 cp /etc/apt/sources.list.d/curtin-dev-ppa.list .
30 cp /etc/apt/sources.list.d/my-repo2.list .
31 cp /etc/apt/sources.list.d/my-repo4.list .
32 cp /etc/apt/sources.list.d/curtin-dev-ubuntu-test-archive-*.list .
33 find /etc/apt/sources.list.d/ -maxdepth 1 -name "*ignore*" | wc -l > ic
34 apt-config dump | grep Retries > aptconf
35 cp /etc/apt/sources.list sources.list
36 cp /etc/cloud/cloud.cfg.d/curtin-preserve-sources.cfg .
37 """)]
38 mirror = "http://us.archive.ubuntu.com/ubuntu"
39 secmirror = "http://security.ubuntu.com/ubuntu"
40
41 def test_output_files_exist(self):
42 """test_output_files_exist - Check if all output files exist"""
43 self.output_files_exist(
44 ["fstab", "ic", "keyid-F430BBA5", "keylongid-F470A0AC",
45 "keyraw-8280B242", "keyppa-0165013E", "aptconf", "sources.list",
46 "curtin-dev-ppa.list", "my-repo2.list", "my-repo4.list"])
47 self.output_files_exist(
48 ["curtin-dev-ubuntu-test-archive-%s.list" % self.release])
49
50 def test_keys_imported(self):
51 """test_keys_imported - Check if all keys are imported correctly"""
52 self.check_file_regex("keyid-F430BBA5",
53 r"Launchpad PPA for Ubuntu Screen Profile")
54 self.check_file_regex("keylongid-F470A0AC",
55 r"Ryan Harper")
56 self.check_file_regex("keyppa-0165013E",
57 r"Launchpad PPA for curtin developers")
58 self.check_file_regex("keyraw-8280B242",
59 r"Christian Ehrhardt")
60
61 def test_preserve_source(self):
62 """test_preserve_source - no clobbering sources.list by cloud-init"""
63 self.output_files_exist(["curtin-preserve-sources.cfg"])
64 self.check_file_regex("curtin-preserve-sources.cfg",
65 "apt_preserve_sources_list.*true")
66
67 def test_source_files(self):
68 """test_source_files - Check generated .lists for correct content"""
69 # hard coded deb lines
70 self.check_file_strippedline("curtin-dev-ppa.list",
71 ("deb http://ppa.launchpad.net/curtin-dev"
72 "/test-archive/ubuntu xenial main"))
73 self.check_file_strippedline("my-repo4.list",
74 ("deb http://ppa.launchpad.net/curtin-dev"
75 "/test-archive/ubuntu xenial main"))
76 # mirror and release replacement in deb line
77 self.check_file_strippedline("my-repo2.list", "deb %s %s multiverse" %
78 (self.mirror, self.release))
79 # auto creation by apt-add-repository
80 self.check_file_regex("curtin-dev-ubuntu-test-archive-%s.list" %
81 self.release,
82 (r"http://ppa.launchpad.net/"
83 r"curtin-dev/test-archive/ubuntu"
84 r" %s main" % self.release))
85
86 def test_ignore_count(self):
87 """test_ignore_count - Check for files that should not be created"""
88 self.check_file_strippedline("ic", "0")
89
90 def test_apt_conf(self):
91 """test_apt_conf - Check if the selected apt conf was set"""
92 self.check_file_strippedline("aptconf", 'Acquire::Retries "3";')
93
94
95class TestAptSrcCustom(TestAptSrcAbs):
96 """TestAptSrcNormal - tests valid in the custom sources.list case"""
97 conf_file = "examples/tests/apt_source_custom.yaml"
98
99 def test_custom_source_list(self):
100 """test_custom_source_list - Check custom sources with replacement"""
101 # check that all replacements happened
102 self.check_file_strippedline("sources.list",
103 "deb %s %s main restricted" %
104 (self.mirror, self.release))
105 self.check_file_strippedline("sources.list",
106 "deb-src %s %s main restricted" %
107 (self.mirror, self.release))
108 self.check_file_strippedline("sources.list",
109 "deb %s %s universe restricted" %
110 (self.mirror, self.release))
111 self.check_file_strippedline("sources.list",
112 "deb %s %s-security multiverse" %
113 (self.secmirror, self.release))
114 # check for something that guarantees us to come from our test
115 self.check_file_strippedline("sources.list",
116 "# nice line to check in test")
117
118
119class TestAptSrcPreserve(TestAptSrcAbs):
120 """TestAptSrcPreserve - tests valid in the preserved sources.list case"""
121 conf_file = "examples/tests/apt_source_preserve.yaml"
122 boot_cloudconf = None
123
124 def test_preserved_source_list(self):
125 """test_preserved_source_list - Check sources to be preserved as-is"""
126 # curtin didn't touch it, so we should find what curtin set as default
127 self.check_file_regex("sources.list",
128 r"this file is written by cloud-init")
129
130 # overwrite inherited check to match situation here
131 def test_preserve_source(self):
132 """test_preserve_source - check apt_preserve_sources_list not set"""
133 self.output_files_dont_exist(["curtin-preserve-sources.cfg"])
134
135
136class TestAptSrcModify(TestAptSrcAbs):
137 """TestAptSrcModify - tests modifying sources.list"""
138 conf_file = "examples/tests/apt_source_modify.yaml"
139
140 def test_modified_source_list(self):
141 """test_modified_source_list - Check sources with replacement"""
142 # we set us.archive which is non default, check for that
143 # this will catch if a target ever changes the expected defaults we
144 # have to replace in case there is no custom template
145 self.check_file_regex("sources.list",
146 r"us.archive.ubuntu.com")
147 self.check_file_regex("sources.list",
148 r"security.ubuntu.com")
149
150
151class TestAptSrcDisablePockets(TestAptSrcAbs):
152 """TestAptSrcDisablePockets - tests disabling a suite in sources.list"""
153 conf_file = "examples/tests/apt_source_modify_disable_suite.yaml"
154
155 def test_disabled_suite(self):
156 """test_disabled_suite - Check if suites were disabled"""
157 # two not disabled
158 self.check_file_regex("sources.list",
159 r"deb.*us.archive.ubuntu.com")
160 self.check_file_regex("sources.list",
161 r"deb.*security.ubuntu.com")
162 # updates disabled
163 self.check_file_regex("sources.list",
164 r"# suite disabled by curtin:.*-updates")
165
166
167class TestAptSrcModifyArches(TestAptSrcModify):
168 """TestAptSrcModify - tests modifying sources.list with per arch mirror"""
169 # same test, just different yaml to specify the mirrors per arch
170 conf_file = "examples/tests/apt_source_modify_arches.yaml"
171
172
173class TestAptSrcSearch(TestAptSrcAbs):
174 """TestAptSrcSearch - tests checking a list of mirror options"""
175 conf_file = "examples/tests/apt_source_search.yaml"
176
177 def test_mirror_search(self):
178 """test_mirror_search
179 Check searching through a mirror list
180 This is checked in the test (late) intentionally.
181 No matter if resolution worked or failed it shouldn't fail
182 fatally (python error and trace).
183 We just can't rely on the content to be found in that case
184 so we skip the check then."""
185 res1 = util.is_resolvable_url("http://does.not.exist/ubuntu")
186 res2 = util.is_resolvable_url("http://does.also.not.exist/ubuntu")
187 res3 = util.is_resolvable_url("http://us.archive.ubuntu.com/ubuntu")
188 res4 = util.is_resolvable_url("http://security.ubuntu.com/ubuntu")
189 if res1 or res2 or not res3 or not res4:
190 raise SkipTest(("Name resolution not as required"
191 "(%s, %s, %s, %s)" % (res1, res2, res3, res4)))
192
193 self.check_file_regex("sources.list",
194 r"us.archive.ubuntu.com")
195 self.check_file_regex("sources.list",
196 r"security.ubuntu.com")
197
198
199class TestAptSrcSearchDNS(VMBaseClass):
200 """TestAptSrcSearchDNS - tests checking for predefined DNS names"""
201 interactive = False
202 extra_disks = []
203 fstab_expected = {}
204 conf_file = "examples/tests/apt_source_search_dns.yaml"
205 disk_to_check = []
206 collect_scripts = [textwrap.dedent("""
207 cd OUTPUT_COLLECT_D
208 cat /etc/fstab > fstab
209 ls /dev/disk/by-dname > ls_dname
210 find /etc/network/interfaces.d > find_interfacesd
211 cp /etc/apt/sources.list.d/dnssearch.list.disabled .
212 """)]
213
214 def test_output_files_exist(self):
215 """test_output_files_exist - Check if all output files exist"""
216 self.output_files_exist(["fstab", "dnssearch.list.disabled"])
217
218 def test_mirror_search_dns(self):
219 """test_mirror_search_dns - tests checking for predefined DNS names"""
220 # these should be the first it got resolved, so they should be in the
221 # sources.list file. We want to see that .lcoaldomain was not picked
222 # but instead what we added to the temp /etc/hosts
223 self.check_file_regex("dnssearch.list.disabled",
224 r"ubuntu-mirror/ubuntu.*multiverse")
225 self.check_file_regex("dnssearch.list.disabled",
226 r"ubuntu-mirror/ubuntu.*universe")
227 self.check_file_regex("dnssearch.list.disabled",
228 r"ubuntu-security-mirror/ubuntu.*main")
229
230
231class XenialTestAptSrcCustom(relbase.xenial, TestAptSrcCustom):
232 """ XenialTestAptSrcCustom
233 apt feature Test for Xenial with a custom template
234 """
235 __test__ = True
236
237
238class XenialTestAptSrcPreserve(relbase.xenial, TestAptSrcPreserve):
239 """ XenialTestAptSrcPreserve
240 apt feature Test for Xenial with apt_preserve_sources_list enabled
241 """
242 __test__ = True
243
244
245class XenialTestAptSrcModify(relbase.xenial, TestAptSrcModify):
246 """ XenialTestAptSrcModify
247 apt feature Test for Xenial modifying the sources.list of the image
248 """
249 __test__ = True
250
251
252class XenialTestAptSrcSearch(relbase.xenial, TestAptSrcSearch):
253 """ XenialTestAptSrcModify
254 apt feature Test for Xenial searching for mirrors
255 """
256 __test__ = True
257
258
259class XenialTestAptSrcSearchDNS(relbase.xenial, TestAptSrcSearchDNS):
260 """ XenialTestAptSrcModify
261 apt feature Test for Xenial searching for predefined DNS names
262 """
263 __test__ = True
264
265
266class XenialTestAptSrcModifyArches(relbase.xenial, TestAptSrcModifyArches):
267 """ XenialTestAptSrcModifyArches
268 apt feature Test for Xenial checking per arch mirror specification
269 """
270 __test__ = True
271
272
273class XenialTestAptSrcDisablePockets(relbase.xenial, TestAptSrcDisablePockets):
274 """ XenialTestAptSrcDisablePockets
275 apt feature Test for Xenial disabling a suite
276 """
277 __test__ = True
0278
=== added file 'tests/vmtests/test_old_apt_features.py'
--- tests/vmtests/test_old_apt_features.py 1970-01-01 00:00:00 +0000
+++ tests/vmtests/test_old_apt_features.py 2016-07-26 19:53:43 +0000
@@ -0,0 +1,80 @@
1""" testold_apt_features
2 Testing the former minimal apt features of curtin
3"""
4import re
5import textwrap
6
7from . import VMBaseClass
8from .releases import base_vm_classes as relbase
9
10from curtin import util
11
12
13class TestOldAptAbs(VMBaseClass):
14 """TestOldAptAbs - Basic tests for old apt features of curtin"""
15 interactive = False
16 extra_disks = []
17 fstab_expected = {}
18 disk_to_check = []
19 collect_scripts = [textwrap.dedent("""
20 cd OUTPUT_COLLECT_D
21 cat /etc/fstab > fstab
22 ls /dev/disk/by-dname > ls_dname
23 find /etc/network/interfaces.d > find_interfacesd
24 grep -A 3 "Name: debconf/priority" /var/cache/debconf/config.dat > debc
25 apt-config dump > aptconf
26 cp /etc/apt/apt.conf.d/90curtin-aptproxy .
27 cp /etc/apt/sources.list .
28 cp /etc/cloud/cloud.cfg.d/curtin-preserve-sources.cfg .
29 """)]
30 arch = util.get_architecture()
31 if arch in ['amd64', 'i386']:
32 conf_file = "examples/tests/test_old_apt_features.yaml"
33 exp_mirror = "http://us.archive.ubuntu.com/ubuntu"
34 exp_secmirror = "http://archive.ubuntu.com/ubuntu"
35 if arch in ['s390x', 'arm64', 'armhf', 'powerpc', 'ppc64el']:
36 conf_file = "examples/tests/test_old_apt_features_ports.yaml"
37 exp_mirror = "http://ports.ubuntu.com/ubuntu-ports"
38 exp_secmirror = "http://ports.ubuntu.com/ubuntu-ports"
39
40 def test_output_files_exist(self):
41 """test_output_files_exist - Check if all output files exist"""
42 self.output_files_exist(
43 ["debc", "aptconf", "sources.list", "90curtin-aptproxy",
44 "curtin-preserve-sources.cfg"])
45
46 def test_preserve_source(self):
47 """test_preserve_source - no clobbering sources.list by cloud-init"""
48 self.check_file_regex("curtin-preserve-sources.cfg",
49 "apt_preserve_sources_list.*true")
50
51 def test_debconf(self):
52 """test_debconf - Check if debconf is in place"""
53 self.check_file_strippedline("debc", "Value: low")
54
55 def test_aptconf(self):
56 """test_aptconf - Check if apt conf for proxy is in place"""
57 # this gets configured by tools/launch and get_apt_proxy in
58 # tests/vmtests/__init__.py, so compare with those
59 rproxy = r"Acquire::http::Proxy \"" + re.escape(self.proxy) + r"\";"
60 self.check_file_regex("aptconf", rproxy)
61 self.check_file_regex("90curtin-aptproxy", rproxy)
62
63 def test_mirrors(self):
64 """test_mirrors - Check for mirrors placed in source.list"""
65
66 self.check_file_strippedline("sources.list",
67 "deb %s %s" %
68 (self.exp_mirror, self.release) +
69 " main restricted universe multiverse")
70 self.check_file_strippedline("sources.list",
71 "deb %s %s-security" %
72 (self.exp_secmirror, self.release) +
73 " main restricted universe multiverse")
74
75
76class XenialTestOldApt(relbase.xenial, TestOldAptAbs):
77 """ XenialTestOldApt
78 Old apt features for Xenial
79 """
80 __test__ = True

Subscribers

People subscribed via source and target branches