diff --git a/cli/cli/commands/cloud/load/load.go b/cli/cli/commands/cloud/load/load.go index 1acfcd7d04..de35378ee8 100644 --- a/cli/cli/commands/cloud/load/load.go +++ b/cli/cli/commands/cloud/load/load.go @@ -95,9 +95,18 @@ func run(ctx context.Context, _ *flags.ParsedFlags, args *args.ParsedArgs) error if err != nil { return stacktrace.Propagate(err, "While attempting to reload the context with uuid %s an error occurred while removing it from the context store", parsedContext.Uuid) } - if add.AddContext(parsedContext) != nil { + if add.AddContext(parsedContext, assembleEnvVars(result)) != nil { return stacktrace.Propagate(err, "Unable to add context to context store") } contextIdentifier := parsedContext.GetName() return context_switch.SwitchContext(ctx, contextIdentifier) } + +func assembleEnvVars(cloudInstanceConfig *api.GetCloudInstanceConfigResponse) *string { + if cloudInstanceConfig.GetUserKey() == nil { + return nil + } + envVars := fmt.Sprintf(`{"AWS_ACCESS_KEY_ID": %q, "AWS_SECRET_ACCESS_KEY": %q, "USER_ID": %q}`, + cloudInstanceConfig.GetUserKey().GetId(), cloudInstanceConfig.GetUserKey().GetSecret(), cloudInstanceConfig.UserId) + return &envVars +} diff --git a/cli/cli/commands/kurtosis_context/add/add.go b/cli/cli/commands/kurtosis_context/add/add.go index a59ab51ebb..1636ccf47c 100644 --- a/cli/cli/commands/kurtosis_context/add/add.go +++ b/cli/cli/commands/kurtosis_context/add/add.go @@ -7,6 +7,7 @@ import ( "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" + "github.com/kurtosis-tech/kurtosis/contexts-config-store/api/golang" "github.com/kurtosis-tech/kurtosis/contexts-config-store/api/golang/generated" "github.com/kurtosis-tech/kurtosis/contexts-config-store/store" "github.com/kurtosis-tech/stacktrace" @@ -51,15 +52,30 @@ func run(_ context.Context, _ *flags.ParsedFlags, args *args.ParsedArgs) error { if err != nil { return stacktrace.Propagate(err, "Unable to read content of context file at '%s'", contextFilePath) } - return AddContext(newContextToAdd) + return AddContext(newContextToAdd, nil) } -func AddContext(newContextToAdd *generated.KurtosisContext) error { +func AddContext(newContextToAdd *generated.KurtosisContext, envVars *string) error { logrus.Infof("Adding new context '%s'", newContextToAdd.GetName()) contextsConfigStore := store.GetContextsConfigStore() - if err := contextsConfigStore.AddNewContext(newContextToAdd); err != nil { + var enrichedContextData *generated.KurtosisContext + if envVars != nil && *envVars != "" { + enrichedContextData = golang.NewRemoteV0Context( + newContextToAdd.GetUuid(), + newContextToAdd.GetName(), + newContextToAdd.GetRemoteContextV0().GetHost(), + newContextToAdd.GetRemoteContextV0().GetRemotePortalPort(), + newContextToAdd.GetRemoteContextV0().GetKurtosisBackendPort(), + newContextToAdd.GetRemoteContextV0().GetTunnelPort(), + newContextToAdd.GetRemoteContextV0().GetTlsConfig(), + envVars, + ) + } else { + enrichedContextData = newContextToAdd + } + if err := contextsConfigStore.AddNewContext(enrichedContextData); err != nil { return stacktrace.Propagate(err, "New context '%s' with UUID '%s' could not be added to the list of "+ - "contexts already configured", newContextToAdd.GetName(), newContextToAdd.GetUuid().GetValue()) + "contexts already configured", enrichedContextData.GetName(), enrichedContextData.GetUuid().GetValue()) } logrus.Info("Context successfully added") return nil diff --git a/cli/cli/helpers/engine_manager/engine_existence_guarantor.go b/cli/cli/helpers/engine_manager/engine_existence_guarantor.go index 7e14bcb605..c6f46310c7 100644 --- a/cli/cli/helpers/engine_manager/engine_existence_guarantor.go +++ b/cli/cli/helpers/engine_manager/engine_existence_guarantor.go @@ -67,6 +67,8 @@ type engineExistenceGuarantor struct { onBastionHost bool poolSize uint8 + + enclaveEnvVars string } func newEngineExistenceGuarantorWithDefaultVersion( @@ -80,6 +82,7 @@ func newEngineExistenceGuarantorWithDefaultVersion( kurtosisClusterType resolved_config.KurtosisClusterType, onBastionHost bool, poolSize uint8, + enclaveEnvVars string, ) *engineExistenceGuarantor { return newEngineExistenceGuarantorWithCustomVersion( ctx, @@ -93,6 +96,7 @@ func newEngineExistenceGuarantorWithDefaultVersion( kurtosisClusterType, onBastionHost, poolSize, + enclaveEnvVars, ) } @@ -108,6 +112,7 @@ func newEngineExistenceGuarantorWithCustomVersion( kurtosisClusterType resolved_config.KurtosisClusterType, onBastionHost bool, poolSize uint8, + enclaveEnvVars string, ) *engineExistenceGuarantor { return &engineExistenceGuarantor{ ctx: ctx, @@ -123,6 +128,7 @@ func newEngineExistenceGuarantorWithCustomVersion( kurtosisClusterType: kurtosisClusterType, onBastionHost: onBastionHost, poolSize: poolSize, + enclaveEnvVars: enclaveEnvVars, } } @@ -151,6 +157,7 @@ func (guarantor *engineExistenceGuarantor) VisitStopped() error { guarantor.engineServerKurtosisBackendConfigSupplier, guarantor.onBastionHost, guarantor.poolSize, + guarantor.enclaveEnvVars, ) } else { _, _, engineLaunchErr = guarantor.engineServerLauncher.LaunchWithCustomVersion( @@ -163,6 +170,7 @@ func (guarantor *engineExistenceGuarantor) VisitStopped() error { guarantor.engineServerKurtosisBackendConfigSupplier, guarantor.onBastionHost, guarantor.poolSize, + guarantor.enclaveEnvVars, ) } if engineLaunchErr != nil { diff --git a/cli/cli/helpers/engine_manager/engine_manager.go b/cli/cli/helpers/engine_manager/engine_manager.go index 3294e38ea2..e720401a36 100644 --- a/cli/cli/helpers/engine_manager/engine_manager.go +++ b/cli/cli/helpers/engine_manager/engine_manager.go @@ -41,6 +41,7 @@ type EngineManager struct { engineServerKurtosisBackendConfigSupplier engine_server_launcher.KurtosisBackendConfigSupplier clusterConfig *resolved_config.KurtosisClusterConfig onBastionHost bool + enclaveEnvVars string // Make engine IP, port, and protocol configurable in the future } @@ -84,10 +85,12 @@ func NewEngineManager(ctx context.Context) (*EngineManager, error) { engineBackendConfigSupplier := clusterConfig.GetEngineBackendConfigSupplier() onBastionHost := false + var enclaveEnvVars string currentContext, _ := store.GetContextsConfigStore().GetCurrentContext() if currentContext != nil { if store.IsRemote(currentContext) { onBastionHost = true + enclaveEnvVars = currentContext.GetRemoteContextV0().GetEnvVars() } } @@ -95,8 +98,9 @@ func NewEngineManager(ctx context.Context) (*EngineManager, error) { kurtosisBackend: kurtosisBackend, shouldSendMetrics: kurtosisConfig.GetShouldSendMetrics(), engineServerKurtosisBackendConfigSupplier: engineBackendConfigSupplier, - clusterConfig: clusterConfig, - onBastionHost: onBastionHost, + clusterConfig: clusterConfig, + onBastionHost: onBastionHost, + enclaveEnvVars: enclaveEnvVars, }, nil } @@ -186,6 +190,7 @@ func (manager *EngineManager) StartEngineIdempotentlyWithDefaultVersion(ctx cont clusterType, manager.onBastionHost, poolSize, + manager.enclaveEnvVars, ) // 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 @@ -216,6 +221,7 @@ func (manager *EngineManager) StartEngineIdempotentlyWithCustomVersion(ctx conte clusterType, manager.onBastionHost, poolSize, + manager.enclaveEnvVars, ) engineClient, engineClientCloseFunc, err := manager.startEngineWithGuarantor(ctx, status, engineGuarantor) if err != nil { diff --git a/contexts-config-store/api/golang/contexts_config_constructors.go b/contexts-config-store/api/golang/contexts_config_constructors.go index 2e260f210a..39d3b450ce 100644 --- a/contexts-config-store/api/golang/contexts_config_constructors.go +++ b/contexts-config-store/api/golang/contexts_config_constructors.go @@ -24,3 +24,29 @@ func NewLocalOnlyContext(uuid *generated.ContextUuid, name string) *generated.Ku }, } } + +func NewRemoteV0Context( + uuid *generated.ContextUuid, + name string, + host string, + remotePortalPort uint32, + kurtosisBackendPort uint32, + tunnelPort uint32, + tlsConfig *generated.TlsConfig, + envVars *string, +) *generated.KurtosisContext { + return &generated.KurtosisContext{ + Uuid: uuid, + Name: name, + KurtosisContextInfo: &generated.KurtosisContext_RemoteContextV0{ + RemoteContextV0: &generated.RemoteContextV0{ + Host: host, + RemotePortalPort: remotePortalPort, + KurtosisBackendPort: kurtosisBackendPort, + TunnelPort: tunnelPort, + TlsConfig: tlsConfig, + EnvVars: envVars, + }, + }, + } +} diff --git a/contexts-config-store/api/golang/generated/contexts_config.pb.go b/contexts-config-store/api/golang/generated/contexts_config.pb.go index ed240680cb..86a7455d92 100644 --- a/contexts-config-store/api/golang/generated/contexts_config.pb.go +++ b/contexts-config-store/api/golang/generated/contexts_config.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.28.1 -// protoc v3.21.12 +// protoc-gen-go v1.31.0 +// protoc v4.23.4 // source: contexts_config.proto package generated @@ -85,6 +85,7 @@ type KurtosisContext struct { Uuid *ContextUuid `protobuf:"bytes,1,opt,name=uuid,proto3" json:"uuid,omitempty"` Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` // Types that are assignable to KurtosisContextInfo: + // // *KurtosisContext_LocalOnlyContextV0 // *KurtosisContext_RemoteContextV0 KurtosisContextInfo isKurtosisContext_KurtosisContextInfo `protobuf_oneof:"kurtosis_context_info"` @@ -275,6 +276,7 @@ type RemoteContextV0 struct { TunnelPort uint32 `protobuf:"varint,4,opt,name=tunnel_port,json=tunnelPort,proto3" json:"tunnel_port,omitempty"` // TLS config to use to connect to remote Kurtosis. If absent, HTTP will be used TlsConfig *TlsConfig `protobuf:"bytes,5,opt,name=tls_config,json=tlsConfig,proto3,oneof" json:"tls_config,omitempty"` + EnvVars *string `protobuf:"bytes,6,opt,name=env_vars,json=envVars,proto3,oneof" json:"env_vars,omitempty"` } func (x *RemoteContextV0) Reset() { @@ -344,6 +346,13 @@ func (x *RemoteContextV0) GetTlsConfig() *TlsConfig { return nil } +func (x *RemoteContextV0) GetEnvVars() string { + if x != nil && x.EnvVars != nil { + return *x.EnvVars + } + return "" +} + type TlsConfig struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -449,7 +458,7 @@ var file_contexts_config_proto_rawDesc = []byte{ 0x6e, 0x74, 0x65, 0x78, 0x74, 0x55, 0x75, 0x69, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x14, 0x0a, 0x12, 0x4c, 0x6f, 0x63, 0x61, 0x6c, 0x4f, 0x6e, 0x6c, 0x79, 0x43, 0x6f, 0x6e, 0x74, - 0x65, 0x78, 0x74, 0x56, 0x30, 0x22, 0xfc, 0x01, 0x0a, 0x0f, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, + 0x65, 0x78, 0x74, 0x56, 0x30, 0x22, 0xa9, 0x02, 0x0a, 0x0f, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x56, 0x30, 0x12, 0x12, 0x0a, 0x04, 0x68, 0x6f, 0x73, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x68, 0x6f, 0x73, 0x74, 0x12, 0x2c, 0x0a, 0x12, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x5f, 0x70, 0x6f, 0x72, 0x74, 0x61, 0x6c, 0x5f, 0x70, @@ -464,22 +473,25 @@ var file_contexts_config_proto_rawDesc = []byte{ 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2e, 0x54, 0x6c, 0x73, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x48, 0x00, 0x52, 0x09, 0x74, 0x6c, 0x73, 0x43, 0x6f, 0x6e, 0x66, - 0x69, 0x67, 0x88, 0x01, 0x01, 0x42, 0x0d, 0x0a, 0x0b, 0x5f, 0x74, 0x6c, 0x73, 0x5f, 0x63, 0x6f, - 0x6e, 0x66, 0x69, 0x67, 0x22, 0x8e, 0x01, 0x0a, 0x09, 0x54, 0x6c, 0x73, 0x43, 0x6f, 0x6e, 0x66, - 0x69, 0x67, 0x12, 0x33, 0x0a, 0x15, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, - 0x65, 0x5f, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x0c, 0x52, 0x14, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x41, 0x75, - 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x12, 0x2d, 0x0a, 0x12, 0x63, 0x6c, 0x69, 0x65, 0x6e, - 0x74, 0x5f, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x0c, 0x52, 0x11, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x43, 0x65, 0x72, 0x74, 0x69, - 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, - 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x63, 0x6c, 0x69, 0x65, - 0x6e, 0x74, 0x4b, 0x65, 0x79, 0x42, 0x4e, 0x5a, 0x4c, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, - 0x63, 0x6f, 0x6d, 0x2f, 0x6b, 0x75, 0x72, 0x74, 0x6f, 0x73, 0x69, 0x73, 0x2d, 0x74, 0x65, 0x63, - 0x68, 0x2f, 0x6b, 0x75, 0x72, 0x74, 0x6f, 0x73, 0x69, 0x73, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x65, - 0x78, 0x74, 0x73, 0x2d, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2d, 0x73, 0x74, 0x6f, 0x72, 0x65, - 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x67, 0x6f, 0x6c, 0x61, 0x6e, 0x67, 0x2f, 0x67, 0x65, 0x6e, 0x65, - 0x72, 0x61, 0x74, 0x65, 0x64, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x69, 0x67, 0x88, 0x01, 0x01, 0x12, 0x1e, 0x0a, 0x08, 0x65, 0x6e, 0x76, 0x5f, 0x76, 0x61, 0x72, + 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x48, 0x01, 0x52, 0x07, 0x65, 0x6e, 0x76, 0x56, 0x61, + 0x72, 0x73, 0x88, 0x01, 0x01, 0x42, 0x0d, 0x0a, 0x0b, 0x5f, 0x74, 0x6c, 0x73, 0x5f, 0x63, 0x6f, + 0x6e, 0x66, 0x69, 0x67, 0x42, 0x0b, 0x0a, 0x09, 0x5f, 0x65, 0x6e, 0x76, 0x5f, 0x76, 0x61, 0x72, + 0x73, 0x22, 0x8e, 0x01, 0x0a, 0x09, 0x54, 0x6c, 0x73, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, + 0x33, 0x0a, 0x15, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x5f, 0x61, + 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x14, + 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x41, 0x75, 0x74, 0x68, 0x6f, + 0x72, 0x69, 0x74, 0x79, 0x12, 0x2d, 0x0a, 0x12, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x63, + 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, + 0x52, 0x11, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, + 0x61, 0x74, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x6b, 0x65, + 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x4b, + 0x65, 0x79, 0x42, 0x4e, 0x5a, 0x4c, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, + 0x2f, 0x6b, 0x75, 0x72, 0x74, 0x6f, 0x73, 0x69, 0x73, 0x2d, 0x74, 0x65, 0x63, 0x68, 0x2f, 0x6b, + 0x75, 0x72, 0x74, 0x6f, 0x73, 0x69, 0x73, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x73, + 0x2d, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2d, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2f, 0x61, 0x70, + 0x69, 0x2f, 0x67, 0x6f, 0x6c, 0x61, 0x6e, 0x67, 0x2f, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, + 0x65, 0x64, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/contexts-config-store/api/protobuf/contexts_config.proto b/contexts-config-store/api/protobuf/contexts_config.proto index 7e66e94af8..ff04a39754 100644 --- a/contexts-config-store/api/protobuf/contexts_config.proto +++ b/contexts-config-store/api/protobuf/contexts_config.proto @@ -48,6 +48,8 @@ message RemoteContextV0 { // TLS config to use to connect to remote Kurtosis. If absent, HTTP will be used optional TlsConfig tls_config = 5; + + optional string env_vars = 6; } message TlsConfig { diff --git a/contexts-config-store/store/serde/kurtosis_context_test.go b/contexts-config-store/store/serde/kurtosis_context_test.go index da67e431f3..b49adfd3cc 100644 --- a/contexts-config-store/store/serde/kurtosis_context_test.go +++ b/contexts-config-store/store/serde/kurtosis_context_test.go @@ -37,6 +37,7 @@ var ( ClientCertificate: []byte(fakeCert), ClientKey: []byte(fakeKey), }, + EnvVars: new(string), }, }, } @@ -55,7 +56,8 @@ var ( "certificateAuthority":"ZmFrZS1jYQ==", "clientCertificate":"ZmFrZS1jZXJ0", "clientKey":"ZmFrZS1rZXk=" - } + }, + "envVars": "" } }` ) diff --git a/core/launcher/api_container_launcher/api_container_launcher.go b/core/launcher/api_container_launcher/api_container_launcher.go index bb2375ed85..32ec3073dc 100644 --- a/core/launcher/api_container_launcher/api_container_launcher.go +++ b/core/launcher/api_container_launcher/api_container_launcher.go @@ -37,6 +37,7 @@ func (launcher ApiContainerLauncher) LaunchWithDefaultVersion( enclaveId enclave.EnclaveUUID, grpcListenPort uint16, backendConfigSupplier KurtosisBackendConfigSupplier, + enclaveEnvVars string, ) ( resultApiContainer *api_container.APIContainer, resultErr error, @@ -48,6 +49,7 @@ func (launcher ApiContainerLauncher) LaunchWithDefaultVersion( enclaveId, grpcListenPort, backendConfigSupplier, + enclaveEnvVars, ) if err != nil { return nil, stacktrace.Propagate(err, "An error occurred launching the API container with default version tag '%v'", kurtosis_version.KurtosisVersion) @@ -62,6 +64,7 @@ func (launcher ApiContainerLauncher) LaunchWithCustomVersion( enclaveUuid enclave.EnclaveUUID, grpcPortNum uint16, backendConfigSupplier KurtosisBackendConfigSupplier, + enclaveEnvVars string, ) ( resultApiContainer *api_container.APIContainer, resultErr error, @@ -75,6 +78,7 @@ func (launcher ApiContainerLauncher) LaunchWithCustomVersion( enclaveDataVolumeDirpath, kurtosisBackendType, kurtosisBackendConfig, + enclaveEnvVars, ) if err != nil { return nil, stacktrace.Propagate(err, "An error occurred creating the API container args") diff --git a/core/launcher/args/api_container_args.go b/core/launcher/args/api_container_args.go index 75e1f28aa7..45a7abc417 100644 --- a/core/launcher/args/api_container_args.go +++ b/core/launcher/args/api_container_args.go @@ -34,6 +34,8 @@ type APIContainerArgs struct { // Should be deserialized differently depending on value of KurtosisBackendType KurtosisBackendConfig interface{} `json:"kurtosisBackendConfig"` + + EnclaveEnvVars string `json:"enclaveEnvVars"` } func (args *APIContainerArgs) UnmarshalJSON(data []byte) error { @@ -78,6 +80,7 @@ func NewAPIContainerArgs( enclaveDataVolumeDirpath string, kurtosisBackendType KurtosisBackendType, kurtosisBackendConfig interface{}, + enclaveEnvVars string, ) (*APIContainerArgs, error) { result := &APIContainerArgs{ Version: version, @@ -87,6 +90,7 @@ func NewAPIContainerArgs( EnclaveDataVolumeDirpath: enclaveDataVolumeDirpath, KurtosisBackendType: kurtosisBackendType, KurtosisBackendConfig: kurtosisBackendConfig, + EnclaveEnvVars: enclaveEnvVars, } if err := result.validate(); err != nil { diff --git a/core/server/api_container/main.go b/core/server/api_container/main.go index 6485ee4ad3..64b7432645 100644 --- a/core/server/api_container/main.go +++ b/core/server/api_container/main.go @@ -164,7 +164,7 @@ func runMain() error { runtimeValueStore := runtime_value_store.NewRuntimeValueStore() // TODO: Consolidate Interpreter, Validator and Executor into a single interface startosisRunner := startosis_engine.NewStartosisRunner( - startosis_engine.NewStartosisInterpreter(serviceNetwork, gitPackageContentProvider, runtimeValueStore), + startosis_engine.NewStartosisInterpreter(serviceNetwork, gitPackageContentProvider, runtimeValueStore, serverArgs.EnclaveEnvVars), startosis_engine.NewStartosisValidator(&kurtosisBackend, serviceNetwork, filesArtifactStore), startosis_engine.NewStartosisExecutor(runtimeValueStore)) diff --git a/core/server/api_container/server/startosis_engine/builtins/kurtosis_module.go b/core/server/api_container/server/startosis_engine/builtins/kurtosis_module.go index 4793ced4b4..dd74a84bf0 100644 --- a/core/server/api_container/server/startosis_engine/builtins/kurtosis_module.go +++ b/core/server/api_container/server/startosis_engine/builtins/kurtosis_module.go @@ -2,23 +2,64 @@ package builtins import ( "github.com/kurtosis-tech/kurtosis/core/server/api_container/server/startosis_engine/startosis_errors" + starlarkjson "go.starlark.net/lib/json" "go.starlark.net/starlark" "go.starlark.net/starlarkstruct" ) const ( KurtosisModuleName = "kurtosis" + + EnvironmentVarsKey = "env" +) + +const ( + decoderKey = "decode" +) + +var ( + noKwargs []starlark.Tuple ) // TODO This module was created for storing Kurtosis constatns that then can be used for any Kurtosis module or package // TODO we use to store the contants related with subnetworks (BLOCKED, ALLOWED) here but these were removed since we deprecate the network partitioning feature // TODO it's planned to store some another constants like the port protols here, so we are leaving it here for this reason. -func KurtosisModule() (*starlarkstruct.Module, *startosis_errors.InterpretationError) { +func KurtosisModule(thread *starlark.Thread, enclaveEnvVars string) (*starlarkstruct.Module, *startosis_errors.InterpretationError) { + enclaveEnvVarsDict, interpretationErr := convertEnvVarsToDict(thread, enclaveEnvVars) + if interpretationErr != nil { + return nil, interpretationErr + } return &starlarkstruct.Module{ - Name: KurtosisModuleName, + Name: KurtosisModuleName, Members: starlark.StringDict{ - //Add sub-modules here + EnvironmentVarsKey: enclaveEnvVarsDict, }, }, nil } + +func convertEnvVarsToDict(thread *starlark.Thread, enclaveEnvVars string) (*starlark.Dict, *startosis_errors.InterpretationError) { + if enclaveEnvVars == "" { + return starlark.NewDict(0), nil + } + if !starlarkjson.Module.Members.Has(decoderKey) { + return nil, startosis_errors.NewInterpretationError("Unable to deserialize enclave env vars because Starlark deserializer was not found.") + } + decoder, ok := starlarkjson.Module.Members[decoderKey].(*starlark.Builtin) + if !ok { + return nil, startosis_errors.NewInterpretationError("Unable to deserialize enclave env vars because Starlark deserializer could not be loaded.") + } + + args := []starlark.Value{ + starlark.String(enclaveEnvVars), + } + deserializedInputValue, err := decoder.CallInternal(thread, args, noKwargs) + if err != nil { + return nil, startosis_errors.WrapWithInterpretationError(err, "Unable to deserialize enclave env vars '%v'. Is it a valid JSON?", enclaveEnvVars) + } + parsedDeserializedInputValue, ok := deserializedInputValue.(*starlark.Dict) + if !ok { + return nil, startosis_errors.NewInterpretationError("Unable to parse enclave env vars '%v' into a dictionary. JSON other than dictionaries aren't support right now.", deserializedInputValue) + } + return parsedDeserializedInputValue, nil +} diff --git a/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/test_engine/starlark_framework_engine_test.go b/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/test_engine/starlark_framework_engine_test.go index 117234f8f6..25e27c2894 100644 --- a/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/test_engine/starlark_framework_engine_test.go +++ b/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/test_engine/starlark_framework_engine_test.go @@ -71,7 +71,7 @@ func testKurtosisPlanInstruction(t *testing.T, builtin KurtosisPlanInstructionBa instructionsPlan := instructions_plan.NewInstructionsPlan() thread := newStarlarkThread(frameworkTestThreadName) - predeclared := getBasePredeclaredDict(t) + predeclared := getBasePredeclaredDict(t, thread) // Add the KurtosisPlanInstruction that is being tested instructionFromBuiltin := builtin.GetInstruction() emptyEnclaveComponents := enclave_structure.NewEnclaveComponents() @@ -109,7 +109,7 @@ func testKurtosisHelper(t *testing.T, builtin KurtosisHelperBaseTest) { testId := builtin.GetId() thread := newStarlarkThread("framework-testing-engine") - predeclared := getBasePredeclaredDict(t) + predeclared := getBasePredeclaredDict(t, thread) // Add the KurtosisPlanInstruction that is being tested helper := builtin.GetHelper() predeclared[helper.GetName()] = starlark.NewBuiltin(helper.GetName(), helper.CreateBuiltin()) @@ -126,7 +126,7 @@ func testKurtosisTypeConstructor(t *testing.T, builtin KurtosisTypeConstructorBa testId := builtin.GetId() thread := newStarlarkThread("framework-testing-engine") - predeclared := getBasePredeclaredDict(t) + predeclared := getBasePredeclaredDict(t, thread) starlarkCode := builtin.GetStarlarkCode() starlarkCodeToExecute := codeToExecute(starlarkCode) @@ -149,8 +149,8 @@ func testKurtosisTypeConstructor(t *testing.T, builtin KurtosisTypeConstructorBa require.Equal(t, starlarkCode, serializedType) } -func getBasePredeclaredDict(t *testing.T) starlark.StringDict { - kurtosisModule, err := builtins.KurtosisModule() +func getBasePredeclaredDict(t *testing.T, thread *starlark.Thread) starlark.StringDict { + kurtosisModule, err := builtins.KurtosisModule(thread, "") require.Nil(t, err) // TODO: refactor this with the one we have in the interpreter predeclared := starlark.StringDict{ diff --git a/core/server/api_container/server/startosis_engine/startosis_interpreter.go b/core/server/api_container/server/startosis_engine/startosis_interpreter.go index 3f16531a57..3cc3b5feee 100644 --- a/core/server/api_container/server/startosis_engine/startosis_interpreter.go +++ b/core/server/api_container/server/startosis_engine/startosis_interpreter.go @@ -59,17 +59,19 @@ type StartosisInterpreter struct { moduleGlobalsCache map[string]*startosis_packages.ModuleCacheEntry // TODO AUTH there will be a leak here in case people with different repo visibility access a module moduleContentProvider startosis_packages.PackageContentProvider + enclaveEnvVars string } type SerializedInterpretationOutput string -func NewStartosisInterpreter(serviceNetwork service_network.ServiceNetwork, moduleContentProvider startosis_packages.PackageContentProvider, runtimeValueStore *runtime_value_store.RuntimeValueStore) *StartosisInterpreter { +func NewStartosisInterpreter(serviceNetwork service_network.ServiceNetwork, moduleContentProvider startosis_packages.PackageContentProvider, runtimeValueStore *runtime_value_store.RuntimeValueStore, enclaveVarEnvs string) *StartosisInterpreter { return &StartosisInterpreter{ mutex: &sync.Mutex{}, serviceNetwork: serviceNetwork, recipeExecutor: runtimeValueStore, moduleGlobalsCache: make(map[string]*startosis_packages.ModuleCacheEntry), moduleContentProvider: moduleContentProvider, + enclaveEnvVars: enclaveVarEnvs, } } @@ -318,7 +320,7 @@ func (interpreter *StartosisInterpreter) interpretInternal(moduleLocator string, // previous calls inside the same thread // The thread name is set to the locator of the module so that we can use it to resolve relative paths thread := newStarlarkThread(moduleLocator) - predeclared, interpretationErr := interpreter.buildBindings(instructionPlan) + predeclared, interpretationErr := interpreter.buildBindings(thread, instructionPlan) if interpretationErr != nil { return nil, interpretationErr } @@ -331,7 +333,7 @@ func (interpreter *StartosisInterpreter) interpretInternal(moduleLocator string, return globalVariables, nil } -func (interpreter *StartosisInterpreter) buildBindings(instructionPlan *instructions_plan.InstructionsPlan) (*starlark.StringDict, *startosis_errors.InterpretationError) { +func (interpreter *StartosisInterpreter) buildBindings(thread *starlark.Thread, instructionPlan *instructions_plan.InstructionsPlan) (*starlark.StringDict, *startosis_errors.InterpretationError) { recursiveInterpretForModuleLoading := func(moduleId string, serializedStartosis string) (starlark.StringDict, *startosis_errors.InterpretationError) { result, err := interpreter.interpretInternal(moduleId, serializedStartosis, instructionPlan) if err != nil { @@ -340,7 +342,7 @@ func (interpreter *StartosisInterpreter) buildBindings(instructionPlan *instruct return result, nil } - kurtosisModule, interpretationErr := builtins.KurtosisModule() + kurtosisModule, interpretationErr := builtins.KurtosisModule(thread, interpreter.enclaveEnvVars) if interpretationErr != nil { return nil, interpretationErr } diff --git a/core/server/api_container/server/startosis_engine/startosis_interpreter_idempotent_test.go b/core/server/api_container/server/startosis_engine/startosis_interpreter_idempotent_test.go index 6d7f69ebb5..f79d8efbdb 100644 --- a/core/server/api_container/server/startosis_engine/startosis_interpreter_idempotent_test.go +++ b/core/server/api_container/server/startosis_engine/startosis_interpreter_idempotent_test.go @@ -31,7 +31,7 @@ func (suite *StartosisInterpreterIdempotentTestSuite) SetupTest() { serviceNetwork.EXPECT().GetApiContainerInfo().Maybe().Return( service_network.NewApiContainerInfo(net.IPv4(0, 0, 0, 0), uint16(1234), "0.0.0"), ) - suite.interpreter = NewStartosisInterpreter(serviceNetwork, suite.packageContentProvider, runtimeValueStore) + suite.interpreter = NewStartosisInterpreter(serviceNetwork, suite.packageContentProvider, runtimeValueStore, "") } func TestRunStartosisInterpreterIdempotentTestSuite(t *testing.T) { diff --git a/core/server/api_container/server/startosis_engine/startosis_interpreter_test.go b/core/server/api_container/server/startosis_engine/startosis_interpreter_test.go index b212d8b776..b321387fba 100644 --- a/core/server/api_container/server/startosis_engine/startosis_interpreter_test.go +++ b/core/server/api_container/server/startosis_engine/startosis_interpreter_test.go @@ -62,7 +62,7 @@ func (suite *StartosisInterpreterTestSuite) SetupTest() { suite.runtimeValueStore = runtime_value_store.NewRuntimeValueStore() suite.serviceNetwork = service_network.NewMockServiceNetwork(suite.T()) - suite.interpreter = NewStartosisInterpreter(suite.serviceNetwork, suite.packageContentProvider, suite.runtimeValueStore) + suite.interpreter = NewStartosisInterpreter(suite.serviceNetwork, suite.packageContentProvider, suite.runtimeValueStore, "") service.NewServiceRegistration( testServiceName, diff --git a/engine/launcher/args/args.go b/engine/launcher/args/args.go index 644bb9d549..f5f2d6f209 100644 --- a/engine/launcher/args/args.go +++ b/engine/launcher/args/args.go @@ -11,6 +11,8 @@ import ( const ( jsonFieldTag = "json" + + emptyJsonField = "{}" ) // Fields are public for JSON de/serialization @@ -41,6 +43,10 @@ type EngineServerArgs struct { // PoolSize represents the enclave pool size, for instance if this value is 3, these amount of idle enclaves // will be created when the engine start in order to be used when users request for a new enclave. PoolSize uint8 `json:"poolSize"` + + // Environment variable to pass to all the enclaves the engine is going to create. Those environment variable will + // then be accessible in Starlark scripts in the `kurtosis` module + EnclaveEnvVars string `json:"enclaveEnvVars"` } func (args *EngineServerArgs) UnmarshalJSON(data []byte) error { @@ -86,7 +92,11 @@ func NewEngineServerArgs( kurtosisLocalBackendConfig interface{}, onBastionHost bool, poolSize uint8, + enclaveEnvVars string, ) (*EngineServerArgs, error) { + if enclaveEnvVars == "" { + enclaveEnvVars = emptyJsonField + } result := &EngineServerArgs{ GrpcListenPortNum: grpcListenPortNum, LogLevelStr: logLevelStr, @@ -97,6 +107,7 @@ func NewEngineServerArgs( KurtosisLocalBackendConfig: kurtosisLocalBackendConfig, OnBastionHost: onBastionHost, PoolSize: poolSize, + EnclaveEnvVars: enclaveEnvVars, } if err := result.validate(); err != nil { return nil, stacktrace.Propagate(err, "An error occurred validating engine server args") diff --git a/engine/launcher/engine_server_launcher/engine_server_launcher.go b/engine/launcher/engine_server_launcher/engine_server_launcher.go index c406fa5b8b..9b7f42581b 100644 --- a/engine/launcher/engine_server_launcher/engine_server_launcher.go +++ b/engine/launcher/engine_server_launcher/engine_server_launcher.go @@ -38,6 +38,7 @@ func (launcher *EngineServerLauncher) LaunchWithDefaultVersion( backendConfigSupplier KurtosisBackendConfigSupplier, onBastionHost bool, poolSize uint8, + enclaveEnvVars string, ) ( resultPublicIpAddr net.IP, resultPublicGrpcPortSpec *port_spec.PortSpec, @@ -53,6 +54,7 @@ func (launcher *EngineServerLauncher) LaunchWithDefaultVersion( backendConfigSupplier, onBastionHost, poolSize, + enclaveEnvVars, ) if err != nil { return nil, nil, stacktrace.Propagate(err, "An error occurred launching the engine server container with default version tag '%v'", kurtosis_version.KurtosisVersion) @@ -70,6 +72,7 @@ func (launcher *EngineServerLauncher) LaunchWithCustomVersion( backendConfigSupplier KurtosisBackendConfigSupplier, onBastionHost bool, poolSize uint8, + enclaveEnvVars string, ) ( resultPublicIpAddr net.IP, resultPublicGrpcPortSpec *port_spec.PortSpec, @@ -86,6 +89,7 @@ func (launcher *EngineServerLauncher) LaunchWithCustomVersion( kurtosisBackendConfig, onBastionHost, poolSize, + enclaveEnvVars, ) if err != nil { return nil, nil, stacktrace.Propagate(err, "An error occurred creating the engine server args") diff --git a/engine/server/engine/enclave_manager/enclave_creator.go b/engine/server/engine/enclave_manager/enclave_creator.go index 71416d9590..1013fc957e 100644 --- a/engine/server/engine/enclave_manager/enclave_creator.go +++ b/engine/server/engine/enclave_manager/enclave_creator.go @@ -35,6 +35,7 @@ func (creator *EnclaveCreator) CreateEnclave( apiContainerLogLevel logrus.Level, //If blank, will use a random one enclaveName string, + enclaveEnvVars string, ) (*kurtosis_engine_rpc_api_bindings.EnclaveInfo, error) { uuid, err := uuid_generator.GenerateUUIDString() @@ -72,6 +73,7 @@ func (creator *EnclaveCreator) CreateEnclave( apiContainerLogLevel, enclaveUuid, apiContainerListenGrpcPortNumInsideNetwork, + enclaveEnvVars, ) if err != nil { @@ -144,6 +146,7 @@ func (creator *EnclaveCreator) launchApiContainer( logLevel logrus.Level, enclaveUuid enclave.EnclaveUUID, grpcListenPort uint16, + enclaveEnvVars string, ) ( resultApiContainer *api_container.APIContainer, resultErr error, @@ -159,6 +162,7 @@ func (creator *EnclaveCreator) launchApiContainer( enclaveUuid, grpcListenPort, creator.apiContainerKurtosisBackendConfigSupplier, + enclaveEnvVars, ) if err != nil { return nil, stacktrace.Propagate(err, "Expected to be able to launch api container for enclave '%v' with custom version '%v', but an error occurred", enclaveUuid, apiContainerImageVersionTag) @@ -171,6 +175,7 @@ func (creator *EnclaveCreator) launchApiContainer( enclaveUuid, grpcListenPort, creator.apiContainerKurtosisBackendConfigSupplier, + enclaveEnvVars, ) if err != nil { return nil, stacktrace.Propagate(err, "Expected to be able to launch api container for enclave '%v' with the default version, but an error occurred", enclaveUuid) diff --git a/engine/server/engine/enclave_manager/enclave_manager.go b/engine/server/engine/enclave_manager/enclave_manager.go index 309f103ee8..7f43f305e4 100644 --- a/engine/server/engine/enclave_manager/enclave_manager.go +++ b/engine/server/engine/enclave_manager/enclave_manager.go @@ -61,6 +61,7 @@ type EnclaveManager struct { enclaveCreator *EnclaveCreator enclavePool *EnclavePool + enclaveEnvVars string } func CreateEnclaveManager( @@ -69,6 +70,7 @@ func CreateEnclaveManager( apiContainerKurtosisBackendConfigSupplier api_container_launcher.KurtosisBackendConfigSupplier, engineVersion string, poolSize uint8, + enclaveEnvVars string, ) (*EnclaveManager, error) { enclaveCreator := newEnclaveCreator(kurtosisBackend, apiContainerKurtosisBackendConfigSupplier) @@ -79,7 +81,7 @@ func CreateEnclaveManager( // The enclave pool feature is only available for Kubernetes so far if kurtosisBackendType == args.KurtosisBackendType_Kubernetes { - enclavePool, err = CreateEnclavePool(kurtosisBackend, enclaveCreator, poolSize, engineVersion) + enclavePool, err = CreateEnclavePool(kurtosisBackend, enclaveCreator, poolSize, engineVersion, enclaveEnvVars) if err != nil { return nil, stacktrace.Propagate(err, "An error occurred creating enclave pool with pool-size '%v' and engine version '%v'", poolSize, engineVersion) } @@ -92,6 +94,7 @@ func CreateEnclaveManager( allExistingAndHistoricalIdentifiers: []*kurtosis_engine_rpc_api_bindings.EnclaveIdentifiers{}, enclaveCreator: enclaveCreator, enclavePool: enclavePool, + enclaveEnvVars: enclaveEnvVars, } return enclaveManager, nil @@ -155,6 +158,7 @@ func (manager *EnclaveManager) CreateEnclave( apiContainerImageVersionTag, apiContainerLogLevel, enclaveName, + manager.enclaveEnvVars, ) if err != nil { return nil, stacktrace.Propagate( diff --git a/engine/server/engine/enclave_manager/enclave_pool.go b/engine/server/engine/enclave_manager/enclave_pool.go index eef6b54084..20b14e0635 100644 --- a/engine/server/engine/enclave_manager/enclave_pool.go +++ b/engine/server/engine/enclave_manager/enclave_pool.go @@ -27,6 +27,7 @@ type EnclavePool struct { fillChan chan bool engineVersion string cancelSubRoutineCtxFunc context.CancelFunc + enclaveEnvVars string } // CreateEnclavePool will do the following: @@ -39,6 +40,7 @@ func CreateEnclavePool( enclaveCreator *EnclaveCreator, poolSize uint8, engineVersion string, + enclaveEnvVars string, ) (*EnclavePool, error) { //TODO the current implementation only removes the previous idle enclave, it's pending to implement the reusable feature @@ -80,6 +82,7 @@ func CreateEnclavePool( fillChan: fillChan, engineVersion: engineVersion, cancelSubRoutineCtxFunc: cancelCtxFunc, + enclaveEnvVars: enclaveEnvVars, } go enclavePool.run(ctxWithCancel) @@ -260,6 +263,7 @@ func (pool *EnclavePool) createNewIdleEnclave(ctx context.Context) (*kurtosis_en apiContainerVersion, defaultApiContainerLogLevel, enclaveName, + pool.enclaveEnvVars, ) if err != nil { return nil, stacktrace.Propagate( diff --git a/engine/server/engine/main.go b/engine/server/engine/main.go index ec5fafec84..d5734bb75a 100644 --- a/engine/server/engine/main.go +++ b/engine/server/engine/main.go @@ -130,7 +130,7 @@ func runMain() error { return stacktrace.Propagate(err, "An error occurred getting the Kurtosis backend for backend type '%v' and config '%+v'", serverArgs.KurtosisBackendType, backendConfig) } - enclaveManager, err := getEnclaveManager(kurtosisBackend, serverArgs.KurtosisBackendType, serverArgs.ImageVersionTag, serverArgs.PoolSize) + enclaveManager, err := getEnclaveManager(kurtosisBackend, serverArgs.KurtosisBackendType, serverArgs.ImageVersionTag, serverArgs.PoolSize, serverArgs.EnclaveEnvVars) if err != nil { return stacktrace.Propagate(err, "Failed to create an enclave manager for backend type '%v' and config '%+v'", serverArgs.KurtosisBackendType, backendConfig) } @@ -185,6 +185,7 @@ func getEnclaveManager( kurtosisBackendType args.KurtosisBackendType, engineVersion string, poolSize uint8, + enclaveEnvVars string, ) (*enclave_manager.EnclaveManager, error) { var apiContainerKurtosisBackendConfigSupplier api_container_launcher.KurtosisBackendConfigSupplier switch kurtosisBackendType { @@ -202,6 +203,7 @@ func getEnclaveManager( apiContainerKurtosisBackendConfigSupplier, engineVersion, poolSize, + enclaveEnvVars, ) if err != nil { return nil, stacktrace.Propagate(err, "An error occurred creating enclave manager for backend type '%+v' using pool-size '%v' and engine version '%v'", kurtosisBackendType, poolSize, engineVersion)