Story #2637

Updated by about 7 years ago

h1. Context 

 Debian dependency relationships should be represented as fields on the unit model, so that a developer could resolve dependencies simply by querying the available units, without having to download the package. 

 This story does not imply such a dependency resolution would be implemented anywhere in the plugin - only that we need to make the data available for other potential consumers. 

 h1. Description 

 Pulp currently represents Requires and Provides for rpm packages as lists of dictionaries: 

 > :ivar provides: List of packages/libraries this package provides. Each entry is a 
 > dictionary with the "release", "epoch", "version", "flags", and "name" 
 > field. 
 > :type provides: list of dict 
 > :ivar requires: List of packages/libraries this package requires. Each entry is a dictionary 
 > with the "release", "epoch", "version", "flags", and "name" field. 
 > :type requires: list of dict 

 flags is one of None, EQ, LT, LE, GT, GE. 

 Debian package dependencies are "different". They support version comparisons, just like rpm packages. In addition to that, they support architecture matching (positive or negative), build profile matching (positive or negative). They also support disjunction (I want python2 or python3). 

 The python-debian library has a deb822 module that will parse these dependencies and return some format that I don't want to store directly in pulp, because it's far too distant from what we have in rpm. 

 Here is the documentation from deb822: 

 > @property 
 > def relations(self): 
 > """Return a dictionary of inter-package relationships among the current 
 > and other packages. 
 > Dictionary keys depend on the package kind. Binary packages have keys 
 > like 'depends', 'recommends', ... while source packages have keys like 
 > 'build-depends', 'build-depends-indep' and so on. See the Debian policy 
 > for the comprehensive field list. 
 > Dictionary values are package relationships returned as lists of lists 
 > of dictionaries (see below for some examples). 
 > The encoding of package relationships is as follows: 
 > - the top-level lists corresponds to the comma-separated list of 
 > Deb822, their components form a conjunction, i.e. they have to be 
 > AND-ed together 
 > - the inner lists corresponds to the pipe-separated list of Deb822, 
 > their components form a disjunction, i.e. they have to be OR-ed 
 > together 
 > - member of the inner lists are dictionaries with the following keys: 
 > - name:         package (or virtual package) name 
 > - version:      A pair <operator, version> if the relationship is 
 > versioned, None otherwise. operator is one of "<<", 
 > "<=", "=", ">=", ">>"; version is the given version as 
 > a string. 
 > - arch:         A list of pairs <enabled, arch> if the 
 > relationship is architecture specific, None otherwise. 
 > Enabled is a boolean (false if the architecture is 
 > negated with "!", true otherwise), arch the 
 > Debian architecture name as a string. 
 > - restrictions: A list of lists of tuples <enabled, profile> 
 > if there is a restriction formula defined, None 
 > otherwise. Each list of tuples represents a restriction 
 > list while each tuple represents an individual term 
 > within the restriction list. Enabled is a boolean 
 > (false if the restriction is negated with "!", true 
 > otherwise). The profile is the name of the build 
 > restriction. 
 > The arch and restrictions tuples are available as named tuples so 
 > elements are available as term[0] or alternatively as 
 > term.enabled (and so forth). 
 > Examples: 
 > "emacs | emacsen, make, debianutils (>= 1.7)"       becomes 
 > [ [ {'name': 'emacs'}, {'name': 'emacsen'} ], 
 > [ {'name': 'make'} ], 
 > [ {'name': 'debianutils', 'version': ('>=', '1.7')} ] ] 
 > "tcl8.4-dev, procps [!hurd-i386]"                   becomes 
 > [ [ {'name': 'tcl8.4-dev'} ], 
 > [ {'name': 'procps', 'arch': (false, 'hurd-i386')} ] ] 
 > "texlive <!cross>"                                  becomes 
 > [ [ {'name': 'texlive', 
 > 'restriction': [[(false, 'cross')]]} ] ] 
 > """ 
 I personally don't like the list of (disjunction of lists) representation, given that it's a relatively uncommon case. Also, I don't like breaking out the arch and profile as tuples just to represent positive or negative matches. 

 My proposal is to reuse as much as possible from Yum's syntax. Like so: 

 * single package is a dictionary, conjunction is a list of packages (i.e. dictionaries). 
 * flag represents the operator of the relationship if one exists, and use the same string values as rpm: GT, GE, LT, LE, EQ. arch 
 * arch is a list of one or more strings. Negation is represented with a leading exclamation mark 
 * restriction, if present, is a list of one or more strings, with the same meaning as arch. 

 So, for the examples in the deb822 package: 

 "emacs | emacsen, make, debianutils (>= 1.7)" 
 deb822: [ [ {'name': 'emacs'}, {'name': 'emacsen'} ], [ {'name': 'make'} ], [ {'name': 'debianutils', 'version': ('>=', '1.7')} ] ] 
 pulp: [ [{'name': 'emacs'}, {'name': 'emacsen'} ], {'name': 'make'}, {'name': 'debianutils', 'version': '1.7', 'flag': 'GE'} ] 

 "tcl8.4-dev, procps [!hurd-i386]" 
 deb822: [ [ {'name': 'tcl8.4-dev'} ], [ {'name': 'procps', 'arch': (false, 'hurd-i386')} ] ] 
 pulp: [ {'name': 'tcl8.4-dev'}, {'name': 'procps', 'arch': ['!hurd-i383']} ] 

 "texlive <!cross>" 
 deb822: [ [ {'name': 'texlive', 'restriction': [[(false, 'cross')]]} ] ] 
 pulp: [ {'name': 'texlive', 'restriction': ['!cross']} ] 

 h1. Acceptance Criteria 

 * PASS: import debian package with some dependency data. pulp-admin should display the dependency data.