-
Notifications
You must be signed in to change notification settings - Fork 200
/
solver.go
114 lines (92 loc) · 2.8 KB
/
solver.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
package cnab
import (
"sort"
"github.com/Masterminds/semver/v3"
"github.com/google/go-containerregistry/pkg/crane"
"github.com/pkg/errors"
)
type DependencyLock struct {
Alias string
Reference string
}
// TODO: move this logic onto the new ExtendedBundle struct
type DependencySolver struct {
}
func (s *DependencySolver) ResolveDependencies(bun ExtendedBundle) ([]DependencyLock, error) {
if !bun.HasDependencies() {
return nil, nil
}
rawDeps, err := bun.ReadDependencies()
// We need make sure the Dependencies are ordered by the desired sequence
orderedDeps := rawDeps.ListBySequence()
if err != nil {
return nil, errors.Wrapf(err, "error executing dependencies for %s", bun.Name)
}
q := make([]DependencyLock, 0, len(orderedDeps))
for _, dep := range orderedDeps {
ref, err := s.ResolveVersion(dep.Name, dep)
if err != nil {
return nil, err
}
lock := DependencyLock{
Alias: dep.Name,
Reference: ref.String(),
}
q = append(q, lock)
}
return q, nil
}
// ResolveVersion returns the bundle name, its version and any error.
func (s *DependencySolver) ResolveVersion(name string, dep Dependency) (OCIReference, error) {
ref, err := ParseOCIReference(dep.Bundle)
if err != nil {
return OCIReference{}, errors.Wrapf(err, "error parsing dependency (%s) bundle %q as OCI reference", name, dep.Bundle)
}
// Here is where we could split out this logic into multiple strategy funcs / structs if necessary
if dep.Version == nil || len(dep.Version.Ranges) == 0 {
// Check if they specified an explicit tag in referenced bundle already
if ref.HasTag() {
return ref, nil
}
tag, err := s.determineDefaultTag(dep)
if err != nil {
return OCIReference{}, err
}
return ref.WithTag(tag)
}
return OCIReference{}, errors.Errorf("not implemented: dependency version range specified for %s", name)
}
func (s *DependencySolver) determineDefaultTag(dep Dependency) (string, error) {
tags, err := crane.ListTags(dep.Bundle)
if err != nil {
return "", errors.Wrapf(err, "error listing tags for %s", dep.Bundle)
}
allowPrereleases := false
if dep.Version != nil && dep.Version.AllowPrereleases {
allowPrereleases = true
}
var hasLatest bool
versions := make(semver.Collection, 0, len(tags))
for _, tag := range tags {
if tag == "latest" {
hasLatest = true
continue
}
version, err := semver.NewVersion(tag)
if err == nil {
if !allowPrereleases && version.Prerelease() != "" {
continue
}
versions = append(versions, version)
}
}
if len(versions) == 0 {
if hasLatest {
return "latest", nil
} else {
return "", errors.Errorf("no tag was specified for %s and none of the tags defined in the registry meet the criteria: semver formatted or 'latest'", dep.Bundle)
}
}
sort.Sort(sort.Reverse(versions))
return versions[0].Original(), nil
}