2022-09-05 03:06:08 -04:00
/ *
Copyright ( c ) Edgeless Systems GmbH
SPDX - License - Identifier : AGPL - 3.0 - only
* /
2022-05-12 09:14:52 -04:00
package cmd
import (
2022-06-09 10:10:42 -04:00
"fmt"
2023-02-21 08:05:41 -05:00
"strings"
2022-06-09 10:10:42 -04:00
2023-06-09 09:41:02 -04:00
"github.com/edgelesssys/constellation/v2/internal/attestation/variant"
2022-09-21 07:47:57 -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/state"
2022-09-21 07:47:57 -04:00
"github.com/edgelesssys/constellation/v2/internal/file"
2023-02-21 08:05:41 -05:00
"github.com/edgelesssys/constellation/v2/internal/versions"
2022-05-12 09:14:52 -04:00
"github.com/spf13/afero"
"github.com/spf13/cobra"
2023-10-16 09:05:29 -04:00
"github.com/spf13/pflag"
2023-02-21 08:05:41 -05:00
"golang.org/x/mod/semver"
2022-05-12 09:14:52 -04:00
)
func newConfigGenerateCmd ( ) * cobra . Command {
cmd := & cobra . Command {
2023-05-30 03:02:50 -04:00
Use : "generate {aws|azure|gcp|openstack|qemu|stackit}" ,
2023-10-16 14:18:59 -04:00
Short : "Generate a default configuration and state file" ,
Long : "Generate a default configuration and state file for your selected cloud provider." ,
2022-05-18 05:39:14 -04:00
Args : cobra . MatchAll (
cobra . ExactArgs ( 1 ) ,
isCloudProvider ( 0 ) ,
) ,
ValidArgsFunction : generateCompletion ,
RunE : runConfigGenerate ,
2022-05-12 09:14:52 -04:00
}
2023-09-19 07:50:00 -04:00
cmd . Flags ( ) . StringP ( "kubernetes" , "k" , semver . MajorMinor ( string ( config . Default ( ) . KubernetesVersion ) ) , "Kubernetes version to use in format MAJOR.MINOR" )
2023-06-25 17:32:39 -04:00
cmd . Flags ( ) . StringP ( "attestation" , "a" , "" , fmt . Sprintf ( "attestation variant to use %s. If not specified, the default for the cloud provider is used" , printFormattedSlice ( variant . GetAvailableAttestationVariants ( ) ) ) )
2022-05-12 09:14:52 -04:00
return cmd
}
type generateFlags struct {
2023-10-16 09:05:29 -04:00
rootFlags
2023-09-19 07:50:00 -04:00
k8sVersion versions . ValidK8sVersion
2023-05-17 10:53:56 -04:00
attestationVariant variant . Variant
2022-05-12 09:14:52 -04:00
}
2023-10-16 09:05:29 -04:00
func ( f * generateFlags ) parse ( flags * pflag . FlagSet ) error {
if err := f . rootFlags . parse ( flags ) ; err != nil {
return err
}
k8sVersion , err := parseK8sFlag ( flags )
if err != nil {
return err
}
f . k8sVersion = k8sVersion
variant , err := parseAttestationFlag ( flags )
if err != nil {
return err
}
f . attestationVariant = variant
return nil
}
2023-01-04 04:46:29 -05:00
type configGenerateCmd struct {
2023-10-16 09:05:29 -04:00
flags generateFlags
log debugLog
2023-01-04 04:46:29 -05:00
}
2022-05-12 09:14:52 -04:00
func runConfigGenerate ( cmd * cobra . Command , args [ ] string ) error {
2023-01-04 04:46:29 -05:00
log , err := newCLILogger ( cmd )
if err != nil {
return fmt . Errorf ( "creating logger: %w" , err )
}
2023-10-16 09:05:29 -04:00
2022-05-12 09:14:52 -04:00
fileHandler := file . NewHandler ( afero . NewOsFs ( ) )
2022-05-18 05:39:14 -04:00
provider := cloudprovider . FromString ( args [ 0 ] )
2023-10-16 09:05:29 -04:00
2023-01-04 04:46:29 -05:00
cg := & configGenerateCmd { log : log }
2023-10-16 09:05:29 -04:00
if err := cg . flags . parse ( cmd . Flags ( ) ) ; err != nil {
return fmt . Errorf ( "parsing flags: %w" , err )
}
2024-02-08 09:20:01 -05:00
log . Debug ( fmt . Sprintf ( "Parsed flags as %+v" , cg . flags ) )
2023-10-16 09:05:29 -04:00
2023-05-30 03:02:50 -04:00
return cg . configGenerate ( cmd , fileHandler , provider , args [ 0 ] )
2022-05-12 09:14:52 -04:00
}
2023-05-30 03:02:50 -04:00
func ( cg * configGenerateCmd ) configGenerate ( cmd * cobra . Command , fileHandler file . Handler , provider cloudprovider . Provider , rawProvider string ) error {
2024-02-08 09:20:01 -05:00
cg . log . Debug ( fmt . Sprintf ( "Using cloud provider %s" , provider . String ( ) ) )
2023-10-16 14:18:59 -04:00
// Config creation
2023-10-16 09:05:29 -04:00
conf , err := createConfigWithAttestationVariant ( provider , rawProvider , cg . flags . attestationVariant )
2023-05-17 10:53:56 -04:00
if err != nil {
return fmt . Errorf ( "creating config: %w" , err )
}
2023-10-16 09:05:29 -04:00
conf . KubernetesVersion = cg . flags . k8sVersion
2024-02-08 09:20:01 -05:00
cg . log . Debug ( "Writing YAML data to configuration file" )
2023-08-04 07:53:51 -04:00
if err := fileHandler . WriteYAML ( constants . ConfigFilename , conf , file . OptMkdirAll ) ; err != nil {
2023-10-16 14:18:59 -04:00
return fmt . Errorf ( "writing config file: %w" , err )
2022-08-08 05:04:17 -04:00
}
2023-01-12 05:35:26 -05:00
2023-10-16 09:05:29 -04:00
cmd . Println ( "Config file written to" , cg . flags . pathPrefixer . PrefixPrintablePath ( constants . ConfigFilename ) )
2022-09-11 10:24:15 -04:00
cmd . Println ( "Please fill in your CSP-specific configuration before proceeding." )
2023-10-16 14:18:59 -04:00
// State-file creation
stateFile := state . New ( )
switch provider {
case cloudprovider . GCP :
stateFile . SetInfrastructure ( state . Infrastructure { GCP : & state . GCP { } } )
case cloudprovider . Azure :
stateFile . SetInfrastructure ( state . Infrastructure { Azure : & state . Azure { } } )
}
if err = stateFile . WriteToFile ( fileHandler , constants . StateFilename ) ; err != nil {
return fmt . Errorf ( "writing state file: %w" , err )
}
cmd . Println ( "State file written to" , cg . flags . pathPrefixer . PrefixPrintablePath ( constants . StateFilename ) )
2022-10-31 11:39:56 -04:00
cmd . Println ( "For more information refer to the documentation:" )
2022-09-10 07:44:17 -04:00
cmd . Println ( "\thttps://docs.edgeless.systems/constellation/getting-started/first-steps" )
2022-09-02 11:11:06 -04:00
2022-08-08 05:04:17 -04:00
return nil
2022-05-12 09:14:52 -04:00
}
2023-06-25 17:32:39 -04:00
// createConfigWithAttestationVariant creates a config file for the given provider.
func createConfigWithAttestationVariant ( provider cloudprovider . Provider , rawProvider string , attestationVariant variant . Variant ) ( * config . Config , error ) {
2023-05-30 03:02:50 -04:00
conf := config . Default ( ) . WithOpenStackProviderDefaults ( rawProvider )
2023-01-12 05:35:26 -05:00
conf . RemoveProviderExcept ( provider )
// set a lower default for QEMU's state disk
if provider == cloudprovider . QEMU {
2023-08-02 04:42:18 -04:00
for groupName , group := range conf . NodeGroups {
group . StateDiskSizeGB = 10
conf . NodeGroups [ groupName ] = group
}
2023-01-12 05:35:26 -05:00
}
2023-05-17 10:53:56 -04:00
if provider == cloudprovider . Unknown {
return conf , nil
}
if attestationVariant . Equal ( variant . Dummy { } ) {
attestationVariant = variant . GetDefaultAttestation ( provider )
if attestationVariant . Equal ( variant . Dummy { } ) {
return nil , fmt . Errorf ( "provider %s does not have a default attestation variant" , provider )
}
} else if ! variant . ValidProvider ( provider , attestationVariant ) {
2023-06-25 17:32:39 -04:00
return nil , fmt . Errorf ( "provider %s does not support attestation variant %s" , provider , attestationVariant )
2023-05-17 10:53:56 -04:00
}
conf . SetAttestation ( attestationVariant )
2024-01-24 09:10:15 -05:00
conf . SetCSPNodeGroupDefaults ( provider )
2023-05-17 10:53:56 -04:00
return conf , nil
}
// createConfig creates a config file for the given provider.
func createConfig ( provider cloudprovider . Provider ) * config . Config {
2023-05-30 03:02:50 -04:00
// rawProvider can be hardcoded as it only matters for OpenStack
2023-06-25 17:32:39 -04:00
res , _ := createConfigWithAttestationVariant ( provider , "" , variant . Dummy { } )
2023-05-17 10:53:56 -04:00
return res
2023-01-12 05:35:26 -05:00
}
2023-05-17 10:53:56 -04:00
// generateCompletion handles the completion of the create command. It is frequently called
2022-05-18 05:39:14 -04:00
// while the user types arguments of the command to suggest completion.
2023-03-20 06:03:36 -04:00
func generateCompletion ( _ * cobra . Command , args [ ] string , _ string ) ( [ ] string , cobra . ShellCompDirective ) {
2022-05-18 05:39:14 -04:00
switch len ( args ) {
case 0 :
2023-05-30 03:02:50 -04:00
return [ ] string { "aws" , "gcp" , "azure" , "qemu" , "stackit" } , cobra . ShellCompDirectiveNoFileComp
2022-05-18 05:39:14 -04:00
default :
return [ ] string { } , cobra . ShellCompDirectiveError
}
}
2023-03-03 03:04:54 -05:00
2023-05-17 10:53:56 -04:00
func printFormattedSlice [ T any ] ( input [ ] T ) string {
return fmt . Sprintf ( "{%s}" , strings . Join ( toString ( input ) , "|" ) )
}
func toString [ T any ] ( t [ ] T ) [ ] string {
var res [ ] string
for _ , v := range t {
res = append ( res , fmt . Sprintf ( "%v" , v ) )
}
return res
}
2023-10-16 09:05:29 -04:00
func parseK8sFlag ( flags * pflag . FlagSet ) ( versions . ValidK8sVersion , error ) {
versionString , err := flags . GetString ( "kubernetes" )
if err != nil {
return "" , fmt . Errorf ( "getting kubernetes flag: %w" , err )
}
resolvedVersion , err := versions . ResolveK8sPatchVersion ( versionString )
if err != nil {
return "" , fmt . Errorf ( "resolving kubernetes patch version from flag: %w" , err )
}
k8sVersion , err := versions . NewValidK8sVersion ( resolvedVersion , true )
if err != nil {
return "" , fmt . Errorf ( "resolving Kubernetes version from flag: %w" , err )
}
return k8sVersion , nil
}
func parseAttestationFlag ( flags * pflag . FlagSet ) ( variant . Variant , error ) {
attestationString , err := flags . GetString ( "attestation" )
if err != nil {
return nil , fmt . Errorf ( "getting attestation flag: %w" , err )
}
var attestationVariant variant . Variant
// if no attestation variant is specified, use the default for the cloud provider
if attestationString == "" {
attestationVariant = variant . Dummy { }
} else {
attestationVariant , err = variant . FromString ( attestationString )
if err != nil {
return nil , fmt . Errorf ( "invalid attestation variant: %s" , attestationString )
}
}
return attestationVariant , nil
}