-
Notifications
You must be signed in to change notification settings - Fork 47
/
list.go
173 lines (161 loc) · 5.37 KB
/
list.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
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
/*
Copyright (c) Edgeless Systems GmbH
SPDX-License-Identifier: AGPL-3.0-only
*/
package versionsapi
import (
"errors"
"fmt"
"net/url"
"path"
"github.com/edgelesssys/constellation/v2/internal/constants"
"golang.org/x/mod/semver"
)
// List represents a list of versions for a kind of resource.
// It has a granularity of either "major" or "minor".
//
// For example, a List with granularity "major" could contain
// the base version "v1" and a list of minor versions "v1.0", "v1.1", "v1.2" etc.
// A List with granularity "minor" could contain the base version
// "v1.0" and a list of patch versions "v1.0.0", "v1.0.1", "v1.0.2" etc.
type List struct {
// Ref is the branch name the list belongs to.
Ref string `json:"ref,omitempty"`
// Stream is the update stream of the list.
Stream string `json:"stream,omitempty"`
// Granularity is the granularity of the base version of this list.
// It can be either "major" or "minor".
Granularity Granularity `json:"granularity,omitempty"`
// Base is the base version of the list.
// Every version in the list is a finer-grained version of this base version.
Base string `json:"base,omitempty"`
// Kind is the kind of resource this list is for.
Kind VersionKind `json:"kind,omitempty"`
// Versions is a list of all versions in this list.
Versions []string `json:"versions,omitempty"`
}
// JSONPath returns the S3 JSON path for this object.
func (l List) JSONPath() string {
return path.Join(
constants.CDNAPIPrefix,
"ref", l.Ref,
"stream", l.Stream,
"versions", l.Granularity.String(), l.Base,
l.Kind.String()+".json",
)
}
// URL returns the URL for this object.
func (l List) URL() (string, error) {
url, err := url.Parse(constants.CDNRepositoryURL)
if err != nil {
return "", fmt.Errorf("parsing CDN URL: %w", err)
}
url.Path = l.JSONPath()
return url.String(), nil
}
// ValidateRequest validates the request parameters of the list.
// The versions field must be empty.
func (l List) ValidateRequest() error {
var retErr error
if err := ValidateRef(l.Ref); err != nil {
retErr = errors.Join(retErr, err)
}
if err := ValidateStream(l.Ref, l.Stream); err != nil {
retErr = errors.Join(retErr, err)
}
if l.Granularity != GranularityMajor && l.Granularity != GranularityMinor {
retErr = errors.Join(retErr, fmt.Errorf("granularity %q is not supported", l.Granularity))
}
if l.Kind == VersionKindUnknown {
retErr = errors.Join(retErr, fmt.Errorf("kind %q is not supported", l.Kind))
}
if !semver.IsValid(l.Base) {
retErr = errors.Join(retErr, fmt.Errorf("base version %q is not a valid semantic version", l.Base))
}
var normalizeFunc func(string) string
switch l.Granularity {
case GranularityMajor:
normalizeFunc = semver.Major
case GranularityMinor:
normalizeFunc = semver.MajorMinor
default:
normalizeFunc = func(s string) string { return s }
}
if normalizeFunc(l.Base) != l.Base {
retErr = errors.Join(retErr, fmt.Errorf("base version %q does not match granularity %q", l.Base, l.Granularity))
}
if len(l.Versions) != 0 {
retErr = errors.Join(retErr, fmt.Errorf("versions must be empty for request"))
}
return retErr
}
// Validate checks if the list is valid.
// This performs the following checks:
// - The ref is set.
// - The stream is supported.
// - The granularity is "major" or "minor".
// - The kind is supported.
// - The base version is a valid semantic version that matches the granularity.
// - All versions in the list are valid semantic versions that are finer-grained than the base version.
func (l List) Validate() error {
var retErr error
if err := ValidateRef(l.Ref); err != nil {
retErr = errors.Join(retErr, err)
}
if err := ValidateStream(l.Ref, l.Stream); err != nil {
retErr = errors.Join(retErr, err)
}
if l.Granularity != GranularityMajor && l.Granularity != GranularityMinor {
retErr = errors.Join(retErr, fmt.Errorf("granularity %q is not supported", l.Granularity))
}
if l.Kind == VersionKindUnknown {
retErr = errors.Join(retErr, fmt.Errorf("kind %q is not supported", l.Kind))
}
if !semver.IsValid(l.Base) {
retErr = errors.Join(retErr, fmt.Errorf("base version %q is not a valid semantic version", l.Base))
}
var normalizeFunc func(string) string
switch l.Granularity {
case GranularityMajor:
normalizeFunc = semver.Major
case GranularityMinor:
normalizeFunc = semver.MajorMinor
default:
normalizeFunc = func(s string) string { return s }
}
if normalizeFunc(l.Base) != l.Base {
retErr = errors.Join(retErr, fmt.Errorf("base version %q does not match granularity %q", l.Base, l.Granularity))
}
for _, ver := range l.Versions {
if !semver.IsValid(ver) {
retErr = errors.Join(retErr, fmt.Errorf("version %q is not a valid semantic version", ver))
}
if normalizeFunc(ver) != l.Base || normalizeFunc(ver) == ver {
retErr = errors.Join(retErr, fmt.Errorf("version %q is not finer-grained than base version %q", ver, l.Base))
}
}
return retErr
}
// Contains returns true if the list contains the given version.
func (l List) Contains(version string) bool {
for _, v := range l.Versions {
if v == version {
return true
}
}
return false
}
// StructuredVersions returns the versions of the list as slice of
// Version structs.
func (l List) StructuredVersions() []Version {
versions := make([]Version, len(l.Versions))
for i, v := range l.Versions {
versions[i] = Version{
ref: l.Ref,
stream: l.Stream,
version: v,
kind: l.Kind,
}
}
return versions
}