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-08-08 09:42:06 -04:00
"github.com/edgelesssys/constellation/v2/cli/internal/cmd/pathprefix"
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"
2023-02-21 08:05:41 -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/constants"
"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-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}" ,
2022-05-12 09:14:52 -04:00
Short : "Generate a default configuration file" ,
Long : "Generate a default configuration 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-02-21 08:05:41 -05:00
cmd . Flags ( ) . StringP ( "kubernetes" , "k" , semver . MajorMinor ( 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-08-08 09:42:06 -04:00
pf pathprefix . PathPrefixer
2023-05-17 10:53:56 -04:00
k8sVersion string
attestationVariant variant . Variant
2022-05-12 09:14:52 -04:00
}
2023-01-04 04:46:29 -05:00
type configGenerateCmd struct {
log debugLog
}
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 )
}
defer log . Sync ( )
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-01-04 04:46:29 -05:00
cg := & configGenerateCmd { log : log }
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 {
2022-05-12 09:14:52 -04:00
flags , err := parseGenerateFlags ( cmd )
if err != nil {
return err
}
2023-01-12 05:35:26 -05:00
2023-01-04 04:46:29 -05:00
cg . log . Debugf ( "Parsed flags as %v" , flags )
cg . log . Debugf ( "Using cloud provider %s" , provider . String ( ) )
2023-06-25 17:32:39 -04:00
conf , err := createConfigWithAttestationVariant ( provider , rawProvider , flags . attestationVariant )
2023-05-17 10:53:56 -04:00
if err != nil {
return fmt . Errorf ( "creating config: %w" , err )
}
2023-03-03 03:04:54 -05:00
conf . KubernetesVersion = flags . k8sVersion
2023-01-04 04:46:29 -05:00
cg . log . Debugf ( "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 {
2022-08-08 05:04:17 -04:00
return err
}
2023-01-12 05:35:26 -05:00
2023-08-16 09:38:40 -04:00
cmd . Println ( "Config file written to" , flags . pf . PrefixPrintablePath ( constants . ConfigFilename ) )
2022-09-11 10:24:15 -04:00
cmd . Println ( "Please fill in your CSP-specific configuration before proceeding." )
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 )
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-02-21 08:05:41 -05:00
// supportedVersions prints the supported version without v prefix and without patch version.
// Should only be used when accepting Kubernetes versions from --kubernetes.
func supportedVersions ( ) string {
builder := strings . Builder { }
for i , version := range versions . SupportedK8sVersions ( ) {
if i > 0 {
builder . WriteString ( " " )
}
builder . WriteString ( strings . TrimPrefix ( semver . MajorMinor ( version ) , "v" ) )
}
return builder . String ( )
}
2022-05-12 09:14:52 -04:00
func parseGenerateFlags ( cmd * cobra . Command ) ( generateFlags , error ) {
2023-08-08 09:42:06 -04:00
workDir , err := cmd . Flags ( ) . GetString ( "workspace" )
2022-05-12 09:14:52 -04:00
if err != nil {
2023-08-04 07:53:51 -04:00
return generateFlags { } , fmt . Errorf ( "parsing workspace flag: %w" , err )
2022-05-12 09:14:52 -04:00
}
2023-02-21 08:05:41 -05:00
k8sVersion , err := cmd . Flags ( ) . GetString ( "kubernetes" )
if err != nil {
return generateFlags { } , fmt . Errorf ( "parsing kuberentes flag: %w" , err )
}
2023-03-03 03:04:54 -05:00
resolvedVersion , err := resolveK8sVersion ( k8sVersion )
if err != nil {
return generateFlags { } , fmt . Errorf ( "resolving kuberentes version from flag: %w" , err )
2023-02-21 08:05:41 -05:00
}
2023-05-17 10:53:56 -04:00
attestationString , err := cmd . Flags ( ) . GetString ( "attestation" )
if err != nil {
return generateFlags { } , fmt . Errorf ( "parsing attestation flag: %w" , err )
}
2023-06-25 17:32:39 -04:00
var attestationVariant variant . Variant
// if no attestation variant is specified, use the default for the cloud provider
2023-05-17 10:53:56 -04:00
if attestationString == "" {
2023-06-25 17:32:39 -04:00
attestationVariant = variant . Dummy { }
2023-05-17 10:53:56 -04:00
} else {
2023-06-25 17:32:39 -04:00
attestationVariant , err = variant . FromString ( attestationString )
2023-05-17 10:53:56 -04:00
if err != nil {
return generateFlags { } , fmt . Errorf ( "invalid attestation variant: %s" , attestationString )
}
}
2022-05-12 09:14:52 -04:00
return generateFlags {
2023-08-08 09:42:06 -04:00
pf : pathprefix . New ( workDir ) ,
2023-05-17 10:53:56 -04:00
k8sVersion : resolvedVersion ,
2023-06-25 17:32:39 -04:00
attestationVariant : attestationVariant ,
2022-05-12 09:14:52 -04:00
} , nil
}
2022-05-18 05:39:14 -04: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
// resolveK8sVersion takes the user input from --kubernetes and transforms a MAJOR.MINOR definition into a supported
// MAJOR.MINOR.PATCH release.
func resolveK8sVersion ( k8sVersion string ) ( string , error ) {
prefixedVersion := compatibility . EnsurePrefixV ( k8sVersion )
if ! semver . IsValid ( prefixedVersion ) {
return "" , fmt . Errorf ( "kubernetes flag does not specify a valid semantic version: %s" , k8sVersion )
}
extendedVersion := config . K8sVersionFromMajorMinor ( prefixedVersion )
if extendedVersion == "" {
return "" , fmt . Errorf ( "--kubernetes (%s) does not specify a valid Kubernetes version. Supported versions: %s" , strings . TrimPrefix ( k8sVersion , "v" ) , supportedVersions ( ) )
}
return extendedVersion , nil
}
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
}