2022-10-07 03:38:43 -04:00
/ *
Copyright ( c ) Edgeless Systems GmbH
SPDX - License - Identifier : AGPL - 3.0 - only
* /
package cmd
import (
"errors"
"fmt"
2023-10-31 07:46:40 -04:00
"os"
2023-10-24 09:39:18 -04:00
"time"
2022-10-07 03:38:43 -04:00
"github.com/edgelesssys/constellation/v2/cli/internal/libvirt"
2023-06-07 10:16:32 -04:00
"github.com/edgelesssys/constellation/v2/internal/api/attestationconfigapi"
2022-10-07 03:38:43 -04:00
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
"github.com/edgelesssys/constellation/v2/internal/config"
"github.com/edgelesssys/constellation/v2/internal/constants"
2023-12-08 10:27:04 -05:00
"github.com/edgelesssys/constellation/v2/internal/constellation/featureset"
2022-10-07 03:38:43 -04:00
"github.com/edgelesssys/constellation/v2/internal/file"
"github.com/spf13/afero"
"github.com/spf13/cobra"
)
func newMiniUpCmd ( ) * cobra . Command {
cmd := & cobra . Command {
Use : "up" ,
2022-10-14 04:48:20 -04:00
Short : "Create and initialize a new MiniConstellation cluster" ,
2023-01-17 08:01:56 -05:00
Long : "Create and initialize a new MiniConstellation cluster.\n\n" +
"A mini cluster consists of a single control-plane and worker node, hosted using QEMU/KVM." ,
2022-10-07 03:38:43 -04:00
Args : cobra . ExactArgs ( 0 ) ,
RunE : runUp ,
}
2023-03-08 09:48:36 -05:00
cmd . Flags ( ) . Bool ( "merge-kubeconfig" , true , "merge Constellation kubeconfig file with default kubeconfig file in $HOME/.kube/config" )
2022-10-07 03:38:43 -04:00
return cmd
}
2023-01-04 04:46:29 -05:00
type miniUpCmd struct {
2023-06-01 07:55:46 -04:00
log debugLog
2023-06-07 10:16:32 -04:00
configFetcher attestationconfigapi . Fetcher
2023-10-16 09:05:29 -04:00
fileHandler file . Handler
flags rootFlags
2023-01-04 04:46:29 -05:00
}
2023-03-20 06:03:36 -04:00
func runUp ( cmd * cobra . Command , _ [ ] string ) error {
2023-01-04 04:46:29 -05:00
log , err := newCLILogger ( cmd )
if err != nil {
return fmt . Errorf ( "creating logger: %w" , err )
}
defer log . Sync ( )
2022-10-07 13:35:07 -04:00
2023-10-16 09:05:29 -04:00
m := & miniUpCmd {
log : log ,
configFetcher : attestationconfigapi . NewFetcher ( ) ,
fileHandler : file . NewHandler ( afero . NewOsFs ( ) ) ,
}
if err := m . flags . parse ( cmd . Flags ( ) ) ; err != nil {
return err
}
2023-10-31 07:46:40 -04:00
2023-11-20 06:10:16 -05:00
return m . up ( cmd )
2022-10-07 13:35:07 -04:00
}
2023-11-20 06:10:16 -05:00
func ( m * miniUpCmd ) up ( cmd * cobra . Command ) ( retErr error ) {
2023-01-04 04:46:29 -05:00
if err := m . checkSystemRequirements ( cmd . ErrOrStderr ( ) ) ; err != nil {
2022-10-07 03:38:43 -04:00
return fmt . Errorf ( "system requirements not met: %w" , err )
}
2023-10-31 07:46:40 -04:00
if clean , err := m . fileHandler . IsEmpty ( constants . TerraformWorkingDir ) ; err != nil && ! errors . Is ( err , os . ErrNotExist ) {
return fmt . Errorf ( "checking if terraform working directory is empty: %w" , err )
} else if err == nil && ! clean {
return fmt . Errorf (
"directory %q already exists and is not empty, run 'constellation mini down' before creating a new cluster" ,
m . flags . pathPrefixer . PrefixPrintablePath ( constants . TerraformWorkingDir ) ,
)
}
// create config if not present in directory and set default values
2023-10-16 09:05:29 -04:00
config , err := m . prepareConfig ( cmd )
2022-10-07 03:38:43 -04:00
if err != nil {
return fmt . Errorf ( "preparing config: %w" , err )
}
2023-11-20 06:10:16 -05:00
// clean up cluster resources if setup fails
defer func ( ) {
if retErr != nil {
cmd . PrintErrf ( "An error occurred: %s\n" , retErr )
cmd . PrintErrln ( "Attempting to roll back." )
err = runDown ( cmd , [ ] string { } )
if err != nil {
cmd . PrintErrf ( "Rollback failed: %s\n" , err )
} else {
cmd . PrintErrf ( "Rollback succeeded.\n\n" )
}
}
} ( )
// set flags not defined by "mini up"
cmd . Flags ( ) . StringSlice ( "skip-phases" , [ ] string { } , "" )
cmd . Flags ( ) . Bool ( "yes" , true , "" )
cmd . Flags ( ) . Bool ( "skip-helm-wait" , false , "" )
cmd . Flags ( ) . Bool ( "conformance" , false , "" )
2023-11-30 04:12:51 -05:00
cmd . Flags ( ) . Duration ( "helm-timeout" , time . Hour , "" )
2023-11-20 06:10:16 -05:00
// create and initialize the cluster
if err := runApply ( cmd , nil ) ; err != nil {
2022-10-07 03:38:43 -04:00
return fmt . Errorf ( "creating cluster: %w" , err )
}
2023-11-20 06:10:16 -05:00
2022-10-07 03:38:43 -04:00
connectURI := config . Provider . QEMU . LibvirtURI
if connectURI == "" {
connectURI = libvirt . LibvirtTCPConnectURI
}
cmd . Println ( "Connect to the VMs by executing:" )
cmd . Printf ( "\tvirsh -c %s\n\n" , connectURI )
return nil
}
// prepareConfig reads a given config, or creates a new minimal QEMU config.
2023-10-16 09:05:29 -04:00
func ( m * miniUpCmd ) prepareConfig ( cmd * cobra . Command ) ( * config . Config , error ) {
_ , err := m . fileHandler . Stat ( constants . ConfigFilename )
2023-08-04 07:53:51 -04:00
if err == nil {
// config already exists, prompt user if they want to use this file
cmd . PrintErrln ( "A config file already exists in the configured workspace." )
ok , err := askToConfirm ( cmd , "Do you want to create the Constellation using that config?" )
2022-10-07 03:38:43 -04:00
if err != nil {
2023-02-07 06:56:25 -05:00
return nil , err
2022-10-07 03:38:43 -04:00
}
2023-08-04 07:53:51 -04:00
if ok {
2023-10-16 09:05:29 -04:00
return m . prepareExistingConfig ( cmd )
2022-10-07 03:38:43 -04:00
}
2023-08-04 07:53:51 -04:00
// user declined to reuse config file, prompt if they want to overwrite it
ok , err = askToConfirm ( cmd , "Do you want to overwrite it and create a new config?" )
2022-10-20 07:36:20 -04:00
if err != nil {
return nil , err
}
if ! ok {
return nil , errors . New ( "not overwriting existing config" )
}
}
2023-08-04 07:53:51 -04:00
2023-06-28 04:28:48 -04:00
if ! featureset . CanUseEmbeddedMeasurmentsAndImage {
cmd . PrintErrln ( "Generating a valid default config is not supported in the OSS build of the Constellation CLI. Consult the documentation for instructions on where to download the enterprise version." )
return nil , errors . New ( "cannot create a mini cluster without a config file in the OSS build" )
}
config , err := config . MiniDefault ( )
if err != nil {
return nil , fmt . Errorf ( "mini default config is invalid: %v" , err )
2023-05-12 11:14:32 -04:00
}
2023-01-18 07:10:24 -05:00
m . log . Debugf ( "Prepared configuration" )
2022-10-07 03:38:43 -04:00
2023-10-16 09:05:29 -04:00
return config , m . fileHandler . WriteYAML ( constants . ConfigFilename , config , file . OptOverwrite )
2022-10-07 03:38:43 -04:00
}
2023-10-16 09:05:29 -04:00
func ( m * miniUpCmd ) prepareExistingConfig ( cmd * cobra . Command ) ( * config . Config , error ) {
conf , err := config . New ( m . fileHandler , constants . ConfigFilename , m . configFetcher , m . flags . force )
2023-08-04 07:53:51 -04:00
var configValidationErr * config . ValidationError
if errors . As ( err , & configValidationErr ) {
cmd . PrintErrln ( configValidationErr . LongMessage ( ) )
}
if err != nil {
return nil , err
}
if conf . GetProvider ( ) != cloudprovider . QEMU {
return nil , errors . New ( "invalid provider for MiniConstellation cluster" )
}
return conf , nil
}