mirror of
https://github.com/edgelesssys/constellation.git
synced 2025-07-23 15:30:44 -04:00
versionsapi: canonicalize ref in version constructors (#3652)
* versionsapi: canonicalize ref in version constructors Co-authored-by: 3u13r <lc@edgeless.systems> Signed-off-by: Paul Meyer <katexochen0@gmail.com> * hack/cli-k8s-compat: canonicalize ref --------- Signed-off-by: Paul Meyer <katexochen0@gmail.com> Co-authored-by: Paul Meyer <katexochen0@gmail.com>
This commit is contained in:
parent
f8a95de174
commit
99a81cd246
4 changed files with 156 additions and 75 deletions
|
@ -45,7 +45,7 @@ func main() {
|
||||||
}
|
}
|
||||||
|
|
||||||
cliInfo := versionsapi.CLIInfo{
|
cliInfo := versionsapi.CLIInfo{
|
||||||
Ref: *refFlag,
|
Ref: versionsapi.CanonicalizeRef(*refFlag),
|
||||||
Stream: *streamFlag,
|
Stream: *streamFlag,
|
||||||
Version: *versionFlag,
|
Version: *versionFlag,
|
||||||
Kubernetes: []string{},
|
Kubernetes: []string{},
|
||||||
|
|
|
@ -16,7 +16,6 @@ import (
|
||||||
"github.com/edgelesssys/constellation/v2/internal/api/versionsapi"
|
"github.com/edgelesssys/constellation/v2/internal/api/versionsapi"
|
||||||
"github.com/edgelesssys/constellation/v2/internal/logger"
|
"github.com/edgelesssys/constellation/v2/internal/logger"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"golang.org/x/mod/semver"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func newAddCmd() *cobra.Command {
|
func newAddCmd() *cobra.Command {
|
||||||
|
@ -53,19 +52,8 @@ func runAdd(cmd *cobra.Command, _ []string) (retErr error) {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
log := logger.NewTextLogger(flags.logLevel)
|
log := logger.NewTextLogger(flags.logLevel)
|
||||||
log.Debug("Using flags", "dryRun", flags.dryRun, "kind", flags.kind, "latest", flags.latest, "ref", flags.ref,
|
log.Debug("Using flags", "dryRun", flags.dryRun, "kind", flags.version.Kind(), "latest", flags.latest, "ref", flags.version.Ref(),
|
||||||
"release", flags.release, "stream", flags.stream, "version", flags.version)
|
"stream", flags.version.Stream(), "version", flags.version.Version())
|
||||||
|
|
||||||
log.Debug("Validating flags")
|
|
||||||
if err := flags.validate(log); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Debug("Creating version struct")
|
|
||||||
ver, err := versionsapi.NewVersion(flags.ref, flags.stream, flags.version, flags.kind)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("creating version: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Debug("Creating versions API client")
|
log.Debug("Creating versions API client")
|
||||||
client, clientClose, err := versionsapi.NewClient(cmd.Context(), flags.region, flags.bucket, flags.distributionID, flags.dryRun, log)
|
client, clientClose, err := versionsapi.NewClient(cmd.Context(), flags.region, flags.bucket, flags.distributionID, flags.dryRun, log)
|
||||||
|
@ -80,27 +68,27 @@ func runAdd(cmd *cobra.Command, _ []string) (retErr error) {
|
||||||
}()
|
}()
|
||||||
|
|
||||||
log.Info("Adding version")
|
log.Info("Adding version")
|
||||||
if err := ensureVersion(cmd.Context(), client, flags.kind, ver, versionsapi.GranularityMajor, log); err != nil {
|
if err := ensureVersion(cmd.Context(), client, flags.version, versionsapi.GranularityMajor, log); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := ensureVersion(cmd.Context(), client, flags.kind, ver, versionsapi.GranularityMinor, log); err != nil {
|
if err := ensureVersion(cmd.Context(), client, flags.version, versionsapi.GranularityMinor, log); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if flags.latest {
|
if flags.latest {
|
||||||
if err := updateLatest(cmd.Context(), client, flags.kind, ver, log); err != nil {
|
if err := updateLatest(cmd.Context(), client, flags.version, log); err != nil {
|
||||||
return fmt.Errorf("setting latest version: %w", err)
|
return fmt.Errorf("setting latest version: %w", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Info(fmt.Sprintf("List major->minor URL: %s", ver.ListURL(versionsapi.GranularityMajor)))
|
log.Info(fmt.Sprintf("List major->minor URL: %s", flags.version.ListURL(versionsapi.GranularityMajor)))
|
||||||
log.Info(fmt.Sprintf("List minor->patch URL: %s", ver.ListURL(versionsapi.GranularityMinor)))
|
log.Info(fmt.Sprintf("List minor->patch URL: %s", flags.version.ListURL(versionsapi.GranularityMinor)))
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func ensureVersion(ctx context.Context, client *versionsapi.Client, kind versionsapi.VersionKind, ver versionsapi.Version, gran versionsapi.Granularity,
|
func ensureVersion(ctx context.Context, client *versionsapi.Client, ver versionsapi.Version, gran versionsapi.Granularity,
|
||||||
log *slog.Logger,
|
log *slog.Logger,
|
||||||
) error {
|
) error {
|
||||||
verListReq := versionsapi.List{
|
verListReq := versionsapi.List{
|
||||||
|
@ -108,7 +96,7 @@ func ensureVersion(ctx context.Context, client *versionsapi.Client, kind version
|
||||||
Stream: ver.Stream(),
|
Stream: ver.Stream(),
|
||||||
Granularity: gran,
|
Granularity: gran,
|
||||||
Base: ver.WithGranularity(gran),
|
Base: ver.WithGranularity(gran),
|
||||||
Kind: kind,
|
Kind: ver.Kind(),
|
||||||
}
|
}
|
||||||
verList, err := client.FetchVersionList(ctx, verListReq)
|
verList, err := client.FetchVersionList(ctx, verListReq)
|
||||||
var notFoundErr *apiclient.NotFoundError
|
var notFoundErr *apiclient.NotFoundError
|
||||||
|
@ -140,11 +128,11 @@ func ensureVersion(ctx context.Context, client *versionsapi.Client, kind version
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateLatest(ctx context.Context, client *versionsapi.Client, kind versionsapi.VersionKind, ver versionsapi.Version, log *slog.Logger) error {
|
func updateLatest(ctx context.Context, client *versionsapi.Client, ver versionsapi.Version, log *slog.Logger) error {
|
||||||
latest := versionsapi.Latest{
|
latest := versionsapi.Latest{
|
||||||
Ref: ver.Ref(),
|
Ref: ver.Ref(),
|
||||||
Stream: ver.Stream(),
|
Stream: ver.Stream(),
|
||||||
Kind: kind,
|
Kind: ver.Kind(),
|
||||||
}
|
}
|
||||||
latest, err := client.FetchVersionLatest(ctx, latest)
|
latest, err := client.FetchVersionLatest(ctx, latest)
|
||||||
var notFoundErr *apiclient.NotFoundError
|
var notFoundErr *apiclient.NotFoundError
|
||||||
|
@ -164,7 +152,7 @@ func updateLatest(ctx context.Context, client *versionsapi.Client, kind versions
|
||||||
Ref: ver.Ref(),
|
Ref: ver.Ref(),
|
||||||
Stream: ver.Stream(),
|
Stream: ver.Stream(),
|
||||||
Version: ver.Version(),
|
Version: ver.Version(),
|
||||||
Kind: kind,
|
Kind: ver.Kind(),
|
||||||
}
|
}
|
||||||
if err := client.UpdateVersionLatest(ctx, latest); err != nil {
|
if err := client.UpdateVersionLatest(ctx, latest); err != nil {
|
||||||
return fmt.Errorf("updating latest version: %w", err)
|
return fmt.Errorf("updating latest version: %w", err)
|
||||||
|
@ -174,60 +162,20 @@ func updateLatest(ctx context.Context, client *versionsapi.Client, kind versions
|
||||||
}
|
}
|
||||||
|
|
||||||
type addFlags struct {
|
type addFlags struct {
|
||||||
version string
|
version versionsapi.Version
|
||||||
stream string
|
|
||||||
ref string
|
|
||||||
release bool
|
|
||||||
latest bool
|
latest bool
|
||||||
dryRun bool
|
dryRun bool
|
||||||
region string
|
region string
|
||||||
bucket string
|
bucket string
|
||||||
distributionID string
|
distributionID string
|
||||||
kind versionsapi.VersionKind
|
|
||||||
logLevel slog.Level
|
logLevel slog.Level
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *addFlags) validate(log *slog.Logger) error {
|
|
||||||
if !semver.IsValid(f.version) {
|
|
||||||
return fmt.Errorf("version %q is not a valid semantic version", f.version)
|
|
||||||
}
|
|
||||||
if semver.Canonical(f.version) != f.version {
|
|
||||||
return fmt.Errorf("version %q is not a canonical semantic version", f.version)
|
|
||||||
}
|
|
||||||
|
|
||||||
if f.ref == "" && !f.release {
|
|
||||||
return fmt.Errorf("either --ref or --release must be set")
|
|
||||||
}
|
|
||||||
|
|
||||||
if f.kind == versionsapi.VersionKindUnknown {
|
|
||||||
return fmt.Errorf("unknown version kind %q", f.kind)
|
|
||||||
}
|
|
||||||
|
|
||||||
if f.release {
|
|
||||||
log.Debug(fmt.Sprintf("Setting ref to %q, as release flag is set", versionsapi.ReleaseRef))
|
|
||||||
f.ref = versionsapi.ReleaseRef
|
|
||||||
} else {
|
|
||||||
log.Debug("Setting latest to true, as release flag is not set")
|
|
||||||
f.latest = true // always set latest for non-release versions
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := versionsapi.ValidateRef(f.ref); err != nil {
|
|
||||||
return fmt.Errorf("invalid ref %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := versionsapi.ValidateStream(f.ref, f.stream); err != nil {
|
|
||||||
return fmt.Errorf("invalid stream %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseAddFlags(cmd *cobra.Command) (addFlags, error) {
|
func parseAddFlags(cmd *cobra.Command) (addFlags, error) {
|
||||||
ref, err := cmd.Flags().GetString("ref")
|
ref, err := cmd.Flags().GetString("ref")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return addFlags{}, err
|
return addFlags{}, err
|
||||||
}
|
}
|
||||||
ref = versionsapi.CanonicalizeRef(ref)
|
|
||||||
stream, err := cmd.Flags().GetString("stream")
|
stream, err := cmd.Flags().GetString("stream")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return addFlags{}, err
|
return addFlags{}, err
|
||||||
|
@ -274,17 +222,24 @@ func parseAddFlags(cmd *cobra.Command) (addFlags, error) {
|
||||||
return addFlags{}, err
|
return addFlags{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if release {
|
||||||
|
ref = versionsapi.ReleaseRef
|
||||||
|
} else {
|
||||||
|
latest = true // always set latest for non-release versions
|
||||||
|
}
|
||||||
|
|
||||||
|
ver, err := versionsapi.NewVersion(ref, stream, version, kind)
|
||||||
|
if err != nil {
|
||||||
|
return addFlags{}, fmt.Errorf("creating version: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
return addFlags{
|
return addFlags{
|
||||||
version: version,
|
version: ver,
|
||||||
stream: stream,
|
|
||||||
ref: versionsapi.CanonicalizeRef(ref),
|
|
||||||
release: release,
|
|
||||||
latest: latest,
|
latest: latest,
|
||||||
dryRun: dryRun,
|
dryRun: dryRun,
|
||||||
region: region,
|
region: region,
|
||||||
bucket: bucket,
|
bucket: bucket,
|
||||||
distributionID: distributionID,
|
distributionID: distributionID,
|
||||||
logLevel: logLevel,
|
logLevel: logLevel,
|
||||||
kind: kind,
|
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,7 +41,7 @@ type Version struct {
|
||||||
// NewVersion creates a new Version object and validates it.
|
// NewVersion creates a new Version object and validates it.
|
||||||
func NewVersion(ref, stream, version string, kind VersionKind) (Version, error) {
|
func NewVersion(ref, stream, version string, kind VersionKind) (Version, error) {
|
||||||
ver := Version{
|
ver := Version{
|
||||||
ref: ref,
|
ref: CanonicalizeRef(ref),
|
||||||
stream: stream,
|
stream: stream,
|
||||||
version: version,
|
version: version,
|
||||||
kind: kind,
|
kind: kind,
|
||||||
|
@ -62,7 +62,7 @@ func NewVersionFromShortPath(shortPath string, kind VersionKind) (Version, error
|
||||||
}
|
}
|
||||||
|
|
||||||
ver := Version{
|
ver := Version{
|
||||||
ref: ref,
|
ref: ref, // Canonicalized by parseShortPath.
|
||||||
stream: stream,
|
stream: stream,
|
||||||
version: version,
|
version: version,
|
||||||
kind: kind,
|
kind: kind,
|
||||||
|
@ -331,7 +331,7 @@ func CanonicalizeRef(ref string) string {
|
||||||
canRef := notAZ09Regexp.ReplaceAllString(ref, "-")
|
canRef := notAZ09Regexp.ReplaceAllString(ref, "-")
|
||||||
|
|
||||||
if canRef == ReleaseRef {
|
if canRef == ReleaseRef {
|
||||||
return "" // No ref should be cannonicalized to the release ref.
|
return "" // No ref should be canonicalized to the release ref.
|
||||||
}
|
}
|
||||||
|
|
||||||
return canRef
|
return canRef
|
||||||
|
@ -401,7 +401,7 @@ func MeasurementURL(version Version) (measurementURL, signatureURL *url.URL, err
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
shortPathRegex = regexp.MustCompile(`^ref/([a-zA-Z0-9-]+)/stream/([a-zA-Z0-9-]+)/([a-zA-Z0-9.-]+)$`)
|
shortPathRegex = regexp.MustCompile(`^ref/([^/]+)/stream/([a-zA-Z0-9-]+)/([a-zA-Z0-9.-]+)$`)
|
||||||
shortPathReleaseRegex = regexp.MustCompile(`^stream/([a-zA-Z0-9-]+)/([a-zA-Z0-9.-]+)$`)
|
shortPathReleaseRegex = regexp.MustCompile(`^stream/([a-zA-Z0-9-]+)/([a-zA-Z0-9.-]+)$`)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -422,6 +422,7 @@ func parseShortPath(shortPath string) (ref, stream, version string, err error) {
|
||||||
if shortPathRegex.MatchString(shortPath) {
|
if shortPathRegex.MatchString(shortPath) {
|
||||||
matches := shortPathRegex.FindStringSubmatch(shortPath)
|
matches := shortPathRegex.FindStringSubmatch(shortPath)
|
||||||
ref := matches[1]
|
ref := matches[1]
|
||||||
|
ref = CanonicalizeRef(ref)
|
||||||
if err := ValidateRef(ref); err != nil {
|
if err := ValidateRef(ref); err != nil {
|
||||||
return "", "", "", err
|
return "", "", "", err
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,111 @@ import (
|
||||||
"github.com/edgelesssys/constellation/v2/internal/constants"
|
"github.com/edgelesssys/constellation/v2/internal/constants"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func TestNewVersion(t *testing.T) {
|
||||||
|
testCases := map[string]struct {
|
||||||
|
ref string
|
||||||
|
stream string
|
||||||
|
version string
|
||||||
|
kind VersionKind
|
||||||
|
wantVer Version
|
||||||
|
wantErr bool
|
||||||
|
}{
|
||||||
|
"stable release image": {
|
||||||
|
ref: ReleaseRef,
|
||||||
|
stream: "stable",
|
||||||
|
version: "v9.9.9",
|
||||||
|
kind: VersionKindImage,
|
||||||
|
wantVer: Version{
|
||||||
|
ref: ReleaseRef,
|
||||||
|
stream: "stable",
|
||||||
|
version: "v9.9.9",
|
||||||
|
kind: VersionKindImage,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"release debug image": {
|
||||||
|
ref: ReleaseRef,
|
||||||
|
stream: "debug",
|
||||||
|
version: "v9.9.9",
|
||||||
|
kind: VersionKindImage,
|
||||||
|
wantVer: Version{
|
||||||
|
ref: ReleaseRef,
|
||||||
|
stream: "debug",
|
||||||
|
version: "v9.9.9",
|
||||||
|
kind: VersionKindImage,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"stable release cli": {
|
||||||
|
ref: ReleaseRef,
|
||||||
|
stream: "stable",
|
||||||
|
version: "v9.9.9",
|
||||||
|
kind: VersionKindCLI,
|
||||||
|
wantVer: Version{
|
||||||
|
ref: ReleaseRef,
|
||||||
|
stream: "stable",
|
||||||
|
version: "v9.9.9",
|
||||||
|
kind: VersionKindCLI,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"release debug cli": {
|
||||||
|
ref: ReleaseRef,
|
||||||
|
stream: "debug",
|
||||||
|
version: "v9.9.9",
|
||||||
|
kind: VersionKindCLI,
|
||||||
|
wantVer: Version{
|
||||||
|
ref: ReleaseRef,
|
||||||
|
stream: "debug",
|
||||||
|
version: "v9.9.9",
|
||||||
|
kind: VersionKindCLI,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"unknown kind": {
|
||||||
|
ref: ReleaseRef,
|
||||||
|
stream: "debug",
|
||||||
|
version: "v9.9.9",
|
||||||
|
kind: VersionKindUnknown,
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
|
"non-release ref as input": {
|
||||||
|
ref: "working-branch",
|
||||||
|
stream: "debug",
|
||||||
|
version: "v9.9.9",
|
||||||
|
kind: VersionKindImage,
|
||||||
|
wantVer: Version{
|
||||||
|
ref: "working-branch",
|
||||||
|
stream: "debug",
|
||||||
|
version: "v9.9.9",
|
||||||
|
kind: VersionKindImage,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"non-canonical ref as input": {
|
||||||
|
ref: "testing-1.23",
|
||||||
|
stream: "debug",
|
||||||
|
version: "v9.9.9",
|
||||||
|
kind: VersionKindImage,
|
||||||
|
wantVer: Version{
|
||||||
|
ref: "testing-1-23",
|
||||||
|
stream: "debug",
|
||||||
|
version: "v9.9.9",
|
||||||
|
kind: VersionKindImage,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for name, tc := range testCases {
|
||||||
|
t.Run(name, func(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
|
||||||
|
ver, err := NewVersion(tc.ref, tc.stream, tc.version, tc.kind)
|
||||||
|
if tc.wantErr {
|
||||||
|
assert.Error(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
assert.NoError(err)
|
||||||
|
assert.Equal(tc.wantVer, ver)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestNewVersionFromShortPath(t *testing.T) {
|
func TestNewVersionFromShortPath(t *testing.T) {
|
||||||
testCases := map[string]struct {
|
testCases := map[string]struct {
|
||||||
path string
|
path string
|
||||||
|
@ -78,6 +183,26 @@ func TestNewVersionFromShortPath(t *testing.T) {
|
||||||
kind: VersionKindCLI,
|
kind: VersionKindCLI,
|
||||||
wantErr: true,
|
wantErr: true,
|
||||||
},
|
},
|
||||||
|
"non-release ref as input": {
|
||||||
|
path: "ref/working-branch/stream/debug/v9.9.9",
|
||||||
|
kind: VersionKindImage,
|
||||||
|
wantVer: Version{
|
||||||
|
ref: "working-branch",
|
||||||
|
stream: "debug",
|
||||||
|
version: "v9.9.9",
|
||||||
|
kind: VersionKindImage,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"non-canonical ref as input": {
|
||||||
|
path: "ref/testing-1.23/stream/debug/v9.9.9",
|
||||||
|
kind: VersionKindImage,
|
||||||
|
wantVer: Version{
|
||||||
|
ref: "testing-1-23",
|
||||||
|
stream: "debug",
|
||||||
|
version: "v9.9.9",
|
||||||
|
kind: VersionKindImage,
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for name, tc := range testCases {
|
for name, tc := range testCases {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue