Skip to content

Commit

Permalink
feat: Self cloud hosting (#2485)
Browse files Browse the repository at this point in the history
## Description
The Kurtosis CLI supports the local use case (localhost) and the
Kurtosis cloud use case (cloud.kurtosis.com). We need to be able to
build the CLI once and deploy it to multiple environments with different
domain names. Add option to the engine to set a domain name used by the
EM UI. The domain name is set in a static js file read by the EM UI.
More details on this approach discussed here:
https://kurtosistech.slack.com/archives/C032ZB39AHH/p1717770540781579

Adding Nginx in front of the engine + install script will be done as a
cloud-config PR.

## Is this change user facing?
YES
  • Loading branch information
laurentluce authored Jun 12, 2024
1 parent 9ff7b9d commit 343dbbd
Show file tree
Hide file tree
Showing 17 changed files with 110 additions and 21 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ func (cmd *EngineConsumingKurtosisCommand) getSetupFunc() func(context.Context)
kurtosisBackend := engineManager.GetKurtosisBackend()

dontRestartAPIContainers := false
engineClient, closeClientFunc, err := engineManager.StartEngineIdempotentlyWithDefaultVersion(ctx, defaults.DefaultEngineLogLevel, defaults.DefaultEngineEnclavePoolSize, defaults.DefaultGitHubAuthTokenOverride, dontRestartAPIContainers)
engineClient, closeClientFunc, err := engineManager.StartEngineIdempotentlyWithDefaultVersion(ctx, defaults.DefaultEngineLogLevel, defaults.DefaultEngineEnclavePoolSize, defaults.DefaultGitHubAuthTokenOverride, dontRestartAPIContainers, defaults.DefaultDomain)
if err != nil {
return nil, stacktrace.Propagate(err, "An error occurred creating a new Kurtosis engine client")
}
Expand Down
2 changes: 1 addition & 1 deletion cli/cli/commands/cluster/set/set.go
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ func run(ctx context.Context, flags *flags.ParsedFlags, args *args.ParsedArgs) e
// TODO - fix the idempotent starter longer term
if engineStatus == engine_manager.EngineStatus_Stopped {
dontRestartAPIContainers := false
_, engineClientCloseFunc, err := engineManagerNewCluster.StartEngineIdempotentlyWithDefaultVersion(ctx, defaults.DefaultEngineLogLevel, defaults.DefaultEngineEnclavePoolSize, defaults.DefaultGitHubAuthTokenOverride, dontRestartAPIContainers)
_, engineClientCloseFunc, err := engineManagerNewCluster.StartEngineIdempotentlyWithDefaultVersion(ctx, defaults.DefaultEngineLogLevel, defaults.DefaultEngineEnclavePoolSize, defaults.DefaultGitHubAuthTokenOverride, dontRestartAPIContainers, defaults.DefaultDomain)
if err != nil {
return stacktrace.Propagate(err, "Engine could not be started after cluster was updated. Its status can be retrieved "+
"running 'kurtosis %s %s' and it can potentially be started running 'kurtosis %s %s'",
Expand Down
3 changes: 2 additions & 1 deletion cli/cli/commands/enclave/add/add.go
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,8 @@ func run(
if err != nil {
return stacktrace.Propagate(err, "An error occurred creating an engine manager.")
}
engineClient, closeClientFunc, err := engineManager.StartEngineIdempotentlyWithDefaultVersion(ctx, defaults.DefaultEngineLogLevel, defaults.DefaultEngineEnclavePoolSize, defaults.DefaultGitHubAuthTokenOverride, dontRestartAPIContainers)

engineClient, closeClientFunc, err := engineManager.StartEngineIdempotentlyWithDefaultVersion(ctx, defaults.DefaultEngineLogLevel, defaults.DefaultEngineEnclavePoolSize, defaults.DefaultGitHubAuthTokenOverride, dontRestartAPIContainers, defaults.DefaultDomain)
if err != nil {
return stacktrace.Propagate(err, "An error occurred creating a new Kurtosis engine client")
}
Expand Down
22 changes: 19 additions & 3 deletions cli/cli/commands/engine/restart/restart.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ package restart
import (
"context"
"fmt"
"strconv"
"strings"

"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"
Expand All @@ -14,8 +17,6 @@ import (
"github.com/kurtosis-tech/kurtosis/kurtosis_version"
"github.com/kurtosis-tech/stacktrace"
"github.com/sirupsen/logrus"
"strconv"
"strings"
)

const (
Expand All @@ -29,6 +30,9 @@ const (

defaultShouldRestartAPIContainers = "false"
restartAPIContainersFlagKey = "restart-api-containers"

domainFlagKey = "domain"
defaultDomain = ""
)

var RestartCmd = &lowlevel.LowlevelKurtosisCommand{
Expand Down Expand Up @@ -81,6 +85,13 @@ var RestartCmd = &lowlevel.LowlevelKurtosisCommand{
Type: flags.FlagType_Bool,
Default: defaultShouldRestartAPIContainers,
},
{
Key: domainFlagKey,
Usage: "The domain name of the enclave manager UI if self-hosting (blank defaults to localhost for the local use case and cloud.kurtosis.com for the Kurtosis cloud use case)",
Shorthand: "",
Type: flags.FlagType_String,
Default: defaultDomain,
},
},
PreValidationAndRunFunc: nil,
RunFunc: run,
Expand Down Expand Up @@ -142,9 +153,14 @@ func run(_ context.Context, flags *flags.ParsedFlags, _ *args.ParsedArgs) error
return stacktrace.Propagate(err, "Expected a value for the '%v' flag but failed to get it", restartAPIContainersFlagKey)
}

domain, err := flags.GetString(domainFlagKey)
if err != nil {
return stacktrace.Propagate(err, "An error occurred while getting the Kurtosis engine enclave manager UI domain name using the flag with key '%v'; this is a bug in Kurtosis", domainFlagKey)
}

var engineClientCloseFunc func() error
var restartEngineErr error
_, engineClientCloseFunc, restartEngineErr = engineManager.RestartEngineIdempotently(ctx, logLevel, engineVersion, restartEngineOnSameVersionIfAnyRunning, enclavePoolSize, shouldStartInDebugMode, githubAuthTokenOverride, shouldRestartAPIContainers)
_, engineClientCloseFunc, restartEngineErr = engineManager.RestartEngineIdempotently(ctx, logLevel, engineVersion, restartEngineOnSameVersionIfAnyRunning, enclavePoolSize, shouldStartInDebugMode, githubAuthTokenOverride, shouldRestartAPIContainers, domain)
if restartEngineErr != nil {
return stacktrace.Propagate(restartEngineErr, "An error occurred restarting the Kurtosis engine")
}
Expand Down
26 changes: 21 additions & 5 deletions cli/cli/commands/engine/start/start.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ package start
import (
"context"
"fmt"
"strconv"
"strings"

"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"
Expand All @@ -14,8 +17,6 @@ import (
"github.com/kurtosis-tech/kurtosis/kurtosis_version"
"github.com/kurtosis-tech/stacktrace"
"github.com/sirupsen/logrus"
"strconv"
"strings"
)

const (
Expand All @@ -30,6 +31,9 @@ const (

defaultShouldRestartAPIContainers = "false"
restartAPIContainersFlagKey = "restart-api-containers"

domainFlagKey = "domain"
defaultDomain = ""
)

var StartCmd = &lowlevel.LowlevelKurtosisCommand{
Expand Down Expand Up @@ -82,6 +86,13 @@ var StartCmd = &lowlevel.LowlevelKurtosisCommand{
Type: flags.FlagType_Bool,
Default: defaultShouldRestartAPIContainers,
},
{
Key: domainFlagKey,
Usage: "The domain name of the enclave manager UI if self-hosting (blank defaults to localhost for the local use case and cloud.kurtosis.com for the Kurtosis cloud use case)",
Shorthand: "",
Type: flags.FlagType_String,
Default: defaultDomain,
},
},
PreValidationAndRunFunc: nil,
RunFunc: run,
Expand Down Expand Up @@ -138,16 +149,21 @@ func run(_ context.Context, flags *flags.ParsedFlags, _ *args.ParsedArgs) error
return stacktrace.Propagate(err, "Expected a value for the '%v' flag but failed to get it", restartAPIContainersFlagKey)
}

domain, err := flags.GetString(domainFlagKey)
if err != nil {
return stacktrace.Propagate(err, "An error occurred while getting the Kurtosis engine enclave manager UI domain name using the flag with key '%v'; this is a bug in Kurtosis", domainFlagKey)
}

if engineVersion == defaultEngineVersion && isDebugMode {
engineDebugVersion := fmt.Sprintf("%s-%s", kurtosis_version.KurtosisVersion, defaults.DefaultKurtosisContainerDebugImageNameSuffix)
logrus.Infof("Starting Kurtosis engine in debug mode from image '%v%v%v'...", kurtosisTechEngineImagePrefix, imageVersionDelimiter, engineDebugVersion)
_, engineClientCloseFunc, startEngineErr = engineManager.StartEngineIdempotentlyWithCustomVersion(ctx, engineDebugVersion, logLevel, enclavePoolSize, true, githubAuthTokenOverride, shouldRestartAPIContainers)
_, engineClientCloseFunc, startEngineErr = engineManager.StartEngineIdempotentlyWithCustomVersion(ctx, engineDebugVersion, logLevel, enclavePoolSize, true, githubAuthTokenOverride, shouldRestartAPIContainers, domain)
} else if engineVersion == defaultEngineVersion {
logrus.Infof("Starting Kurtosis engine from image '%v%v%v'...", kurtosisTechEngineImagePrefix, imageVersionDelimiter, kurtosis_version.KurtosisVersion)
_, engineClientCloseFunc, startEngineErr = engineManager.StartEngineIdempotentlyWithDefaultVersion(ctx, logLevel, enclavePoolSize, githubAuthTokenOverride, shouldRestartAPIContainers)
_, engineClientCloseFunc, startEngineErr = engineManager.StartEngineIdempotentlyWithDefaultVersion(ctx, logLevel, enclavePoolSize, githubAuthTokenOverride, shouldRestartAPIContainers, domain)
} else {
logrus.Infof("Starting Kurtosis engine from image '%v%v%v'...", kurtosisTechEngineImagePrefix, imageVersionDelimiter, engineVersion)
_, engineClientCloseFunc, startEngineErr = engineManager.StartEngineIdempotentlyWithCustomVersion(ctx, engineVersion, logLevel, enclavePoolSize, defaults.DefaultEnableDebugMode, githubAuthTokenOverride, shouldRestartAPIContainers)
_, engineClientCloseFunc, startEngineErr = engineManager.StartEngineIdempotentlyWithCustomVersion(ctx, engineVersion, logLevel, enclavePoolSize, defaults.DefaultEnableDebugMode, githubAuthTokenOverride, shouldRestartAPIContainers, domain)
}
if startEngineErr != nil {
return stacktrace.Propagate(startEngineErr, "An error occurred starting the Kurtosis engine")
Expand Down
2 changes: 1 addition & 1 deletion cli/cli/commands/github/login/login.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ func RestartEngineAfterGitHubAuth(ctx context.Context) error {
var engineClientCloseFunc func() error
var restartEngineErr error
dontRestartAPIContainers := false
_, engineClientCloseFunc, restartEngineErr = engineManager.RestartEngineIdempotently(ctx, defaults.DefaultEngineLogLevel, defaultEngineVersion, restartEngineOnSameVersionIfAnyRunning, defaults.DefaultEngineEnclavePoolSize, defaults.DefaultEnableDebugMode, defaults.DefaultGitHubAuthTokenOverride, dontRestartAPIContainers)
_, engineClientCloseFunc, restartEngineErr = engineManager.RestartEngineIdempotently(ctx, defaults.DefaultEngineLogLevel, defaultEngineVersion, restartEngineOnSameVersionIfAnyRunning, defaults.DefaultEngineEnclavePoolSize, defaults.DefaultEnableDebugMode, defaults.DefaultGitHubAuthTokenOverride, dontRestartAPIContainers, defaults.DefaultDomain)
if restartEngineErr != nil {
return stacktrace.Propagate(restartEngineErr, "An error occurred restarting the Kurtosis engine")
}
Expand Down
2 changes: 1 addition & 1 deletion cli/cli/commands/kurtosis_context/set/set.go
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ func SetContext(
}

dontRestartAPIContainers := false
_, engineClientCloseFunc, startEngineErr := engineManager.StartEngineIdempotentlyWithDefaultVersion(ctx, logrus.InfoLevel, defaults.DefaultEngineEnclavePoolSize, defaults.DefaultGitHubAuthTokenOverride, dontRestartAPIContainers)
_, engineClientCloseFunc, startEngineErr := engineManager.StartEngineIdempotentlyWithDefaultVersion(ctx, logrus.InfoLevel, defaults.DefaultEngineEnclavePoolSize, defaults.DefaultGitHubAuthTokenOverride, dontRestartAPIContainers, defaults.DefaultDomain)
if startEngineErr != nil {
logrus.Warnf("The context was successfully set to '%s' but Kurtosis failed to start an engine in "+
"this new context. A new engine should be started manually with '%s %s %s'. The error was:\n%v",
Expand Down
2 changes: 2 additions & 0 deletions cli/cli/defaults/defaults.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ const (
DefaultKurtosisContainerDebugImageNameSuffix = "debug"

DefaultGitHubAuthTokenOverride = ""

DefaultDomain = ""
)

var DefaultApiContainerLogLevel = logrus.DebugLevel
Expand Down
10 changes: 10 additions & 0 deletions cli/cli/helpers/engine_manager/engine_existence_guarantor.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package engine_manager
import (
"context"
"fmt"

"github.com/kurtosis-tech/kurtosis/cli/cli/helpers/github_auth_store"

"github.com/Masterminds/semver/v3"
Expand Down Expand Up @@ -84,6 +85,9 @@ type engineExistenceGuarantor struct {

// To restart the current API containers after the engine has been restarted
restartAPIContainers bool

// Enclave manager UI domain name
domain string
}

func newEngineExistenceGuarantorWithDefaultVersion(
Expand All @@ -102,6 +106,7 @@ func newEngineExistenceGuarantorWithDefaultVersion(
shouldRunInDebugMode bool,
githubAuthTokenOverride string,
restartAPIContainers bool,
domain string,
) *engineExistenceGuarantor {
return newEngineExistenceGuarantorWithCustomVersion(
ctx,
Expand All @@ -120,6 +125,7 @@ func newEngineExistenceGuarantorWithDefaultVersion(
shouldRunInDebugMode,
githubAuthTokenOverride,
restartAPIContainers,
domain,
)
}

Expand All @@ -140,6 +146,7 @@ func newEngineExistenceGuarantorWithCustomVersion(
shouldRunInDebugMode bool,
githubAuthTokenOverride string,
restartAPIContainers bool,
domain string,
) *engineExistenceGuarantor {
return &engineExistenceGuarantor{
ctx: ctx,
Expand All @@ -160,6 +167,7 @@ func newEngineExistenceGuarantorWithCustomVersion(
shouldRunInDebugMode: shouldRunInDebugMode,
githubAuthTokenOverride: githubAuthTokenOverride,
restartAPIContainers: restartAPIContainers,
domain: domain,
}
}

Expand Down Expand Up @@ -219,6 +227,7 @@ func (guarantor *engineExistenceGuarantor) VisitStopped() error {
guarantor.shouldRunInDebugMode,
githubAuthToken,
guarantor.restartAPIContainers,
guarantor.domain,
)
} else {
_, _, engineLaunchErr = guarantor.engineServerLauncher.LaunchWithCustomVersion(
Expand All @@ -239,6 +248,7 @@ func (guarantor *engineExistenceGuarantor) VisitStopped() error {
guarantor.shouldRunInDebugMode,
githubAuthToken,
guarantor.restartAPIContainers,
guarantor.domain,
)
}
if engineLaunchErr != nil {
Expand Down
9 changes: 7 additions & 2 deletions cli/cli/helpers/engine_manager/engine_manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,7 @@ func (manager *EngineManager) StartEngineIdempotentlyWithDefaultVersion(
poolSize uint8,
githubAuthTokenOverride string,
restartAPIContainers bool,
domain string,
) (kurtosis_engine_rpc_api_bindings.EngineServiceClient, func() error, error) {
status, maybeHostMachinePortBinding, engineVersion, err := manager.GetEngineStatus(ctx)
if err != nil {
Expand All @@ -205,6 +206,7 @@ func (manager *EngineManager) StartEngineIdempotentlyWithDefaultVersion(
doNotStartTheEngineInDebugModeForDefaultVersion,
githubAuthTokenOverride,
restartAPIContainers,
domain,
)
// TODO Need to handle the Kubernetes case, where a gateway needs to be started after the engine is started but
// before we can return an EngineClient
Expand All @@ -224,6 +226,7 @@ func (manager *EngineManager) StartEngineIdempotentlyWithCustomVersion(
shouldStartInDebugMode bool,
githubAuthTokenOverride string,
restartAPIContainers bool,
domain string,
) (kurtosis_engine_rpc_api_bindings.EngineServiceClient, func() error, error) {
status, maybeHostMachinePortBinding, engineVersion, err := manager.GetEngineStatus(ctx)
if err != nil {
Expand All @@ -248,6 +251,7 @@ func (manager *EngineManager) StartEngineIdempotentlyWithCustomVersion(
shouldStartInDebugMode,
githubAuthTokenOverride,
restartAPIContainers,
domain,
)
engineClient, engineClientCloseFunc, err := manager.startEngineWithGuarantor(ctx, status, engineGuarantor)
if err != nil {
Expand Down Expand Up @@ -348,6 +352,7 @@ func (manager *EngineManager) RestartEngineIdempotently(
shouldStartInDebugMode bool,
githubAuthTokenOverride string,
shouldRestartAPIContainers bool,
domain string,
) (kurtosis_engine_rpc_api_bindings.EngineServiceClient, func() error, error) {
var versionOfNewEngine string
// We try to do our best to restart an engine on the same version the current on is on
Expand All @@ -373,9 +378,9 @@ func (manager *EngineManager) RestartEngineIdempotently(
var engineClientCloseFunc func() error
var restartEngineErr error
if versionOfNewEngine != defaultEngineVersion {
_, engineClientCloseFunc, restartEngineErr = manager.StartEngineIdempotentlyWithCustomVersion(ctx, versionOfNewEngine, logLevel, poolSize, shouldStartInDebugMode, githubAuthTokenOverride, shouldRestartAPIContainers)
_, engineClientCloseFunc, restartEngineErr = manager.StartEngineIdempotentlyWithCustomVersion(ctx, versionOfNewEngine, logLevel, poolSize, shouldStartInDebugMode, githubAuthTokenOverride, shouldRestartAPIContainers, domain)
} else {
_, engineClientCloseFunc, restartEngineErr = manager.StartEngineIdempotentlyWithDefaultVersion(ctx, logLevel, poolSize, githubAuthTokenOverride, shouldRestartAPIContainers)
_, engineClientCloseFunc, restartEngineErr = manager.StartEngineIdempotentlyWithDefaultVersion(ctx, logLevel, poolSize, githubAuthTokenOverride, shouldRestartAPIContainers, domain)
}
if restartEngineErr != nil {
return nil, nil, stacktrace.Propagate(restartEngineErr, "An error occurred starting a new engine")
Expand Down
1 change: 1 addition & 0 deletions enclave-manager/web/packages/app/public/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<meta name="description" content="Kurtosis Enclave Manager" />
<script src="./env.js" type="text/javascript"></script>
<!--
Notice the use of %PUBLIC_URL% in the tags above.
It will be replaced with the URL of the `public` folder during the build.
Expand Down
8 changes: 8 additions & 0 deletions enclave-manager/web/packages/app/src/client/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,11 @@ export const KURTOSIS_DEFAULT_EM_API_PORT = isDefined(process.env.REACT_APP_KURT
: 8081;
export const KURTOSIS_EM_API_DEFAULT_URL =
process.env.REACT_APP_KURTOSIS_DEFAULT_URL || `http://${KURTOSIS_EM_DEFAULT_HOST}:${KURTOSIS_DEFAULT_EM_API_PORT}`;

declare global {
interface Window {
env: {
domain: string;
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,12 @@ import { KurtosisClient } from "./KurtosisClient";
export class LocalKurtosisClient extends KurtosisClient {
constructor() {
const defaultUrl = new URL(`${window.location.protocol}//${window.location.host}`);
var baseUrl = KURTOSIS_EM_API_DEFAULT_URL;
if (window.env !== undefined && window.env.domain !== undefined) {
baseUrl = "https://" + window.env.domain;
}
super(
createPromiseClient(
KurtosisEnclaveManagerServer,
createConnectTransport({ baseUrl: KURTOSIS_EM_API_DEFAULT_URL }),
),
createPromiseClient(KurtosisEnclaveManagerServer, createConnectTransport({ baseUrl: baseUrl })),
defaultUrl,
defaultUrl,
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ export const getPortTableRows = (
`${port.number}-${shortUUID(serviceUUID)}-${shortUUID(enclaveUUID)}-${shortUUID(instanceUUID)}` +
`.${KURTOSIS_CLOUD_HOST}`;
}
} else if (window.env !== undefined && window.env.domain !== undefined) {
link = `https://${port.number}-${shortUUID(serviceUUID)}-${shortUUID(enclaveUUID)}.${window.env.domain}`;
} else {
link = `${port.maybeApplicationProtocol ? port.maybeApplicationProtocol + "://" : ""}${publicIp}:${
publicPorts[name].number
Expand Down
6 changes: 6 additions & 0 deletions engine/launcher/args/args.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,11 +64,15 @@ type EngineServerArgs struct {

// To restart the current API containers after the engine has been restarted
RestartAPIContainers bool `json:"restart_api_containers"`

// Enclave manager UI domain name
Domain string `json:"domain"`
}

var skipValidation = map[string]bool{
"cloud_instance_id": true,
"cloud_user_id": true,
"domain": true,
}

func (args *EngineServerArgs) UnmarshalJSON(data []byte) error {
Expand Down Expand Up @@ -120,6 +124,7 @@ func NewEngineServerArgs(
cloudInstanceID metrics_client.CloudInstanceID,
allowedCORSOrigins *[]string,
restartAPIContainers bool,
domain string,
) (*EngineServerArgs, error) {
if enclaveEnvVars == "" {
enclaveEnvVars = emptyJsonField
Expand All @@ -140,6 +145,7 @@ func NewEngineServerArgs(
CloudInstanceID: cloudInstanceID,
AllowedCORSOrigins: allowedCORSOrigins,
RestartAPIContainers: restartAPIContainers,
Domain: domain,
}
if err := result.validate(); err != nil {
return nil, stacktrace.Propagate(err, "An error occurred validating engine server args")
Expand Down
Loading

0 comments on commit 343dbbd

Please sign in to comment.