2022-12-29 11:23:23 -05:00
|
|
|
/*
|
|
|
|
Copyright (c) Edgeless Systems GmbH
|
|
|
|
|
|
|
|
SPDX-License-Identifier: AGPL-3.0-only
|
|
|
|
*/
|
|
|
|
|
|
|
|
package versionsapi
|
|
|
|
|
|
|
|
import (
|
2023-02-07 06:56:25 -05:00
|
|
|
"errors"
|
2022-12-29 11:23:23 -05:00
|
|
|
"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 {
|
2023-02-07 06:56:25 -05:00
|
|
|
retErr = errors.Join(retErr, err)
|
2022-12-29 11:23:23 -05:00
|
|
|
}
|
|
|
|
if err := ValidateStream(l.Ref, l.Stream); err != nil {
|
2023-02-07 06:56:25 -05:00
|
|
|
retErr = errors.Join(retErr, err)
|
2022-12-29 11:23:23 -05:00
|
|
|
}
|
|
|
|
if l.Granularity != GranularityMajor && l.Granularity != GranularityMinor {
|
2023-02-07 06:56:25 -05:00
|
|
|
retErr = errors.Join(retErr, fmt.Errorf("granularity %q is not supported", l.Granularity))
|
2022-12-29 11:23:23 -05:00
|
|
|
}
|
2023-03-10 04:21:58 -05:00
|
|
|
if l.Kind == VersionKindUnknown {
|
2023-02-07 06:56:25 -05:00
|
|
|
retErr = errors.Join(retErr, fmt.Errorf("kind %q is not supported", l.Kind))
|
2022-12-29 11:23:23 -05:00
|
|
|
}
|
|
|
|
if !semver.IsValid(l.Base) {
|
2023-02-07 06:56:25 -05:00
|
|
|
retErr = errors.Join(retErr, fmt.Errorf("base version %q is not a valid semantic version", l.Base))
|
2022-12-29 11:23:23 -05:00
|
|
|
}
|
|
|
|
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 {
|
2023-02-07 06:56:25 -05:00
|
|
|
retErr = errors.Join(retErr, fmt.Errorf("base version %q does not match granularity %q", l.Base, l.Granularity))
|
2022-12-29 11:23:23 -05:00
|
|
|
}
|
|
|
|
if len(l.Versions) != 0 {
|
2023-02-07 06:56:25 -05:00
|
|
|
retErr = errors.Join(retErr, fmt.Errorf("versions must be empty for request"))
|
2022-12-29 11:23:23 -05:00
|
|
|
}
|
|
|
|
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 {
|
2023-02-07 06:56:25 -05:00
|
|
|
retErr = errors.Join(retErr, err)
|
2022-12-29 11:23:23 -05:00
|
|
|
}
|
|
|
|
if err := ValidateStream(l.Ref, l.Stream); err != nil {
|
2023-02-07 06:56:25 -05:00
|
|
|
retErr = errors.Join(retErr, err)
|
2022-12-29 11:23:23 -05:00
|
|
|
}
|
|
|
|
if l.Granularity != GranularityMajor && l.Granularity != GranularityMinor {
|
2023-02-07 06:56:25 -05:00
|
|
|
retErr = errors.Join(retErr, fmt.Errorf("granularity %q is not supported", l.Granularity))
|
2022-12-29 11:23:23 -05:00
|
|
|
}
|
2023-03-10 04:21:58 -05:00
|
|
|
if l.Kind == VersionKindUnknown {
|
2023-02-07 06:56:25 -05:00
|
|
|
retErr = errors.Join(retErr, fmt.Errorf("kind %q is not supported", l.Kind))
|
2022-12-29 11:23:23 -05:00
|
|
|
}
|
|
|
|
if !semver.IsValid(l.Base) {
|
2023-02-07 06:56:25 -05:00
|
|
|
retErr = errors.Join(retErr, fmt.Errorf("base version %q is not a valid semantic version", l.Base))
|
2022-12-29 11:23:23 -05:00
|
|
|
}
|
|
|
|
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 {
|
2023-02-07 06:56:25 -05:00
|
|
|
retErr = errors.Join(retErr, fmt.Errorf("base version %q does not match granularity %q", l.Base, l.Granularity))
|
2022-12-29 11:23:23 -05:00
|
|
|
}
|
|
|
|
for _, ver := range l.Versions {
|
|
|
|
if !semver.IsValid(ver) {
|
2023-02-07 06:56:25 -05:00
|
|
|
retErr = errors.Join(retErr, fmt.Errorf("version %q is not a valid semantic version", ver))
|
2022-12-29 11:23:23 -05:00
|
|
|
}
|
|
|
|
if normalizeFunc(ver) != l.Base || normalizeFunc(ver) == ver {
|
2023-02-07 06:56:25 -05:00
|
|
|
retErr = errors.Join(retErr, fmt.Errorf("version %q is not finer-grained than base version %q", ver, l.Base))
|
2022-12-29 11:23:23 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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
|
|
|
|
}
|