Merge lp:~mvo/launchpad/support-timeframe-fix-660433 into lp:launchpad/db-devel

Proposed by Michael Vogt
Status: Merged
Merge reported by: Gavin Panella
Merged at revision: not available
Proposed branch: lp:~mvo/launchpad/support-timeframe-fix-660433
Merge into: lp:launchpad/db-devel
Diff against target: 767 lines (+399/-67)
6 files modified
cronscripts/publishing/cron.germinate (+25/-9)
cronscripts/publishing/maintenance-check.py (+104/-58)
lib/lp/soyuz/scripts/tests/germinate-test-data/mock-bin/germinate (+5/-0)
lib/lp/soyuz/scripts/tests/germinate-test-data/mock-bin/lockfile (+8/-0)
lib/lp/soyuz/scripts/tests/germinate-test-data/mock-lp-root/scripts/ftpmaster-tools/lp-query-distro.py (+27/-0)
lib/lp/soyuz/scripts/tests/test_cron_germinate.py (+230/-0)
To merge this branch: bzr merge lp:~mvo/launchpad/support-timeframe-fix-660433
Reviewer Review Type Date Requested Status
Michael Vogt (community) Needs Resubmitting
Jeroen T. Vermeulen (community) code Approve
Colin Watson Pending
Review via email: mp+38503@code.launchpad.net

Commit message

Update support-timeframe info for NEW packages.

Description of the change

Hello,

this branch fixes the soyuz bug #660433. It will re-generate the support-timeframe information (Supported tag in more-extras.overrides) for all supported distros to update it for packages like new kernels that get added as NEW packages after the final release.

I added a simple test for the functionality. I'm not sure it follows the guidelines, there was no tests/ directory under cronscripts/publishing (and the test is done with sh).

Its also prepares mock-data (./mock-data/ubuntu-archive/ubuntu) for testing germinate integration. This is not used (yet?), so that part of the tree could be removed (this would make the diff less cluttered).

Someone with soyuz experience (and knowledge about e.g. the content of "crontab -l") needs to review this carefully as it will affect the extra-overrides file from stable distros and therefore modify stuff in $distro-updates.

Cheers,
 Michael

To post a comment you must log in.
Revision history for this message
Michael Vogt (mvo) wrote :

It also modifies maintenance-check.py to put information only for lucid and later. While the script will work for hardy as well it does not make sense to generate "Supported" info in hardy-updates for it when there is none for the main archive.

Revision history for this message
Robert Collins (lifeless) wrote :

That sample data will add to the tree size and isn't necessary or
desirable - could you please uncommit back to before you added it and
resubmit? Thanks.

Binary files in bzr is a bad idea, and anytime it seems like a good
one... its not :)

-Rob

Revision history for this message
Michael Vogt (mvo) wrote :

Hey Robert, thanks for your review. I'm happy to remove the data. Please note that its empty gz files currently, so the data added to bzr should be small. But I'm fine with removing it as its currently not required (something small binray (one or two stanzas will be required when we test the germinate output though, so advise is welcome how to handle this).

Revision history for this message
Robert Collins (lifeless) wrote :

On Fri, Oct 15, 2010 at 8:57 PM, Michael Vogt <email address hidden> wrote:
> Hey Robert, thanks for your review. I'm happy to remove the data. Please note that its empty gz files currently, so the data added to bzr should be small. But I'm fine with removing it as its currently not required (something small binray (one or two stanzas will be required when we test the germinate output though, so advise is welcome how to handle this).

We already have such data elsewhere in the tree, or we can generate it
on demand.

We're currently working on parallel testing too, which means that
tests *cannot* depend on mutating data in the tree.

(and yes, you'll need to have it be a python unittest).

-Rob

9888. By Michael Vogt

remove all binary data from the tree and generate it on the fly

9889. By Michael Vogt

cronscripts/publishing/tests/test_cron.germinate.py: convert test to python

9890. By Michael Vogt

cronscripts/publishing/tests/test_cron.germinate: remove shell script test

Revision history for this message
Michael Vogt (mvo) wrote :

I generate the data on the fly now and converted the shell to proper unittest. Please have another look.

9891. By Michael Vogt

clear mock ubuntu/dists data too

Revision history for this message
Jeroen T. Vermeulen (jtv) wrote :
Download full text (14.5 KiB)

Hi Michael,

Thanks for the branch. I'm ecstatic to see someone work on these scripts, especially with tests attached; but a bit uncomfortable reviewing the changes. Most of the existing code seems absolutely ancient and ill-matched to today's source tree. I'll resist the temptation to say "while you're here, could you also clean up this bit of other code" a lot. :-)

Julian is still looking at the testing and design sides, but I'll review the code on the assumption that that will come out okay. Lots of notes though. I hope you won't be put off by them!

=== modified file 'cronscripts/publishing/cron.germinate'
--- cronscripts/publishing/cron.germinate 2010-09-06 09:46:01 +0000
+++ cronscripts/publishing/cron.germinate 2010-10-15 12:12:43 +0000

@@ -126,10 +125,19 @@
 done
 echo " done."

-# now generate the Supported extra overrides
-$MAINTAINCE_CHECK $suite > "$MISCROOT/more-extra.override.$suite.main.supported" 2> _maintenance-check.stderr
-if [ $? -eq 0 ]; then
- cat "$MISCROOT/more-extra.override.$suite.main.supported" >> "$MISCROOT/more-extra.override.$suite.main.new"
-fi
-
 mv -f "$MISCROOT/more-extra.override.$suite.main.new" "$MISCROOT/more-extra.override.$suite.main"
+
+# now generate the Supported extra overrides for all supported distros
+for supported_suite in `$LAUNCHPADROOT/scripts/ftpmaster-tools/lp-query-distro.py supported`; do

The lines in this script get pretty long. Could you place the "do" on a separate line?

for supported_suite in `$LAUNCHPADROOT/scripts/ftpmaster-tools/lp-query-distro.py supported`
do
    # ...
done

+ echo -n "Running maintenance-check for $supported_suite... "
+ if $MAINTAINCE_CHECK $supported_suite > "$MISCROOT/more-extra.override.$supported_suite.main.supported" 2> _maintenance-check.$supported_suite.stderr; then

And this one is now definitely too long! It'd help if these enormous filenames were in sensibly-named variables. Makes it easier to get an overview of what's happening.

+ # make sure the more-extra.override file exists
+ touch "$MISCROOT/more-extra.override.$supported_suite.main"

This comment just re-words the code. Instead, could you make it say very briefly _why_ it does this?

(Also, contrary to what you see in these scripts, please start your sentences with capital letters and punctuate them!).

+ # remove old "Supporte" info from extra overrides

Typo: missing "d." Also, please capitalize & punctuate.

+ sed /"^.* Supported"/d "$MISCROOT/more-extra.override.$supported_suite.main" > "$MISCROOT/more-extra.override.$supported_suite.main.new"
+ cat "$MISCROOT/more-extra.override.$supported_suite.main.supported" >> "$MISCROOT/more-extra.override.$supported_suite.main.new"
+ mv "$MISCROOT/more-extra.override.$supported_suite.main.new" "$MISCROOT/more-extra.override.$supported_suite.main"
+ fi
+ echo " done"
+done

Does this piece of the script want to live in its own function?

=== modified file 'cronscripts/publishing/maintenance-check.py'
--- cronscripts/publishing/maintenance-check.py 2010-05-28 16:17:51 +0000
+++ cronscripts/publishing/maintenance-check.py 2010-10-15 12:12:43 +0000

@@ -297,7 +297,11 @@
    ...

review: Needs Fixing
9892. By Michael Vogt

cronscripts/publishing/cron.germinate: address merge review issues raised by jtv

9893. By Michael Vogt

cronscripts/publishing/maintenance-check.py: fix missing capitalization and punctation and kill off whitespace

9894. By Michael Vogt

cronscripts/publishing/tests/mock-data/mock-bin/germinate: add comment in the script header

9895. By Michael Vogt

cronscripts/publishing/tests/mock-data/mock-lp-root/scripts/ftpmaster-tools/lp-query-distro.py: update to include comment and error checking and a else claue

9896. By Michael Vogt

cronscripts/publishing/tests/test_cron.germinate.py: address a lot of the review points, punctation, test template, variable naming, comments and docstrings (more to come)

Revision history for this message
Martin Pool (mbp) wrote :

Thanks for reviewing it, Jeroen. I want to contribute a meta-review.

First off, thanks for reviewing this quickly and expressing enthusiasm for what mvo did. Those two things make a big difference to the speed at which anyone can contribute and they're especially important for new contributors.

> + gz = gzip.GzipFile(os.path.join(d, "Sources.gz"), "w")
> + gz.close()
>
> Here too it would be nice to have a helper: "create gzip file."
>
>
> + # binaries
>
> Cap & Punct.
>
>
> + for arch in self.ARCHES:
> + for p in ["", "debian-installer"]:
>
> Please replace "p" with a full variable name.
>
>
> + d = os.path.join(
>
> Same for "d."

This kind of review comment I think is not so good, for a couple of reasons.

It's bad scaling behaviour to manually comment on all N instances of a problem, when a single comment would cover all of them. It seems to me that, like in my recent contribution, the problem is not that mvo made some particular mistakes, but that he just didn't know of some Launchpad conventions. I think it would be better to have a shorter review with the coding standard URL and pointing out the particular bits that are relevant. It will be quicker for you too.

I don't know how much mvo has participated in lp development before but I'm guessing not that much. So he probably won't know he ought to run 'make lint', or the quirk that it only covers currently uncommitted code.
However when somebody is putting up an early patch that violates these pretty minor rules, I wonder how much it is worth getting them to go back and eg remove whitespace. istm it is better to build up more knowledge of conventions in their head, and more enthusiasm for the project. Again, probably better scaling behaviour.

Saying "I don't understand this, please add a comment" or "you don't need this" is of course fine.

my 2c.

Revision history for this message
Jeroen T. Vermeulen (jtv) wrote :

Martin, you make a good point. I was actually just trying to be helpful: since I'm "proof-reading" the entire diff anyway, there's no need for Michael to go over the whole thing to find each individual instance that needs changing. I just noted them consistently, as is common practice in Launchpad reviews.

I'm used to this practice and don't feel in any way accused when a reviewer does it for me (or accusatory when I do it for someone else). But now that you mention it, I suppose it does need an explanation for new contributors.

Revision history for this message
Jeroen T. Vermeulen (jtv) wrote :

The starting point for the ins and outs of writing Launchpad branches, by the way, is here: https://dev.launchpad.net/LaunchpadHackingFAQ

Unfortunately there's rather a lot of it. The actual coding rules is in pages linked from there, notably the Python style guide.

Revision history for this message
Michael Vogt (mvo) wrote :

Thanks for the review Jeroen and thanks for the meta-review from Martin as well!

I update the code to adress a lot of the points, but there is more to be done. I'm happy
about the detailed review as it means I can just go over it point by point. The result
is that the code is now better and more readable. Martin is right, I don't usually do LP
development and I'm not familiar with the processes. I just want to fix this bug as it
affects the distro (badly) :)

I do not nessecarily agree with all of the points in the review (e.g. I think short variable
like "d" are fine if they are just used in a two lines or a short function). But that is fine, its more important that its consistent with the rest of the codebase.

The only "grief" I have is the testing template. To test a small part of LP like this
that does not need the database or other fancy part using the template drags in a huge
set of python dependencies, including psycopg2, storm, transactions, windmill
(that appears to be not packaged in ubuntu maverick) and more. It would be great to be
able to use the test template without all this if its not actually needed for the test
at hand.

Revision history for this message
Martin Pool (mbp) wrote :

On 21 October 2010 15:28, Jeroen T. Vermeulen <email address hidden> wrote:
> Martin, you make a good point.  I was actually just trying to be helpful: since I'm "proof-reading" the entire diff anyway, there's no need for Michael to go over the whole thing to find each individual instance that needs changing.  I just noted them consistently, as is common practice in Launchpad reviews.
>
> I'm used to this practice and don't feel in any way accused when a reviewer does it for me (or accusatory when I do it for someone else).  But now that you mention it, I suppose it does need an explanation for new contributors.

I don't take it as accusatory at all. There are probably some things
we could do in lp reviews to help with this - it would be neat if your
editor could jump through a list of review comments with the lines
they correspond to, or perhaps if a reviewer could more easily get in
and edit the branch...

--
Martin

9897. By Michael Vogt

cronscripts/publishing/tests/test_cron.germinate.py: refactor to create fake ubuntu archive root as a tempdir and add new helper create_directory_if_missing

9898. By Michael Vogt

cronscripts/publishing/tests/test_cron.germinate.py: move creating the germinate process environment into its own function, add gzip helper, add directory lists helper

9899. By Michael Vogt

cronscripts/publishing/tests/test_cron.germinate.py: add helper create_file()

Revision history for this message
Michael Vogt (mvo) wrote :

Beside the following two open issues I think I fixed all of the things mentioned.
Open is:

I don't use from lp.testing import TestCase because I could not make it work on my machine.
Adding the launchpad PPA and installing the launchpad-dependencies did not help.

I either get:
 ImportError: No module named windmill.bin.admin_lib (when importing canonical.testing.layers)
or
 ImportError: No module named fixtures (when importing from lp.testing import TestCase only)
I guess I do something wrong but I don't know what.

The other one is that I did not change the directory layout yet (where the tests lives).

Revision history for this message
Colin Watson (cjwatson) wrote :

I don't expect problems here from a deployment point of view; obviously (say) dists/lucid/ won't be changed, as opposed to dists/lucid-updates/, but that's OK. I trust you're aware that this means that it's only possible to generate updated Supported fields for packages that have been changed or added in lucid-updates.

One thing that jumped out at me, although I realise it was in the pre-existing code too:

- if distro[0] < 'h':
- print "ERROR: only hardy or later is supported"
+ if distro[0] < 'l':
+ logging.error("only lucid or later is supported")

We're only six years or so away from wrapping around the end of the alphabet. Perhaps this needs to be more explicit somehow? Of course, having to list all historical release names here isn't particularly great either ...

Revision history for this message
Michael Vogt (mvo) wrote :

Hello Colin, thanks for this feedback. The generated Suported fields will indeed be only be updated for changed or added Packages. But that is ok, before lucid-final we reviewed the data to made sure that it is sane. The open issue is that newly published kernels to not get the right Supported field currently and that makes ubuntu-support-status print out wrong data for those.

The wrapping is a good point, I just added the relevant historic names to make it explicit.

9900. By Michael Vogt

cronscripts/publishing/tests/mock-data/mock-lp-root/scripts/ftpmaster-tools/lp-query-distro.py: return more supported distros to test filter in maintenance-check.py

9901. By Michael Vogt

cronscripts/publishing/maintenance-check.py: make unsupported release names explicit to avoid future problems when the codenames wrap around (thanks to Colin!)

Revision history for this message
Julian Edwards (julian-edwards) wrote :

167 +# mvo: I would love to use this, but it appears doing this import
168 +# requires python-psycopg2, python-storm, python-transaction,
169 +# python-lazr.restful and now "windmill" that is not packaged
170 +# at this point I give up and let someone else with a proper
171 +# LP environment fix this
172 +#from canonical.testing.layers import DatabaseFunctionalLayer

In an ideal world every test would not use any layers because it's massively quicker to run the tests. However, I can't see any reason that your tests need layers so I think it's fine to remove this commented block entirely.

9902. By Michael Vogt

cronscripts/publishing/tests/test_cron.germinate.py: remove (commented out) import for canonical.testing.layers, use abspath() for BASEPATH to ensure it works correctly when run from different dirs

Revision history for this message
Michael Vogt (mvo) wrote :

Thanks Julian, I removed that part of the diff now. I still can not use from lp.testing import TestCase for some reason.

Revision history for this message
Julian Edwards (julian-edwards) wrote :

I'd personally be happy with you using the unittest one if it means you can run your own tests.

Revision history for this message
Jeroen T. Vermeulen (jtv) wrote :

With apologies for the delays—holiday and crisis, yin and yang—I'm just coming back to this branch. I hope I can help with this problem:

> Beside the following two open issues I think I fixed all of the things
> mentioned.
> Open is:
>
> I don't use from lp.testing import TestCase because I could not make it work
> on my machine.
> Adding the launchpad PPA and installing the launchpad-dependencies did not
> help.
>
> I either get:
> ImportError: No module named windmill.bin.admin_lib (when importing
> canonical.testing.layers)
> or
> ImportError: No module named fixtures (when importing from lp.testing import
> TestCase only)
> I guess I do something wrong but I don't know what.

These are external modules that have recently been updated. Try running rocketfuel-get (or if that doesn't do the trick, rocketfuel-setup). Then, in your branch, run
{{{
. ~/.rocketfuel-env.sh
utilities/link-external-sourcecode "$LP_TRUNK_NAME"
make
}}}

That should update your branch to use the latest external modules. You need to re-run rocketfuel-get from time to time to keep them updated. Sometimes you also need to merge in newer versions of your parent branch.

Revision history for this message
Jeroen T. Vermeulen (jtv) wrote :
Download full text (13.6 KiB)

Thanks for all the work you did on this! Is it just me or does the diff look a lot shorter now, despite having the new bits code? It certainly seems easier to read.

=== modified file 'cronscripts/publishing/cron.germinate'
--- cronscripts/publishing/cron.germinate 2010-09-06 09:46:01 +0000
+++ cronscripts/publishing/cron.germinate 2010-10-21 19:06:53 +0000

+# Now generate the Supported extra overrides for all supported distros.
+SUITES=`$LAUNCHPADROOT/scripts/ftpmaster-tools/lp-query-distro.py supported`
+for supported_suite in $SUITES; do
+ echo -n "Running maintenance-check for $supported_suite... "
+ # The support timeframe information is stored here
+ SUPPORTED="$MISCROOT/more-extra.override.$supported_suite.main.supported"
+ # This is the target override file that contains germinate plus support info
+ TARGET="$MISCROOT/more-extra.override.$supported_suite.main"
+ # Debug/Log information
+ LOG="_maintenance-check.$supported_suite.stderr"
+ if $MAINTAINCE_CHECK $supported_suite > $SUPPORTED 2> $LOG; then
+ # The target file may be missing on the server and the script should
+ # not fail if that is the case.
+ touch $TARGET
+ # Remove old "Supported" info from extra-overrides as it may be
+ # stale now and we replace it with fresh content below.
+ sed /"^.* Supported"/d $TARGET > ${TARGET}.new
+ cat $SUPPORTED >> ${TARGET}.new
+ mv ${TARGET}.new $TARGET
+ fi
+ echo " done"
+done

This looks nice and clear.

=== modified file 'cronscripts/publishing/maintenance-check.py'
--- cronscripts/publishing/maintenance-check.py 2010-05-28 16:17:51 +0000
+++ cronscripts/publishing/maintenance-check.py 2010-10-21 19:06:53 +0000

+# Names of the distribution releases that are not supported by this
+# tool. All later versions are supported.
+UNSUPPORTED_DISTRO_RELEASED = ["dapper", "edgy", "feisty", "gutsy", "hardy",
+ "intrepid", "jaunty", "karmic"]

A small thing: our coding standards say this should be formatted as

UNSUPPORTED_DISTRO_RELEASED = [
    "dapper",
    "edgy",
    "feisty",
    "gutsy",
    "hardy",
    "intrepid",
    "jaunty",
    "karmic",
    ]

(Notice the trailing comma even on the last line.)

@@ -275,8 +282,8 @@

- if distro[0] < 'h':
- print "ERROR: only hardy or later is supported"
+ if distro in UNSUPPORTED_DISTRO_RELEASED:
+ logging.error("only lucid or later is supported")

Much nicer, yes!

=== added directory 'cronscripts/publishing/tests'
=== added directory 'cronscripts/publishing/tests/mock-data'
=== added directory 'cronscripts/publishing/tests/mock-data/mock-bin'
=== added file 'cronscripts/publishing/tests/mock-data/mock-bin/germinate'
--- cronscripts/publishing/tests/mock-data/mock-bin/germinate 1970-01-01 00:00:00 +0000
+++ cronscripts/publishing/tests/mock-data/mock-bin/germinate 2010-10-21 19:06:53 +0000
@@ -0,0 +1,5 @@
+#!/bin/sh
+#
+# This is a mock germinate script that just produces enough (empty)
+# files so that the cron.germinate shell script can run.
+touch structure all all.sources minimal standard

Perfect. This tells me exactly what I'm looki...

review: Approve
Revision history for this message
Jeroen T. Vermeulen (jtv) wrote :

(Setting review type to "code," because the ec2 landing script requires an Approved vote of that type.)

review: Approve (code)
9903. By Michael Vogt

cronscripts/publishing/tests/mock-data/mock-lp-root/scripts/ftpmaster-tools/lp-query-distro.py: fix pocketlint errors (indent, spacing)

9904. By Michael Vogt

cronscripts/publishing/maintenance-check.py: fix pocketlint errors (spacing, indent, linewrap)

9905. By Michael Vogt

cronscripts/publishing/tests/test_cron.germinate.py: fix pocketlint errors (spacing, indent, linewrap)

9906. By Michael Vogt

cronscripts/publishing/tests/test_cron.germinate.py: updated to follow LP coding standards

9907. By Michael Vogt

cronscripts/publishing/tests/test_cron.germinate.py, cronscripts/publishing/maintenance-check.py: add SPACE around "=" to follow LP coding standard

9908. By Michael Vogt

cronscripts/publishing/tests/test_cron.germinate.py: adress formating issues and assert messages

Revision history for this message
Michael Vogt (mvo) wrote :
Download full text (8.9 KiB)

On Wed, Oct 27, 2010 at 10:46:02AM -0000, Jeroen T. Vermeulen wrote:

> Thanks for all the work you did on this! Is it just me or does the
> diff look a lot shorter now, despite having the new bits code? It
> certainly seems easier to read.

Thanks for the detailed review and sorry for the slow reply, the UDS
was taking all my attention. Its indeed much nicer now.

[..]
> === modified file 'cronscripts/publishing/maintenance-check.py'
> --- cronscripts/publishing/maintenance-check.py 2010-05-28 16:17:51 +0000
> +++ cronscripts/publishing/maintenance-check.py 2010-10-21 19:06:53 +0000
>
> +# Names of the distribution releases that are not supported by this
> +# tool. All later versions are supported.
> +UNSUPPORTED_DISTRO_RELEASED = ["dapper", "edgy", "feisty", "gutsy", "hardy",
> + "intrepid", "jaunty", "karmic"]
>
> A small thing: our coding standards say this should be formatted as
>
> UNSUPPORTED_DISTRO_RELEASED = [
> "dapper",
> "edgy",
> "feisty",
> "gutsy",
> "hardy",
> "intrepid",
> "jaunty",
> "karmic",
> ]

> (Notice the trailing comma even on the last line.)

Thanks, updated in my branch.

[..]
> --- cronscripts/publishing/tests/mock-data/mock-lp-root/scripts/ftpmaster-tools/lp-query-distro.py 1970-01-01 00:00:00 +0000
> +++ cronscripts/publishing/tests/mock-data/mock-lp-root/scripts/ftpmaster-tools/lp-query-distro.py 2010-10-21 19:06:53 +0000
> @@ -0,0 +1,24 @@
> +#!/usr/bin/python
> +#
> +# This is a mock version of the lp-query-distro.py script that
> +# returns static data so that the cron.germinate shell script
> +# can be tested.
> +
> +import sys
> +
> +def error_and_exit():
>
> Another small thing from our coding standards: each top-level (i.e. unindented) class or function definition in a file should have two blank lines above it.
>
> Running "make lint" should tell you about these things. With such old code though, it's possible that your changes drown among warnings for code you didn't even touch.
[..]

Thanks, I fixed that in my branch now as well. I can not run make lint
apparently without setting up a proper LP dev environment. It
complains about missing external dependencies, if I run the suggested
script it will fails with a non-obvious error. But checking lint.sh.in
shows that pocketlint is used and that makes it trivial to run it.

I went over the pocketlint errors and fixed all of them (except for
one in line 88 that I'm not sure what the correct style to use is).

[..]
> +# mvo: I would love to use this, but it complains about a missing
> +# import for "fixtures" and I can not find a package that
> +# provides this module
> +#from lp.testing import TestCase # or TestCaseWithFactory
>
> The disadvantage of having a very active hands-on architect is that things change so fast. :-) When in doubt, I generally go through these steps:
[..]

Thanks for these tips. For this particular piece of code I do not need
a full launchpad environment so I haven't setup one. But I will keep
this in my tomboy notes for when I need it :)

> + self.ubuntu_misc_dir = os.path.join(self.archive_dir, "ubuntu-misc")
> + self.ubuntu_ger...

Read more...

Revision history for this message
Jeroen T. Vermeulen (jtv) wrote :

Hi Michael,

This sure isn't an easy review. My compliments on your tenacity—it'll be really good to have this landed. The code as such is good as far as I'm concerned.

To be honest, developing a branch without a full LP environment set up is probably not a good idea. It messes with your system setup and drops any postgres clusters you may have, but on the bright side, it is fully automated (including easy updates and faster bzr interaction with future branches) and gives you access to "make lint" and the test runner. It may be worth setting up a virtual machine for.

I set up a copy of your branch locally. It turns out that on the one hand, the test suite does run your script test. On the other, it's not run in the right way somehow and it breaks. As it turns out, moving the test and the mock-data directory into lib/lp/soyuz/scripts/tests/ fixes that. You can see my changes at lp:~jtv/launchpad/mvo

Unfortunately it's still not quite working. The cron.germinate script seems to run a command "lockfile" that it can't find. If this is from a package that needs to be installed on the system, it should be included in either the dependencies for Launchpad or the zc.buildout packages. Otherwise the test suite will not pass on the EC2 and buildbot machines, which start out with clean base systems.

Jeroen

9909. By Michael Vogt

merged with db-devel

9910. By Michael Vogt

merged from jtv (many thanks)

9911. By Michael Vogt

lib/lp/soyuz/scripts/tests/germinate-test-data/mock-bin/lockfile: add mock lockfile command to ensure cron.germinate finds this command when it needs it

9912. By Michael Vogt

lib/lp/soyuz/scripts/tests/germinate-test-data/mock-bin/lockfile: return 1 instead of 0 (this is what cron.germinate expects)

Revision history for this message
Michael Vogt (mvo) wrote :

Thanks for your branch. I merged it into my branch and I have a LP setup now (that took a while to setup). I created a mock lockfile command, however the test is still failing in my lucid VM. Probably some problem with the path shuffling. I look into this tomorrow.

9913. By Michael Vogt

lib/lp/soyuz/scripts/tests/germinate-test-data/mock-lp-root/cronscripts/publishing/maintenance-check.py: fix symlink target now that the tests moved to a different directory

Revision history for this message
Michael Vogt (mvo) wrote :

The branch should be ready now, the tests pass in a LP dev environment for me now.

review: Needs Resubmitting
Revision history for this message
Jeroen T. Vermeulen (jtv) wrote :

Argh! The test gets stuck when I run it:

Running germinate... ........................ done.
Running maintenance-check for hardy... done
Running maintenance-check for jaunty... done
Running maintenance-check for karmic... done
Running maintenance-check for lucid...

At that point maintenance-check.py sits effectively idle forever.

What I suspect happens is that the script tries to access the URLs that it has hard-coded into it:

{{{
# germinate output base directory
BASE_URL = "http://people.canonical.com/~ubuntu-archive/germinate-output/"

# hints dir url, hints file is "$distro.hints" by default
# (e.g. lucid.hints)
HINTS_DIR_URL = "http://people.canonical.com/~ubuntu-archive/seeds/platform.%s/SUPPORTED_HINTS"

# we need the archive root to parse the Sources file to support
# by-source hints
ARCHIVE_ROOT = "http://archive.ubuntu.com/ubuntu"
}}}

The test suite should not make any network connections outside of the test system. I'm afraid that to get this working, we may have to inject test doubles for these URLs as well.

(Also, on a very minor note, the current branch still adds cronscripts/publishing/tests although of course it's now empty.)

Revision history for this message
Michael Vogt (mvo) wrote :

Thanks Jeroen for running this inside the test evnironment. I look into adding test doubles next, that is going to be a bit of work. And I removed the empty dir now :)

9914. By Michael Vogt

cronscripts/publishing/tests: removed, empty now

9915. By Michael Vogt

cronscripts/publishing/maintenance-check.py: support url overrides in the environment for {MAINTENANCE_CHECK_BASE_URL, MAINTENANCE_CHECK_HINTS_DIR_URL, MAINTENANCE_CHECK_ARCHIVE_ROOT}

Revision history for this message
Gavin Panella (allenap) wrote :

Michael, this is a long-lived branch into which you and the reviewers have put a lot of work. Is there something I can do to help you get this landed?

Revision history for this message
Michael Vogt (mvo) wrote :

Thanks Gavin! I did look into this one again yesterday. I need to create the test dummies, that is tedious work. But the bugfix is important, so I will give it another go.

9916. By Michael Vogt

lib/lp/soyuz/scripts/tests/test_cron_germinate.py: create mock environment for get-maintenance-check.py because it can not use the network to fetch the real data when the testsuite runs

Revision history for this message
Michael Vogt (mvo) wrote :

This update should create all the needed files locally now without the need to touch the network. Please note that I added a "class PoorMvosTestCase(unittest.TestCase)" that is used as a fallback if the full lp.testing import TestCase can not be imported. Feel free to remove it in the final merge (or I can remove it too once the rest looks ok). I added it because I only have a full LP environment in a VM and that is a bit of a hassle to work with. But this script really only needs the makeTemporaryFile feature. The test passes now for me without network and the data looks sane too.

Revision history for this message
Gavin Panella (allenap) wrote :

Michael, I've merged devel into your branch, cleaned up your last revision and put it in lp:~allenap/launchpad/support-timeframe-fix-660433. Please can you merge my branch into yours and push back to Launchpad?

Revision history for this message
Gavin Panella (allenap) wrote :

Don't worry, I'll land this directly from my branch.

Revision history for this message
Gavin Panella (allenap) wrote :

I've merged this into *devel* via lp:~allenap/launchpad/support-timeframe-fix-660433.

Revision history for this message
Michael Vogt (mvo) wrote :

On Thu, Jan 20, 2011 at 07:01:55PM -0000, Gavin Panella wrote:
> Michael, I've merged devel into your branch, cleaned up your last revision and put it in lp:~allenap/launchpad/support-timeframe-fix-660433. Please can you merge my branch into yours and push back to Launchpad?

Thanks a lot for this! And sorry for my late reply, I was away for a
cross distro meeting a couple of days and just returned. Merged and
pushed.

I just read that you will land it from your branch, thats great. I
hope you did not had too much trouble with merge conflicts and all?

Cheers,
 Michael

Revision history for this message
Gavin Panella (allenap) wrote :

Michael, it landed without a hitch, perfect. Thank you for all the work you've done on this :)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'cronscripts/publishing/cron.germinate'
--- cronscripts/publishing/cron.germinate 2010-11-02 15:54:52 +0000
+++ cronscripts/publishing/cron.germinate 2011-01-07 13:50:46 +0000
@@ -6,12 +6,12 @@
6set -e6set -e
7set -u7set -u
88
9ARCHIVEROOT=/srv/launchpad.net/ubuntu-archive/ubuntu9ARCHIVEROOT=${TEST_ARCHIVEROOT:-/srv/launchpad.net/ubuntu-archive/ubuntu}
10MISCROOT=$ARCHIVEROOT/../ubuntu-misc10MISCROOT=$ARCHIVEROOT/../ubuntu-misc
11LOCKROOT=$ARCHIVEROOT/..11LOCKROOT=$ARCHIVEROOT/..
12GERMINATEROOT=$ARCHIVEROOT/../ubuntu-germinate12GERMINATEROOT=$ARCHIVEROOT/../ubuntu-germinate
1313
14LAUNCHPADROOT=/srv/launchpad.net/codelines/current14LAUNCHPADROOT=${TEST_LAUNCHPADROOT:-/srv/launchpad.net/codelines/current}
15MAINTAINCE_CHECK=$LAUNCHPADROOT/cronscripts/publishing/maintenance-check.py15MAINTAINCE_CHECK=$LAUNCHPADROOT/cronscripts/publishing/maintenance-check.py
1616
17## Check to see if another germinate run is in progress17## Check to see if another germinate run is in progress
@@ -28,7 +28,6 @@
2828
29trap cleanup EXIT29trap cleanup EXIT
3030
31LAUNCHPADROOT=/srv/launchpad.net/codelines/current
32suite=`$LAUNCHPADROOT/scripts/ftpmaster-tools/lp-query-distro.py development`31suite=`$LAUNCHPADROOT/scripts/ftpmaster-tools/lp-query-distro.py development`
3332
34echo -n "Running germinate... "33echo -n "Running germinate... "
@@ -131,10 +130,27 @@
131done130done
132echo " done."131echo " done."
133132
134# now generate the Supported extra overrides
135$MAINTAINCE_CHECK $suite > "$MISCROOT/more-extra.override.$suite.main.supported" 2> _maintenance-check.stderr
136if [ $? -eq 0 ]; then
137 cat "$MISCROOT/more-extra.override.$suite.main.supported" >> "$MISCROOT/more-extra.override.$suite.main.new"
138fi
139
140mv -f "$MISCROOT/more-extra.override.$suite.main.new" "$MISCROOT/more-extra.override.$suite.main"133mv -f "$MISCROOT/more-extra.override.$suite.main.new" "$MISCROOT/more-extra.override.$suite.main"
134
135# Now generate the Supported extra overrides for all supported distros.
136SUITES=`$LAUNCHPADROOT/scripts/ftpmaster-tools/lp-query-distro.py supported`
137for supported_suite in $SUITES; do
138 echo -n "Running maintenance-check for $supported_suite... "
139 # The support timeframe information is stored here
140 SUPPORTED="$MISCROOT/more-extra.override.$supported_suite.main.supported"
141 # This is the target override file that contains germinate plus support info
142 TARGET="$MISCROOT/more-extra.override.$supported_suite.main"
143 # Debug/Log information
144 LOG="_maintenance-check.$supported_suite.stderr"
145 if $MAINTAINCE_CHECK $supported_suite > $SUPPORTED 2> $LOG; then
146 # The target file may be missing on the server and the script should
147 # not fail if that is the case.
148 touch $TARGET
149 # Remove old "Supported" info from extra-overrides as it may be
150 # stale now and we replace it with fresh content below.
151 sed /"^.* Supported"/d $TARGET > ${TARGET}.new
152 cat $SUPPORTED >> ${TARGET}.new
153 mv ${TARGET}.new $TARGET
154 fi
155 echo " done"
156done
141157
=== modified file 'cronscripts/publishing/maintenance-check.py'
--- cronscripts/publishing/maintenance-check.py 2010-05-28 16:17:51 +0000
+++ cronscripts/publishing/maintenance-check.py 2011-01-07 13:50:46 +0000
@@ -1,7 +1,7 @@
1#!/usr/bin/python1#!/usr/bin/python
2#2#
3# python port of the nice maintainace-check script by Nick Barcet3# python port of the nice maintainace-check script by Nick Barcet
4# 4#
5# taken from:5# taken from:
6# https://code.edge.launchpad.net/~mvo/ubuntu-maintenance-check/python-port6# https://code.edge.launchpad.net/~mvo/ubuntu-maintenance-check/python-port
7# (where it will vanish once taken here)7# (where it will vanish once taken here)
@@ -9,7 +9,7 @@
9# this warning filter is only needed on older versions of python-apt,9# this warning filter is only needed on older versions of python-apt,
10# once the machine runs lucid it can be removed10# once the machine runs lucid it can be removed
11import warnings11import warnings
12warnings.filterwarnings("ignore","apt API not stable yet")12warnings.filterwarnings("ignore", "apt API not stable yet")
13import apt13import apt
14warnings.resetwarnings()14warnings.resetwarnings()
1515
@@ -29,21 +29,21 @@
29# - distros "kubuntu", "edubuntu" and "netbook" need to be29# - distros "kubuntu", "edubuntu" and "netbook" need to be
30# considered *but* only follow SUPPORT_TIMEFRAME30# considered *but* only follow SUPPORT_TIMEFRAME
31# - anything that is in armel follows SUPPORT_TIMEFRAME31# - anything that is in armel follows SUPPORT_TIMEFRAME
32# 32#
3333
34# codename of the lts releases34# codename of the lts releases
35LTS_RELEASES = [ "dapper", "hardy", "lucid" ]35LTS_RELEASES = ["dapper", "hardy", "lucid"]
3636
37# architectures that are full supported (including LTS time)37# architectures that are full supported (including LTS time)
38PRIMARY_ARCHES = ["i386", "amd64"]38PRIMARY_ARCHES = ["i386", "amd64"]
3939
40# architectures we support (but not for LTS time)40# architectures we support (but not for LTS time)
41SUPPORTED_ARCHES = PRIMARY_ARCHES + ["armel"]41SUPPORTED_ARCHES = PRIMARY_ARCHES + ["armel"]
4242
43# what defines the seeds is documented in wiki.ubuntu.com/SeedManagement43# what defines the seeds is documented in wiki.ubuntu.com/SeedManagement
44SERVER_SEEDS = [ "supported-server", "server-ship"]44SERVER_SEEDS = ["supported-server", "server-ship"]
45DESKTOP_SEEDS = ["ship", "supported-desktop", "supported-desktop-extra"]45DESKTOP_SEEDS = ["ship", "supported-desktop", "supported-desktop-extra"]
46SUPPORTED_SEEDS = [ "all" ]46SUPPORTED_SEEDS = ["all"]
4747
48# normal support timeframe48# normal support timeframe
49# time, seeds, arches49# time, seeds, arches
@@ -60,25 +60,46 @@
60]60]
6161
62# distro names and if they get LTS support (order is important)62# distro names and if they get LTS support (order is important)
63DISTRO_NAMES_AND_LTS_SUPPORT = [ ("ubuntu", True),63DISTRO_NAMES_AND_LTS_SUPPORT = [
64 ("kubuntu", True),64 ("ubuntu", True),
65 ("netbook", False),65 ("kubuntu", True),
66 ]66 ("netbook", False),
67 ]
68
69# Names of the distribution releases that are not supported by this
70# tool. All later versions are supported.
71UNSUPPORTED_DISTRO_RELEASED = [
72 "dapper",
73 "edgy",
74 "feisty",
75 "gutsy",
76 "hardy",
77 "intrepid",
78 "jaunty",
79 "karmic",
80 ]
81
6782
68# germinate output base directory83# germinate output base directory
69BASE_URL = "http://people.canonical.com/~ubuntu-archive/germinate-output/"84BASE_URL = os.environ.get(
85 "MAINTENANCE_CHECK_BASE_URL",
86 "http://people.canonical.com/~ubuntu-archive/germinate-output/")
7087
71# hints dir url, hints file is "$distro.hints" by default88# hints dir url, hints file is "$distro.hints" by default
72# (e.g. lucid.hints)89# (e.g. lucid.hints)
73HINTS_DIR_URL = "http://people.canonical.com/~ubuntu-archive/seeds/platform.%s/SUPPORTED_HINTS"90HINTS_DIR_URL = os.environ.get(
91 "MAINTENANCE_CHECK_HINTS_DIR_URL",
92 "http://people.canonical.com/~ubuntu-archive/seeds/platform.%s/SUPPORTED_HINTS")
7493
75# we need the archive root to parse the Sources file to support94# we need the archive root to parse the Sources file to support
76# by-source hints95# by-source hints
77ARCHIVE_ROOT = "http://archive.ubuntu.com/ubuntu"96ARCHIVE_ROOT = os.environ.get(
97 "MAINTENANCE_CHECK_ARCHIVE_ROOT", "http://archive.ubuntu.com/ubuntu")
7898
79# support timeframe tag used in the Packages file99# support timeframe tag used in the Packages file
80SUPPORT_TAG = "Supported"100SUPPORT_TAG = "Supported"
81101
102
82def get_binaries_for_source_pkg(srcname):103def get_binaries_for_source_pkg(srcname):
83 """ Return all binary package names for the given source package name.104 """ Return all binary package names for the given source package name.
84105
@@ -92,38 +113,41 @@
92 pkgnames.add(binary)113 pkgnames.add(binary)
93 return pkgnames114 return pkgnames
94115
116
95def expand_src_pkgname(pkgname):117def expand_src_pkgname(pkgname):
96 """ Expand a package name if it is prefixed with src.118 """ Expand a package name if it is prefixed with src.
97119
98 If the package name is prefixed with src it will be expanded120 If the package name is prefixed with src it will be expanded
99 to a list of binary package names. Otherwise the original121 to a list of binary package names. Otherwise the original
100 package name will be returned.122 package name will be returned.
101 123
102 :param pkgname: The package name (that may include src:prefix).124 :param pkgname: The package name (that may include src:prefix).
103 :return: A list of binary package names (the list may be one element long).125 :return: A list of binary package names (the list may be one element
126 long).
104 """127 """
105 if not pkgname.startswith("src:"):128 if not pkgname.startswith("src:"):
106 return [pkgname]129 return [pkgname]
107 return get_binaries_for_source_pkg(pkgname.split("src:")[1])130 return get_binaries_for_source_pkg(pkgname.split("src:")[1])
108131
132
109def create_and_update_deb_src_source_list(distroseries):133def create_and_update_deb_src_source_list(distroseries):
110 """ Create sources.list and update cache.134 """ Create sources.list and update cache.
111135
112 This creates a sources.list file with deb-src entries for a given 136 This creates a sources.list file with deb-src entries for a given
113 distroseries and apt.Cache.update() to make sure the data is up-to-date.137 distroseries and apt.Cache.update() to make sure the data is up-to-date.
114 :param distro: The code name of the distribution series (e.g. lucid).138 :param distro: The code name of the distribution series (e.g. lucid).
115 :return: None139 :return: None
116 :raises: IOError: When cache update fails.140 :raises: IOError: When cache update fails.
117 """141 """
118 # apt root dir142 # apt root dir
119 rootdir="./aptroot.%s" % distroseries143 rootdir = "./aptroot.%s" % distroseries
120 sources_list_dir = os.path.join(rootdir, "etc","apt")144 sources_list_dir = os.path.join(rootdir, "etc", "apt")
121 if not os.path.exists(sources_list_dir):145 if not os.path.exists(sources_list_dir):
122 os.makedirs(sources_list_dir)146 os.makedirs(sources_list_dir)
123 sources_list = open(os.path.join(sources_list_dir, "sources.list"),"w")147 sources_list = open(os.path.join(sources_list_dir, "sources.list"), "w")
124 for pocket in [148 for pocket in [
125 "%s" % distroseries, 149 "%s" % distroseries,
126 "%s-updates" % distroseries, 150 "%s-updates" % distroseries,
127 "%s-security" % distroseries]:151 "%s-security" % distroseries]:
128 sources_list.write(152 sources_list.write(
129 "deb-src %s %s main restricted\n" % (153 "deb-src %s %s main restricted\n" % (
@@ -134,13 +158,13 @@
134 sources_list.close()158 sources_list.close()
135 # create required dirs/files for apt.Cache(rootdir) to work on older159 # create required dirs/files for apt.Cache(rootdir) to work on older
136 # versions of python-apt. once lucid is used it can be removed160 # versions of python-apt. once lucid is used it can be removed
137 for d in ["var/lib/dpkg", 161 for d in ["var/lib/dpkg",
138 "var/cache/apt/archives/partial",162 "var/cache/apt/archives/partial",
139 "var/lib/apt/lists/partial"]:163 "var/lib/apt/lists/partial"]:
140 if not os.path.exists(os.path.join(rootdir,d)):164 if not os.path.exists(os.path.join(rootdir, d)):
141 os.makedirs(os.path.join(rootdir,d))165 os.makedirs(os.path.join(rootdir, d))
142 if not os.path.exists(os.path.join(rootdir,"var/lib/dpkg/status")):166 if not os.path.exists(os.path.join(rootdir, "var/lib/dpkg/status")):
143 open(os.path.join(rootdir,"var/lib/dpkg/status"),"w")167 open(os.path.join(rootdir, "var/lib/dpkg/status"), "w")
144 # open cache with our just prepared rootdir168 # open cache with our just prepared rootdir
145 cache = apt.Cache(rootdir=rootdir)169 cache = apt.Cache(rootdir=rootdir)
146 try:170 try:
@@ -148,24 +172,28 @@
148 except SystemError:172 except SystemError:
149 logging.exception("cache.update() failed")173 logging.exception("cache.update() failed")
150174
175
151def get_structure(distroname, version):176def get_structure(distroname, version):
152 """ Get structure file conent for named distro and distro version.177 """ Get structure file conent for named distro and distro version.
153 178
154 :param name: Name of the distribution (e.g. kubuntu, ubuntu, xubuntu).179 :param name: Name of the distribution (e.g. kubuntu, ubuntu, xubuntu).
155 :param version: Code name of the distribution version (e.g. lucid).180 :param version: Code name of the distribution version (e.g. lucid).
156 :return: List of strings with the structure file content181 :return: List of strings with the structure file content
157 """182 """
158 f = urllib2.urlopen("%s/%s.%s/structure" % (BASE_URL, distroname, version))183 f = urllib2.urlopen("%s/%s.%s/structure" % (
184 BASE_URL, distroname, version))
159 structure = f.readlines()185 structure = f.readlines()
160 f.close()186 f.close()
161 return structure187 return structure
162188
189
163def expand_seeds(structure, seedname):190def expand_seeds(structure, seedname):
164 """ Expand seed by its dependencies using the strucure file.191 """ Expand seed by its dependencies using the strucure file.
165192
166 :param structure: The content of the STRUCTURE file as string list.193 :param structure: The content of the STRUCTURE file as string list.
167 :param seedname: The name of the seed as string that needs to be expanded.194 :param seedname: The name of the seed as string that needs to be expanded.
168 :return: a set() for the seed dependencies (excluding the original seedname)195 :return: a set() for the seed dependencies (excluding the original
196 seedname)
169 """197 """
170 seeds = []198 seeds = []
171 for line in structure:199 for line in structure:
@@ -175,9 +203,10 @@
175 seeds += expand_seeds(structure, seed)203 seeds += expand_seeds(structure, seed)
176 return set(seeds)204 return set(seeds)
177205
206
178def get_packages_for_seeds(name, distro, seeds):207def get_packages_for_seeds(name, distro, seeds):
179 """208 """
180 get packages for the given name (e.g. ubuntu) and distro release 209 get packages for the given name (e.g. ubuntu) and distro release
181 (e.g. lucid) that are in the given list of seeds210 (e.g. lucid) that are in the given list of seeds
182 returns a set() of package names211 returns a set() of package names
183 """212 """
@@ -185,7 +214,7 @@
185 for bseed in seeds:214 for bseed in seeds:
186 for seed in [bseed]: #, bseed+".build-depends", bseed+".seed"]:215 for seed in [bseed]: #, bseed+".build-depends", bseed+".seed"]:
187 pkgs_in_seeds[seed] = set()216 pkgs_in_seeds[seed] = set()
188 seedurl = "%s/%s.%s/%s" % (BASE_URL,name, distro, seed)217 seedurl = "%s/%s.%s/%s" % (BASE_URL, name, distro, seed)
189 logging.debug("looking for '%s'" % seedurl)218 logging.debug("looking for '%s'" % seedurl)
190 try:219 try:
191 f = urllib2.urlopen(seedurl)220 f = urllib2.urlopen(seedurl)
@@ -204,6 +233,7 @@
204 logging.error("seed %s failed (%s)" % (seedurl, e))233 logging.error("seed %s failed (%s)" % (seedurl, e))
205 return pkgs_in_seeds234 return pkgs_in_seeds
206235
236
207def what_seeds(pkgname, seeds):237def what_seeds(pkgname, seeds):
208 in_seeds = set()238 in_seeds = set()
209 for s in seeds:239 for s in seeds:
@@ -211,6 +241,7 @@
211 in_seeds.add(s)241 in_seeds.add(s)
212 return in_seeds242 return in_seeds
213243
244
214def compare_support_level(x, y):245def compare_support_level(x, y):
215 """246 """
216 compare two support level strings of the form 18m, 3y etc247 compare two support level strings of the form 18m, 3y etc
@@ -218,9 +249,10 @@
218 :parm y: the second support level249 :parm y: the second support level
219 :return: negative if x < y, zero if x==y, positive if x > y250 :return: negative if x < y, zero if x==y, positive if x > y
220 """251 """
252
221 def support_to_int(support_time):253 def support_to_int(support_time):
222 """254 """
223 helper that takes a support time string and converts it to 255 helper that takes a support time string and converts it to
224 a integer for cmp()256 a integer for cmp()
225 """257 """
226 # allow strings like "5y (kubuntu-common)258 # allow strings like "5y (kubuntu-common)
@@ -233,7 +265,9 @@
233 raise ValueError("support time '%s' has to end with y or m" % x)265 raise ValueError("support time '%s' has to end with y or m" % x)
234 return cmp(support_to_int(x), support_to_int(y))266 return cmp(support_to_int(x), support_to_int(y))
235267
236def get_packages_support_time(structure, name, pkg_support_time, support_timeframe_list):268
269def get_packages_support_time(structure, name, pkg_support_time,
270 support_timeframe_list):
237 """271 """
238 input a structure file and a list of pair<timeframe, seedlist>272 input a structure file and a list of pair<timeframe, seedlist>
239 return a dict of pkgnames -> support timeframe string273 return a dict of pkgnames -> support timeframe string
@@ -255,18 +289,21 @@
255 pkg, old_timeframe, timeframe))289 pkg, old_timeframe, timeframe))
256 pkg_support_time[pkg] = timeframe290 pkg_support_time[pkg] = timeframe
257 if options.with_seeds:291 if options.with_seeds:
258 pkg_support_time[pkg] += " (%s)" % ", ".join(what_seeds(pkg, pkgs_in_seeds))292 pkg_support_time[pkg] += " (%s)" % ", ".join(
293 what_seeds(pkg, pkgs_in_seeds))
259294
260295
261 return pkg_support_time296 return pkg_support_time
262297
298
263if __name__ == "__main__":299if __name__ == "__main__":
264 parser = OptionParser()300 parser = OptionParser()
265 parser.add_option("--with-seeds", "", default=False,301 parser.add_option("--with-seeds", "", default=False,
266 action="store_true", 302 action="store_true",
267 help="add seed(s) of the package that are responsible for the maintaince time")303 help="add seed(s) of the package that are responsible "
304 "for the maintaince time")
268 parser.add_option("--source-packages", "", default=False,305 parser.add_option("--source-packages", "", default=False,
269 action="store_true", 306 action="store_true",
270 help="show as source pkgs")307 help="show as source pkgs")
271 parser.add_option("--hints-file", "", default=None,308 parser.add_option("--hints-file", "", default=None,
272 help="use diffenrt use hints file location")309 help="use diffenrt use hints file location")
@@ -275,8 +312,8 @@
275 # init312 # init
276 if len(args) > 0:313 if len(args) > 0:
277 distro = args[0]314 distro = args[0]
278 if distro[0] < 'h':315 if distro in UNSUPPORTED_DISTRO_RELEASED:
279 print "ERROR: only hardy or later is supported"316 logging.error("only lucid or later is supported")
280 sys.exit(1)317 sys.exit(1)
281 else:318 else:
282 distro = "lucid"319 distro = "lucid"
@@ -286,26 +323,32 @@
286323
287 if options.hints_file:324 if options.hints_file:
288 hints_file = options.hints_file325 hints_file = options.hints_file
289 (schema, netloc, path, query, fragment) = urlparse.urlsplit(hints_file)326 (schema, netloc, path, query, fragment) = urlparse.urlsplit(
327 hints_file)
290 if not schema:328 if not schema:
291 hints_file = "file:%s" % path329 hints_file = "file:%s" % path
292 else:330 else:
293 hints_file = HINTS_DIR_URL % distro331 hints_file = HINTS_DIR_URL % distro
294 332
295 # go over the distros we need to check333 # go over the distros we need to check
296 pkg_support_time = {}334 pkg_support_time = {}
297 for (name, lts_supported) in DISTRO_NAMES_AND_LTS_SUPPORT:335 for (name, lts_supported) in DISTRO_NAMES_AND_LTS_SUPPORT:
298336
299 # get basic structure file337 # get basic structure file
300 structure = get_structure(name, distro)338 try:
301 339 structure = get_structure(name, distro)
340 except urllib2.HTTPError:
341 logging.error("Can not get structure for '%s'." % name)
342 continue
343
302 # get dicts of pkgname -> support timeframe string344 # get dicts of pkgname -> support timeframe string
303 support_timeframe = SUPPORT_TIMEFRAME345 support_timeframe = SUPPORT_TIMEFRAME
304 if lts_supported and distro in LTS_RELEASES:346 if lts_supported and distro in LTS_RELEASES:
305 support_timeframe = SUPPORT_TIMEFRAME_LTS347 support_timeframe = SUPPORT_TIMEFRAME_LTS
306 else:348 else:
307 support_timeframe = SUPPORT_TIMEFRAME349 support_timeframe = SUPPORT_TIMEFRAME
308 get_packages_support_time(structure, name, pkg_support_time, support_timeframe)350 get_packages_support_time(
351 structure, name, pkg_support_time, support_timeframe)
309352
310 # now go over the bits in main that we have not seen (because353 # now go over the bits in main that we have not seen (because
311 # they are not in any seed and got added manually into "main"354 # they are not in any seed and got added manually into "main"
@@ -321,10 +364,11 @@
321 for pkg in cache:364 for pkg in cache:
322 if not pkg.name in pkg_support_time:365 if not pkg.name in pkg_support_time:
323 pkg_support_time[pkg.name] = support_timeframe[-1][0]366 pkg_support_time[pkg.name] = support_timeframe[-1][0]
324 logging.warn("add package in main but not in seeds %s with %s" % 367 logging.warn(
325 (pkg.name, pkg_support_time[pkg.name]))368 "add package in main but not in seeds %s with %s" % (
369 pkg.name, pkg_support_time[pkg.name]))
326370
327 # now check the hints file that is used to overwrite 371 # now check the hints file that is used to overwrite
328 # the default seeds372 # the default seeds
329 try:373 try:
330 for line in urllib2.urlopen(hints_file):374 for line in urllib2.urlopen(hints_file):
@@ -337,14 +381,15 @@
337 if support_time == 'unsupported':381 if support_time == 'unsupported':
338 try:382 try:
339 del pkg_support_time[pkgname]383 del pkg_support_time[pkgname]
340 sys.stderr.write("hints-file: marking %s unsupported\n" % pkgname)384 sys.stderr.write("hints-file: marking %s "
385 "unsupported\n" % pkgname)
341 except KeyError:386 except KeyError:
342 pass387 pass
343 else:388 else:
344 if pkg_support_time.get(pkgname) != support_time:389 if pkg_support_time.get(pkgname) != support_time:
345 sys.stderr.write(390 sys.stderr.write(
346 "hints-file: changing %s from %s to %s\n" % (391 "hints-file: changing %s from %s to %s\n" % (
347 pkgname, pkg_support_time.get(pkgname), 392 pkgname, pkg_support_time.get(pkgname),
348 support_time))393 support_time))
349 pkg_support_time[pkgname] = support_time394 pkg_support_time[pkgname] = support_time
350 except:395 except:
@@ -353,7 +398,7 @@
353 if e.code != 404:398 if e.code != 404:
354 raise399 raise
355 sys.stderr.write("hints-file: %s gave 404 error\n" % hints_file)400 sys.stderr.write("hints-file: %s gave 404 error\n" % hints_file)
356 401
357 # output suitable for the extra-override file402 # output suitable for the extra-override file
358 for pkgname in sorted(pkg_support_time.keys()):403 for pkgname in sorted(pkg_support_time.keys()):
359 # special case, the hints file may contain overrides that404 # special case, the hints file may contain overrides that
@@ -362,7 +407,7 @@
362 print "%s %s %s" % (407 print "%s %s %s" % (
363 pkgname, SUPPORT_TAG, pkg_support_time[pkgname])408 pkgname, SUPPORT_TAG, pkg_support_time[pkgname])
364 else:409 else:
365 # go over the supported arches, they are divided in 410 # go over the supported arches, they are divided in
366 # first-class (PRIMARY) and second-class with different411 # first-class (PRIMARY) and second-class with different
367 # support levels412 # support levels
368 for arch in SUPPORTED_ARCHES:413 for arch in SUPPORTED_ARCHES:
@@ -373,10 +418,11 @@
373 if arch in PRIMARY_ARCHES:418 if arch in PRIMARY_ARCHES:
374 # arch with full LTS support419 # arch with full LTS support
375 print "%s %s %s" % (420 print "%s %s %s" % (
376 pkgname_and_arch, SUPPORT_TAG, pkg_support_time[pkgname])421 pkgname_and_arch, SUPPORT_TAG,
422 pkg_support_time[pkgname])
377 else:423 else:
378 # not a LTS supported architecture, gets only regular424 # not a LTS supported architecture, gets only regular
379 # support_timeframe425 # support_timeframe
380 print "%s %s %s" % (426 print "%s %s %s" % (
381 pkgname_and_arch, SUPPORT_TAG, SUPPORT_TIMEFRAME[0][0])427 pkgname_and_arch, SUPPORT_TAG,
382 428 SUPPORT_TIMEFRAME[0][0])
383429
=== added directory 'lib/lp/soyuz/scripts/tests/germinate-test-data'
=== added directory 'lib/lp/soyuz/scripts/tests/germinate-test-data/mock-bin'
=== added file 'lib/lp/soyuz/scripts/tests/germinate-test-data/mock-bin/germinate'
--- lib/lp/soyuz/scripts/tests/germinate-test-data/mock-bin/germinate 1970-01-01 00:00:00 +0000
+++ lib/lp/soyuz/scripts/tests/germinate-test-data/mock-bin/germinate 2011-01-07 13:50:46 +0000
@@ -0,0 +1,5 @@
1#!/bin/sh
2#
3# This is a mock germinate script that just produces enough (empty)
4# files so that the cron.germinate shell script can run.
5touch structure all all.sources minimal standard
06
=== added file 'lib/lp/soyuz/scripts/tests/germinate-test-data/mock-bin/lockfile'
--- lib/lp/soyuz/scripts/tests/germinate-test-data/mock-bin/lockfile 1970-01-01 00:00:00 +0000
+++ lib/lp/soyuz/scripts/tests/germinate-test-data/mock-bin/lockfile 2011-01-07 13:50:46 +0000
@@ -0,0 +1,8 @@
1#!/bin/sh
2
3# This is a mock "lockfile" command that is used during the test
4# There is no need to have the real lockfile installed (its part
5# of procmail) because there can be no multiple germinate instances
6# while we run the tests
7
8exit 1
09
=== added directory 'lib/lp/soyuz/scripts/tests/germinate-test-data/mock-lp-root'
=== added directory 'lib/lp/soyuz/scripts/tests/germinate-test-data/mock-lp-root/cronscripts'
=== added directory 'lib/lp/soyuz/scripts/tests/germinate-test-data/mock-lp-root/cronscripts/publishing'
=== added symlink 'lib/lp/soyuz/scripts/tests/germinate-test-data/mock-lp-root/cronscripts/publishing/maintenance-check.py'
=== target is u'../../../../../../../../../cronscripts/publishing/maintenance-check.py'
=== added directory 'lib/lp/soyuz/scripts/tests/germinate-test-data/mock-lp-root/scripts'
=== added directory 'lib/lp/soyuz/scripts/tests/germinate-test-data/mock-lp-root/scripts/ftpmaster-tools'
=== added file 'lib/lp/soyuz/scripts/tests/germinate-test-data/mock-lp-root/scripts/ftpmaster-tools/lp-query-distro.py'
--- lib/lp/soyuz/scripts/tests/germinate-test-data/mock-lp-root/scripts/ftpmaster-tools/lp-query-distro.py 1970-01-01 00:00:00 +0000
+++ lib/lp/soyuz/scripts/tests/germinate-test-data/mock-lp-root/scripts/ftpmaster-tools/lp-query-distro.py 2011-01-07 13:50:46 +0000
@@ -0,0 +1,27 @@
1#!/usr/bin/python
2#
3# This is a mock version of the lp-query-distro.py script that
4# returns static data so that the cron.germinate shell script
5# can be tested.
6
7import sys
8
9
10def error_and_exit():
11 sys.stderr.write("ERROR: I'm a mock, I only support 'development' "
12 "and 'supported' as argument\n")
13 sys.exit(1)
14
15
16if __name__ == "__main__":
17 # There is only a very limited subset of arguments that we support,
18 # test for it and error if it looks wrong
19 if len(sys.argv) != 2:
20 error_and_exit()
21 distro = sys.argv[1]
22 if distro == "development":
23 print "natty"
24 elif distro == "supported":
25 print "hardy jaunty karmic lucid maverick"
26 else:
27 error_and_exit()
028
=== added file 'lib/lp/soyuz/scripts/tests/test_cron_germinate.py'
--- lib/lp/soyuz/scripts/tests/test_cron_germinate.py 1970-01-01 00:00:00 +0000
+++ lib/lp/soyuz/scripts/tests/test_cron_germinate.py 2011-01-07 13:50:46 +0000
@@ -0,0 +1,230 @@
1#!/usr/bin/python
2# Copyright 2010 Canonical Ltd. This software is licensed under the
3# GNU Affero General Public License version 3 (see the file LICENSE).
4
5"""This is a test for the soyuz cron.germinate script."""
6
7__metaclass__ = type
8
9import copy
10import gzip
11import os
12import subprocess
13import unittest
14
15try:
16 from lp.testing import TestCase
17except ImportError:
18 # mvo: *cough* poor mans version if no full LP environment is installed
19 class PoorMvosTestCase(unittest.TestCase):
20 def makeTemporaryDirectory(self):
21 import tempfile
22 return tempfile.mkdtemp(prefix="test_cron_germinate_")
23 TestCase = PoorMvosTestCase
24
25class TestCronGerminate(TestCase):
26
27 DISTRO_NAMES = [ "platform", "ubuntu", "kubuntu", "netbook" ]
28 DISTS = ["hardy", "lucid", "maverick"]
29 DEVELOPMENT_DIST = "natty"
30 COMPONENTS = ["main", "restricted", "universe", "multiverse"]
31 ARCHES = ["i386", "amd64", "armel", "powerpc"]
32 BASEPATH = os.path.abspath(os.path.dirname(__file__))
33 source_root = os.path.normpath(
34 os.path.join(BASEPATH, "..", "..", "..", "..", ".."))
35
36 def setUp(self):
37 super(TestCronGerminate, self).setUp()
38
39 # Setup a temp archive directory and populate it with the right
40 # sub-directories.
41 self.tmpdir = self.makeTemporaryDirectory()
42 self.archive_dir = self.setup_mock_archive_environment()
43 self.ubuntu_misc_dir = os.path.join(self.archive_dir, "ubuntu-misc")
44 self.ubuntu_germinate_dir = os.path.join(
45 self.archive_dir, "ubuntu-germinate")
46 # create a mock archive environment for all the distros we
47 # support and also include "updates" and "security"
48 for dist in self.DISTS + [self.DEVELOPMENT_DIST]:
49 self.populate_mock_archive_environment(
50 self.archive_dir, self.COMPONENTS, self.ARCHES, dist)
51 for component in ["security", "updates"]:
52 self.populate_mock_archive_environment(
53 self.archive_dir, self.COMPONENTS, self.ARCHES,
54 "%s-%s" % (dist, component))
55 # generate test dummies for maintenance-time.py, if this is
56 # set to "None" instead it will use the network to test against
57 # the real data
58 self.germinate_output_dir = self.setup_mock_germinate_output()
59
60 def create_directory_if_missing(self, directory):
61 """Create the given directory if it does not exist."""
62 if not os.path.exists(directory):
63 os.makedirs(directory)
64
65 def create_directory_list_if_missing(self, directory_list):
66 """Create the given directories from the list if they don't exist."""
67 for directory in directory_list:
68 self.create_directory_if_missing(directory)
69
70 def create_gzip_file(self, filepath, content=""):
71 """Create a gziped file in the given path with the given content.
72 If not content is given a empty file is created.
73 """
74 gz = gzip.GzipFile(filepath, "w")
75 gz.write(content)
76 gz.close()
77
78 def create_file(self, filepath, content=""):
79 """Create a file in the given path with the given content.
80 If not content is given a empty file is created.
81 """
82 f = open(filepath, "w")
83 f.write(content)
84 f.close()
85
86 def setup_mock_germinate_output(self):
87 # empty structure files
88 germinate_output_dir = os.path.join(
89 self.tmpdir, "germinate-test-data", "germinate-output")
90 dirs = []
91 for distro_name in self.DISTRO_NAMES:
92 for distro_series in self.DISTS:
93 dirs.append(os.path.join(germinate_output_dir, "%s.%s" % (distro_name, distro_series)))
94 self.create_directory_list_if_missing(dirs)
95 for dir in dirs:
96 self.create_file(os.path.join(dir, "structure"))
97 return germinate_output_dir
98
99 def setup_mock_archive_environment(self):
100 """Creates a mock archive environment and populate
101 it with the subdirectories that germinate will expect.
102 """
103 archive_dir = os.path.join(
104 self.tmpdir, "germinate-test-data", "ubuntu-archive")
105 ubuntu_misc_dir = os.path.join(archive_dir, "ubuntu-misc")
106 ubuntu_germinate_dir = os.path.join(archive_dir, "ubuntu-germinate")
107 ubuntu_dists_dir = os.path.join(archive_dir, "ubuntu", "dists")
108 self.create_directory_list_if_missing([
109 archive_dir,
110 ubuntu_misc_dir,
111 ubuntu_germinate_dir,
112 ubuntu_dists_dir])
113 return archive_dir
114
115 def populate_mock_archive_environment(self, archive_dir, components_list,
116 arches_list, current_devel_distro):
117 """Populates a mock archive environment with empty source packages
118 and empty binary packages.
119 """
120 for component in components_list:
121 # Create the environment for the source packages.
122 targetdir = os.path.join(
123 archive_dir,
124 "ubuntu/dists/%s/%s/source" % (
125 current_devel_distro, component))
126 self.create_directory_if_missing(targetdir)
127 self.create_gzip_file(os.path.join(targetdir, "Sources.gz"))
128
129 # Create the environment for the binary packages.
130 for arch in arches_list:
131 for subpath in ["", "debian-installer"]:
132 targetdir = os.path.join(
133 self.archive_dir,
134 "ubuntu/dists/%s/%s/%s/binary-%s" % (
135 current_devel_distro, component, subpath, arch))
136 self.create_directory_if_missing(targetdir)
137 self.create_gzip_file(os.path.join(
138 targetdir, "Packages.gz"))
139
140 def create_fake_environment(self, basepath, archive_dir,
141 germinate_output_dir):
142 """Create a fake process envirionment based on os.environ that
143 sets TEST_ARCHIVEROOT, TEST_LAUNCHPADROOT and modifies PATH
144 to point to the mock lp-bin directory.
145 """
146 fake_environ = copy.copy(os.environ)
147 fake_environ["TEST_ARCHIVEROOT"] = os.path.abspath(
148 os.path.join(archive_dir, "ubuntu"))
149 fake_environ["TEST_LAUNCHPADROOT"] = os.path.abspath(
150 os.path.join(basepath, "germinate-test-data/mock-lp-root"))
151 # Set the PATH in the fake environment so that our mock germinate
152 # is used. We could use the real germinate as well, but that will
153 # slow down the tests a lot and its also not interessting for this
154 # test as we do not use any of the germinate information.
155 fake_environ["PATH"] = "%s:%s" % (
156 os.path.abspath(os.path.join(
157 basepath, "germinate-test-data/mock-bin")),
158 os.environ["PATH"])
159 # test dummies for get-support-timeframe.py, they need to be
160 # in URI format
161 if germinate_output_dir:
162 # redirect base url to the mock environment
163 fake_environ["MAINTENANCE_CHECK_BASE_URL"] = "file://%s" % \
164 germinate_output_dir
165 # point to mock archive root
166 archive_root_url = "file://%s" % os.path.abspath(
167 os.path.join(archive_dir, "ubuntu"))
168 fake_environ["MAINTENANCE_CHECK_ARCHIVE_ROOT"] = archive_root_url
169 # maintenance-check.py expects a format string
170 hints_file_url = germinate_output_dir + "/platform.%s/SUPPORTED_HINTS"
171 for distro in self.DISTS:
172 open(hints_file_url % distro, "w")
173 fake_environ["MAINTENANCE_CHECK_HINTS_DIR_URL"] = "file://%s" % \
174 os.path.abspath(hints_file_url)
175 # add hints override to test that feature
176 f=open(hints_file_url % "lucid", "a")
177 f.write("linux-image-2.6.32-25-server 5y\n")
178 f.close()
179 return fake_environ
180
181 def test_maintenance_update(self):
182 """Test the maintenance-check.py porition of the soyuz cron.germinate
183 shell script by running it inside a fake environment and ensure
184 that it did update the "Support" override information for
185 apt-ftparchive without destroying/modifying the information
186 that the "germinate" script added to it earlier.
187 """
188 # Write into more-extras.overrides to ensure it is alive after we
189 # mucked around.
190 canary = "abrowser Task mock\n"
191 # Build fake environment based on the real one.
192 fake_environ = self.create_fake_environment(
193 self.BASEPATH, self.archive_dir, self.germinate_output_dir)
194 # Create mock override data files that include the canary string
195 # so that we can test later if it is still there.
196 for dist in self.DISTS:
197 self.create_file(
198 os.path.join(self.ubuntu_misc_dir,
199 "more-extra.override.%s.main" % dist),
200 canary)
201
202 # Run cron.germinate in the fake environment.
203 cron_germinate_path = os.path.join(
204 self.source_root, "cronscripts", "publishing", "cron.germinate")
205 subprocess.call(
206 [cron_germinate_path], env=fake_environ, cwd=self.BASEPATH)
207
208 # And check the output it generated for correctness.
209 for dist in self.DISTS:
210 supported_override_file = os.path.join(
211 self.ubuntu_misc_dir,
212 "more-extra.override.%s.main.supported" % dist)
213 self.assertTrue(os.path.exists(supported_override_file),
214 "no override file created for '%s'" % dist)
215 main_override_file = os.path.join(
216 self.ubuntu_misc_dir,
217 "more-extra.override.%s.main" % dist)
218 self.assertIn(canary, open(main_override_file).read(),
219 "canary no longer there for '%s'" % dist)
220
221 # Check here if we got the data from maintenance-check.py that
222 # we expected. This is a kernel name from lucid-updates and it
223 # will be valid for 5 years.
224 needle = "linux-image-2.6.32-25-server/i386 Supported 5y"
225 lucid_supported_override_file = os.path.join(
226 self.ubuntu_misc_dir, "more-extra.override.lucid.main")
227 self.assertIn(needle, open(lucid_supported_override_file).read())
228
229if __name__ == "__main__":
230 unittest.main()

Subscribers

People subscribed via source and target branches

to status/vote changes: