Skip to content

Commit

Permalink
feat: add cloud add (#1015)
Browse files Browse the repository at this point in the history
## Description:
With this change users can create a new cloud instance, by calling
`kurtosis cloud add`. Callers must define the `KURTOSIS_CLOUD_API_KEY`
env var.

## Is this change user facing?
YES

## References (if applicable):
<!-- Add relevant Github Issues, Discord threads, or other helpful
information. -->
  • Loading branch information
adschwartz committed Jul 31, 2023
1 parent 828f366 commit 48aecd0
Show file tree
Hide file tree
Showing 6 changed files with 137 additions and 42 deletions.
1 change: 1 addition & 0 deletions cli/cli/command_str_consts/command_str_consts.go
Expand Up @@ -13,6 +13,7 @@ var KurtosisCmdStr = path.Base(os.Args[0])
const (
Analytics = "analytics"
CleanCmdStr = "clean"
CloudAddCmdStr = "add"
CloudCmdStr = "cloud"
CloudLoadCmdStr = "load"
ClusterCmdStr = "cluster"
Expand Down
2 changes: 1 addition & 1 deletion cli/cli/commands/analytics/analytics.go
Expand Up @@ -77,7 +77,7 @@ func run(ctx context.Context, flags *flags.ParsedFlags, args *args.ParsedArgs) e
metricsUserIdStore := metrics_user_id_store.GetMetricsUserIDStore()
metricsUserId, err := metricsUserIdStore.GetUserID()
if err != nil {
return stacktrace.Propagate(err, "An error occurred while getting the users metrics id")
return stacktrace.Propagate(err, "An error occurred while getting the user's metrics id")
}
out.PrintOutLn(metricsUserId)
return nil
Expand Down
73 changes: 73 additions & 0 deletions cli/cli/commands/cloud/add/add.go
@@ -0,0 +1,73 @@
package add

import (
"context"
"fmt"
"github.com/kurtosis-tech/kurtosis/api/golang/engine/lib/cloud"
"github.com/kurtosis-tech/kurtosis/cli/cli/command_framework/lowlevel"
"github.com/kurtosis-tech/kurtosis/cli/cli/command_framework/lowlevel/args"
"github.com/kurtosis-tech/kurtosis/cli/cli/command_framework/lowlevel/flags"
"github.com/kurtosis-tech/kurtosis/cli/cli/command_str_consts"
cloudhelper "github.com/kurtosis-tech/kurtosis/cli/cli/helpers/cloud"
"github.com/kurtosis-tech/kurtosis/cli/cli/helpers/metrics_user_id_store"
api "github.com/kurtosis-tech/kurtosis/cloud/api/golang/kurtosis_backend_server_rpc_api_bindings"
"github.com/kurtosis-tech/stacktrace"
"github.com/sirupsen/logrus"
)

var AddCmd = &lowlevel.LowlevelKurtosisCommand{
CommandStr: command_str_consts.CloudAddCmdStr,
ShortDescription: "Create a new Kurtosis Cloud instance",
LongDescription: "Create a new remote Kurtosis Cloud instance",
Flags: []*flags.FlagConfig{},
Args: nil,
PreValidationAndRunFunc: nil,
RunFunc: run,
PostValidationAndRunFunc: nil,
}

func run(ctx context.Context, _ *flags.ParsedFlags, _ *args.ParsedArgs) error {
logrus.Info("Creating a new remote Kurtosis Cloud instance")
apiKey, err := cloudhelper.LoadApiKey()
if err != nil {
return stacktrace.Propagate(err, "Could not load an API Key. Check that it's defined using the "+
"%s env var and it's a valid (active) key", cloudhelper.KurtosisCloudApiKeyEnvVarArg)
}

// Use metrics id for now until we replace with a proper auth'd id:
metricsUserIdStore := metrics_user_id_store.GetMetricsUserIDStore()
metricsUserId, err := metricsUserIdStore.GetUserID()
if err != nil {
return stacktrace.Propagate(err, "An error occurred while getting the user's id")
}

cloudConfig, err := cloudhelper.GetCloudConfig()
if err != nil {
return stacktrace.Propagate(err, "An error occurred while loading the Cloud Config")
}
// Create the connection
connectionStr := fmt.Sprintf("%s:%d", cloudConfig.ApiUrl, cloudConfig.Port)
client, err := cloud.CreateCloudClient(connectionStr, cloudConfig.CertificateChain)
if err != nil {
return stacktrace.Propagate(err, "Error building client for Kurtosis Cloud")
}

getConfigArgs := &api.CreateCloudInstanceConfigArgs{
ApiKey: *apiKey,
UserId: metricsUserId,
}
result, err := client.CreateCloudInstance(ctx, getConfigArgs)
if err != nil {
return stacktrace.Propagate(err, "An error occurred while calling the Kurtosis Cloud API")
}

instanceId := result.GetInstanceId()
logrus.Infof("Success! The Kurtosis Cloud instance is being created with id: %s", instanceId)
logrus.Infof("The Kurtosis cloud instance is currently being created and it should take about 2-3 mins to become ready. "+
"Once ready, load the instance by calling: kurtosis %s %s %s",
command_str_consts.CloudCmdStr,
command_str_consts.CloudLoadCmdStr,
instanceId,
)
return nil
}
2 changes: 2 additions & 0 deletions cli/cli/commands/cloud/cloud.go
Expand Up @@ -2,6 +2,7 @@ package cloud

import (
"github.com/kurtosis-tech/kurtosis/cli/cli/command_str_consts"
"github.com/kurtosis-tech/kurtosis/cli/cli/commands/cloud/add"
"github.com/kurtosis-tech/kurtosis/cli/cli/commands/cloud/load"
"github.com/spf13/cobra"
)
Expand All @@ -16,4 +17,5 @@ var CloudCmd = &cobra.Command{

func init() {
CloudCmd.AddCommand(load.LoadCmd.MustGetCobraCommand())
CloudCmd.AddCommand(add.AddCmd.MustGetCobraCommand())
}
53 changes: 12 additions & 41 deletions cli/cli/commands/cloud/load/load.go
Expand Up @@ -12,13 +12,11 @@ import (
"github.com/kurtosis-tech/kurtosis/cli/cli/command_str_consts"
"github.com/kurtosis-tech/kurtosis/cli/cli/commands/kurtosis_context/add"
"github.com/kurtosis-tech/kurtosis/cli/cli/commands/kurtosis_context/context_switch"
"github.com/kurtosis-tech/kurtosis/cli/cli/kurtosis_config"
"github.com/kurtosis-tech/kurtosis/cli/cli/kurtosis_config/resolved_config"
cloudhelper "github.com/kurtosis-tech/kurtosis/cli/cli/helpers/cloud"
api "github.com/kurtosis-tech/kurtosis/cloud/api/golang/kurtosis_backend_server_rpc_api_bindings"
"github.com/kurtosis-tech/kurtosis/contexts-config-store/store"
"github.com/kurtosis-tech/stacktrace"
"github.com/sirupsen/logrus"
"os"
)

const (
Expand Down Expand Up @@ -49,15 +47,15 @@ func run(ctx context.Context, _ *flags.ParsedFlags, args *args.ParsedArgs) error
}
logrus.Infof("Loading cloud instance %s", instanceID)

apiKey, err := loadApiKey()
apiKey, err := cloudhelper.LoadApiKey()
if err != nil {
return stacktrace.Propagate(err, "Could not load an API Key. Check that it's defined using the "+
"%s env var and it's a valid (active) key", kurtosisCloudApiKeyEnvVarArg)
}

cloudConfig, err := getCloudConfig()
cloudConfig, err := cloudhelper.GetCloudConfig()
if err != nil {
return stacktrace.Propagate(err, "An error occured while loading the Cloud Config")
return stacktrace.Propagate(err, "An error occurred while loading the Cloud Config")
}
// Create the connection
connectionStr := fmt.Sprintf("%s:%d", cloudConfig.ApiUrl, cloudConfig.Port)
Expand All @@ -74,6 +72,14 @@ func run(ctx context.Context, _ *flags.ParsedFlags, args *args.ParsedArgs) error
if err != nil {
return stacktrace.Propagate(err, "An error occurred while calling the Kurtosis Cloud API")
}

// TODO: Create shared enums for instance states:
if result.Status != "running" {
logrus.Warnf("The Kurtosis Cloud instance is in state \"%s\" and cannot currently be loaded."+
" Instance needs to be in state \"running\"", result.Status)
return nil
}

decodedConfigBytes, err := base64.StdEncoding.DecodeString(result.ContextConfig)
if err != nil {
return stacktrace.Propagate(err, "Failed to base64 decode context config")
Expand All @@ -96,38 +102,3 @@ func run(ctx context.Context, _ *flags.ParsedFlags, args *args.ParsedArgs) error
contextIdentifier := parsedContext.GetName()
return context_switch.SwitchContext(ctx, contextIdentifier)
}

func loadApiKey() (*string, error) {
apiKey := os.Getenv(kurtosisCloudApiKeyEnvVarArg)
if len(apiKey) < 1 {
return nil, stacktrace.NewError("No API Key was found. An API Key must be provided as env var %s", kurtosisCloudApiKeyEnvVarArg)
}
logrus.Info("Successfully Loaded API Key...")
return &apiKey, nil
}

func getCloudConfig() (*resolved_config.KurtosisCloudConfig, error) {
// Get the configuration
kurtosisConfigStore := kurtosis_config.GetKurtosisConfigStore()
configProvider := kurtosis_config.NewKurtosisConfigProvider(kurtosisConfigStore)
kurtosisConfig, err := configProvider.GetOrInitializeConfig()
if err != nil {
return nil, stacktrace.Propagate(err, "Failed to get or initialize Kurtosis configuration")
}
if kurtosisConfig.GetCloudConfig() == nil {
return nil, stacktrace.Propagate(err, "No cloud config was found. This is an internal Kurtosis error.")
}
cloudConfig := kurtosisConfig.GetCloudConfig()

if cloudConfig.Port == 0 {
cloudConfig.Port = resolved_config.DefaultCloudConfigPort
}
if len(cloudConfig.ApiUrl) < 1 {
cloudConfig.ApiUrl = resolved_config.DefaultCloudConfigApiUrl
}
if len(cloudConfig.CertificateChain) < 1 {
cloudConfig.CertificateChain = resolved_config.DefaultCertificateChain
}

return cloudConfig, nil
}
48 changes: 48 additions & 0 deletions cli/cli/helpers/cloud/cloud_helpers.go
@@ -0,0 +1,48 @@
package cloud

import (
"github.com/kurtosis-tech/kurtosis/cli/cli/kurtosis_config"
"github.com/kurtosis-tech/kurtosis/cli/cli/kurtosis_config/resolved_config"
"github.com/kurtosis-tech/stacktrace"
"github.com/sirupsen/logrus"
"os"
)

const (
KurtosisCloudApiKeyEnvVarArg = "KURTOSIS_CLOUD_API_KEY"
)

func LoadApiKey() (*string, error) {
apiKey := os.Getenv(KurtosisCloudApiKeyEnvVarArg)
if len(apiKey) < 1 {
return nil, stacktrace.NewError("No API Key was found. An API Key must be provided as env var %s", KurtosisCloudApiKeyEnvVarArg)
}
logrus.Info("Successfully Loaded API Key...")
return &apiKey, nil
}

func GetCloudConfig() (*resolved_config.KurtosisCloudConfig, error) {
// Get the configuration
kurtosisConfigStore := kurtosis_config.GetKurtosisConfigStore()
configProvider := kurtosis_config.NewKurtosisConfigProvider(kurtosisConfigStore)
kurtosisConfig, err := configProvider.GetOrInitializeConfig()
if err != nil {
return nil, stacktrace.Propagate(err, "Failed to get or initialize Kurtosis configuration")
}
if kurtosisConfig.GetCloudConfig() == nil {
return nil, stacktrace.Propagate(err, "No cloud config was found. This is an internal Kurtosis error.")
}
cloudConfig := kurtosisConfig.GetCloudConfig()

if cloudConfig.Port == 0 {
cloudConfig.Port = resolved_config.DefaultCloudConfigPort
}
if len(cloudConfig.ApiUrl) < 1 {
cloudConfig.ApiUrl = resolved_config.DefaultCloudConfigApiUrl
}
if len(cloudConfig.CertificateChain) < 1 {
cloudConfig.CertificateChain = resolved_config.DefaultCertificateChain
}

return cloudConfig, nil
}

0 comments on commit 48aecd0

Please sign in to comment.