summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJulian Andres Klode <jak@debian.org>2021-01-08 14:07:09 +0000
committerJulian Andres Klode <jak@debian.org>2021-01-08 14:07:09 +0000
commitd35d51ddbdc75512a73e713972f4bbb5a1fd73ae (patch)
tree9fb90d98cb20bb0be193a63efc7c32b513408bad
parent4d28ddc501738d571c21ff6d41168f6c53ea462d (diff)
parentbd64bb79537fac17e1474672402d6b0572dce94a (diff)
Merge branch 'pu/phased-updates' into 'master'
Add support for Phased-Update-Percentage See merge request apt-team/apt!129
-rw-r--r--apt-pkg/aptconfiguration.cc61
-rw-r--r--apt-pkg/aptconfiguration.h7
-rw-r--r--apt-pkg/cacheiterators.h14
-rw-r--r--apt-pkg/deb/deblistparser.cc7
-rw-r--r--apt-pkg/pkgcache.h12
-rw-r--r--apt-pkg/pkgcachegen.cc4
-rw-r--r--apt-pkg/policy.cc48
-rw-r--r--apt-pkg/policy.h3
-rw-r--r--apt-pkg/tagfile-keys.list1
-rw-r--r--apt-private/private-show.cc8
-rw-r--r--debian/NEWS7
-rw-r--r--doc/apt_preferences.5.xml19
-rw-r--r--doc/examples/configure-index10
-rw-r--r--test/integration/framework3
-rwxr-xr-xtest/integration/test-phased-updates272
15 files changed, 469 insertions, 7 deletions
diff --git a/apt-pkg/aptconfiguration.cc b/apt-pkg/aptconfiguration.cc
index 61e53ec3a..b3b423fdc 100644
--- a/apt-pkg/aptconfiguration.cc
+++ b/apt-pkg/aptconfiguration.cc
@@ -480,4 +480,65 @@ std::string const Configuration::getBuildProfilesString() {
return list;
}
/*}}}*/
+
+// getMachineID - supported data.tar extensions /*{{{*/
+// ---------------------------------------------------------------------
+/* */
+std::string const Configuration::getMachineID()
+{
+ std::string id = _config->Find("APT::Machine-ID");
+
+ if (id.empty())
+ {
+ std::string file = _config->FindFile("Dir::Etc::machine-id");
+
+ if (file.empty())
+ {
+ file = flCombine(_config->FindDir("Dir::Etc"), "../machine-id");
+ }
+ FileFd fd;
+ _error->PushToStack();
+
+ if (not OpenConfigurationFileFd(file, fd) || not fd.ReadLine(id))
+ {
+ if (_config->FindB("Debug::Phasing", false))
+ {
+ _error->DumpErrors(std::clog);
+ }
+ }
+
+ _error->RevertToStack();
+ }
+
+ return id;
+}
+ /*}}}*/
+// isChroot - whether we are inside a chroot /*{{{*/
+// ---------------------------------------------------------------------
+/* */
+bool Configuration::isChroot()
+{
+ static struct once
+ {
+ bool res = false;
+ once()
+ {
+ pid_t child = ExecFork();
+ if (child == 0)
+ {
+ auto binary = _config->FindFile("Dir::Bin::ischroot", "/usr/bin/ischroot");
+ const char *const Args[] = {
+ binary.c_str(),
+ nullptr};
+ execvp(Args[0], const_cast<char **>(Args));
+ _exit(127);
+ }
+
+ res = ExecWait(child, "ischroot", true);
+ }
+ } once;
+
+ return once.res;
+}
+ /*}}}*/
}
diff --git a/apt-pkg/aptconfiguration.h b/apt-pkg/aptconfiguration.h
index 2cb2d823a..bbeb156b9 100644
--- a/apt-pkg/aptconfiguration.h
+++ b/apt-pkg/aptconfiguration.h
@@ -122,7 +122,12 @@ namespace Configuration { /*{{{*/
APT_PUBLIC std::vector<std::string> const getBuildProfiles();
/** \return Return a comma-separated list of enabled build profile specifications */
APT_PUBLIC std::string const getBuildProfilesString();
- /*}}}*/
+
+ std::string const getMachineID();
+
+ /** \return Whether we are running in a chroot */
+ bool isChroot();
+ /*}}}*/
}
/*}}}*/
}
diff --git a/apt-pkg/cacheiterators.h b/apt-pkg/cacheiterators.h
index 1b049b6e5..6261b5089 100644
--- a/apt-pkg/cacheiterators.h
+++ b/apt-pkg/cacheiterators.h
@@ -242,6 +242,20 @@ class APT_PUBLIC pkgCache::VerIterator : public Iterator<Version, VerIterator> {
bool Automatic() const;
VerFileIterator NewestFile() const;
+#ifdef APT_COMPILING_APT
+ inline unsigned int PhasedUpdatePercentage() const
+ {
+ return (static_cast<Version::Extra *>(Owner->Map.Data()) + S->d)->PhasedUpdatePercentage;
+ }
+ inline bool PhasedUpdatePercentage(unsigned int percentage)
+ {
+ if (percentage > 100)
+ return false;
+ (static_cast<Version::Extra *>(Owner->Map.Data()) + S->d)->PhasedUpdatePercentage = static_cast<uint8_t>(percentage);
+ return true;
+ }
+#endif
+
inline VerIterator(pkgCache &Owner,Version *Trg = 0) : Iterator<Version, VerIterator>(Owner, Trg) {
if (S == 0)
S = OwnerPointer();
diff --git a/apt-pkg/deb/deblistparser.cc b/apt-pkg/deb/deblistparser.cc
index 95f6f6fc8..b5c9c066e 100644
--- a/apt-pkg/deb/deblistparser.cc
+++ b/apt-pkg/deb/deblistparser.cc
@@ -338,6 +338,13 @@ bool debListParser::UsePackage(pkgCache::PkgIterator &Pkg,
else if (std::find(forceImportant.begin(), forceImportant.end(), Pkg.Name()) != forceImportant.end())
Pkg->Flags |= pkgCache::Flag::Important;
+ auto phased = Section.FindULL(pkgTagSection::Key::Phased_Update_Percentage, 100);
+ if (phased != 100)
+ {
+ if (not Ver.PhasedUpdatePercentage(phased))
+ _error->Warning("Ignoring invalid Phased-Update-Percentage value");
+ }
+
if (ParseStatus(Pkg,Ver) == false)
return false;
return true;
diff --git a/apt-pkg/pkgcache.h b/apt-pkg/pkgcache.h
index 6c6a4664e..55baa3cef 100644
--- a/apt-pkg/pkgcache.h
+++ b/apt-pkg/pkgcache.h
@@ -623,6 +623,8 @@ struct pkgCache::DescFile
or handled as separate versions based on the Hash value. */
struct pkgCache::Version
{
+ struct Extra;
+
/** \brief complete version string */
map_stringitem_t VerStr;
/** \brief section this version is filled in */
@@ -688,8 +690,16 @@ struct pkgCache::Version
map_pointer<Version> NextInSource;
/** \brief Private pointer */
- map_pointer<void> d;
+ map_pointer<Extra> d;
};
+
+#ifdef APT_COMPILING_APT
+/// \brief Extra information for packages. APT-internal use only.
+struct pkgCache::Version::Extra
+{
+ uint8_t PhasedUpdatePercentage;
+};
+#endif
/*}}}*/
// Description structure /*{{{*/
/** \brief datamember of a linked list of available description for a version */
diff --git a/apt-pkg/pkgcachegen.cc b/apt-pkg/pkgcachegen.cc
index d02c49f03..bd81ca0f5 100644
--- a/apt-pkg/pkgcachegen.cc
+++ b/apt-pkg/pkgcachegen.cc
@@ -873,6 +873,10 @@ map_pointer<pkgCache::Version> pkgCacheGenerator::NewVersion(pkgCache::VerIterat
// Fill it in
Ver = pkgCache::VerIterator(Cache,Cache.VerP + Version);
+ Ver->d = AllocateInMap<pkgCache::Version::Extra>();
+ if (not Ver.PhasedUpdatePercentage(100))
+ abort();
+
//Dynamic<pkgCache::VerIterator> DynV(Ver); // caller MergeListVersion already takes care of it
Ver->NextVer = Next;
Ver->ParentPkg = ParentPkg;
diff --git a/apt-pkg/policy.cc b/apt-pkg/policy.cc
index 761a6ede1..39879f754 100644
--- a/apt-pkg/policy.cc
+++ b/apt-pkg/policy.cc
@@ -14,6 +14,7 @@
// Include Files /*{{{*/
#include <config.h>
+#include <apt-pkg/aptconfiguration.h>
#include <apt-pkg/cachefilter.h>
#include <apt-pkg/configuration.h>
#include <apt-pkg/error.h>
@@ -26,6 +27,7 @@
#include <apt-pkg/versionmatch.h>
#include <iostream>
+#include <random>
#include <sstream>
#include <string>
#include <vector>
@@ -40,12 +42,17 @@ using namespace std;
constexpr short NEVER_PIN = std::numeric_limits<short>::min();
+struct pkgPolicy::Private
+{
+ std::string machineID;
+};
+
// Policy::Init - Startup and bind to a cache /*{{{*/
// ---------------------------------------------------------------------
/* Set the defaults for operation. The default mode with no loaded policy
file matches the V0 policy engine. */
pkgPolicy::pkgPolicy(pkgCache *Owner) : VerPins(nullptr),
- PFPriority(nullptr), Cache(Owner), d(NULL)
+ PFPriority(nullptr), Cache(Owner), d(new Private)
{
if (Owner == 0)
return;
@@ -77,6 +84,8 @@ pkgPolicy::pkgPolicy(pkgCache *Owner) : VerPins(nullptr),
CreatePin(pkgVersionMatch::Release,"",DefRel,990);
}
InitDefaults();
+
+ d->machineID = APT::Configuration::getMachineID();
}
/*}}}*/
// Policy::InitDefaults - Compute the default selections /*{{{*/
@@ -274,8 +283,38 @@ void pkgPolicy::CreatePin(pkgVersionMatch::MatchType Type,string Name,
// Policy::GetPriority - Get the priority of the package pin /*{{{*/
// ---------------------------------------------------------------------
/* */
+// Returns true if this update is excluded by phasing.
+static inline bool ExcludePhased(std::string machineID, pkgCache::VerIterator const &Ver)
+{
+ // The order and fallbacks for the always/never checks come from update-manager and exist
+ // to preserve compatibility.
+ if (_config->FindB("APT::Get::Always-Include-Phased-Updates",
+ _config->FindB("Update-Manager::Always-Include-Phased-Updates", false)))
+ return false;
+
+ if (_config->FindB("APT::Get::Never-Include-Phased-Updates",
+ _config->FindB("Update-Manager::Never-Include-Phased-Updates", false)))
+ return true;
+
+ if (machineID.empty() // no machine-id
+ || getenv("SOURCE_DATE_EPOCH") != nullptr // reproducible build - always include
+ || APT::Configuration::isChroot())
+ return false;
+
+ std::string seedStr = std::string(Ver.SourcePkgName()) + "-" + Ver.SourceVerStr() + "-" + machineID;
+ std::seed_seq seed(seedStr.begin(), seedStr.end());
+ std::minstd_rand rand(seed);
+ std::uniform_int_distribution<unsigned int> dist(0, 100);
+
+ return dist(rand) > Ver.PhasedUpdatePercentage();
+}
APT_PURE signed short pkgPolicy::GetPriority(pkgCache::VerIterator const &Ver, bool ConsiderFiles)
{
+ if (Ver.ParentPkg()->CurrentVer && Ver.PhasedUpdatePercentage() != 100)
+ {
+ if (ExcludePhased(d->machineID, Ver))
+ return 1;
+ }
if (VerPins[Ver->ID].Type != pkgVersionMatch::None)
{
// If all sources are never pins, the never pin wins.
@@ -454,4 +493,9 @@ bool ReadPinFile(pkgPolicy &Plcy,string File)
}
/*}}}*/
-pkgPolicy::~pkgPolicy() {delete [] PFPriority; delete [] VerPins; }
+pkgPolicy::~pkgPolicy()
+{
+ delete[] PFPriority;
+ delete[] VerPins;
+ delete d;
+}
diff --git a/apt-pkg/policy.h b/apt-pkg/policy.h
index 7f30d40ed..589cebcde 100644
--- a/apt-pkg/policy.h
+++ b/apt-pkg/policy.h
@@ -83,7 +83,8 @@ class APT_PUBLIC pkgPolicy : public pkgDepCache::Policy
explicit pkgPolicy(pkgCache *Owner);
virtual ~pkgPolicy();
private:
- void * const d;
+ struct Private;
+ Private *const d;
};
APT_PUBLIC bool ReadPinFile(pkgPolicy &Plcy, std::string File = "");
diff --git a/apt-pkg/tagfile-keys.list b/apt-pkg/tagfile-keys.list
index a16bc686a..b5cc2f1a4 100644
--- a/apt-pkg/tagfile-keys.list
+++ b/apt-pkg/tagfile-keys.list
@@ -46,6 +46,7 @@ Package-List
Package_Revision
Package-Revision
Package-Type
+Phased-Update-Percentage
Pre-Depends
Priority
Protected
diff --git a/apt-private/private-show.cc b/apt-private/private-show.cc
index 103fa57e4..30b99b013 100644
--- a/apt-private/private-show.cc
+++ b/apt-private/private-show.cc
@@ -563,7 +563,13 @@ bool Policy(CommandLine &CmdL)
else
std::cout << " " << V.VerStr();
- std::cout << " " << Plcy->GetPriority(V) << std::endl;
+ std::cout << " " << Plcy->GetPriority(V);
+
+ if (V.PhasedUpdatePercentage() != 100)
+ std::cout << " "
+ << "(" << _("phased") << " " << V.PhasedUpdatePercentage() << "%)";
+
+ std::cout << std::endl;
for (pkgCache::VerFileIterator VF = V.FileList(); VF.end() == false; ++VF)
{
// Locate the associated index files so we can derive a description
diff --git a/debian/NEWS b/debian/NEWS
index 9d9c963c2..eb8e2c116 100644
--- a/debian/NEWS
+++ b/debian/NEWS
@@ -3,6 +3,13 @@ apt (2.1.16) UNRELEASED; urgency=medium
Automatically remove unused kernels on dist-upgrade. To revert to previous
behavior, set APT::Get::AutomaticRemove::Kernels to false.
+ Packages files can now set the Phased-Update-Percentage field to restrict
+ update rollout to a specified percentage of machines. Previously, this has
+ only been available to users of Ubuntu's update-manager tool. See
+ apt_preferences(5) for details and how to configure multiple systems to get
+ the same updates. Phased updates are disabled in chroots for now to not
+ break buildd-style setups.
+
-- Julian Andres Klode <jak@debian.org> Mon, 04 Jan 2021 10:47:14 +0100
apt (1.9.11) experimental; urgency=medium
diff --git a/doc/apt_preferences.5.xml b/doc/apt_preferences.5.xml
index 4ef5282e7..23f2d2d6b 100644
--- a/doc/apt_preferences.5.xml
+++ b/doc/apt_preferences.5.xml
@@ -102,7 +102,8 @@ algorithm to set the priorities of the versions of a package. Assign:
<term>priority 1</term>
<listitem><simpara>to the versions coming from archives which in their <filename>Release</filename>
files are marked as "NotAutomatic: yes" but <emphasis>not</emphasis> as "ButAutomaticUpgrades: yes"
-like the Debian <literal>experimental</literal> archive.</simpara></listitem>
+like the Debian <literal>experimental</literal> archive.</simpara></listitem>,
+as well as versions that are not phased on this systems.
</varlistentry>
<varlistentry>
@@ -175,6 +176,22 @@ because at least <emphasis>one</emphasis> of the available versions has a higher
priority than the installed version.</para>
</refsect2>
+<refsect2><title>Phased Updates</title>
+<para>APT understands a field called <literal>Phased-Update-Percentage</literal>
+which can be used to control the rollout of a new version. It is an integer between
+0 and 100.</para>
+
+<para>A system's eligibility to a phased update is determined by seeding
+random number generator with the package source name, the version number,
+and /etc/machine-id, and then calculating an integer in the range [0, 100].
+If this integer is larger than the <literal>Phased-Update-Percentage</literal>,
+the version is pinned to 1, and thus held back. Otherwise, normal policy rules apply.
+</para>
+
+<para>In case you have multiple systems that you want to receive the same set of updates, you can set
+<code>APT::Machine-ID</code> to a UUID such that they all phase the same, or set <code>APT::Get::Never-Include-Phased-Updates</code>
+or <code>APT::Get::Always-Include-Phased-Updates</code> to true such that APT will never/always consider phased updates.</para>
+</refsect2>
<refsect2><title>The Effect of APT Preferences</title>
<para>The APT preferences file allows the system administrator to control the
diff --git a/doc/examples/configure-index b/doc/examples/configure-index
index 15b020198..5ddf4132c 100644
--- a/doc/examples/configure-index
+++ b/doc/examples/configure-index
@@ -101,6 +101,8 @@ APT
Upgrade "<BOOL>";
Only-Upgrade "<BOOL>";
Upgrade-Allow-New "<BOOL>";
+ Always-Include-Phased-Updates "<BOOL>";
+ Never-Include-Phased-Updates "<BOOL>";
Purge "<BOOL>";
ReInstall "<BOOL>";
Compile "<BOOL>";
@@ -219,6 +221,8 @@ APT
// control parameters for cron jobs by /etc/cron.daily/apt documented there
Periodic {};
+
+ Machine-ID "<STRING>"; // Value of /etc/machine-id
};
// Options for the downloading routines
@@ -417,6 +421,7 @@ Dir "<DIR>"
SourceParts "<DIR>";
Trusted "<FILE>";
TrustedParts "<DIR>";
+ Machine-ID "<FILE>"; // by default points to ../machine-id effectively
};
// Locations of binaries
@@ -434,6 +439,7 @@ Dir "<DIR>"
bzip2 "<PROGRAM_PATH>";
lzma "<PROGRAM_PATH>";
uncompressed "<PROGRAM_PATH>";
+ ischroot "<PROGRAM_PATH>";
solvers "<LIST>"; // of directories
planners "<LIST>"; // of directories
@@ -561,6 +567,7 @@ Debug
APT::Progress::PackageManagerFd "<BOOL>";
SetupAPTPartialDirectory::AssumeGood "<BOOL>";
Locking "<BOOL>";
+ Phasing "<BOOL>";
};
pkgCacheGen
@@ -843,3 +850,6 @@ Rred::Compress "<STRING>";
APT::Internal::OpProgress::Absolute "<BOOL>";
APT::Color "<BOOL>";
+
+update-manager::always-include-phased-updates "<BOOL>";
+update-manager::never-include-phased-updates "<BOOL>";
diff --git a/test/integration/framework b/test/integration/framework
index 3973ad863..696fcd8cd 100644
--- a/test/integration/framework
+++ b/test/integration/framework
@@ -484,6 +484,8 @@ EOF
# Allow release files to be 10 hours in the future, rather than 10 seconds
echo 'Acquire::Max-FutureTime "'$((10 * 60 * 60))'";' > rootdir/etc/apt/apt.conf.d/future-time
+ echo 'APT::Machine-ID "912e43bd1c1d4ba481f9f8ccab25f9ee";' > rootdir/etc/apt/apt.conf.d/machine-id
+
configcompression '.' 'gz' #'bz2' 'lzma' 'xz'
confighashes 'SHA256' # these are tests, not security best-practices
@@ -505,6 +507,7 @@ EOF
# Make dpkg inherit testing path
echo 'DPkg::Path "";' >> aptconfig.conf
+ echo 'Dir::Bin::ischroot "/bin/false";' >> aptconfig.conf
# Make gcov shut up
export GCOV_ERROR_FILE=/dev/null
diff --git a/test/integration/test-phased-updates b/test/integration/test-phased-updates
new file mode 100755
index 000000000..50f151f79
--- /dev/null
+++ b/test/integration/test-phased-updates
@@ -0,0 +1,272 @@
+#!/bin/sh
+set -e
+
+TESTDIR="$(readlink -f "$(dirname "$0")")"
+. "$TESTDIR/framework"
+
+setupenvironment
+echo 'Debug::Phasing "1";' > rootdir/etc/apt/apt.conf.d/debug-phasing
+configarchitecture 'i386' 'armel'
+
+
+insertinstalledpackage 'phased1' 'all' '1'
+insertinstalledpackage 'phased2' 'all' '1'
+
+insertpackage 'unstable' 'phased1' 'all' '10' 'Phased-Update-Percentage: 10'
+insertpackage 'unstable' 'phased2' 'all' '10' 'Phased-Update-Percentage: 10'
+insertpackage 'unstable' 'phased3' 'all' '10' 'Phased-Update-Percentage: 10'
+
+insertpackage 'unstable' 'phased1' 'all' '100' 'Phased-Update-Percentage: 100'
+insertpackage 'unstable' 'phased2' 'all' '100' 'Phased-Update-Percentage: 100'
+insertpackage 'unstable' 'phased3' 'all' '100' 'Phased-Update-Percentage: 100'
+
+insertpackage 'unstable' 'phased1' 'all' '50' 'Phased-Update-Percentage: 50'
+insertpackage 'unstable' 'phased2' 'all' '50' 'Phased-Update-Percentage: 50'
+insertpackage 'unstable' 'phased3' 'all' '50' 'Phased-Update-Percentage: 50'
+
+setupaptarchive
+
+msgmsg "Basic test"
+testsuccessequal "phased1:
+ Installed: 1
+ Candidate: 100
+ Version table:
+ 100 500
+ 500 file:${TMPWORKINGDIRECTORY}/aptarchive unstable/main all Packages
+ 50 500 (phased 50%)
+ 500 file:${TMPWORKINGDIRECTORY}/aptarchive unstable/main all Packages
+ 10 1 (phased 10%)
+ 500 file:${TMPWORKINGDIRECTORY}/aptarchive unstable/main all Packages
+ *** 1 100
+ 100 ${TMPWORKINGDIRECTORY}/rootdir/var/lib/dpkg/status
+phased2:
+ Installed: 1
+ Candidate: 100
+ Version table:
+ 100 500
+ 500 file:${TMPWORKINGDIRECTORY}/aptarchive unstable/main all Packages
+ 50 1 (phased 50%)
+ 500 file:${TMPWORKINGDIRECTORY}/aptarchive unstable/main all Packages
+ 10 1 (phased 10%)
+ 500 file:${TMPWORKINGDIRECTORY}/aptarchive unstable/main all Packages
+ *** 1 100
+ 100 ${TMPWORKINGDIRECTORY}/rootdir/var/lib/dpkg/status
+phased3:
+ Installed: (none)
+ Candidate: 100
+ Version table:
+ 100 500
+ 500 file:${TMPWORKINGDIRECTORY}/aptarchive unstable/main all Packages
+ 50 500 (phased 50%)
+ 500 file:${TMPWORKINGDIRECTORY}/aptarchive unstable/main all Packages
+ 10 500 (phased 10%)
+ 500 file:${TMPWORKINGDIRECTORY}/aptarchive unstable/main all Packages" aptcache policy phased1 phased2 phased3
+
+msgmsg "Test for always-include-phased-updates"
+for always in Update-Manager::Always-Include-Phased-Updates APT::Get::Always-Include-Phased-Updates; do
+testsuccessequal "phased1:
+ Installed: 1
+ Candidate: 100
+ Version table:
+ 100 500
+ 500 file:${TMPWORKINGDIRECTORY}/aptarchive unstable/main all Packages
+ 50 500 (phased 50%)
+ 500 file:${TMPWORKINGDIRECTORY}/aptarchive unstable/main all Packages
+ 10 500 (phased 10%)
+ 500 file:${TMPWORKINGDIRECTORY}/aptarchive unstable/main all Packages
+ *** 1 100
+ 100 ${TMPWORKINGDIRECTORY}/rootdir/var/lib/dpkg/status
+phased2:
+ Installed: 1
+ Candidate: 100
+ Version table:
+ 100 500
+ 500 file:${TMPWORKINGDIRECTORY}/aptarchive unstable/main all Packages
+ 50 500 (phased 50%)
+ 500 file:${TMPWORKINGDIRECTORY}/aptarchive unstable/main all Packages
+ 10 500 (phased 10%)
+ 500 file:${TMPWORKINGDIRECTORY}/aptarchive unstable/main all Packages
+ *** 1 100
+ 100 ${TMPWORKINGDIRECTORY}/rootdir/var/lib/dpkg/status
+phased3:
+ Installed: (none)
+ Candidate: 100
+ Version table:
+ 100 500
+ 500 file:${TMPWORKINGDIRECTORY}/aptarchive unstable/main all Packages
+ 50 500 (phased 50%)
+ 500 file:${TMPWORKINGDIRECTORY}/aptarchive unstable/main all Packages
+ 10 500 (phased 10%)
+ 500 file:${TMPWORKINGDIRECTORY}/aptarchive unstable/main all Packages" aptcache policy phased1 phased2 phased3 -o $always=true
+done
+
+msgmsg "Test for never-include-phased-updates"
+for never in Update-Manager::Never-Include-Phased-Updates APT::Get::Never-Include-Phased-Updates; do
+testsuccessequal "phased1:
+ Installed: 1
+ Candidate: 100
+ Version table:
+ 100 500
+ 500 file:${TMPWORKINGDIRECTORY}/aptarchive unstable/main all Packages
+ 50 1 (phased 50%)
+ 500 file:${TMPWORKINGDIRECTORY}/aptarchive unstable/main all Packages
+ 10 1 (phased 10%)
+ 500 file:${TMPWORKINGDIRECTORY}/aptarchive unstable/main all Packages
+ *** 1 100
+ 100 ${TMPWORKINGDIRECTORY}/rootdir/var/lib/dpkg/status
+phased2:
+ Installed: 1
+ Candidate: 100
+ Version table:
+ 100 500
+ 500 file:${TMPWORKINGDIRECTORY}/aptarchive unstable/main all Packages
+ 50 1 (phased 50%)
+ 500 file:${TMPWORKINGDIRECTORY}/aptarchive unstable/main all Packages
+ 10 1 (phased 10%)
+ 500 file:${TMPWORKINGDIRECTORY}/aptarchive unstable/main all Packages
+ *** 1 100
+ 100 ${TMPWORKINGDIRECTORY}/rootdir/var/lib/dpkg/status
+phased3:
+ Installed: (none)
+ Candidate: 100
+ Version table:
+ 100 500
+ 500 file:${TMPWORKINGDIRECTORY}/aptarchive unstable/main all Packages
+ 50 500 (phased 50%)
+ 500 file:${TMPWORKINGDIRECTORY}/aptarchive unstable/main all Packages
+ 10 500 (phased 10%)
+ 500 file:${TMPWORKINGDIRECTORY}/aptarchive unstable/main all Packages" aptcache policy phased1 phased2 phased3 -o $never=true
+done
+
+msgmsg "Test that being in a chroot equals always-include-phased-updates"
+testsuccessequal "phased1:
+ Installed: 1
+ Candidate: 100
+ Version table:
+ 100 500
+ 500 file:${TMPWORKINGDIRECTORY}/aptarchive unstable/main all Packages
+ 50 500 (phased 50%)
+ 500 file:${TMPWORKINGDIRECTORY}/aptarchive unstable/main all Packages
+ 10 500 (phased 10%)
+ 500 file:${TMPWORKINGDIRECTORY}/aptarchive unstable/main all Packages
+ *** 1 100
+ 100 ${TMPWORKINGDIRECTORY}/rootdir/var/lib/dpkg/status
+phased2:
+ Installed: 1
+ Candidate: 100
+ Version table:
+ 100 500
+ 500 file:${TMPWORKINGDIRECTORY}/aptarchive unstable/main all Packages
+ 50 500 (phased 50%)
+ 500 file:${TMPWORKINGDIRECTORY}/aptarchive unstable/main all Packages
+ 10 500 (phased 10%)
+ 500 file:${TMPWORKINGDIRECTORY}/aptarchive unstable/main all Packages
+ *** 1 100
+ 100 ${TMPWORKINGDIRECTORY}/rootdir/var/lib/dpkg/status" aptcache policy phased1 phased2 -o Dir::Bin::ischroot=/bin/true
+
+msgmsg "Test that empty machine-id equals always-include-phased-updates"
+testsuccessequal "phased1:
+ Installed: 1
+ Candidate: 100
+ Version table:
+ 100 500
+ 500 file:${TMPWORKINGDIRECTORY}/aptarchive unstable/main all Packages
+ 50 500 (phased 50%)
+ 500 file:${TMPWORKINGDIRECTORY}/aptarchive unstable/main all Packages
+ 10 500 (phased 10%)
+ 500 file:${TMPWORKINGDIRECTORY}/aptarchive unstable/main all Packages
+ *** 1 100
+ 100 ${TMPWORKINGDIRECTORY}/rootdir/var/lib/dpkg/status
+phased2:
+ Installed: 1
+ Candidate: 100
+ Version table:
+ 100 500
+ 500 file:${TMPWORKINGDIRECTORY}/aptarchive unstable/main all Packages
+ 50 500 (phased 50%)
+ 500 file:${TMPWORKINGDIRECTORY}/aptarchive unstable/main all Packages
+ 10 500 (phased 10%)
+ 500 file:${TMPWORKINGDIRECTORY}/aptarchive unstable/main all Packages
+ *** 1 100
+ 100 ${TMPWORKINGDIRECTORY}/rootdir/var/lib/dpkg/status" aptcache policy phased1 phased2 -o Dir::Etc::machine-id=/dev/null -o APT::Machine-Id=""
+
+msgmsg "Test that never-include-phased-updates trumps empty machine-id"
+testsuccessequal "phased1:
+ Installed: 1
+ Candidate: 100
+ Version table:
+ 100 500
+ 500 file:${TMPWORKINGDIRECTORY}/aptarchive unstable/main all Packages
+ 50 1 (phased 50%)
+ 500 file:${TMPWORKINGDIRECTORY}/aptarchive unstable/main all Packages
+ 10 1 (phased 10%)
+ 500 file:${TMPWORKINGDIRECTORY}/aptarchive unstable/main all Packages
+ *** 1 100
+ 100 ${TMPWORKINGDIRECTORY}/rootdir/var/lib/dpkg/status
+phased2:
+ Installed: 1
+ Candidate: 100
+ Version table:
+ 100 500
+ 500 file:${TMPWORKINGDIRECTORY}/aptarchive unstable/main all Packages
+ 50 1 (phased 50%)
+ 500 file:${TMPWORKINGDIRECTORY}/aptarchive unstable/main all Packages
+ 10 1 (phased 10%)
+ 500 file:${TMPWORKINGDIRECTORY}/aptarchive unstable/main all Packages
+ *** 1 100
+ 100 ${TMPWORKINGDIRECTORY}/rootdir/var/lib/dpkg/status" aptcache policy phased1 phased2 -o Dir::Etc::machine-id=/dev/null -o APT::Machine-Id="" -o APT::Get::Never-Include-Phased-Updates=1
+
+
+msgmsg "Test that SOURCE_DATE_EPOCH set equals always-include-phased-updates"
+export SOURCE_DATE_EPOCH=0
+testsuccessequal "phased1:
+ Installed: 1
+ Candidate: 100
+ Version table:
+ 100 500
+ 500 file:${TMPWORKINGDIRECTORY}/aptarchive unstable/main all Packages
+ 50 500 (phased 50%)
+ 500 file:${TMPWORKINGDIRECTORY}/aptarchive unstable/main all Packages
+ 10 500 (phased 10%)
+ 500 file:${TMPWORKINGDIRECTORY}/aptarchive unstable/main all Packages
+ *** 1 100
+ 100 ${TMPWORKINGDIRECTORY}/rootdir/var/lib/dpkg/status
+phased2:
+ Installed: 1
+ Candidate: 100
+ Version table:
+ 100 500
+ 500 file:${TMPWORKINGDIRECTORY}/aptarchive unstable/main all Packages
+ 50 500 (phased 50%)
+ 500 file:${TMPWORKINGDIRECTORY}/aptarchive unstable/main all Packages
+ 10 500 (phased 10%)
+ 500 file:${TMPWORKINGDIRECTORY}/aptarchive unstable/main all Packages
+ *** 1 100
+ 100 ${TMPWORKINGDIRECTORY}/rootdir/var/lib/dpkg/status" aptcache policy phased1 phased2
+unset SOURCE_DATE_EPOCH
+
+msgmsg "Test that different machine-id produces different outcome"
+testsuccessequal "phased1:
+ Installed: 1
+ Candidate: 100
+ Version table:
+ 100 500
+ 500 file:${TMPWORKINGDIRECTORY}/aptarchive unstable/main all Packages
+ 50 500 (phased 50%)
+ 500 file:${TMPWORKINGDIRECTORY}/aptarchive unstable/main all Packages
+ 10 1 (phased 10%)
+ 500 file:${TMPWORKINGDIRECTORY}/aptarchive unstable/main all Packages
+ *** 1 100
+ 100 ${TMPWORKINGDIRECTORY}/rootdir/var/lib/dpkg/status
+phased2:
+ Installed: 1
+ Candidate: 100
+ Version table:
+ 100 500
+ 500 file:${TMPWORKINGDIRECTORY}/aptarchive unstable/main all Packages
+ 50 500 (phased 50%)
+ 500 file:${TMPWORKINGDIRECTORY}/aptarchive unstable/main all Packages
+ 10 1 (phased 10%)
+ 500 file:${TMPWORKINGDIRECTORY}/aptarchive unstable/main all Packages
+ *** 1 100
+ 100 ${TMPWORKINGDIRECTORY}/rootdir/var/lib/dpkg/status" aptcache policy phased1 phased2 -o apt::machine-id="00000000000000000000000000000000"