2022-09-05 03:06:08 -04:00
/ *
Copyright ( c ) Edgeless Systems GmbH
SPDX - License - Identifier : AGPL - 3.0 - only
* /
2022-08-29 10:49:44 -04:00
package cmd
import (
"context"
2023-01-04 07:55:10 -05:00
"errors"
2022-12-19 10:52:15 -05:00
"fmt"
"time"
2022-08-29 10:49:44 -04:00
2023-01-04 07:55:10 -05:00
"github.com/edgelesssys/constellation/v2/cli/internal/helm"
2023-03-30 10:13:14 -04:00
"github.com/edgelesssys/constellation/v2/cli/internal/kubernetes"
2023-03-14 13:34:58 -04:00
"github.com/edgelesssys/constellation/v2/internal/attestation/measurements"
2023-03-24 12:07:14 -04:00
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
2023-02-28 04:23:09 -05:00
"github.com/edgelesssys/constellation/v2/internal/compatibility"
2022-09-21 07:47:57 -04:00
"github.com/edgelesssys/constellation/v2/internal/config"
"github.com/edgelesssys/constellation/v2/internal/file"
2022-08-29 10:49:44 -04:00
"github.com/spf13/afero"
"github.com/spf13/cobra"
2023-03-14 13:34:58 -04:00
corev1 "k8s.io/api/core/v1"
2022-08-29 10:49:44 -04:00
)
2023-02-01 04:56:47 -05:00
func newUpgradeApplyCmd ( ) * cobra . Command {
2022-08-29 10:49:44 -04:00
cmd := & cobra . Command {
2023-02-09 09:54:12 -05:00
Use : "apply" ,
2023-02-01 04:56:47 -05:00
Short : "Apply an upgrade to a Constellation cluster" ,
Long : "Apply an upgrade to a Constellation cluster by applying the chosen configuration." ,
2022-08-29 10:49:44 -04:00
Args : cobra . NoArgs ,
2023-02-01 04:56:47 -05:00
RunE : runUpgradeApply ,
2022-08-29 10:49:44 -04:00
}
2023-01-17 08:01:56 -05:00
cmd . Flags ( ) . BoolP ( "yes" , "y" , false , "run upgrades without further confirmation\n" +
2023-03-14 13:34:58 -04:00
"WARNING: might delete your resources in case you are using cert-manager in your cluster. Please read the docs.\n" +
"WARNING: might unintentionally overwrite measurements in the running cluster." )
2023-01-17 08:01:56 -05:00
cmd . Flags ( ) . Duration ( "timeout" , 3 * time . Minute , "change helm upgrade timeout\n" +
2023-02-01 05:23:57 -05:00
"Might be useful for slow connections or big clusters." )
2022-12-19 10:52:15 -05:00
if err := cmd . Flags ( ) . MarkHidden ( "timeout" ) ; err != nil {
panic ( err )
}
2022-08-29 10:49:44 -04:00
return cmd
}
2023-03-20 06:03:36 -04:00
func runUpgradeApply ( cmd * cobra . Command , _ [ ] string ) error {
2022-12-19 10:52:15 -05:00
log , err := newCLILogger ( cmd )
if err != nil {
return fmt . Errorf ( "creating logger: %w" , err )
}
defer log . Sync ( )
2022-08-29 10:49:44 -04:00
fileHandler := file . NewHandler ( afero . NewOsFs ( ) )
2023-03-30 10:13:14 -04:00
upgrader , err := kubernetes . NewUpgrader ( cmd . OutOrStdout ( ) , log )
2022-08-29 10:49:44 -04:00
if err != nil {
return err
}
2023-02-09 09:54:12 -05:00
applyCmd := upgradeApplyCmd { upgrader : upgrader , log : log }
2023-03-03 03:38:23 -05:00
return applyCmd . upgradeApply ( cmd , fileHandler )
2022-08-29 10:49:44 -04:00
}
2023-02-09 09:54:12 -05:00
type upgradeApplyCmd struct {
upgrader cloudUpgrader
log debugLog
}
2023-03-03 03:38:23 -05:00
func ( u * upgradeApplyCmd ) upgradeApply ( cmd * cobra . Command , fileHandler file . Handler ) error {
2023-02-01 04:56:47 -05:00
flags , err := parseUpgradeApplyFlags ( cmd )
2022-08-29 10:49:44 -04:00
if err != nil {
2023-01-04 07:55:10 -05:00
return fmt . Errorf ( "parsing flags: %w" , err )
2022-08-29 10:49:44 -04:00
}
2023-01-31 05:45:31 -05:00
conf , err := config . New ( fileHandler , flags . configPath , flags . force )
2023-02-07 06:56:25 -05:00
var configValidationErr * config . ValidationError
if errors . As ( err , & configValidationErr ) {
cmd . PrintErrln ( configValidationErr . LongMessage ( ) )
}
2022-08-29 10:49:44 -04:00
if err != nil {
2023-02-07 06:56:25 -05:00
return err
2022-08-29 10:49:44 -04:00
}
2023-03-24 12:07:14 -04:00
if conf . GetProvider ( ) == cloudprovider . Azure || conf . GetProvider ( ) == cloudprovider . GCP {
err = u . handleServiceUpgrade ( cmd , conf , flags )
upgradeErr := & compatibility . InvalidUpgradeError { }
switch {
case errors . As ( err , & upgradeErr ) :
cmd . PrintErrln ( err )
case err != nil :
return fmt . Errorf ( "upgrading services: %w" , err )
}
2022-12-19 10:52:15 -05:00
2023-03-24 12:07:14 -04:00
err = u . upgrader . UpgradeNodeVersion ( cmd . Context ( ) , conf )
switch {
2023-03-30 10:13:14 -04:00
case errors . Is ( err , kubernetes . ErrInProgress ) :
2023-03-24 12:07:14 -04:00
cmd . PrintErrln ( "Skipping image and Kubernetes upgrades. Another upgrade is in progress." )
case errors . As ( err , & upgradeErr ) :
cmd . PrintErrln ( err )
case err != nil :
return fmt . Errorf ( "upgrading NodeVersion: %w" , err )
}
} else {
cmd . PrintErrln ( "WARNING: Skipping service and image upgrades, which are currently only supported for Azure and GCP." )
2023-02-09 09:54:12 -05:00
}
2023-03-14 13:34:58 -04:00
// If an image upgrade was just executed there won't be a diff. The function will return nil in that case.
if err := u . upgradeMeasurementsIfDiff ( cmd , conf . GetMeasurements ( ) , flags ) ; err != nil {
return fmt . Errorf ( "upgrading measurements: %w" , err )
}
return nil
}
// upgradeMeasurementsIfDiff checks if the locally configured measurements are different from the cluster's measurements.
// If so the function will ask the user to confirm (if --yes is not set) and upgrade the measurements only.
func ( u * upgradeApplyCmd ) upgradeMeasurementsIfDiff ( cmd * cobra . Command , newMeasurements measurements . M , flags upgradeApplyFlags ) error {
clusterMeasurements , _ , err := u . upgrader . GetClusterMeasurements ( cmd . Context ( ) )
if err != nil {
return fmt . Errorf ( "getting cluster measurements: %w" , err )
}
if clusterMeasurements . EqualTo ( newMeasurements ) {
return nil
}
if ! flags . yes {
ok , err := askToConfirm ( cmd , "You are about to change your cluster's measurements. Are you sure you want to continue?" )
if err != nil {
return fmt . Errorf ( "asking for confirmation: %w" , err )
}
if ! ok {
cmd . Println ( "Aborting upgrade." )
return nil
}
}
if err := u . upgrader . UpdateMeasurements ( cmd . Context ( ) , newMeasurements ) ; err != nil {
return fmt . Errorf ( "updating measurements: %w" , err )
}
2023-02-09 09:54:12 -05:00
return nil
2023-02-01 05:23:57 -05:00
}
2023-02-09 09:54:12 -05:00
func ( u * upgradeApplyCmd ) handleServiceUpgrade ( cmd * cobra . Command , conf * config . Config , flags upgradeApplyFlags ) error {
err := u . upgrader . UpgradeHelmServices ( cmd . Context ( ) , conf , flags . upgradeTimeout , helm . DenyDestructive )
2023-02-01 05:23:57 -05:00
if errors . Is ( err , helm . ErrConfirmationMissing ) {
if ! flags . yes {
cmd . PrintErrln ( "WARNING: Upgrading cert-manager will destroy all custom resources you have manually created that are based on the current version of cert-manager." )
ok , askErr := askToConfirm ( cmd , "Do you want to upgrade cert-manager anyway?" )
if askErr != nil {
return fmt . Errorf ( "asking for confirmation: %w" , err )
}
if ! ok {
cmd . Println ( "Aborting upgrade." )
return nil
}
}
2023-02-09 09:54:12 -05:00
err = u . upgrader . UpgradeHelmServices ( cmd . Context ( ) , conf , flags . upgradeTimeout , helm . AllowDestructive )
2023-02-01 05:23:57 -05:00
}
2023-03-03 03:38:23 -05:00
return err
2022-08-29 10:49:44 -04:00
}
2023-02-01 04:56:47 -05:00
func parseUpgradeApplyFlags ( cmd * cobra . Command ) ( upgradeApplyFlags , error ) {
2023-01-04 07:55:10 -05:00
configPath , err := cmd . Flags ( ) . GetString ( "config" )
if err != nil {
2023-02-01 04:56:47 -05:00
return upgradeApplyFlags { } , err
2023-01-04 07:55:10 -05:00
}
yes , err := cmd . Flags ( ) . GetBool ( "yes" )
if err != nil {
2023-02-01 04:56:47 -05:00
return upgradeApplyFlags { } , err
2023-01-04 07:55:10 -05:00
}
timeout , err := cmd . Flags ( ) . GetDuration ( "timeout" )
if err != nil {
2023-02-01 04:56:47 -05:00
return upgradeApplyFlags { } , err
2023-01-04 07:55:10 -05:00
}
2023-01-31 05:45:31 -05:00
force , err := cmd . Flags ( ) . GetBool ( "force" )
if err != nil {
2023-02-01 04:56:47 -05:00
return upgradeApplyFlags { } , fmt . Errorf ( "parsing force argument: %w" , err )
2023-01-31 05:45:31 -05:00
}
2023-02-01 05:23:57 -05:00
return upgradeApplyFlags { configPath : configPath , yes : yes , upgradeTimeout : timeout , force : force } , nil
2023-01-04 07:55:10 -05:00
}
2023-02-01 04:56:47 -05:00
type upgradeApplyFlags struct {
2023-01-04 07:55:10 -05:00
configPath string
yes bool
upgradeTimeout time . Duration
2023-01-31 05:45:31 -05:00
force bool
2023-01-04 07:55:10 -05:00
}
2022-08-29 10:49:44 -04:00
type cloudUpgrader interface {
2023-03-03 03:38:23 -05:00
UpgradeNodeVersion ( ctx context . Context , conf * config . Config ) error
2023-01-04 07:55:10 -05:00
UpgradeHelmServices ( ctx context . Context , config * config . Config , timeout time . Duration , allowDestructive bool ) error
2023-03-14 13:34:58 -04:00
UpdateMeasurements ( ctx context . Context , newMeasurements measurements . M ) error
GetClusterMeasurements ( ctx context . Context ) ( measurements . M , * corev1 . ConfigMap , error )
2022-08-29 10:49:44 -04:00
}