2022-09-05 09:06:08 +02:00
/ *
Copyright ( c ) Edgeless Systems GmbH
SPDX - License - Identifier : AGPL - 3.0 - only
* /
2022-05-12 15:14:52 +02:00
package cmd
import (
2022-06-09 14:10:42 +00:00
"fmt"
2023-02-21 14:05:41 +01:00
"strings"
2022-06-09 14:10:42 +00:00
2023-06-09 15:41:02 +02:00
"github.com/edgelesssys/constellation/v2/internal/attestation/variant"
2022-09-21 13:47:57 +02:00
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
2023-02-21 14:05:41 +01:00
"github.com/edgelesssys/constellation/v2/internal/compatibility"
2022-09-21 13:47:57 +02: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 14:05:41 +01:00
"github.com/edgelesssys/constellation/v2/internal/versions"
2023-01-02 13:33:56 +01:00
"github.com/siderolabs/talos/pkg/machinery/config/encoder"
2022-05-12 15:14:52 +02:00
"github.com/spf13/afero"
"github.com/spf13/cobra"
2023-02-21 14:05:41 +01:00
"golang.org/x/mod/semver"
2022-05-12 15:14:52 +02:00
)
func newConfigGenerateCmd ( ) * cobra . Command {
cmd := & cobra . Command {
2023-05-30 09:02:50 +02:00
Use : "generate {aws|azure|gcp|openstack|qemu|stackit}" ,
2022-05-12 15:14:52 +02:00
Short : "Generate a default configuration file" ,
Long : "Generate a default configuration file for your selected cloud provider." ,
2022-05-18 11:39:14 +02:00
Args : cobra . MatchAll (
cobra . ExactArgs ( 1 ) ,
isCloudProvider ( 0 ) ,
) ,
ValidArgsFunction : generateCompletion ,
RunE : runConfigGenerate ,
2022-05-12 15:14:52 +02:00
}
2022-05-24 11:56:40 +02:00
cmd . Flags ( ) . StringP ( "file" , "f" , constants . ConfigFilename , "path to output file, or '-' for stdout" )
2023-02-21 14:05:41 +01:00
cmd . Flags ( ) . StringP ( "kubernetes" , "k" , semver . MajorMinor ( config . Default ( ) . KubernetesVersion ) , "Kubernetes version to use in format MAJOR.MINOR" )
2023-06-25 23:32:39 +02: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 15:14:52 +02:00
return cmd
}
type generateFlags struct {
2023-05-17 16:53:56 +02:00
file string
k8sVersion string
attestationVariant variant . Variant
2022-05-12 15:14:52 +02:00
}
2023-01-04 09:46:29 +00:00
type configGenerateCmd struct {
log debugLog
}
2022-05-12 15:14:52 +02:00
func runConfigGenerate ( cmd * cobra . Command , args [ ] string ) error {
2023-01-04 09:46:29 +00:00
log , err := newCLILogger ( cmd )
if err != nil {
return fmt . Errorf ( "creating logger: %w" , err )
}
defer log . Sync ( )
2022-05-12 15:14:52 +02:00
fileHandler := file . NewHandler ( afero . NewOsFs ( ) )
2022-05-18 11:39:14 +02:00
provider := cloudprovider . FromString ( args [ 0 ] )
2023-01-04 09:46:29 +00:00
cg := & configGenerateCmd { log : log }
2023-05-30 09:02:50 +02:00
return cg . configGenerate ( cmd , fileHandler , provider , args [ 0 ] )
2022-05-12 15:14:52 +02:00
}
2023-05-30 09:02:50 +02:00
func ( cg * configGenerateCmd ) configGenerate ( cmd * cobra . Command , fileHandler file . Handler , provider cloudprovider . Provider , rawProvider string ) error {
2022-05-12 15:14:52 +02:00
flags , err := parseGenerateFlags ( cmd )
if err != nil {
return err
}
2023-01-12 11:35:26 +01:00
2023-01-04 09:46:29 +00:00
cg . log . Debugf ( "Parsed flags as %v" , flags )
cg . log . Debugf ( "Using cloud provider %s" , provider . String ( ) )
2023-06-25 23:32:39 +02:00
conf , err := createConfigWithAttestationVariant ( provider , rawProvider , flags . attestationVariant )
2023-05-17 16:53:56 +02:00
if err != nil {
return fmt . Errorf ( "creating config: %w" , err )
}
2023-03-03 09:04:54 +01:00
conf . KubernetesVersion = flags . k8sVersion
2022-05-12 15:14:52 +02:00
if flags . file == "-" {
2022-05-18 11:39:14 +02:00
content , err := encoder . NewEncoder ( conf ) . Encode ( )
2022-05-16 18:54:25 +02:00
if err != nil {
2022-06-09 14:10:42 +00:00
return fmt . Errorf ( "encoding config content: %w" , err )
2022-05-16 18:54:25 +02:00
}
2023-01-04 09:46:29 +00:00
cg . log . Debugf ( "Writing YAML data to stdout" )
2022-05-16 18:54:25 +02:00
_ , err = cmd . OutOrStdout ( ) . Write ( content )
return err
2022-05-12 15:14:52 +02:00
}
2023-01-04 09:46:29 +00:00
cg . log . Debugf ( "Writing YAML data to configuration file" )
2022-08-08 11:04:17 +02:00
if err := fileHandler . WriteYAML ( flags . file , conf , file . OptMkdirAll ) ; err != nil {
return err
}
2023-01-12 11:35:26 +01:00
2022-08-08 11:04:17 +02:00
cmd . Println ( "Config file written to" , flags . file )
2022-09-11 16:24:15 +02:00
cmd . Println ( "Please fill in your CSP-specific configuration before proceeding." )
2022-10-31 16:39:56 +01:00
cmd . Println ( "For more information refer to the documentation:" )
2022-09-10 13:44:17 +02:00
cmd . Println ( "\thttps://docs.edgeless.systems/constellation/getting-started/first-steps" )
2022-09-02 17:11:06 +02:00
2022-08-08 11:04:17 +02:00
return nil
2022-05-12 15:14:52 +02:00
}
2023-06-25 23:32:39 +02: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 09:02:50 +02:00
conf := config . Default ( ) . WithOpenStackProviderDefaults ( rawProvider )
2023-01-12 11:35:26 +01:00
conf . RemoveProviderExcept ( provider )
// set a lower default for QEMU's state disk
if provider == cloudprovider . QEMU {
conf . StateDiskSizeGB = 10
}
2023-05-17 16:53:56 +02: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 23:32:39 +02:00
return nil , fmt . Errorf ( "provider %s does not support attestation variant %s" , provider , attestationVariant )
2023-05-17 16:53:56 +02: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 09:02:50 +02:00
// rawProvider can be hardcoded as it only matters for OpenStack
2023-06-25 23:32:39 +02:00
res , _ := createConfigWithAttestationVariant ( provider , "" , variant . Dummy { } )
2023-05-17 16:53:56 +02:00
return res
2023-01-12 11:35:26 +01:00
}
2023-02-21 14:05:41 +01: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 15:14:52 +02:00
func parseGenerateFlags ( cmd * cobra . Command ) ( generateFlags , error ) {
file , err := cmd . Flags ( ) . GetString ( "file" )
if err != nil {
2023-02-21 14:05:41 +01:00
return generateFlags { } , fmt . Errorf ( "parsing file flag: %w" , err )
2022-05-12 15:14:52 +02:00
}
2023-02-21 14:05:41 +01:00
k8sVersion , err := cmd . Flags ( ) . GetString ( "kubernetes" )
if err != nil {
return generateFlags { } , fmt . Errorf ( "parsing kuberentes flag: %w" , err )
}
2023-03-03 09:04:54 +01:00
resolvedVersion , err := resolveK8sVersion ( k8sVersion )
if err != nil {
return generateFlags { } , fmt . Errorf ( "resolving kuberentes version from flag: %w" , err )
2023-02-21 14:05:41 +01:00
}
2023-05-17 16:53:56 +02:00
attestationString , err := cmd . Flags ( ) . GetString ( "attestation" )
if err != nil {
return generateFlags { } , fmt . Errorf ( "parsing attestation flag: %w" , err )
}
2023-06-25 23:32:39 +02:00
var attestationVariant variant . Variant
// if no attestation variant is specified, use the default for the cloud provider
2023-05-17 16:53:56 +02:00
if attestationString == "" {
2023-06-25 23:32:39 +02:00
attestationVariant = variant . Dummy { }
2023-05-17 16:53:56 +02:00
} else {
2023-06-25 23:32:39 +02:00
attestationVariant , err = variant . FromString ( attestationString )
2023-05-17 16:53:56 +02:00
if err != nil {
return generateFlags { } , fmt . Errorf ( "invalid attestation variant: %s" , attestationString )
}
}
2022-05-12 15:14:52 +02:00
return generateFlags {
2023-05-17 16:53:56 +02:00
file : file ,
k8sVersion : resolvedVersion ,
2023-06-25 23:32:39 +02:00
attestationVariant : attestationVariant ,
2022-05-12 15:14:52 +02:00
} , nil
}
2022-05-18 11:39:14 +02:00
2023-05-17 16:53:56 +02:00
// generateCompletion handles the completion of the create command. It is frequently called
2022-05-18 11:39:14 +02:00
// while the user types arguments of the command to suggest completion.
2023-03-20 11:03:36 +01:00
func generateCompletion ( _ * cobra . Command , args [ ] string , _ string ) ( [ ] string , cobra . ShellCompDirective ) {
2022-05-18 11:39:14 +02:00
switch len ( args ) {
case 0 :
2023-05-30 09:02:50 +02:00
return [ ] string { "aws" , "gcp" , "azure" , "qemu" , "stackit" } , cobra . ShellCompDirectiveNoFileComp
2022-05-18 11:39:14 +02:00
default :
return [ ] string { } , cobra . ShellCompDirectiveError
}
}
2023-03-03 09:04:54 +01: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 16:53:56 +02: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
}