2022-03-22 11:03:15 -04:00
package cmd
import (
"context"
"encoding/base64"
"errors"
"fmt"
"io"
"io/fs"
"net"
2022-05-13 07:10:27 -04:00
"strconv"
2022-04-05 03:13:09 -04:00
"text/tabwriter"
2022-03-22 11:03:15 -04:00
2022-06-07 10:30:41 -04:00
"github.com/edgelesssys/constellation/cli/internal/azure"
2022-06-08 02:26:08 -04:00
"github.com/edgelesssys/constellation/cli/internal/cloudcmd"
2022-06-07 08:52:47 -04:00
"github.com/edgelesssys/constellation/cli/internal/gcp"
2022-06-07 05:05:52 -04:00
"github.com/edgelesssys/constellation/cli/internal/proto"
"github.com/edgelesssys/constellation/cli/internal/vpn"
2022-05-16 11:32:00 -04:00
"github.com/edgelesssys/constellation/coordinator/pubapi/pubproto"
2022-03-22 11:03:15 -04:00
coordinatorstate "github.com/edgelesssys/constellation/coordinator/state"
"github.com/edgelesssys/constellation/coordinator/util"
2022-06-01 09:08:42 -04:00
"github.com/edgelesssys/constellation/internal/atls"
2022-06-07 05:08:44 -04:00
"github.com/edgelesssys/constellation/internal/cloud/cloudprovider"
2022-06-08 02:17:52 -04:00
"github.com/edgelesssys/constellation/internal/cloud/cloudtypes"
2022-03-22 11:03:15 -04:00
"github.com/edgelesssys/constellation/internal/config"
2022-04-06 04:36:58 -04:00
"github.com/edgelesssys/constellation/internal/constants"
2022-05-16 11:32:00 -04:00
"github.com/edgelesssys/constellation/internal/deploy/ssh"
"github.com/edgelesssys/constellation/internal/file"
2022-03-22 11:03:15 -04:00
"github.com/edgelesssys/constellation/internal/state"
2022-06-08 03:19:15 -04:00
"github.com/edgelesssys/constellation/internal/statuswaiter"
2022-04-05 03:11:45 -04:00
"github.com/kr/text"
2022-04-12 08:20:46 -04:00
wgquick "github.com/nmiculinic/wg-quick-go"
2022-04-05 03:11:45 -04:00
"github.com/spf13/afero"
"github.com/spf13/cobra"
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
2022-03-22 11:03:15 -04:00
)
2022-06-08 02:14:28 -04:00
// NewInitCmd returns a new cobra.Command for the init command.
func NewInitCmd ( ) * cobra . Command {
2022-03-22 11:03:15 -04:00
cmd := & cobra . Command {
Use : "init" ,
2022-05-06 11:51:41 -04:00
Short : "Initialize the Constellation cluster" ,
2022-05-04 03:13:46 -04:00
Long : "Initialize the Constellation cluster. Start your confidential Kubernetes." ,
2022-03-22 11:03:15 -04:00
ValidArgsFunction : initCompletion ,
Args : cobra . ExactArgs ( 0 ) ,
RunE : runInitialize ,
}
2022-05-06 11:51:41 -04:00
cmd . Flags ( ) . String ( "privatekey" , "" , "path to your private key" )
2022-05-09 11:02:47 -04:00
cmd . Flags ( ) . String ( "master-secret" , "" , "path to base64-encoded master secret" )
2022-05-06 11:51:41 -04:00
cmd . Flags ( ) . Bool ( "wg-autoconfig" , false , "enable automatic configuration of WireGuard interface" )
2022-05-04 02:50:50 -04:00
must ( cmd . Flags ( ) . MarkHidden ( "wg-autoconfig" ) )
2022-05-06 11:51:41 -04:00
cmd . Flags ( ) . Bool ( "autoscale" , false , "enable Kubernetes cluster-autoscaler" )
2022-03-22 11:03:15 -04:00
return cmd
}
// runInitialize runs the initialize command.
func runInitialize ( cmd * cobra . Command , args [ ] string ) error {
fileHandler := file . NewHandler ( afero . NewOsFs ( ) )
2022-04-12 08:20:46 -04:00
vpnHandler := vpn . NewConfigHandler ( )
2022-04-13 07:01:38 -04:00
serviceAccountCreator := cloudcmd . NewServiceAccountCreator ( )
2022-06-08 03:19:15 -04:00
waiter := statuswaiter . New ( )
2022-04-13 09:01:02 -04:00
protoClient := & proto . Client { }
defer protoClient . Close ( )
2022-04-13 07:01:38 -04:00
2022-03-22 11:03:15 -04:00
// We have to parse the context separately, since cmd.Context()
// returns nil during the tests otherwise.
2022-04-13 09:01:02 -04:00
return initialize ( cmd . Context ( ) , cmd , protoClient , serviceAccountCreator , fileHandler , waiter , vpnHandler )
2022-03-22 11:03:15 -04:00
}
2022-04-27 08:21:36 -04:00
// initialize initializes a Constellation. Coordinator instances are activated as contole-plane nodes and will
// themself activate the other peers as workers.
2022-04-13 07:01:38 -04:00
func initialize ( ctx context . Context , cmd * cobra . Command , protCl protoClient , serviceAccCreator serviceAccountCreator ,
2022-04-13 09:01:02 -04:00
fileHandler file . Handler , waiter statusWaiter , vpnHandler vpnHandler ,
2022-03-22 11:03:15 -04:00
) error {
2022-04-13 09:01:02 -04:00
flags , err := evalFlagArgs ( cmd , fileHandler )
if err != nil {
return err
}
2022-05-23 09:01:39 -04:00
var stat state . ConstellationState
err = fileHandler . ReadJSON ( constants . StateFilename , & stat )
if errors . Is ( err , fs . ErrNotExist ) {
return fmt . Errorf ( "nothing to initialize: %w" , err )
} else if err != nil {
return err
}
provider := cloudprovider . FromString ( stat . CloudProvider )
config , err := readConfig ( cmd . OutOrStdout ( ) , fileHandler , flags . configPath , provider )
2022-03-22 11:03:15 -04:00
if err != nil {
return err
}
2022-05-17 04:52:37 -04:00
var sshUsers [ ] * ssh . UserKey
for _ , user := range config . SSHUsers {
sshUsers = append ( sshUsers , & ssh . UserKey {
Username : user . Username ,
PublicKey : user . PublicKey ,
} )
}
2022-05-23 09:01:39 -04:00
validators , err := cloudcmd . NewValidators ( provider , config )
2022-04-19 11:02:02 -04:00
if err != nil {
return err
2022-03-22 11:03:15 -04:00
}
2022-04-19 11:02:02 -04:00
cmd . Print ( validators . WarningsIncludeInit ( ) )
2022-03-22 11:03:15 -04:00
2022-04-05 03:11:45 -04:00
cmd . Println ( "Creating service account ..." )
2022-04-13 07:01:38 -04:00
serviceAccount , stat , err := serviceAccCreator . Create ( ctx , stat , config )
2022-03-22 11:03:15 -04:00
if err != nil {
return err
}
2022-04-06 04:36:58 -04:00
if err := fileHandler . WriteJSON ( constants . StateFilename , stat , file . OptOverwrite ) ; err != nil {
2022-03-22 11:03:15 -04:00
return err
}
coordinators , nodes , err := getScalingGroupsFromConfig ( stat , config )
if err != nil {
return err
}
2022-05-13 07:10:27 -04:00
endpoints := ipsToEndpoints ( append ( coordinators . PublicIPs ( ) , nodes . PublicIPs ( ) ... ) , strconv . Itoa ( constants . CoordinatorPort ) )
2022-04-19 11:02:02 -04:00
2022-04-27 08:21:36 -04:00
cmd . Println ( "Waiting for cloud provider resource creation and boot ..." )
2022-04-27 02:30:47 -04:00
if err := waiter . InitializeValidators ( validators . V ( ) ) ; err != nil {
return err
}
2022-03-29 03:10:22 -04:00
if err := waiter . WaitForAll ( ctx , endpoints , coordinatorstate . AcceptingInit ) ; err != nil {
2022-03-22 11:03:15 -04:00
return fmt . Errorf ( "failed to wait for peer status: %w" , err )
}
var autoscalingNodeGroups [ ] string
2022-04-13 09:01:02 -04:00
if flags . autoscale {
2022-03-22 11:03:15 -04:00
autoscalingNodeGroups = append ( autoscalingNodeGroups , nodes . GroupID )
}
input := activationInput {
coordinatorPubIP : coordinators . PublicIPs ( ) [ 0 ] ,
2022-04-13 09:01:02 -04:00
pubKey : flags . userPubKey ,
masterSecret : flags . masterSecret ,
2022-03-22 11:03:15 -04:00
nodePrivIPs : nodes . PrivateIPs ( ) ,
2022-04-25 11:21:58 -04:00
coordinatorPrivIPs : coordinators . PrivateIPs ( ) [ 1 : ] ,
2022-03-22 11:03:15 -04:00
autoscalingNodeGroups : autoscalingNodeGroups ,
cloudServiceAccountURI : serviceAccount ,
2022-05-23 09:01:39 -04:00
sshUserKeys : ssh . ToProtoSlice ( sshUsers ) ,
2022-03-22 11:03:15 -04:00
}
2022-05-13 07:10:27 -04:00
result , err := activate ( ctx , cmd , protCl , input , validators . V ( ) )
2022-03-22 11:03:15 -04:00
if err != nil {
return err
}
2022-04-06 04:36:58 -04:00
err = result . writeOutput ( cmd . OutOrStdout ( ) , fileHandler )
2022-03-22 11:03:15 -04:00
if err != nil {
return err
}
2022-04-12 10:07:17 -04:00
vpnConfig , err := vpnHandler . Create ( result . coordinatorPubKey , result . coordinatorPubIP , string ( flags . userPrivKey ) , result . clientVpnIP , constants . WireguardAdminMTU )
2022-04-12 08:20:46 -04:00
if err != nil {
return err
}
2022-04-06 04:36:58 -04:00
if err := writeWGQuickFile ( fileHandler , vpnHandler , vpnConfig ) ; err != nil {
2022-03-29 05:38:14 -04:00
return fmt . Errorf ( "write wg-quick file: %w" , err )
}
2022-04-13 09:01:02 -04:00
if flags . autoconfigureWG {
2022-04-12 08:20:46 -04:00
if err := vpnHandler . Apply ( vpnConfig ) ; err != nil {
2022-03-22 11:03:15 -04:00
return err
}
}
return nil
}
2022-04-19 11:02:02 -04:00
func activate ( ctx context . Context , cmd * cobra . Command , client protoClient , input activationInput ,
2022-05-13 07:10:27 -04:00
validators [ ] atls . Validator ,
2022-04-19 11:02:02 -04:00
) ( activationResult , error ) {
2022-05-13 07:10:27 -04:00
err := client . Connect ( net . JoinHostPort ( input . coordinatorPubIP , strconv . Itoa ( constants . CoordinatorPort ) ) , validators )
2022-04-13 09:01:02 -04:00
if err != nil {
2022-03-22 11:03:15 -04:00
return activationResult { } , err
}
2022-05-16 11:32:00 -04:00
respCl , err := client . Activate ( ctx , input . pubKey , input . masterSecret , input . nodePrivIPs , input . coordinatorPrivIPs , input . autoscalingNodeGroups , input . cloudServiceAccountURI , input . sshUserKeys )
2022-03-22 11:03:15 -04:00
if err != nil {
return activationResult { } , err
}
2022-04-05 03:11:45 -04:00
indentOut := text . NewIndentWriter ( cmd . OutOrStdout ( ) , [ ] byte { '\t' } )
cmd . Println ( "Activating the cluster ..." )
if err := respCl . WriteLogStream ( indentOut ) ; err != nil {
2022-03-22 11:03:15 -04:00
return activationResult { } , err
}
clientVpnIp , err := respCl . GetClientVpnIp ( )
if err != nil {
return activationResult { } , err
}
coordinatorPubKey , err := respCl . GetCoordinatorVpnKey ( )
if err != nil {
return activationResult { } , err
}
kubeconfig , err := respCl . GetKubeconfig ( )
if err != nil {
return activationResult { } , err
}
ownerID , err := respCl . GetOwnerID ( )
if err != nil {
return activationResult { } , err
}
clusterID , err := respCl . GetClusterID ( )
if err != nil {
return activationResult { } , err
}
return activationResult {
clientVpnIP : clientVpnIp ,
coordinatorPubKey : coordinatorPubKey ,
coordinatorPubIP : input . coordinatorPubIP ,
kubeconfig : kubeconfig ,
ownerID : ownerID ,
clusterID : clusterID ,
} , nil
}
type activationInput struct {
coordinatorPubIP string
pubKey [ ] byte
masterSecret [ ] byte
nodePrivIPs [ ] string
2022-04-25 11:21:58 -04:00
coordinatorPrivIPs [ ] string
2022-03-22 11:03:15 -04:00
autoscalingNodeGroups [ ] string
cloudServiceAccountURI string
2022-05-16 11:32:00 -04:00
sshUserKeys [ ] * pubproto . SSHUserKey
2022-03-22 11:03:15 -04:00
}
type activationResult struct {
clientVpnIP string
coordinatorPubKey string
coordinatorPubIP string
kubeconfig string
ownerID string
clusterID string
}
2022-03-29 05:38:14 -04:00
// writeWGQuickFile writes the wg-quick file to the default path.
2022-04-06 04:36:58 -04:00
func writeWGQuickFile ( fileHandler file . Handler , vpnHandler vpnHandler , vpnConfig * wgquick . Config ) error {
2022-04-12 08:20:46 -04:00
data , err := vpnHandler . Marshal ( vpnConfig )
2022-03-29 05:38:14 -04:00
if err != nil {
2022-04-12 08:20:46 -04:00
return err
2022-03-29 05:38:14 -04:00
}
2022-04-06 04:36:58 -04:00
return fileHandler . Write ( constants . WGQuickConfigFilename , data , file . OptNone )
2022-03-29 05:38:14 -04:00
}
2022-04-06 04:36:58 -04:00
func ( r activationResult ) writeOutput ( wr io . Writer , fileHandler file . Handler ) error {
2022-04-27 08:21:36 -04:00
fmt . Fprint ( wr , "Your Constellation cluster was successfully initialized.\n\n" )
2022-04-05 03:13:09 -04:00
tw := tabwriter . NewWriter ( wr , 0 , 0 , 2 , ' ' , 0 )
writeRow ( tw , "Your WireGuard IP" , r . clientVpnIP )
2022-04-27 08:21:36 -04:00
writeRow ( tw , "Control plane's public IP" , r . coordinatorPubIP )
writeRow ( tw , "Control plane's public key" , r . coordinatorPubKey )
2022-05-04 03:13:46 -04:00
writeRow ( tw , "Constellation cluster's owner identifier" , r . ownerID )
writeRow ( tw , "Constellation cluster's unique identifier" , r . clusterID )
2022-04-06 04:36:58 -04:00
writeRow ( tw , "WireGuard configuration file" , constants . WGQuickConfigFilename )
writeRow ( tw , "Kubernetes configuration" , constants . AdminConfFilename )
2022-04-05 03:13:09 -04:00
tw . Flush ( )
fmt . Fprintln ( wr )
2022-04-06 04:36:58 -04:00
if err := fileHandler . Write ( constants . AdminConfFilename , [ ] byte ( r . kubeconfig ) , file . OptNone ) ; err != nil {
2022-04-05 03:13:09 -04:00
return fmt . Errorf ( "write kubeconfig: %w" , err )
2022-03-22 11:03:15 -04:00
}
2022-04-05 03:13:09 -04:00
2022-05-04 03:13:46 -04:00
fmt . Fprintln ( wr , "You can now connect to your cluster by executing:" )
2022-04-06 04:36:58 -04:00
fmt . Fprintf ( wr , "\twg-quick up ./%s\n" , constants . WGQuickConfigFilename )
fmt . Fprintf ( wr , "\texport KUBECONFIG=\"$PWD/%s\"\n" , constants . AdminConfFilename )
2022-03-22 11:03:15 -04:00
return nil
}
2022-04-05 03:13:09 -04:00
func writeRow ( wr io . Writer , col1 string , col2 string ) {
fmt . Fprint ( wr , col1 , "\t" , col2 , "\n" )
}
2022-03-22 11:03:15 -04:00
// evalFlagArgs gets the flag values and does preprocessing of these values like
// reading the content from file path flags and deriving other values from flag combinations.
2022-04-13 09:01:02 -04:00
func evalFlagArgs ( cmd * cobra . Command , fileHandler file . Handler ) ( initFlags , error ) {
2022-03-22 11:03:15 -04:00
userPrivKeyPath , err := cmd . Flags ( ) . GetString ( "privatekey" )
if err != nil {
2022-04-13 09:01:02 -04:00
return initFlags { } , err
2022-03-22 11:03:15 -04:00
}
2022-03-28 02:58:56 -04:00
userPrivKey , userPubKey , err := readOrGenerateVPNKey ( fileHandler , userPrivKeyPath )
2022-03-22 11:03:15 -04:00
if err != nil {
2022-04-13 09:01:02 -04:00
return initFlags { } , err
2022-03-22 11:03:15 -04:00
}
2022-03-28 02:58:56 -04:00
autoconfigureWG , err := cmd . Flags ( ) . GetBool ( "wg-autoconfig" )
2022-03-22 11:03:15 -04:00
if err != nil {
2022-04-13 09:01:02 -04:00
return initFlags { } , err
2022-03-22 11:03:15 -04:00
}
masterSecretPath , err := cmd . Flags ( ) . GetString ( "master-secret" )
if err != nil {
2022-04-13 09:01:02 -04:00
return initFlags { } , err
2022-03-22 11:03:15 -04:00
}
2022-04-06 04:36:58 -04:00
masterSecret , err := readOrGeneratedMasterSecret ( cmd . OutOrStdout ( ) , fileHandler , masterSecretPath )
2022-03-22 11:03:15 -04:00
if err != nil {
2022-04-13 09:01:02 -04:00
return initFlags { } , err
2022-03-22 11:03:15 -04:00
}
autoscale , err := cmd . Flags ( ) . GetBool ( "autoscale" )
if err != nil {
2022-04-13 09:01:02 -04:00
return initFlags { } , err
}
2022-05-13 05:56:43 -04:00
configPath , err := cmd . Flags ( ) . GetString ( "config" )
2022-04-13 09:01:02 -04:00
if err != nil {
return initFlags { } , err
2022-03-22 11:03:15 -04:00
}
2022-04-13 09:01:02 -04:00
return initFlags {
2022-05-13 05:56:43 -04:00
configPath : configPath ,
2022-03-22 11:03:15 -04:00
userPrivKey : userPrivKey ,
userPubKey : userPubKey ,
2022-03-28 02:58:56 -04:00
autoconfigureWG : autoconfigureWG ,
2022-03-22 11:03:15 -04:00
autoscale : autoscale ,
masterSecret : masterSecret ,
} , nil
}
2022-04-13 09:01:02 -04:00
// initFlags are the resulting values of flag preprocessing.
type initFlags struct {
2022-05-13 05:56:43 -04:00
configPath string
2022-03-22 11:03:15 -04:00
userPrivKey [ ] byte
userPubKey [ ] byte
masterSecret [ ] byte
autoconfigureWG bool
autoscale bool
}
2022-03-28 02:58:56 -04:00
func readOrGenerateVPNKey ( fileHandler file . Handler , privKeyPath string ) ( privKey , pubKey [ ] byte , err error ) {
var privKeyParsed wgtypes . Key
if privKeyPath == "" {
privKeyParsed , err = wgtypes . GeneratePrivateKey ( )
2022-03-22 11:03:15 -04:00
if err != nil {
return nil , nil , err
}
2022-03-28 02:58:56 -04:00
privKey = [ ] byte ( privKeyParsed . String ( ) )
} else {
privKey , err = fileHandler . Read ( privKeyPath )
2022-03-22 11:03:15 -04:00
if err != nil {
return nil , nil , err
}
2022-03-28 02:58:56 -04:00
privKeyParsed , err = wgtypes . ParseKey ( string ( privKey ) )
2022-03-22 11:03:15 -04:00
if err != nil {
return nil , nil , err
}
}
2022-03-28 02:58:56 -04:00
pubKey = [ ] byte ( privKeyParsed . PublicKey ( ) . String ( ) )
2022-03-22 11:03:15 -04:00
return privKey , pubKey , nil
}
func ipsToEndpoints ( ips [ ] string , port string ) [ ] string {
var endpoints [ ] string
for _ , ip := range ips {
2022-05-24 04:04:42 -04:00
if ip == "" {
continue
}
2022-03-22 11:03:15 -04:00
endpoints = append ( endpoints , net . JoinHostPort ( ip , port ) )
}
return endpoints
}
// readOrGeneratedMasterSecret reads a base64 encoded master secret from file or generates a new 32 byte secret.
2022-04-06 04:36:58 -04:00
func readOrGeneratedMasterSecret ( w io . Writer , fileHandler file . Handler , filename string ) ( [ ] byte , error ) {
2022-03-22 11:03:15 -04:00
if filename != "" {
// Try to read the base64 secret from file
encodedSecret , err := fileHandler . Read ( filename )
if err != nil {
return nil , err
}
decoded , err := base64 . StdEncoding . DecodeString ( string ( encodedSecret ) )
if err != nil {
return nil , err
}
2022-04-12 10:07:17 -04:00
if len ( decoded ) < constants . MasterSecretLengthMin {
2022-03-22 11:03:15 -04:00
return nil , errors . New ( "provided master secret is smaller than the required minimum of 16 Bytes" )
}
return decoded , nil
}
// No file given, generate a new secret, and save it to disk
2022-04-12 10:07:17 -04:00
masterSecret , err := util . GenerateRandomBytes ( constants . MasterSecretLengthDefault )
2022-03-22 11:03:15 -04:00
if err != nil {
return nil , err
}
2022-04-06 04:36:58 -04:00
if err := fileHandler . Write ( constants . MasterSecretFilename , [ ] byte ( base64 . StdEncoding . EncodeToString ( masterSecret ) ) , file . OptNone ) ; err != nil {
2022-03-22 11:03:15 -04:00
return nil , err
}
2022-04-06 04:36:58 -04:00
fmt . Fprintf ( w , "Your Constellation master secret was successfully written to ./%s\n" , constants . MasterSecretFilename )
2022-03-22 11:03:15 -04:00
return masterSecret , nil
}
2022-06-07 11:15:23 -04:00
func getScalingGroupsFromConfig ( stat state . ConstellationState , config * config . Config ) ( coordinators , nodes cloudtypes . ScalingGroup , err error ) {
2022-03-22 11:03:15 -04:00
switch {
case len ( stat . GCPCoordinators ) != 0 :
return getGCPInstances ( stat , config )
case len ( stat . AzureCoordinators ) != 0 :
2022-03-29 07:30:50 -04:00
return getAzureInstances ( stat , config )
2022-05-02 04:54:54 -04:00
case len ( stat . QEMUCoordinators ) != 0 :
return getQEMUInstances ( stat , config )
2022-03-22 11:03:15 -04:00
default :
2022-06-07 11:15:23 -04:00
return cloudtypes . ScalingGroup { } , cloudtypes . ScalingGroup { } , errors . New ( "no instances to initialize" )
2022-03-22 11:03:15 -04:00
}
}
2022-06-07 11:15:23 -04:00
func getGCPInstances ( stat state . ConstellationState , config * config . Config ) ( coordinators , nodes cloudtypes . ScalingGroup , err error ) {
if len ( stat . GCPCoordinators ) == 0 {
return cloudtypes . ScalingGroup { } , cloudtypes . ScalingGroup { } , errors . New ( "no control-plane nodes available, can't create Constellation without any instance" )
2022-03-22 11:03:15 -04:00
}
2022-06-07 11:15:23 -04:00
2022-03-22 11:03:15 -04:00
// GroupID of coordinators is empty, since they currently do not scale.
2022-06-07 11:15:23 -04:00
coordinators = cloudtypes . ScalingGroup {
Instances : stat . GCPCoordinators ,
2022-04-25 11:21:58 -04:00
GroupID : "" ,
}
2022-03-22 11:03:15 -04:00
2022-06-07 11:15:23 -04:00
if len ( stat . GCPNodes ) == 0 {
return cloudtypes . ScalingGroup { } , cloudtypes . ScalingGroup { } , errors . New ( "no worker nodes available, can't create Constellation with one instance" )
2022-03-22 11:03:15 -04:00
}
// TODO: make min / max configurable and abstract autoscaling for different cloud providers
2022-06-07 11:15:23 -04:00
nodes = cloudtypes . ScalingGroup {
Instances : stat . GCPNodes ,
2022-05-24 05:57:48 -04:00
GroupID : gcp . AutoscalingNodeGroup ( stat . GCPProject , stat . GCPZone , stat . GCPNodeInstanceGroup , config . AutoscalingNodeGroupMin , config . AutoscalingNodeGroupMax ) ,
2022-03-22 11:03:15 -04:00
}
return
}
2022-06-07 11:15:23 -04:00
func getAzureInstances ( stat state . ConstellationState , config * config . Config ) ( coordinators , nodes cloudtypes . ScalingGroup , err error ) {
if len ( stat . AzureCoordinators ) == 0 {
return cloudtypes . ScalingGroup { } , cloudtypes . ScalingGroup { } , errors . New ( "no control-plane nodes available, can't create Constellation cluster without any instance" )
2022-03-22 11:03:15 -04:00
}
2022-06-07 11:15:23 -04:00
2022-03-22 11:03:15 -04:00
// GroupID of coordinators is empty, since they currently do not scale.
2022-06-07 11:15:23 -04:00
coordinators = cloudtypes . ScalingGroup {
Instances : stat . AzureCoordinators ,
2022-04-25 11:21:58 -04:00
GroupID : "" ,
}
2022-03-22 11:03:15 -04:00
2022-06-07 11:15:23 -04:00
if len ( stat . AzureNodes ) == 0 {
return cloudtypes . ScalingGroup { } , cloudtypes . ScalingGroup { } , errors . New ( "no worker nodes available, can't create Constellation cluster with one instance" )
2022-03-22 11:03:15 -04:00
}
// TODO: make min / max configurable and abstract autoscaling for different cloud providers
2022-06-07 11:15:23 -04:00
nodes = cloudtypes . ScalingGroup {
Instances : stat . AzureNodes ,
2022-05-24 05:57:48 -04:00
GroupID : azure . AutoscalingNodeGroup ( stat . AzureNodesScaleSet , config . AutoscalingNodeGroupMin , config . AutoscalingNodeGroupMax ) ,
2022-03-22 11:03:15 -04:00
}
return
}
2022-06-07 11:15:23 -04:00
func getQEMUInstances ( stat state . ConstellationState , config * config . Config ) ( coordinators , nodes cloudtypes . ScalingGroup , err error ) {
2022-05-02 04:54:54 -04:00
coordinatorMap := stat . QEMUCoordinators
if len ( coordinatorMap ) == 0 {
2022-06-07 11:15:23 -04:00
return cloudtypes . ScalingGroup { } , cloudtypes . ScalingGroup { } , errors . New ( "no coordinators available, can't create Constellation without any instance" )
2022-05-02 04:54:54 -04:00
}
2022-06-07 11:15:23 -04:00
2022-05-02 04:54:54 -04:00
// QEMU does not support autoscaling
2022-06-07 11:15:23 -04:00
coordinators = cloudtypes . ScalingGroup {
Instances : stat . QEMUCoordinators ,
2022-05-02 04:54:54 -04:00
GroupID : "" ,
}
2022-06-07 11:15:23 -04:00
if len ( stat . QEMUNodes ) == 0 {
return cloudtypes . ScalingGroup { } , cloudtypes . ScalingGroup { } , errors . New ( "no nodes available, can't create Constellation with one instance" )
2022-05-02 04:54:54 -04:00
}
// QEMU does not support autoscaling
2022-06-07 11:15:23 -04:00
nodes = cloudtypes . ScalingGroup {
Instances : stat . QEMUNodes ,
2022-05-02 04:54:54 -04:00
GroupID : "" ,
}
return
}
2022-03-22 11:03:15 -04:00
// initCompletion handels the completion of CLI arguments. It is frequently called
// while the user types arguments of the command to suggest completion.
func initCompletion ( cmd * cobra . Command , args [ ] string , toComplete string ) ( [ ] string , cobra . ShellCompDirective ) {
if len ( args ) != 0 {
return [ ] string { } , cobra . ShellCompDirectiveError
}
return [ ] string { } , cobra . ShellCompDirectiveDefault
}