2022-12-29 11:48:10 -05:00
|
|
|
/*
|
|
|
|
Copyright (c) Edgeless Systems GmbH
|
|
|
|
|
|
|
|
SPDX-License-Identifier: AGPL-3.0-only
|
|
|
|
*/
|
|
|
|
|
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
2023-01-13 12:46:48 -05:00
|
|
|
"encoding/json"
|
2022-12-29 11:48:10 -05:00
|
|
|
"errors"
|
|
|
|
"fmt"
|
|
|
|
|
|
|
|
"github.com/spf13/cobra"
|
|
|
|
"go.uber.org/zap/zapcore"
|
2023-01-04 09:24:59 -05:00
|
|
|
"golang.org/x/mod/semver"
|
2023-04-04 06:10:07 -04:00
|
|
|
|
2023-06-05 04:51:05 -04:00
|
|
|
apiclient "github.com/edgelesssys/constellation/v2/internal/api/client"
|
2023-06-07 10:16:32 -04:00
|
|
|
"github.com/edgelesssys/constellation/v2/internal/api/versionsapi"
|
2023-04-04 06:10:07 -04:00
|
|
|
"github.com/edgelesssys/constellation/v2/internal/logger"
|
2022-12-29 11:48:10 -05:00
|
|
|
)
|
|
|
|
|
|
|
|
func newListCmd() *cobra.Command {
|
|
|
|
cmd := &cobra.Command{
|
|
|
|
Use: "list",
|
|
|
|
Short: "List versions",
|
|
|
|
Long: "List all versions of a ref/stream. The returned version are in short format, if --json flag is not set.",
|
|
|
|
RunE: runList,
|
|
|
|
Args: cobra.ExactArgs(0),
|
|
|
|
}
|
|
|
|
|
|
|
|
cmd.Flags().String("ref", "-", "Ref to query")
|
|
|
|
cmd.Flags().String("stream", "stable", "Stream to query")
|
|
|
|
cmd.Flags().String("minor-version", "", "Minor version to query (format: \"v1.2\")")
|
|
|
|
cmd.Flags().Bool("json", false, "Whether to output the result as JSON")
|
|
|
|
|
|
|
|
return cmd
|
|
|
|
}
|
|
|
|
|
2023-08-31 04:31:45 -04:00
|
|
|
func runList(cmd *cobra.Command, _ []string) (retErr error) {
|
2022-12-29 11:48:10 -05:00
|
|
|
flags, err := parseListFlags(cmd)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
log := logger.New(logger.PlainLog, flags.logLevel)
|
|
|
|
log.Debugf("Parsed flags: %+v", flags)
|
|
|
|
|
2023-01-31 06:12:19 -05:00
|
|
|
log.Debugf("Validating flags")
|
2022-12-29 11:48:10 -05:00
|
|
|
if err := flags.validate(); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2023-01-31 06:12:19 -05:00
|
|
|
log.Debugf("Creating versions API client")
|
2023-06-07 10:16:32 -04:00
|
|
|
client, clientClose, err := versionsapi.NewReadOnlyClient(cmd.Context(), flags.region, flags.bucket, flags.distributionID, log)
|
2022-12-29 11:48:10 -05:00
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("creating client: %w", err)
|
|
|
|
}
|
2023-06-02 05:20:01 -04:00
|
|
|
defer func() {
|
2023-08-31 04:31:45 -04:00
|
|
|
err := clientClose(cmd.Context())
|
|
|
|
if err != nil {
|
|
|
|
retErr = errors.Join(retErr, fmt.Errorf("failed to invalidate cache: %w", err))
|
2023-06-02 05:20:01 -04:00
|
|
|
}
|
|
|
|
}()
|
2022-12-29 11:48:10 -05:00
|
|
|
|
|
|
|
var minorVersions []string
|
|
|
|
if flags.minorVersion != "" {
|
|
|
|
minorVersions = []string{flags.minorVersion}
|
|
|
|
} else {
|
2023-01-31 06:12:19 -05:00
|
|
|
log.Debugf("Getting minor versions")
|
2022-12-29 11:48:10 -05:00
|
|
|
minorVersions, err = listMinorVersions(cmd.Context(), client, flags.ref, flags.stream)
|
2023-06-05 04:51:05 -04:00
|
|
|
var errNotFound *apiclient.NotFoundError
|
2022-12-29 11:48:10 -05:00
|
|
|
if err != nil && errors.As(err, &errNotFound) {
|
|
|
|
log.Infof("No minor versions found for ref %q and stream %q.", flags.ref, flags.stream)
|
|
|
|
return nil
|
|
|
|
} else if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-01-31 06:12:19 -05:00
|
|
|
log.Debugf("Getting patch versions")
|
2022-12-29 11:48:10 -05:00
|
|
|
patchVersions, err := listPatchVersions(cmd.Context(), client, flags.ref, flags.stream, minorVersions)
|
2023-06-05 04:51:05 -04:00
|
|
|
var errNotFound *apiclient.NotFoundError
|
2022-12-29 11:48:10 -05:00
|
|
|
if err != nil && errors.As(err, &errNotFound) {
|
|
|
|
log.Infof("No patch versions found for ref %q, stream %q and minor versions %v.", flags.ref, flags.stream, minorVersions)
|
|
|
|
return nil
|
|
|
|
} else if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2023-01-13 12:46:48 -05:00
|
|
|
if flags.json {
|
2023-01-31 06:12:19 -05:00
|
|
|
log.Debugf("Printing versions as JSON")
|
2023-01-13 12:46:48 -05:00
|
|
|
var vers []string
|
|
|
|
for _, v := range patchVersions {
|
2023-08-01 10:48:13 -04:00
|
|
|
vers = append(vers, v.Version())
|
2023-01-13 12:46:48 -05:00
|
|
|
}
|
|
|
|
raw, err := json.Marshal(vers)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("marshaling versions: %w", err)
|
|
|
|
}
|
|
|
|
fmt.Println(string(raw))
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2023-01-31 06:12:19 -05:00
|
|
|
log.Debugf("Printing versions")
|
2022-12-29 11:48:10 -05:00
|
|
|
for _, v := range patchVersions {
|
|
|
|
fmt.Println(v.ShortPath())
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2023-06-07 10:16:32 -04:00
|
|
|
func listMinorVersions(ctx context.Context, client *versionsapi.Client, ref string, stream string) ([]string, error) {
|
2022-12-29 11:48:10 -05:00
|
|
|
list := versionsapi.List{
|
|
|
|
Ref: ref,
|
|
|
|
Stream: stream,
|
|
|
|
Granularity: versionsapi.GranularityMajor,
|
|
|
|
Base: "v2",
|
|
|
|
Kind: versionsapi.VersionKindImage,
|
|
|
|
}
|
|
|
|
list, err := client.FetchVersionList(ctx, list)
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("listing minor versions: %w", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return list.Versions, nil
|
|
|
|
}
|
|
|
|
|
2023-06-07 10:16:32 -04:00
|
|
|
func listPatchVersions(ctx context.Context, client *versionsapi.Client, ref string, stream string, minorVer []string,
|
2022-12-29 11:48:10 -05:00
|
|
|
) ([]versionsapi.Version, error) {
|
|
|
|
var patchVers []versionsapi.Version
|
|
|
|
|
|
|
|
list := versionsapi.List{
|
|
|
|
Ref: ref,
|
|
|
|
Stream: stream,
|
|
|
|
Granularity: versionsapi.GranularityMinor,
|
|
|
|
Kind: versionsapi.VersionKindImage,
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, ver := range minorVer {
|
|
|
|
list.Base = ver
|
|
|
|
list, err := client.FetchVersionList(ctx, list)
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("listing patch versions: %w", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
patchVers = append(patchVers, list.StructuredVersions()...)
|
|
|
|
}
|
|
|
|
|
|
|
|
return patchVers, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
type listFlags struct {
|
|
|
|
ref string
|
|
|
|
stream string
|
|
|
|
minorVersion string
|
|
|
|
region string
|
|
|
|
bucket string
|
|
|
|
distributionID string
|
2023-01-13 12:46:48 -05:00
|
|
|
json bool
|
2022-12-29 11:48:10 -05:00
|
|
|
logLevel zapcore.Level
|
|
|
|
}
|
|
|
|
|
|
|
|
func (l *listFlags) validate() error {
|
|
|
|
if err := versionsapi.ValidateRef(l.ref); err != nil {
|
|
|
|
return fmt.Errorf("invalid ref: %w", err)
|
|
|
|
}
|
|
|
|
if err := versionsapi.ValidateStream(l.ref, l.stream); err != nil {
|
|
|
|
return fmt.Errorf("invalid stream: %w", err)
|
|
|
|
}
|
|
|
|
if l.minorVersion != "" {
|
|
|
|
if !semver.IsValid(l.minorVersion) || semver.MajorMinor(l.minorVersion) != l.minorVersion {
|
|
|
|
return fmt.Errorf("invalid minor version: %q", l.minorVersion)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func parseListFlags(cmd *cobra.Command) (listFlags, error) {
|
|
|
|
ref, err := cmd.Flags().GetString("ref")
|
|
|
|
if err != nil {
|
|
|
|
return listFlags{}, err
|
|
|
|
}
|
|
|
|
ref = versionsapi.CanonicalizeRef(ref)
|
|
|
|
stream, err := cmd.Flags().GetString("stream")
|
|
|
|
if err != nil {
|
|
|
|
return listFlags{}, err
|
|
|
|
}
|
|
|
|
minorVersion, err := cmd.Flags().GetString("minor-version")
|
|
|
|
if err != nil {
|
|
|
|
return listFlags{}, err
|
|
|
|
}
|
|
|
|
region, err := cmd.Flags().GetString("region")
|
|
|
|
if err != nil {
|
|
|
|
return listFlags{}, err
|
|
|
|
}
|
|
|
|
bucket, err := cmd.Flags().GetString("bucket")
|
|
|
|
if err != nil {
|
|
|
|
return listFlags{}, err
|
|
|
|
}
|
|
|
|
distributionID, err := cmd.Flags().GetString("distribution-id")
|
|
|
|
if err != nil {
|
|
|
|
return listFlags{}, err
|
|
|
|
}
|
2023-01-13 12:46:48 -05:00
|
|
|
json, err := cmd.Flags().GetBool("json")
|
|
|
|
if err != nil {
|
|
|
|
return listFlags{}, err
|
|
|
|
}
|
2022-12-29 11:48:10 -05:00
|
|
|
verbose, err := cmd.Flags().GetBool("verbose")
|
|
|
|
if err != nil {
|
|
|
|
return listFlags{}, err
|
|
|
|
}
|
|
|
|
logLevel := zapcore.InfoLevel
|
|
|
|
if verbose {
|
|
|
|
logLevel = zapcore.DebugLevel
|
|
|
|
}
|
|
|
|
|
|
|
|
return listFlags{
|
|
|
|
ref: ref,
|
|
|
|
stream: stream,
|
|
|
|
minorVersion: minorVersion,
|
|
|
|
region: region,
|
|
|
|
bucket: bucket,
|
|
|
|
distributionID: distributionID,
|
2023-01-13 12:46:48 -05:00
|
|
|
json: json,
|
2022-12-29 11:48:10 -05:00
|
|
|
logLevel: logLevel,
|
|
|
|
}, nil
|
|
|
|
}
|