2022-12-07 05:48:54 -05:00
/ *
Copyright ( c ) Edgeless Systems GmbH
SPDX - License - Identifier : AGPL - 3.0 - only
* /
package cmd
import (
2023-02-01 05:32:01 -05:00
"context"
"encoding/base64"
"encoding/json"
"fmt"
"regexp"
"strings"
"github.com/edgelesssys/constellation/v2/cli/internal/cloudcmd"
2023-08-08 09:42:06 -04:00
"github.com/edgelesssys/constellation/v2/cli/internal/cmd/pathprefix"
2023-04-14 08:15:07 -04:00
"github.com/edgelesssys/constellation/v2/cli/internal/terraform"
2023-02-01 05:32:01 -05:00
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
"github.com/edgelesssys/constellation/v2/internal/config"
"github.com/edgelesssys/constellation/v2/internal/constants"
"github.com/edgelesssys/constellation/v2/internal/file"
"github.com/spf13/afero"
2022-12-07 05:48:54 -05:00
"github.com/spf13/cobra"
)
2023-02-01 05:32:01 -05:00
var (
// GCP-specific validation regexes
2023-07-04 07:55:52 -04:00
// Source: https://cloud.google.com/compute/docs/regions-zones
zoneRegex = regexp . MustCompile ( ` ^\w+-\w+-[abc]$ ` )
regionRegex = regexp . MustCompile ( ` ^\w+-\w+[0-9]$ ` )
2023-02-01 05:32:01 -05:00
// Source: https://cloud.google.com/resource-manager/reference/rest/v1/projects.
2023-07-05 10:44:57 -04:00
gcpIDRegex = regexp . MustCompile ( ` ^[a-z][-a-z0-9] { 4,28}[a-z0-9]$ ` )
2023-02-01 05:32:01 -05:00
)
2022-12-07 05:48:54 -05:00
// NewIAMCmd returns a new cobra.Command for the iam parent command. It needs another verb and does nothing on its own.
func NewIAMCmd ( ) * cobra . Command {
cmd := & cobra . Command {
Use : "iam" ,
Short : "Work with the IAM configuration on your cloud provider" ,
Long : "Work with the IAM configuration on your cloud provider." ,
Args : cobra . ExactArgs ( 0 ) ,
}
cmd . AddCommand ( newIAMCreateCmd ( ) )
2023-02-24 05:36:41 -05:00
cmd . AddCommand ( newIAMDestroyCmd ( ) )
2023-07-26 11:29:03 -04:00
cmd . AddCommand ( newIAMUpgradeCmd ( ) )
2022-12-07 05:48:54 -05:00
return cmd
}
// NewIAMCreateCmd returns a new cobra.Command for the iam create parent command. It needs another verb, and does nothing on its own.
func newIAMCreateCmd ( ) * cobra . Command {
cmd := & cobra . Command {
Use : "create" ,
Short : "Create IAM configuration on a cloud platform for your Constellation cluster" ,
Long : "Create IAM configuration on a cloud platform for your Constellation cluster." ,
Args : cobra . ExactArgs ( 0 ) ,
}
2023-03-31 11:26:14 -04:00
cmd . PersistentFlags ( ) . BoolP ( "yes" , "y" , false , "create the IAM configuration without further confirmation" )
2023-07-05 10:44:57 -04:00
cmd . PersistentFlags ( ) . Bool ( "update-config" , false , "update the config file with the specific IAM information" )
2023-01-12 05:35:26 -05:00
2022-12-07 05:48:54 -05:00
cmd . AddCommand ( newIAMCreateAWSCmd ( ) )
cmd . AddCommand ( newIAMCreateAzureCmd ( ) )
cmd . AddCommand ( newIAMCreateGCPCmd ( ) )
return cmd
}
2023-02-01 05:32:01 -05:00
// newIAMCreateAWSCmd returns a new cobra.Command for the iam create aws command.
func newIAMCreateAWSCmd ( ) * cobra . Command {
cmd := & cobra . Command {
Use : "aws" ,
Short : "Create IAM configuration on AWS for your Constellation cluster" ,
Long : "Create IAM configuration on AWS for your Constellation cluster." ,
Args : cobra . ExactArgs ( 0 ) ,
RunE : createRunIAMFunc ( cloudprovider . AWS ) ,
}
cmd . Flags ( ) . String ( "prefix" , "" , "name prefix for all resources (required)" )
must ( cobra . MarkFlagRequired ( cmd . Flags ( ) , "prefix" ) )
2023-07-05 10:44:57 -04:00
cmd . Flags ( ) . String ( "zone" , "" , "AWS availability zone the resources will be created in, e.g., us-east-2a (required)\n" +
"See the Constellation docs for a list of currently supported regions." )
2023-02-01 05:32:01 -05:00
must ( cobra . MarkFlagRequired ( cmd . Flags ( ) , "zone" ) )
return cmd
}
// newIAMCreateAzureCmd returns a new cobra.Command for the iam create azure command.
func newIAMCreateAzureCmd ( ) * cobra . Command {
cmd := & cobra . Command {
Use : "azure" ,
Short : "Create IAM configuration on Microsoft Azure for your Constellation cluster" ,
Long : "Create IAM configuration on Microsoft Azure for your Constellation cluster." ,
Args : cobra . ExactArgs ( 0 ) ,
RunE : createRunIAMFunc ( cloudprovider . Azure ) ,
}
cmd . Flags ( ) . String ( "resourceGroup" , "" , "name prefix of the two resource groups your cluster / IAM resources will be created in (required)" )
must ( cobra . MarkFlagRequired ( cmd . Flags ( ) , "resourceGroup" ) )
2023-07-05 10:44:57 -04:00
cmd . Flags ( ) . String ( "region" , "" , "region the resources will be created in, e.g., westus (required)" )
2023-02-01 05:32:01 -05:00
must ( cobra . MarkFlagRequired ( cmd . Flags ( ) , "region" ) )
cmd . Flags ( ) . String ( "servicePrincipal" , "" , "name of the service principal that will be created (required)" )
must ( cobra . MarkFlagRequired ( cmd . Flags ( ) , "servicePrincipal" ) )
return cmd
}
// NewIAMCreateGCPCmd returns a new cobra.Command for the iam create gcp command.
func newIAMCreateGCPCmd ( ) * cobra . Command {
cmd := & cobra . Command {
Use : "gcp" ,
Short : "Create IAM configuration on GCP for your Constellation cluster" ,
Long : "Create IAM configuration on GCP for your Constellation cluster." ,
Args : cobra . ExactArgs ( 0 ) ,
RunE : createRunIAMFunc ( cloudprovider . GCP ) ,
}
cmd . Flags ( ) . String ( "zone" , "" , "GCP zone the cluster will be deployed in (required)\n" +
2023-07-05 10:44:57 -04:00
"Find a list of available zones here: https://cloud.google.com/compute/docs/regions-zones#available" )
2023-02-01 05:32:01 -05:00
must ( cobra . MarkFlagRequired ( cmd . Flags ( ) , "zone" ) )
cmd . Flags ( ) . String ( "serviceAccountID" , "" , "ID for the service account that will be created (required)\n" +
2023-07-05 10:44:57 -04:00
"Must be 6 to 30 lowercase letters, digits, or hyphens." )
2023-02-01 05:32:01 -05:00
must ( cobra . MarkFlagRequired ( cmd . Flags ( ) , "serviceAccountID" ) )
cmd . Flags ( ) . String ( "projectID" , "" , "ID of the GCP project the configuration will be created in (required)\n" +
2023-07-05 10:44:57 -04:00
"Find it on the welcome screen of your project: https://console.cloud.google.com/welcome" )
2023-02-01 05:32:01 -05:00
must ( cobra . MarkFlagRequired ( cmd . Flags ( ) , "projectID" ) )
return cmd
}
// createRunIAMFunc is the entrypoint for the iam create command. It sets up the iamCreator
// and starts IAM creation for the specific cloud provider.
func createRunIAMFunc ( provider cloudprovider . Provider ) func ( cmd * cobra . Command , args [ ] string ) error {
2023-08-08 09:42:06 -04:00
var providerCreator func ( pf pathprefix . PathPrefixer ) providerIAMCreator
2023-02-01 05:32:01 -05:00
switch provider {
case cloudprovider . AWS :
2023-08-08 09:42:06 -04:00
providerCreator = func ( pathprefix . PathPrefixer ) providerIAMCreator { return & awsIAMCreator { } }
2023-02-01 05:32:01 -05:00
case cloudprovider . Azure :
2023-08-08 09:42:06 -04:00
providerCreator = func ( pathprefix . PathPrefixer ) providerIAMCreator { return & azureIAMCreator { } }
2023-02-01 05:32:01 -05:00
case cloudprovider . GCP :
2023-08-08 09:42:06 -04:00
providerCreator = func ( pf pathprefix . PathPrefixer ) providerIAMCreator {
return & gcpIAMCreator { pf }
2023-08-04 07:53:51 -04:00
}
2023-02-01 05:32:01 -05:00
default :
return func ( cmd * cobra . Command , args [ ] string ) error {
return fmt . Errorf ( "unknown provider %s" , provider )
}
}
2023-04-14 08:15:07 -04:00
2023-02-01 05:32:01 -05:00
return func ( cmd * cobra . Command , args [ ] string ) error {
2023-04-14 08:15:07 -04:00
logLevelString , err := cmd . Flags ( ) . GetString ( "tf-log" )
if err != nil {
return fmt . Errorf ( "parsing tf-log string: %w" , err )
}
logLevel , err := terraform . ParseLogLevel ( logLevelString )
if err != nil {
return fmt . Errorf ( "parsing Terraform log level %s: %w" , logLevelString , err )
}
2023-08-08 09:42:06 -04:00
workDir , err := cmd . Flags ( ) . GetString ( "workspace" )
2023-08-04 07:53:51 -04:00
if err != nil {
return fmt . Errorf ( "parsing workspace string: %w" , err )
}
2023-08-08 09:42:06 -04:00
pf := pathprefix . New ( workDir )
2023-04-14 08:15:07 -04:00
2023-08-08 09:42:06 -04:00
iamCreator , err := newIAMCreator ( cmd , pf , logLevel )
2023-02-09 04:37:22 -05:00
if err != nil {
return fmt . Errorf ( "creating iamCreator: %w" , err )
}
2023-02-01 05:32:01 -05:00
defer iamCreator . spinner . Stop ( )
2023-02-09 04:37:22 -05:00
defer iamCreator . log . Sync ( )
2023-02-01 05:32:01 -05:00
iamCreator . provider = provider
2023-08-08 09:42:06 -04:00
iamCreator . providerCreator = providerCreator ( pf )
2023-02-01 05:32:01 -05:00
return iamCreator . create ( cmd . Context ( ) )
}
}
// newIAMCreator creates a new iamiamCreator.
2023-08-08 09:42:06 -04:00
func newIAMCreator ( cmd * cobra . Command , pf pathprefix . PathPrefixer , logLevel terraform . LogLevel ) ( * iamCreator , error ) {
2023-02-09 04:37:22 -05:00
spinner , err := newSpinnerOrStderr ( cmd )
if err != nil {
return nil , fmt . Errorf ( "creating spinner: %w" , err )
}
log , err := newCLILogger ( cmd )
if err != nil {
return nil , fmt . Errorf ( "creating logger: %w" , err )
}
2023-08-16 09:38:40 -04:00
log . Debugf ( "Terraform logs will be written into %s at level %s" , pf . PrefixPrintablePath ( constants . TerraformLogFile ) , logLevel . String ( ) )
2023-04-14 08:15:07 -04:00
2023-02-01 05:32:01 -05:00
return & iamCreator {
cmd : cmd ,
spinner : spinner ,
2023-02-09 04:37:22 -05:00
log : log ,
2023-02-01 05:32:01 -05:00
creator : cloudcmd . NewIAMCreator ( spinner ) ,
fileHandler : file . NewHandler ( afero . NewOsFs ( ) ) ,
2023-04-14 08:15:07 -04:00
iamConfig : & cloudcmd . IAMConfigOptions {
2023-08-04 07:53:51 -04:00
TFWorkspace : constants . TerraformIAMWorkingDir ,
TFLogLevel : logLevel ,
2023-04-14 08:15:07 -04:00
} ,
2023-02-09 04:37:22 -05:00
} , nil
2023-02-01 05:32:01 -05:00
}
// iamCreator is the iamCreator for the iam create command.
type iamCreator struct {
cmd * cobra . Command
spinner spinnerInterf
creator cloudIAMCreator
fileHandler file . Handler
provider cloudprovider . Provider
providerCreator providerIAMCreator
2023-04-14 08:15:07 -04:00
iamConfig * cloudcmd . IAMConfigOptions
2023-02-09 04:37:22 -05:00
log debugLog
2023-08-08 09:42:06 -04:00
pf pathprefix . PathPrefixer
2023-02-01 05:32:01 -05:00
}
// create IAM configuration on the iamCreator's cloud provider.
func ( c * iamCreator ) create ( ctx context . Context ) error {
flags , err := c . parseFlagsAndSetupConfig ( )
if err != nil {
return err
}
2023-02-09 04:37:22 -05:00
c . log . Debugf ( "Using flags: %+v" , flags )
2023-02-01 05:32:01 -05:00
2023-08-08 09:42:06 -04:00
if err := c . checkWorkingDir ( ) ; err != nil {
2023-02-13 02:42:54 -05:00
return err
}
2023-02-01 05:32:01 -05:00
if ! flags . yesFlag {
c . cmd . Printf ( "The following IAM configuration will be created:\n\n" )
c . providerCreator . printConfirmValues ( c . cmd , flags )
ok , err := askToConfirm ( c . cmd , "Do you want to create the configuration?" )
if err != nil {
return err
}
if ! ok {
c . cmd . Println ( "The creation of the configuration was aborted." )
return nil
}
}
2023-06-28 08:47:44 -04:00
var conf config . Config
if flags . updateConfig {
2023-08-16 09:38:40 -04:00
c . log . Debugf ( "Parsing config %s" , c . pf . PrefixPrintablePath ( constants . ConfigFilename ) )
2023-08-04 07:53:51 -04:00
if err = c . fileHandler . ReadYAML ( constants . ConfigFilename , & conf ) ; err != nil {
2023-06-28 08:47:44 -04:00
return fmt . Errorf ( "error reading the configuration file: %w" , err )
}
2023-07-04 07:55:52 -04:00
if err := validateConfigWithFlagCompatibility ( c . provider , conf , flags ) ; err != nil {
return err
}
2023-08-16 09:38:40 -04:00
c . cmd . Printf ( "The configuration file %q will be automatically updated with the IAM values and zone/region information.\n" , c . pf . PrefixPrintablePath ( constants . ConfigFilename ) )
2023-06-28 08:47:44 -04:00
}
2023-02-01 05:32:01 -05:00
2023-06-28 08:47:44 -04:00
c . spinner . Start ( "Creating" , false )
2023-02-01 05:32:01 -05:00
iamFile , err := c . creator . Create ( ctx , c . provider , c . iamConfig )
c . spinner . Stop ( )
if err != nil {
return err
}
c . cmd . Println ( ) // Print empty line to separate after spinner ended.
2023-02-09 04:37:22 -05:00
c . log . Debugf ( "Successfully created the IAM cloud resources" )
2023-02-01 05:32:01 -05:00
err = c . providerCreator . parseAndWriteIDFile ( iamFile , c . fileHandler )
if err != nil {
return err
}
2023-06-28 08:47:44 -04:00
if flags . updateConfig {
2023-08-16 09:38:40 -04:00
c . log . Debugf ( "Writing IAM configuration to %s" , c . pf . PrefixPrintablePath ( constants . ConfigFilename ) )
2023-06-28 08:47:44 -04:00
c . providerCreator . writeOutputValuesToConfig ( & conf , flags , iamFile )
2023-08-04 07:53:51 -04:00
if err := c . fileHandler . WriteYAML ( constants . ConfigFilename , conf , file . OptOverwrite ) ; err != nil {
2023-02-01 05:32:01 -05:00
return err
}
2023-08-16 09:38:40 -04:00
c . cmd . Printf ( "Your IAM configuration was created and filled into %s successfully.\n" , c . pf . PrefixPrintablePath ( constants . ConfigFilename ) )
2023-02-01 05:32:01 -05:00
return nil
}
c . providerCreator . printOutputValues ( c . cmd , flags , iamFile )
c . cmd . Println ( "Your IAM configuration was created successfully. Please fill the above values into your configuration file." )
return nil
}
// parseFlagsAndSetupConfig parses the flags of the iam create command and fills the values into the IAM config (output values of the command).
func ( c * iamCreator ) parseFlagsAndSetupConfig ( ) ( iamFlags , error ) {
2023-08-08 09:42:06 -04:00
workDir , err := c . cmd . Flags ( ) . GetString ( "workspace" )
2023-02-01 05:32:01 -05:00
if err != nil {
return iamFlags { } , fmt . Errorf ( "parsing config string: %w" , err )
}
2023-08-08 09:42:06 -04:00
c . pf = pathprefix . New ( workDir )
2023-03-03 03:04:54 -05:00
yesFlag , err := c . cmd . Flags ( ) . GetBool ( "yes" )
if err != nil {
return iamFlags { } , fmt . Errorf ( "parsing yes bool: %w" , err )
}
2023-06-28 08:47:44 -04:00
updateConfig , err := c . cmd . Flags ( ) . GetBool ( "update-config" )
2023-02-01 05:32:01 -05:00
if err != nil {
2023-06-28 08:47:44 -04:00
return iamFlags { } , fmt . Errorf ( "parsing update-config bool: %w" , err )
2023-02-01 05:32:01 -05:00
}
flags := iamFlags {
2023-06-28 08:47:44 -04:00
yesFlag : yesFlag ,
updateConfig : updateConfig ,
2023-02-01 05:32:01 -05:00
}
flags , err = c . providerCreator . parseFlagsAndSetupConfig ( c . cmd , flags , c . iamConfig )
if err != nil {
return iamFlags { } , fmt . Errorf ( "parsing provider-specific value: %w" , err )
}
return flags , nil
}
2023-06-28 08:47:44 -04:00
// checkWorkingDir checks if the current working directory already contains a Terraform dir.
2023-08-08 09:42:06 -04:00
func ( c * iamCreator ) checkWorkingDir ( ) error {
2023-02-13 02:42:54 -05:00
if _ , err := c . fileHandler . Stat ( constants . TerraformIAMWorkingDir ) ; err == nil {
2023-08-16 09:38:40 -04:00
return fmt . Errorf ( "the current working directory already contains the Terraform workspace directory %q. Please run the command in a different directory or destroy the existing workspace" , c . pf . PrefixPrintablePath ( constants . TerraformIAMWorkingDir ) )
2023-02-13 02:42:54 -05:00
}
return nil
}
2023-02-01 05:32:01 -05:00
// iamFlags contains the parsed flags of the iam create command, including the parsed flags of the selected cloud provider.
type iamFlags struct {
2023-06-28 08:47:44 -04:00
aws awsFlags
azure azureFlags
gcp gcpFlags
yesFlag bool
updateConfig bool
2023-02-01 05:32:01 -05:00
}
// awsFlags contains the parsed flags of the iam create aws command.
type awsFlags struct {
prefix string
region string
zone string
}
// azureFlags contains the parsed flags of the iam create azure command.
type azureFlags struct {
region string
resourceGroup string
servicePrincipal string
}
// gcpFlags contains the parsed flags of the iam create gcp command.
type gcpFlags struct {
serviceAccountID string
zone string
region string
projectID string
}
// providerIAMCreator is an interface for the IAM actions of different cloud providers.
type providerIAMCreator interface {
// printConfirmValues prints the values that will be created on the cloud provider and need to be confirmed by the user.
printConfirmValues ( cmd * cobra . Command , flags iamFlags )
// printOutputValues prints the values that were created on the cloud provider.
2023-08-08 06:06:22 -04:00
printOutputValues ( cmd * cobra . Command , flags iamFlags , iamFile cloudcmd . IAMOutput )
2023-02-01 05:32:01 -05:00
// writeOutputValuesToConfig writes the output values of the IAM creation to the constellation config file.
2023-08-08 06:06:22 -04:00
writeOutputValuesToConfig ( conf * config . Config , flags iamFlags , iamFile cloudcmd . IAMOutput )
2023-02-01 05:32:01 -05:00
// parseFlagsAndSetupConfig parses the provider-specific flags and fills the values into the IAM config (output values of the command).
2023-04-14 08:15:07 -04:00
parseFlagsAndSetupConfig ( cmd * cobra . Command , flags iamFlags , iamConfig * cloudcmd . IAMConfigOptions ) ( iamFlags , error )
2023-02-01 05:32:01 -05:00
// parseAndWriteIDFile parses the GCP service account key and writes it to a keyfile. It is only implemented for GCP.
2023-08-08 06:06:22 -04:00
parseAndWriteIDFile ( iamFile cloudcmd . IAMOutput , fileHandler file . Handler ) error
2023-02-01 05:32:01 -05:00
}
// awsIAMCreator implements the providerIAMCreator interface for AWS.
type awsIAMCreator struct { }
2023-04-14 08:15:07 -04:00
func ( c * awsIAMCreator ) parseFlagsAndSetupConfig ( cmd * cobra . Command , flags iamFlags , iamConfig * cloudcmd . IAMConfigOptions ) ( iamFlags , error ) {
2023-02-01 05:32:01 -05:00
prefix , err := cmd . Flags ( ) . GetString ( "prefix" )
if err != nil {
return iamFlags { } , fmt . Errorf ( "parsing prefix string: %w" , err )
}
2023-04-25 07:29:07 -04:00
if len ( prefix ) > 36 {
return iamFlags { } , fmt . Errorf ( "prefix must be 36 characters or less" )
}
2023-02-01 05:32:01 -05:00
zone , err := cmd . Flags ( ) . GetString ( "zone" )
if err != nil {
return iamFlags { } , fmt . Errorf ( "parsing zone string: %w" , err )
}
2023-07-04 07:55:52 -04:00
if ! config . ValidateAWSZone ( zone ) {
return iamFlags { } , fmt . Errorf ( "invalid AWS zone. To find a valid zone, please refer to our docs and https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/using-regions-availability-zones.html#concepts-availability-zones" )
}
// Infer region from zone.
region := zone [ : len ( zone ) - 1 ]
if ! config . ValidateAWSRegion ( region ) {
return iamFlags { } , fmt . Errorf ( "invalid AWS region: %s" , region )
}
2023-02-01 05:32:01 -05:00
flags . aws = awsFlags {
prefix : prefix ,
zone : zone ,
2023-07-04 07:55:52 -04:00
region : region ,
2023-02-01 05:32:01 -05:00
}
// Setup IAM config.
iamConfig . AWS = cloudcmd . AWSIAMConfig {
Region : flags . aws . region ,
Prefix : flags . aws . prefix ,
}
return flags , nil
}
func ( c * awsIAMCreator ) printConfirmValues ( cmd * cobra . Command , flags iamFlags ) {
cmd . Printf ( "Region:\t\t%s\n" , flags . aws . region )
cmd . Printf ( "Name Prefix:\t%s\n\n" , flags . aws . prefix )
}
2023-08-08 06:06:22 -04:00
func ( c * awsIAMCreator ) printOutputValues ( cmd * cobra . Command , flags iamFlags , iamFile cloudcmd . IAMOutput ) {
2023-02-01 05:32:01 -05:00
cmd . Printf ( "region:\t\t\t%s\n" , flags . aws . region )
cmd . Printf ( "zone:\t\t\t%s\n" , flags . aws . zone )
cmd . Printf ( "iamProfileControlPlane:\t%s\n" , iamFile . AWSOutput . ControlPlaneInstanceProfile )
cmd . Printf ( "iamProfileWorkerNodes:\t%s\n\n" , iamFile . AWSOutput . WorkerNodeInstanceProfile )
}
2023-08-08 06:06:22 -04:00
func ( c * awsIAMCreator ) writeOutputValuesToConfig ( conf * config . Config , flags iamFlags , iamFile cloudcmd . IAMOutput ) {
2023-02-01 05:32:01 -05:00
conf . Provider . AWS . Region = flags . aws . region
conf . Provider . AWS . Zone = flags . aws . zone
conf . Provider . AWS . IAMProfileControlPlane = iamFile . AWSOutput . ControlPlaneInstanceProfile
conf . Provider . AWS . IAMProfileWorkerNodes = iamFile . AWSOutput . WorkerNodeInstanceProfile
2023-08-02 04:23:15 -04:00
for groupName , group := range conf . NodeGroups {
group . Zone = flags . aws . zone
conf . NodeGroups [ groupName ] = group
}
2023-02-01 05:32:01 -05:00
}
2023-08-08 06:06:22 -04:00
func ( c * awsIAMCreator ) parseAndWriteIDFile ( _ cloudcmd . IAMOutput , _ file . Handler ) error {
2023-02-01 05:32:01 -05:00
return nil
}
// azureIAMCreator implements the providerIAMCreator interface for Azure.
type azureIAMCreator struct { }
2023-04-14 08:15:07 -04:00
func ( c * azureIAMCreator ) parseFlagsAndSetupConfig ( cmd * cobra . Command , flags iamFlags , iamConfig * cloudcmd . IAMConfigOptions ) ( iamFlags , error ) {
2023-02-01 05:32:01 -05:00
region , err := cmd . Flags ( ) . GetString ( "region" )
if err != nil {
return iamFlags { } , fmt . Errorf ( "parsing region string: %w" , err )
}
resourceGroup , err := cmd . Flags ( ) . GetString ( "resourceGroup" )
if err != nil {
return iamFlags { } , fmt . Errorf ( "parsing resourceGroup string: %w" , err )
}
servicePrincipal , err := cmd . Flags ( ) . GetString ( "servicePrincipal" )
if err != nil {
return iamFlags { } , fmt . Errorf ( "parsing servicePrincipal string: %w" , err )
}
flags . azure = azureFlags {
region : region ,
resourceGroup : resourceGroup ,
servicePrincipal : servicePrincipal ,
}
// Setup IAM config.
iamConfig . Azure = cloudcmd . AzureIAMConfig {
Region : flags . azure . region ,
ResourceGroup : flags . azure . resourceGroup ,
ServicePrincipal : flags . azure . servicePrincipal ,
}
return flags , nil
}
func ( c * azureIAMCreator ) printConfirmValues ( cmd * cobra . Command , flags iamFlags ) {
cmd . Printf ( "Region:\t\t\t%s\n" , flags . azure . region )
cmd . Printf ( "Resource Group:\t\t%s\n" , flags . azure . resourceGroup )
cmd . Printf ( "Service Principal:\t%s\n\n" , flags . azure . servicePrincipal )
}
2023-08-08 06:06:22 -04:00
func ( c * azureIAMCreator ) printOutputValues ( cmd * cobra . Command , flags iamFlags , iamFile cloudcmd . IAMOutput ) {
2023-02-01 05:32:01 -05:00
cmd . Printf ( "subscription:\t\t%s\n" , iamFile . AzureOutput . SubscriptionID )
cmd . Printf ( "tenant:\t\t\t%s\n" , iamFile . AzureOutput . TenantID )
cmd . Printf ( "location:\t\t%s\n" , flags . azure . region )
cmd . Printf ( "resourceGroup:\t\t%s\n" , flags . azure . resourceGroup )
cmd . Printf ( "userAssignedIdentity:\t%s\n" , iamFile . AzureOutput . UAMIID )
}
2023-08-08 06:06:22 -04:00
func ( c * azureIAMCreator ) writeOutputValuesToConfig ( conf * config . Config , flags iamFlags , iamFile cloudcmd . IAMOutput ) {
2023-02-01 05:32:01 -05:00
conf . Provider . Azure . SubscriptionID = iamFile . AzureOutput . SubscriptionID
conf . Provider . Azure . TenantID = iamFile . AzureOutput . TenantID
conf . Provider . Azure . Location = flags . azure . region
conf . Provider . Azure . ResourceGroup = flags . azure . resourceGroup
conf . Provider . Azure . UserAssignedIdentity = iamFile . AzureOutput . UAMIID
}
2023-08-08 06:06:22 -04:00
func ( c * azureIAMCreator ) parseAndWriteIDFile ( _ cloudcmd . IAMOutput , _ file . Handler ) error {
2023-02-01 05:32:01 -05:00
return nil
}
// gcpIAMCreator implements the providerIAMCreator interface for GCP.
2023-08-04 07:53:51 -04:00
type gcpIAMCreator struct {
2023-08-08 09:42:06 -04:00
pf pathprefix . PathPrefixer
2023-08-04 07:53:51 -04:00
}
2023-02-01 05:32:01 -05:00
2023-04-14 08:15:07 -04:00
func ( c * gcpIAMCreator ) parseFlagsAndSetupConfig ( cmd * cobra . Command , flags iamFlags , iamConfig * cloudcmd . IAMConfigOptions ) ( iamFlags , error ) {
2023-02-01 05:32:01 -05:00
zone , err := cmd . Flags ( ) . GetString ( "zone" )
if err != nil {
return iamFlags { } , fmt . Errorf ( "parsing zone string: %w" , err )
}
if ! zoneRegex . MatchString ( zone ) {
return iamFlags { } , fmt . Errorf ( "invalid zone string: %s" , zone )
}
// Infer region from zone.
zoneParts := strings . Split ( zone , "-" )
region := fmt . Sprintf ( "%s-%s" , zoneParts [ 0 ] , zoneParts [ 1 ] )
if ! regionRegex . MatchString ( region ) {
return iamFlags { } , fmt . Errorf ( "invalid region string: %s" , region )
}
projectID , err := cmd . Flags ( ) . GetString ( "projectID" )
if err != nil {
return iamFlags { } , fmt . Errorf ( "parsing projectID string: %w" , err )
}
2023-07-05 10:44:57 -04:00
if ! gcpIDRegex . MatchString ( projectID ) {
return iamFlags { } , fmt . Errorf ( "projectID %q doesn't match %s" , projectID , gcpIDRegex )
2023-02-01 05:32:01 -05:00
}
serviceAccID , err := cmd . Flags ( ) . GetString ( "serviceAccountID" )
if err != nil {
return iamFlags { } , fmt . Errorf ( "parsing serviceAccountID string: %w" , err )
}
2023-07-05 10:44:57 -04:00
if ! gcpIDRegex . MatchString ( serviceAccID ) {
return iamFlags { } , fmt . Errorf ( "serviceAccountID %q doesn't match %s" , serviceAccID , gcpIDRegex )
2023-02-01 05:32:01 -05:00
}
flags . gcp = gcpFlags {
zone : zone ,
region : region ,
projectID : projectID ,
serviceAccountID : serviceAccID ,
}
// Setup IAM config.
iamConfig . GCP = cloudcmd . GCPIAMConfig {
Zone : flags . gcp . zone ,
Region : flags . gcp . region ,
ProjectID : flags . gcp . projectID ,
ServiceAccountID : flags . gcp . serviceAccountID ,
}
return flags , nil
}
func ( c * gcpIAMCreator ) printConfirmValues ( cmd * cobra . Command , flags iamFlags ) {
cmd . Printf ( "Project ID:\t\t%s\n" , flags . gcp . projectID )
cmd . Printf ( "Service Account ID:\t%s\n" , flags . gcp . serviceAccountID )
cmd . Printf ( "Region:\t\t\t%s\n" , flags . gcp . region )
cmd . Printf ( "Zone:\t\t\t%s\n\n" , flags . gcp . zone )
}
2023-08-08 06:06:22 -04:00
func ( c * gcpIAMCreator ) printOutputValues ( cmd * cobra . Command , flags iamFlags , _ cloudcmd . IAMOutput ) {
2023-08-04 07:53:51 -04:00
cmd . Printf ( "projectID:\t\t%s\n" , flags . gcp . projectID )
cmd . Printf ( "region:\t\t\t%s\n" , flags . gcp . region )
cmd . Printf ( "zone:\t\t\t%s\n" , flags . gcp . zone )
2023-08-16 09:38:40 -04:00
cmd . Printf ( "serviceAccountKeyPath:\t%s\n\n" , c . pf . PrefixPrintablePath ( constants . GCPServiceAccountKeyFilename ) )
2023-02-01 05:32:01 -05:00
}
2023-08-08 06:06:22 -04:00
func ( c * gcpIAMCreator ) writeOutputValuesToConfig ( conf * config . Config , flags iamFlags , _ cloudcmd . IAMOutput ) {
2023-02-15 08:24:52 -05:00
conf . Provider . GCP . Project = flags . gcp . projectID
2023-08-08 09:42:06 -04:00
conf . Provider . GCP . ServiceAccountKeyPath = constants . GCPServiceAccountKeyFilename // File was created in workspace, so only the filename is needed.
2023-02-15 08:24:52 -05:00
conf . Provider . GCP . Region = flags . gcp . region
conf . Provider . GCP . Zone = flags . gcp . zone
2023-08-02 04:23:15 -04:00
for groupName , group := range conf . NodeGroups {
group . Zone = flags . gcp . zone
conf . NodeGroups [ groupName ] = group
}
2023-02-01 05:32:01 -05:00
}
2023-08-08 06:06:22 -04:00
func ( c * gcpIAMCreator ) parseAndWriteIDFile ( iamFile cloudcmd . IAMOutput , fileHandler file . Handler ) error {
2023-02-01 05:32:01 -05:00
// GCP needs to write the service account key to a file.
tmpOut , err := parseIDFile ( iamFile . GCPOutput . ServiceAccountKey )
if err != nil {
return err
}
2023-08-08 09:42:06 -04:00
return fileHandler . WriteJSON ( constants . GCPServiceAccountKeyFilename , tmpOut , file . OptNone )
2023-02-01 05:32:01 -05:00
}
// parseIDFile parses the given base64 encoded JSON string of the GCP service account key and returns a map.
func parseIDFile ( serviceAccountKeyBase64 string ) ( map [ string ] string , error ) {
dec , err := base64 . StdEncoding . DecodeString ( serviceAccountKeyBase64 )
if err != nil {
return nil , err
}
out := make ( map [ string ] string )
if err = json . Unmarshal ( dec , & out ) ; err != nil {
return nil , err
}
return out , nil
}
2023-05-03 05:07:47 -04:00
2023-07-04 07:55:52 -04:00
// validateConfigWithFlagCompatibility checks if the config is compatible with the flags.
func validateConfigWithFlagCompatibility ( iamProvider cloudprovider . Provider , cfg config . Config , flags iamFlags ) error {
if ! cfg . HasProvider ( iamProvider ) {
return fmt . Errorf ( "cloud provider from the the configuration file differs from the one provided via the command %q" , iamProvider )
}
return checkIfCfgZoneAndFlagZoneDiffer ( iamProvider , flags , cfg )
}
func checkIfCfgZoneAndFlagZoneDiffer ( iamProvider cloudprovider . Provider , flags iamFlags , cfg config . Config ) error {
flagZone := flagZoneOrAzRegion ( iamProvider , flags )
configZone := cfg . GetZone ( )
if configZone != "" && flagZone != configZone {
return fmt . Errorf ( "zone/region from the configuration file %q differs from the one provided via flags %q" , configZone , flagZone )
}
return nil
}
func flagZoneOrAzRegion ( provider cloudprovider . Provider , flags iamFlags ) string {
switch provider {
case cloudprovider . AWS :
return flags . aws . zone
case cloudprovider . Azure :
return flags . azure . region
case cloudprovider . GCP :
return flags . gcp . zone
2023-05-03 05:07:47 -04:00
}
2023-07-04 07:55:52 -04:00
return ""
2023-05-03 05:07:47 -04:00
}