@@ -12,6 +12,7 @@ import (
1212 "github.com/microsoft/azurelinux/toolkit/tools/internal/packagerepo/repocloner/rpmrepocloner"
1313 "github.com/microsoft/azurelinux/toolkit/tools/internal/pkggraph"
1414 "github.com/microsoft/azurelinux/toolkit/tools/internal/pkgjson"
15+ "github.com/microsoft/azurelinux/toolkit/tools/internal/sliceutils"
1516 "github.com/microsoft/azurelinux/toolkit/tools/internal/timestamp"
1617 "github.com/microsoft/azurelinux/toolkit/tools/pkg/profile"
1718
@@ -23,14 +24,13 @@ var (
2324 input = exe .InputFlag (app , "Input json listing all local SRPMs" )
2425 output = exe .OutputFlag (app , "Output file to export the graph to" )
2526
26- logFlags = exe .SetupLogFlags (app )
27- profFlags = exe .SetupProfileFlags (app )
28- strictGoals = app .Flag ("strict-goals" , "Don't allow missing goal packages" ).Bool ()
29- strictUnresolved = app .Flag ("strict-unresolved" , "Don't allow missing unresolved packages" ).Bool ()
30- timestampFile = app .Flag ("timestamp-file" , "File that stores timestamps for this program." ).String ()
31- usePMCtoResolveCycles = app .Flag ("usePMCtoresolvecycles" , "Cycles will be resolved by downloading rpm packages from PMC if locally unavailable" ).Bool ()
32- tlsClientCert = app .Flag ("tls-cert" , "TLS client certificate to use when downloading files." ).String ()
33- tlsClientKey = app .Flag ("tls-key" , "TLS client key to use when downloading files." ).String ()
27+ logFlags = exe .SetupLogFlags (app )
28+ profFlags = exe .SetupProfileFlags (app )
29+ strictGoals = app .Flag ("strict-goals" , "Don't allow missing goal packages" ).Bool ()
30+ strictUnresolved = app .Flag ("strict-unresolved" , "Don't allow missing unresolved packages" ).Bool ()
31+ timestampFile = app .Flag ("timestamp-file" , "File that stores timestamps for this program." ).String ()
32+ tlsClientCert = app .Flag ("tls-cert" , "TLS client certificate to use when downloading files." ).String ()
33+ tlsClientKey = app .Flag ("tls-key" , "TLS client key to use when downloading files." ).String ()
3434
3535 resolveCyclesFromUpstream = app .Flag ("resolve-cycles-from-upstream" , "Let grapher resolve cycles by marking rpms available in repo as remote" ).Bool ()
3636 outDir = exe .OutputDirFlag (app , "Directory to download packages into." )
4343 disableDefaultRepos = app .Flag ("disable-default-repos" , "Disable pulling packages from PMC repos" ).Bool ()
4444 ignoreVersionToResolveSelfDep = app .Flag ("ignore-version-to-resolve-selfdep" , "Ignore package version while downloading package from upstream when resolving cycle" ).Bool ()
4545 repoSnapshotTime = app .Flag ("repo-snapshot-time" , "Optional: Repo time limit for tdnf virtual snapshot" ).String ()
46-
47- depGraph = pkggraph .NewPkgGraph ()
4846)
4947
5048func main () {
@@ -113,7 +111,7 @@ func main() {
113111}
114112
115113// addUnresolvedPackage adds an unresolved node to the graph representing the
116- // packged described in the PackgetVer structure. Returns an error if the node
114+ // package described in the PackageVer structure. Returns an error if the node
117115// could not be created.
118116func addUnresolvedPackage (g * pkggraph.PkgGraph , pkgVer * pkgjson.PackageVer ) (newRunNode * pkggraph.PkgNode , err error ) {
119117 logger .Log .Debugf ("Adding unresolved %s" , pkgVer )
@@ -122,7 +120,9 @@ func addUnresolvedPackage(g *pkggraph.PkgGraph, pkgVer *pkgjson.PackageVer) (new
122120 return
123121 }
124122
125- nodes , err := g .FindBestPkgNode (pkgVer )
123+ // Double check that the package is not already in the graph. A previous check should have already found the node
124+ // and no call to addUnresolvedPackage() should have been made.
125+ nodes , err := g .FindExactPkgNodeFromPkg (pkgVer )
126126 if err != nil {
127127 return
128128 }
@@ -205,6 +205,36 @@ func addNodesForPackage(g *pkggraph.PkgGraph, pkg *pkgjson.Package) (foundDuplic
205205 return
206206}
207207
208+ // findOrAddExactRemoteDependency ensures that a remote node is available in the graph for every unresolved dependency.
209+ // 1. Check if the exact dependency is already in the graph. If it is, reuse it.
210+ // 2. If it is not, create a new unresolved node for the dependency.
211+ // It is important that we only match on the exact dependency name and version. If we don't, we may end up with
212+ // unpredictable behavior in the scheduler. If two different remote dependencies are added to two different build
213+ // nodes of a single SRPM, then the scheduler may queue that node twice.
214+ func findOrAddExactRemoteDependency (g * pkggraph.PkgGraph , dependency * pkgjson.PackageVer ) (selectedRemoteNode * pkggraph.PkgNode , err error ) {
215+ existingRemoteNode , err := g .FindExactPkgNodeFromPkg (dependency )
216+ if err != nil {
217+ err = fmt .Errorf ("failed to check lookup list for exact remote %+v:\n %w" , dependency , err )
218+ return nil , err
219+ }
220+
221+ if existingRemoteNode == nil {
222+ // No exact match, add a new one.
223+ selectedRemoteNode , err = addUnresolvedPackage (g , dependency )
224+ if err != nil {
225+ err = fmt .Errorf ("failed to add a remote node (%s):\n %w" , dependency .Name , err )
226+ return nil , err
227+ }
228+ logger .Log .Debugf ("Added new node: '%s' for dependency %+v" , selectedRemoteNode .FriendlyName (), dependency )
229+ } else {
230+ // This exact dependency is already in the graph, so reuse it.
231+ selectedRemoteNode = existingRemoteNode .RunNode
232+ logger .Log .Debugf ("Found existing exact remote node: '%s' for dependency %+v" , selectedRemoteNode .FriendlyName (), dependency )
233+ }
234+
235+ return selectedRemoteNode , nil
236+ }
237+
208238// addSingleDependency will add an edge between packageNode and the "Run" node for the
209239// dependency described in the PackageVer structure. Returns an error if the
210240// addition failed.
@@ -217,15 +247,17 @@ func addSingleDependency(g *pkggraph.PkgGraph, packageNode *pkggraph.PkgNode, de
217247 return err
218248 }
219249
220- if nodes == nil {
221- dependentNode , err = addUnresolvedPackage (g , dependency )
250+ // If we can't find the dependency in the graph, or it is a remote dependency, we need to do a bit of extra validation.
251+ if nodes == nil || nodes .RunNode .Type != pkggraph .TypeLocalRun {
252+ dependentNode , err = findOrAddExactRemoteDependency (g , dependency )
222253 if err != nil {
223- err = fmt .Errorf ("failed to add a package (%s) :\n %w" , dependency . Name , err )
254+ err = fmt .Errorf ("failed to handle remote dependency from %+v to %+v :\n %w" , packageNode . VersionedPkg , dependency , err )
224255 return err
225256 }
226257 } else {
227258 // All dependencies are assumed to be "Run" dependencies
228259 dependentNode = nodes .RunNode
260+ logger .Log .Debugf ("Found existing node: '%s' for dependency %+v" , dependentNode .FriendlyName (), dependency )
229261 }
230262
231263 if packageNode == dependentNode {
@@ -335,10 +367,14 @@ func populateGraph(graph *pkggraph.PkgGraph, repo *pkgjson.PackageRepo) (err err
335367 timestamp .StopEvent (nil ) // add package nodes
336368 timestamp .StartEvent ("add dependencies" , nil )
337369
370+ // Sort the map to ensure the order is deterministic
371+ packageList := sliceutils .MapToSlice (uniquePackages )
372+ pkgjson .SortPackageList (packageList )
373+
338374 // Rescan and add all the dependencies
339375 logger .Log .Infof ("Adding all dependencies from (%s)" , * input )
340376 dependenciesAdded := 0
341- for uniquePkg := range uniquePackages {
377+ for _ , uniquePkg := range packageList {
342378 num , err := addPkgDependencies (graph , uniquePkg )
343379 if err != nil {
344380 err = fmt .Errorf ("failed to add dependency %+v:\n %w" , uniquePkg , err )
0 commit comments