mirror of
https://github.com/edgelesssys/constellation.git
synced 2024-10-01 01:36:09 -04:00
cli: add version validation and force flag
Version validation checks that the configured versions are not more than one minor version below the CLI's version. The validation can be disabled using --force. This is necessary for now during development as the CLI does not have a prerelease version, as our images do.
This commit is contained in:
parent
3a7b829107
commit
f204c24174
@ -155,7 +155,7 @@ runs:
|
||||
echo "Creating cluster using config:"
|
||||
cat constellation-conf.yaml
|
||||
sudo sh -c 'echo "127.0.0.1 license.confidential.cloud" >> /etc/hosts' || true
|
||||
constellation create -c ${{ inputs.controlNodesCount }} -w ${{ inputs.workerNodesCount }} --name e2e-test -y
|
||||
constellation create -c ${{ inputs.controlNodesCount }} -w ${{ inputs.workerNodesCount }} --name e2e-test -y --force
|
||||
|
||||
- name: Cdbg deploy
|
||||
if: inputs.isDebugImage == 'true'
|
||||
@ -173,14 +173,15 @@ runs:
|
||||
--info logcollect.github.run-attempt="${{ github.run_attempt }}" \
|
||||
--info logcollect.github.ref-name="${{ github.ref_name }}" \
|
||||
--info logcollect.github.sha="${{ github.sha }}" \
|
||||
--info logcollect.github.runner-os="${{ runner.os }}"
|
||||
--info logcollect.github.runner-os="${{ runner.os }}" \
|
||||
--force
|
||||
echo "::endgroup::"
|
||||
|
||||
- name: Constellation init
|
||||
id: constellation-init
|
||||
shell: bash
|
||||
run: |
|
||||
constellation init
|
||||
constellation init --force
|
||||
echo "KUBECONFIG=$(pwd)/constellation-admin.conf" >> $GITHUB_OUTPUT
|
||||
echo "MASTERSECRET=$(pwd)/constellation-mastersecret.json" >> $GITHUB_OUTPUT
|
||||
|
||||
|
@ -62,7 +62,7 @@ add_custom_target(debugd ALL
|
||||
# cdbg
|
||||
#
|
||||
add_custom_target(cdbg ALL
|
||||
CGO_ENABLED=0 go build -o ${CMAKE_BINARY_DIR}/cdbg -buildvcs=false -ldflags "-buildid=''"
|
||||
CGO_ENABLED=0 go build -o ${CMAKE_BINARY_DIR}/cdbg -buildvcs=false -ldflags "-buildid='' -X github.com/edgelesssys/constellation/v2/internal/constants.VersionInfo=${PROJECT_VERSION}"
|
||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/debugd/cmd/cdbg
|
||||
BYPRODUCTS cdbg
|
||||
)
|
||||
|
@ -47,6 +47,7 @@ func NewRootCmd() *cobra.Command {
|
||||
must(rootCmd.MarkPersistentFlagFilename("config", "yaml"))
|
||||
|
||||
rootCmd.PersistentFlags().Bool("debug", false, "enable debug logging")
|
||||
rootCmd.PersistentFlags().Bool("force", false, "disables version validation errors - might result in corrupted clusters")
|
||||
|
||||
rootCmd.AddCommand(cmd.NewConfigCmd())
|
||||
rootCmd.AddCommand(cmd.NewCreateCmd())
|
||||
|
@ -45,6 +45,7 @@ type fetchMeasurementsFlags struct {
|
||||
measurementsURL *url.URL
|
||||
signatureURL *url.URL
|
||||
configPath string
|
||||
force bool
|
||||
}
|
||||
|
||||
type configFetchMeasurementsCmd struct {
|
||||
@ -78,9 +79,9 @@ func (cfm *configFetchMeasurementsCmd) configFetchMeasurements(
|
||||
cfm.log.Debugf("Using flags %v", flags)
|
||||
|
||||
cfm.log.Debugf("Loading configuration file from %q", flags.configPath)
|
||||
conf, err := config.New(fileHandler, flags.configPath)
|
||||
conf, err := config.New(fileHandler, flags.configPath, flags.force)
|
||||
if err != nil {
|
||||
return displayConfigValidationErrors(cmd.ErrOrStderr(), err)
|
||||
return config.DisplayValidationErrors(cmd.ErrOrStderr(), err)
|
||||
}
|
||||
|
||||
if !conf.IsReleaseImage() {
|
||||
@ -161,10 +162,16 @@ func (cfm *configFetchMeasurementsCmd) parseFetchMeasurementsFlags(cmd *cobra.Co
|
||||
}
|
||||
cfm.log.Debugf("Configuration path is %q", config)
|
||||
|
||||
force, err := cmd.Flags().GetBool("force")
|
||||
if err != nil {
|
||||
return &fetchMeasurementsFlags{}, fmt.Errorf("parsing force argument: %w", err)
|
||||
}
|
||||
|
||||
return &fetchMeasurementsFlags{
|
||||
measurementsURL: measurementsURL,
|
||||
signatureURL: measurementsSignatureURL,
|
||||
configPath: config,
|
||||
force: force,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
@ -36,6 +36,7 @@ func TestParseFetchMeasurementsFlags(t *testing.T) {
|
||||
urlFlag string
|
||||
signatureURLFlag string
|
||||
configFlag string
|
||||
forceFlag bool
|
||||
wantFlags *fetchMeasurementsFlags
|
||||
wantErr bool
|
||||
}{
|
||||
@ -74,6 +75,7 @@ func TestParseFetchMeasurementsFlags(t *testing.T) {
|
||||
|
||||
cmd := newConfigFetchMeasurementsCmd()
|
||||
cmd.Flags().String("config", constants.ConfigFilename, "") // register persistent flag manually
|
||||
cmd.Flags().Bool("force", false, "") // register persistent flag manually
|
||||
|
||||
if tc.urlFlag != "" {
|
||||
require.NoError(cmd.Flags().Set("url", tc.urlFlag))
|
||||
@ -243,10 +245,12 @@ func TestConfigFetchMeasurements(t *testing.T) {
|
||||
|
||||
cmd := newConfigFetchMeasurementsCmd()
|
||||
cmd.Flags().String("config", constants.ConfigFilename, "") // register persistent flag manually
|
||||
cmd.Flags().Bool("force", true, "") // register persistent flag manually
|
||||
fileHandler := file.NewHandler(afero.NewMemMapFs())
|
||||
|
||||
gcpConfig := defaultConfigWithExpectedMeasurements(t, config.Default(), cloudprovider.GCP)
|
||||
gcpConfig.Image = "v999.999.999"
|
||||
constants.VersionInfo = "v999.999.999"
|
||||
|
||||
err := fileHandler.WriteYAML(constants.ConfigFilename, gcpConfig, file.OptMkdirAll)
|
||||
require.NoError(err)
|
||||
|
@ -1,28 +0,0 @@
|
||||
/*
|
||||
Copyright (c) Edgeless Systems GmbH
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"go.uber.org/multierr"
|
||||
)
|
||||
|
||||
func displayConfigValidationErrors(errWriter io.Writer, configError error) error {
|
||||
errs := multierr.Errors(configError)
|
||||
if errs != nil {
|
||||
fmt.Fprintln(errWriter, "Problems validating config file:")
|
||||
for _, err := range errs {
|
||||
fmt.Fprintln(errWriter, "\t"+err.Error())
|
||||
}
|
||||
fmt.Fprintln(errWriter, "Fix the invalid entries or generate a new configuration using `constellation config generate`")
|
||||
return errors.New("invalid configuration")
|
||||
}
|
||||
return nil
|
||||
}
|
@ -73,9 +73,9 @@ func (c *createCmd) create(cmd *cobra.Command, creator cloudCreator, fileHandler
|
||||
}
|
||||
|
||||
c.log.Debugf("Loading configuration file from %q", flags.configPath)
|
||||
conf, err := config.New(fileHandler, flags.configPath)
|
||||
conf, err := config.New(fileHandler, flags.configPath, flags.force)
|
||||
if err != nil {
|
||||
return displayConfigValidationErrors(cmd.ErrOrStderr(), err)
|
||||
return config.DisplayValidationErrors(cmd.ErrOrStderr(), err)
|
||||
}
|
||||
|
||||
c.log.Debugf("Checking configuration for warnings")
|
||||
@ -201,11 +201,18 @@ func (c *createCmd) parseCreateFlags(cmd *cobra.Command) (createFlags, error) {
|
||||
}
|
||||
c.log.Debugf("Configuration path flag is %q", configPath)
|
||||
|
||||
force, err := cmd.Flags().GetBool("force")
|
||||
if err != nil {
|
||||
return createFlags{}, fmt.Errorf("parsing force argument: %w", err)
|
||||
}
|
||||
c.log.Debugf("force flag is %t", force)
|
||||
|
||||
return createFlags{
|
||||
controllerCount: controllerCount,
|
||||
workerCount: workerCount,
|
||||
name: name,
|
||||
configPath: configPath,
|
||||
force: force,
|
||||
yes: yes,
|
||||
}, nil
|
||||
}
|
||||
@ -216,6 +223,7 @@ type createFlags struct {
|
||||
workerCount int
|
||||
name string
|
||||
configPath string
|
||||
force bool
|
||||
yes bool
|
||||
}
|
||||
|
||||
|
@ -195,6 +195,8 @@ func TestCreate(t *testing.T) {
|
||||
cmd.SetErr(&bytes.Buffer{})
|
||||
cmd.SetIn(bytes.NewBufferString(tc.stdin))
|
||||
cmd.Flags().String("config", constants.ConfigFilename, "") // register persistent flag manually
|
||||
cmd.Flags().Bool("force", true, "") // register persistent flag manually
|
||||
|
||||
if tc.yesFlag {
|
||||
require.NoError(cmd.Flags().Set("yes", "true"))
|
||||
}
|
||||
|
@ -93,9 +93,9 @@ func (i *initCmd) initialize(cmd *cobra.Command, newDialer func(validator *cloud
|
||||
}
|
||||
i.log.Debugf("Using flags: %+v", flags)
|
||||
i.log.Debugf("Loading configuration file from %q", flags.configPath)
|
||||
conf, err := config.New(fileHandler, flags.configPath)
|
||||
conf, err := config.New(fileHandler, flags.configPath, flags.force)
|
||||
if err != nil {
|
||||
return displayConfigValidationErrors(cmd.ErrOrStderr(), err)
|
||||
return config.DisplayValidationErrors(cmd.ErrOrStderr(), err)
|
||||
}
|
||||
|
||||
i.log.Debugf("Checking cluster ID file")
|
||||
@ -282,10 +282,17 @@ func (i *initCmd) evalFlagArgs(cmd *cobra.Command) (initFlags, error) {
|
||||
}
|
||||
i.log.Debugf("Configuration path flag is %q", configPath)
|
||||
|
||||
force, err := cmd.Flags().GetBool("force")
|
||||
if err != nil {
|
||||
return initFlags{}, fmt.Errorf("parsing force argument: %w", err)
|
||||
}
|
||||
i.log.Debugf("force flag is %t", configPath)
|
||||
|
||||
return initFlags{
|
||||
configPath: configPath,
|
||||
conformance: conformance,
|
||||
masterSecretPath: masterSecretPath,
|
||||
force: force,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@ -294,6 +301,7 @@ type initFlags struct {
|
||||
configPath string
|
||||
masterSecretPath string
|
||||
conformance bool
|
||||
force bool
|
||||
}
|
||||
|
||||
// readOrGenerateMasterSecret reads a base64 encoded master secret from file or generates a new 32 byte secret.
|
||||
|
@ -145,6 +145,7 @@ func TestInitialize(t *testing.T) {
|
||||
|
||||
// Flags
|
||||
cmd.Flags().String("config", constants.ConfigFilename, "") // register persistent flag manually
|
||||
cmd.Flags().Bool("force", true, "") // register persistent flag manually
|
||||
|
||||
// File system preparation
|
||||
fs := afero.NewMemMapFs()
|
||||
@ -390,6 +391,7 @@ func TestAttestation(t *testing.T) {
|
||||
|
||||
cmd := NewInitCmd()
|
||||
cmd.Flags().String("config", constants.ConfigFilename, "") // register persistent flag manually
|
||||
cmd.Flags().Bool("force", true, "") // register persistent flag manually
|
||||
var out bytes.Buffer
|
||||
cmd.SetOut(&out)
|
||||
var errOut bytes.Buffer
|
||||
|
@ -179,11 +179,16 @@ func (m *miniUpCmd) prepareConfig(cmd *cobra.Command, fileHandler file.Handler)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
force, err := cmd.Flags().GetBool("force")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("parsing force argument: %w", err)
|
||||
}
|
||||
|
||||
// check for existing config
|
||||
if configPath != "" {
|
||||
conf, err := config.New(fileHandler, configPath)
|
||||
conf, err := config.New(fileHandler, configPath, force)
|
||||
if err != nil {
|
||||
return nil, displayConfigValidationErrors(cmd.ErrOrStderr(), err)
|
||||
return nil, config.DisplayValidationErrors(cmd.ErrOrStderr(), err)
|
||||
}
|
||||
if conf.GetProvider() != cloudprovider.QEMU {
|
||||
return nil, errors.New("invalid provider for MiniConstellation cluster")
|
||||
|
@ -79,9 +79,9 @@ func (r *recoverCmd) recover(
|
||||
}
|
||||
|
||||
r.log.Debugf("Loading configuration file from %q", flags.configPath)
|
||||
conf, err := config.New(fileHandler, flags.configPath)
|
||||
conf, err := config.New(fileHandler, flags.configPath, flags.force)
|
||||
if err != nil {
|
||||
return displayConfigValidationErrors(cmd.ErrOrStderr(), err)
|
||||
return config.DisplayValidationErrors(cmd.ErrOrStderr(), err)
|
||||
}
|
||||
provider := conf.GetProvider()
|
||||
r.log.Debugf("Got provider %s", provider.String())
|
||||
@ -202,6 +202,7 @@ type recoverFlags struct {
|
||||
endpoint string
|
||||
secretPath string
|
||||
configPath string
|
||||
force bool
|
||||
}
|
||||
|
||||
func (r *recoverCmd) parseRecoverFlags(cmd *cobra.Command, fileHandler file.Handler) (recoverFlags, error) {
|
||||
@ -232,10 +233,16 @@ func (r *recoverCmd) parseRecoverFlags(cmd *cobra.Command, fileHandler file.Hand
|
||||
}
|
||||
r.log.Debugf("Configuration path flag is %s", configPath)
|
||||
|
||||
force, err := cmd.Flags().GetBool("force")
|
||||
if err != nil {
|
||||
return recoverFlags{}, fmt.Errorf("parsing force argument: %w", err)
|
||||
}
|
||||
|
||||
return recoverFlags{
|
||||
endpoint: endpoint,
|
||||
secretPath: masterSecretPath,
|
||||
configPath: configPath,
|
||||
force: force,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
@ -140,6 +140,7 @@ func TestRecover(t *testing.T) {
|
||||
cmd := NewRecoverCmd()
|
||||
cmd.SetContext(context.Background())
|
||||
cmd.Flags().String("config", constants.ConfigFilename, "") // register persistent flag manually
|
||||
cmd.Flags().Bool("force", true, "") // register persistent flag manually
|
||||
out := &bytes.Buffer{}
|
||||
cmd.SetOut(out)
|
||||
cmd.SetErr(out)
|
||||
@ -225,6 +226,7 @@ func TestParseRecoverFlags(t *testing.T) {
|
||||
|
||||
cmd := NewRecoverCmd()
|
||||
cmd.Flags().String("config", "", "") // register persistent flag manually
|
||||
cmd.Flags().Bool("force", false, "") // register persistent flag manually
|
||||
require.NoError(cmd.ParseFlags(tc.args))
|
||||
|
||||
fileHandler := file.NewHandler(afero.NewMemMapFs())
|
||||
|
@ -69,9 +69,9 @@ func upgradeExecute(cmd *cobra.Command, imageFetcher imageFetcher, upgrader clou
|
||||
if err != nil {
|
||||
return fmt.Errorf("parsing flags: %w", err)
|
||||
}
|
||||
conf, err := config.New(fileHandler, flags.configPath)
|
||||
conf, err := config.New(fileHandler, flags.configPath, flags.force)
|
||||
if err != nil {
|
||||
return displayConfigValidationErrors(cmd.ErrOrStderr(), err)
|
||||
return config.DisplayValidationErrors(cmd.ErrOrStderr(), err)
|
||||
}
|
||||
|
||||
if flags.helmFlag {
|
||||
@ -130,7 +130,13 @@ func parseUpgradeExecuteFlags(cmd *cobra.Command) (upgradeExecuteFlags, error) {
|
||||
if err != nil {
|
||||
return upgradeExecuteFlags{}, err
|
||||
}
|
||||
return upgradeExecuteFlags{configPath: configPath, helmFlag: helmFlag, yes: yes, upgradeTimeout: timeout}, nil
|
||||
|
||||
force, err := cmd.Flags().GetBool("force")
|
||||
if err != nil {
|
||||
return upgradeExecuteFlags{}, fmt.Errorf("parsing force argument: %w", err)
|
||||
}
|
||||
|
||||
return upgradeExecuteFlags{configPath: configPath, helmFlag: helmFlag, yes: yes, upgradeTimeout: timeout, force: force}, nil
|
||||
}
|
||||
|
||||
type upgradeExecuteFlags struct {
|
||||
@ -138,6 +144,7 @@ type upgradeExecuteFlags struct {
|
||||
helmFlag bool
|
||||
yes bool
|
||||
upgradeTimeout time.Duration
|
||||
force bool
|
||||
}
|
||||
|
||||
type cloudUpgrader interface {
|
||||
|
@ -51,6 +51,7 @@ func TestUpgradeExecute(t *testing.T) {
|
||||
require := require.New(t)
|
||||
cmd := newUpgradeExecuteCmd()
|
||||
cmd.Flags().String("config", constants.ConfigFilename, "") // register persistent flag manually
|
||||
cmd.Flags().Bool("force", true, "") // register persistent flag manually
|
||||
|
||||
handler := file.NewHandler(afero.NewMemMapFs())
|
||||
cfg := defaultConfigWithExpectedMeasurements(t, config.Default(), cloudprovider.Azure)
|
||||
|
@ -79,9 +79,9 @@ func (up *upgradePlanCmd) upgradePlan(cmd *cobra.Command, planner upgradePlanner
|
||||
fileHandler file.Handler, client *http.Client, rekor rekorVerifier, flags upgradePlanFlags,
|
||||
cliVersion string,
|
||||
) error {
|
||||
conf, err := config.New(fileHandler, flags.configPath)
|
||||
conf, err := config.New(fileHandler, flags.configPath, true)
|
||||
if err != nil {
|
||||
return displayConfigValidationErrors(cmd.ErrOrStderr(), err)
|
||||
return config.DisplayValidationErrors(cmd.ErrOrStderr(), err)
|
||||
}
|
||||
up.log.Debugf("Read configuration from %q", flags.configPath)
|
||||
// get current image version of the cluster
|
||||
|
@ -75,9 +75,9 @@ func (v *verifyCmd) verify(cmd *cobra.Command, fileHandler file.Handler, verifyC
|
||||
v.log.Debugf("Using flags: %+v", flags)
|
||||
|
||||
v.log.Debugf("Loading configuration file from %q", flags.configPath)
|
||||
conf, err := config.New(fileHandler, flags.configPath)
|
||||
conf, err := config.New(fileHandler, flags.configPath, flags.force)
|
||||
if err != nil {
|
||||
return displayConfigValidationErrors(cmd.ErrOrStderr(), err)
|
||||
return config.DisplayValidationErrors(cmd.ErrOrStderr(), err)
|
||||
}
|
||||
|
||||
provider := conf.GetProvider()
|
||||
@ -133,6 +133,12 @@ func (v *verifyCmd) parseVerifyFlags(cmd *cobra.Command, fileHandler file.Handle
|
||||
}
|
||||
v.log.Debugf("'node-endpoint' flag is %q", endpoint)
|
||||
|
||||
force, err := cmd.Flags().GetBool("force")
|
||||
if err != nil {
|
||||
return verifyFlags{}, fmt.Errorf("parsing force argument: %w", err)
|
||||
}
|
||||
v.log.Debugf("'force' flag is %t", force)
|
||||
|
||||
// Get empty values from ID file
|
||||
emptyEndpoint := endpoint == ""
|
||||
emptyIDs := ownerID == "" && clusterID == ""
|
||||
@ -168,6 +174,7 @@ func (v *verifyCmd) parseVerifyFlags(cmd *cobra.Command, fileHandler file.Handle
|
||||
configPath: configPath,
|
||||
ownerID: ownerID,
|
||||
clusterID: clusterID,
|
||||
force: force,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@ -176,6 +183,7 @@ type verifyFlags struct {
|
||||
ownerID string
|
||||
clusterID string
|
||||
configPath string
|
||||
force bool
|
||||
}
|
||||
|
||||
func addPortIfMissing(endpoint string, defaultPort int) (string, error) {
|
||||
|
@ -141,6 +141,7 @@ func TestVerify(t *testing.T) {
|
||||
|
||||
cmd := NewVerifyCmd()
|
||||
cmd.Flags().String("config", constants.ConfigFilename, "") // register persistent flag manually
|
||||
cmd.Flags().Bool("force", true, "") // register persistent flag manually
|
||||
out := &bytes.Buffer{}
|
||||
cmd.SetOut(out)
|
||||
cmd.SetErr(&bytes.Buffer{})
|
||||
|
@ -58,13 +58,18 @@ func runDeploy(cmd *cobra.Command, args []string) error {
|
||||
if err != nil {
|
||||
return fmt.Errorf("parsing config path argument: %w", err)
|
||||
}
|
||||
force, err := cmd.Flags().GetBool("force")
|
||||
if err != nil {
|
||||
return fmt.Errorf("getting force flag: %w", err)
|
||||
}
|
||||
|
||||
fs := afero.NewOsFs()
|
||||
fileHandler := file.NewHandler(fs)
|
||||
streamer := streamer.New(fs)
|
||||
transfer := filetransfer.New(log, streamer, filetransfer.ShowProgress)
|
||||
constellationConfig, err := config.FromFile(fileHandler, configName)
|
||||
constellationConfig, err := config.New(fileHandler, configName, force)
|
||||
if err != nil {
|
||||
return err
|
||||
return config.DisplayValidationErrors(cmd.ErrOrStderr(), err)
|
||||
}
|
||||
|
||||
return deploy(cmd, fileHandler, constellationConfig, transfer, log)
|
||||
|
@ -22,6 +22,8 @@ func newRootCmd() *cobra.Command {
|
||||
It connects to Constellation instances running debugd and deploys a self-compiled version of the bootstrapper.`,
|
||||
}
|
||||
cmd.PersistentFlags().String("config", constants.ConfigFilename, "Constellation config file")
|
||||
cmd.PersistentFlags().Bool("force", false, "disables version validation errors - might result in corrupted clusters")
|
||||
|
||||
cmd.AddCommand(newDeployCmd())
|
||||
return cmd
|
||||
}
|
||||
|
1
go.mod
1
go.mod
@ -86,6 +86,7 @@ require (
|
||||
github.com/spf13/afero v1.9.3
|
||||
github.com/spf13/cobra v1.6.1
|
||||
github.com/stretchr/testify v1.8.1
|
||||
github.com/tj/assert v0.0.0-20171129193455-018094318fb0
|
||||
go.uber.org/goleak v1.2.0
|
||||
go.uber.org/multierr v1.9.0
|
||||
go.uber.org/zap v1.24.0
|
||||
|
1
go.sum
1
go.sum
@ -1310,6 +1310,7 @@ github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhV
|
||||
github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs=
|
||||
github.com/titanous/rocacheck v0.0.0-20171023193734-afe73141d399 h1:e/5i7d4oYZ+C1wj2THlRK+oAhjeS/TRQwMfkIuet3w0=
|
||||
github.com/titanous/rocacheck v0.0.0-20171023193734-afe73141d399/go.mod h1:LdwHTNJT99C5fTAzDz0ud328OgXz+gierycbcIx2fRs=
|
||||
github.com/tj/assert v0.0.0-20171129193455-018094318fb0 h1:Rw8kxzWo1mr6FSaYXjQELRe88y2KdfynXdnK72rdjtA=
|
||||
github.com/tj/assert v0.0.0-20171129193455-018094318fb0/go.mod h1:mZ9/Rh9oLWpLLDRpvE+3b7gP/C2YyLFYxNmcLnPTMe0=
|
||||
github.com/tj/go-elastic v0.0.0-20171221160941-36157cbbebc2/go.mod h1:WjeM0Oo1eNAjXGDx2yma7uG2XoyRZTq1uv3M/o7imD0=
|
||||
github.com/tj/go-kinesis v0.0.0-20171128231115-08b17f58cb1b/go.mod h1:/yhzCV0xPfx6jb1bBgRFjl5lytqVqZXEaeqWP8lTEao=
|
||||
|
@ -1305,6 +1305,7 @@ github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhV
|
||||
github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs=
|
||||
github.com/titanous/rocacheck v0.0.0-20171023193734-afe73141d399 h1:e/5i7d4oYZ+C1wj2THlRK+oAhjeS/TRQwMfkIuet3w0=
|
||||
github.com/titanous/rocacheck v0.0.0-20171023193734-afe73141d399/go.mod h1:LdwHTNJT99C5fTAzDz0ud328OgXz+gierycbcIx2fRs=
|
||||
github.com/tj/assert v0.0.0-20171129193455-018094318fb0 h1:Rw8kxzWo1mr6FSaYXjQELRe88y2KdfynXdnK72rdjtA=
|
||||
github.com/tj/assert v0.0.0-20171129193455-018094318fb0/go.mod h1:mZ9/Rh9oLWpLLDRpvE+3b7gP/C2YyLFYxNmcLnPTMe0=
|
||||
github.com/tj/go-elastic v0.0.0-20171221160941-36157cbbebc2/go.mod h1:WjeM0Oo1eNAjXGDx2yma7uG2XoyRZTq1uv3M/o7imD0=
|
||||
github.com/tj/go-kinesis v0.0.0-20171128231115-08b17f58cb1b/go.mod h1:/yhzCV0xPfx6jb1bBgRFjl5lytqVqZXEaeqWP8lTEao=
|
||||
|
139
internal/compatibility/compatibility.go
Normal file
139
internal/compatibility/compatibility.go
Normal file
@ -0,0 +1,139 @@
|
||||
/*
|
||||
Copyright (c) Edgeless Systems GmbH
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
/*
|
||||
Package compatibility offers helper functions for comparing and filtering versions.
|
||||
*/
|
||||
package compatibility
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/edgelesssys/constellation/v2/internal/constants"
|
||||
"golang.org/x/mod/semver"
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrMajorMismatch signals that the major version of two compared versions don't match.
|
||||
ErrMajorMismatch = errors.New("missmatching major version")
|
||||
// ErrMinorDrift signals that the minor version of two compared versions are further apart than one.
|
||||
ErrMinorDrift = errors.New("target version needs to be equal or up to one minor version higher")
|
||||
// ErrSemVer signals that a given version does not adhere to the Semver syntax.
|
||||
ErrSemVer = errors.New("invalid semver")
|
||||
)
|
||||
|
||||
// ensurePrefixV returns the input string prefixed with the letter "v", if the string doesn't already start with that letter.
|
||||
func ensurePrefixV(str string) string {
|
||||
if strings.HasPrefix(str, "v") {
|
||||
return str
|
||||
}
|
||||
return "v" + str
|
||||
}
|
||||
|
||||
// IsValidUpgrade checks that a and b adhere to a version drift of 1 and b is greater than a.
|
||||
func IsValidUpgrade(a, b string) error {
|
||||
a = ensurePrefixV(a)
|
||||
b = ensurePrefixV(b)
|
||||
|
||||
if !semver.IsValid(a) || !semver.IsValid(b) {
|
||||
return ErrSemVer
|
||||
}
|
||||
|
||||
if semver.Compare(a, b) >= 0 {
|
||||
return errors.New("current version newer than new version")
|
||||
}
|
||||
|
||||
aMajor, aMinor, err := parseCanonicalSemver(a)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
bMajor, bMinor, err := parseCanonicalSemver(b)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if aMajor != bMajor {
|
||||
return ErrMajorMismatch
|
||||
}
|
||||
|
||||
if bMinor-aMinor > 1 {
|
||||
return ErrMinorDrift
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// BinaryWith tests that this binarie's version is greater or equal than some target version, but not further away than one minor version.
|
||||
func BinaryWith(target string) error {
|
||||
binaryVersion := ensurePrefixV(constants.VersionInfo)
|
||||
target = ensurePrefixV(target)
|
||||
if !semver.IsValid(binaryVersion) || !semver.IsValid(target) {
|
||||
return ErrSemVer
|
||||
}
|
||||
cliMajor, cliMinor, err := parseCanonicalSemver(binaryVersion)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
targetMajor, targetMinor, err := parseCanonicalSemver(target)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Major versions always have to match.
|
||||
if cliMajor != targetMajor {
|
||||
return ErrMajorMismatch
|
||||
}
|
||||
if semver.Compare(binaryVersion, target) == -1 {
|
||||
return ErrMinorDrift
|
||||
}
|
||||
// Abort if minor version drift between CLI and versionA value is greater than 1.
|
||||
if cliMinor-targetMinor > 1 {
|
||||
return ErrMinorDrift
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// FilterNewerVersion filters the list of versions to only include versions newer than currentVersion.
|
||||
func FilterNewerVersion(currentVersion string, newVersions []string) []string {
|
||||
currentVersion = ensurePrefixV(currentVersion)
|
||||
var result []string
|
||||
|
||||
for _, image := range newVersions {
|
||||
image = ensurePrefixV(image)
|
||||
// check if image is newer than current version
|
||||
if semver.Compare(image, currentVersion) <= 0 {
|
||||
continue
|
||||
}
|
||||
result = append(result, image)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// NextMinorVersion returns the next minor version for a given canonical semver.
|
||||
// The returned format is vMAJOR.MINOR.
|
||||
func NextMinorVersion(version string) (string, error) {
|
||||
major, minor, err := parseCanonicalSemver(ensurePrefixV(version))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return fmt.Sprintf("v%d.%d", major, minor+1), nil
|
||||
}
|
||||
|
||||
func parseCanonicalSemver(version string) (major int, minor int, err error) {
|
||||
version = semver.Canonical(version) // ensure version is in canonical form (vX.Y.Z)
|
||||
num, err := fmt.Sscanf(version, "v%d.%d", &major, &minor)
|
||||
if err != nil {
|
||||
return 0, 0, fmt.Errorf("parsing version: %w", err)
|
||||
}
|
||||
if num != 2 {
|
||||
return 0, 0, fmt.Errorf("parsing version: expected 3 numbers, got %d", num)
|
||||
}
|
||||
|
||||
return major, minor, nil
|
||||
}
|
208
internal/compatibility/compatibility_test.go
Normal file
208
internal/compatibility/compatibility_test.go
Normal file
@ -0,0 +1,208 @@
|
||||
/*
|
||||
Copyright (c) Edgeless Systems GmbH
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package compatibility
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/edgelesssys/constellation/v2/internal/constants"
|
||||
"github.com/tj/assert"
|
||||
)
|
||||
|
||||
func TestFilterNewerVersion(t *testing.T) {
|
||||
imageList := []string{
|
||||
"v0.0.0",
|
||||
"v1.0.0",
|
||||
"v1.0.1",
|
||||
"v1.0.2",
|
||||
"v1.1.0",
|
||||
}
|
||||
|
||||
testCases := map[string]struct {
|
||||
images []string
|
||||
version string
|
||||
wantImages []string
|
||||
}{
|
||||
"filters <= v1.0.0": {
|
||||
images: imageList,
|
||||
version: "v1.0.0",
|
||||
wantImages: []string{
|
||||
"v1.0.1",
|
||||
"v1.0.2",
|
||||
"v1.1.0",
|
||||
},
|
||||
},
|
||||
"no compatible images": {
|
||||
images: imageList,
|
||||
version: "v999.999.999",
|
||||
},
|
||||
}
|
||||
|
||||
for name, tc := range testCases {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
compatibleImages := FilterNewerVersion(tc.version, tc.images)
|
||||
assert.EqualValues(tc.wantImages, compatibleImages)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestNextMinorVersion(t *testing.T) {
|
||||
testCases := map[string]struct {
|
||||
version string
|
||||
wantNextMinorVersion string
|
||||
wantErr bool
|
||||
}{
|
||||
"gets next": {
|
||||
version: "v1.0.0",
|
||||
wantNextMinorVersion: "v1.1",
|
||||
},
|
||||
"gets next from minor version": {
|
||||
version: "v1.0",
|
||||
wantNextMinorVersion: "v1.1",
|
||||
},
|
||||
"empty version": {
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
for name, tc := range testCases {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
gotNext, err := NextMinorVersion(tc.version)
|
||||
if tc.wantErr {
|
||||
assert.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
assert.NoError(err)
|
||||
assert.Equal(tc.wantNextMinorVersion, gotNext)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCLIWith(t *testing.T) {
|
||||
testCases := map[string]struct {
|
||||
cli string
|
||||
target string
|
||||
wantError bool
|
||||
}{
|
||||
"success": {
|
||||
cli: "v0.0.0",
|
||||
target: "v0.0.0",
|
||||
},
|
||||
"different major version": {
|
||||
cli: "v1",
|
||||
target: "v2",
|
||||
wantError: true,
|
||||
},
|
||||
"major version diff too large": {
|
||||
cli: "v1.0",
|
||||
target: "v1.2",
|
||||
wantError: true,
|
||||
},
|
||||
"a has to be the newer version": {
|
||||
cli: "v2.4.0",
|
||||
target: "v2.5.0",
|
||||
wantError: true,
|
||||
},
|
||||
"pre prelease version ordering is correct": {
|
||||
cli: "v2.5.0-pre",
|
||||
target: "v2.4.0",
|
||||
},
|
||||
"pre prelease version ordering is correct #2": {
|
||||
cli: "v2.4.0",
|
||||
target: "v2.5.0-pre",
|
||||
wantError: true,
|
||||
},
|
||||
"pre release versions match": {
|
||||
cli: "v2.6.0-pre",
|
||||
target: "v2.6.0-pre",
|
||||
},
|
||||
"pseudo version is newer than first pre release": {
|
||||
cli: "v2.6.0-pre",
|
||||
target: "v2.6.0-pre.0.20230125085856-aaaaaaaaaaaa",
|
||||
wantError: true,
|
||||
},
|
||||
}
|
||||
|
||||
for name, tc := range testCases {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
constants.VersionInfo = tc.cli
|
||||
err := BinaryWith(tc.target)
|
||||
if tc.wantError {
|
||||
assert.Error(err)
|
||||
return
|
||||
}
|
||||
assert.NoError(err)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsValidUpgrade(t *testing.T) {
|
||||
testCases := map[string]struct {
|
||||
a string
|
||||
b string
|
||||
wantError bool
|
||||
}{
|
||||
"success": {
|
||||
a: "v0.0.0",
|
||||
b: "v0.1.0",
|
||||
},
|
||||
"different major version": {
|
||||
a: "v1",
|
||||
b: "v2",
|
||||
wantError: true,
|
||||
},
|
||||
"minor version diff too large": {
|
||||
a: "v1.0",
|
||||
b: "v1.2",
|
||||
wantError: true,
|
||||
},
|
||||
"b has to be the newer version": {
|
||||
a: "v2.5.0",
|
||||
b: "v2.4.0",
|
||||
wantError: true,
|
||||
},
|
||||
"pre prelease version ordering is correct": {
|
||||
a: "v2.4.0",
|
||||
b: "v2.5.0-pre",
|
||||
},
|
||||
"wrong pre release ordering creates error": {
|
||||
a: "v2.5.0-pre",
|
||||
b: "v2.4.0",
|
||||
wantError: true,
|
||||
},
|
||||
"pre release versions are equal": {
|
||||
a: "v2.6.0-pre",
|
||||
b: "v2.6.0-pre",
|
||||
wantError: true,
|
||||
},
|
||||
"pseudo version is newer than first pre release": {
|
||||
a: "v2.6.0-pre.0.20230125085856-aaaaaaaaaaaa",
|
||||
b: "v2.6.0-pre",
|
||||
wantError: true,
|
||||
},
|
||||
}
|
||||
|
||||
for name, tc := range testCases {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
err := IsValidUpgrade(tc.a, tc.b)
|
||||
if tc.wantError {
|
||||
assert.Error(err)
|
||||
return
|
||||
}
|
||||
assert.NoError(err)
|
||||
})
|
||||
}
|
||||
}
|
@ -58,7 +58,7 @@ type Config struct {
|
||||
Version string `yaml:"version" validate:"eq=v2"`
|
||||
// description: |
|
||||
// Machine image used to create Constellation nodes.
|
||||
Image string `yaml:"image" validate:"required"`
|
||||
Image string `yaml:"image" validate:"required,version_compatibility"`
|
||||
// description: |
|
||||
// Size (in GB) of a node's disk to store the non-volatile state.
|
||||
StateDiskSizeGB int `yaml:"stateDiskSizeGB" validate:"min=0"`
|
||||
@ -317,7 +317,7 @@ func FromFile(fileHandler file.Handler, name string) (*Config, error) {
|
||||
// 1. Reading config file via provided fileHandler from file with name.
|
||||
// 2. Read secrets from environment variables.
|
||||
// 3. Validate config.
|
||||
func New(fileHandler file.Handler, name string) (*Config, error) {
|
||||
func New(fileHandler file.Handler, name string, force bool) (*Config, error) {
|
||||
// Read config file
|
||||
c, err := FromFile(fileHandler, name)
|
||||
if err != nil {
|
||||
@ -330,7 +330,7 @@ func New(fileHandler file.Handler, name string) (*Config, error) {
|
||||
c.Provider.Azure.ClientSecretValue = clientSecretValue
|
||||
}
|
||||
|
||||
return c, c.Validate()
|
||||
return c, c.Validate(force)
|
||||
}
|
||||
|
||||
// HasProvider checks whether the config contains the provider.
|
||||
@ -456,7 +456,7 @@ func (c *Config) DeployCSIDriver() bool {
|
||||
}
|
||||
|
||||
// Validate checks the config values and returns validation errors.
|
||||
func (c *Config) Validate() error {
|
||||
func (c *Config) Validate(force bool) error {
|
||||
trans := ut.New(en.New()).GetFallback()
|
||||
validate := validator.New()
|
||||
if err := en_translations.RegisterDefaultTranslations(validate, trans); err != nil {
|
||||
@ -493,6 +493,14 @@ func (c *Config) Validate() error {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := validate.RegisterTranslation("version_compatibility", trans, registerVersionCompatibilityError, translateVersionCompatibilityError); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := validate.RegisterTranslation("supported_k8s_version", trans, registerInvalidK8sVersionError, translateInvalidK8sVersionError); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := validate.RegisterValidation("no_placeholders", validateNoPlaceholder); err != nil {
|
||||
return err
|
||||
}
|
||||
@ -502,6 +510,14 @@ func (c *Config) Validate() error {
|
||||
return err
|
||||
}
|
||||
|
||||
versionCompatibilityValidator := validateVersionCompatibility
|
||||
if force {
|
||||
versionCompatibilityValidator = returnsTrue
|
||||
}
|
||||
if err := validate.RegisterValidation("version_compatibility", versionCompatibilityValidator); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// register custom validator with label aws_instance_type to validate the AWS instance type from config input.
|
||||
if err := validate.RegisterValidation("aws_instance_type", validateAWSInstanceType); err != nil {
|
||||
return err
|
||||
|
@ -173,7 +173,7 @@ func TestNewWithDefaultOptions(t *testing.T) {
|
||||
}
|
||||
|
||||
// Test
|
||||
c, err := New(fileHandler, constants.ConfigFilename)
|
||||
c, err := New(fileHandler, constants.ConfigFilename, false)
|
||||
if tc.wantErr {
|
||||
assert.Error(err)
|
||||
return
|
||||
@ -279,7 +279,7 @@ func TestValidate(t *testing.T) {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
err := tc.cnf.Validate()
|
||||
err := tc.cnf.Validate(false)
|
||||
if tc.wantErr {
|
||||
assert.Error(err)
|
||||
assert.Len(multierr.Errors(err), tc.wantErrCount)
|
||||
|
@ -8,25 +8,41 @@ package config
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/edgelesssys/constellation/v2/internal/attestation/measurements"
|
||||
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
|
||||
"github.com/edgelesssys/constellation/v2/internal/compatibility"
|
||||
"github.com/edgelesssys/constellation/v2/internal/config/instancetypes"
|
||||
"github.com/edgelesssys/constellation/v2/internal/constants"
|
||||
"github.com/edgelesssys/constellation/v2/internal/versions"
|
||||
"github.com/edgelesssys/constellation/v2/internal/versionsapi"
|
||||
ut "github.com/go-playground/universal-translator"
|
||||
"github.com/go-playground/validator/v10"
|
||||
"go.uber.org/multierr"
|
||||
"golang.org/x/mod/semver"
|
||||
)
|
||||
|
||||
func validateK8sVersion(fl validator.FieldLevel) bool {
|
||||
return versions.IsSupportedK8sVersion(fl.Field().String())
|
||||
// DisplayValidationErrors shows all validation errors inside configError as one formatted string.
|
||||
func DisplayValidationErrors(errWriter io.Writer, configError error) error {
|
||||
errs := multierr.Errors(configError)
|
||||
if errs != nil {
|
||||
fmt.Fprintln(errWriter, "Problems validating config file:")
|
||||
for _, err := range errs {
|
||||
fmt.Fprintln(errWriter, "\t"+err.Error())
|
||||
}
|
||||
fmt.Fprintln(errWriter, "Fix the invalid entries or generate a new configuration using `constellation config generate`")
|
||||
return errors.New("invalid configuration")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func registerInvalidK8sVersionError(ut ut.Translator) error {
|
||||
return ut.Add("invalid_k8s_version", "{0} specifies an unsupported Kubernetes version. {1}", true)
|
||||
return ut.Add("supported_k8s_version", "{0} specifies an unsupported Kubernetes version. {1}", true)
|
||||
}
|
||||
|
||||
func translateInvalidK8sVersionError(ut ut.Translator, fe validator.FieldError) string {
|
||||
@ -55,7 +71,7 @@ func translateInvalidK8sVersionError(ut ut.Translator, fe validator.FieldError)
|
||||
errorMsg = fmt.Sprintf("The configured version %s is newer than the newest version supported by this CLI: %s.", configured, maxVersion)
|
||||
}
|
||||
|
||||
t, _ := ut.T("invalid_k8s_version", fe.Field(), errorMsg)
|
||||
t, _ := ut.T("supported_k8s_version", fe.Field(), errorMsg)
|
||||
|
||||
return t
|
||||
}
|
||||
@ -281,3 +297,56 @@ func getPlaceholderEntries(m Measurements) []uint32 {
|
||||
|
||||
return placeholders
|
||||
}
|
||||
|
||||
func validateK8sVersion(fl validator.FieldLevel) bool {
|
||||
return versions.IsSupportedK8sVersion(fl.Field().String())
|
||||
}
|
||||
|
||||
func registerVersionCompatibilityError(ut ut.Translator) error {
|
||||
return ut.Add("version_compatibility", "{0} specifies an invalid version: {1}", true)
|
||||
}
|
||||
|
||||
func translateVersionCompatibilityError(ut ut.Translator, fe validator.FieldError) string {
|
||||
err := validateVersionCompatibilityHelper(fe.Field(), fe.Value().(string))
|
||||
var msg string
|
||||
|
||||
switch err {
|
||||
case compatibility.ErrSemVer:
|
||||
msg = fmt.Sprintf("configured version (%s) does not adhere to SemVer syntax", fe.Value().(string))
|
||||
case compatibility.ErrMajorMismatch:
|
||||
msg = fmt.Sprintf("the CLI's major version (%s) has to match your configured major version (%s)", constants.VersionInfo, fe.Value().(string))
|
||||
case compatibility.ErrMinorDrift:
|
||||
msg = fmt.Sprintf("only the CLI (%s) can be up to one minor version newer than the configured version (%s)", constants.VersionInfo, fe.Value().(string))
|
||||
default:
|
||||
msg = err.Error()
|
||||
}
|
||||
|
||||
t, _ := ut.T("version_compatibility", fe.Field(), msg)
|
||||
|
||||
return t
|
||||
}
|
||||
|
||||
// Check that the validated field and the CLI version are not more than one minor version apart.
|
||||
func validateVersionCompatibility(fl validator.FieldLevel) bool {
|
||||
if err := validateVersionCompatibilityHelper(fl.FieldName(), fl.Field().String()); err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func validateVersionCompatibilityHelper(fieldName string, configuredVersion string) error {
|
||||
if fieldName == "Image" {
|
||||
imageVersion, err := versionsapi.NewVersionFromShortPath(configuredVersion, versionsapi.VersionKindImage)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
configuredVersion = imageVersion.Version
|
||||
}
|
||||
|
||||
return compatibility.BinaryWith(configuredVersion)
|
||||
}
|
||||
|
||||
func returnsTrue(fl validator.FieldLevel) bool {
|
||||
return true
|
||||
}
|
||||
|
51
internal/config/validation_test.go
Normal file
51
internal/config/validation_test.go
Normal file
@ -0,0 +1,51 @@
|
||||
/*
|
||||
Copyright (c) Edgeless Systems GmbH
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package config
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/edgelesssys/constellation/v2/internal/constants"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
// TestValidateVersionCompatibilityHelper checks that basic version and image short paths are correctly validated.
|
||||
func TestValidateVersionCompatibilityHelper(t *testing.T) {
|
||||
testCases := map[string]struct {
|
||||
cli string
|
||||
target string
|
||||
wantError bool
|
||||
}{
|
||||
"full version works": {
|
||||
cli: "v0.1.0",
|
||||
target: "v0.0.0",
|
||||
},
|
||||
"short path works": {
|
||||
cli: "v0.1.0",
|
||||
target: "ref/main/stream/debug/v0.0.0-pre.0.20230109121528-d24fac00f018",
|
||||
},
|
||||
"minor version difference > 1": {
|
||||
cli: "0.0.0",
|
||||
target: "ref/main/stream/debug/v0.2.0-pre.0.20230109121528-d24fac00f018",
|
||||
wantError: true,
|
||||
},
|
||||
}
|
||||
|
||||
for name, tc := range testCases {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
constants.VersionInfo = tc.cli
|
||||
err := validateVersionCompatibilityHelper("Image", tc.target)
|
||||
if tc.wantError {
|
||||
assert.Error(err)
|
||||
return
|
||||
}
|
||||
assert.NoError(err)
|
||||
})
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user